diff options
Diffstat (limited to 'vgui2/dme_controls')
51 files changed, 28144 insertions, 0 deletions
diff --git a/vgui2/dme_controls/AssetBuilder.cpp b/vgui2/dme_controls/AssetBuilder.cpp new file mode 100644 index 0000000..cfb7c2a --- /dev/null +++ b/vgui2/dme_controls/AssetBuilder.cpp @@ -0,0 +1,1725 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/AssetBuilder.h" +#include "dme_controls/DmePanel.h" +#include "dme_controls/dmecontrols_utils.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/MenuButton.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/FileOpenDialog.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/FileOpenStateMachine.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui/ischeme.h" +#include "vgui/IVGui.h" +#include "vgui/ISurface.h" +#include "tier1/tier1.h" +#include "movieobjects/dmemakefile.h" +#include "matsys_controls/picker.h" +#include "tier2/fileutils.h" +#include "vgui/keycode.h" +#include "filesystem.h" +#include "movieobjects/idmemakefileutils.h" +#include "tier3/tier3.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +#define ASSET_FILE_FORMAT "model" + +//----------------------------------------------------------------------------- +// Compile status bar +//----------------------------------------------------------------------------- +class CCompileStatusBar : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CCompileStatusBar, EditablePanel ); + +public: + enum CompileStatus_t + { + NOT_COMPILING, + CURRENTLY_COMPILING, + COMPILATION_FAILED, + COMPILATION_SUCCESSFUL + }; + + CCompileStatusBar( vgui::Panel *pParent, const char *pPanelName ); + virtual ~CCompileStatusBar(); + + virtual void PaintBackground(); + + void SetStatus( CompileStatus_t status, const char *pMessage ); + +private: + vgui::Label *m_pStatus; + CompileStatus_t m_Status; + int m_CompilingId; +}; + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CCompileStatusBar::CCompileStatusBar( vgui::Panel *pParent, const char *pPanelName ) : + BaseClass( pParent, pPanelName ) +{ + m_pStatus = new vgui::Label( this, "StatusLabel", "" ); + m_pStatus->SetAutoResize( PIN_TOPLEFT, AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); + m_pStatus->SetContentAlignment( vgui::Label::a_center ); + m_pStatus->SetTextColorState( vgui::Label::CS_BRIGHT ); + SetStatus( NOT_COMPILING, "" ); + SetPaintBackgroundEnabled( true ); + m_CompilingId = vgui::surface()->DrawGetTextureId( "vgui/progressbar" ); + if ( m_CompilingId == -1 ) // we didn't find it, so create a new one + { + m_CompilingId = vgui::surface()->CreateNewTextureID(); + vgui::surface()->DrawSetTextureFile( m_CompilingId, "vgui/progressbar", true, false ); + } +} + +CCompileStatusBar::~CCompileStatusBar() +{ +} + + +//----------------------------------------------------------------------------- +// Sets compile status +//----------------------------------------------------------------------------- +void CCompileStatusBar::SetStatus( CompileStatus_t status, const char *pMessage ) +{ + m_Status = status; + m_pStatus->SetText( pMessage ); +} + + +void CCompileStatusBar::PaintBackground() +{ + int w, h; + GetSize( w, h ); + + switch( m_Status ) + { + case NOT_COMPILING: + break; + + case COMPILATION_FAILED: + vgui::surface()->DrawSetColor( 255, 0, 0, 255 ); + vgui::surface()->DrawFilledRect( 0, 0, w, h ); + break; + + case COMPILATION_SUCCESSFUL: + vgui::surface()->DrawSetColor( 0, 255, 0, 255 ); + vgui::surface()->DrawFilledRect( 0, 0, w, h ); + break; + + case CURRENTLY_COMPILING: + { + float du = Plat_FloatTime() / 5.0f; + du -= (int)du; + du = 1.0f - du; + + Vertex_t verts[4]; + verts[0].Init( Vector2D( 0.0f, 0.0f ), Vector2D( du, 0.0f ) ); + verts[1].Init( Vector2D( w, 0.0f ), Vector2D( 1.0f + du, 0.0f ) ); + verts[2].Init( Vector2D( w, h ), Vector2D( 1.0f + du, 1.0f ) ); + verts[3].Init( Vector2D( 0.0f, h ), Vector2D( du, 1.0f ) ); + + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + vgui::surface()->DrawSetTexture( m_CompilingId ); + vgui::surface()->DrawTexturedPolygon( 4, verts ); + } + break; + } +} + + +//----------------------------------------------------------------------------- +// +// Asset Builder +// +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CAssetBuilder, DmeMakefile, "DmeMakeFileDefault", "MakeFile Editor", true ); + + +//----------------------------------------------------------------------------- +// Static data +//----------------------------------------------------------------------------- +static PickerList_t s_AssetTypes; +static bool s_bAssetTypeListBuilt = false; + + +//----------------------------------------------------------------------------- +// Builds the list of asset types +//----------------------------------------------------------------------------- +void BuildAssetTypeList( ) +{ + if ( s_bAssetTypeListBuilt ) + return; + + s_bAssetTypeListBuilt = true; + + CDisableUndoScopeGuard guard; + + int hFactory = g_pDataModel->GetFirstFactory(); + while ( g_pDataModel->IsValidFactory( hFactory ) ) + { + // Add all DmeElements that inherit from DmeMakefile + const char *pFactoryName = g_pDataModel->GetFactoryName( hFactory ); + CDmElement *pElement = GetElement< CDmElement >( g_pDataModel->CreateElement( pFactoryName, "temp" ) ); + CDmeMakefile *pMakeFile = CastElement<CDmeMakefile>( pElement ); + if ( pMakeFile && pMakeFile->GetMakefileType() ) + { + int i = s_AssetTypes.AddToTail(); + s_AssetTypes[i].m_pChoiceString = pMakeFile->GetMakefileType()->m_pHumanReadableName; + s_AssetTypes[i].m_pChoiceValue = pFactoryName; + } + DestroyElement( pElement ); + + hFactory = g_pDataModel->GetNextFactory( hFactory ); + } +} + + +//----------------------------------------------------------------------------- +// Builds the list of asset types +//----------------------------------------------------------------------------- +static PickerList_t &BuildAssetSubTypeList( const char **ppSubTypes, PickerList_t &pickerList ) +{ + if ( !ppSubTypes ) + return s_AssetTypes; + + pickerList.RemoveAll(); + + CDisableUndoScopeGuard guard; + + int nCount = s_AssetTypes.Count(); + for ( int i = 0; i < nCount; ++i ) + { + // Add all DmeElements that inherit from DmeMakefile + CDmElement *pElement = GetElement< CDmElement >( g_pDataModel->CreateElement( s_AssetTypes[i].m_pChoiceValue, "temp" ) ); + CDmeMakefile *pMakeFile = CastElement< CDmeMakefile >( pElement ); + + for ( int j = 0; ppSubTypes[j]; ++j ) + { + if ( !pElement->IsA( ppSubTypes[j] ) ) + continue; + + int k = pickerList.AddToTail(); + pickerList[k].m_pChoiceString = pMakeFile->GetMakefileType()->m_pHumanReadableName; + pickerList[k].m_pChoiceValue = s_AssetTypes[i].m_pChoiceValue; + break; + } + DestroyElement( pElement ); + } + + return pickerList; +} + + +//----------------------------------------------------------------------------- +// Shows the overwrite existing file dialog +//----------------------------------------------------------------------------- +static void OverwriteFileDialog( vgui::Panel *pActionTarget, const char *pFileName, KeyValues *pOkCommand ) +{ + if ( !g_pFullFileSystem->FileExists( pFileName ) ) + { + pActionTarget->PostMessage( pActionTarget->GetVPanel(), pOkCommand ); + return; + } + + char pBuf[1024]; + Q_snprintf( pBuf, sizeof(pBuf), "File already exists. Overwrite it?\n\n\"%s\"\n", pFileName ); + vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Overwrite Existing File?", pBuf, pActionTarget ); + pMessageBox->AddActionSignalTarget( pActionTarget ); + pMessageBox->SetOKButtonVisible( true ); + pMessageBox->SetOKButtonText( "Yes" ); + pMessageBox->SetCancelButtonVisible( true ); + pMessageBox->SetCancelButtonText( "No" ); + pMessageBox->SetCloseButtonVisible( false ); + pMessageBox->SetCommand( pOkCommand ); + pMessageBox->DoModal(); +} + + +//----------------------------------------------------------------------------- +// Utility to load a makefile +//----------------------------------------------------------------------------- +static CDmeMakefile *ReadMakefile( const char *pFileName, CDmElement **ppRoot = NULL ) +{ + if ( ppRoot ) + { + *ppRoot = NULL; + } + + CDmElement *pRoot; + DmFileId_t fileid = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pRoot, CR_DELETE_OLD ); + if ( fileid == DMFILEID_INVALID || !pRoot ) + { + Warning( "Unable to read makefile \"%s\"!\n", pFileName ); + return NULL; + } + + CDmeMakefile *pMakeFile = CastElement< CDmeMakefile >( pRoot ); + if ( !pMakeFile ) + { + CDmElement *pElement = CastElement< CDmElement >( pRoot ); + pMakeFile = pElement->GetValueElement< CDmeMakefile >( "makefile" ); + if ( !pMakeFile ) + { + DmFileId_t fileId = pRoot->GetFileId(); + DestroyElement( pRoot ); + if ( fileId != DMFILEID_INVALID && g_pDataModel->GetFileName( fileId )[0] ) + { + g_pDataModel->RemoveFileId( fileId ); + } + return NULL; + } + } + + if ( ppRoot ) + { + *ppRoot = CastElement< CDmElement >( pRoot ); + } + return pMakeFile; +} + + + +//----------------------------------------------------------------------------- +// Sort by MDL name +//----------------------------------------------------------------------------- +static int __cdecl TypeSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("type"); + const char *string2 = item2.kv->GetString("type"); + int nRetVal = Q_stricmp( string1, string2 ); + if ( nRetVal != 0 ) + return nRetVal; + + string1 = item1.kv->GetString("file"); + string2 = item2.kv->GetString("file"); + nRetVal = Q_stricmp( string1, string2 ); + if ( nRetVal != 0 ) + return nRetVal; + + int nIndex1 = item1.kv->GetInt( "index" ); + int nIndex2 = item2.kv->GetInt( "index" ); + return nIndex1 - nIndex2; +} + +static int __cdecl FileSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("file"); + const char *string2 = item2.kv->GetString("file"); + int nRetVal = Q_stricmp( string1, string2 ); + if ( nRetVal != 0 ) + return nRetVal; + + string1 = item1.kv->GetString("type"); + string2 = item2.kv->GetString("type"); + nRetVal = Q_stricmp( string1, string2 ); + if ( nRetVal != 0 ) + return nRetVal; + + int nIndex1 = item1.kv->GetInt( "index" ); + int nIndex2 = item2.kv->GetInt( "index" ); + return nIndex1 - nIndex2; +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor, destructor +//----------------------------------------------------------------------------- +CAssetBuilder::CAssetBuilder( vgui::Panel *pParent, const char *pPanelName ) : + BaseClass( pParent, pPanelName ) +{ + m_hContextMenu = NULL; + m_hRootMakefile = NULL; + m_bIsCompiling = false; + m_bDestroyMakefileOnClose = true; + + m_pInputOutputSheet = new vgui::PropertySheet( this, "InputOutputSheet" ); + m_pInputOutputSheet->AddActionSignalTarget( this ); + + m_pInputPage = new PropertyPage( m_pInputOutputSheet, "InputPage" ); + m_pOutputPage = new PropertyPage( m_pInputOutputSheet, "OutputPage" ); + m_pCompilePage = new PropertyPage( m_pInputOutputSheet, "CompilePage" ); + m_pOutputPreviewPage = new PropertyPage( m_pInputOutputSheet, "OutputPreviewPage" ); + + m_pPropertiesSplitter = new vgui::Splitter( m_pInputPage, "PropertiesSplitter", SPLITTER_MODE_VERTICAL, 1 ); + + vgui::Panel *pSplitterLeftSide = m_pPropertiesSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pPropertiesSplitter->GetChild( 1 ); + m_pDmePanel = new CDmePanel( pSplitterRightSide, "CompileOptions" ); + m_pDmePanel->AddActionSignalTarget( this ); + + m_pOututPreviewPanel = new CDmePanel( m_pOutputPreviewPage, "OutputPreview", false ); + m_pOututPreviewPanel->AddActionSignalTarget( this ); + + m_pSourcesList = new vgui::ListPanel( pSplitterLeftSide, "SourcesList" ); + m_pSourcesList->AddColumnHeader( 0, "type", "type", 100, 0 ); + m_pSourcesList->AddColumnHeader( 1, "file", "file", 52, 0 ); + m_pSourcesList->AddActionSignalTarget( this ); + m_pSourcesList->SetSortFunc( 0, TypeSortFunc ); + m_pSourcesList->SetSortFunc( 1, FileSortFunc ); + m_pSourcesList->SetSortColumn( 0 ); +// m_pSourcesList->SetSelectIndividualCells( true ); + m_pSourcesList->SetEmptyListText("No sources"); +// m_pSourcesList->SetDragEnabled( true ); + + m_pOutputList = new vgui::ListPanel( m_pOutputPage, "OutputList" ); + m_pOutputList->AddColumnHeader( 0, "type", "type", 100, 0 ); + m_pOutputList->AddColumnHeader( 1, "file", "file", 52, 0 ); + m_pOutputList->AddActionSignalTarget( this ); + m_pOutputList->SetSortFunc( 0, TypeSortFunc ); + m_pOutputList->SetSortFunc( 1, FileSortFunc ); + m_pOutputList->SetSortColumn( 0 ); + m_pOutputList->SetEmptyListText("No outputs"); + + m_pCompileOutput = new vgui::TextEntry( m_pCompilePage, "CompileOutput" ); + m_pCompileOutput->SetMultiline( true ); + m_pCompileOutput->SetVerticalScrollbar( true ); + m_pCompile = new vgui::Button( this, "CompileButton", "Compile", this, "OnCompile" ); + m_pPublish = new vgui::Button( this, "PublishButton", "Publish", this, "OnPublish" ); + m_pAbortCompile = new vgui::Button( this, "AbortCompileButton", "AbortCompile", this, "OnAbortCompile" ); + m_pCompileStatusBar = new CCompileStatusBar( this, "CompileStatus" ); + + m_pInputPage->LoadControlSettingsAndUserConfig( "resource/assetbuilderinputpage.res" ); + m_pOutputPage->LoadControlSettingsAndUserConfig( "resource/assetbuilderoutputpage.res" ); + m_pCompilePage->LoadControlSettingsAndUserConfig( "resource/assetbuildercompilepage.res" ); + m_pOutputPreviewPage->LoadControlSettingsAndUserConfig( "resource/assetbuilderoutputpreviewpage.res" ); + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettingsAndUserConfig( "resource/assetbuilder.res" ); + + // NOTE: Page adding happens *after* LoadControlSettingsAndUserConfig + // because the layout of the sheet is correct at this point. + m_pInputOutputSheet->AddPage( m_pInputPage, "Input" ); + m_pInputOutputSheet->AddPage( m_pOutputPage, "Output" ); + m_pInputOutputSheet->AddPage( m_pCompilePage, "Compile" ); + m_pInputOutputSheet->AddPage( m_pOutputPreviewPage, "Preview" ); + + m_pCompile->SetEnabled( false ); + m_pPublish->SetEnabled( false ); + m_pAbortCompile->SetEnabled( false ); +} + +CAssetBuilder::~CAssetBuilder() +{ + if ( m_bDestroyMakefileOnClose ) + { + CleanupMakefile(); + } + CleanupContextMenu(); + SaveUserConfig(); +} + + +//----------------------------------------------------------------------------- +// Default behavior is to destroy the makefile when we close +//----------------------------------------------------------------------------- +void CAssetBuilder::DestroyMakefileOnClose( bool bEnable ) +{ + m_bDestroyMakefileOnClose = bEnable; +} + + +//----------------------------------------------------------------------------- +// Builds a unique list of file IDs +//----------------------------------------------------------------------------- +void CAssetBuilder::BuildFileIDList( CDmeMakefile *pMakeFile, CUtlVector<DmFileId_t> &fileIds ) +{ + if ( !pMakeFile ) + return; + + // NOTE: Not hugely efficient. If the CDmeDependencyMakefile starts + // getting large, we can optimize this + DmFileId_t id = pMakeFile->GetFileId(); + int nCount = fileIds.Count(); + int i; + for ( i = 0; i < nCount; ++i ) + { + if ( fileIds[i] == id ) + break; + } + + if ( i == nCount ) + { + fileIds.AddToTail( id ); + } + + int nSourceCount = pMakeFile->GetSourceCount(); + for ( int i = 0; i < nSourceCount; ++i ) + { + CDmeSource *pSource = pMakeFile->GetSource(i); + BuildFileIDList( pSource->GetDependentMakefile(), fileIds ); + } +} + + +//----------------------------------------------------------------------------- +// Removes a makefile from memory +//----------------------------------------------------------------------------- +void CAssetBuilder::CleanupMakefile() +{ + m_hMakefileStack.Clear(); + m_pDmePanel->SetDmeElement( NULL ); + m_pOututPreviewPanel->SetDmeElement( NULL ); + + if ( !m_hRootMakefile.Get() ) + return; + + // First, build a list of unique file IDs + CUtlVector<DmFileId_t> fileIds; + BuildFileIDList( m_hRootMakefile, fileIds ); + + CDisableUndoScopeGuard guard; + + m_hRootMakefile = NULL; + + int nCount = fileIds.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( fileIds[i] != DMFILEID_INVALID && g_pDataModel->GetFileName( fileIds[i] )[0] ) + { + g_pDataModel->RemoveFileId( fileIds[i] ); + } + } +} + + +//----------------------------------------------------------------------------- +// Marks the file as dirty (or not) +//----------------------------------------------------------------------------- +void CAssetBuilder::SetDirty() +{ + PostActionSignal( new KeyValues( "DmeElementChanged" ) ); +} + + +//----------------------------------------------------------------------------- +// Returns the current makefile +//----------------------------------------------------------------------------- +CDmeMakefile *CAssetBuilder::GetMakeFile() +{ + return m_hMakefile.Get(); +} + +CDmeMakefile *CAssetBuilder::GetRootMakeFile() +{ + return m_hRootMakefile.Get(); +} + + +//----------------------------------------------------------------------------- +// Resets the lists; called when file name changes +//----------------------------------------------------------------------------- +void CAssetBuilder::Refresh() +{ + RefreshSourceList(); + RefreshOutputList(); +} + + +//----------------------------------------------------------------------------- +// Resets the root makefile +//----------------------------------------------------------------------------- +void CAssetBuilder::SetRootMakefile( CDmeMakefile *pMakeFile ) +{ + CleanupMakefile(); + + if ( pMakeFile ) + { + m_hRootMakefile = pMakeFile; + m_hMakefileStack.Push( m_hRootMakefile ); + } + SetCurrentMakefile( pMakeFile ); +} + + +//----------------------------------------------------------------------------- +// Resets the current makefile +//----------------------------------------------------------------------------- +void CAssetBuilder::SetCurrentMakefile( CDmeMakefile *pMakeFile ) +{ + m_hMakefile = pMakeFile; + m_pDmePanel->SetDmeElement( NULL ); + m_pOututPreviewPanel->SetDmeElement( pMakeFile, true, "DmeMakeFileOutputPreview" ); + RefreshSourceList(); + RefreshOutputList(); + + // Lets the asset builder update the title bar + PostActionSignal( new KeyValues( "UpdateFileName" ) ); +} + + +//----------------------------------------------------------------------------- +// Hook into the DME panel framework +//----------------------------------------------------------------------------- +void CAssetBuilder::SetDmeElement( CDmeMakefile *pMakeFile ) +{ + SetRootMakefile( pMakeFile ); +} + + +//----------------------------------------------------------------------------- +// Refresh the source list +//----------------------------------------------------------------------------- +void CAssetBuilder::RefreshSourceList( ) +{ + m_pSourcesList->RemoveAll(); + if ( !m_hMakefile.Get() ) + return; + + DmeMakefileType_t *pSourceTypes = m_hMakefile->GetSourceTypes(); + for ( int i = 0; pSourceTypes[i].m_pTypeName; ++i ) + { + CUtlVector< CDmeHandle< CDmeSource > > sources; + m_hMakefile->GetSources( pSourceTypes[i].m_pTypeName, sources ); + int nCount = sources.Count(); + for ( int j = 0; j < nCount; ++j ) + { + char pFullPath[MAX_PATH]; + m_hMakefile->GetSourceFullPath( sources[j], pFullPath, sizeof(pFullPath) ); + + KeyValues *pItemKeys = new KeyValues( "node", "type", pSourceTypes[i].m_pHumanReadableName ); + pItemKeys->SetString( "file", pFullPath ); + pItemKeys->SetInt( "sourceTypeIndex", i ); + pItemKeys->SetInt( "index", j ); // for sorting in the listpanel + SetElementKeyValue( pItemKeys, "dmeSource", sources[j] ); + m_pSourcesList->AddItem( pItemKeys, 0, false, false ); + } + } + + m_pSourcesList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Refreshes the output list +//----------------------------------------------------------------------------- +void CAssetBuilder::RefreshOutputList() +{ + m_pOutputList->RemoveAll(); + m_pCompile->SetEnabled( false ); + m_pPublish->SetEnabled( false ); + if ( !m_hMakefile.Get() ) + return; + + CUtlVector<CUtlString> outputs; + m_hMakefile->GetOutputs( outputs ); + int nCount = outputs.Count(); + for ( int j = 0; j < nCount; ++j ) + { + KeyValues *pItemKeys = new KeyValues( "node", "type", "Output" ); + pItemKeys->SetString( "file", outputs[j] ); + pItemKeys->SetInt( "index", j ); + m_pOutputList->AddItem( pItemKeys, 0, false, false ); + } + + bool bEnabled = ( nCount > 0 ) && ( g_pDmeMakefileUtils != NULL ); + m_pCompile->SetEnabled( bEnabled ); + m_pPublish->SetEnabled( bEnabled ); + + m_pOutputList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Selects a particular source +//----------------------------------------------------------------------------- +void CAssetBuilder::SelectSource( CDmeSource *pSource ) +{ + int nItemID = m_pSourcesList->FirstItem(); + for ( ; nItemID != m_pSourcesList->InvalidItemID(); nItemID = m_pSourcesList->NextItem( nItemID ) ) + { + KeyValues *kv = m_pSourcesList->GetItem( nItemID ); + if ( GetElementKeyValue< CDmeSource >( kv, "dmeSource" ) != pSource ) + continue; + + m_pSourcesList->SetSingleSelectedItem( nItemID ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called by the picker popped up in OnFileNew +//----------------------------------------------------------------------------- +void CAssetBuilder::OnPicked( KeyValues *kv ) +{ + const char *pValue = kv->GetString( "choice" ); + + KeyValues *pContextKeys = kv->FindKey( "OnAddSource" ); + if ( pContextKeys ) + { + OnSourceFileAdded( "", pValue ); + return; + } + + CDisableUndoScopeGuard guard; + CDmeMakefile *pMakeFile = GetElement< CDmeMakefile >( g_pDataModel->CreateElement( pValue, "unnamed" ) ); + if ( !pMakeFile ) + return; + + DmeMakefileType_t *pType = pMakeFile->GetMakefileType(); + + char pContext[MAX_PATH]; + Q_snprintf( pContext, sizeof(pContext), "asset_builder_session_%s", pType->m_pTypeName ); + + char pStartingDir[MAX_PATH]; + pMakeFile->GetDefaultDirectory( pType->m_pDefaultDirectoryID, pStartingDir, sizeof(pStartingDir) ); + g_pFullFileSystem->CreateDirHierarchy( pStartingDir ); + + KeyValues *pDialogKeys = new KeyValues( "NewSourceFileSelected", "makefileType", pValue ); + FileOpenDialog *pDialog = new FileOpenDialog( this, "Select Asset Builder File Name", false, pDialogKeys ); + pDialog->SetStartDirectoryContext( pContext, pStartingDir ); + pDialog->AddFilter( pType->m_pFileFilter, pType->m_pFileFilterString, true ); + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( false ); + DestroyElement( pMakeFile ); +} + + +//----------------------------------------------------------------------------- +// Creates a new source file, hooks it in +//----------------------------------------------------------------------------- +void CAssetBuilder::OnNewSourceFile( ) +{ + KeyValues *pKeyValues = GetSelectedSourceKeyvalues(); + CDmeSource *pSource = GetElementKeyValue< CDmeSource >( pKeyValues, "dmeSource" ); + if ( !pSource ) + return; + + BuildAssetTypeList(); + + PickerList_t typePickerList; + PickerList_t &pickerList = BuildAssetSubTypeList( pSource->GetSourceMakefileTypes(), typePickerList ); + + // Create a list indicating which type of asset to create + CPickerFrame *pPicker = new CPickerFrame( this, "Select Sub-Asset Type", "Asset Type", "assetType" ); + pPicker->DoModal( pickerList ); +} + + +//----------------------------------------------------------------------------- +// Called when the button to add a file is clicked +//----------------------------------------------------------------------------- +void CAssetBuilder::OnAddSource( ) +{ + if ( !m_hMakefile.Get() ) + return; + + PickerList_t sourceType; + + DmeMakefileType_t *pSourceTypes = m_hMakefile->GetSourceTypes(); + for ( int i = 0; pSourceTypes[i].m_pTypeName; ++i ) + { + if ( pSourceTypes[i].m_bIsSingleton ) + { + if ( m_hMakefile->HasSourceOfType( pSourceTypes[i].m_pTypeName ) ) + continue; + } + + int j = sourceType.AddToTail( ); + sourceType[j].m_pChoiceString = pSourceTypes[i].m_pHumanReadableName; + sourceType[j].m_pChoiceValue = pSourceTypes[i].m_pTypeName; + } + + if ( sourceType.Count() == 0 ) + return; + + KeyValues *pContextKeys = new KeyValues( "OnAddSource" ); + CPickerFrame *pPicker = new CPickerFrame( this, "Select Source Type", "Source Type", "sourceType" ); + pPicker->DoModal( sourceType, pContextKeys ); +} + + +//----------------------------------------------------------------------------- +// Returns the curerntly selected row +//----------------------------------------------------------------------------- +int CAssetBuilder::GetSelectedRow( ) +{ + int nItemID = m_pSourcesList->GetSelectedItem( 0 ); + return ( nItemID != -1 ) ? m_pSourcesList->GetItemCurrentRow( nItemID ) : -1; +} + + +//----------------------------------------------------------------------------- +// Selects a particular row of the source list +//----------------------------------------------------------------------------- +void CAssetBuilder::SelectSourceListRow( int nRow ) +{ + int nVisibleRowCount = m_pSourcesList->GetItemCount(); + if ( nVisibleRowCount == 0 || nRow < 0 ) + return; + + if ( nRow >= nVisibleRowCount ) + { + nRow = nVisibleRowCount - 1; + } + + int nNewItemID = m_pSourcesList->GetItemIDFromRow( nRow ); + m_pSourcesList->SetSingleSelectedItem( nNewItemID ); +} + + +//----------------------------------------------------------------------------- +// Called when the button to remove a file is clicked +//----------------------------------------------------------------------------- +void CAssetBuilder::OnRemoveSource( ) +{ + int nCount = m_pSourcesList->GetSelectedItemsCount(); + if ( nCount == 0 || !m_hMakefile.Get() ) + return; + + int nRow = GetSelectedRow(); + Assert( nRow >= 0 ); + + // Update the selection to be reasonable after deletion + CDisableUndoScopeGuard guard; + for ( int i = 0; i < nCount; ++i ) + { + int nItemID = m_pSourcesList->GetSelectedItem( i ); + KeyValues *pKeyValues = m_pSourcesList->GetItem( nItemID ); + CDmeSource *pSource = GetElementKeyValue< CDmeSource >( pKeyValues, "dmeSource" ); + if ( pSource ) + { + m_hMakefile->RemoveSource( pSource ); + DestroyElement( pSource ); + SetDirty( ); + } + } + + RefreshSourceList(); + + SelectSourceListRow( nRow ); +} + + +//----------------------------------------------------------------------------- +// Called to make a particular source the currently selected source +//----------------------------------------------------------------------------- +void CAssetBuilder::OnZoomInSource() +{ + // Called to zoom into the currently selected source + CDmeSource *pSource = GetSelectedSource( ); + if ( !pSource ) + return; + + CDmeMakefile *pChild = m_hMakefile->FindDependentMakefile( pSource ); + if ( pChild ) + { + CDmeHandle< CDmeMakefile > hChild; + hChild = pChild; + m_hMakefileStack.Push( hChild ); + SetCurrentMakefile( pChild ); + } +} + + +//----------------------------------------------------------------------------- +// Called to zoom out of a particular source +//----------------------------------------------------------------------------- +void CAssetBuilder::OnZoomOutSource() +{ + // Called to zoom into the currently selected source + if ( m_hMakefileStack.Count() <= 1 ) + return; + + CDmeMakefile *pOldParent = m_hMakefileStack.Top().Get(); + m_hMakefileStack.Pop( ); + CDmeMakefile *pParent = m_hMakefileStack.Top().Get(); + if ( pParent ) + { + SetCurrentMakefile( pParent ); + CDmeSource *pSource = pParent->FindAssociatedSource( pOldParent ); + if ( pSource ) + { + SelectSource( pSource ); + } + } +} + + +//----------------------------------------------------------------------------- +// Called when a key is typed +//----------------------------------------------------------------------------- +void CAssetBuilder::OnKeyCodeTyped( vgui::KeyCode code ) +{ + if ( code == KEY_DELETE ) + { + OnRemoveSource(); + return; + } + + if ( code == KEY_ENTER ) + { + OnZoomInSource(); + return; + } + + BaseClass::OnKeyCodeTyped( code ); +} + + +//----------------------------------------------------------------------------- +// Called when we're browsing for a source file and one was selected +//----------------------------------------------------------------------------- +void CAssetBuilder::OnSourceFileAdded( const char *pFileName, const char *pTypeName ) +{ + CDmeSource *pSource = NULL; + { + CDisableUndoScopeGuard guard; + pSource = m_hMakefile->AddSource( pTypeName, pFileName ); + } + SetDirty( ); + RefreshSourceList( ); + SelectSource( pSource ); +} + + +//----------------------------------------------------------------------------- +// Called when the file open dialog for browsing source files selects something +//----------------------------------------------------------------------------- +void CAssetBuilder::OnNewSourceFileSelected( const char *pFileName, KeyValues *kv ) +{ + int nCount = m_pSourcesList->GetSelectedItemsCount(); + if ( nCount != 1 || !m_hMakefile.Get() ) + return; + + int nItemID = m_pSourcesList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pSourcesList->GetItem( nItemID ); + CDmeSource *pSource = GetElementKeyValue< CDmeSource >( pKeyValues, "dmeSource" ); + if ( !pSource ) + return; + + const char *pMakeFileType = kv->GetString( "makefileType" ); + + { + CDisableUndoScopeGuard guard; + m_hMakefile->SetSourceFullPath( pSource, pFileName ); + + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( pFileName ); + CDmeMakefile *pSourceMakeFile = CreateElement< CDmeMakefile >( pMakeFileType, pFileName, fileid ); + pSourceMakeFile->SetFileName( pFileName ); + + m_hMakefile->SetAssociation( pSource, pSourceMakeFile ); + SetDirty( ); + } + + pKeyValues->SetString( "file", pFileName ); + m_pSourcesList->ApplyItemChanges( nItemID ); + m_pSourcesList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Called when the file open dialog for browsing source files selects something +//----------------------------------------------------------------------------- +void CAssetBuilder::OnFileSelected( KeyValues *kv ) +{ + const char *pFileName = kv->GetString( "fullpath", NULL ); + if ( !pFileName ) + return; + + KeyValues *pDialogKeys = kv->FindKey( "SelectSourceFile" ); + if ( pDialogKeys ) + { + OnSourceFileNameChanged( pFileName ); + return; + } + + pDialogKeys = kv->FindKey( "NewSourceFileSelected" ); + if ( pDialogKeys ) + { + if ( !g_pFullFileSystem->FileExists( pFileName ) ) + { + OnNewSourceFileSelected( pFileName, pDialogKeys ); + } + else + { + OnSourceFileNameChanged( pFileName ); + } + return; + } +} + + +//----------------------------------------------------------------------------- +// Shows the source file browser +//----------------------------------------------------------------------------- +void CAssetBuilder::ShowSourceFileBrowser( const char *pTitle, DmeMakefileType_t *pSourceType, KeyValues *pDialogKeys ) +{ + char pContext[MAX_PATH]; + Q_snprintf( pContext, sizeof(pContext), "asset_builder_session_%s", pSourceType->m_pTypeName ); + + char pStartingDir[MAX_PATH]; + m_hMakefile->GetDefaultDirectory( pSourceType->m_pDefaultDirectoryID, pStartingDir, sizeof(pStartingDir) ); + g_pFullFileSystem->CreateDirHierarchy( pStartingDir ); + + FileOpenDialog *pDialog = new FileOpenDialog( this, pTitle, true, pDialogKeys ); + pDialog->SetStartDirectoryContext( pContext, pStartingDir ); + pDialog->AddFilter( pSourceType->m_pFileFilter, pSourceType->m_pFileFilterString, true ); + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( false ); +} + + +//----------------------------------------------------------------------------- +// Called when the button to browse for a source file is clicked +//----------------------------------------------------------------------------- +void CAssetBuilder::OnBrowseSourceFile( ) +{ + KeyValues *pKeyValues = GetSelectedSourceKeyvalues(); + if ( !pKeyValues ) + return; + + int nSourceTypeIndex = pKeyValues->GetInt( "sourceTypeIndex", -1 ); + + KeyValues *pDialogKeys = new KeyValues( "SelectSourceFile" ); + DmeMakefileType_t &sourceType = m_hMakefile->GetSourceTypes()[nSourceTypeIndex]; + ShowSourceFileBrowser( "Select Source File", &sourceType, pDialogKeys ); +} + + +//----------------------------------------------------------------------------- +// Command handler +//----------------------------------------------------------------------------- +void CAssetBuilder::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "OnCompile" ) ) + { + OnCompile(); + return; + } + + if ( !Q_stricmp( pCommand, "OnAbortCompile" ) ) + { + OnAbortCompile(); + return; + } + + if ( !Q_stricmp( pCommand, "OnPublish" ) ) + { + OnPublish(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// Cleans up the context menu +//----------------------------------------------------------------------------- +void CAssetBuilder::CleanupContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + m_hContextMenu->MarkForDeletion(); + m_hContextMenu = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CAssetBuilder::OnOpenContextMenu( KeyValues *kv ) +{ + CleanupContextMenu(); + if ( !m_hMakefile.Get() ) + return; + + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + int nItemID = kv->GetInt( "itemID", -1 ); + + if ( pPanel != m_pSourcesList ) + return; + + m_hContextMenu = new Menu( this, "ActionMenu" ); + m_hContextMenu->AddMenuItem( "Add...", new KeyValues( "AddSource" ), this ); + int nCount = m_pSourcesList->GetSelectedItemsCount(); + if ( nCount > 0 ) + { + m_hContextMenu->AddMenuItem( "Remove", new KeyValues( "RemoveSource" ), this ); + } + + bool bShowZoomIn = false; + bool bShowZoomOut = m_hMakefileStack.Count() > 1; + bool bShowLoadSourceFile = false; + bool bHasValidSourceFile = false; + if ( nCount == 1 && nItemID != -1 ) + { + KeyValues *kv = m_pSourcesList->GetItem( nItemID ); + CDmeSource *pSource = GetElementKeyValue< CDmeSource >( kv, "dmeSource" ); + if ( pSource ) + { + bHasValidSourceFile = pSource->GetRelativeFileName()[0] != 0; + if ( m_hMakefile->FindDependentMakefile( pSource ) ) + { + bShowZoomIn = true; + } + else + { + bShowLoadSourceFile = bHasValidSourceFile; + } + } + } + + if ( bShowZoomIn || bShowZoomOut ) + { + m_hContextMenu->AddSeparator(); + if ( bShowZoomIn ) + { + m_hContextMenu->AddMenuItem( "Zoom In", new KeyValues( "ZoomInSource" ), this ); + } + if ( bShowZoomOut ) + { + m_hContextMenu->AddMenuItem( "Zoom Out", new KeyValues( "ZoomOutSource" ), this ); + } + } + + if ( nCount == 1 ) + { + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "New Source File...", new KeyValues( "NewSourceFile" ), this ); + m_hContextMenu->AddMenuItem( "Select Source File...", new KeyValues( "BrowseSourceFile" ), this ); + if ( bShowLoadSourceFile ) + { + m_hContextMenu->AddMenuItem( "Load Source File", new KeyValues( "LoadSourceFile" ), this ); + } + if ( bHasValidSourceFile ) + { + m_hContextMenu->AddMenuItem( "Edit Source File", new KeyValues( "EditSourceFile" ), this ); + } + } + + Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CAssetBuilder::OnSourceItemSelectionChanged( ) +{ + int nCount = m_pSourcesList->GetSelectedItemsCount(); + if ( nCount != 1 ) + { + m_pDmePanel->SetDmeElement( NULL ); + return; + } + + int nItemID = m_pSourcesList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pSourcesList->GetItem( nItemID ); + CDmeSource *pSource = GetElementKeyValue< CDmeSource >( pKeyValues, "dmeSource" ); + m_pDmePanel->SetDmeElement( pSource ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CAssetBuilder::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pSourcesList ) + { + OnSourceItemSelectionChanged(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CAssetBuilder::OnItemDeselected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pSourcesList ) + { + OnSourceItemSelectionChanged(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Returns the selected source (if there's only 1 source selected) +//----------------------------------------------------------------------------- +CDmeSource *CAssetBuilder::GetSelectedSource( ) +{ + int nCount = m_pSourcesList->GetSelectedItemsCount(); + if ( nCount != 1 || !m_hMakefile.Get() ) + return NULL; + + int nItemID = m_pSourcesList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pSourcesList->GetItem( nItemID ); + return GetElementKeyValue< CDmeSource >( pKeyValues, "dmeSource" ); +} + +KeyValues *CAssetBuilder::GetSelectedSourceKeyvalues( ) +{ + int nCount = m_pSourcesList->GetSelectedItemsCount(); + if ( nCount != 1 || !m_hMakefile.Get() ) + return NULL; + + int nItemID = m_pSourcesList->GetSelectedItem( 0 ); + return m_pSourcesList->GetItem( nItemID ); +} + + +//----------------------------------------------------------------------------- +// Called when the source file name changes +//----------------------------------------------------------------------------- +void CAssetBuilder::OnSourceFileNameChanged( const char *pFileName ) +{ + int nCount = m_pSourcesList->GetSelectedItemsCount(); + if ( nCount != 1 || !m_hMakefile.Get() ) + return; + + int nItemID = m_pSourcesList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pSourcesList->GetItem( nItemID ); + CDmeSource *pSource = GetElementKeyValue< CDmeSource >( pKeyValues, "dmeSource" ); + if ( !pSource ) + return; + + { + CDisableUndoScopeGuard guard; + m_hMakefile->SetSourceFullPath( pSource, pFileName ); + SetDirty( ); + } + + pKeyValues->SetString( "file", pFileName ); + m_pSourcesList->ApplyItemChanges( nItemID ); + m_pSourcesList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Called during compilation +//----------------------------------------------------------------------------- +void CAssetBuilder::OnLoadSourceFile() +{ + CDmeSource *pSource = GetSelectedSource( ); + if ( !pSource ) + return; + + char pFullPath[MAX_PATH]; + m_hMakefile->GetSourceFullPath( pSource, pFullPath, sizeof(pFullPath) ); + + { + CDisableUndoScopeGuard guard; + + CDmElement *pRoot; + CDmeMakefile *pMakeFile = ReadMakefile( pFullPath, &pRoot ); + if ( !pMakeFile ) + return; + + // Successfully loaded a makefile. Set up the association. + m_hMakefile->SetAssociation( pSource, pMakeFile ); + + // Refresh the dme panel... setting association could provoke changes + m_pDmePanel->SetDmeElement( pSource, true ); + } +} + + +//----------------------------------------------------------------------------- +// Called to open an external editor for this source file +//----------------------------------------------------------------------------- +void CAssetBuilder::OnEditSourceFile() +{ + CDmeSource *pSource = GetSelectedSource( ); + if ( pSource ) + { + pSource->OpenEditor(); + } +} + + +//----------------------------------------------------------------------------- +// Finishes compilation +//----------------------------------------------------------------------------- +void CAssetBuilder::FinishCompilation( CompilationState_t state ) +{ + // NOTE: compilation can cause the makefile to be completely + // rebuilt if it's sitting in the output file. Therefore, + // Detach the source preview panel from the source and refresh the + // source list to get it to correctly reconnect to the new source elements + m_pDmePanel->SetDmeElement( NULL ); + int nRow = GetSelectedRow(); + + m_pOututPreviewPanel->SetDmeElement( m_hMakefile, true, "DmeMakeFileOutputPreview" ); + m_bIsCompiling = false; + + // NOTE: Sort of side-effecty. These two things must be done after + // m_pOututPreviewPanel->SetDmeElement, since that's what reloads the output element, + // which is also what can cause a reload of the makefile + RefreshSourceList(); + SelectSourceListRow( nRow ); + + // Lets the asset builder update the title bar + // (compilation could have changed the dirty state if the makefile is in the file) + PostActionSignal( new KeyValues( "UpdateFileName" ) ); + + if ( state == COMPILATION_FAILED ) + { + char pBuf[256]; + Q_snprintf( pBuf, sizeof(pBuf), "Compilation Error (return code %d)", g_pDmeMakefileUtils->GetExitCode() ); + m_pCompileStatusBar->SetStatus( CCompileStatusBar::COMPILATION_FAILED, pBuf ); + } + else + { + m_pCompileStatusBar->SetStatus( CCompileStatusBar::COMPILATION_SUCCESSFUL, "Compile Successful!" ); + } +} + + +//----------------------------------------------------------------------------- +// Called during compilation +//----------------------------------------------------------------------------- +void CAssetBuilder::OnTick() +{ + BaseClass::OnTick(); + + if ( m_bIsCompiling ) + { + int nLen = g_pDmeMakefileUtils->GetCompileOutputSize( ); + char *pBuf = (char*)_alloca( nLen+1 ); + CompilationState_t state = g_pDmeMakefileUtils->UpdateCompilation( pBuf, nLen ); + if ( nLen > 0 ) + { + m_pCompileOutput->InsertString( pBuf ); + } + Assert( m_hMakefile.Get() ); + if ( state != COMPILATION_NOT_COMPLETE ) + { + FinishCompilation( state ); + } + } + + if ( !m_bIsCompiling ) + { + m_pAbortCompile->SetEnabled( false ); + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + } +} + + +//----------------------------------------------------------------------------- +// Abort compile asset +//----------------------------------------------------------------------------- +void CAssetBuilder::OnAbortCompile() +{ + if ( m_bIsCompiling ) + { + g_pDmeMakefileUtils->AbortCurrentCompilation(); + m_bIsCompiling = false; + m_pAbortCompile->SetEnabled( false ); + m_pCompileStatusBar->SetStatus( CCompileStatusBar::COMPILATION_FAILED, "Compile Aborted" ); + } +} + + +//----------------------------------------------------------------------------- +// Compile asset +//----------------------------------------------------------------------------- +void CAssetBuilder::OnCompile( ) +{ + if ( !m_hMakefile.Get() ) + return; + + OnAbortCompile(); + + m_pCompileOutput->SetText( "" ); + g_pDmeMakefileUtils->PerformCompile( m_hMakefile, false ); + m_bIsCompiling = true; + m_pAbortCompile->SetEnabled( true ); + m_pCompileStatusBar->SetStatus( CCompileStatusBar::CURRENTLY_COMPILING, "Compiling..." ); + + vgui::ivgui()->AddTickSignal( GetVPanel(), 10 ); +} + + +//----------------------------------------------------------------------------- +// Compile, then publish +//----------------------------------------------------------------------------- +void CAssetBuilder::OnPublish( ) +{ + if ( !m_hMakefile.Get() ) + return; + + OnAbortCompile(); + + m_pCompileOutput->SetText( "" ); + g_pDmeMakefileUtils->PerformCompile( m_hMakefile, false ); + m_bIsCompiling = true; + m_pAbortCompile->SetEnabled( true ); + m_pCompileStatusBar->SetStatus( CCompileStatusBar::CURRENTLY_COMPILING, "Compiling..." ); + vgui::ivgui()->AddTickSignal( GetVPanel(), 10 ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Constructor, destructor +//----------------------------------------------------------------------------- +CAssetBuilderFrame::CAssetBuilderFrame( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "AssetBuilderFrame" ) +{ + m_TitleString = pTitle; + + SetMenuButtonVisible( true ); + SetImages( "resource/downarrow" ); + + m_pAssetBuilder = new CAssetBuilder( this, "AssetBuilder" ); + m_pAssetBuilder->AddActionSignalTarget( this ); + + vgui::Menu *pMenu = new vgui::Menu( NULL, "FileMenu" ); + pMenu->AddMenuItem( "new", "#AssetBuilder_FileNew", new KeyValues( "FileNew" ), this ); + pMenu->AddMenuItem( "open", "#AssetBuilder_FileOpen", new KeyValues( "FileOpen" ), this ); + pMenu->AddMenuItem( "save", "#AssetBuilder_FileSave", new KeyValues( "FileSave" ), this ); + pMenu->AddMenuItem( "saveas", "#AssetBuilder_FileSaveAs", new KeyValues( "FileSaveAs" ), this ); + SetSysMenu( pMenu ); + + m_pFileOpenStateMachine = new vgui::FileOpenStateMachine( this, this ); + m_pFileOpenStateMachine->AddActionSignalTarget( this ); + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettingsAndUserConfig( "resource/assetbuilderframe.res" ); + + UpdateFileName(); +} + +CAssetBuilderFrame::~CAssetBuilderFrame() +{ +} + + +//----------------------------------------------------------------------------- +// Inherited from IFileOpenStateMachineClient +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::SetupFileOpenDialog( vgui::FileOpenDialog *pDialog, bool bOpenFile, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + // Compute starting directory + char pStartingDir[ MAX_PATH ]; + GetModContentSubdirectory( "", pStartingDir, sizeof(pStartingDir) ); + + if ( bOpenFile ) + { + // Clear out the existing makefile if we're opening a file + m_pAssetBuilder->SetRootMakefile( NULL ); + pDialog->SetTitle( "Open Asset MakeFile", true ); + } + else + { + pDialog->SetTitle( "Save Asset MakeFile As", true ); + } + + pDialog->SetStartDirectoryContext( "asset_browser_makefile", pStartingDir ); + pDialog->AddFilter( "*.*", "All Files (*.*)", false ); + pDialog->AddFilter( "*.dmx", "Asset MakeFiles (*.dmx)", true, "keyvalues2" ); +} + +bool CAssetBuilderFrame::OnReadFileFromDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + CDmElement *pRoot; + CDmeMakefile *pMakeFile = ReadMakefile( pFileName, &pRoot ); + if ( !pMakeFile ) + return false; + + Reset( pMakeFile ); + return true; +} + +bool CAssetBuilderFrame::OnWriteFileToDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + // Recompute relative paths for each source now that we know the file name + // NOTE: This also updates the name of the fileID in the datamodel system + CDmeMakefile *pMakefile = m_pAssetBuilder->GetMakeFile(); + bool bOk; + { + CDisableUndoScopeGuard guard; + bOk = pMakefile->SetFileName( pFileName ); + } + if ( !bOk ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "#AssetBuilder_CannotRenameSourceFiles", "#AssetBuilder_CannotRenameSourceFilesText", this ); + pError->DoModal(); + return false; + } + + CDmElement *pRoot = GetElement< CDmElement >( g_pDataModel->GetFileRoot( pMakefile->GetFileId() ) ); + if ( !pRoot ) + { + pRoot = pMakefile; + } + bOk = g_pDataModel->SaveToFile( pFileName, NULL, g_pDataModel->GetDefaultEncoding( pFileFormat ), pFileFormat, pRoot ); + m_pAssetBuilder->Refresh(); + return bOk; +} + + +//----------------------------------------------------------------------------- +// Updates the file name +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::UpdateFileName( ) +{ + CDmeMakefile *pMakeFile = m_pAssetBuilder->GetMakeFile(); + if ( !pMakeFile ) + { + SetTitle( m_TitleString.Get(), true ); + return; + } + + DmeMakefileType_t *pMakefileType = pMakeFile->GetMakefileType(); + + DmFileId_t fileId = pMakeFile->GetFileId(); + const char *pFileName = ( fileId != DMFILEID_INVALID ) ? g_pDataModel->GetFileName( fileId ) : "<unnamed>"; + if ( !pFileName || !pFileName[0] ) + { + pFileName = "<unnamed>"; + } + + char pBuf[2*MAX_PATH]; + if ( m_TitleString.Get() ) + { + Q_snprintf( pBuf, sizeof(pBuf), "%s - %s - %s%s", m_TitleString.Get(), pMakefileType->m_pHumanReadableName, pFileName, pMakeFile->IsDirty() ? " *" : "" ); + } + else + { + Q_snprintf( pBuf, sizeof(pBuf), "%s - %s%s", pMakefileType->m_pHumanReadableName, pFileName, pMakeFile->IsDirty() ? " *" : "" ); + } + SetTitle( pBuf, true ); +} + + +//----------------------------------------------------------------------------- +// Marks the file as dirty (or not) +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::SetDirty( bool bDirty ) +{ + CDmeMakefile *pMakeFile = m_pAssetBuilder->GetMakeFile(); + if ( pMakeFile && ( pMakeFile->IsDirty() != bDirty ) ) + { + pMakeFile->SetDirty( bDirty ); + + // Necessary because we draw a * if it's dirty before the name + UpdateFileName(); + } +} + + +//----------------------------------------------------------------------------- +// Called when the asset builder changes something +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::OnDmeElementChanged() +{ + SetDirty( true ); +} + + +//----------------------------------------------------------------------------- +// Resets the state +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::Reset( CDmeMakefile *pMakeFile ) +{ + // NOTE: Don't need to call SetDirty because we call UpdateFileName below + m_pAssetBuilder->SetRootMakefile( pMakeFile ); + UpdateFileName(); +} + + +//----------------------------------------------------------------------------- +// Called when the file open dialog for selecting the new asset name is selected +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::OnPerformFileNew( KeyValues *kv ) +{ + const char *pMakefileType = kv->GetString( "makefileType" ); + const char *pFileName = kv->GetString( "fileName" ); + CDmeMakefile *pMakeFile; + { + DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( pFileName ); + CDisableUndoScopeGuard guard; + pMakeFile = CreateElement< CDmeMakefile >( pMakefileType, pFileName, fileid ); + } + if ( !pMakeFile ) + return; + + pMakeFile->SetFileName( pFileName ); + Reset( pMakeFile ); + SetDirty( true ); +} + + +//----------------------------------------------------------------------------- +// Called when the file open dialog for browsing source files selects something +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::OnFileSelected( KeyValues *kv ) +{ + const char *pFileName = kv->GetString( "fullpath", NULL ); + if ( !pFileName ) + return; + + KeyValues *pDialogKeys = kv->FindKey( "OnFileNew" ); + if ( pDialogKeys ) + { + KeyValues *pOkCommand = new KeyValues( "PerformFileNew", "makefileType", pDialogKeys->GetString( "makefileType" ) ); + pOkCommand->SetString( "fileName", pFileName ); + OverwriteFileDialog( this, pFileName, pOkCommand ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called by the picker popped up in OnFileNew +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::OnPicked( KeyValues *kv ) +{ + const char *pValue = kv->GetString( "choice" ); + + CDisableUndoScopeGuard guard; + CDmeMakefile *pMakeFile = GetElement< CDmeMakefile >( g_pDataModel->CreateElement( pValue, "unnamed" ) ); + if ( !pMakeFile ) + return; + + DmeMakefileType_t *pType = pMakeFile->GetMakefileType(); + + char pContext[MAX_PATH]; + Q_snprintf( pContext, sizeof(pContext), "asset_builder_session_%s", pType->m_pTypeName ); + + char pStartingDir[MAX_PATH]; + pMakeFile->GetDefaultDirectory( pType->m_pDefaultDirectoryID, pStartingDir, sizeof(pStartingDir) ); + g_pFullFileSystem->CreateDirHierarchy( pStartingDir ); + + char pTitle[MAX_PATH]; + Q_snprintf( pTitle, sizeof(pTitle), "Select %s File Name", pType->m_pHumanReadableName ); + + KeyValues *pDialogKeys = new KeyValues( "OnFileNew", "makefileType", pValue ); + FileOpenDialog *pDialog = new FileOpenDialog( this, pTitle, false, pDialogKeys ); + pDialog->SetStartDirectoryContext( pContext, pStartingDir ); + pDialog->AddFilter( pType->m_pFileFilter, pType->m_pFileFilterString, true ); + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( false ); + DestroyElement( pMakeFile ); +} + + +//----------------------------------------------------------------------------- +// Called by the file open state machine when an operation has completed +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::OnFileStateMachineFinished( KeyValues *pKeyValues ) +{ + KeyValues *pNewFile = pKeyValues->FindKey( "FileNew" ); + if ( pNewFile ) + { + if ( pKeyValues->GetInt( "wroteFile", 0 ) != 0 ) + { + SetDirty( false ); + UpdateFileName(); + } + if ( pKeyValues->GetInt( "completionState", FileOpenStateMachine::IN_PROGRESS ) == FileOpenStateMachine::SUCCESSFUL ) + { + ShowNewAssetPicker(); + } + return; + } + + KeyValues *pSaveFile = pKeyValues->FindKey( "FileSave" ); + if ( pSaveFile ) + { + if ( pKeyValues->GetInt( "wroteFile", 0 ) != 0 ) + { + SetDirty( false ); + UpdateFileName(); + } + return; + } +} + + +//----------------------------------------------------------------------------- +// Shows a picker for creating a new asset +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::ShowNewAssetPicker( ) +{ + BuildAssetTypeList(); + + // Create a list indicating which type of asset to create + CPickerFrame *pPicker = new CPickerFrame( this, "Select Asset Type", "Asset Type", "assetType" ); + pPicker->DoModal( s_AssetTypes ); +} + + +//----------------------------------------------------------------------------- +// Creates a new file +//----------------------------------------------------------------------------- +void CAssetBuilderFrame::OnFileNew( ) +{ + CDmeMakefile *pMakeFile = m_pAssetBuilder->GetMakeFile(); + if ( pMakeFile && pMakeFile->IsDirty() ) + { + KeyValues *pContextKeyValues = new KeyValues( "FileNew" ); + const char *pFileName = g_pDataModel->GetFileName( pMakeFile->GetFileId() ); + m_pFileOpenStateMachine->SaveFile( pContextKeyValues, pFileName, ASSET_FILE_FORMAT, FOSM_SHOW_PERFORCE_DIALOGS | FOSM_SHOW_SAVE_QUERY ); + return; + } + + ShowNewAssetPicker(); +} + +void CAssetBuilderFrame::OnFileOpen( ) +{ + int nFlags = 0; + const char *pFileName = NULL; + CDmeMakefile *pMakeFile = m_pAssetBuilder->GetMakeFile(); + if ( pMakeFile && pMakeFile->IsDirty() ) + { + nFlags = FOSM_SHOW_PERFORCE_DIALOGS | FOSM_SHOW_SAVE_QUERY; + pFileName = g_pDataModel->GetFileName( pMakeFile->GetFileId() ); + } + KeyValues *pContextKeyValues = new KeyValues( "FileOpen" ); + m_pFileOpenStateMachine->OpenFile( ASSET_FILE_FORMAT, pContextKeyValues, pFileName, NULL, nFlags ); +} + +void CAssetBuilderFrame::OnFileSave( ) +{ + CDmeMakefile *pMakeFile = m_pAssetBuilder->GetMakeFile(); + if ( !pMakeFile ) + return; + + KeyValues *pContextKeyValues = new KeyValues( "FileSave" ); + const char *pFileName = g_pDataModel->GetFileName( pMakeFile->GetFileId() ); + m_pFileOpenStateMachine->SaveFile( pContextKeyValues, pFileName, ASSET_FILE_FORMAT, FOSM_SHOW_PERFORCE_DIALOGS ); +} + +void CAssetBuilderFrame::OnFileSaveAs( ) +{ + CDmeMakefile *pMakeFile = m_pAssetBuilder->GetMakeFile(); + if ( !pMakeFile ) + return; + + KeyValues *pContextKeyValues = new KeyValues( "FileSave" ); + m_pFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, ASSET_FILE_FORMAT, FOSM_SHOW_PERFORCE_DIALOGS ); +} + + diff --git a/vgui2/dme_controls/AttributeBasePickerPanel.cpp b/vgui2/dme_controls/AttributeBasePickerPanel.cpp new file mode 100644 index 0000000..0f7007a --- /dev/null +++ b/vgui2/dme_controls/AttributeBasePickerPanel.cpp @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeBasePickerPanel.h" +#include "filesystem.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/FileOpenDialog.h" +#include "dme_controls/AttributeTextEntry.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeBasePickerPanel::CAttributeBasePickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ + m_pOpen = new vgui::Button( this, "Open", "...", this, "open" ); +} + +void CAttributeBasePickerPanel::OnCommand( char const *cmd ) +{ + if ( !Q_stricmp( cmd, "open" ) ) + { + ShowPickerDialog(); + } + else + { + BaseClass::OnCommand( cmd ); + } +} + +void CAttributeBasePickerPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, w, h; + m_pType->GetBounds( x, y, w, h ); + + int inset = 25; + m_pType->SetWide( w - inset ); + + x += w; + x -= inset; + + h -= 2; + + m_pOpen->SetBounds( x, y, inset, h ); +} diff --git a/vgui2/dme_controls/AttributeBoolChoicePanel.cpp b/vgui2/dme_controls/AttributeBoolChoicePanel.cpp new file mode 100644 index 0000000..034ba09 --- /dev/null +++ b/vgui2/dme_controls/AttributeBoolChoicePanel.cpp @@ -0,0 +1,159 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeBoolChoicePanel.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ComboBox.h" +#include "datamodel/dmelement.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "datamodel/dmelementfactoryhelper.h" +#include "dme_controls/inotifyui.h" +#include "dme_controls/dmecontrols.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Expose DmeEditorAttributeInfo to the scene database +//----------------------------------------------------------------------------- +IMPLEMENT_ELEMENT_FACTORY( DmeEditorBoolChoicesInfo, CDmeEditorBoolChoicesInfo ); + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +void CDmeEditorBoolChoicesInfo::OnConstruction() +{ + // Add a true + false method, default + CreateChoice( "false" ); + CreateChoice( "true" ); +} + +void CDmeEditorBoolChoicesInfo::OnDestruction() +{ +} + + +//----------------------------------------------------------------------------- +// Add a choice +//----------------------------------------------------------------------------- +void CDmeEditorBoolChoicesInfo::SetFalseChoice( const char *pChoiceString ) +{ + m_Choices[0]->SetValue<CUtlString>( "string", pChoiceString ); +} + +void CDmeEditorBoolChoicesInfo::SetTrueChoice( const char *pChoiceString ) +{ + m_Choices[1]->SetValue<CUtlString>( "string", pChoiceString ); +} + + +//----------------------------------------------------------------------------- +// Gets the choices +//----------------------------------------------------------------------------- +const char *CDmeEditorBoolChoicesInfo::GetFalseChoiceString( ) const +{ + return GetChoiceString( 0 ); +} + +const char *CDmeEditorBoolChoicesInfo::GetTrueChoiceString( ) const +{ + return GetChoiceString( 1 ); +} + + +//----------------------------------------------------------------------------- +// +// Constructor +// +//----------------------------------------------------------------------------- +CAttributeBoolChoicePanel::CAttributeBoolChoicePanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + + +//----------------------------------------------------------------------------- +// Derived classes can re-implement this to fill the combo box however they like +//----------------------------------------------------------------------------- +void CAttributeBoolChoicePanel::PopulateComboBox( vgui::ComboBox *pComboBox ) +{ + pComboBox->DeleteAllItems(); + + CDmeEditorBoolChoicesInfo *pInfo = CastElement<CDmeEditorBoolChoicesInfo>( GetEditorInfo() ); + if ( !pInfo ) + return; + + // Fill in the choices + const char *pFalseChoice = pInfo->GetFalseChoiceString( ); + const char *pTrueChoice = pInfo->GetTrueChoiceString( ); + + // Add the dynamic choices next + if ( pInfo->HasChoiceType() ) + { + const char *choices[2]; + if ( ElementPropertiesChoices()->GetBoolChoiceList( pInfo->GetChoiceType(), GetPanelElement(), GetAttributeName(), IsArrayEntry(), choices ) ) + { + pFalseChoice = choices[0]; + pTrueChoice = choices[1]; + } + } + + KeyValues *kv = new KeyValues( "entry" ); + kv->SetInt( "value", false ); + pComboBox->AddItem( pFalseChoice, kv ); + + kv = new KeyValues( "entry" ); + kv->SetInt( "value", true ); + pComboBox->AddItem( pTrueChoice, kv ); +} + + +//----------------------------------------------------------------------------- +// Sets the attribute based on the combo box +//----------------------------------------------------------------------------- +void CAttributeBoolChoicePanel::SetAttributeFromComboBox( vgui::ComboBox *pComboBox, KeyValues *pKeyValues ) +{ + bool bOldValue = GetAttributeValue<bool>(); + bool bValue = pKeyValues->GetInt( "value", 0 ) != 0; + if ( bOldValue == bValue ) + return; + + CUndoScopeGuard guard( NOTIFY_SOURCE_PROPERTIES_TREE, NOTIFY_SETDIRTYFLAG, "Set Attribute Value", "Set Attribute Value" ); + SetAttributeValue( bValue ); +} + + +//----------------------------------------------------------------------------- +// Sets the combo box from the attribute +//----------------------------------------------------------------------------- +void CAttributeBoolChoicePanel::SetComboBoxFromAttribute( vgui::ComboBox *pComboBox ) +{ + CDmeEditorBoolChoicesInfo *pInfo = CastElement<CDmeEditorBoolChoicesInfo>( GetEditorInfo() ); + if ( !pInfo ) + return; + + bool bValue = GetAttributeValue<bool>(); + + // Check the dynamic choices next + if ( pInfo->HasChoiceType() ) + { + const char *choices[2]; + if ( ElementPropertiesChoices()->GetBoolChoiceList( pInfo->GetChoiceType(), GetPanelElement(), GetAttributeName(), IsArrayEntry(), choices ) ) + { + pComboBox->SetText( choices[ bValue ] ); + return; + } + } + + pComboBox->SetText( pInfo->GetChoiceString( bValue ) ); +} diff --git a/vgui2/dme_controls/AttributeColorPickerPanel.cpp b/vgui2/dme_controls/AttributeColorPickerPanel.cpp new file mode 100644 index 0000000..11e5e3c --- /dev/null +++ b/vgui2/dme_controls/AttributeColorPickerPanel.cpp @@ -0,0 +1,170 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeColorPickerPanel.h" +#include "datamodel/dmelement.h" +#include "vgui_controls/Button.h" +#include "dme_controls/AttributeTextEntry.h" +#include "matsys_controls/colorpickerpanel.h" +#include "tier1/KeyValues.h" +#include "dme_controls/inotifyui.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeColorPickerPanel::CAttributeColorPickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ + m_pOpen = new vgui::Button( this, "Open", "", this, "open" ); +} + +void CAttributeColorPickerPanel::OnCommand( char const *cmd ) +{ + if ( !Q_stricmp( cmd, "open" ) ) + { + m_InitialColor = GetAttributeValue<Color>(); + CColorPickerFrame *pColorPickerDialog = new CColorPickerFrame( this, "Select Color" ); + pColorPickerDialog->AddActionSignalTarget( this ); + pColorPickerDialog->DoModal( m_InitialColor ); + } + else + { + BaseClass::OnCommand( cmd ); + } +} + + +//----------------------------------------------------------------------------- +// Update button color +//----------------------------------------------------------------------------- +void CAttributeColorPickerPanel::UpdateButtonColor() +{ + Color clr = GetAttributeValue<Color>(); + m_pOpen->SetDefaultColor( clr, clr ); + m_pOpen->SetArmedColor( clr, clr ); + m_pOpen->SetDepressedColor( clr, clr ); +} + + +//----------------------------------------------------------------------------- +// Used to set the current color on the picker button +//----------------------------------------------------------------------------- +void CAttributeColorPickerPanel::Refresh() +{ + BaseClass::Refresh(); + UpdateButtonColor(); +} + +void CAttributeColorPickerPanel::ApplySchemeSettings(IScheme *pScheme) +{ + // Need to override the scheme settings for this button + BaseClass::ApplySchemeSettings( pScheme ); + UpdateButtonColor(); +} + + +//----------------------------------------------------------------------------- +// Called when the picker gets a new color but apply hasn't happened yet +//----------------------------------------------------------------------------- +void CAttributeColorPickerPanel::OnPreview( KeyValues *data ) +{ + { + CDisableUndoScopeGuard guard; + Color c = data->GetColor( "color" ); + SetAttributeValue( c ); + } + + Refresh( ); + if ( IsAutoApply() ) + { + // NOTE: Don't call Apply since that generates an undo record + CElementTreeNotifyScopeGuard guard( "CAttributeColorPickerPanel::OnPreview", NOTIFY_CHANGE_ATTRIBUTE_VALUE, GetNotify() ); + } + else + { + SetDirty( true ); + } +} + + +//----------------------------------------------------------------------------- +// Called when cancel is hit in the picker +//----------------------------------------------------------------------------- +void CAttributeColorPickerPanel::OnCancelled( ) +{ + // Restore the initial color + CDisableUndoScopeGuard guard; + SetAttributeValue( m_InitialColor ); + Refresh( ); + CElementTreeNotifyScopeGuard notify( "CAttributeColorPickerPanel::OnCancelled", NOTIFY_CHANGE_ATTRIBUTE_VALUE, GetNotify() ); + SetDirty( false ); +} + + +//----------------------------------------------------------------------------- +// Called when the picker gets a new color +//----------------------------------------------------------------------------- +void CAttributeColorPickerPanel::OnPicked( KeyValues *data ) +{ + Color c = data->GetColor( "color" ); + if ( c == m_InitialColor ) + { + SetDirty( false ); + return; + } + + // Kind of an evil hack, but "undo" copies the "old value" off for doing undo, and that value is the new value because + // we haven't been tracking undo while manipulating this. So we'll turn off undo and set the value to the original value. + + // In effect, all of the wheeling will drop out and it'll look just like we started at the original value and ended up at the + // final value... + { + CDisableUndoScopeGuard guard; + SetAttributeValue( m_InitialColor ); + } + + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, GetNotify(), "Set Attribute Value", "Set Attribute Value" ); + SetAttributeValue( c ); + } + + if ( IsAutoApply() ) + { + Refresh(); + SetDirty( false ); + } +} + + +//----------------------------------------------------------------------------- +// Lays out the button position +//----------------------------------------------------------------------------- +void CAttributeColorPickerPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, w, h; + m_pType->GetBounds( x, y, w, h ); + + int inset = 25; + m_pType->SetWide( w - inset ); + + x += w; + x -= inset; + + h -= 2; + + m_pOpen->SetBounds( x, y, inset, h ); +} diff --git a/vgui2/dme_controls/AttributeElementPanel.cpp b/vgui2/dme_controls/AttributeElementPanel.cpp new file mode 100644 index 0000000..858121a --- /dev/null +++ b/vgui2/dme_controls/AttributeElementPanel.cpp @@ -0,0 +1,104 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeElementPanel.h" +#include "dme_controls/AttributeTextEntry.h" +#include "dme_controls/AttributeWidgetFactory.h" +#include "tier1/KeyValues.h" +#include "datamodel/dmelement.h" +#include "movieobjects/dmeeditortypedictionary.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +// ---------------------------------------------------------------------------- +CAttributeElementPanel::CAttributeElementPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ), m_pData( 0 ) +{ + m_pData = new CAttributeTextEntry( this, "AttributeValue" ); + m_pData->SetEnabled( !HasFlag( READONLY ) ); + m_pData->AddActionSignalTarget(this); + m_pType->SetText( "element" ); + + m_bShowMemoryUsage = info.m_bShowMemoryUsage; +} + +void CAttributeElementPanel::Apply() +{ + // FIXME: Implement when needed + Assert( 0 ); +} + +vgui::Panel *CAttributeElementPanel::GetDataPanel() +{ + return static_cast< vgui::Panel * >( m_pData ); +} + +void CAttributeElementPanel::OnCreateDragData( KeyValues *msg ) +{ + if ( GetPanelElement() ) + { + char txt[ 256 ]; + m_pData->GetText( txt, sizeof( txt ) ); + + msg->SetString( "text", txt ); + CDmElement *element = NULL; + if ( GetPanelElement()->HasAttribute( GetAttributeName() ) ) + { + element = GetElement<CDmElement>( GetAttributeValue<DmElementHandle_t>( ) ); + msg->SetInt( "dmeelement", element->GetHandle() ); + } + + } +} + +void CAttributeElementPanel::Refresh() +{ + char elemText[ 512 ]; + elemText[0] = 0; + + CDmElement *element = NULL; + if ( !GetEditorInfo() || !GetEditorInfo()->GetValue<bool>( "hideText" ) ) + { + if ( HasAttribute( ) ) + { + element = GetAttributeValueElement( ); + } + else + { + element = GetPanelElement(); + } + } + + if ( element ) + { + char idstr[ 37 ]; + UniqueIdToString( element->GetId(), idstr, sizeof( idstr ) ); + if ( m_bShowMemoryUsage ) + { + Q_snprintf( elemText, sizeof( elemText ), "%s %s %.3fMB", element->GetTypeString(), idstr, element->EstimateMemoryUsage() / float( 1 << 20 ) ); + } + else + { + Q_snprintf( elemText, sizeof( elemText ), "%s %s", element->GetTypeString(), idstr ); + } + } + + m_pData->SetText( elemText ); + m_pData->SetEnabled(false); +} + +void CAttributeElementPanel::PostConstructor() +{ + Refresh(); +} + +// ---------------------------------------------------------------------------- diff --git a/vgui2/dme_controls/AttributeElementPickerPanel.cpp b/vgui2/dme_controls/AttributeElementPickerPanel.cpp new file mode 100644 index 0000000..44725d9 --- /dev/null +++ b/vgui2/dme_controls/AttributeElementPickerPanel.cpp @@ -0,0 +1,174 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeElementPickerPanel.h" +#include "dme_controls/AttributeTextEntry.h" +#include "dme_controls/AttributeWidgetFactory.h" +#include "datamodel/dmelement.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/ComboBox.h" +#include "dme_controls/dmepicker.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "dme_controls/inotifyui.h" +#include "dme_controls/dmecontrols.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; + +// ---------------------------------------------------------------------------- +CAttributeElementPickerPanel::CAttributeElementPickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : +BaseClass( parent, info ) +{ + m_hEdit = new vgui::Button( this, "Open", "...", this, "open" ); + + m_pData = new CAttributeTextEntry( this, "AttributeValue" ); + m_pData->SetEnabled( !HasFlag( READONLY ) ); + m_pData->AddActionSignalTarget(this); + m_pType->SetText( "element" ); + + m_bShowMemoryUsage = info.m_bShowMemoryUsage; +} + +void CAttributeElementPickerPanel::PostConstructor() +{ + Refresh(); +} + +// ---------------------------------------------------------------------------- +vgui::Panel *CAttributeElementPickerPanel::GetDataPanel() +{ + return static_cast< vgui::Panel * >( m_pData ); +} + +void CAttributeElementPickerPanel::Apply() +{ + // FIXME: Implement when needed + Assert( 0 ); +} + +void CAttributeElementPickerPanel::Refresh() +{ + char elemText[ 512 ]; + elemText[0] = 0; + + CDmElement *element = NULL; + if ( !GetEditorInfo() || !GetEditorInfo()->GetValue<bool>( "hideText" ) ) + { + if ( HasAttribute( ) ) + { + element = GetAttributeValueElement( ); + } + else + { + element = GetPanelElement(); + } + } + + if ( element ) + { + char idstr[ 37 ]; + UniqueIdToString( element->GetId(), idstr, sizeof( idstr ) ); + if ( m_bShowMemoryUsage ) + { + Q_snprintf( elemText, sizeof( elemText ), "%s %s %.3fMB", element->GetTypeString(), idstr, element->EstimateMemoryUsage() / float( 1 << 20 ) ); + } + else + { + Q_snprintf( elemText, sizeof( elemText ), "%s %s", element->GetTypeString(), idstr ); + } + } + + m_pData->SetText( elemText ); + m_pData->SetEditable( false ); +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the Dme picker +//----------------------------------------------------------------------------- +void CAttributeElementPickerPanel::ShowPickerDialog() +{ + CDmeEditorChoicesInfo *pInfo = CastElement<CDmeEditorChoicesInfo>( GetEditorInfo() ); + if ( !pInfo ) + return; + + // FIXME: Sucky. Should we make GetElementChoiceList return a DmeHandleVec_t? + ElementChoiceList_t choices; + CUtlVector< DmePickerInfo_t > vec; + if ( ElementPropertiesChoices()->GetElementChoiceList( pInfo->GetChoiceType(), GetPanelElement(), GetAttributeName(), IsArrayEntry(), choices ) ) + { + int c = choices.Count(); + vec.EnsureCapacity( c ); + for ( int i = 0; i < c; ++i ) + { + int j = vec.AddToTail( ); + vec[j].m_hElement = choices[i].m_pValue->GetHandle(); + vec[j].m_pChoiceString = choices[i].m_pChoiceString; + } + } + + CDmePickerFrame *pDmePickerDialog = new CDmePickerFrame( this, "Select DME Element" ); + pDmePickerDialog->AddActionSignalTarget( this ); + pDmePickerDialog->DoModal( vec ); +} + + +//----------------------------------------------------------------------------- +// Called by the dme picker dialog if a dme was selected +//----------------------------------------------------------------------------- +void CAttributeElementPickerPanel::OnDmeSelected( KeyValues *pKeyValues ) +{ + // We're either going to get an activity or sequence name + CDmElement *pElement = GetElementKeyValue< CDmElement >( pKeyValues, "dme" ); + SetAttributeValueElement( pElement ); + Refresh( ); +} + + +//----------------------------------------------------------------------------- +// Handle commands +//----------------------------------------------------------------------------- +void CAttributeElementPickerPanel::OnCommand( char const *cmd ) +{ + if ( !Q_stricmp( cmd, "open" ) ) + { + ShowPickerDialog(); + } + else + { + BaseClass::OnCommand( cmd ); + } +} + + +//----------------------------------------------------------------------------- +// Lay out the panel +//----------------------------------------------------------------------------- +void CAttributeElementPickerPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, w, h; + m_pType->GetBounds( x, y, w, h ); + + int inset = 25; + m_pType->SetWide( w - inset ); + + x += w; + x -= inset; + + h -= 2; + + m_hEdit->SetBounds( x, y, inset, h ); +} + diff --git a/vgui2/dme_controls/AttributeFilePickerPanel.cpp b/vgui2/dme_controls/AttributeFilePickerPanel.cpp new file mode 100644 index 0000000..c83237b --- /dev/null +++ b/vgui2/dme_controls/AttributeFilePickerPanel.cpp @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeFilePickerPanel.h" +#include "filesystem.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/FileOpenDialog.h" +#include "dme_controls/AttributeTextEntry.h" +#include "vgui/IInput.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Various file picker types +//----------------------------------------------------------------------------- +IMPLEMENT_ATTRIBUTE_FILE_PICKER( CAttributeTgaFilePickerPanel, "Choose TGA file", "TGA", "tga" ); +IMPLEMENT_ATTRIBUTE_FILE_PICKER( CAttributeDmeFilePickerPanel, "Choose DmE .xml file", "DmE XML", "xml" ); +IMPLEMENT_ATTRIBUTE_FILE_PICKER( CAttributeAviFilePickerPanel, "Choose AVI file", "AVI", "avi" ); +IMPLEMENT_ATTRIBUTE_FILE_PICKER( CAttributeShtFilePickerPanel, "Choose Sheet file", "SHT", "sht" ); +IMPLEMENT_ATTRIBUTE_FILE_PICKER( CAttributeRawFilePickerPanel, "Choose RAW file", "RAW", "raw" ); + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeFilePickerPanel::CAttributeFilePickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + +CAttributeFilePickerPanel::~CAttributeFilePickerPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Shows the picker dialog +//----------------------------------------------------------------------------- +void CAttributeFilePickerPanel::ShowPickerDialog() +{ + FileOpenDialog *pFileOpenDialog = new FileOpenDialog( this, "Choose file", true ); + SetupFileOpenDialog( pFileOpenDialog ); + pFileOpenDialog->AddActionSignalTarget( this ); + pFileOpenDialog->SetDeleteSelfOnClose( true ); + pFileOpenDialog->DoModal( true ); + input()->SetAppModalSurface( pFileOpenDialog->GetVPanel() ); +} + +void CAttributeFilePickerPanel::OnFileSelected( char const *fullpath ) +{ + if ( !fullpath || !fullpath[ 0 ] ) + return; + + char relativepath[ 512 ]; + g_pFullFileSystem->FullPathToRelativePath( fullpath, relativepath, sizeof( relativepath ) ); + + // Apply to text panel + m_pData->SetText( relativepath ); + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +}
\ No newline at end of file diff --git a/vgui2/dme_controls/AttributeIntChoicePanel.cpp b/vgui2/dme_controls/AttributeIntChoicePanel.cpp new file mode 100644 index 0000000..d106e01 --- /dev/null +++ b/vgui2/dme_controls/AttributeIntChoicePanel.cpp @@ -0,0 +1,170 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeIntChoicePanel.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ComboBox.h" +#include "datamodel/dmelement.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "datamodel/dmelementfactoryhelper.h" +#include "dme_controls/inotifyui.h" +#include "dme_controls/dmecontrols.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Expose DmeEditorAttributeInfo to the scene database +//----------------------------------------------------------------------------- +IMPLEMENT_ELEMENT_FACTORY( DmeEditorIntChoicesInfo, CDmeEditorIntChoicesInfo ); + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +void CDmeEditorIntChoicesInfo::OnConstruction() +{ +} + +void CDmeEditorIntChoicesInfo::OnDestruction() +{ +} + + +//----------------------------------------------------------------------------- +// Add a choice +//----------------------------------------------------------------------------- +void CDmeEditorIntChoicesInfo::AddChoice( int nValue, const char *pChoiceString ) +{ + CDmElement *pChoice = CreateChoice( pChoiceString ); + pChoice->SetValue( "value", nValue ); +} + + +//----------------------------------------------------------------------------- +// Gets the choices +//----------------------------------------------------------------------------- +int CDmeEditorIntChoicesInfo::GetChoiceValue( int nIndex ) const +{ + Assert( ( nIndex < GetChoiceCount() ) && ( nIndex >= 0 ) ); + CDmElement *pChoice = m_Choices[nIndex]; + if ( !pChoice ) + return 0; + + return pChoice->GetValue<int>( "value" ); +} + + +//----------------------------------------------------------------------------- +// +// Constructor +// +//----------------------------------------------------------------------------- +CAttributeIntChoicePanel::CAttributeIntChoicePanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + + +//----------------------------------------------------------------------------- +// Derived classes can re-implement this to fill the combo box however they like +//----------------------------------------------------------------------------- +void CAttributeIntChoicePanel::PopulateComboBox( vgui::ComboBox *pComboBox ) +{ + pComboBox->DeleteAllItems(); + + CDmeEditorIntChoicesInfo *pInfo = CastElement<CDmeEditorIntChoicesInfo>( GetEditorInfo() ); + if ( !pInfo ) + return; + + // Fill in the choices + int c = pInfo->GetChoiceCount(); + for ( int i = 0; i < c; ++i ) + { + KeyValues *kv = new KeyValues( "entry" ); + kv->SetInt( "value", pInfo->GetChoiceValue( i ) ); + pComboBox->AddItem( pInfo->GetChoiceString( i ) , kv ); + } + + // Add the dynamic choices next + if ( pInfo->HasChoiceType() ) + { + IntChoiceList_t choices; + if ( ElementPropertiesChoices()->GetIntChoiceList( pInfo->GetChoiceType(), GetPanelElement(), GetAttributeName(), IsArrayEntry(), choices ) ) + { + c = choices.Count(); + for ( int i = 0; i < c; ++i ) + { + KeyValues *kv = new KeyValues( "entry" ); + kv->SetInt( "value", choices[i].m_nValue ); + pComboBox->AddItem( choices[i].m_pChoiceString, kv ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Sets the attribute based on the combo box +//----------------------------------------------------------------------------- +void CAttributeIntChoicePanel::SetAttributeFromComboBox( vgui::ComboBox *pComboBox, KeyValues *pKeyValues ) +{ + int nOldValue = GetAttributeValue<int>(); + int nValue = pKeyValues->GetInt( "value", 0 ); + if ( nOldValue == nValue ) + return; + + CUndoScopeGuard guard( NOTIFY_SOURCE_PROPERTIES_TREE, NOTIFY_SETDIRTYFLAG, "Set Attribute Value", "Set Attribute Value" ); + SetAttributeValue( nValue ); +} + + +//----------------------------------------------------------------------------- +// Sets the combo box from the attribute +//----------------------------------------------------------------------------- +void CAttributeIntChoicePanel::SetComboBoxFromAttribute( vgui::ComboBox *pComboBox ) +{ + CDmeEditorIntChoicesInfo *pInfo = CastElement<CDmeEditorIntChoicesInfo>( GetEditorInfo() ); + if ( !pInfo ) + return; + + int nValue = GetAttributeValue<int>(); + int c = pInfo->GetChoiceCount(); + for ( int i = 0; i < c; ++i ) + { + if ( nValue == pInfo->GetChoiceValue( i ) ) + { + pComboBox->SetText( pInfo->GetChoiceString( i ) ); + return; + } + } + + // Check the dynamic choices next + if ( pInfo->HasChoiceType() ) + { + IntChoiceList_t choices; + if ( ElementPropertiesChoices()->GetIntChoiceList( pInfo->GetChoiceType(), GetPanelElement(), GetAttributeName(), IsArrayEntry(), choices ) ) + { + c = choices.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( nValue == choices[i].m_nValue ) + { + pComboBox->SetText( choices[i].m_pChoiceString ); + return; + } + } + } + } + + pComboBox->SetText( "Unknown value" ); +} diff --git a/vgui2/dme_controls/AttributeInterpolatorChoicePanel.cpp b/vgui2/dme_controls/AttributeInterpolatorChoicePanel.cpp new file mode 100644 index 0000000..71e455f --- /dev/null +++ b/vgui2/dme_controls/AttributeInterpolatorChoicePanel.cpp @@ -0,0 +1,91 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeInterpolatorChoicePanel.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ComboBox.h" +#include "datamodel/dmelement.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "datamodel/dmelementfactoryhelper.h" +#include "dme_controls/inotifyui.h" +#include "interpolatortypes.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +//----------------------------------------------------------------------------- +// +// Constructor +// +//----------------------------------------------------------------------------- +CAttributeInterpolatorChoicePanel::CAttributeInterpolatorChoicePanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + + +//----------------------------------------------------------------------------- +// Derived classes can re-implement this to fill the combo box however they like +//----------------------------------------------------------------------------- +void CAttributeInterpolatorChoicePanel::PopulateComboBoxes( vgui::ComboBox *pComboBox[ 2 ] ) +{ + pComboBox[ 0 ]->DeleteAllItems(); + pComboBox[ 1 ]->DeleteAllItems(); + + // Fill in the choices + int c = NUM_INTERPOLATE_TYPES; + for ( int i = 0; i < c; ++i ) + { + KeyValues *kv = new KeyValues( "entry" ); + kv->SetInt( "value", i ); + pComboBox[ 0 ]->AddItem( Interpolator_NameForInterpolator( i, true ) , kv ); + + kv = new KeyValues( "entry" ); + kv->SetInt( "value", i ); + pComboBox[ 1 ]->AddItem( Interpolator_NameForInterpolator( i, true ) , kv ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the attribute based on the combo box +//----------------------------------------------------------------------------- +void CAttributeInterpolatorChoicePanel::SetAttributeFromComboBoxes( vgui::ComboBox *pComboBox[ 2 ], KeyValues *pKeyValues[ 2 ] ) +{ + int nOldValue = GetAttributeValue<int>(); + int nValueLeft = pKeyValues[ 0 ]->GetInt( "value", 0 ); + int nValueRight= pKeyValues[ 1 ]->GetInt( "value" , 0 ); + + int nValue = MAKE_CURVE_TYPE( nValueLeft, nValueRight ); + + // No change + if ( nOldValue == nValue ) + return; + + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, GetNotify(), "Set Attribute Value", "Set Attribute Value" ); + SetAttributeValue( nValue ); +} + + +//----------------------------------------------------------------------------- +// Sets the combo box from the attribute +//----------------------------------------------------------------------------- +void CAttributeInterpolatorChoicePanel::SetComboBoxesFromAttribute( vgui::ComboBox *pComboBox[ 2 ] ) +{ + int nValue = GetAttributeValue<int>(); + + // Decompose + int leftPart = GET_LEFT_CURVE( nValue ); + int rightPart = GET_RIGHT_CURVE( nValue ); + + pComboBox[ 0 ]->SetText( Interpolator_NameForInterpolator( leftPart, true ) ); + pComboBox[ 1 ]->SetText( Interpolator_NameForInterpolator( rightPart, true ) ); +} diff --git a/vgui2/dme_controls/AttributeMDLPickerPanel.cpp b/vgui2/dme_controls/AttributeMDLPickerPanel.cpp new file mode 100644 index 0000000..82860eb --- /dev/null +++ b/vgui2/dme_controls/AttributeMDLPickerPanel.cpp @@ -0,0 +1,62 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "dme_controls/AttributeMDLPickerPanel.h" +#include "filesystem.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/FileOpenDialog.h" +#include "dme_controls/AttributeTextEntry.h" +#include "matsys_controls/MDLPicker.h" +#include "tier1/KeyValues.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeMDLPickerPanel::CAttributeMDLPickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + +CAttributeMDLPickerPanel::~CAttributeMDLPickerPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the MDL picker +//----------------------------------------------------------------------------- +void CAttributeMDLPickerPanel::ShowPickerDialog() +{ + // Open file + CMDLPickerFrame *pMDLPickerDialog = new CMDLPickerFrame( this, "Select .MDL File" ); + pMDLPickerDialog->AddActionSignalTarget( this ); + pMDLPickerDialog->DoModal( ); +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the MDL picker +//----------------------------------------------------------------------------- +void CAttributeMDLPickerPanel::OnMDLSelected( KeyValues *pKeyValues ) +{ + const char *pMDLName = pKeyValues->GetString( "mdl", NULL ); + if ( !pMDLName || !pMDLName[ 0 ] ) + return; + + // Apply to text panel + m_pData->SetText( pMDLName ); + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +}
\ No newline at end of file diff --git a/vgui2/dme_controls/AttributeSequencePickerPanel.cpp b/vgui2/dme_controls/AttributeSequencePickerPanel.cpp new file mode 100644 index 0000000..bbb3012 --- /dev/null +++ b/vgui2/dme_controls/AttributeSequencePickerPanel.cpp @@ -0,0 +1,109 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "dme_controls/AttributeSequencePickerPanel.h" +#include "filesystem.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/FileOpenDialog.h" +#include "dme_controls/AttributeTextEntry.h" +#include "matsys_controls/MDLPicker.h" +#include "matsys_controls/sequencepicker.h" +#include "tier1/KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeSequencePickerPanel::CAttributeSequencePickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + +CAttributeSequencePickerPanel::~CAttributeSequencePickerPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the MDL picker +//----------------------------------------------------------------------------- +void CAttributeSequencePickerPanel::ShowPickerDialog() +{ + CMDLPickerFrame *pMDLPickerDialog = new CMDLPickerFrame( this, "Select .MDL File" ); + pMDLPickerDialog->AddActionSignalTarget( this ); + pMDLPickerDialog->DoModal( ); +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the MDL picker +//----------------------------------------------------------------------------- +void CAttributeSequencePickerPanel::OnMDLSelected( KeyValues *pKeyValues ) +{ + const char *pMDLName = pKeyValues->GetString( "asset", NULL ); + + char pRelativePath[MAX_PATH]; + Q_snprintf( pRelativePath, sizeof(pRelativePath), "models\\%s", pMDLName ); + ShowSequencePickerDialog( pRelativePath ); +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the sequence picker +//----------------------------------------------------------------------------- +void CAttributeSequencePickerPanel::ShowSequencePickerDialog( const char *pMDLName ) +{ + if ( !pMDLName || !pMDLName[ 0 ] ) + return; + + // Open file + CSequencePicker::PickType_t pickType = CSequencePicker::PICK_ALL; + const char *pTextType = GetTextType(); + if ( pTextType ) + { + if ( !Q_stricmp( pTextType, "activityName" ) ) + { + pickType = CSequencePicker::PICK_ACTIVITIES; + } + else if ( !Q_stricmp( pTextType, "sequenceName" ) ) + { + pickType = CSequencePicker::PICK_SEQUENCES; + } + } + + CSequencePickerFrame *pSequencePickerDialog = new CSequencePickerFrame( this, pickType ); + pSequencePickerDialog->AddActionSignalTarget( this ); + pSequencePickerDialog->DoModal( pMDLName ); +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the MDL picker +//----------------------------------------------------------------------------- +void CAttributeSequencePickerPanel::OnSequenceSelected( KeyValues *pKeyValues ) +{ + // We're either going to get an activity or sequence name + const char *pActivityName = pKeyValues->GetString( "activity", NULL ); + const char *pSequenceName = pKeyValues->GetString( "sequence", pActivityName ); + if ( !pSequenceName || !pSequenceName[ 0 ] ) + return; + + // Apply to text panel + m_pData->SetText( pSequenceName ); + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +} diff --git a/vgui2/dme_controls/AttributeSoundPickerPanel.cpp b/vgui2/dme_controls/AttributeSoundPickerPanel.cpp new file mode 100644 index 0000000..c167c24 --- /dev/null +++ b/vgui2/dme_controls/AttributeSoundPickerPanel.cpp @@ -0,0 +1,85 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeSoundPickerPanel.h" +#include "dme_controls/soundpicker.h" +#include "tier1/KeyValues.h" +#include "dme_controls/AttributeTextEntry.h" +#include "datamodel/dmelement.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeSoundPickerPanel::CAttributeSoundPickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + +CAttributeSoundPickerPanel::~CAttributeSoundPickerPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the sound picker +//----------------------------------------------------------------------------- +void CAttributeSoundPickerPanel::ShowPickerDialog() +{ + // Open file + CSoundPicker::PickType_t pickType = CSoundPicker::PICK_ALL; + const char *pTextType = GetTextType(); + if ( pTextType ) + { + if ( !Q_stricmp( pTextType, "gamesoundName" ) ) + { + pickType = CSoundPicker::PICK_GAMESOUNDS; + } + else if ( !Q_stricmp( pTextType, "wavName" ) ) + { + pickType = CSoundPicker::PICK_WAVFILES; + } + } + + const char *pCurrentSound = GetAttributeValue<CUtlString>().Get(); + CSoundPickerFrame *pSoundPickerDialog = new CSoundPickerFrame( this, "Select sound", pickType ); + pSoundPickerDialog->AddActionSignalTarget( this ); + + if ( pickType == CSoundPicker::PICK_ALL ) + { + pickType = CSoundPicker::PICK_NONE; + } + pSoundPickerDialog->DoModal( pickType, pCurrentSound ); +} + + +//----------------------------------------------------------------------------- +// Called when the sound picker has picked a sound +//----------------------------------------------------------------------------- +void CAttributeSoundPickerPanel::OnSoundSelected( KeyValues *pKeyValues ) +{ + // We're either going to get an activity or sequence name + const char *pGameSoundName = pKeyValues->GetString( "gamesound", NULL ); + const char *pSoundName = pKeyValues->GetString( "wav", pGameSoundName ); + if ( !pSoundName || !pSoundName[ 0 ] ) + return; + + // Apply to text panel + m_pData->SetText( pSoundName ); + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +} diff --git a/vgui2/dme_controls/AttributeStringChoicePanel.cpp b/vgui2/dme_controls/AttributeStringChoicePanel.cpp new file mode 100644 index 0000000..d60229f --- /dev/null +++ b/vgui2/dme_controls/AttributeStringChoicePanel.cpp @@ -0,0 +1,169 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeStringChoicePanel.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ComboBox.h" +#include "datamodel/dmelement.h" +#include "datamodel/dmelementfactoryhelper.h" +#include "dme_controls/inotifyui.h" +#include "dme_controls/dmecontrols.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Expose DmeEditorAttributeInfo to the scene database +//----------------------------------------------------------------------------- +IMPLEMENT_ELEMENT_FACTORY( DmeEditorStringChoicesInfo, CDmeEditorStringChoicesInfo ); + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +void CDmeEditorStringChoicesInfo::OnConstruction() +{ +} + +void CDmeEditorStringChoicesInfo::OnDestruction() +{ +} + + +//----------------------------------------------------------------------------- +// Add a choice +//----------------------------------------------------------------------------- +CDmElement *CDmeEditorStringChoicesInfo::AddChoice( const char *pValueString, const char *pChoiceString ) +{ + CDmElement *pChoice = CreateChoice( pChoiceString ); + pChoice->SetValue<CUtlString>( "value", pValueString ); + return pChoice; +} + + +//----------------------------------------------------------------------------- +// Gets the choices +//----------------------------------------------------------------------------- +const char *CDmeEditorStringChoicesInfo::GetChoiceValue( int nIndex ) const +{ + Assert( ( nIndex < GetChoiceCount() ) && ( nIndex >= 0 ) ); + CDmElement *pChoice = m_Choices[nIndex]; + if ( !pChoice ) + return 0; + + return pChoice->GetValue<CUtlString>( "value" ); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeStringChoicePanel::CAttributeStringChoicePanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + + +//----------------------------------------------------------------------------- +// Derived classes can re-implement this to fill the combo box however they like +//----------------------------------------------------------------------------- +void CAttributeStringChoicePanel::PopulateComboBox( vgui::ComboBox *pComboBox ) +{ + pComboBox->DeleteAllItems(); + + CDmeEditorStringChoicesInfo *pInfo = CastElement<CDmeEditorStringChoicesInfo>( GetEditorInfo() ); + if ( !pInfo ) + return; + + // Fill in the standard choices first + int c = pInfo->GetChoiceCount(); + for ( int i = 0; i < c; ++i ) + { + KeyValues *kv = new KeyValues( "entry" ); + kv->SetString( "value", pInfo->GetChoiceValue( i ) ); + pComboBox->AddItem( pInfo->GetChoiceString( i ) , kv ); + } + + // Add the dynamic choices next + if ( pInfo->HasChoiceType() ) + { + StringChoiceList_t choices; + if ( ElementPropertiesChoices()->GetStringChoiceList( pInfo->GetChoiceType(), GetPanelElement(), GetAttributeName(), IsArrayEntry(), choices ) ) + { + c = choices.Count(); + for ( int i = 0; i < c; ++i ) + { + KeyValues *kv = new KeyValues( "entry" ); + kv->SetString( "value", choices[i].m_pValue ); + pComboBox->AddItem( choices[i].m_pChoiceString, kv ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Sets the attribute based on the combo box +//----------------------------------------------------------------------------- +void CAttributeStringChoicePanel::SetAttributeFromComboBox( vgui::ComboBox *pComboBox, KeyValues *pKeyValues ) +{ + const char *pOldString = GetAttributeValue<CUtlString>(); + const char *pNewString = pKeyValues->GetString( "value", "" ); + if ( pOldString == pNewString ) + return; + + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, GetNotify(), "Set Attribute Value", "Set Attribute Value" ); + SetAttributeValue( pNewString ); +} + + +//----------------------------------------------------------------------------- +// Sets the combo box from the attribute +//----------------------------------------------------------------------------- +void CAttributeStringChoicePanel::SetComboBoxFromAttribute( vgui::ComboBox *pComboBox ) +{ + CDmeEditorStringChoicesInfo *pInfo = CastElement<CDmeEditorStringChoicesInfo>( GetEditorInfo() ); + if ( !pInfo ) + return; + + const char *pValue = GetAttributeValue<CUtlString>(); + int c = pInfo->GetChoiceCount(); + for ( int i = 0; i < c; ++i ) + { + if ( !Q_stricmp( pValue, pInfo->GetChoiceValue( i ) ) ) + { + pComboBox->SetText( pInfo->GetChoiceString( i ) ); + return; + } + } + + // Check the dynamic choices next + if ( pInfo->HasChoiceType() ) + { + StringChoiceList_t choices; + if ( ElementPropertiesChoices()->GetStringChoiceList( pInfo->GetChoiceType(), GetPanelElement(), GetAttributeName(), IsArrayEntry(), choices ) ) + { + c = choices.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( !Q_stricmp( pValue, choices[i].m_pValue ) ) + { + pComboBox->SetText( choices[i].m_pChoiceString ); + return; + } + } + } + } + + pComboBox->SetText( "Unknown value" ); +} diff --git a/vgui2/dme_controls/AttributeTextEntry.cpp b/vgui2/dme_controls/AttributeTextEntry.cpp new file mode 100644 index 0000000..ade6ff3 --- /dev/null +++ b/vgui2/dme_controls/AttributeTextEntry.cpp @@ -0,0 +1,500 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeTextEntry.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/Menu.h" +#include "datamodel/dmelement.h" +#include "dme_controls/AttributeTextPanel.h" +#include "vgui/MouseCode.h" +#include "vgui/KeyCode.h" +#include "vgui/IInput.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "dme_controls/inotifyui.h" + +using namespace vgui; + +// ---------------------------------------------------------------------------- +// CAttributeTextEntry + +CAttributeTextEntry::CAttributeTextEntry( Panel *parent, const char *panelName ) : + BaseClass( parent, panelName ), + m_bValueStored( false ), + m_flOriginalValue( 0.0f ) +{ + SetDragEnabled( true ); + SetDropEnabled( true, 0.5f ); + m_szOriginalText[ 0 ] = 0; + AddActionSignalTarget( this ); +} + +void CAttributeTextEntry::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetBorder(NULL); + + //HFont font = pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ); + //SetFont(font); +} + + +//----------------------------------------------------------------------------- +// Returns the parent panel +//----------------------------------------------------------------------------- +inline CAttributeTextPanel *CAttributeTextEntry::GetParentAttributePanel() +{ + return static_cast< CAttributeTextPanel * >( GetParent() ); +} + + +//----------------------------------------------------------------------------- +// Drag + drop +//----------------------------------------------------------------------------- +bool CAttributeTextEntry::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + menu->AddMenuItem( "Drop as Text", "#BxDropText", "droptext", this ); + return true; +} + +bool CAttributeTextEntry::IsDroppable( CUtlVector< KeyValues * >& msglist ) +{ + if ( !IsEnabled() ) + return false; + + if ( msglist.Count() != 1 ) + return false; + + KeyValues *msg = msglist[ 0 ]; + + Panel *draggedPanel = ( Panel * )msg->GetPtr( "panel", NULL ); + if ( draggedPanel == GetParent() ) + return false; + + CAttributeTextPanel *pPanel = GetParentAttributePanel(); + if ( !pPanel ) + return false; + + // If a specific text type is specified, then filter if it doesn't match + const char *pTextType = pPanel->GetTextType(); + if ( pTextType[0] ) + { + const char *pMsgTextType = msg->GetString( "texttype" ); + if ( Q_stricmp( pTextType, pMsgTextType ) ) + return false; + } + + DmAttributeType_t t = pPanel->GetAttributeType(); + switch ( t ) + { + default: + break; + case AT_ELEMENT: + { + CDmElement *ptr = reinterpret_cast< CDmElement * >( g_pDataModel->GetElement( DmElementHandle_t( msg->GetInt( "root" ) ) ) ); + if ( ptr ) + { + return true; + } + return false; + } + break; + + case AT_ELEMENT_ARRAY: + return false; + } + + return true; +} + +void CAttributeTextEntry::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) +{ + if ( msglist.Count() != 1 ) + return; + + KeyValues *data = msglist[ 0 ]; + Panel *draggedPanel = ( Panel * )data->GetPtr( "panel", NULL ); + if ( draggedPanel == GetParent() ) + return; + + CAttributeTextPanel *pPanel = GetParentAttributePanel(); + if ( !pPanel ) + return; + + // If a specific text type is specified, then filter if it doesn't match + const char *pTextType = pPanel->GetTextType(); + if ( pTextType[0] ) + { + const char *pMsgTextType = data->GetString( "texttype" ); + if ( Q_stricmp( pTextType, pMsgTextType ) ) + return; + } + + const char *cmd = data->GetString( "command" ); + if ( !Q_stricmp( cmd, "droptext" ) || !Q_stricmp( cmd, "default" ) ) + { + DmAttributeType_t t = pPanel->GetAttributeType(); + switch ( t ) + { + default: + { + pPanel->SetDirty( true ); + SetText( data->GetString( "text" ) ); + if ( pPanel->IsAutoApply() ) + { + pPanel->Apply(); + } + } + break; + + case AT_ELEMENT: + { + CDmElement *ptr = reinterpret_cast< CDmElement * >( g_pDataModel->GetElement( DmElementHandle_t( data->GetInt( "root" ) ) ) ); + if ( !ptr ) + { + break; + } + pPanel->SetDirty( true ); + SetText( data->GetString( "text" ) ); + if ( pPanel->IsAutoApply() ) + { + pPanel->Apply(); + } + } + break; + case AT_ELEMENT_ARRAY: + Assert( 0 ); + break; + } + } + + StoreInitialValue( true ); +} + + +//----------------------------------------------------------------------------- +// Enter causes changes to be applied +//----------------------------------------------------------------------------- +void CAttributeTextEntry::OnKeyCodeTyped(KeyCode code) +{ + bool bCtrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + + switch ( code ) + { + case KEY_ENTER: + { + CAttributeTextPanel *pPanel = GetParentAttributePanel(); + if ( !pPanel->IsAutoApply() ) + { + pPanel->Apply(); + StoreInitialValue( true ); + } + else + { + WriteValueToAttribute(); + } + } + break; + + // Override the base class undo feature, it behaves poorly when typing in data + case KEY_Z: + if ( bCtrl ) + { + WriteInitialValueToAttribute( ); + break; + } + + // NOTE: Fall through to default if it's not Ctrl-Z + + default: + BaseClass::OnKeyCodeTyped(code); + break; + } +} + + +void CAttributeTextEntry::OnTextChanged( KeyValues *data ) +{ + m_bValueStored = true; +} + + +//----------------------------------------------------------------------------- +// We'll only create an "undo" record if the values differ upon focus change +//----------------------------------------------------------------------------- +void CAttributeTextEntry::StoreInitialValue( bool bForce ) +{ + // Already storing value??? + if ( m_bValueStored && !bForce ) + return; + + m_bValueStored = true; + + CAttributeTextPanel *pPanel = GetParentAttributePanel(); + Assert( pPanel ); + + switch ( pPanel->GetAttributeType() ) + { + case AT_FLOAT: + m_flOriginalValue = pPanel->GetAttributeValue<float>( ); + break; + case AT_INT: + m_nOriginalValue = pPanel->GetAttributeValue<int>( ); + break; + case AT_BOOL: + m_bOriginalValue = pPanel->GetAttributeValue<bool>( ); + break; + default: + GetText( m_szOriginalText, sizeof( m_szOriginalText ) ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Performs undo +//----------------------------------------------------------------------------- +void CAttributeTextEntry::WriteInitialValueToAttribute( ) +{ + // Already storing value??? + if ( !m_bValueStored ) + return; + + CDisableUndoScopeGuard guard; + + CAttributeTextPanel *pPanel = GetParentAttributePanel(); + Assert( pPanel ); + + switch ( pPanel->GetAttributeType() ) + { + case AT_FLOAT: + pPanel->SetAttributeValue( m_flOriginalValue ); + break; + case AT_INT: + pPanel->SetAttributeValue( m_nOriginalValue ); + break; + case AT_BOOL: + pPanel->SetAttributeValue<bool>( m_bOriginalValue ); + break; + default: + pPanel->SetAttributeValueFromString( m_szOriginalText ); + break; + } + + pPanel->SetDirty( false ); + pPanel->Refresh(); +} + + +//----------------------------------------------------------------------------- +// We'll only create an "undo" record if the values differ upon focus change +//----------------------------------------------------------------------------- +void CAttributeTextEntry::OnSetFocus() +{ + BaseClass::OnSetFocus(); + StoreInitialValue(); +} + + +//----------------------------------------------------------------------------- +// Called when focus is lost +//----------------------------------------------------------------------------- +template<class T> +void CAttributeTextEntry::ApplyMouseWheel( T newValue, T originalValue ) +{ + CAttributeTextPanel *pPanel = GetParentAttributePanel(); + + // Kind of an evil hack, but "undo" copies the "old value" off for doing undo, and that value is the new value because + // we haven't been tracking undo while manipulating this. So we'll turn off undo and set the value to the original value. + + // In effect, all of the wheeling will drop out and it'll look just like we started at the original value and ended up at the + // final value... + { + CDisableUndoScopeGuard guard; + pPanel->SetAttributeValue( originalValue ); + } + + if ( pPanel->IsAutoApply() ) + { + pPanel->Apply(); + } + else + { + CElementTreeUndoScopeGuard guard( 0, pPanel->GetNotify(), "Set Attribute Value", "Set Attribute Value" ); + pPanel->SetAttributeValue( newValue ); + } +} + + +void CAttributeTextEntry::WriteValueToAttribute() +{ + if ( !m_bValueStored ) + return; + + m_bValueStored = false; + + char newText[ MAX_TEXT_LENGTH ]; + GetText( newText, sizeof( newText ) ); + + CAttributeTextPanel *pPanel = GetParentAttributePanel(); + Assert( pPanel ); + switch (pPanel->GetAttributeType() ) + { + case AT_FLOAT: + ApplyMouseWheel( (float)atof(newText), m_flOriginalValue ); + break; + case AT_INT: + ApplyMouseWheel( atoi(newText), m_nOriginalValue ); + break; + case AT_BOOL: + ApplyMouseWheel( atoi(newText) != 0, m_bOriginalValue ); + break; + default: + if ( Q_strcmp( newText, m_szOriginalText ) ) + { + pPanel->SetDirty( true ); + if ( pPanel->IsAutoApply() ) + { + pPanel->Apply(); + StoreInitialValue( true ); + } + } + else + { + pPanel->SetDirty( false ); + } + break; + } +} + + +//----------------------------------------------------------------------------- +// Called when focus is lost +//----------------------------------------------------------------------------- +void CAttributeTextEntry::OnKillFocus() +{ + BaseClass::OnKillFocus(); + WriteValueToAttribute(); + StoreInitialValue(); +} + +void CAttributeTextEntry::OnMouseWheeled( int delta ) +{ + // Must have *keyboard* focus for it to work + if ( !HasFocus() ) + { + // Otherwise, let the base class scroll up + down + BaseClass::OnMouseWheeled( delta ); + return; + } + + CAttributeTextPanel *pPanel = GetParentAttributePanel(); + if ( pPanel->GetDirty() ) + { + if ( pPanel->IsAutoApply() ) + { + pPanel->Apply(); + StoreInitialValue( true ); + } + else + { + // FIXME: Make this work for non-auto-apply panels + } + } + + switch ( pPanel->GetAttributeType() ) + { + case AT_FLOAT: + { + float deltaFactor; + if ( input()->IsKeyDown(KEY_LSHIFT) ) + { + deltaFactor = ((float)delta) * 10.0f; + } + else if ( input()->IsKeyDown(KEY_LCONTROL) ) + { + deltaFactor = ((float)delta) / 100.0; + } + else + { + deltaFactor = ((float)delta) / 10.0; + } + + float val = pPanel->GetAttributeValue<float>() + deltaFactor; + + if ( input()->IsKeyDown(KEY_LALT) ) + { + //val = clamp(val, 0.0, 1.0); + val = (val > 1) ? 1 : ((val < 0) ? 0 : val); + } + + { + // Note, these calls to Set won't create Undo Records, + // since we'll check the value in SetFocus/KillFocus so that we + // don't gum up the undo system with hundreds of records... + CDisableUndoScopeGuard guard; + pPanel->SetAttributeValue( val ); + } + } + break; + + case AT_INT: + { + if ( input()->IsKeyDown(KEY_LSHIFT) ) + { + delta *= 10; + } + + int val = pPanel->GetAttributeValue<int>() + delta; + + { + // Note, these calls to Set won't create Undo Records, + // since we'll check the value in SetFocus/KillFocus so that we + // don't gum up the undo system with hundreds of records... + CDisableUndoScopeGuard guard; + pPanel->SetAttributeValue( val ); + } + } + break; + + case AT_BOOL: + { + bool val = !pPanel->GetAttributeValue<bool>(); + + { + // Note, these calls to Set won't create Undo Records, + // since we'll check the value in SetFocus/KillFocus so that we + // don't gum up the undo system with hundreds of records... + CDisableUndoScopeGuard guard; + pPanel->SetAttributeValue( val ); + } + } + break; + + default: + return; + } + + pPanel->Refresh(); + if ( pPanel->IsAutoApply() ) + { + // NOTE: Don't call Apply since that generates an undo record + CElementTreeNotifyScopeGuard notify( "CAttributeTextEntry::OnMouseWheeled", NOTIFY_CHANGE_ATTRIBUTE_VALUE | NOTIFY_SETDIRTYFLAG, pPanel->GetNotify() ); + } + else + { + pPanel->SetDirty( true ); + } + + //SetDirty(true); + //UpdateTime( m_flLastMouseTime ); + //UpdateZoom( -10.0f * delta ); + //UpdateTransform(); +} + +// ---------------------------------------------------------------------------- diff --git a/vgui2/dme_controls/AttributeTextPanel.cpp b/vgui2/dme_controls/AttributeTextPanel.cpp new file mode 100644 index 0000000..c956f06 --- /dev/null +++ b/vgui2/dme_controls/AttributeTextPanel.cpp @@ -0,0 +1,119 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeTextPanel.h" +#include "dme_controls/AttributeTextEntry.h" +#include "dme_controls/AttributeWidgetFactory.h" +#include "tier1/KeyValues.h" +#include "datamodel/dmelement.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "dme_controls/inotifyui.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// CAttributeTextPanel constructor +//----------------------------------------------------------------------------- +CAttributeTextPanel::CAttributeTextPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ), m_pData( 0 ), m_bShowMemoryUsage( info.m_bShowMemoryUsage ) +{ + m_pData = new CAttributeTextEntry( this, "AttributeValue" ); + m_pData->SetEnabled( !HasFlag( READONLY ) ); + + m_pData->AddActionSignalTarget(this); + SetAllowKeyBindingChainToParent( false ); +} + +void CAttributeTextPanel::SetFont( HFont font ) +{ + BaseClass::SetFont( font ); + m_pData->SetFont( font ); +} + +//----------------------------------------------------------------------------- +// Returns the text type +//----------------------------------------------------------------------------- +const char *CAttributeTextPanel::GetTextType() +{ + // If a specific text type is specified, then filter if it doesn't match + CDmeEditorAttributeInfo *pInfo = GetEditorInfo(); + const char *pTextType = pInfo ? pInfo->GetValueString( "texttype" ) : NULL; + return pTextType ? pTextType : ""; +} + + +void CAttributeTextPanel::Apply() +{ + char txt[ 256 ]; + m_pData->GetText( txt, sizeof( txt ) ); + + // Apply means we no longer look blue + SetDirty( false ); + + if ( GetAttributeType( ) == AT_UNKNOWN ) + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, GetNotify(), "Set Attribute Value", "Set Attribute Value" ); + SetAttributeValue( "" ); + return; + } + + char curvalue[ 256 ]; + GetAttributeValueAsString( curvalue, sizeof( curvalue ) ); + + // Only if differnt + if ( Q_strcmp( curvalue, txt ) ) + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, GetNotify(), "Set Attribute Value", "Set Attribute Value" ); + SetAttributeValueFromString( txt ); + } +} + +vgui::Panel *CAttributeTextPanel::GetDataPanel() +{ + return static_cast< vgui::Panel * >( m_pData ); +} + +void CAttributeTextPanel::Refresh() +{ + char buf[ 512 ]; + if ( IsArrayType( GetAttributeType() ) ) + { + int count = GetAttributeArrayCount(); + if ( m_bShowMemoryUsage ) + { + CDmAttribute *pAttr = GetPanelElement()->GetAttribute( GetAttributeName() ); + Q_snprintf( buf, sizeof( buf ), "%d items %.3fMB", count, pAttr->EstimateMemoryUsage( TD_DEEP ) / float( 1 << 20 ) ); + } + else + { + Q_snprintf( buf, sizeof( buf ), "%d items", count ); + } + m_pData->SetText( buf ); + m_pData->SetEnabled(false); + } + else if ( GetAttributeType() == AT_ELEMENT ) + { + m_pData->SetText( "" ); + } + else + { + GetAttributeValueAsString( buf, sizeof( buf ) ); + m_pData->SetText( buf ); + } +} + +void CAttributeTextPanel::PostConstructor() +{ + Refresh(); +} + diff --git a/vgui2/dme_controls/AttributeWidgetFactory.cpp b/vgui2/dme_controls/AttributeWidgetFactory.cpp new file mode 100644 index 0000000..3a9292f --- /dev/null +++ b/vgui2/dme_controls/AttributeWidgetFactory.cpp @@ -0,0 +1,370 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeWidgetFactory.h" +#include "tier1/utldict.h" +#include "tier1/KeyValues.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "dme_controls/AttributeTextEntry.h" +#include "dme_controls/AttributeFilePickerPanel.h" +#include "dme_controls/AttributeBoolChoicePanel.h" +#include "dme_controls/AttributeIntChoicePanel.h" +#include "dme_controls/AttributeStringChoicePanel.h" +#include "dme_controls/AttributeElementPanel.h" +#include "dme_controls/AttributeElementPickerPanel.h" +#include "dme_controls/AttributeMDLPickerPanel.h" +#include "dme_controls/AttributeSequencePickerPanel.h" +#include "dme_controls/AttributeSoundPickerPanel.h" +#include "dme_controls/AttributeAssetPickerPanel.h" +#include "dme_controls/AttributeShaderPickerPanel.h" +#include "dme_controls/AttributeSurfacePropertyPickerPanel.h" +#include "dme_controls/AttributeDetailTypePickerPanel.h" +#include "dme_controls/AttributeColorPickerPanel.h" +#include "dme_controls/AttributeInterpolatorChoicePanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Forward declaration +//----------------------------------------------------------------------------- +class CAttributeWidgetFactoryList; + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// CAttributeWidgetFactoryList class definition +//----------------------------------------------------------------------------- +class CAttributeWidgetFactoryList : public IAttributeWidgetFactoryList +{ +public: + // Inherited from IAttributeWidgetFactoryList + virtual IAttributeWidgetFactory *GetWidgetFactory( const char *pWidgetName ); + virtual IAttributeWidgetFactory *GetWidgetFactory( CDmElement *object, CDmAttribute *pAttribute, CDmeEditorTypeDictionary *pTypeDictionary ); + virtual IAttributeWidgetFactory *GetArrayWidgetFactory( CDmElement *object, CDmAttribute *pAttribute, CDmeEditorTypeDictionary *pTypeDictionary ); + virtual void ApplyChanges( vgui::Panel *pWidget, vgui::Panel *pSender = NULL ); + virtual void Refresh( vgui::Panel *pWidget, vgui::Panel *pSender = NULL ); + + // Adds a widget to the factory + void AddWidgetFactory( IAttributeWidgetFactory *pFactory, const char *pWidgetName ); + + // Finds a widget factory by name + IAttributeWidgetFactory *FindWidgetFactory( const char *pWidgetName ); + + // Creates a widget using editor attribute info +// vgui::Panel *CreateWidget( vgui::Panel *parent, CDmElement *obj, INotifyUI *pNotify, CDmeEditorAttributeInfo *pWidgetInfo, bool bAutoApply ); + +private: + CUtlDict< IAttributeWidgetFactory*, unsigned short > m_Factories; +}; + + +//----------------------------------------------------------------------------- +// Singleton instance +//----------------------------------------------------------------------------- +static CAttributeWidgetFactoryList *g_pWidgetFactoryFactoryList; +IAttributeWidgetFactoryList *attributewidgetfactorylist; + +CAttributeWidgetFactoryList *GetWidgetFactoryManager() +{ + if ( !g_pWidgetFactoryFactoryList ) + { + g_pWidgetFactoryFactoryList = new CAttributeWidgetFactoryList; + attributewidgetfactorylist = g_pWidgetFactoryFactoryList; + } + return g_pWidgetFactoryFactoryList; +} + + +//----------------------------------------------------------------------------- +// Standard implementation of a widget factory +//----------------------------------------------------------------------------- +template < class T > +class CAttributeWidgetFactory : public IAttributeWidgetFactory +{ +public: + CAttributeWidgetFactory( const char *pWidgetName ) + { + GetWidgetFactoryManager()->AddWidgetFactory( this, pWidgetName ); + } + + // Backward compat + virtual vgui::Panel *Create( vgui::Panel *pParent, const AttributeWidgetInfo_t &info ) + { + CBaseAttributePanel *newPanel = new T( pParent, info ); + if ( newPanel ) + { + newPanel->PostConstructor(); + } + return newPanel; + } +}; + + +//----------------------------------------------------------------------------- +// create all the AttributeWidgetFactorys +//----------------------------------------------------------------------------- + +// An Attribute Widget Factory for: text entry +static CAttributeWidgetFactory<CAttributeTextPanel> g_AttributeTextWidgetFactory( "text" ); + +// An Attribute Widget Factory for: picking files +static CAttributeWidgetFactory<CAttributeDmeFilePickerPanel> g_AttributeFilePickerWidgetFactory( "filepicker" ); + +// An Attribute Widget Factory for: choosing integers +static CAttributeWidgetFactory<CAttributeBoolChoicePanel> g_AttributeBoolChoiceWidgetFactory( "boolchoice" ); + +// An Attribute Widget Factory for: choosing integers +static CAttributeWidgetFactory<CAttributeIntChoicePanel> g_AttributeIntChoiceWidgetFactory( "intchoice" ); + +// An Attribute Widget Factory for: choosing strings +static CAttributeWidgetFactory<CAttributeStringChoicePanel> g_AttributeStringChoiceWidgetFactory( "stringchoice" ); + +// An Attribute Widget Factory for: elements +static CAttributeWidgetFactory<CAttributeElementPanel> g_AttributeElementWidgetFactory( "element" ); + +// An Attribute Widget Factory for: picking elements +static CAttributeWidgetFactory<CAttributeElementPickerPanel> g_AttributeElementPickerWidgetFactory( "elementchoice" ); + +// An Attribute Widget Factory for: picking MDLs +static CAttributeWidgetFactory<CAttributeMDLPickerPanel> g_AttributeMDLPickerWidgetFactory( "mdlpicker" ); + +// An Attribute Widget Factory for: picking sequences +static CAttributeWidgetFactory<CAttributeSequencePickerPanel> g_AttributeSequencePickerWidgetFactory( "sequencepicker" ); + +// An Attribute Widget Factory for: picking sounds +static CAttributeWidgetFactory<CAttributeSoundPickerPanel> g_AttributeSoundPickerWidgetFactory( "soundpicker" ); + +// An Attribute Widget Factory for: picking bsps +static CAttributeWidgetFactory<CAttributeBspPickerPanel> g_AttributeBspPickerWidgetFactory( "bsppicker" ); + +// An Attribute Widget Factory for: picking vmts +static CAttributeWidgetFactory<CAttributeVmtPickerPanel> g_AttributeVmtPickerWidgetFactory( "vmtpicker" ); + +// An Attribute Widget Factory for: picking vtfs +static CAttributeWidgetFactory<CAttributeVtfPickerPanel> g_AttributeVtfPickerWidgetFactory( "vtfpicker" ); + +// An Attribute Widget Factory for: picking tgas +static CAttributeWidgetFactory<CAttributeTgaFilePickerPanel> g_AttributeTgaPickerWidgetFactory( "tgapicker" ); + +// An Attribute Widget Factory for: picking shaders +static CAttributeWidgetFactory<CAttributeShaderPickerPanel> g_AttributeShaderPickerWidgetFactory( "shaderpicker" ); + +// An Attribute Widget Factory for: picking surface properties +static CAttributeWidgetFactory<CAttributeSurfacePropertyPickerPanel> g_AttributeSurfacePropertyPickerWidgetFactory( "surfacepropertypicker" ); + +// An Attribute Widget Factory for: picking surface properties +static CAttributeWidgetFactory<CAttributeColorPickerPanel> g_AttributeColorPickerWidgetFactory( "colorpicker" ); + +// An Attribute Widget Factory for: picking avis +static CAttributeWidgetFactory<CAttributeAviFilePickerPanel> g_AttributeAviPickerWidgetFactory( "avipicker" ); + +// An Attribute Widget Factory for: picking sht +static CAttributeWidgetFactory<CAttributeShtFilePickerPanel> g_AttributeShtPickerWidgetFactory( "shtpicker" ); + +// An Attribute Widget Factory for: picking detail types +static CAttributeWidgetFactory<CAttributeDetailTypePickerPanel> g_AttributeDetailTypePickerWidgetFactory( "detailtypepicker" ); + +// An Attribute Widget Factory for: picking color correction lookup files +static CAttributeWidgetFactory<CAttributeRawFilePickerPanel> g_AttributeRawPickerWidgetFactory( "rawpicker" ); + +// An Attribute Widget Factory for: choosing interpolator types (left and right) +static CAttributeWidgetFactory<CAttributeInterpolatorChoicePanel> g_AttributeInterpolatorChoiceWidgetFactory( "interpolatorchoice" ); + + +//----------------------------------------------------------------------------- +// Name-based widget factories +//----------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------- +// g_AttributeWidgetFactories +// Purpose: a mapping of all attribute types to AttributeWidgetFactories + +struct DefaultAttributeFactoryEntry_t +{ + int attributeType; + IAttributeWidgetFactory *factory; +}; + +static DefaultAttributeFactoryEntry_t g_AttributeWidgetFactories[] = +{ + + { AT_UNKNOWN, NULL }, + + { AT_ELEMENT, &g_AttributeElementWidgetFactory }, + { AT_INT, &g_AttributeTextWidgetFactory }, + { AT_FLOAT, &g_AttributeTextWidgetFactory }, + { AT_BOOL, &g_AttributeTextWidgetFactory }, + { AT_STRING, &g_AttributeTextWidgetFactory }, + { AT_VOID, &g_AttributeTextWidgetFactory }, + { AT_OBJECTID, &g_AttributeTextWidgetFactory }, + { AT_COLOR, &g_AttributeColorPickerWidgetFactory }, + { AT_VECTOR2, &g_AttributeTextWidgetFactory }, + { AT_VECTOR3, &g_AttributeTextWidgetFactory }, + { AT_VECTOR4, &g_AttributeTextWidgetFactory }, + { AT_QANGLE, &g_AttributeTextWidgetFactory }, + { AT_QUATERNION, &g_AttributeTextWidgetFactory }, + { AT_VMATRIX, &g_AttributeTextWidgetFactory }, + + { AT_ELEMENT_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_INT_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_FLOAT_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_BOOL_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_STRING_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_VOID_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_ELEMENT_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_OBJECTID_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_COLOR_ARRAY, &g_AttributeColorPickerWidgetFactory }, + { AT_VECTOR2_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_VECTOR3_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_VECTOR4_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_QANGLE_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_QUATERNION_ARRAY, &g_AttributeTextWidgetFactory }, + { AT_VMATRIX_ARRAY, &g_AttributeTextWidgetFactory }, + +}; + + +//----------------------------------------------------------------------------- +// +// CAttributeWidgetFactoryList +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Adds a widget to the factory +//----------------------------------------------------------------------------- +void CAttributeWidgetFactoryList::AddWidgetFactory( IAttributeWidgetFactory *pFactory, const char *pWidgetName ) +{ + m_Factories.Insert( pWidgetName, pFactory ); +} + + +//----------------------------------------------------------------------------- +// Finds a widget factory by name +//----------------------------------------------------------------------------- +IAttributeWidgetFactory *CAttributeWidgetFactoryList::FindWidgetFactory( const char *pWidgetName ) +{ + unsigned short i = m_Factories.Find( pWidgetName ); + if ( i != m_Factories.InvalidIndex() ) + return m_Factories[i]; + return NULL; +} + + +//----------------------------------------------------------------------------- +// Returns a factory requested by name +//----------------------------------------------------------------------------- +IAttributeWidgetFactory *CAttributeWidgetFactoryList::GetWidgetFactory( const char *pWidgetName ) +{ + return FindWidgetFactory( pWidgetName ); +} + + +//----------------------------------------------------------------------------- +// Returns a factory used to create widget for the attribute passed in +//----------------------------------------------------------------------------- +IAttributeWidgetFactory *CAttributeWidgetFactoryList::GetWidgetFactory( CDmElement *object, + CDmAttribute *pAttribute, CDmeEditorTypeDictionary *pTypeDictionary ) +{ + if ( !object ) + return NULL; + + DmAttributeType_t attributeType = pAttribute->GetType(); + IAttributeWidgetFactory *pFactory = g_AttributeWidgetFactories[ attributeType ].factory; + + // Override behavior with editor info, if it exists + if ( pTypeDictionary ) + { + const char *pAttributeName = pAttribute->GetName(); + CDmeEditorAttributeInfo *pEditorInfo = pTypeDictionary->GetAttributeInfo( object, pAttributeName ); + if ( pEditorInfo ) + { + if ( !pEditorInfo->m_bIsVisible ) + return NULL; + + if ( pEditorInfo->GetWidgetName() ) + { + IAttributeWidgetFactory *pOverriddenFactory = g_pWidgetFactoryFactoryList->FindWidgetFactory( pEditorInfo->GetWidgetName() ); + if ( pOverriddenFactory ) + { + pFactory = pOverriddenFactory; + } + } + } + } + return pFactory; +} + + +//----------------------------------------------------------------------------- +// Returns a factory used to create widgets for entries in an attribute array +//----------------------------------------------------------------------------- +IAttributeWidgetFactory *CAttributeWidgetFactoryList::GetArrayWidgetFactory( CDmElement *object, + CDmAttribute *pAttribute, CDmeEditorTypeDictionary *pTypeDictionary ) +{ + if ( !object ) + return NULL; + + DmAttributeType_t attributeType = ArrayTypeToValueType( pAttribute->GetType() ); + IAttributeWidgetFactory *pFactory = g_AttributeWidgetFactories[ attributeType ].factory; + + // Override behavior with editor info, if it exists + if ( pTypeDictionary ) + { + CDmeEditorAttributeInfo *pEditorInfo = pTypeDictionary->GetAttributeArrayInfo( object, pAttribute->GetName() ); + if ( pEditorInfo ) + { + if ( !pEditorInfo->m_bIsVisible ) + return NULL; + + if ( pEditorInfo->GetWidgetName() ) + { + IAttributeWidgetFactory *pOverriddenFactory = g_pWidgetFactoryFactoryList->FindWidgetFactory( pEditorInfo->GetWidgetName() ); + if ( pOverriddenFactory ) + { + pFactory = pOverriddenFactory; + } + } + } + } + + return pFactory; +} + + +//----------------------------------------------------------------------------- +// Applies changes to a widget +//----------------------------------------------------------------------------- +void CAttributeWidgetFactoryList::ApplyChanges( vgui::Panel *pWidget, vgui::Panel *pSender ) +{ + CBaseAttributePanel *pPanel = dynamic_cast< CBaseAttributePanel *>( pWidget ); + if ( pPanel && pPanel->GetDirty() ) + { + Assert( !pPanel->IsAutoApply() ); + vgui::ipanel()->SendMessage( pWidget->GetVPanel(), new KeyValues( "ApplyChanges" ), pSender ? pSender->GetVPanel() : NULL ); + } +} + + +//----------------------------------------------------------------------------- +// Refreshes a widget when attributes change +//----------------------------------------------------------------------------- +void CAttributeWidgetFactoryList::Refresh( vgui::Panel *pWidget, vgui::Panel *pSender ) +{ + if ( pWidget ) + { + vgui::ipanel()->SendMessage( pWidget->GetVPanel(), new KeyValues( "Refresh" ), pSender ? pWidget->GetVPanel() : NULL ); + } +} + diff --git a/vgui2/dme_controls/BaseAnimSetAttributeSliderPanel.cpp b/vgui2/dme_controls/BaseAnimSetAttributeSliderPanel.cpp new file mode 100644 index 0000000..cc57436 --- /dev/null +++ b/vgui2/dme_controls/BaseAnimSetAttributeSliderPanel.cpp @@ -0,0 +1,1534 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/BaseAnimSetAttributeSliderPanel.h" +#include "movieobjects/dmechannel.h" +#include "movieobjects/dmeanimationset.h" +#include "vgui_controls/TextImage.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Slider.h" +#include "vgui_controls/PanelListPanel.h" +#include "dme_controls/BaseAnimSetPresetFaderPanel.h" +#include "dme_controls/BaseAnimationSetEditor.h" +#include "dme_controls/attributeslider.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IInput.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +#define ANIMATION_SET_EDITOR_ATTRIBUTESLIDERS_BUTTONTRAY_HEIGHT 32 +#define ANIMATION_SET_EDITOR_ATTRIBUTESLIDERS_BUTTONTRAY_YPOS 0 + +//----------------------------------------------------------------------------- +// Enums +//----------------------------------------------------------------------------- +#define SLIDER_PIXEL_SPACING 3 +#define RECOMPUTE_PREVIEW_INTERVAL ( 0.5f ) +#define CIRCULAR_CONTROL_RADIUS 6.0f +#define FRAC_PER_PIXEL 0.0025f + +static ConVar ifm_attributesliderbalance_sensitivity( "ifm_attributesliderbalance_sensitivity", "1.0", 0 ); + +extern float ifm_fader_timescale; + +enum +{ + // Just some arbitrary number that we're not likely to see in another .lib or the main app's code + UNDO_CHAIN_MOUSEWHEEL_ATTRIBUTE_SLIDER = 9876, +}; + + +bool CBaseAnimSetAttributeSliderPanel::ChannelToSliderLookup_t::Less( const ChannelToSliderLookup_t& lhs, const ChannelToSliderLookup_t& rhs ) +{ + return lhs.ch.Get() < rhs.ch.Get(); +} + + +//----------------------------------------------------------------------------- +// Blends flex values in left-right space instead of balance/value space +//----------------------------------------------------------------------------- +static void BlendFlexValues( AttributeValue_t *pResult, const AttributeValue_t &src, const AttributeValue_t &dest, float flBlend, float flBalanceFilter = 0.5f ) +{ + // Apply the left-right balance to the target + float flLeftFilter, flRightFilter; + ValueBalanceToLeftRight( &flLeftFilter, &flRightFilter, flBlend, flBalanceFilter ); + + // Do the math in 'left-right' space because we filter in that space + float flSrcLeft, flSrcRight; + ValueBalanceToLeftRight( &flSrcLeft, &flSrcRight, src.m_pValue[ANIM_CONTROL_VALUE], src.m_pValue[ANIM_CONTROL_BALANCE] ); + + float flDestLeft, flDestRight; + ValueBalanceToLeftRight( &flDestLeft, &flDestRight, dest.m_pValue[ANIM_CONTROL_VALUE], dest.m_pValue[ANIM_CONTROL_BALANCE] ); + + float flTargetLeft = flSrcLeft + flLeftFilter * ( flDestLeft - flSrcLeft ); + float flTargetRight = flSrcRight + flRightFilter * ( flDestRight - flSrcRight ); + + LeftRightToValueBalance( &pResult->m_pValue[ANIM_CONTROL_VALUE], &pResult->m_pValue[ANIM_CONTROL_BALANCE], flTargetLeft, flTargetRight, + ( flBlend <= 0.5f ) ? src.m_pValue[ANIM_CONTROL_BALANCE] : dest.m_pValue[ANIM_CONTROL_BALANCE] ); + + pResult->m_pValue[ANIM_CONTROL_MULTILEVEL] = src.m_pValue[ANIM_CONTROL_MULTILEVEL] + ( dest.m_pValue[ANIM_CONTROL_MULTILEVEL] - src.m_pValue[ANIM_CONTROL_MULTILEVEL] ) * flBlend; +} + + +//----------------------------------------------------------------------------- +// +// CPresetSideFilterSlider class begins +// +//----------------------------------------------------------------------------- +class CPresetSideFilterSlider : public Slider +{ + DECLARE_CLASS_SIMPLE( CPresetSideFilterSlider, Slider ); + +public: + CPresetSideFilterSlider( CBaseAnimSetAttributeSliderPanel *pParent, const char *panelName ); + virtual ~CPresetSideFilterSlider(); + + float GetPos(); + void SetPos( float frac ); + +protected: + virtual void Paint(); + virtual void PaintBackground(); + virtual void ApplySchemeSettings( IScheme *scheme ); + virtual void GetTrackRect( int &x, int &y, int &w, int &h ); + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + +private: + CBaseAnimSetAttributeSliderPanel *m_pParent; + + Color m_ZeroColor; + Color m_TextColor; + Color m_TextColorFocus; + TextImage *m_pName; + float m_flCurrent; +}; + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CPresetSideFilterSlider::CPresetSideFilterSlider( CBaseAnimSetAttributeSliderPanel *parent, const char *panelName ) : + BaseClass( (Panel *)parent, panelName ), m_pParent( parent ) +{ + SetRange( 0, 1000 ); + SetDragOnRepositionNob( true ); + SetPos( 0.5f ); + SetPaintBackgroundEnabled( true ); + + m_pName = new TextImage( "Preset Side Filter" ); + + SetBgColor( Color( 128, 128, 128, 128 ) ); + + m_ZeroColor = Color( 33, 33, 33, 255 ); + m_TextColor = Color( 200, 200, 200, 255 ); + m_TextColorFocus = Color( 208, 143, 40, 255 ); +} + +CPresetSideFilterSlider::~CPresetSideFilterSlider() +{ + delete m_pName; +} + +void CPresetSideFilterSlider::OnMousePressed(MouseCode code) +{ + if ( code == MOUSE_RIGHT ) + { + SetPos( 0.5f ); + return; + } + + BaseClass::OnMousePressed( code ); +} + +void CPresetSideFilterSlider::OnMouseDoublePressed(MouseCode code) +{ + if ( code == MOUSE_LEFT ) + { + SetPos( 0.5f ); + return; + } + + BaseClass::OnMouseDoublePressed( code ); +} + +float CPresetSideFilterSlider::GetPos() +{ + return GetValue() * 0.001f; +} + +void CPresetSideFilterSlider::SetPos( float frac ) +{ + SetValue( (int)( frac * 1000.0f + 0.5f ), false ); +} + +void CPresetSideFilterSlider::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + + m_pName->SetFont( scheme->GetFont( "DefaultBold" ) ); + m_pName->SetColor( m_TextColor ); + m_pName->ResizeImageToContent(); + + SetFgColor( Color( 194, 120, 0, 255 ) ); + SetThumbWidth( 3 ); +} + +void CPresetSideFilterSlider::GetTrackRect( int &x, int &y, int &w, int &h ) +{ + GetSize( w, h ); + x = 0; + y = 2; + h -= 4; +} + +void CPresetSideFilterSlider::Paint() +{ + // horizontal nob + int x, y; + int wide,tall; + GetTrackRect( x, y, wide, tall ); + + Color col = GetFgColor(); + surface()->DrawSetColor( col ); + surface()->DrawFilledRect( _nobPos[0], 1, _nobPos[1], GetTall() - 1 ); + surface()->DrawSetColor( m_ZeroColor ); + surface()->DrawFilledRect( _nobPos[0] - 1, y + 1, _nobPos[0], y + tall - 1 ); +} + +void CPresetSideFilterSlider::PaintBackground() +{ + int w, h; + GetSize( w, h ); + + int tx, ty, tw, th; + GetTrackRect( tx, ty, tw, th ); + surface()->DrawSetColor( m_ZeroColor ); + surface()->DrawFilledRect( tx, ty, tx + tw, ty + th ); + + int cw, ch; + m_pName->SetColor( _dragging ? m_TextColorFocus : m_TextColor ); + m_pName->GetContentSize( cw, ch ); + m_pName->SetPos( ( w - cw ) * 0.5f, ( h - ch ) * 0.5f ); + m_pName->Paint(); +} + + +//----------------------------------------------------------------------------- +// +// CBaseAnimSetAttributeSliderPanel begins +// +//----------------------------------------------------------------------------- +CBaseAnimSetAttributeSliderPanel::CBaseAnimSetAttributeSliderPanel( vgui::Panel *parent, const char *className, CBaseAnimationSetEditor *editor ) : + BaseClass( parent, className ), + m_flEstimatedValue( 0.0f ), + m_ChannelToSliderLookup( 0, 0, ChannelToSliderLookup_t::Less ), + m_flRecomputePreviewTime( -1.0f ), + m_bRequestedNewPreview( false ), + m_flPrevTime( 0.0f ), + m_nFaderChangeFlags( 0 ) +{ + m_hEditor = editor; + + m_pLeftRightBoth[ 0 ] = new Button( this, "AttributeSliderLeftOnly", "", this, "OnLeftOnly" ); + m_pLeftRightBoth[ 1 ] = new Button( this, "AttributeSliderRightOnly", "", this, "OnRightOnly" ); + m_pPresetSideFilter = new CPresetSideFilterSlider( this, "PresetSideFilter" ); + m_Sliders = new PanelListPanel( this, "AttributeSliders" ); + m_Sliders->SetFirstColumnWidth( 0 ); + m_Sliders->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, ANIMATION_SET_EDITOR_ATTRIBUTESLIDERS_BUTTONTRAY_HEIGHT, + 0, 0 + ); + m_Sliders->SetVerticalBufferPixels( 0 ); + + m_PreviousPreviewFader = ""; + m_Previous.isbeingdragged = false; + m_Previous.holdingctrl = false; + m_Previous.amount = 0.0f; +} + +void CBaseAnimSetAttributeSliderPanel::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "OnLeftOnly" ) ) + { + m_pPresetSideFilter->SetPos( 0.0f ); + return; + } + + if ( !Q_stricmp( pCommand, "OnRightOnly" ) ) + { + m_pPresetSideFilter->SetPos( 1.0f ); + return; + } + + BaseClass::OnCommand( pCommand ); +} + +void CBaseAnimSetAttributeSliderPanel::StampValueIntoLogs( CDmElement *control, AnimationControlType_t type, float flValue ) +{ +} + +void CBaseAnimSetAttributeSliderPanel::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + m_Sliders->SetBgColor( Color( 42, 42, 42, 255 ) ); +} + +void CBaseAnimSetAttributeSliderPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + int w, h; + GetSize( w, h ); + + int availH = ANIMATION_SET_EDITOR_ATTRIBUTESLIDERS_BUTTONTRAY_HEIGHT; + + int btnSize = 9; + m_pLeftRightBoth[ 0 ]->SetBounds( 15, ( availH - btnSize ) / 2, btnSize, btnSize ); + m_pLeftRightBoth[ 1 ]->SetBounds( w - 15, ( availH - btnSize ) / 2, btnSize, btnSize ); + m_pPresetSideFilter->SetBounds( 23 + btnSize, 4, w - 38 - 2 * btnSize, availH - 8 ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Determines: +// a) are we holding the ctrl key still, if so +// figures out the crossfade amount of each preset slider with non-zero influence +// b) not holding control, then just see if we are previewing whichever preset the mouse is over +// Input : - +//----------------------------------------------------------------------------- +void CBaseAnimSetAttributeSliderPanel::OnThink() +{ + BaseClass::OnThink(); + + CBaseAnimSetPresetFaderPanel *presets = m_hEditor->GetPresetFader(); + if ( !presets ) + return; + + FaderPreview_t fader; + presets->GetPreviewFader( fader ); + + bool nameChanged = ( fader.name && ( m_PreviousPreviewFader.IsEmpty() || Q_stricmp( m_PreviousPreviewFader.Get(), fader.name ) ) ) ? true : false; + bool beingDraggedChanged = fader.isbeingdragged != m_Previous.isbeingdragged ? true : false; + bool ctrlKeyChanged = fader.holdingctrl != m_Previous.holdingctrl ? true : false; + bool bFaderChanged = ( nameChanged || beingDraggedChanged || ctrlKeyChanged ); + bool bPresetChanged = fader.preset != m_Previous.preset; + bool faderAmountChanged = fader.amount != m_Previous.amount ? true : false; + + m_nFaderChangeFlags = 0; + if ( nameChanged ) + { + m_nFaderChangeFlags |= FADER_NAME_CHANGED; + } + if ( beingDraggedChanged ) + { + m_nFaderChangeFlags |= FADER_DRAG_CHANGED; + } + if ( ctrlKeyChanged ) + { + m_nFaderChangeFlags |= FADER_CTRLKEY_CHANGED; + } + if ( faderAmountChanged ) + { + m_nFaderChangeFlags |= FADER_AMOUNT_CHANGED; + } + if ( bPresetChanged ) + { + m_nFaderChangeFlags |= FADER_PRESET_CHANGED; + } + + m_PreviousPreviewFader = fader.name; + m_Previous = fader; + + int c = m_SliderList.Count(); + for ( int i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + slider->EnablePreview( false, false, false ); + if ( !slider->IsVisible() ) + continue; + + if ( !slider->GetControl() ) + continue; + + const char *name = slider->GetName(); + Assert( name ); + + if ( m_CtrlKeyPreviewSlider.Get() == slider ) + { + // The preset stuff shouldn't be active when we're holding ctrl over the raw attribute sliders!!! + Assert( !fader.isbeingdragged ); + m_CtrlKeyPreviewSliderElement = NULL; + m_CtrlKeyPreviewSlider = NULL; + + AttributeValue_t dest; + if ( !slider->IsTransform() ) + { + dest.m_pValue[ ANIM_CONTROL_VALUE ] = m_flEstimatedValue; + dest.m_pValue[ ANIM_CONTROL_BALANCE ] = slider->GetValue( ANIM_CONTROL_BALANCE ); + dest.m_pValue[ ANIM_CONTROL_MULTILEVEL ] = slider->GetValue( ANIM_CONTROL_MULTILEVEL ); + } + else + { + dest.m_pValue[ ANIM_CONTROL_VALUE ] = 0.0f; + dest.m_pValue[ ANIM_CONTROL_BALANCE ] = 0.5f; + dest.m_pValue[ ANIM_CONTROL_MULTILEVEL ] = 0.5f; + } + + // If we aren't over any of the preset sliders, then we need to be able to ramp down to the current value, too + slider->EnablePreview( true, false, false ); + slider->SetPreview( dest, dest, true, true ); + continue; + } + + if ( !fader.values ) + continue; + + AttributeValue_t preview; + preview.m_pValue[ ANIM_CONTROL_VALUE ] = slider->IsTransform() ? 0.0f : slider->GetControlDefaultValue( ANIM_CONTROL_VALUE ); + preview.m_pValue[ ANIM_CONTROL_BALANCE ] = 0.5f; + preview.m_pValue[ ANIM_CONTROL_MULTILEVEL ] = 0.5f; + + AttributeValue_t current = slider->GetValue(); + + int idx = fader.values->Find( name ); + if ( idx != fader.values->InvalidIndex() ) + { + preview = (*fader.values)[ idx ]; + } + BlendFlexValues( &preview, current, preview, 1.0f, slider->IsControlActive( ANIM_CONTROL_BALANCE ) ? m_pPresetSideFilter->GetPos() : 0.5f ); + + bool simple = fader.isbeingdragged || ( !fader.holdingctrl && !slider->IsRampingTowardPreview() && !ctrlKeyChanged ); + + slider->EnablePreview( true, simple, fader.isbeingdragged ); + if ( bFaderChanged || fader.isbeingdragged ) + { + // If being dragged, slam to current value right away + if ( simple ) + { + slider->SetPreview( preview, preview, true, true ); + } + else + { + // For the "ramp down" case (just released ctrl key), we need the original values instead of the preview valuees + if ( ctrlKeyChanged && !fader.holdingctrl && slider->IsRampingTowardPreview() ) + { + Assert( !fader.isbeingdragged ); + slider->RampDown(); + } + else + { + // Apply the left-right balance to the target + AttributeValue_t dest; + BlendFlexValues( &dest, current, preview, fader.amount ); + slider->SetPreview( dest, preview, !fader.holdingctrl, !nameChanged ); + } + } + } + + if ( faderAmountChanged || fader.isbeingdragged || fader.holdingctrl ) + { + slider->UpdateFaderAmount( fader.amount ); + } + } + + UpdatePreviewSliderTimes(); + + ApplySliderValues( false ); + + PerformRecomputePreview(); +} + +void CBaseAnimSetAttributeSliderPanel::ChangeAnimationSet( CDmeAnimationSet *newAnimSet ) +{ + int i; + // Force recomputation + m_nActiveControlSetMode = -1; + + bool rebuild = true; + + if ( m_AnimSet.Get() && newAnimSet == m_AnimSet ) + { + // See if every slider is the same as before + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + int controlCount = controls.Count(); + if ( m_SliderList.Count() == controlCount ) + { + rebuild = false; + for ( i = 0 ; i < controlCount; ++i ) + { + CDmElement *control = controls[ i ]; + if ( !control ) + continue; + + if ( Q_stricmp( control->GetName(), m_SliderList[ i ]->GetName() ) ) + { + rebuild = true; + break; + } + + bool bStereo = control->GetValue< bool >( "combo" ); + bool bIsMulti = control->GetValue< bool >( "multi" ); + if ( m_SliderList[ i ]->IsControlActive( ANIM_CONTROL_BALANCE ) != bStereo || + m_SliderList[ i ]->IsControlActive( ANIM_CONTROL_MULTILEVEL ) != bIsMulti ) + { + rebuild = true; + break; + } + } + } + } + + m_AnimSet = newAnimSet; + + if ( !m_AnimSet.Get() ) + return; + + if ( !rebuild ) + return; + + int c = m_SliderList.Count(); + for ( i = 0 ; i < c; ++i ) + { + delete m_SliderList[ i ]; + } + m_SliderList.RemoveAll(); + m_Sliders->RemoveAll(); + m_ChannelToSliderLookup.Purge(); + + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + // Now create sliders for all known controls, nothing visible by default + int controlCount = controls.Count(); + for ( i = 0 ; i < controlCount; ++i ) + { + CDmElement *control = controls[ i ]; + if ( !control ) + continue; + + CAttributeSlider *slider = new CAttributeSlider( this, control->GetName(), control ); + + slider->SetVisible( false ); + slider->SetValue( ANIM_CONTROL_VALUE, control->GetValue< float >( "value" ) ); + slider->SetSize( 100, 20 ); + + bool bStereo = control->GetValue< bool >( "combo" ); + slider->ActivateControl( ANIM_CONTROL_BALANCE, bStereo ); + if ( bStereo ) + { + slider->SetValue( ANIM_CONTROL_BALANCE, control->GetValue< float >( "balance" ) ); + } + + bool bMulti = control->GetValue< bool >( "multi" ); + slider->ActivateControl( ANIM_CONTROL_MULTILEVEL, bMulti ); + if ( bMulti ) + { + slider->SetValue( ANIM_CONTROL_MULTILEVEL, control->GetValue< float >( "multilevel" ) ); + } + m_SliderList.AddToTail( slider ); + + ChannelToSliderLookup_t lookup; + lookup.slider = control; + + CDmeChannel *ctrlChannels[ LOG_PREVIEW_MAX_CHANNEL_COUNT ]; + GetChannelsForControl( control, ctrlChannels ); + for ( int j = 0 ; j < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++j ) + { + lookup.ch = ctrlChannels[ j ]; + lookup.type = (AnimationControlType_t)j; + if ( lookup.ch ) + { + Assert( m_ChannelToSliderLookup.Find( lookup) == m_ChannelToSliderLookup.InvalidIndex() ); + m_ChannelToSliderLookup.Insert( lookup ); + } + } + } + + RecomputePreview(); +} + +void CBaseAnimSetAttributeSliderPanel::SetVisibleControlsForSelectionGroup( CUtlSymbolTable& visible ) +{ + int i, c; + + bool changed = false; + + // Walk through all sliders and show only those in the symbol table + c = m_SliderList.Count(); + for ( i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + const char *sliderName = slider->GetName(); + + bool showSlider = visible.Find( sliderName ) != UTL_INVAL_SYMBOL ? true : false; + if ( slider->IsVisible() != showSlider ) + { + changed = true; + break; + } + } + + if ( !changed ) + return; + + m_Sliders->RemoveAll(); + c = m_SliderList.Count(); + for ( i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + const char *sliderName = slider->GetName(); + + if ( visible.Find( sliderName ) != UTL_INVAL_SYMBOL ) + { + slider->SetVisible( true ); + m_Sliders->AddItem( NULL, slider ); + } + else + { + slider->SetVisible( false ); + } + } + + // Force mode to recompute + m_nActiveControlSetMode = -1; + RecomputePreview(); +} + + +void CBaseAnimSetAttributeSliderPanel::ApplyPreset( float flScale, AttributeDict_t& values ) +{ + int c = m_SliderList.Count(); + for ( int i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + if ( !slider || !slider->IsVisible() ) + continue; + + if ( slider->IsTransform() ) + continue; + + AttributeValue_t current, target; + current = slider->GetValue( ); + + target.m_pValue[ ANIM_CONTROL_VALUE ] = slider->GetControlDefaultValue( ANIM_CONTROL_VALUE ); + target.m_pValue[ ANIM_CONTROL_BALANCE ] = 0.5f; + target.m_pValue[ ANIM_CONTROL_MULTILEVEL ] = 0.5f; + + const char *name = slider->GetName(); + int idx = name ? values.Find( name ) : values.InvalidIndex(); + if ( idx != values.InvalidIndex() ) + { + target = values[ idx ]; + } + + // Apply the left-right balance to the target + AttributeValue_t blend; + BlendFlexValues( &blend, current, target, flScale, slider->IsControlActive( ANIM_CONTROL_BALANCE ) ? m_pPresetSideFilter->GetPos() : 0.5f ); + slider->SetValue( blend ); + } +} + +static const char *s_pAnimControlAttribute[ANIM_CONTROL_COUNT] = +{ + "value", "balance", "multilevel" +}; + +bool CBaseAnimSetAttributeSliderPanel::ApplySliderValues( bool bForce ) +{ + if ( !m_AnimSet.Get() ) + return false; + + if ( !bForce ) + { + bForce = m_Previous.isbeingdragged; + } + + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + bool valuesChanged = false; + + CDisableUndoScopeGuard guard; + + int c = m_SliderList.Count(); + for ( int i = 0; i < c; ++i ) + { + CAttributeSlider *pSlider = m_SliderList[ i ]; + if ( !pSlider->IsVisible() ) + continue; + + CDmElement *pControl = controls[ i ]; + if ( !pControl ) + continue; + + // Skip these types of sliders... + if ( pControl->GetValue< bool >( "transform" ) ) + continue; + + CDmeChannel *ctrlChannels[ LOG_PREVIEW_MAX_CHANNEL_COUNT ]; + GetChannelsForControl( pControl, ctrlChannels ); + + bool bUsePreviewValue = pSlider->IsPreviewEnabled() && ( !pSlider->IsSimplePreview() || bForce ); + + for ( int j = 0; j < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++j ) + { + AnimationControlType_t type = (AnimationControlType_t)j; + CDmeChannel *pChannel = ctrlChannels[ j ]; + + // Figure out what to do based on the channel's mode + ChannelMode_t mode = pChannel ? pChannel->GetMode() : CM_PASS; + bool bPushSlidersIntoScene = ( mode == CM_PASS || mode == CM_RECORD ); + bool bPullSlidersFromScene = ( mode == CM_PLAY ); + + if ( bPullSlidersFromScene ) + { + Assert( pChannel ); + + // If it's actively being manipulated, the UI will be up to date + if ( pSlider->GetDragControl() == type ) + continue; + + // If we're dragging value, we're now going to be changing balance as well + if ( pSlider->GetDragControl() == ANIM_CONTROL_VALUE && type == ANIM_CONTROL_BALANCE ) + continue; + + // Drive value setting based on the output data + // NOTE: GetCurrentPlaybackValue might not overwrite flValue. + float flValue = pSlider->GetControlDefaultValue( type ); + pChannel->GetCurrentPlaybackValue< float >( flValue ); + pSlider->SetValue( type, flValue ); + pControl->SetValue< float >( s_pAnimControlAttribute[type], flValue ); + } + else if ( bPushSlidersIntoScene ) + { + float flValue = bUsePreviewValue ? pSlider->GetPreview( type ) : pSlider->GetValue( type ); + if ( pControl->GetValue< float >( s_pAnimControlAttribute[type] ) != flValue || bForce ) + { + valuesChanged = true; + pControl->SetValue< float >( s_pAnimControlAttribute[type], flValue ); + } + } + } + } + + guard.Release(); + + return valuesChanged; +} + +void CBaseAnimSetAttributeSliderPanel::UpdatePreviewSliderTimes() +{ + if ( !m_AnimSet.Get() ) + return; + + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + float curtime = system()->GetFrameTime(); + float dt = clamp( curtime - m_flPrevTime, 0.0f, 0.1f ); + m_flPrevTime = curtime; + + dt *= ifm_fader_timescale; + + bool previewing = false; + bool changingvalues = false; + + bool ctrlDown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ); + int mx, my; + input()->GetCursorPos( mx, my ); + if ( ctrlDown ) + { + bool bInside = m_Sliders->IsWithin( mx, my ); + if ( !bInside ) + { + ctrlDown = false; + } + + VPANEL topMost = input()->GetMouseOver(); + if ( topMost && !ipanel()->HasParent( topMost, GetVPanel() ) ) + { + ctrlDown = false; + } + } + + CAttributeSlider *dragSlider = NULL; + CDmElement *dragSliderElement = NULL; + + m_CtrlKeyPreviewSliderElement = NULL; + m_CtrlKeyPreviewSlider = NULL; + m_flEstimatedValue = 0.0f; + + int c = m_SliderList.Count(); + for ( int i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + slider->UpdateTime( dt ); + + if ( !slider->IsVisible() ) + continue; + + bool ctrlDownOnThisSlider = false; + if ( ctrlDown ) + { + int x, y; + x = mx; + y = my; + slider->ScreenToLocal( x, y ); + + int sw, st; + slider->GetSize( sw, st ); + if ( x >= 0 && x < sw && y >= 0 && y < st ) + { + // Should only hit one slider!!! + Assert( !ctrlDownOnThisSlider ); + ctrlDownOnThisSlider = true; + if ( !slider->IsTransform() ) + { + m_flEstimatedValue = slider->EstimateValueAtPos( x, y ); + } + } + } + + if ( slider->IsPreviewEnabled() ) + { + if ( !slider->IsSimplePreview() ) + { + previewing = true; + } + } + // If a fader is being dragged, or we're directly manipulating the slider, then we are going to put + // this slider into record mode + if ( slider->IsFaderBeingDragged() ) + { + changingvalues = true; + Assert( !dragSlider ); + dragSlider = NULL; + dragSliderElement = NULL; + } + else if ( slider->IsDragging() ) + { + Assert( !dragSlider ); + dragSlider = slider; + dragSliderElement = controls[ i ]; + changingvalues = true; + } + + if ( ctrlDownOnThisSlider ) + { + m_CtrlKeyPreviewSliderElement = controls[ i ]; + m_CtrlKeyPreviewSlider = slider; + } + } + + switch ( m_hEditor->GetRecordingState() ) + { + default: + Assert( 0 ); + break; + case AS_OFF: + { + ActivateControlSetInMode( CM_OFF, CM_OFF, CM_OFF, NULL ); + } + break; + case AS_RECORD: + { + // Put one or all things into record mode (if dragging a single slider, other channels should be in playback mode) + if ( changingvalues || previewing ) + { + ActivateControlSetInMode( changingvalues ? CM_RECORD : CM_PASS, CM_PLAY, CM_PLAY, dragSlider ); + } + else + { + ActivateControlSetInMode( CM_PLAY, CM_PLAY, CM_PLAY, dragSlider ); + } + } + break; + case AS_PLAYBACK: + { + // Put things into playback, except if dragging a slider, to preview + if ( changingvalues || previewing ) + { + ActivateControlSetInMode( CM_PASS, CM_PLAY, CM_PLAY, NULL ); + } + else + { + ActivateControlSetInMode( CM_PLAY, CM_PLAY, CM_PLAY, NULL ); + } + } + break; + case AS_PREVIEW: + { + // Put things into passthru + ActivateControlSetInMode( CM_PASS, CM_PASS, CM_PLAY, NULL ); + } + break; + } + + if ( dragSliderElement ) + { + SetLogPreviewControl( dragSliderElement ); + } + else if ( m_CtrlKeyPreviewSliderElement.Get() ) + { + SetLogPreviewControl( m_CtrlKeyPreviewSliderElement ); + } +} + +bool CBaseAnimSetAttributeSliderPanel::GetAttributeSliderValue( AttributeValue_t *pValue, const char *name ) +{ + int c = m_SliderList.Count(); + for ( int i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + if ( Q_stricmp( slider->GetName(), name ) ) + continue; + + *pValue = slider->GetValue( ); + return true; + } + return false; +} + +void CBaseAnimSetAttributeSliderPanel::GetChannelsForControl( CDmElement *control, CDmeChannel *channels[LOG_PREVIEW_MAX_CHANNEL_COUNT] ) +{ + if ( control->GetValue< bool >( "transform" ) ) + { + CDmeChannel *ch1 = control->GetValueElement< CDmeChannel >( "position" ); + CDmeChannel *ch2 = control->GetValueElement< CDmeChannel >( "orientation" ); + + channels[ LOG_PREVIEW_POSITION ] = ch1; + channels[ LOG_PREVIEW_ORIENTATION ] = ch2; + for ( int i = 2; i < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++i ) + { + channels[i] = NULL; + } + return; + } + + if ( control->GetValue< bool >( "combo" ) ) + { + channels[ LOG_PREVIEW_VALUE ] = control->GetValueElement< CDmeChannel >( "valuechannel" ); + channels[ LOG_PREVIEW_BALANCE ] = control->GetValueElement< CDmeChannel >( "balancechannel" ); + } + else + { + channels[ LOG_PREVIEW_VALUE ] = control->GetValueElement< CDmeChannel >( "channel" ); + channels[ LOG_PREVIEW_BALANCE ] = 0; + } + + if ( control->GetValue< bool >( "multi" ) ) + { + channels[ LOG_PREVIEW_MULTILEVEL ] = control->GetValueElement< CDmeChannel >( "multilevelchannel" ); + } + else + { + channels[ LOG_PREVIEW_MULTILEVEL ] = NULL; + } + for ( int i = 3; i < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++i ) + { + channels[i] = NULL; + } +} + +void CBaseAnimSetAttributeSliderPanel::GetActiveTimeSelectionParams( DmeLog_TimeSelection_t& params ) +{ + Assert( 0 ); +} + +void CBaseAnimSetAttributeSliderPanel::ActivateControlSetInMode( int mode, int otherChannelsMode, int hiddenChannelsMode, CAttributeSlider *whichSlider /*= NULL*/ ) +{ + int i, c; + + if ( !m_AnimSet.Get() ) + return; + + bool updateChannelsModeChanged = m_nActiveControlSetMode != mode ? true : false; + if ( !updateChannelsModeChanged ) + return; + + int previousMode = m_nActiveControlSetMode; + m_nActiveControlSetMode = mode; + + if ( previousMode == CM_RECORD ) + { + DmeLog_TimeSelection_t params; + GetActiveTimeSelectionParams( params ); + + // Finalize any previously recording layers!!! + g_pChannelRecordingMgr->FinishLayerRecording( params.m_flThreshold, true ); + } + + ChannelMode_t channelMode = ( ChannelMode_t )mode; + + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + // Build the list of channels to alter + CUtlVector< CDmeChannel * > updateChannels; + CUtlVector< CDmeChannel * > otherChannels; + CUtlVector< CDmeChannel * > hiddenChannels; + + CDisableUndoScopeGuard guard; + + c = m_SliderList.Count(); + int j; + for ( i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + CDmElement *ctrl = controls[ i ]; + if ( !ctrl ) + continue; + + CDmeChannel *ctrlChannels[ LOG_PREVIEW_MAX_CHANNEL_COUNT ]; + GetChannelsForControl( ctrl, ctrlChannels ); + + for ( j = 0 ; j < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++j ) + { + if ( !ctrlChannels[ j ] ) + continue; + + CUtlVector< CDmeChannel * > *useArray = NULL;; + + // now characterize it + bool hidden = !slider->IsVisible(); + if ( hidden ) + { + useArray = &hiddenChannels; + } + else + { + if ( !whichSlider ) + { + useArray = &updateChannels; + } + else if ( slider == whichSlider ) + { + // this causes value and balance to get lumped together, since we're now dragging left/right values + if ( slider->GetDragControl() == ANIM_CONTROL_MULTILEVEL ) + { + useArray = ( j == LOG_PREVIEW_MULTILEVEL ) ? &updateChannels : &otherChannels; + } + else + { + useArray = ( j != LOG_PREVIEW_MULTILEVEL ) ? &updateChannels : &otherChannels; + } + } + else + { + useArray = &otherChannels; + } + } + + useArray->AddToTail( ctrlChannels[ j ] ); + } + } + + guard.Release(); + + c = updateChannels.Count(); + if ( c > 0 ) + { + if ( channelMode == CM_RECORD ) + { + DmeLog_TimeSelection_t params; + GetActiveTimeSelectionParams( params ); + params.SetRecordingMode( ( NULL == whichSlider ) ? RECORD_PRESET : RECORD_ATTRIBUTESLIDER ); + + g_pChannelRecordingMgr->StartLayerRecording( "Dragging Sliders", ¶ms ); + + for ( i = 0; i < c; ++i ) + { + // THIS PUTS THEM INTO CM_RECORD + g_pChannelRecordingMgr->AddChannelToRecordingLayer( updateChannels[ i ], GetCurrentMovie(), GetCurrentShot() ); + } + + SetTimeSelectionParametersForRecordingChannels( ( NULL == whichSlider ) ? m_Previous.amount : 1.0f ); + } + else + { + // Don't create undo records for this + CDisableUndoScopeGuard guardSet; + for ( i = 0; i < c; ++i ) + { + updateChannels[ i ]->SetMode( channelMode ); + } + } + } + + c = otherChannels.Count(); + if ( c > 0 ) + { + // Don't create undo records for this + CDisableUndoScopeGuard guardRecord; + + for ( i = 0; i < c; ++i ) + { + // These should never go into record!!! + Assert( (ChannelMode_t)otherChannelsMode != CM_RECORD ); + otherChannels[ i ]->SetMode( (ChannelMode_t)otherChannelsMode ); + } + } + + c = hiddenChannels.Count(); + if ( c > 0 ) + { + // Don't create undo records for this + CDisableUndoScopeGuard guardRecord; + + // For now these should only be able to go into Playback + //Assert( (ChannelMode_t)hiddenChannelsMode == CM_PLAY ); + + for ( i = 0; i < c; ++i ) + { + hiddenChannels[ i ]->SetMode( (ChannelMode_t)hiddenChannelsMode ); + } + } +} + +static const char *s_pDefaultAttributeName[ANIM_CONTROL_COUNT] = +{ + "defaultValue", "defaultBalance", "defaultMultilevel" +}; + +void CBaseAnimSetAttributeSliderPanel::SetupForPreset( FaderPreview_t &fader, int nChangeFlags ) +{ + // Nothing special here +} + +float CBaseAnimSetAttributeSliderPanel::GetBalanceSliderValue() +{ + return m_pPresetSideFilter->GetPos(); +} + +void CBaseAnimSetAttributeSliderPanel::SetTimeSelectionParametersForRecordingChannels( float flIntensity ) +{ + CBaseAnimSetPresetFaderPanel *pPresets = m_hEditor->GetPresetFader(); + if ( !pPresets ) + { + Assert( 0 ); + return; + } + + FaderPreview_t fader; + pPresets->GetPreviewFader( fader ); + + SetupForPreset( fader, m_nFaderChangeFlags ); + + int c = g_pChannelRecordingMgr->GetLayerRecordingChannelCount(); + for ( int i = 0; i < c; ++i ) + { + CDmeChannel *ch = g_pChannelRecordingMgr->GetLayerRecordingChannel( i ); + if ( !ch ) + continue; + + CDmAttribute *pPreset = NULL; + + ChannelToSliderLookup_t search; + search.ch = ch; + unsigned int idx = m_ChannelToSliderLookup.Find( search ); + if ( idx != m_ChannelToSliderLookup.InvalidIndex() && fader.values ) + { + ChannelToSliderLookup_t &item = m_ChannelToSliderLookup[ idx ]; + + CDmElement *pControl = item.slider; + Assert( pControl ); + + int faderIndex = fader.values->Find( pControl->GetName() ); + if ( faderIndex != fader.values->InvalidIndex() ) + { + pPreset = (*fader.values)[ faderIndex ].m_pAttribute[ item.type ]; + } + if ( !pPreset ) + { + pPreset = pControl->GetAttribute( s_pDefaultAttributeName[item.type] ); + } + } + + g_pChannelRecordingMgr->SetPresetValue( ch, pPreset ); + } +} + +static bool CanPreviewType( DmAttributeType_t attType ) +{ + switch ( attType ) + { + default: + return false; + case AT_FLOAT: + case AT_VECTOR3: + case AT_QUATERNION: + break; + } + return true; +} + +void CBaseAnimSetAttributeSliderPanel::MaybeAddPreviewLog( CDmeFilmClip *shot, CUtlVector< LogPreview_t >& list, CDmElement *control, bool bDragging, bool isActiveLog, bool bSelected ) +{ + CDmeChannel *ctrlChannels[ LOG_PREVIEW_MAX_CHANNEL_COUNT ]; + GetChannelsForControl( control, ctrlChannels ); + + LogPreview_t preview; + + preview.m_bDragging = bDragging; + preview.m_bActiveLog = isActiveLog; + preview.m_bSelected = bSelected; + + for ( int channel = 0; channel < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++channel ) + { + CDmeChannel *ch = ctrlChannels[ channel ]; + if ( !ch ) + continue; + + CDmeLog *log = ch->GetLog(); + if ( !log ) + continue; + + DmAttributeType_t attType = log->GetDataType(); + if ( !CanPreviewType( attType ) ) + continue; + + Assert( control ); + + preview.m_hControl = control; + preview.m_hShot = shot; + preview.m_hChannels[ channel ] = ch; + preview.m_hOwner = ch->FindOwnerClipForChannel( shot ); + } + + list.AddToTail( preview ); +} + +void CBaseAnimSetAttributeSliderPanel::RecomputePreview() +{ + float curtime = system()->GetFrameTime(); + m_flRecomputePreviewTime = curtime + RECOMPUTE_PREVIEW_INTERVAL; + m_bRequestedNewPreview = true; +} + +CDmeFilmClip *CBaseAnimSetAttributeSliderPanel::GetCurrentShot() +{ + return NULL; +} + +CDmeFilmClip *CBaseAnimSetAttributeSliderPanel::GetCurrentMovie() +{ + return NULL; +} + +void CBaseAnimSetAttributeSliderPanel::PerformRecomputePreview() +{ + m_CurrentPreview.RemoveAll(); + if ( !m_bRequestedNewPreview ) + return; + +#if 0 + // Tracker 54528: While this saves recomputing things all of the time, the delay is annoying so + // we'll turn it off for now + float curtime = system()->GetFrameTime(); + if ( curtime < m_flRecomputePreviewTime ) + return; +#endif + + m_bRequestedNewPreview = false; + m_flRecomputePreviewTime = -1.0f; + // list of bones/root transforms which are in the control set + m_ActiveTransforms.Purge(); + if ( !m_AnimSet.Get() ) + return; + + CDmeFilmClip *shot = GetCurrentShot(); + CDmElement *previewControl = GetLogPreviewControl(); + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + int c = m_SliderList.Count(); + int i; + for ( i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + if ( !slider->IsVisible() ) + continue; + CDmElement *control = controls[ i ]; + + MaybeAddPreviewLog( shot, m_ActiveTransforms, control, slider->IsDragging(), false, slider->IsSelected() ); + MaybeAddPreviewLog( shot, m_CurrentPreview, control, slider->IsDragging(), ( control == previewControl ), slider->IsSelected() ); + } +} + +CUtlVector< LogPreview_t >* CBaseAnimSetAttributeSliderPanel::GetActiveTransforms() +{ + return &m_ActiveTransforms; +} + +CDmElement *CBaseAnimSetAttributeSliderPanel::GetElementFromSlider( CAttributeSlider *pSlider ) +{ + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + int c = m_SliderList.Count(); + int i; + for ( i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + if ( slider != pSlider ) + continue; + + CDmElement *control = controls[ i ]; + return control; + } + return NULL; +} + +CDmElement *CBaseAnimSetAttributeSliderPanel::GetLogPreviewControl() +{ + return m_PreviewControl.Get(); +} + +void CBaseAnimSetAttributeSliderPanel::SetLogPreviewControlFromSlider( CAttributeSlider *pSlider ) +{ + CDmElement *control = GetElementFromSlider( pSlider ); + // Note can be NULL + SetLogPreviewControl( control ); +} + +void CBaseAnimSetAttributeSliderPanel::SetLogPreviewControl( CDmElement *control ) +{ + if ( !m_hEditor.Get() ) + return; + + bool changed = m_PreviewControl != control ? true : false; + m_PreviewControl = control; + if ( changed ) + { + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + int itemNumber = 0; + int nSliders = m_SliderList.Count(); + for ( int i = 0; i < nSliders; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + slider->SetIsLogPreviewControl( false ); + if ( !slider->IsVisible() ) + continue; + + CDmElement *c = controls[ i ]; + if ( c == control ) + { + slider->SetIsLogPreviewControl( true ); + slider->RequestFocus(); + m_Sliders->ScrollToItem( itemNumber ); + } + + ++itemNumber; + } + + RecomputePreview(); + } +} + + +void CBaseAnimSetAttributeSliderPanel::GetVisibleControls( CUtlVector< VisItem_t>& list ) +{ + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + int i, c; + c = m_SliderList.Count(); + for ( i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + if ( !slider->IsVisible() ) + continue; + + CDmElement *ctrl = controls[ i ]; + if ( !ctrl ) + continue; + + VisItem_t item; + item.element = ctrl; + item.selected = slider->IsSelected(); + item.index = i; + + list.AddToTail( item ); + } +} + +int CBaseAnimSetAttributeSliderPanel::BuildVisibleControlList( CUtlVector< LogPreview_t >& list ) +{ + if ( !m_AnimSet.Get() ) + return 0; + + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + int i, c; + c = m_SliderList.Count(); + for ( i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + if ( !slider->IsVisible() ) + continue; + + CDmElement *ctrl = controls[ i ]; + if ( !ctrl ) + continue; + + MaybeAddPreviewLog( NULL, list, ctrl, slider->IsDragging(), false, slider->IsSelected() ); + } + + return list.Count(); +} + +int CBaseAnimSetAttributeSliderPanel::BuildFullControlList( CUtlVector< LogPreview_t >& list ) +{ + if ( !m_AnimSet.Get() ) + return 0; + + const CDmaElementArray< CDmElement > &controls = m_AnimSet->GetControls(); + + int i, c; + c = m_SliderList.Count(); + for ( i = 0; i < c; ++i ) + { + CAttributeSlider *slider = m_SliderList[ i ]; + CDmElement *ctrl = controls[ i ]; + if ( !ctrl ) + continue; + + MaybeAddPreviewLog( NULL, list, ctrl, slider->IsDragging(), false, slider->IsSelected() ); + } + + return list.Count(); +} + + + + +void SpewLayer( const char *desc, CDmeTypedLogLayer< float > *l ) +{ + Msg( "%s\n", desc ); + + int c = l->GetKeyCount(); + for ( int i = 0; i < c; ++i ) + { + DmeTime_t kt = l->GetKeyTime( i ); + float v = l->GetKeyValue( i ); + + Msg( "%d %.3f = %f\n", i, kt.GetSeconds(), v ); + + if ( i > 20 ) + break; + + } +} + +void CBaseAnimSetAttributeSliderPanel::MoveToSlider( CAttributeSlider *pCurrentSlider, int nDirection ) +{ + Assert( pCurrentSlider ); + if ( !pCurrentSlider ) + return; + + // Find current slider index and then move to next / previous one + CUtlVector< CAttributeSlider * > visible; + + int c = m_SliderList.Count(); + if ( c <= 1 ) + return; + + int i; + for ( i = 0; i < c; ++i ) + { + if ( !m_SliderList[ i ]->IsVisible() ) + continue; + visible.AddToTail( m_SliderList[ i ] ); + } + + c = visible.Count(); + + if ( c <= 1 ) + return; + + for ( i = 0; i < c; ++i ) + { + if ( visible[ i ] != pCurrentSlider ) + continue; + + Msg( "Found slider at %d (old %s)\n", i, pCurrentSlider->GetName() ); + + i += nDirection; + if ( i < 0 ) + { + i = c - 1; + } + else if ( i >= c ) + { + i = 0; + } + + Msg( "Change to slider %d %s\n", i, visible[ i ]->GetName() ); + + // m_SliderList[ i ]->RequestFocus(); + SetLogPreviewControl( visible[ i ]->GetControl() ); + break; + } +} + +void CBaseAnimSetAttributeSliderPanel::OnKBDeselectAll() +{ + ClearSelectedControls(); +} + +void CBaseAnimSetAttributeSliderPanel::ClearSelectedControls() +{ + int c = m_SliderList.Count(); + int i; + for ( i = 0; i < c; ++i ) + { + m_SliderList[ i ]->SetSelected( false ); + } + RecomputePreview(); +} + +void CBaseAnimSetAttributeSliderPanel::SetControlSelected( CAttributeSlider *slider, bool state ) +{ + slider->SetSelected( state); + RecomputePreview(); +} + +void CBaseAnimSetAttributeSliderPanel::SetControlSelected( CDmElement *control, bool state ) +{ + CAttributeSlider *slider = FindSliderForControl( control ); + if ( !slider ) + return; + SetControlSelected( slider, state ); +} + +CAttributeSlider *CBaseAnimSetAttributeSliderPanel::FindSliderForControl( CDmElement *control ) +{ + int c = m_SliderList.Count(); + int i; + for ( i = 0; i < c; ++i ) + { + if ( m_SliderList[ i ]->GetControl() == control ) + return m_SliderList[ i ]; + } + + return NULL; +} + +bool CBaseAnimSetAttributeSliderPanel::GetSliderValues( AttributeValue_t *pValue, int nIndex ) +{ + Assert( pValue ); + Assert( nIndex >= 0 && nIndex < m_SliderList.Count() ); + + CAttributeSlider *pSlider = m_SliderList[ nIndex ]; + bool bForce = m_Previous.isbeingdragged; + bool bGetPreview = ( pSlider->IsPreviewEnabled() && ( !pSlider->IsSimplePreview() || bForce ) ); + *pValue = bGetPreview ? pSlider->GetPreview() : pSlider->GetValue(); + return pSlider->IsVisible(); +}
\ No newline at end of file diff --git a/vgui2/dme_controls/BaseAnimSetControlGroupPanel.cpp b/vgui2/dme_controls/BaseAnimSetControlGroupPanel.cpp new file mode 100644 index 0000000..abbafef --- /dev/null +++ b/vgui2/dme_controls/BaseAnimSetControlGroupPanel.cpp @@ -0,0 +1,523 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= +#include "dme_controls/BaseAnimSetControlGroupPanel.h" +#include "vgui_controls/TreeView.h" +#include "vgui_controls/Menu.h" +#include "tier1/KeyValues.h" +#include "movieobjects/dmeanimationset.h" +#include "dme_controls/BaseAnimSetAttributeSliderPanel.h" +#include "dme_controls/BaseAnimationSetEditor.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; + + +//----------------------------------------------------------------------------- +// Shows the tree view of the animation groups +//----------------------------------------------------------------------------- +class CAnimGroupTree : public TreeView +{ + DECLARE_CLASS_SIMPLE( CAnimGroupTree, TreeView ); +public: + CAnimGroupTree( Panel *parent, const char *panelName, CBaseAnimSetControlGroupPanel *groupPanel ); + virtual ~CAnimGroupTree(); + + virtual bool IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ); + virtual void OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ); + virtual void GenerateContextMenu( int itemIndex, int x, int y ); + +private: + MESSAGE_FUNC( OnImportAnimation, "ImportAnimation" ); + + void CleanupContextMenu(); + + vgui::DHANDLE< vgui::Menu > m_hContextMenu; + CBaseAnimSetControlGroupPanel *m_pGroupPanel; +}; + +CAnimGroupTree::CAnimGroupTree( Panel *parent, const char *panelName, CBaseAnimSetControlGroupPanel *groupPanel ) : + BaseClass( parent, panelName ), + m_pGroupPanel( groupPanel ) +{ +} + +CAnimGroupTree::~CAnimGroupTree() +{ + CleanupContextMenu(); +} + +void CAnimGroupTree::CleanupContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + delete m_hContextMenu.Get(); + m_hContextMenu = NULL; + } +} + +bool CAnimGroupTree::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + if ( msglist.Count() != 1 ) + return false; + + KeyValues *data = msglist[ 0 ]; + if ( !data ) + return false; + + if ( !data->FindKey( "color" ) ) + return false; + + KeyValues *itemData = GetItemData( itemIndex ); + if ( !itemData->FindKey( "handle" ) ) + return false; + DmElementHandle_t handle = (DmElementHandle_t)itemData->GetInt( "handle" ); + if ( handle == DMELEMENT_HANDLE_INVALID ) + return false; + + return true; +} + +void CAnimGroupTree::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + if ( !IsItemDroppable( itemIndex, msglist ) ) + return; + + KeyValues *data = msglist[ 0 ]; + if ( !data ) + return; + + KeyValues *itemData = GetItemData( itemIndex ); + + CDmElement *group = GetElementKeyValue< CDmElement >( itemData, "handle" ); + Assert( m_pGroupPanel ); + Color clr = data->GetColor( "color" ); + SetItemFgColor( itemIndex, clr ); + SetItemSelectionTextColor( itemIndex, clr ); + + if ( group ) + { + CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Change Group Color" ); + group->SetValue< Color >( "treeColor", clr ); + } +} + +void CAnimGroupTree::OnImportAnimation() +{ + PostMessage( m_pGroupPanel->m_hEditor, new KeyValues( "ImportAnimation", "visibleOnly", "1" ), 0.0f ); +} + +// override to open a custom context menu on a node being selected and right-clicked +void CAnimGroupTree::GenerateContextMenu( int itemIndex, int x, int y ) +{ + CleanupContextMenu(); + m_hContextMenu = new Menu( this, "ActionMenu" ); + m_hContextMenu->AddMenuItem( "#ImportAnimation", new KeyValues( "ImportAnimation" ), this ); + Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + + +CBaseAnimSetControlGroupPanel::CBaseAnimSetControlGroupPanel( vgui::Panel *parent, const char *className, CBaseAnimationSetEditor *editor ) : + BaseClass( parent, className ), + m_bStartItemWasSelected( false ), + m_SliderNames( 0, 0, true ) +{ + m_hEditor = editor; + m_hGroups = new CAnimGroupTree( this, "AnimSetGroups", this ); + m_hGroups->SetMultipleItemDragEnabled( true ); + m_hGroups->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 0, + 0, 0 + ); + m_hGroups->SetAllowMultipleSelections( true ); +} + +CBaseAnimSetControlGroupPanel::~CBaseAnimSetControlGroupPanel() +{ +} + +static int AddItemToTree( TreeView *tv, const char *label, int parentIndex, const Color& fg, int groupNumber, int handle ) +{ + Color bgColor( 128, 128, 128, 128 ); + + KeyValues *kv = new KeyValues( "item", "text", label ); + kv->SetInt( "groupNumber", groupNumber ); + kv->SetInt( "droppable", 1 ); + kv->SetInt( "handle", handle ); + int idx = tv->AddItem( kv, parentIndex ); + tv->SetItemFgColor( idx, fg ); + tv->SetItemSelectionTextColor( idx, fg ); + tv->SetItemSelectionBgColor( idx, bgColor ); + tv->SetItemSelectionUnfocusedBgColor( idx, bgColor ); + + tv->RemoveSelectedItem( idx ); + tv->ExpandItem( idx, false ); + + kv->deleteThis(); + + return idx; +} + +void CBaseAnimSetControlGroupPanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + m_hGroups->SetFont( pScheme->GetFont( "DefaultBold", IsProportional() ) ); +} + +void CBaseAnimSetControlGroupPanel::OnTreeViewItemSelectionCleared() +{ + // We check the entire group manually + OnTreeViewItemSelected( -1 ); +} + +void CBaseAnimSetControlGroupPanel::OnTreeViewItemDeselected( int itemIndex ) +{ + OnTreeViewItemSelected( -1 ); +} + +void CBaseAnimSetControlGroupPanel::OnTreeViewItemSelected( int itemIndex ) +{ + if ( !m_AnimSet.Get() ) + return; + + // Build the list of selected groups, and notify the attribute slider panel + CUtlVector< int > selection; + m_hGroups->GetSelectedItems( selection ); + + const CDmaElementArray<> &groups = m_AnimSet->GetSelectionGroups(); + int groupCount = groups.Count(); + + int i; + int rootIndex = m_hGroups->GetRootItemIndex(); + + bool selectionHasRoot = false; + for ( i = 0 ; i < selection.Count(); ++i ) + { + if ( selection[ i ] == rootIndex ) + { + selectionHasRoot = true; + break; + } + } + + m_SliderNames.RemoveAll(); + + if ( selectionHasRoot ) + { + for ( i = 0; i < groups.Count(); ++i ) + { + CDmElement *element = groups[ i ]; + if ( !element ) + continue; + + const CDmrStringArray array( element, "selectedControls" ); + if ( array.IsValid() ) + { + for ( int j = 0 ; j < array.Count(); ++j ) + { + const char *sliderName = array[ j ]; + if ( sliderName && *sliderName ) + { + m_SliderNames.AddString( sliderName ); + } + } + } + } + } + else + { + for ( i = 0 ; i < selection.Count(); ++i ) + { + if ( selection[ i ] == rootIndex ) + continue; + + KeyValues *kv = m_hGroups->GetItemData( selection[ i ] ); + if ( !kv ) + continue; + + int groupNumber = kv->GetInt( "groupNumber" ); + if ( groupNumber < 0 || groupNumber >= groupCount ) + { + const char *sliderName = kv->GetString( "text" ); + if ( sliderName && *sliderName ) + { + m_SliderNames.AddString( sliderName ); + } + continue; + } + + CDmElement *element = groups[ groupNumber ]; + if ( !element ) + continue; + + const CDmrStringArray array( element, "selectedControls" ); + if ( array.IsValid() ) + { + for ( int j = 0 ; j < array.Count(); ++j ) + { + const char *sliderName = array[ j ]; + if ( sliderName && *sliderName ) + { + m_SliderNames.AddString( sliderName ); + } + } + } + } + } + + // now notify the attribute slider panel + CBaseAnimSetAttributeSliderPanel *attSliders = m_hEditor->GetAttributeSlider(); + if ( attSliders ) + { + attSliders->SetVisibleControlsForSelectionGroup( m_SliderNames ); + } +} + +void CBaseAnimSetControlGroupPanel::ChangeAnimationSet( CDmeAnimationSet *newAnimSet ) +{ + bool changed = m_AnimSet.Get() != newAnimSet ? true : false; + + m_AnimSet = newAnimSet; + + if ( !m_AnimSet.Get() ) + { + m_hGroups->RemoveAll(); + m_hSelectableIndices.RemoveAll(); + m_GroupList.RemoveAll(); + return; + } + + // Compare groups + bool bRebuildGroups = false; + const CDmaElementArray< CDmElement > &groups = m_AnimSet->GetSelectionGroups(); + int c = groups.Count(); + if ( c != m_GroupList.Count() ) + { + bRebuildGroups = true; + } + else + { + for ( int i = 0; i < c; ++i ) + { + CDmElement *group = groups[ i ]; + if ( group == m_GroupList[ i ].Get() ) + { + continue; + } + + bRebuildGroups = true; + break; + } + } + + if ( bRebuildGroups ) + { + m_hGroups->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultBold", IsProportional() ) ); + + // Build a tree of every open item in the tree view + OpenItemTree_t openItems; + int nRootIndex = m_hGroups->GetRootItemIndex(); + if ( nRootIndex != -1 ) + { + BuildOpenItemList( openItems, openItems.InvalidIndex(), nRootIndex ); + } + + m_hGroups->RemoveAll(); + m_hSelectableIndices.RemoveAll(); + m_GroupList.RemoveAll(); + + + // Create root + int rootIndex = AddItemToTree( m_hGroups, "root", -1, Color( 128, 128, 128, 255 ), -1, (int)DMELEMENT_HANDLE_INVALID ); + + Color defaultColor( 0, 128, 255, 255 ); + + CAppUndoScopeGuard *guard = NULL; + + for ( int i = 0; i < c; ++i ) + { + CDmElement *group = groups[ i ]; + + if ( !group->HasAttribute( "treeColor" ) ) + { + if ( !guard ) + { + guard = new CAppUndoScopeGuard( NOTIFY_SETDIRTYFLAG, "Set Default Colors" ); + } + group->SetValue< Color >( "treeColor", defaultColor ); + } + int groupIndex = AddItemToTree( m_hGroups, group->GetName(), rootIndex, group->GetValue< Color >( "treeColor" ), i, (int)group->GetHandle() ); + + const CDmrStringArray array( group, "selectedControls" ); + if ( array.IsValid() ) + { + for ( int j = 0 ; j < array.Count(); ++j ) + { + AddItemToTree( m_hGroups, array[ j ], groupIndex, Color( 200, 200, 200, 255 ), -1, (int)DMELEMENT_HANDLE_INVALID ); + } + } + m_hSelectableIndices.AddToTail( groupIndex ); + + m_GroupList.AddToTail( group->GetHandle() ); + } + + if ( ( nRootIndex >= 0 ) && ( rootIndex >= 0 ) && !changed ) + { + // Iterate through all previously open items and expand them if they exist + if ( openItems.Root() != openItems.InvalidIndex() ) + { + ExpandOpenItems( openItems, openItems.Root(), rootIndex, true ); + } + } + else + { + m_hGroups->ExpandItem( rootIndex, true ); + } + + if ( guard ) + { + delete guard; + } + } + + if ( changed ) + { + for ( int i = 0; i < m_hSelectableIndices.Count(); ++i ) + { + + m_hGroups->AddSelectedItem( m_hSelectableIndices[ i ], + false, // don't clear selection + true, // put focus on tree + false ); // don't expand tree to make all of these visible... + } + } +} + + +//----------------------------------------------------------------------------- +// Expands all items in the open item tree if they exist +//----------------------------------------------------------------------------- +void CBaseAnimSetControlGroupPanel::ExpandOpenItems( OpenItemTree_t &tree, int nOpenTreeIndex, int nItemIndex, bool makeVisible ) +{ + int i = tree.FirstChild( nOpenTreeIndex ); + if ( nOpenTreeIndex != tree.InvalidIndex() ) + { + TreeInfo_t& info = tree[ nOpenTreeIndex ]; + if ( info.m_nFlags & EP_EXPANDED ) + { + // Expand the item + m_hGroups->ExpandItem( nItemIndex , true ); + } + if ( info.m_nFlags & EP_SELECTED ) + { + m_hGroups->AddSelectedItem( nItemIndex, false, false ); + if ( makeVisible ) + { + m_hGroups->MakeItemVisible( nItemIndex ); + } + } + } + + while ( i != tree.InvalidIndex() ) + { + TreeInfo_t& info = tree[ i ]; + // Look for a match + int nChildIndex = FindTreeItem( nItemIndex, info.m_Item ); + if ( nChildIndex != -1 ) + { + ExpandOpenItems( tree, i, nChildIndex, makeVisible ); + } + else + { + if ( info.m_nFlags & EP_SELECTED ) + { + // Look for preserved item + nChildIndex = FindTreeItem( nItemIndex, info.m_Item ); + if ( nChildIndex != -1 ) + { + m_hGroups->AddSelectedItem( nChildIndex, false, false ); + if ( makeVisible ) + { + m_hGroups->MakeItemVisible( nChildIndex ); + } + } + } + } + i = tree.NextSibling( i ); + } +} + +void CBaseAnimSetControlGroupPanel::FillInDataForItem( TreeItem_t &item, int nItemIndex ) +{ + KeyValues *data = m_hGroups->GetItemData( nItemIndex ); + if ( !data ) + return; + + item.m_pAttributeName = data->GetString( "text" ); +} + +//----------------------------------------------------------------------------- +// Builds a list of open items +//----------------------------------------------------------------------------- +void CBaseAnimSetControlGroupPanel::BuildOpenItemList( OpenItemTree_t &tree, int nParent, int nItemIndex ) +{ + KeyValues *data = m_hGroups->GetItemData( nItemIndex ); + if ( !data ) + return; + + bool expanded = m_hGroups->IsItemExpanded( nItemIndex ); + bool selected = m_hGroups->IsItemSelected( nItemIndex ); + + int flags = 0; + if ( expanded ) + { + flags |= EP_EXPANDED; + } + if ( selected ) + { + flags |= EP_SELECTED; + } + + int nChild = tree.InsertChildAfter( nParent, tree.InvalidIndex() ); + TreeInfo_t &info = tree[nChild]; + FillInDataForItem( info.m_Item, nItemIndex ); + info.m_nFlags = flags; + + // Deal with children + int nCount = m_hGroups->GetNumChildren( nItemIndex ); + for ( int i = 0; i < nCount; ++i ) + { + int nChildIndex = m_hGroups->GetChild( nItemIndex, i ); + BuildOpenItemList( tree, nChild, nChildIndex ); + } +} +//----------------------------------------------------------------------------- +// Finds the tree index of a child matching the particular element + attribute +//----------------------------------------------------------------------------- +int CBaseAnimSetControlGroupPanel::FindTreeItem( int nParentIndex, const TreeItem_t &info ) +{ + // Look for a match + int nCount = m_hGroups->GetNumChildren( nParentIndex ); + for ( int i = nCount; --i >= 0; ) + { + int nChildIndex = m_hGroups->GetChild( nParentIndex, i ); + KeyValues *data = m_hGroups->GetItemData( nChildIndex ); + Assert( data ); + + const char *pAttributeName = data->GetString( "text" ); + if ( !Q_stricmp( pAttributeName, info.m_pAttributeName ) ) + { + return nChildIndex; + } + } + return -1; +} diff --git a/vgui2/dme_controls/BaseAnimSetPresetFaderPanel.cpp b/vgui2/dme_controls/BaseAnimSetPresetFaderPanel.cpp new file mode 100644 index 0000000..13d97cf --- /dev/null +++ b/vgui2/dme_controls/BaseAnimSetPresetFaderPanel.cpp @@ -0,0 +1,1504 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= +#include "dme_controls/BaseAnimSetPresetFaderPanel.h" +#include "dme_controls/DmePresetGroupEditorPanel.h" +#include "vgui_controls/InputDialog.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Slider.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/TextImage.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/PanelListPanel.h" +#include "movieobjects/dmeanimationset.h" +#include "tier1/KeyValues.h" +#include "dme_controls/dmecontrols_utils.h" +#include "vstdlib/random.h" +#include "vgui/IInput.h" +#include "vgui/ISurface.h" +#include "dme_controls/BaseAnimSetAttributeSliderPanel.h" +#include "dme_controls/BaseAnimationSetEditor.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +float ifm_fader_timescale = 5.0f; + +class CPresetSlider; + + +//----------------------------------------------------------------------------- +// Purpose: Utility dialog, used to let user type in some text +//----------------------------------------------------------------------------- +class CAddPresetDialog : public vgui::BaseInputDialog +{ + DECLARE_CLASS_SIMPLE( CAddPresetDialog, vgui::BaseInputDialog ); + +public: + CAddPresetDialog( vgui::Panel *parent ); + + void DoModal( CDmeAnimationSet *pAnimationSet, KeyValues *pContextKeyValues = NULL ); + +protected: + // command buttons + virtual void OnCommand(const char *command); + +private: + vgui::TextEntry *m_pInput; + vgui::ComboBox *m_pPresetGroup; +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAddPresetDialog::CAddPresetDialog( vgui::Panel *parent ) : BaseClass( parent, "Enter Preset Name" ) +{ + m_pInput = new TextEntry( this, "PresetName" ); + m_pPresetGroup = new vgui::ComboBox( this, "PresetGroup", 8, true ); + SetDeleteSelfOnClose( false ); + + LoadControlSettings( "resource/addpresetdialog.res" ); +} + + +void CAddPresetDialog::DoModal( CDmeAnimationSet *pAnimationSet, KeyValues *pContextKeyValues ) +{ + int nTextLength = m_pInput->GetTextLength() + 1; + char* pCurrentGroupName = (char*)_alloca( nTextLength * sizeof(char) ); + m_pInput->GetText( pCurrentGroupName, nTextLength ); + + m_pPresetGroup->DeleteAllItems(); + + // Populate the combo box with preset group names + CDmrElementArray< CDmePresetGroup > presetGroupList = pAnimationSet->GetPresetGroups(); + int nCount = presetGroupList.Count(); + for ( int i = 0; i < nCount; ++i ) + { + CDmePresetGroup *pPresetGroup = presetGroupList[i]; + if ( pPresetGroup->m_bIsReadOnly ) + continue; + + KeyValues *kv = new KeyValues( "entry" ); + SetElementKeyValue( kv, "presetGroup", pPresetGroup ); + int nItemID = m_pPresetGroup->AddItem( pPresetGroup->GetName(), kv ); + if ( pCurrentGroupName && !Q_stricmp( pPresetGroup->GetName(), pCurrentGroupName ) ) + { + m_pPresetGroup->ActivateItem( nItemID ); + } + } + + BaseClass::DoModal( pContextKeyValues ); + + m_pInput->SetText( "" ); + m_pInput->RequestFocus(); + + PlaceUnderCursor( ); +} + + +//----------------------------------------------------------------------------- +// command handler +//----------------------------------------------------------------------------- +void CAddPresetDialog::OnCommand( const char *command ) +{ + if ( !Q_stricmp( command, "OK" ) ) + { + int nTextLength = m_pInput->GetTextLength() + 1; + char* txt = (char*)_alloca( nTextLength * sizeof(char) ); + m_pInput->GetText( txt, nTextLength ); + + nTextLength = m_pPresetGroup->GetTextLength() + 1; + char* pPresetGroupName = (char*)_alloca( nTextLength * sizeof(char) ); + m_pPresetGroup->GetText( pPresetGroupName, nTextLength ); + + KeyValues *pCurrentGroup = m_pPresetGroup->GetActiveItemUserData(); + CDmePresetGroup *pPresetGroup = pCurrentGroup ? GetElementKeyValue<CDmePresetGroup>( pCurrentGroup, "presetGroup" ) : NULL; + if ( pPresetGroup && Q_stricmp( pPresetGroup->GetName(), pPresetGroupName ) ) + { + pPresetGroup = NULL; + } + KeyValues *kv = new KeyValues( "PresetNameSelected", "text", txt ); + kv->SetString( "presetGroupName", pPresetGroupName ); + SetElementKeyValue( kv, "presetGroup", pPresetGroup ); + + if ( m_pContextKeyValues ) + { + kv->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + PostActionSignal( kv ); + CloseModal(); + return; + } + + if ( !Q_stricmp( command, "Cancel") ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( command ); +} + + + +//----------------------------------------------------------------------------- +// +// CPresetSliderEdgeButton: The buttons that lie on either side of the PresetSlider +// +//----------------------------------------------------------------------------- +class CPresetSliderEdgeButton : public Button +{ + DECLARE_CLASS_SIMPLE( CPresetSliderEdgeButton, Button ); +public: + CPresetSliderEdgeButton( CPresetSlider *parent, const char *panelName, const char *text ); + +private: + virtual void OnCursorMoved(int x, int y); + virtual void OnMousePressed( vgui::MouseCode code ); + virtual void OnMouseReleased( vgui::MouseCode code ); + virtual void OnMouseDoublePressed( vgui::MouseCode code ); + + CPresetSlider *m_pSlider; +}; + + +//----------------------------------------------------------------------------- +// +// CPresetSlider: The actual preset slider itself! +// +//----------------------------------------------------------------------------- +class CPresetSlider : public Slider +{ + DECLARE_CLASS_SIMPLE( CPresetSlider, Slider ); + +public: + + friend class CPresetSliderEdgeButton; + + CPresetSlider( CBaseAnimSetPresetFaderPanel *parent, const char *panelName, CDmePreset *pPreset ); + ~CPresetSlider(); + + void SetControlValues( ); + + void SetGradientColor( const Color& clr ); + + float GetCurrent(); + void SetPos( float frac ); + + AttributeDict_t *GetAttributeDict(); + + bool IsPreviewSlider(); + bool IsDragging(); + + void UpdateProceduralValues(); + + CDmePreset *GetPreset(); + +protected: + + virtual void Paint(); + virtual void PaintBackground(); + virtual void ApplySchemeSettings( IScheme *scheme ); + virtual void GetTrackRect( int &x, int &y, int &w, int &h ); + virtual void PerformLayout(); + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + virtual void OnKeyCodeTyped( KeyCode code ); + virtual void OnCursorMoved(int x, int y); + + MESSAGE_FUNC( OnShowContextMenu, "OnShowContextMenu" ); + MESSAGE_FUNC( OnRename, "OnRename" ); + MESSAGE_FUNC( OnDelete, "OnDelete" ); + + MESSAGE_FUNC( OnOverwrite, "OnOverwrite" ); + + MESSAGE_FUNC_PARAMS( OnInputCompleted, "InputCompleted", params ); + + MESSAGE_FUNC( OnDeleteConfirmed, "OnDeleteConfirmed" ); + MESSAGE_FUNC( OnOverwriteConfirmed, "OnOverwriteConfirmed" ); + +private: + void OnRenameCompleted( const char *pText, KeyValues *pContextKeyValues ); + + void OnDragCompleted( float flValue ); + void UpdateTickPos( int x, int y ); + + CBaseAnimSetPresetFaderPanel *m_pParent; + + Color m_GradientColor; + Color m_ZeroColor; + Color m_TextColor; + Color m_TextColorFocus; + TextImage *m_pName; + float m_flCurrent; + + bool m_bSuppressCompletion; + + CPresetSliderEdgeButton *m_pEdgeButtons[ 2 ]; + + vgui::DHANDLE< vgui::Menu > m_hContextMenu; + vgui::DHANDLE< vgui::InputDialog > m_hInputDialog; + + AttributeDict_t m_AttributeLookup; + + vgui::DHANDLE< MessageBox > m_hConfirm; + CDmeHandle< CDmePreset > m_hSelf; + + static bool s_bResetMousePosOnMouseUp; + static int s_nMousePosX; + static int s_nMousePosY; +}; + + +//----------------------------------------------------------------------------- +// +// CPresetSliderEdgeButton: The buttons that lie on either side of the PresetSlider +// +//----------------------------------------------------------------------------- +CPresetSliderEdgeButton::CPresetSliderEdgeButton( CPresetSlider *parent, const char *panelName, const char *text ) : + BaseClass( (Panel *)parent, panelName, text ), m_pSlider( parent ) +{ + SetPaintBorderEnabled( false ); +} + +void CPresetSliderEdgeButton::OnCursorMoved(int x, int y) +{ + LocalToScreen( x, y ); + m_pSlider->ScreenToLocal( x, y ); + m_pSlider->OnCursorMoved( x, y ); +} + +void CPresetSliderEdgeButton::OnMousePressed( vgui::MouseCode code ) +{ + BaseClass::OnMousePressed( code ); + PostMessage( m_pSlider->GetVPanel(), new KeyValues( "MousePressed", "code", code ) ); + PostMessage( m_pSlider->GetVPanel(), new KeyValues( "MouseReleased", "code", code ), 0.001f ); +} + +void CPresetSliderEdgeButton::OnMouseReleased( vgui::MouseCode code ) +{ + BaseClass::OnMouseReleased( code ); + PostMessage( m_pSlider->GetVPanel(), new KeyValues( "MouseReleased", "code", code ) ); +} + +void CPresetSliderEdgeButton::OnMouseDoublePressed( vgui::MouseCode code ) +{ + BaseClass::OnMouseDoublePressed( code ); + PostMessage( m_pSlider->GetVPanel(), new KeyValues( "MouseDoublePressed", "code", code ) ); + PostMessage( m_pSlider->GetVPanel(), new KeyValues( "MouseReleased", "code", code ), 0.001f ); +} + + +//----------------------------------------------------------------------------- +// +// CPresetSlider: The actual preset slider itself! +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Static members +//----------------------------------------------------------------------------- +bool CPresetSlider::s_bResetMousePosOnMouseUp = false; +int CPresetSlider::s_nMousePosX; +int CPresetSlider::s_nMousePosY; + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CPresetSlider::CPresetSlider( CBaseAnimSetPresetFaderPanel *parent, const char *panelName, CDmePreset *preset ) : + BaseClass( (Panel *)parent, panelName ), m_pParent( parent ), m_bSuppressCompletion( false ) +{ + Assert( preset ); + m_hSelf = preset; + + SetRange( 0, 1000 ); + SetDragOnRepositionNob( true ); + + SetPaintBackgroundEnabled( true ); + + m_pName = new TextImage( panelName ); + + m_pEdgeButtons[ 0 ] = new CPresetSliderEdgeButton( this, "PresetSliderLeftEdge", "" ); + m_pEdgeButtons[ 1 ] = new CPresetSliderEdgeButton( this, "PresetSliderRightEdge", "" ); + + SetBgColor( Color( 128, 128, 128, 128 ) ); + + m_ZeroColor = Color( 69, 69, 69, 255 ); + m_GradientColor = Color( 194, 120, 0, 255 ); + + m_TextColor = Color( 200, 200, 200, 255 ); + m_TextColorFocus = Color( 208, 143, 40, 255 ); +} + +CPresetSlider::~CPresetSlider() +{ + delete m_pName; +} + +CDmePreset *CPresetSlider::GetPreset() +{ + return m_hSelf; +} + +// #define PRORCEDURAL_PRESET_TIMING + +void CPresetSlider::UpdateProceduralValues() +{ + if ( !m_hSelf->IsProcedural() ) + return; +#if defined( PRORCEDURAL_PRESET_TIMING ) + double st = Plat_FloatTime(); +#endif + // Figure out what we need to do + int nPresetType = m_hSelf->GetProceduralPresetType(); + switch ( nPresetType ) + { + default: + Assert( 0 ); + break; + case PROCEDURAL_PRESET_REVEAL: + case PROCEDURAL_PRESET_PASTE: + case PROCEDURAL_PRESET_JITTER: + case PROCEDURAL_PRESET_SMOOTH: + case PROCEDURAL_PRESET_SHARPEN: + case PROCEDURAL_PRESET_SOFTEN: + case PROCEDURAL_PRESET_STAGGER: + // These are handled elsewhere right now... at some point we'll copy in values at head position in order to do ctrl-key preview mode + break; + case PROCEDURAL_PRESET_IN_CROSSFADE: + { + m_pParent->ProceduralPreset_UpdateCrossfade( m_hSelf, true ); + } + break; + case PROCEDURAL_PRESET_OUT_CROSSFADE: + { + m_pParent->ProceduralPreset_UpdateCrossfade( m_hSelf, false ); + } + break; + } +#if defined( PRORCEDURAL_PRESET_TIMING ) + double ed = Plat_FloatTime(); + Msg( "Update %.3f msec\n", 1000.0 * ( ed - st ) ); +#endif +} + +//----------------------------------------------------------------------------- +// Reads the sliders, sets control values into the attribute dictionary +//----------------------------------------------------------------------------- +void CPresetSlider::SetControlValues( ) +{ + m_AttributeLookup.Purge(); + if ( !m_hSelf.Get() ) + return; + + CDmrElementArray< CDmElement > values = m_hSelf->GetControlValues(); + + int nControlValueCount = values.Count(); + for ( int i = 0; i < nControlValueCount; ++i ) + { + CDmElement *v = values[ i ]; + + AnimationControlAttributes_t val; + val.m_pAttribute[ANIM_CONTROL_VALUE] = v->GetAttribute( "value" ); + val.m_pAttribute[ANIM_CONTROL_BALANCE] = v->GetAttribute( "balance" ); + val.m_pAttribute[ANIM_CONTROL_MULTILEVEL] = v->GetAttribute( "multilevel" ); + val.m_pValue[ANIM_CONTROL_VALUE] = v->GetValue< float >( "value" ); + val.m_pValue[ANIM_CONTROL_BALANCE] = v->GetValue< float >( "balance" ); + val.m_pValue[ANIM_CONTROL_MULTILEVEL] = v->GetValue< float >( "multilevel" ); + + m_AttributeLookup.Insert( v->GetName(), val ); + } +} + + +AttributeDict_t *CPresetSlider::GetAttributeDict() +{ + return &m_AttributeLookup; +} + +void CPresetSlider::OnMouseDoublePressed(MouseCode code) +{ + if ( code != MOUSE_LEFT ) + { + BaseClass::OnMouseDoublePressed( code ); + return; + } +} + +void CPresetSlider::OnMousePressed(MouseCode code) +{ + if ( code == MOUSE_RIGHT ) + return; + + BaseClass::OnMousePressed( code ); + + if ( !_dragging ) + return; + + SetCursor( dc_blank ); + int mx, my; + input()->GetCursorPos( mx, my ); + int tx, ty, tw, th; + GetTrackRect( tx, ty, tw, th ); + + ScreenToLocal( mx, my ); + + // Off right? + bool offright = mx >= ( tx + tw ) ? true : false; + + bool ctrldown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ); + if ( !ctrldown && !offright ) + { + if ( mx >= tx ) + { + Assert( !s_bResetMousePosOnMouseUp ); + s_bResetMousePosOnMouseUp = true; + s_nMousePosX = mx; + s_nMousePosY = my; + LocalToScreen( s_nMousePosX, s_nMousePosY ); + + int offset = mx - tx; + mx -= offset; + LocalToScreen( mx, my ); + input()->SetCursorPos( mx, my ); + } + SetPos( 0 ); + } +} + +void CPresetSlider::OnMouseReleased(MouseCode code) +{ + if ( code == MOUSE_RIGHT ) + { + OnShowContextMenu(); + return; + } + + float flLastValue = GetCurrent(); + bool bWasDragging = _dragging; + BaseClass::OnMouseReleased( code ); + if ( bWasDragging ) + { + OnDragCompleted( flLastValue ); + SetCursor( dc_arrow ); + } + + if( s_bResetMousePosOnMouseUp ) + { + s_bResetMousePosOnMouseUp = false; + input()->SetCursorPos( s_nMousePosX, s_nMousePosY ); + } +} + +void CPresetSlider::OnKeyCodeTyped( KeyCode code ) +{ + if ( code != KEY_ESCAPE || !_dragging ) + { + BaseClass::OnKeyCodeTyped( code ); + return; + } + + m_bSuppressCompletion = true; + OnMouseReleased( MOUSE_LEFT ); + m_bSuppressCompletion = false; + SetCursor( dc_arrow ); +} + +void CPresetSlider::OnDragCompleted( float flValue ) +{ + if ( m_bSuppressCompletion ) + return; + + char sz[ 128 ]; + m_pName->GetText( sz, sizeof( sz ) ); + // Msg( "CPresetSlider slider drag completed %s [%.3f ]\n", sz, flValue ); + // Apply settings to attribute sliders + + m_pParent->ApplyPreset( flValue, m_AttributeLookup ); +} + +void CPresetSlider::OnRename() +{ + if ( m_hInputDialog.Get() ) + { + delete m_hInputDialog.Get(); + } + + m_hInputDialog = new InputDialog( this, "Rename Preset", "Name:", GetName() ); + if ( m_hInputDialog.Get() ) + { + KeyValues *pContextKeyValues = new KeyValues( "RenamePreset" ); + m_hInputDialog->SetSmallCaption( true ); + m_hInputDialog->SetMultiline( false ); + m_hInputDialog->DoModal( pContextKeyValues ); + } + else + { + Assert( 0 ); + } +} + +void CPresetSlider::OnRenameCompleted( const char *pText, KeyValues *pContextKeyValues ) +{ + if ( !pText || !*pText ) + { + Warning( "Can't rename preset for %s to an empty name\n", GetName() ); + return; + } + + // No change( case sensitive) + if ( !Q_strcmp( GetName(), pText ) ) + return; + + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Rename Preset" ); + + SetName( pText ); + m_pName->SetText( pText ); + m_pName->ResizeImageToContent(); + + // + Assert( m_hSelf.Get() ); + if ( m_hSelf.Get() ) + { + m_hSelf->SetName( pText ); + } +} + +void CPresetSlider::OnInputCompleted( KeyValues *pParams ) +{ + const char *pText = pParams->GetString( "text", NULL ); + + KeyValues *pContextKeyValues = pParams->FindKey( "RenamePreset" ); + if ( pContextKeyValues ) + { + OnRenameCompleted( pText, pContextKeyValues ); + return; + } + + Assert( 0 ); +} + +void CPresetSlider::OnDelete() +{ + if ( m_hConfirm.Get() ) + delete m_hConfirm.Get(); + + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "Delete '%s'?", GetName() ); + + m_hConfirm = new MessageBox( "Delete Preset", sz, this ); + Assert( m_hConfirm.Get() ); + if ( m_hConfirm ) + { + m_hConfirm->SetCancelButtonVisible( true ); + m_hConfirm->SetCancelButtonText( "#VGui_Cancel" ); + m_hConfirm->SetCommand( new KeyValues( "OnDeleteConfirmed" ) ); + m_hConfirm->AddActionSignalTarget( this ); + m_hConfirm->DoModal(); + } +} + +void CPresetSlider::OnDeleteConfirmed() +{ + m_pParent->OnDeletePreset( m_hSelf.Get() ); +} + +void CPresetSlider::OnOverwrite() +{ + if ( m_hConfirm.Get() ) + delete m_hConfirm.Get(); + + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "Overwrite '%s'?", GetName() ); + + m_hConfirm = new MessageBox( "Overwrite Preset", sz, this ); + Assert( m_hConfirm.Get() ); + if ( m_hConfirm ) + { + m_hConfirm->ShowMessageBoxOverCursor( true ); + m_hConfirm->SetCancelButtonVisible( true ); + m_hConfirm->SetCancelButtonText( "#VGui_Cancel" ); + m_hConfirm->SetCommand( new KeyValues( "OnOverwriteConfirmed" ) ); + m_hConfirm->AddActionSignalTarget( this ); + m_hConfirm->DoModal(); + } +} + +void CPresetSlider::OnOverwriteConfirmed() +{ + m_pParent->OnOverwritePreset( m_hSelf.Get() ); +} + +void CPresetSlider::OnShowContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + delete m_hContextMenu.Get(); + m_hContextMenu = NULL; + } + + m_hContextMenu = new Menu( this, "ActionMenu" ); + + bool bIsReadOnly = m_hSelf.Get() ? m_hSelf->IsReadOnly() : false; + bool bCanOverwrite = !bIsReadOnly; + if ( m_hSelf->IsProcedural() ) + { + switch ( m_hSelf->GetProceduralPresetType() ) + { + default: + break; + case PROCEDURAL_PRESET_REVEAL: + bCanOverwrite = true; + break; + } + } + + if ( bCanOverwrite ) + { + m_hContextMenu->AddMenuItem( "Overwrite", new KeyValues( "OnOverwrite" ), this ); + m_hContextMenu->AddSeparator(); + } + if ( !bIsReadOnly ) + { + m_hContextMenu->AddMenuItem( "Rename...", new KeyValues( "OnRename" ), this ); + if ( Q_stricmp( GetName(), "Default" ) ) + { + m_hContextMenu->AddMenuItem( "Delete...", new KeyValues( "OnDelete" ), this ); + } + + m_hContextMenu->AddSeparator(); + } + + m_hContextMenu->AddMenuItem( "Add...", new KeyValues( "AddPreset" ), m_pParent ); + m_hContextMenu->AddMenuItem( "Change Crossfade Speed...", new KeyValues( "SetPresetCrossfadeSpeed" ), m_pParent ); + + m_hContextMenu->AddSeparator(); + + m_hContextMenu->AddMenuItem( "Manage...", new KeyValues( "ManagePresets" ), m_pParent ); + + Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + +void CPresetSlider::UpdateTickPos( int x, int y ) +{ + int tx, ty, tw, th; + GetTrackRect( tx, ty, tw, th ); + + bool bIsCtrlKeyDown = vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ); + bool bIsAltKeyDown = vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ); + if ( bIsCtrlKeyDown && bIsAltKeyDown && !_dragging ) + { + x = tx + tw; + } + + float previewValue = 0.0f; + if ( x > tx ) + { + if ( x >= ( tx + tw ) || tw <= 0 ) + { + previewValue = 1.0f; + } + else + { + previewValue = (float)( x - tx ) / (float)tw; + } + } + + SetPos( previewValue ); +} + +void CPresetSlider::OnCursorMoved(int x, int y) +{ + UpdateTickPos( x, y ); +} + +bool CPresetSlider::IsDragging() +{ + return _dragging; +} + +bool CPresetSlider::IsPreviewSlider() +{ + VPANEL capturePanel = input()->GetMouseCapture(); + if ( capturePanel ) + { + if ( capturePanel != GetVPanel() ) + return false; + return true; + } + + VPANEL appModal = input()->GetAppModalSurface(); + if ( appModal ) + return false; + + int mx, my; + input()->GetCursorPos( mx, my ); + + /* + VPANEL topMost = IsWithinTraverse( mx, my, true ); + if ( topMost && topMost != GetVPanel() ) + { + const char *name = ipanel()->GetName( topMost ); + return false; + } + */ + + return IsWithin( mx, my ); +} + +float CPresetSlider::GetCurrent() +{ + return GetValue() * 0.001f; +} + +void CPresetSlider::SetPos( float frac ) +{ + SetValue( (int)( frac * 1000.0f + 0.5f ), false ); +} + +void CPresetSlider::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + + m_pName->SetFont( scheme->GetFont( "DefaultBold" ) ); + m_pName->SetColor( m_TextColor ); + m_pName->ResizeImageToContent(); + + SetFgColor( Color( 194, 120, 0, 255 ) ); + SetThumbWidth( 3 ); + + Color fullColor1( Color( 118, 71, 41, 255 ) ); + Color fullColor2( Color( 194, 120, 0, 255 ) ); + m_pEdgeButtons[ 0 ]->SetDefaultColor( m_ZeroColor, m_ZeroColor ); + m_pEdgeButtons[ 1 ]->SetDefaultColor( fullColor1, fullColor1 ); + m_pEdgeButtons[ 0 ]->SetDepressedColor( m_ZeroColor, m_ZeroColor ); + m_pEdgeButtons[ 1 ]->SetDepressedColor( fullColor2, fullColor2 ); + m_pEdgeButtons[ 0 ]->SetArmedColor( m_ZeroColor, m_ZeroColor ); + m_pEdgeButtons[ 1 ]->SetArmedColor( fullColor1, fullColor1 ); + m_pEdgeButtons[ 0 ]->SetButtonActivationType( Button::ACTIVATE_ONPRESSED ); + m_pEdgeButtons[ 1 ]->SetButtonActivationType( Button::ACTIVATE_ONPRESSED ); +} + +void CPresetSlider::PerformLayout() +{ + BaseClass::PerformLayout(); + + int w, h; + GetSize( w, h ); + + int btnSize = 9; + m_pEdgeButtons[ 0 ]->SetBounds( 3, ( h - btnSize ) / 2, btnSize, btnSize ); + m_pEdgeButtons[ 1 ]->SetBounds( w - 12, ( h - btnSize ) / 2, btnSize, btnSize ); +} + +void CPresetSlider::GetTrackRect( int &x, int &y, int &w, int &h ) +{ + GetSize( w, h ); + x = 15; + y = 2; + w -= 30; + h -= 4; +} + + +void CPresetSlider::SetGradientColor( const Color& clr ) +{ + m_GradientColor = clr; +} + +void CPresetSlider::Paint() +{ + if ( !IsPreviewSlider() ) + return; + + bool bIsCtrlKeyDown = vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ); + if ( !IsDragging() && !bIsCtrlKeyDown ) + return; + + int mx, my; + input()->GetCursorPos( mx, my ); + ScreenToLocal( mx, my ); + UpdateTickPos( mx, my ); + + // horizontal nob + int x, y; + int wide,tall; + GetTrackRect( x, y, wide, tall ); + + Color col = GetFgColor(); + surface()->DrawSetColor( col ); + surface()->DrawFilledRect( _nobPos[0], 1, _nobPos[1], GetTall() - 1 ); + surface()->DrawSetColor( m_ZeroColor ); + surface()->DrawFilledRect( _nobPos[0] - 1, y + 1, _nobPos[0], y + tall - 1 ); +} + +void CPresetSlider::PaintBackground() +{ + int w, h; + GetSize( w, h ); + + bool hasFocus = IsPreviewSlider(); + bool bIsCtrlKeyDown = vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ); + bool bIsAltKeyDown = vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ); + if ( hasFocus && ( IsDragging() || bIsCtrlKeyDown ) ) + { + int tx, ty, tw, th; + GetTrackRect( tx, ty, tw, th ); + + surface()->DrawSetColor( Color( 0, 0, 0, 255 ) ); + surface()->DrawOutlinedRect( tx, ty, tx + tw, ty + th ); + surface()->DrawSetColor( m_GradientColor ); + + ++tx; + ++ty; + tw -= 2; + th -= 2; + + // Gradient fill rectangle + int fillw = (int)( (float)tw * GetCurrent() + 0.5f ); + + int minAlpha = 15; + float alphaTarget = 255.0f; + + int curAlpha = max( (int)(GetCurrent() * alphaTarget), minAlpha ); + + if ( _dragging ) + { + surface()->DrawFilledRectFade( tx, ty, tx + fillw, ty + th, minAlpha, curAlpha, true ); + surface()->DrawSetColor( m_ZeroColor ); + surface()->DrawFilledRect( tx + fillw + 1, ty, tx + tw, ty + th ); + } + else + { + surface()->DrawSetColor( bIsAltKeyDown ? m_GradientColor : m_ZeroColor ); + surface()->DrawFilledRect( tx, ty, tx + tw, ty + th ); + } + } + + int cw, ch; + m_pName->SetColor( hasFocus ? m_TextColorFocus : m_TextColor ); + m_pName->GetContentSize( cw, ch ); + m_pName->SetPos( ( w - cw ) * 0.5f, ( h - ch ) * 0.5f ); + m_pName->Paint(); +} + + +//----------------------------------------------------------------------------- +// Slider list panel +//----------------------------------------------------------------------------- +class CSliderListPanel : public PanelListPanel +{ + DECLARE_CLASS_SIMPLE( CSliderListPanel, vgui::PanelListPanel ); + +public: + CSliderListPanel( CBaseAnimSetPresetFaderPanel *parent, vgui::Panel *pParent, const char *panelName ); + + virtual void OnMouseReleased( vgui::MouseCode code ); + virtual void OnMousePressed( vgui::MouseCode code ); + +private: + MESSAGE_FUNC( OnShowContextMenu, "OnShowContextMenu" ); + vgui::DHANDLE< vgui::Menu > m_hContextMenu; + CBaseAnimSetPresetFaderPanel *m_pParent; +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CSliderListPanel::CSliderListPanel( CBaseAnimSetPresetFaderPanel *pFader, vgui::Panel *pParent, const char *panelName ) : + BaseClass( pParent, panelName ) +{ + m_pParent = pFader; +} + + +//----------------------------------------------------------------------------- +// Context menu! +//----------------------------------------------------------------------------- +void CSliderListPanel::OnMousePressed( MouseCode code ) +{ + if ( code == MOUSE_RIGHT ) + return; + + BaseClass::OnMousePressed( code ); +} + +void CSliderListPanel::OnMouseReleased( MouseCode code ) +{ + if ( code == MOUSE_RIGHT ) + { + OnShowContextMenu(); + return; + } + + BaseClass::OnMouseReleased( code ); +} + + +//----------------------------------------------------------------------------- +// Shows the slider context menu +//----------------------------------------------------------------------------- +void CSliderListPanel::OnShowContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + delete m_hContextMenu.Get(); + m_hContextMenu = NULL; + } + + m_hContextMenu = new Menu( this, "ActionMenu" ); + + m_hContextMenu->AddMenuItem( "Add...", new KeyValues( "AddPreset" ), m_pParent ); + m_hContextMenu->AddMenuItem( "Change Crossfade Speed...", new KeyValues( "SetPresetCrossfadeSpeed" ), m_pParent ); + + m_hContextMenu->AddSeparator(); + + m_hContextMenu->AddMenuItem( "Manage...", new KeyValues( "ManagePresets" ), m_pParent ); + + Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CBaseAnimSetPresetFaderPanel::CBaseAnimSetPresetFaderPanel( vgui::Panel *parent, const char *className, CBaseAnimationSetEditor *editor ) : + BaseClass( parent, className ), + m_flLastFrameTime( 0.0f ), + m_pSliders( NULL ), + m_pWorkspace( NULL ) +{ + m_hEditor = editor; + + m_pWorkspace = new EditablePanel( this, "PresetWorkspace" ); + m_pWorkspace->SetPaintBackgroundEnabled( false ); + m_pWorkspace->SetPaintEnabled( false ); + m_pWorkspace->SetPaintBorderEnabled( false ); + + m_pSliders = new CSliderListPanel( this, m_pWorkspace, "PresetSliders" ); + m_pSliders->SetFirstColumnWidth( 0 ); + m_pSliders->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 24, + 0, 0 + ); + m_pSliders->SetPos( 0, 24 ); + m_pSliders->SetVerticalBufferPixels( 0 ); + + m_pFilter = new TextEntry( m_pWorkspace, "PresetFilter" ); + m_pFilter->AddActionSignalTarget( this ); + m_pFilter->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_RIGHT, + 2, 2, + 2, 22 + ); + m_pFilter->SetPos( 0, 0 ); + + m_pWorkspace->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 0, + 0, 0 + ); +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on text changing +//----------------------------------------------------------------------------- +void CBaseAnimSetPresetFaderPanel::OnTextChanged( ) +{ + int nLength = m_pFilter->GetTextLength(); + m_Filter.SetLength( nLength ); + if ( nLength > 0 ) + { + m_pFilter->GetText( m_Filter.GetForModify(), nLength+1 ); + } + PopulateList( true ); +} + + +//----------------------------------------------------------------------------- +// Called from the input dialogs used by this class +//----------------------------------------------------------------------------- +void CBaseAnimSetPresetFaderPanel::OnInputCompleted( KeyValues *pParams ) +{ + const char *pText = pParams->GetString( "text", NULL ); + KeyValues *pContextKeyValues = pParams->FindKey( "CrossfadeSpeed" ); + if ( pContextKeyValues ) + { + float f = Q_atof( pText ); + if ( f > 0.0f ) + { + ifm_fader_timescale = f; + } + else + { + Warning( "Crossfade [%f] invalid, must be a postive number\n", f ); + } + return; + } + + Assert( 0 ); +} + + +//----------------------------------------------------------------------------- +// Called from the add preset name dialogs used by this class +//----------------------------------------------------------------------------- +void CBaseAnimSetPresetFaderPanel::OnPresetNameSelected( KeyValues *pParams ) +{ + const char *pText = pParams->GetString( "text", NULL ); + if ( !pText || !*pText ) + { + vgui::MessageBox *pError = new MessageBox( "Add Preset Error", "Can't add preset with an empty name\n", this ); + pError->SetDeleteSelfOnClose( true ); + pError->DoModal(); + return; + } + + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Add Preset" ); + + CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pParams, "presetGroup" ); + if ( !pPresetGroup ) + { + const char *pGroupName = pParams->GetString( "presetGroupName" ); + if ( !pGroupName ) + return; + + pPresetGroup = m_AnimSet->FindOrAddPresetGroup( pGroupName ); + } + + if ( pPresetGroup->m_bIsReadOnly ) + { + vgui::MessageBox *pError = new MessageBox( "Add Preset Error", "Can't add preset to a read-only preset group!\n", this ); + pError->SetDeleteSelfOnClose( true ); + pError->DoModal(); + return; + } + + if ( pPresetGroup->FindPreset( pText ) ) + { + vgui::MessageBox *pError = new MessageBox( "Add Preset Error", "A preset with that name already exists!\n", this ); + pError->SetDeleteSelfOnClose( true ); + pError->DoModal(); + return; + } + + CDmePreset *pPreset = pPresetGroup->FindOrAddPreset( pText ); + AddNewPreset( pPreset ); + guard.Release(); + + ChangeAnimationSet( m_AnimSet ); +} + + +//----------------------------------------------------------------------------- +// The 'set crossfade speed' context menu option +//----------------------------------------------------------------------------- +void CBaseAnimSetPresetFaderPanel::OnSetCrossfadeSpeed() +{ + if ( m_hInputDialog.Get() ) + { + delete m_hInputDialog.Get(); + } + + char sz[32 ]; + Q_snprintf( sz, sizeof( sz ), "%f", ifm_fader_timescale ); + m_hInputDialog = new InputDialog( this, "Crossfade Speed", "Fader Crossfade Rate:", sz ); + if ( m_hInputDialog.Get() ) + { + KeyValues *pContextKeyValues = new KeyValues( "CrossfadeSpeed" ); + m_hInputDialog->SetSmallCaption( true ); + m_hInputDialog->SetMultiline( false ); + m_hInputDialog->DoModal( pContextKeyValues ); + } +} + + +//----------------------------------------------------------------------------- +// The 'add preset' context menu option +//----------------------------------------------------------------------------- +void CBaseAnimSetPresetFaderPanel::OnAddPreset() +{ + if ( !m_AnimSet.Get() ) + return; + + if ( !m_hAddPresetDialog.Get() ) + { + m_hAddPresetDialog = new CAddPresetDialog( this ); + m_hAddPresetDialog->AddActionSignalTarget( this ); + } + + m_hAddPresetDialog->DoModal( m_AnimSet, NULL ); +} + + +//----------------------------------------------------------------------------- +// Called by the preset group editor panel when it changes presets +//----------------------------------------------------------------------------- +void CBaseAnimSetPresetFaderPanel::OnPresetsChanged() +{ + ChangeAnimationSet( m_AnimSet ); +} + + +//----------------------------------------------------------------------------- +// Brings up the preset manager +//----------------------------------------------------------------------------- +void CBaseAnimSetPresetFaderPanel::OnManagePresets() +{ + if ( !m_hPresetEditor.Get() ) + { + m_hPresetEditor = new CDmePresetGroupEditorFrame( this, "Manage Presets" ); + m_hPresetEditor->AddActionSignalTarget( this ); + m_hPresetEditor->SetVisible( false ); + m_hPresetEditor->SetDeleteSelfOnClose( false ); + m_hPresetEditor->MoveToCenterOfScreen(); + } + + m_hPresetEditor->SetAnimationSet( m_AnimSet ); + m_hPresetEditor->DoModal( ); +} + +void CBaseAnimSetPresetFaderPanel::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + m_pSliders->SetBgColor( Color( 42, 42, 42, 255 ) ); +} + +void CBaseAnimSetPresetFaderPanel::GetPreviewFader( FaderPreview_t& fader ) +{ + Q_memset( &fader, 0, sizeof( fader ) ); + + fader.isbeingdragged = false; + + fader.holdingctrl = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ); + int mx, my; + input()->GetCursorPos( mx, my ); + if ( !IsWithin( mx, my ) ) + { + fader.holdingctrl = false; + } + + // Walk through sliders and figure out which is under the mouse + CPresetSlider *mouseOver = NULL; + + for ( int i = m_pSliders->FirstItem(); i != m_pSliders->InvalidItemID(); i = m_pSliders->NextItem(i) ) + { + CPresetSlider *slider = static_cast< CPresetSlider * >( m_pSliders->GetItemPanel( i ) ); + if ( !slider || !slider->IsPreviewSlider() ) + continue; + + mouseOver = slider; + fader.isbeingdragged = slider->IsDragging(); + break; + } + + if ( mouseOver ) + { + // Deal with procedural presets here + if ( fader.holdingctrl || + fader.isbeingdragged ) + { + mouseOver->UpdateProceduralValues(); + } + + fader.name = mouseOver->GetName(); + fader.amount = mouseOver->GetCurrent(); + fader.values = mouseOver->GetAttributeDict(); + fader.preset = mouseOver->GetPreset(); + } +} + +void CBaseAnimSetPresetFaderPanel::PopulateList( bool bChanged ) +{ + if ( !m_AnimSet.Get() ) + { + m_CurrentPresetList.RemoveAll(); + m_pSliders->DeleteAllItems(); + return; + } + + CDmrElementArray< CDmePresetGroup > presetGroups = m_AnimSet->GetPresetGroups(); + Assert( presetGroups.IsValid() ); + + int c = presetGroups.Count(); + + bool bNeedRebuild = false; + int slot = 0; + for ( int i = 0 ; i < c; ++i ) + { + CDmePresetGroup *pPresetGroup = presetGroups[ i ]; + Assert( pPresetGroup ); + if ( !pPresetGroup || !pPresetGroup->m_bIsVisible ) + continue; + + CDmrElementArray< CDmePreset > presets = pPresetGroup->GetPresets(); + + int cp = presets.Count(); + for ( int j = 0; j < cp; ++j ) + { + CDmePreset *pPreset = presets[ j ]; + Assert( pPreset ); + + const char *pElementName = pPreset->GetName(); + if ( Q_stricmp( pElementName, "Default" ) ) + { + if ( m_Filter.Length() && !Q_stristr( pElementName, m_Filter.Get() ) ) + continue; + } + + if ( slot >= m_CurrentPresetList.Count() ) + { + bNeedRebuild = true; + break; + } + + if ( pPreset != m_CurrentPresetList[ slot ] ) + { + bNeedRebuild = true; + break; + } + + ++slot; + } + } + + if ( slot != m_CurrentPresetList.Count() ) + { + bNeedRebuild = true; + } + + if ( bNeedRebuild ) + { + m_CurrentPresetList.RemoveAll(); + m_pSliders->DeleteAllItems(); + + for ( int i = 0 ; i < c; ++i ) + { + CDmePresetGroup *pPresetGroup = presetGroups[ i ]; + Assert( pPresetGroup ); + if ( !pPresetGroup || !pPresetGroup->m_bIsVisible ) + continue; + + CDmrElementArray< CDmePreset > presets = pPresetGroup->GetPresets(); + + int cp = presets.Count(); + for ( int j = 0; j < cp; ++j ) + { + CDmePreset *pPreset = presets[ j ]; + Assert( pPreset ); + + const char *pElementName = pPreset->GetName(); + if ( Q_stricmp( pElementName, "Default" ) ) + { + if ( m_Filter.Length() && !Q_stristr( pElementName, m_Filter.Get() ) ) + continue; + } + + CPresetSlider *pSlider = new CPresetSlider( this, pPreset->GetName(), pPreset ); + pSlider->SetPos( 0 ); + pSlider->SetSize( 100, 20 ); + pSlider->SetGradientColor( Color( 194, 120, 0, 255 ) ); + + pSlider->SetControlValues( ); + + m_pSliders->AddItem( NULL, pSlider ); + + m_CurrentPresetList.AddToTail( pPreset->GetHandle() ); + } + } + } + else + { + UpdateControlValues(); + } +} + +void CBaseAnimSetPresetFaderPanel::ChangeAnimationSet( CDmeAnimationSet *newAnimSet ) +{ + bool bChanged = m_AnimSet != newAnimSet; + m_AnimSet = newAnimSet; + PopulateList( bChanged ); +} + +void CBaseAnimSetPresetFaderPanel::UpdateControlValues() +{ + for ( int i = m_pSliders->FirstItem(); i != m_pSliders->InvalidItemID(); i = m_pSliders->NextItem(i) ) + { + CPresetSlider *pSlider = static_cast< CPresetSlider * >( m_pSliders->GetItemPanel( i ) ); + if ( pSlider ) + { + pSlider->SetControlValues( ); + } + } +} + +void CBaseAnimSetPresetFaderPanel::ApplyPreset( float flScale, AttributeDict_t& dict ) +{ + CBaseAnimSetAttributeSliderPanel *sliderPanel = m_hEditor->GetAttributeSlider(); + if ( sliderPanel ) + { + sliderPanel->ApplyPreset( flScale, dict ); + } +} + + +//----------------------------------------------------------------------------- +// Reads the current animation set control values, creates presets +//----------------------------------------------------------------------------- +void CBaseAnimSetPresetFaderPanel::SetPresetFromSliders( CDmePreset *pPreset ) +{ + if ( !m_AnimSet.Get() ) + return; + + CBaseAnimSetAttributeSliderPanel *pSliderPanel = m_hEditor->GetAttributeSlider(); + if ( !pSliderPanel ) + return; + + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Set Preset" ); + + const CDmrElementArray< CDmElement > controlList = m_AnimSet->GetControls(); + + // Now set values for each known control + int numControls = controlList.Count(); + for ( int i = 0; i < numControls; ++i ) + { + CDmElement *pControl = controlList[ i ]; + + if ( pControl->GetValue<bool>( "transform" ) ) + continue; + + // Facial sliders below + AttributeValue_t value; + bool bIsDefault = !pSliderPanel->GetAttributeSliderValue( &value, pControl->GetName() ); + if ( !bIsDefault ) + { + float flDefaultValue = pControl->GetValue< float >( "defaultValue" ); + float flDefaultBalance = pControl->GetValue< float >( "defaultBalance" ); + float flDefaultMultilevel = pControl->GetValue< float >( "defaultMultilevel" ); + bIsDefault = ( value.m_pValue[ANIM_CONTROL_VALUE] == flDefaultValue ) && + ( value.m_pValue[ANIM_CONTROL_BALANCE] == flDefaultBalance ) && + ( value.m_pValue[ANIM_CONTROL_MULTILEVEL] == flDefaultMultilevel ); + } + + // Blow away preset values for controls that contain the default values + if ( bIsDefault ) + { + pPreset->RemoveControlValue( pControl->GetName() ); + continue; + } + + bool bIsCombo = pControl->GetValue< bool >( "combo" ); + bool bIsMulti = pControl->GetValue< bool >( "multi" ); + + // Stamp the control value + CDmElement *pControlValue = pPreset->FindOrAddControlValue( pControl->GetName() ); + pControlValue->SetValue< float >( "value", value.m_pValue[ANIM_CONTROL_VALUE] ); + if ( bIsCombo ) + { + pControlValue->SetValue< float >( "balance", value.m_pValue[ANIM_CONTROL_BALANCE] ); + } + else + { + pControlValue->RemoveAttribute( "balance" ); + } + + if ( bIsMulti ) + { + pControlValue->SetValue< float >( "multilevel", value.m_pValue[ANIM_CONTROL_MULTILEVEL] ); + } + else + { + pControlValue->RemoveAttribute( "multilevel" ); + } + } +} + +void CBaseAnimSetPresetFaderPanel::AddNewPreset( CDmePreset *pPreset ) +{ + if ( !pPreset ) + return; + + SetPresetFromSliders( pPreset ); + + CPresetSlider *pSlider = new CPresetSlider( this, pPreset->GetName(), pPreset ); + if ( pSlider ) + { + pSlider->SetPos( 0 ); + pSlider->SetSize( 100, 20 ); + pSlider->SetGradientColor( Color( 194, 120, 0, 255 ) ); + pSlider->SetControlValues( ); + m_pSliders->AddItem( NULL, pSlider ); + } +} + +void CBaseAnimSetPresetFaderPanel::OnAddNewPreset( KeyValues *pKeyValues ) +{ + CDmePreset *pPreset = GetElementKeyValue<CDmePreset>( pKeyValues, "preset" ); + + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Overwrite Preset" ); + AddNewPreset( pPreset ); + guard.Release(); + + ChangeAnimationSet( m_AnimSet ); +} + +void CBaseAnimSetPresetFaderPanel::AddNewPreset( const char *pGroupName, const char *pName ) +{ + if ( !m_AnimSet.Get() ) + return; + + CBaseAnimSetAttributeSliderPanel *sliderPanel = m_hEditor->GetAttributeSlider(); + if ( !sliderPanel ) + return; + + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Add Preset" ); + + CDmePresetGroup *pPresetGroup = m_AnimSet->FindOrAddPresetGroup( pGroupName ); + CDmePreset *pPreset = pPresetGroup->FindOrAddPreset( pName ); + AddNewPreset( pPreset ); + guard.Release(); + + ChangeAnimationSet( m_AnimSet ); +} + +void CBaseAnimSetPresetFaderPanel::OnOverwritePreset( CDmePreset *pPreset ) +{ + SetPresetFromSliders( pPreset ); + UpdateControlValues(); +} + +void CBaseAnimSetPresetFaderPanel::OnDeletePreset( CDmePreset *pPreset ) +{ + { + // Delete it from various things + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Delete Preset" ); + m_AnimSet->RemovePreset( pPreset ); + } + + ChangeAnimationSet( m_AnimSet ); +} + +void CBaseAnimSetPresetFaderPanel::ProceduralPreset_UpdateCrossfade( CDmePreset *pPreset, bool bFadeIn ) +{ + // Handled by derived class in SFM +} diff --git a/vgui2/dme_controls/BaseAnimationSetEditor.cpp b/vgui2/dme_controls/BaseAnimationSetEditor.cpp new file mode 100644 index 0000000..80151ef --- /dev/null +++ b/vgui2/dme_controls/BaseAnimationSetEditor.cpp @@ -0,0 +1,1076 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/BaseAnimationSetEditor.h" +#include "tier1/KeyValues.h" +#include "tier2/fileutils.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/ToggleButton.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/FileOpenDialog.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/perforcefilelistframe.h" +#include "studio.h" +#include "dme_controls/BaseAnimSetAttributeSliderPanel.h" +#include "dme_controls/BaseAnimSetPresetFaderPanel.h" +#include "dme_controls/BaseAnimSetControlGroupPanel.h" +#include "dme_controls/dmecontrols_utils.h" +#include "dme_controls/dmepicker.h" + +#include "sfmobjects/exportfacialanimation.h" + +#include "movieobjects/dmechannel.h" +#include "movieobjects/dmeanimationset.h" +#include "movieobjects/dmeclip.h" +#include "movieobjects/dmeanimationlist.h" +#include "movieobjects/dmegamemodel.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +#define ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT 38 +#define ANIMATION_SET_EDITOR_BUTTONTRAY_YPOS 12 +#define ANIMATION_SET_BUTTON_INSET 0 + +struct AnimSetLayout_t +{ + CBaseAnimationSetEditor::EAnimSetLayout_t type; + const char *shortname; + const char *contextmenulabel; +}; + +static AnimSetLayout_t g_AnimSetLayout[] = +{ + { CBaseAnimationSetEditor::LAYOUT_SPLIT, "split", "#BxAnimSetSplitLayout" }, + { CBaseAnimationSetEditor::LAYOUT_VERTICAL, "vertical", "#BxAnimSetVerticalLayout" }, + { CBaseAnimationSetEditor::LAYOUT_HORIZONTAL, "horizontal", "#BxAnimSetHorizontalLayout" }, +}; + +static const char *NameForLayout( CBaseAnimationSetEditor::EAnimSetLayout_t layout, bool menu ) +{ + int c = ARRAYSIZE( g_AnimSetLayout ); + for ( int i = 0; i < c; ++i ) + { + const AnimSetLayout_t& data = g_AnimSetLayout[ i ]; + if ( data.type == layout ) + { + return menu ? data.contextmenulabel : data.shortname; + } + } + Assert( 0 ); + return menu ? g_AnimSetLayout[ 0 ].contextmenulabel : g_AnimSetLayout[ 0 ].shortname; +} + +static CBaseAnimationSetEditor::EAnimSetLayout_t LayoutForName( const char *name ) +{ + int c = ARRAYSIZE( g_AnimSetLayout ); + for ( int i = 0; i < c; ++i ) + { + const AnimSetLayout_t& data = g_AnimSetLayout[ i ]; + if ( !Q_stricmp( data.shortname, name ) ) + { + return data.type; + } + } + + Assert( 0 ); + return CBaseAnimationSetEditor::LAYOUT_SPLIT; +} + +CBaseAnimationSetEditor::CBaseAnimationSetEditor( vgui::Panel *parent, const char *className, bool bShowGroups ) : + BaseClass( parent, className ), + m_Layout( LAYOUT_SPLIT ), + m_Images( false ) +{ + const char *modes[] = + { + "AS_OFF", + "AS_PREVIEW", + "AS_RECORD", + "AS_PLAYBACK", + }; + + const char *imagefiles[] = + { + "tools/ifm/icon_recordingmode_off", + "tools/ifm/icon_recordingmode_preview", + "tools/ifm/icon_recordingmode_record", + "tools/ifm/icon_recordingmode_playback", + }; + + int i; + for ( i = 0 ; i < NUM_AS_RECORDING_STATES; ++i ) + { + m_pState[ i ] = new ToggleButton( this, modes[ i ], "" ); + m_pState[ i ]->SetContentAlignment( Label::a_center ); + m_pState[ i ]->AddActionSignalTarget( this ); + m_pState[ i ]->SetVisible( bShowGroups ); + m_pState[ i ]->SetKeyBoardInputEnabled( false ); + } + + m_pSelectionModeType = new ToggleButton( this, "AnimSetSelectionModeType", "" ); + m_pSelectionModeType->SetContentAlignment( Label::a_center ); + m_pSelectionModeType->AddActionSignalTarget( this ); + m_pSelectionModeType->SetSelected( false ); + m_pSelectionModeType->SetKeyBoardInputEnabled( false ); + + m_pComboBox = new ComboBox( this, "AnimationSets", 10, false ); + + // m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_lock", false ) ); + // m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_eyedropper", false ) ); + // m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_selectionmodeactive", false ) ); + m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_selectionmodeattached", false ) ); + for ( i = 0; i < NUM_AS_RECORDING_STATES; ++i ) + { + m_Images.AddImage( scheme()->GetImage( imagefiles[ i ], false ) ); + } + + int w, h; + m_Images.GetImage( 1 )->GetContentSize( w, h ); + m_Images.GetImage( 1 )->SetSize( w, h ); + m_Images.GetImage( 2 )->GetContentSize( w, h ); + m_Images.GetImage( 2 )->SetSize( w, h ); + + // SETUP_PANEL( this ); + + PostMessage( GetVPanel(), new KeyValues( "OnChangeLayout", "value", m_Layout ) ); + PostMessage( GetVPanel(), new KeyValues( "PopulateAnimationSetsChoice" ) ); + + m_pSelectionModeType->SetVisible( bShowGroups ); + m_pComboBox->SetVisible( bShowGroups ); + + SetRecordingState( bShowGroups ? AS_PLAYBACK : AS_PREVIEW, true ); + + m_hFileOpenStateMachine = new vgui::FileOpenStateMachine( this, this ); + m_hFileOpenStateMachine->AddActionSignalTarget( this ); +} + +CBaseAnimationSetEditor::~CBaseAnimationSetEditor() +{ +} + +void CBaseAnimationSetEditor::CreateToolsSubPanels() +{ + m_hControlGroup = new CBaseAnimSetControlGroupPanel( (Panel *)NULL, "AnimSetControlGroup", this ); + m_hPresetFader = new CBaseAnimSetPresetFaderPanel( (Panel *)NULL, "AnimSetPresetFader", this ); + m_hAttributeSlider = new CBaseAnimSetAttributeSliderPanel( (Panel *)NULL, "AnimSetAttributeSliderPanel", this ); +} + +void CBaseAnimationSetEditor::OnButtonToggled( KeyValues *params ) +{ + Panel *ptr = reinterpret_cast< Panel * >( params->GetPtr( "panel" ) ); + /* + + if ( ptr == m_pSelectionModeType ) + { + // FIXME, could do this with MESSAGE_FUNC_PARAMS and look up "panel" ptr and compare to figure out which button was manipulated... + g_pMovieMaker->SetTimeSelectionModeType( !m_pSelectionModeType->IsSelected() ? CIFMTool::MODE_DETACHED : CIFMTool::MODE_ATTACHED ); + } + else + */ + { + for ( int i = 0; i < NUM_AS_RECORDING_STATES; ++i ) + { + if ( ptr == m_pState[ i ] ) + { + SetRecordingState( (RecordingState_t)i, true ); + break; + } + } + } +} + +void CBaseAnimationSetEditor::ChangeLayout( EAnimSetLayout_t newLayout ) +{ + int i; + + m_Layout = newLayout; + + // Make sure these don't get blown away... + m_hControlGroup->SetParent( (Panel *)NULL ); + m_hPresetFader->SetParent( (Panel *)NULL ); + m_hAttributeSlider->SetParent( (Panel *)NULL ); + + delete m_Splitter.Get(); + m_Splitter = NULL; + + CUtlVector< Panel * > list; + list.AddToTail( m_hControlGroup.Get() ); + list.AddToTail( m_hPresetFader.Get() ); + list.AddToTail( m_hAttributeSlider.Get() ); + + Splitter *sub = NULL; + + switch ( m_Layout ) + { + default: + case LAYOUT_SPLIT: + { + m_Splitter = new Splitter( this, "AnimSetEditorMainSplitter", SPLITTER_MODE_VERTICAL, 1 ); + m_Splitter->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, + 0, 0 + ); + m_Splitter->SetBounds( 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, GetWide(), GetTall() - ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT ); + m_Splitter->SetSplitterColor( Color(32, 32, 32, 255) ); + + // m_Splitter->EnableBorders( false ); + + m_hControlGroup->SetParent( m_Splitter->GetChild( 0 ) ); + m_hControlGroup->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 0, + 0, 0 + ); + + sub = new Splitter( m_Splitter->GetChild( 1 ), "AnimSetEditorSubSplitter", SPLITTER_MODE_HORIZONTAL, 1 ); + sub->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 0, + 0, 0 + ); + + m_hPresetFader->SetParent( sub->GetChild( 0 ) ); + m_hPresetFader->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 0, + 0, 0 + ); + m_hAttributeSlider->SetParent( sub->GetChild( 1 ) ); + m_hAttributeSlider->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 0, + 0, 0 + ); + } + break; + case LAYOUT_VERTICAL: + { + m_Splitter = new Splitter( this, "AnimSetEditorMainSplitter", SPLITTER_MODE_VERTICAL, 2 ); + m_Splitter->SetSplitterColor( Color(32, 32, 32, 255) ); + m_Splitter->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, + 0, 0 + ); + m_Splitter->SetBounds( 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, GetWide(), GetTall() - ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT ); + + for ( i = 0; i < list.Count(); ++i ) + { + list[ i ]->SetParent( m_Splitter->GetChild( i ) ); + list[ i ]->SetSize( m_Splitter->GetChild( i )->GetWide(), m_Splitter->GetChild( i )->GetTall() ); + list[ i ]->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 0, + 0, 0 + ); + } + + m_Splitter->EvenlyRespaceSplitters(); + } + break; + case LAYOUT_HORIZONTAL: + { + m_Splitter = new Splitter( this, "AnimSetEditorMainSplitter", SPLITTER_MODE_HORIZONTAL, 2 ); + m_Splitter->SetSplitterColor( Color(32, 32, 32, 255) ); + m_Splitter->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, + 0, 0 + ); + + m_Splitter->SetBounds( 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, GetWide(), GetTall() - ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT ); + + for ( i = 0; i < list.Count(); ++i ) + { + list[ i ]->SetParent( m_Splitter->GetChild( i ) ); + list[ i ]->SetSize( m_Splitter->GetChild( i )->GetWide(), m_Splitter->GetChild( i )->GetTall() ); + list[ i ]->SetAutoResize + ( + Panel::PIN_TOPLEFT, + Panel::AUTORESIZE_DOWNANDRIGHT, + 0, 0, + 0, 0 + ); + } + + m_Splitter->EvenlyRespaceSplitters(); + } + break; + } + + if ( sub ) + { + sub->OnSizeChanged( sub->GetWide(), sub->GetTall() ); + sub->EvenlyRespaceSplitters(); + } +} + +void CBaseAnimationSetEditor::PerformLayout() +{ + BaseClass::PerformLayout(); + + int w, h; + GetSize( w, h ); + + int ypos = ANIMATION_SET_EDITOR_BUTTONTRAY_YPOS; + + int xpos = w - 25; + m_pSelectionModeType->SetBounds( xpos, ypos, 20, 20 ); + for ( int i = NUM_AS_RECORDING_STATES - 1; i >= 0 ; --i ) + { + xpos -= 23; + m_pState[ i ]->SetBounds( xpos, ypos, 20, 20 ); + } + + m_pComboBox->SetBounds( 10, ypos, xpos - 10- 5, 20 ); +} + +void CBaseAnimationSetEditor::OnChangeLayout( int value ) +{ + ChangeLayout( ( EAnimSetLayout_t )value ); +} + + +//----------------------------------------------------------------------------- +// Finds a channel in the animation set to overwrite with import data +//----------------------------------------------------------------------------- +CDmeChannel* CBaseAnimationSetEditor::FindImportChannel( CDmeChannel *pChannel, CDmeChannelsClip *pChannelsClip ) +{ + CDmElement *pTargetElement = pChannel->GetToElement(); + if ( !pTargetElement ) + return NULL; + + CDmAttribute *pTargetAttribute = pChannel->GetToAttribute(); + if ( !pTargetAttribute ) + return NULL; + + const char *pTarget = pTargetAttribute->GetName(); + const char *pTargetName = pTargetElement->GetName(); + CDmeLog *pTargetLog = pChannel->GetLog(); + + int nCount = pChannelsClip->m_Channels.Count(); + for ( int j = 0; j < nCount; ++j ) + { + CDmeChannel *pImportChannel = pChannelsClip->m_Channels[j]; + if ( !pImportChannel ) + continue; + + CDmeLog *pImportLog = pImportChannel->GetLog(); + if ( !pImportLog ) + continue; + + if ( pTargetLog && ( pImportLog->GetType() != pTargetLog->GetType() ) ) + continue; + + if ( !pImportChannel->GetToAttribute() ) + continue; + + const char *pImportTarget = pImportChannel->GetToAttribute()->GetName(); + + // Attribute to write into has to match exactly + if ( Q_stricmp( pTarget, pImportTarget ) ) + continue; + + CDmElement *pImportTargetElement = pImportChannel->GetToElement(); + const char *pImportName = pImportTargetElement->GetName(); + + // Element name has to match exactly or be of the form *(channel name)* + if ( !Q_stricmp( pTargetName, pImportName ) ) + return pImportChannel; + + char pTemp[512]; + const char *pParen = strrchr( pTargetName, '(' ); + if ( !pParen ) + continue; + Q_strncpy( pTemp, pParen+1, sizeof(pTemp) ); + char *pParen2 = strchr( pTemp, ')' ); + if ( !pParen2 ) + continue; + *pParen2 = 0; + if ( !Q_stricmp( pImportName, pTemp ) ) + return pImportChannel; + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Transforms an imported channel, if necessary +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::TransformImportedChannel( CDmeChannel *pChannel ) +{ + CDmElement *pTarget = pChannel->GetToElement(); + const static UtlSymId_t symBones = g_pDataModel->GetSymbol( "bones" ); + CDmeGameModel *pGameModel = FindReferringElement<CDmeGameModel>( pTarget, symBones ); + if ( !pGameModel ) + return; + + int nBoneIndex = pGameModel->FindBone( CastElement< CDmeTransform >( pTarget ) ); + if ( nBoneIndex < 0 ) + return; + + // If we've got logs that have been imported, we need to compute our bounds. + pGameModel->m_bComputeBounds = true; + + DmAttributeType_t logType = pChannel->GetLog()->GetDataType(); + int nLayerCount = pChannel->GetLog()->GetNumLayers(); + + bool bHasPreTransform = false; + bool bHasPostTransform = false; + + matrix3x4_t preTransform, postTransform; + if ( pGameModel->GetSrcBoneTransforms( &preTransform, &postTransform, nBoneIndex ) ) + { + bHasPreTransform = true; + bHasPostTransform = true; + } + + if ( pGameModel->IsRootTransform( nBoneIndex ) ) + { + // NOTE: Root transforms require a pre-multiply of log data + + // Deal with the 'up axis' rotation + matrix3x4_t rootTransform; + RadianEuler angles( M_PI / 2.0f, 0.0f, M_PI / 2.0f ); + if ( bHasPreTransform ) + { + AngleMatrix( angles, rootTransform ); + ConcatTransforms( rootTransform, preTransform, preTransform ); + } + else + { + AngleMatrix( angles, preTransform ); + } + bHasPreTransform = true; + } + + if ( !bHasPreTransform && !bHasPostTransform ) + return; + + for ( int i = 0; i < nLayerCount; ++i ) + { + if ( logType == AT_VECTOR3 ) + { + CDmeVector3LogLayer *pPositionLog = CastElement< CDmeVector3LogLayer >( pChannel->GetLog()->GetLayer( i ) ); + if ( bHasPreTransform ) + { + RotatePositionLog( pPositionLog, preTransform ); + } + +#ifdef _DEBUG + // At the moment, we don't support anything but prerotation. + // This would be tricky because we'd need to read the quat logs + // to figure out how to translate in local space. + if ( bHasPostTransform ) + { + Assert( fabs( postTransform[0][3] ) < 1e-3 && fabs( postTransform[1][3] ) < 1e-3 && fabs( postTransform[2][3] ) < 1e-3 ); + } +#endif + } + else + { + CDmeQuaternionLogLayer *pOrientationLog = CastElement< CDmeQuaternionLogLayer >( pChannel->GetLog()->GetLayer( i ) ); + if ( bHasPreTransform ) + { + RotateOrientationLog( pOrientationLog, preTransform, true ); + } + if ( bHasPostTransform ) + { + RotateOrientationLog( pOrientationLog, postTransform, false ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Expands channels clip time to encompass log +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::FixupChannelsClipTime( CDmeChannelsClip *pChannelsClip, CDmeLog *pLog ) +{ + // Expand the channels clip to include the entire channel + DmeTime_t st = pLog->GetBeginTime(); + DmeTime_t et = pLog->GetEndTime(); + st = pChannelsClip->FromChildMediaTime( st, false ); + et = pChannelsClip->FromChildMediaTime( et, false ); + if ( et < pChannelsClip->GetEndTime() ) + { + et = pChannelsClip->GetEndTime(); + } + if ( st < pChannelsClip->GetStartTime() ) + { + DmeTime_t tDelta = pChannelsClip->GetStartTime() - st; + DmeTime_t tOffset = pChannelsClip->GetTimeOffset(); + pChannelsClip->SetStartTime( st ); + pChannelsClip->SetTimeOffset( tOffset - tDelta ); + } + else + { + st = pChannelsClip->GetStartTime(); + } + DmeTime_t duration = et - st; + if ( duration > pChannelsClip->GetDuration() ) + { + pChannelsClip->SetDuration( duration ); + } +} + + +//----------------------------------------------------------------------------- +// Expands channels clip time to encompass log +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::FixupChannelsClipTime( CDmeChannel *pChannel, CDmeLog *pLog ) +{ + CUtlVector< CDmeChannelsClip* > clips; + FindAncestorsReferencingElement( pChannel, clips ); + int nCount = clips.Count(); + for ( int i = 0; i < nCount; ++i ) + { + FixupChannelsClipTime( clips[i], pLog ); + } +} + + +//----------------------------------------------------------------------------- +// Imports a specific channels clip into the animation set +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::OnImportConfirmed( KeyValues *pParams ) +{ + KeyValues *pImportParams = pParams->FindKey( "context" ); + CDmeChannelsClip *pChannelsClip = GetElementKeyValue< CDmeChannelsClip >( pImportParams, "channelsClip" ); + if ( pParams->GetInt( "operationPerformed" ) == 0 ) + { + CDisableUndoScopeGuard sg; + g_pDataModel->RemoveFileId( pChannelsClip->GetFileId() ); + return; + } + + bool bVisibleOnly = pImportParams->GetInt( "visibleOnly" ) != 0; + + CUtlVector< LogPreview_t > controls; + int nCount = bVisibleOnly ? BuildVisibleControlList( controls ) : BuildFullControlList( controls ); + + CUndoScopeGuard guard( "Import Animation" ); + for ( int i = 0; i < nCount; ++i ) + { + for ( int k = 0; k < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++k ) + { + CDmeChannel *pChannel = controls[i].m_hChannels[k]; + if ( !pChannel ) + continue; + + CDmeChannel *pImportChannel = FindImportChannel( pChannel, pChannelsClip ); + if ( !pImportChannel ) + continue; + + // Switch the log over + CDmeLog *pImportLog = pImportChannel->GetLog(); + pChannel->SetLog( pImportLog ); + pImportChannel->SetLog( NULL ); + pImportLog->SetFileId( pChannel->GetFileId(), TD_DEEP ); + + TransformImportedChannel( pChannel ); + + // Expand the channels clip to include the entire channel + FixupChannelsClipTime( pChannel, pChannel->GetLog() ); + } + } + guard.Release(); + + // Cleanup the file + CDisableUndoScopeGuard sg; + g_pDataModel->RemoveFileId( pChannelsClip->GetFileId() ); +} + + +//----------------------------------------------------------------------------- +// Imports a specific channels clip into the animation set +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::ImportAnimation( CDmeChannelsClip *pChannelsClip, bool bVisibleOnly ) +{ + CUtlVector< LogPreview_t > controls; + int nCount = bVisibleOnly ? BuildVisibleControlList( controls ) : BuildFullControlList( controls ); + + COperationFileListFrame *pStatusFrame = new COperationFileListFrame( this, + "Import the Following Channels?", "Target Control", false ); + pStatusFrame->SetCloseButtonVisible( false ); + pStatusFrame->SetOperationColumnHeaderText( "Source Channel" ); + + int nSrcCount = pChannelsClip->m_Channels.Count(); + CDmeChannel** ppFoundChannels = (CDmeChannel**)_alloca( nSrcCount * sizeof(CDmeChannel*) ); + int nFoundCount = 0; + + for ( int i = 0; i < nCount; ++i ) + { + for ( int k = 0; k < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++k ) + { + CDmeChannel *pChannel = controls[i].m_hChannels[k]; + if ( !pChannel || pChannel->GetToElement() == NULL ) + continue; + + char pChannelInfo[512]; + Q_snprintf( pChannelInfo, sizeof(pChannelInfo), "\"%s\" : %s", + pChannel->GetToElement()->GetName(), pChannel->GetToAttribute()->GetName() ); + + CDmeChannel *pImportChannel = FindImportChannel( pChannel, pChannelsClip ); + if ( !pImportChannel ) + { + pStatusFrame->AddOperation( "No source channel", pChannelInfo, Color( 255, 0, 0, 255 ) ); + continue; + } + + ppFoundChannels[nFoundCount++] = pImportChannel; + + char pImportInfo[512]; + Q_snprintf( pImportInfo, sizeof(pImportInfo), "\"%s\" : %s", + pImportChannel->GetToElement()->GetName(), pImportChannel->GetToAttribute()->GetName() ); + pStatusFrame->AddOperation( pImportInfo, pChannelInfo, Color( 0, 255, 0, 255 ) ); + } + } + + for ( int i = 0; i < nSrcCount; ++i ) + { + CDmeChannel *pMissingChannel = pChannelsClip->m_Channels[i]; + + int j; + for ( j = 0; j < nFoundCount; ++j ) + { + if ( ppFoundChannels[j] == pMissingChannel ) + break; + } + + if ( j != nFoundCount ) + continue; + + char pImportInfo[512]; + Q_snprintf( pImportInfo, sizeof(pImportInfo), "\"%s\" : %s", + pMissingChannel->GetToElement()->GetName(), pMissingChannel->GetToAttribute()->GetName() ); + pStatusFrame->AddOperation( pImportInfo, "No destination control", Color( 255, 255, 0, 255 ) ); + } + + KeyValues *pContext = new KeyValues( "context" ); + SetElementKeyValue( pContext, "channelsClip", pChannelsClip ); + pContext->SetInt( "visibleOnly", bVisibleOnly ); + pStatusFrame->DoModal( pContext, "ImportConfirmed" ); +} + + +//----------------------------------------------------------------------------- +// Called by CDmePickerFrame in SelectImportAnimation +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::OnImportAnimationSelected( KeyValues *pParams ) +{ + KeyValues *pContextKeyValues = pParams->FindKey( "context" ); + CDmeChannelsClip *pChannelsClip = GetElementKeyValue< CDmeChannelsClip >( pParams, "dme" ); + if ( pChannelsClip ) + { + bool bVisibleOnly = pContextKeyValues->GetInt( "visibleOnly" ) != 0; + ImportAnimation( pChannelsClip, bVisibleOnly ); + } + else + { + OnImportAnimationCancelled( pParams ); + } +} + + +//----------------------------------------------------------------------------- +// Called by CDmePickerFrame in SelectImportAnimation +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::OnImportAnimationCancelled( KeyValues *pParams ) +{ + KeyValues *pContextKeyValues = pParams->FindKey( "context" ); + CDmElement *pAnimationList = GetElementKeyValue<CDmElement>( pContextKeyValues, "animationList" ); + + // Cleanup the file + if ( pAnimationList ) + { + CDisableUndoScopeGuard sg; + g_pDataModel->RemoveFileId( pAnimationList->GetFileId() ); + } +} + + +//----------------------------------------------------------------------------- +// Selects an animation to import +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::SelectImportAnimation( CDmeAnimationList *pAnimationList, bool bVisibleOnly ) +{ + KeyValues *pContextKeyValues = new KeyValues( "context" ); + SetElementKeyValue( pContextKeyValues, "animationList", pAnimationList ); + pContextKeyValues->SetInt( "visibleOnly", bVisibleOnly ); + + int nCount = pAnimationList->GetAnimationCount(); + CUtlVector< DmePickerInfo_t > choices( 0, nCount ); + for ( int i = 0; i < nCount; ++i ) + { + CDmeChannelsClip *pAnimation = pAnimationList->GetAnimation( i ); + if ( !pAnimation ) + continue; + + int j = choices.AddToTail(); + DmePickerInfo_t& info = choices[j]; + info.m_hElement = pAnimation->GetHandle(); + info.m_pChoiceString = pAnimation->GetName(); + } + + CDmePickerFrame *pAnimationPicker = new CDmePickerFrame( this, "Select Animation To Import" ); + pAnimationPicker->DoModal( choices, pContextKeyValues ); +} + + +//----------------------------------------------------------------------------- +// Called by the FileOpenDialog in OnImportAnimation +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::OnFileSelected( KeyValues *kv ) +{ + KeyValues *pContextKeyValues = kv->FindKey( "ImportAnimation" ); + if ( !pContextKeyValues ) + return; + + bool bVisibleOnly = pContextKeyValues->GetInt( "visibleOnly" ); + if ( bVisibleOnly ) + { + CUtlVector< LogPreview_t > controls; + int nCount = BuildVisibleControlList( controls ); + if ( nCount == 0 ) + { + vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Error Importing Animations\n", + "Cannot import because there are no visible controls!\n", GetParent() ); + pMessageBox->DoModal( ); + return; + } + } + + const char *pFileName = kv->GetString( "fullpath", NULL ); + if ( !pFileName ) + return; + + CDmElement *pRoot; + CDisableUndoScopeGuard guard; + DmFileId_t fileId = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pRoot, CR_FORCE_COPY ); + guard.Release(); + + if ( fileId == DMFILEID_INVALID ) + return; + + CDmeChannelsClip *pChannelsClip = CastElement< CDmeChannelsClip >( pRoot ); + if ( pChannelsClip ) + { + ImportAnimation( pChannelsClip, bVisibleOnly ); + return; + } + + CDmeAnimationList* pAnimationList = CastElement< CDmeAnimationList >( pRoot ); + if ( !pAnimationList ) + { + pAnimationList = pRoot->GetValueElement< CDmeAnimationList >( "animationList" ); + } + + if ( !pAnimationList || pAnimationList->GetAnimationCount() == 0 ) + { + char pBuf[1024]; + Q_snprintf( pBuf, sizeof(pBuf), "File \"%s\" contains no animations!\n", pFileName ); + vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Error Importing Animations\n", pBuf, GetParent() ); + pMessageBox->DoModal( ); + + CDisableUndoScopeGuard sg; + g_pDataModel->RemoveFileId( pRoot->GetFileId() ); + sg.Release(); + return; + } + + if ( pAnimationList->GetAnimationCount() == 1 ) + { + ImportAnimation( pAnimationList->GetAnimation( 0 ), bVisibleOnly ); + } + else + { + SelectImportAnimation( pAnimationList, bVisibleOnly ); + } +} + + +//----------------------------------------------------------------------------- +// Called by the context menu to import animation files +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::OnImportAnimation( KeyValues *pParams ) +{ + // Compute starting directory + CDmeGameModel *pGameModel = m_AnimSet->GetValueElement< CDmeGameModel >( "gameModel" ); + + char pStartingDir[ MAX_PATH ]; + if ( !pGameModel ) + { + GetModContentSubdirectory( "models", pStartingDir, sizeof(pStartingDir) ); + } + else + { + char pModelName[ MAX_PATH ]; + studiohdr_t *pStudioHdr = pGameModel->GetStudioHdr(); + Q_StripExtension( pStudioHdr->pszName(), pModelName, sizeof(pModelName) ); + + char pRelativePath[ MAX_PATH ]; + Q_snprintf( pRelativePath, sizeof(pRelativePath), "models/%s/animations/dmx", pModelName ); + GetModContentSubdirectory( pRelativePath, pStartingDir, sizeof(pStartingDir) ); + if ( !g_pFullFileSystem->IsDirectory( pStartingDir ) ) + { + Q_snprintf( pRelativePath, sizeof(pRelativePath), "models/%s", pModelName ); + GetModContentSubdirectory( pRelativePath, pStartingDir, sizeof(pStartingDir) ); + if ( !g_pFullFileSystem->IsDirectory( pStartingDir ) ) + { + GetModContentSubdirectory( "models", pStartingDir, sizeof(pStartingDir) ); + } + } + } + + KeyValues *pContextKeyValues = new KeyValues( "ImportAnimation", "visibleOnly", pParams->GetInt( "visibleOnly" ) ); + FileOpenDialog *pDialog = new FileOpenDialog( this, "Select Animation File Name", true, pContextKeyValues ); + pDialog->SetStartDirectoryContext( "animation_set_import_animation", pStartingDir ); + pDialog->AddFilter( "*.*", "All Files (*.*)", false ); + pDialog->AddFilter( "*.dmx", "Animation file (*.dmx)", true ); + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( ); +} + + +//----------------------------------------------------------------------------- +// Main entry point for exporting facial animation +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::SetupFileOpenDialog( vgui::FileOpenDialog *pDialog, + bool bOpenFile, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + // Compute starting directory + char pStartingDir[ MAX_PATH ]; + GetModSubdirectory( "scenes", pStartingDir, sizeof(pStartingDir) ); + + Assert( !bOpenFile ); + pDialog->SetTitle( "Save Facial Animation As", true ); + + Assert( !V_strcmp( pFileFormat, "facial_animation" ) ); + pDialog->SetStartDirectoryContext( "facial_animation_export", pStartingDir ); + pDialog->AddFilter( "*.*", "All Files (*.*)", false ); + pDialog->AddFilter( "*.dmx", "Facial animation file (*.dmx)", true, pFileFormat ); +} + +bool CBaseAnimationSetEditor::OnReadFileFromDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + Assert( 0 ); + return false; +} + + +//----------------------------------------------------------------------------- +// Called when it's time to write the file +//----------------------------------------------------------------------------- +bool CBaseAnimationSetEditor::OnWriteFileToDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + // Recompute relative paths for each source now that we know the file name + // NOTE: This also updates the name of the fileID in the datamodel system + CDisableUndoScopeGuard guard; + bool bOk = ExportFacialAnimation( pFileName, GetRootClip(), GetAnimationSetClip(), m_AnimSet ); + return bOk; +} + + +//----------------------------------------------------------------------------- +// Main entry point for exporting facial animation +//----------------------------------------------------------------------------- +void CBaseAnimationSetEditor::OnExportFacialAnimation() +{ + KeyValues *pContextKeyValues = new KeyValues( "ExportFacialAnimation" ); + m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, "facial_animation", FOSM_SHOW_PERFORCE_DIALOGS ); +} + + +void CBaseAnimationSetEditor::OnOpenContextMenu( KeyValues *params ) +{ + if ( m_hContextMenu.Get() ) + { + delete m_hContextMenu.Get(); + m_hContextMenu = NULL; + } + + m_hContextMenu = new Menu( this, "ActionMenu" ); + + int c = ARRAYSIZE( g_AnimSetLayout ); + for ( int i = 0; i < c; ++i ) + { + const AnimSetLayout_t& data = g_AnimSetLayout[ i ]; + + m_hContextMenu->AddMenuItem( data.contextmenulabel, new KeyValues( "OnChangeLayout", "value", (int)data.type ), this ); + } + + if ( m_AnimSet.Get() ) + { + m_hContextMenu->AddSeparator( ); + m_hContextMenu->AddMenuItem( "#ImportAnimation", new KeyValues( "ImportAnimation" ), this ); + m_hContextMenu->AddMenuItem( "#ReattachToModel", new KeyValues( "ReattachToModel" ), this ); + m_hContextMenu->AddMenuItem( "#ExportFacialAnimation", new KeyValues( "ExportFacialAnimation" ), this ); + } + + Panel *rpanel = reinterpret_cast< Panel * >( params->GetPtr( "contextlabel" ) ); + if ( rpanel ) + { + // force the menu to compute required width/height + m_hContextMenu->PerformLayout(); + m_hContextMenu->PositionRelativeToPanel( rpanel, Menu::DOWN, 0, true ); + } + else + { + Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); + } +} + +void CBaseAnimationSetEditor::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + // Have to manually apply settings here if they aren't attached in hierarchy + if ( m_hControlGroup->GetParent() != this ) + { + m_hControlGroup->ApplySchemeSettings( pScheme ); + } + if ( m_hPresetFader->GetParent() != this ) + { + m_hPresetFader->ApplySchemeSettings( pScheme ); + } + if ( m_hAttributeSlider->GetParent() != this ) + { + m_hAttributeSlider->ApplySchemeSettings( pScheme ); + } + + m_pSelectionModeType->ClearImages(); + m_pSelectionModeType->AddImage( m_Images.GetImage( 1 ), 0 ); + + for ( int i = 0; i < NUM_AS_RECORDING_STATES; ++i ) + { + m_pState[ i ]->ClearImages(); + m_pState[ i ]->AddImage( m_Images.GetImage( i + 2 ), 0 ); + } + + m_pComboBox->SetFont( pScheme->GetFont( "DefaultBold" ) ); +} + +CBaseAnimSetControlGroupPanel *CBaseAnimationSetEditor::GetControlGroup() +{ + return m_hControlGroup.Get(); +} + +CBaseAnimSetPresetFaderPanel *CBaseAnimationSetEditor::GetPresetFader() +{ + return m_hPresetFader.Get(); +} + +CBaseAnimSetAttributeSliderPanel *CBaseAnimationSetEditor::GetAttributeSlider() +{ + return m_hAttributeSlider.Get(); +} + + + +void CBaseAnimationSetEditor::ChangeAnimationSet( CDmeAnimationSet *newAnimSet ) +{ + m_AnimSet = newAnimSet; + + if ( newAnimSet ) + { + CUndoScopeGuard guard( "Auto-create Procedural Presets" ); + newAnimSet->EnsureProceduralPresets(); + } + + // send to sub controls + m_hControlGroup->ChangeAnimationSet( newAnimSet ); + m_hPresetFader->ChangeAnimationSet( newAnimSet ); + m_hAttributeSlider->ChangeAnimationSet( newAnimSet ); +} + +void CBaseAnimationSetEditor::OnDataChanged() +{ +} + +void CBaseAnimationSetEditor::OnTextChanged() +{ + KeyValues *kv = m_pComboBox->GetActiveItemUserData(); + if ( !kv ) + return; + + CDmeAnimationSet *set = GetElementKeyValue< CDmeAnimationSet >( kv, "handle" ); + if ( set ) + { + ChangeAnimationSet( set ); + } +} + +void CBaseAnimationSetEditor::SetRecordingState( RecordingState_t state, bool /*updateSettings*/ ) +{ + m_RecordingState = state; + + // Reset buttons as needed + for ( int i = 0; i < NUM_AS_RECORDING_STATES; ++i ) + { + if ( (RecordingState_t)i == state ) + { + m_pState[ i ]->SetSelected( true ); + m_pState[ i ]->ForceDepressed( true ); + } + else + { + m_pState[ i ]->SetSelected( false ); + m_pState[ i ]->ForceDepressed( false ); + } + } +} + +RecordingState_t CBaseAnimationSetEditor::GetRecordingState() const +{ + return m_RecordingState; +} + +CDmeAnimationSet *CBaseAnimationSetEditor::GetAnimationSet() +{ + return m_AnimSet; +} + +int CBaseAnimationSetEditor::BuildVisibleControlList( CUtlVector< LogPreview_t >& list ) +{ + return m_hAttributeSlider->BuildVisibleControlList( list ); +} + +int CBaseAnimationSetEditor::BuildFullControlList( CUtlVector< LogPreview_t >& list ) +{ + return m_hAttributeSlider->BuildFullControlList( list ); +} + +void CBaseAnimationSetEditor::RecomputePreview() +{ + m_hAttributeSlider->RecomputePreview(); +} + diff --git a/vgui2/dme_controls/BaseAttributeChoicePanel.cpp b/vgui2/dme_controls/BaseAttributeChoicePanel.cpp new file mode 100644 index 0000000..3440a7f --- /dev/null +++ b/vgui2/dme_controls/BaseAttributeChoicePanel.cpp @@ -0,0 +1,91 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/BaseAttributeChoicePanel.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ComboBox.h" +#include "datamodel/dmelement.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CBaseAttributeChoicePanel::CBaseAttributeChoicePanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ), m_pData( 0 ) +{ + SetDropEnabled( false ); + m_pData = new vgui::ComboBox( this, "AttributeValue", 10, false ); + m_pData->SetEnabled( !HasFlag( FATTRIB_READONLY ) ); + m_pData->AddActionSignalTarget( this ); +} + + +//----------------------------------------------------------------------------- +// Called after the constructor is finished +//----------------------------------------------------------------------------- +void CBaseAttributeChoicePanel::PostConstructor() +{ + BaseClass::PostConstructor(); + PopulateComboBox( m_pData ); + Refresh(); +} + + +void CBaseAttributeChoicePanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + HFont font = pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ); + m_pData->SetFont(font); +} + +vgui::Panel *CBaseAttributeChoicePanel::GetDataPanel() +{ + return static_cast< vgui::Panel * >( m_pData ); +} + + +//----------------------------------------------------------------------------- +// Called when it is time to set the attribute from the combo box state +//----------------------------------------------------------------------------- +void CBaseAttributeChoicePanel::Apply( ) +{ + KeyValues *kv = m_pData->GetActiveItemUserData(); + SetAttributeFromComboBox( m_pData, kv ); +} + + +//----------------------------------------------------------------------------- +// Called when it is time to set the combo box from the attribute +//----------------------------------------------------------------------------- +void CBaseAttributeChoicePanel::Refresh() +{ + SetComboBoxFromAttribute( m_pData ); +} + + +//----------------------------------------------------------------------------- +// Called when the text in the panel changes +//----------------------------------------------------------------------------- +void CBaseAttributeChoicePanel::OnTextChanged( Panel *panel ) +{ + if ( IsAutoApply() ) + { + Apply(); + } + else + { + SetDirty(true); + } +} + diff --git a/vgui2/dme_controls/BaseAttributeDoubleChoicePanel.cpp b/vgui2/dme_controls/BaseAttributeDoubleChoicePanel.cpp new file mode 100644 index 0000000..40dc9e8 --- /dev/null +++ b/vgui2/dme_controls/BaseAttributeDoubleChoicePanel.cpp @@ -0,0 +1,127 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/BaseAttributeDoubleChoicePanel.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ComboBox.h" +#include "datamodel/dmelement.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +CDoubleComboBoxContainerPanel::CDoubleComboBoxContainerPanel( vgui::Panel *parent, char const *name ) : + BaseClass( parent, name ) +{ + m_pBoxes[ 0 ] = m_pBoxes[ 1 ] = NULL; +} + +void CDoubleComboBoxContainerPanel::AddComboBox( int slot, vgui::ComboBox *box ) +{ + m_pBoxes[ slot ] = box; +} + +void CDoubleComboBoxContainerPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + int w, h; + GetSize( w, h ); + + if ( m_pBoxes[ 0 ] ) + { + m_pBoxes[ 0 ]->SetBounds( 0, 0, w/2, h ); + } + if ( m_pBoxes[ 1 ] ) + { + m_pBoxes[ 1 ]->SetBounds( w/2, 0, w/2, h ); + } +} + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CBaseAttributeDoubleChoicePanel::CBaseAttributeDoubleChoicePanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ + SetDropEnabled( false ); + + m_pContainerPanel = new CDoubleComboBoxContainerPanel( this, "Container" ); + m_pData[0] = new vgui::ComboBox( m_pContainerPanel, "AttributeValue", 10, false ); + m_pData[0]->SetEnabled( !HasFlag( FATTRIB_READONLY ) ); + m_pData[0]->AddActionSignalTarget( this ); + m_pContainerPanel->AddComboBox( 0, m_pData[ 0 ] ); + m_pData[1] = new vgui::ComboBox( m_pContainerPanel, "AttributeValue", 10, false ); + m_pData[1]->SetEnabled( !HasFlag( FATTRIB_READONLY ) ); + m_pData[1]->AddActionSignalTarget( this ); + m_pContainerPanel->AddComboBox( 1, m_pData[ 1 ] ); +} + + +//----------------------------------------------------------------------------- +// Called after the constructor is finished +//----------------------------------------------------------------------------- +void CBaseAttributeDoubleChoicePanel::PostConstructor() +{ + BaseClass::PostConstructor(); + PopulateComboBoxes( m_pData ); + Refresh(); +} + + +void CBaseAttributeDoubleChoicePanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + HFont font = pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ); + m_pData[0]->SetFont(font); + m_pData[1]->SetFont(font); +} + +vgui::Panel *CBaseAttributeDoubleChoicePanel::GetDataPanel() +{ + return static_cast< vgui::Panel * >( m_pContainerPanel ); +} + + +//----------------------------------------------------------------------------- +// Called when it is time to set the attribute from the combo box state +//----------------------------------------------------------------------------- +void CBaseAttributeDoubleChoicePanel::Apply( ) +{ + Assert( m_pData[ 0 ] && m_pData[ 1 ] ); + + KeyValues *kv[ 2 ]; + kv[ 0 ] = m_pData[ 0 ]->GetActiveItemUserData(); + kv[ 1 ] = m_pData[ 1 ]->GetActiveItemUserData(); + + SetAttributeFromComboBoxes( m_pData, kv ); +} + + +//----------------------------------------------------------------------------- +// Called when it is time to set the combo box from the attribute +//----------------------------------------------------------------------------- +void CBaseAttributeDoubleChoicePanel::Refresh() +{ + SetComboBoxesFromAttribute( m_pData ); +} + + +//----------------------------------------------------------------------------- +// Called when the text in the panel changes +//----------------------------------------------------------------------------- +void CBaseAttributeDoubleChoicePanel::OnTextChanged( Panel *panel ) +{ + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +} + diff --git a/vgui2/dme_controls/BaseAttributePanel.cpp b/vgui2/dme_controls/BaseAttributePanel.cpp new file mode 100644 index 0000000..85f6b72 --- /dev/null +++ b/vgui2/dme_controls/BaseAttributePanel.cpp @@ -0,0 +1,365 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: base class for all element attribute panels +// An attribute panel is a one line widget that can be used by a list +// or tree control. +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/BaseAttributePanel.h" +#include "dme_controls/attributewidgetfactory.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/Label.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "dme_controls/inotifyui.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Lessfunc for columns. +//----------------------------------------------------------------------------- +bool CBaseAttributePanel::ColInfoLessFunc( const CBaseAttributePanel::colinfo_t& lhs, const CBaseAttributePanel::colinfo_t& rhs ) +{ + return lhs.panel < rhs.panel; +} + + +//----------------------------------------------------------------------------- +// CBaseAttributePanel constructor +//----------------------------------------------------------------------------- +CBaseAttributePanel::CBaseAttributePanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info.m_pAttributeName ), + m_pType( 0 ), + m_hObject( info.m_pElement ), + m_hEditorInfo( info.m_pEditorInfo ), + m_hEditorTypeDict( info.m_pEditorTypeDictionary ), + m_pNotify( info.m_pNotify ), + m_nArrayIndex( info.m_nArrayIndex ), + m_ColumnSize( 0, 0, ColInfoLessFunc ) +{ + Assert( info.m_pElement ); + + InitializeFlags( info ); + + Assert( info.m_pAttributeName ); + Q_strncpy( m_szAttributeName, info.m_pAttributeName, sizeof( m_szAttributeName ) ); + + m_pType = new Label( this, "AttributeType", "" ); + SetColumnSize( m_pType, 100 ); + + CDmAttribute *pAttribute = info.m_pElement->GetAttribute( info.m_pAttributeName ); + m_AttributeType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN; + if ( m_nArrayIndex >= 0 ) + { + m_AttributeType = ArrayTypeToValueType( m_AttributeType ); + } + + m_pType->SetText( g_pDataModel->GetAttributeNameForType( m_AttributeType ) ); + + m_hFont = NULL; + + // These are draggable + SetDragEnabled( true ); +} + + +//----------------------------------------------------------------------------- +// This only exists so every class always can chain PostConstructors +//----------------------------------------------------------------------------- +void CBaseAttributePanel::PostConstructor() +{ +} + + +//----------------------------------------------------------------------------- +// Initializes flags from the attribute editor info +//----------------------------------------------------------------------------- +void CBaseAttributePanel::InitializeFlags( const AttributeWidgetInfo_t &info ) +{ + m_nFlags = 0; + if ( info.m_pEditorInfo ) + { + if ( info.m_pEditorInfo->m_bHideType ) + { + m_nFlags |= HIDETYPE; + } + if ( info.m_pEditorInfo->m_bHideValue ) + { + m_nFlags |= HIDEVALUE; + } + if ( info.m_pEditorInfo->m_bIsReadOnly ) + { + m_nFlags |= READONLY; + } + } + + CDmAttribute *pAttribute = info.m_pElement->GetAttribute( info.m_pAttributeName ); + if ( pAttribute && pAttribute->IsFlagSet( FATTRIB_READONLY ) ) + { + m_nFlags |= READONLY; + } + + if ( info.m_bAutoApply ) + { + m_nFlags |= AUTOAPPLY; + } +} + + +//----------------------------------------------------------------------------- +// Returns the editor info +//----------------------------------------------------------------------------- +CDmeEditorTypeDictionary *CBaseAttributePanel::GetEditorTypeDictionary() +{ + return m_hEditorTypeDict; +} + +CDmeEditorAttributeInfo *CBaseAttributePanel::GetEditorInfo() +{ + return m_hEditorInfo; +} + + +//----------------------------------------------------------------------------- +// Does the element have the attribute we're attempting to reference? +//----------------------------------------------------------------------------- +bool CBaseAttributePanel::HasAttribute() const +{ + return GetPanelElement()->HasAttribute( m_szAttributeName ); +} + + +//----------------------------------------------------------------------------- +// Returns the attribute array count +//----------------------------------------------------------------------------- +int CBaseAttributePanel::GetAttributeArrayCount() const +{ + CDmrGenericArrayConst array( GetPanelElement(), m_szAttributeName ); + return array.IsValid() ? array.Count() : -1; +} + + +//----------------------------------------------------------------------------- +// Sets the font +//----------------------------------------------------------------------------- +void CBaseAttributePanel::SetFont( HFont font ) +{ + m_hFont = font; + m_pType->SetFont(font); +} + +//----------------------------------------------------------------------------- +// Applies scheme settings +//----------------------------------------------------------------------------- +void CBaseAttributePanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + // set the color of the "type" column + m_pType->SetFgColor( Color ( 160, 160, 160, 255 ) ); + + if ( GetDirty() ) + { + SetBgColor( pScheme->GetColor( "AttributeWidget.DirtyBgColor", Color( 100, 100, 200, 63 ) ) ); + } + else + { + SetBgColor( pScheme->GetColor( "Panel.BgColor", Color( 0, 0, 0, 0 ) ) ); + } + HFont font = pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ); + // m_pType->SetFont(font); + + if ( !m_hFont ) + { + m_hFont = font; + } + SetFont( m_hFont ); + +} + +//----------------------------------------------------------------------------- +// Returns the panel element +//----------------------------------------------------------------------------- +CDmElement *CBaseAttributePanel::GetPanelElement() +{ + return m_hObject; +} + +const CDmElement *CBaseAttributePanel::GetPanelElement() const +{ + return m_hObject; +} + + +//----------------------------------------------------------------------------- +// Gets/Sets the attribute value from a string +//----------------------------------------------------------------------------- +void CBaseAttributePanel::SetAttributeValueFromString( const char *pString ) +{ + if ( m_nArrayIndex < 0 ) + { + GetPanelElement()->SetValueFromString( m_szAttributeName, pString ); + } + else + { + CDmrGenericArray array( GetPanelElement(), m_szAttributeName ); + array.SetFromString( m_nArrayIndex, pString ); + } +} + +const char *CBaseAttributePanel::GetAttributeValueAsString( char *pBuf, int nLength ) +{ + if ( m_nArrayIndex < 0 ) + { + GetPanelElement()->GetValueAsString( m_szAttributeName, pBuf, nLength ); + } + else + { + CDmrGenericArray array( GetPanelElement(), m_szAttributeName ); + array.GetAsString( m_nArrayIndex, pBuf, nLength ); + } + return pBuf; +} + + +//----------------------------------------------------------------------------- +// Helper to get/set the attribute value for elements +//----------------------------------------------------------------------------- +CDmElement *CBaseAttributePanel::GetAttributeValueElement() +{ + return GetElement< CDmElement >( GetAttributeValue<DmElementHandle_t>( ) ); +} + +void CBaseAttributePanel::SetAttributeValueElement( CDmElement *pElement ) +{ + return SetAttributeValue( pElement->GetHandle() ); +} + + +void CBaseAttributePanel::SetDirty( bool dirty ) +{ + SetFlag( DIRTY, dirty ); + InvalidateLayout( false, true ); +} + +void CBaseAttributePanel::SetColumnSize( Panel *panel, int width ) +{ + colinfo_t search; + search.panel = panel; + int idx = m_ColumnSize.Find( search ); + if ( idx == m_ColumnSize.InvalidIndex() ) + { + idx = m_ColumnSize.Insert( search ); + } + m_ColumnSize[ idx ].width = width; +} + +int CBaseAttributePanel::GetSizeForColumn( Panel *panel ) +{ + colinfo_t search; + search.panel = panel; + int idx = m_ColumnSize.Find( search ); + if ( idx == m_ColumnSize.InvalidIndex() ) + { + return 100; + } + return m_ColumnSize[ idx ].width; +} + + +//----------------------------------------------------------------------------- +// Creates a widget using editor attribute info +//----------------------------------------------------------------------------- +void CBaseAttributePanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + CUtlVector< Panel * > vispanels; + + if ( HasFlag( HIDETYPE ) ) + { + m_pType->SetVisible( false ); + } + else + { + vispanels.AddToTail( m_pType ); + } + + vgui::Panel *dataPanel = GetDataPanel(); + if ( dataPanel ) + { + if ( HasFlag( HIDEVALUE ) ) + { + dataPanel->SetVisible( false ); + } + else + { + vispanels.AddToTail( dataPanel ); + } + } + + int c = vispanels.Count(); + + Assert( c >= 0 ); + if ( c == 0 ) + { + return; + } + + int w, h; + GetSize( w, h ); + + int x = 1; + int y = 0; + w-= 2; + + for ( int i = 0; i < c; ++i ) + { + Panel *panel = vispanels[ i ]; + int width = GetSizeForColumn( panel ); + if ( i == c - 1 ) + { + width = w - x; + } + + panel->SetBounds( x, y, width, h ); + x += width; + } +} + +void CBaseAttributePanel::OnApplyChanges() +{ + Assert( !IsAutoApply() ); + + Apply(); + SetDirty(false); +} + +void CBaseAttributePanel::OnRefresh() +{ + Refresh(); +} + +void CBaseAttributePanel::OnCreateDragData( KeyValues *msg ) +{ + if ( GetPanelElement() ) + { + msg->SetInt( "root", GetPanelElement() ? GetPanelElement()->GetHandle() : DMELEMENT_HANDLE_INVALID ); + msg->SetString( "type", g_pDataModel->GetAttributeNameForType( m_AttributeType ) ); + msg->SetString( "attributename", m_szAttributeName ); + if ( m_nArrayIndex >= 0 ) + { + msg->SetInt( "arrayIndex", m_nArrayIndex ); + } + + if ( m_AttributeType != AT_ELEMENT && m_AttributeType != AT_ELEMENT_ARRAY ) + { + char pTemp[512]; + GetAttributeValueAsString( pTemp, sizeof( pTemp ) ); + msg->SetString( "text", pTemp ); + } + } +} diff --git a/vgui2/dme_controls/ChannelGraphPanel.cpp b/vgui2/dme_controls/ChannelGraphPanel.cpp new file mode 100644 index 0000000..b7b1ed9 --- /dev/null +++ b/vgui2/dme_controls/ChannelGraphPanel.cpp @@ -0,0 +1,368 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include <math.h> + +#include <dme_controls/ChannelGraphPanel.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include "vgui/IInput.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +DECLARE_BUILD_FACTORY( CChannelGraphPanel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CChannelGraphPanel::CChannelGraphPanel( Panel *parent, const char *name ) + : BaseClass( parent, name ), m_font( 0 ), + m_graphMinTime( 0 ), m_graphMaxTime( 0 ), + m_graphMinValue( 0.0f ), m_graphMaxValue( 0.0f ), + m_nMouseStartX( -1 ), m_nMouseStartY( -1 ), + m_nMouseLastX( -1 ), m_nMouseLastY( -1 ), + m_nTextBorder( 2 ), m_nGraphOriginX( 40 ), m_nGraphOriginY( 10 ) +{ +} + + +void CChannelGraphPanel::SetChannel( CDmeChannel *pChannel ) +{ + m_hChannel = pChannel; + CDmeLog *pLog = m_hChannel->GetLog(); + m_graphMinTime = pLog->GetBeginTime(); + m_graphMaxTime = pLog->GetEndTime(); + + m_graphMinValue = FLT_MAX; + m_graphMaxValue = -FLT_MAX; + + int nComponents = NumComponents( pLog->GetDataType() ); + int nKeys = pLog->GetKeyCount(); + for ( int k = 0; k < nKeys; ++k ) + { + DmeTime_t t = pLog->GetKeyTime( k ); + for ( int i = 0; i < nComponents; ++i ) + { + float f = pLog->GetComponent( t, i ); + m_graphMinValue = min( m_graphMinValue, f ); + m_graphMaxValue = max( m_graphMaxValue, f ); + } + } +} + +//----------------------------------------------------------------------------- +// input methods +//----------------------------------------------------------------------------- +void CChannelGraphPanel::OnSizeChanged( int newWide, int newTall ) // called after the size of a panel has been changed +{ + int wide = newWide - m_nGraphOriginX; + int tall = newTall - m_nGraphOriginY; + m_flTimeToPixel = wide / ( m_graphMaxTime - m_graphMinTime ).GetSeconds(); + m_flValueToPixel = tall / ( m_graphMaxValue - m_graphMinValue ); +} + +void CChannelGraphPanel::OnMousePressed( MouseCode code ) +{ + BaseClass::OnMousePressed( code ); + if ( code != MOUSE_LEFT ) + return; + + vgui::input()->GetCursorPos( m_nMouseStartX, m_nMouseStartY ); + ScreenToLocal( m_nMouseStartX, m_nMouseStartY ); + m_nMouseLastX = m_nMouseStartX; + m_nMouseLastY = m_nMouseStartY; + + input()->SetMouseCapture( GetVPanel() ); +} + +void CChannelGraphPanel::OnMouseReleased( MouseCode code ) +{ + BaseClass::OnMouseReleased( code ); + if ( code != MOUSE_LEFT ) + return; + + m_nMouseStartX = m_nMouseStartY = -1; + m_nMouseLastX = m_nMouseLastY = -1; + + input()->SetMouseCapture( NULL ); +} + +void CChannelGraphPanel::OnCursorMoved( int mx, int my ) +{ + BaseClass::OnCursorMoved( mx, my ); + if ( !vgui::input()->IsMouseDown( MOUSE_LEFT ) ) + return; + + bool bInValueLegend = m_nMouseStartX < m_nGraphOriginX; + bool bInTimeLegend = m_nMouseStartY > GetTall() - m_nGraphOriginY - 1; + if ( bInTimeLegend && bInValueLegend ) + { + bInTimeLegend = bInValueLegend = false; + } + + int dx = mx - m_nMouseLastX; + int dy = my - m_nMouseLastY; + + if ( bInTimeLegend ) + { + if ( abs( dy ) > abs( dx ) ) + { + m_graphMinTime -= DmeTime_t( dy / m_flTimeToPixel ); + m_graphMaxTime += DmeTime_t( dy / m_flTimeToPixel ); + m_flTimeToPixel = ( GetWide() - m_nGraphOriginX ) / ( m_graphMaxTime - m_graphMinTime ).GetSeconds(); + + int x = mx = m_nMouseLastX; + int y = my = m_nMouseLastY; + LocalToScreen( x, y ); + vgui::input()->SetCursorPos( x, y ); + } + else + { + m_graphMinTime -= DmeTime_t( dx / m_flTimeToPixel ); + m_graphMaxTime -= DmeTime_t( dx / m_flTimeToPixel ); + } + } + else if ( bInValueLegend ) + { + if ( abs( dx ) > abs( dy ) ) + { + m_graphMinValue += dx / m_flValueToPixel; + m_graphMaxValue -= dx / m_flValueToPixel; + m_flValueToPixel = ( GetTall() - m_nGraphOriginY ) / ( m_graphMaxValue - m_graphMinValue ); + + int x = mx = m_nMouseLastX; + int y = my = m_nMouseLastY; + LocalToScreen( x, y ); + vgui::input()->SetCursorPos( x, y ); + } + else + { + m_graphMinValue += dy / m_flValueToPixel; + m_graphMaxValue += dy / m_flValueToPixel; + } + } + + m_nMouseLastX = mx; + m_nMouseLastY = my; +} + +void CChannelGraphPanel::OnMouseWheeled( int delta ) +{ + // TODO - zoom in around current time? +} + +//----------------------------------------------------------------------------- +// Purpose: lays out the graph +//----------------------------------------------------------------------------- +void CChannelGraphPanel::PerformLayout() +{ + BaseClass::PerformLayout(); +} + + +float GetDisplayIncrement( int windowpixels, int fontpixels, float valuerange, int *pDecimalPlaces = NULL ) +{ + float ratio = valuerange * fontpixels / ( windowpixels ); + int nPower = ( int )ceil( log10( ratio ) ); + if ( pDecimalPlaces ) + { + *pDecimalPlaces = max( 0, -nPower ); + } + return pow( 10.0f, nPower ); +} + +int CChannelGraphPanel::TimeToPixel( DmeTime_t time ) +{ + return m_nGraphOriginX + ( int )floor( m_flTimeToPixel * ( time - m_graphMinTime ).GetSeconds() + 0.5f ); +} + +int CChannelGraphPanel::ValueToPixel( float flValue ) +{ + return m_nGraphOriginY + ( int )floor( m_flValueToPixel * ( flValue - m_graphMinValue ) + 0.5f ); +} + +//----------------------------------------------------------------------------- +// Purpose: draws the graph +//----------------------------------------------------------------------------- +void CChannelGraphPanel::Paint() +{ + // estimate the size of the graph marker text + int wide = GetWide() - m_nGraphOriginX; + int tall = GetTall() - m_nGraphOriginY; + + int textwidth = 40, textheight = 10; + surface()->GetTextSize( m_font, L"999.9", textwidth, textheight ); + + // draw current time marker + DmeTime_t curtime = m_hChannel->GetCurrentTime(); + if ( curtime >= m_graphMinTime && curtime <= m_graphMaxTime ) + { + Color cyan( 0, 255, 255, 255 ); + surface()->DrawSetColor( cyan ); + int x = TimeToPixel( curtime ); + surface()->DrawLine( x, 0, x, GetTall() - m_nGraphOriginY - 1 ); + } + + // draw left/bottom graph border + Color black( 0, 0, 0, 255 ); + surface()->DrawSetColor( black ); + surface()->DrawLine( m_nGraphOriginX, GetTall() - m_nGraphOriginY - 1, GetWide(), GetTall() - m_nGraphOriginY - 1 ); + surface()->DrawLine( m_nGraphOriginX, GetTall() - m_nGraphOriginY - 1, m_nGraphOriginX, 0 ); + + surface()->DrawSetTextColor( black ); + surface()->DrawSetTextFont( m_font ); + + // draw graph tickmarks and values along the left border + int nDecimalPlaces = 0; + float flValueIncrement = GetDisplayIncrement( tall, ( int )( 1.5f * textheight ), m_graphMaxValue - m_graphMinValue, &nDecimalPlaces ); + int nMinValueIndex = ( int )ceil ( m_graphMinValue / flValueIncrement ); + int nMaxValueIndex = ( int )floor( m_graphMaxValue / flValueIncrement ); + float flValue = nMinValueIndex * flValueIncrement; + for ( int i = nMinValueIndex; i <= nMaxValueIndex; ++i, flValue += flValueIncrement ) + { + wchar_t pFormat[ 32 ]; + V_swprintf_safe( pFormat, L"%%.%df", nDecimalPlaces ); + + wchar_t wstring[ 32 ]; + V_swprintf_safe( wstring, pFormat, flValue ); + + int tw = 0, th = 0; + surface()->GetTextSize( m_font, wstring, tw, th ); + + int y = GetTall() - ValueToPixel( flValue ) - 1; + surface()->DrawSetTextPos( m_nGraphOriginX - m_nTextBorder - tw, y - textheight / 2 ); + surface()->DrawPrintText( wstring, wcslen( wstring ) ); + + surface()->DrawLine( m_nGraphOriginX - m_nTextBorder, y, m_nGraphOriginX, y ); + } + + // draw graph tickmarks and times along the bottom border + float flTimeIncrement = GetDisplayIncrement( wide, textwidth, ( m_graphMaxTime - m_graphMinTime ).GetSeconds(), &nDecimalPlaces ); + int nMinTimeIndex = ( int )ceil ( m_graphMinTime.GetSeconds() / flTimeIncrement ); + int nMaxTimeIndex = ( int )floor( m_graphMaxTime.GetSeconds() / flTimeIncrement ); + float flTime = nMinTimeIndex * flTimeIncrement; + for ( int i = nMinTimeIndex; i <= nMaxTimeIndex; ++i, flTime += flTimeIncrement ) + { + wchar_t pFormat[ 32 ]; + V_swprintf_safe( pFormat, L"%%.%df", nDecimalPlaces ); + + wchar_t wstring[ 32 ]; + V_swprintf_safe( wstring, pFormat, flTime ); + + int tw = 0, th = 0; + surface()->GetTextSize( m_font, wstring, tw, th ); + + int x = TimeToPixel( DmeTime_t( flTime ) ); + surface()->DrawSetTextPos( x - tw / 2, GetTall() - m_nGraphOriginY + m_nTextBorder - 1 ); + surface()->DrawPrintText( wstring, wcslen( wstring ) ); + + surface()->DrawLine( x, GetTall() - m_nGraphOriginY + m_nTextBorder - 1, x, GetTall() - m_nGraphOriginY - 1 ); + } + + static Color s_componentColors[] = + { + Color( 255, 0, 0, 255 ), + Color( 0, 255, 0, 255 ), + Color( 0, 0, 255, 255 ), + Color( 0, 0, 0, 255 ), + }; + + CDmeLog *pLog = m_hChannel->GetLog(); + int nComponents = NumComponents( pLog->GetDataType() ); + int nKeys = pLog->GetKeyCount(); + + // draw plotted graph + for ( int i = 0; i < nComponents; ++i ) + { + Color &color = s_componentColors[ i % ARRAYSIZE( s_componentColors ) ]; + surface()->DrawSetColor( color ); + + int lastx = -1; + int lasty = -1; + for ( int k = 0; k < nKeys; ++k ) + { + DmeTime_t t = pLog->GetKeyTime( k ); + float f = pLog->GetComponent( t, i ); + int x = TimeToPixel( t ); + int y = GetTall() - ValueToPixel( f ) - 1; + if ( k ) + { + surface()->DrawLine( lastx, lasty, x, y ); + } + lastx = x; + lasty = y; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets up colors +//----------------------------------------------------------------------------- +void CChannelGraphPanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + m_font = pScheme->GetFont( "DefaultVerySmall" ); + + surface()->GetTextSize( m_font, L"999.9", m_nGraphOriginX, m_nGraphOriginY ); + m_nGraphOriginX += 2 * m_nTextBorder; + m_nGraphOriginY += 2 * m_nTextBorder; + + SetFgColor(GetSchemeColor("CChannelGraphPanel.FgColor", pScheme)); + SetBgColor(GetSchemeColor("CChannelGraphPanel.BgColor", pScheme)); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); +} + + +//----------------------------------------------------------------------------- +// +// CChannelGraphFrame methods +// +//----------------------------------------------------------------------------- +CChannelGraphFrame::CChannelGraphFrame( Panel *parent, const char *pTitle ) +: BaseClass( parent, "CChannelGraphFrame" ) +{ + SetTitle( pTitle, true ); + + SetSizeable( true ); + SetCloseButtonVisible( true ); + SetMinimumSize( 200, 100 ); + + SetVisible( true ); + + SetSize( 400, 200 ); + SetPos( 100, 100 ); + + m_pChannelGraph = new CChannelGraphPanel( this, "ChannelGraph" ); + + SetScheme( vgui::scheme()->LoadSchemeFromFile( "Resource/BoxRocket.res", "BoxRocket" ) ); +} + +void CChannelGraphFrame::SetChannel( CDmeChannel *pChannel ) +{ + m_pChannelGraph->SetChannel( pChannel ); +} + +void CChannelGraphFrame::OnCommand( const char *cmd ) +{ + BaseClass::OnCommand( cmd ); + m_pChannelGraph->OnCommand( cmd ); +} + +void CChannelGraphFrame::PerformLayout() +{ + BaseClass::PerformLayout(); + + int border = 5; + int iWidth, iHeight; + GetSize( iWidth, iHeight ); + m_pChannelGraph->SetPos( border, GetCaptionHeight() + border ); + m_pChannelGraph->SetSize( iWidth - 2 * border, iHeight - GetCaptionHeight() - 2 * border ); +} diff --git a/vgui2/dme_controls/DmeSourceDCCFilePanel.cpp b/vgui2/dme_controls/DmeSourceDCCFilePanel.cpp new file mode 100644 index 0000000..0c5f30b --- /dev/null +++ b/vgui2/dme_controls/DmeSourceDCCFilePanel.cpp @@ -0,0 +1,488 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/DmeSourceDCCFilePanel.h" +#include "dme_controls/DmePanel.h" +#include "movieobjects/dmedccmakefile.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/InputDialog.h" +#include "vgui_controls/MessageBox.h" +#include "vgui/keycode.h" +#include "tier1/KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Hook into the dme panel editor system +// +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CDmeSourceDCCFilePanel, DmeSourceDCCFile, "DmeSourceDCCFileDefault", "Maya/XSI Source File Editor", true ); + + +//----------------------------------------------------------------------------- +// Sort by MDL name +//----------------------------------------------------------------------------- +static int __cdecl DccObjectSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString( "dccobject" ); + const char *string2 = item2.kv->GetString( "dccobject" ); + return Q_stricmp( string1, string2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor, destructor +//----------------------------------------------------------------------------- +CDmeSourceDCCFilePanel::CDmeSourceDCCFilePanel( vgui::Panel *pParent, const char *pPanelName ) : + BaseClass( pParent, pPanelName ) +{ + m_pRootDCCObjects = new vgui::ListPanel( this, "DCCObjectList" ); + m_pRootDCCObjects->AddColumnHeader( 0, "dccobject", "Maya/XSI Object Name", 100, 0 ); + m_pRootDCCObjects->AddActionSignalTarget( this ); + m_pRootDCCObjects->SetSortFunc( 0, DccObjectSortFunc ); + m_pRootDCCObjects->SetSortColumn( 0 ); + // m_pRootDCCObjects->SetSelectIndividualCells( true ); + m_pRootDCCObjects->SetEmptyListText("No sources"); + // m_pRootDCCObjects->SetDragEnabled( true ); + + m_pDCCObjectBrowser = new vgui::Button( this, "DCCObjectBrowser", "...", this, "OnBrowseDCCObject" ); + m_pDCCObjectName = new vgui::TextEntry( this, "DCCObjectName" ); + m_pDCCObjectName->SendNewLine( true ); + m_pDCCObjectName->AddActionSignalTarget( this ); + + m_pAddDCCObject = new vgui::Button( this, "AddDCCObjectButton", "Add", this, "OnAddDCCObject" ); + m_pRemoveDCCObject = new vgui::Button( this, "RemoveDCCObjectButton", "Remove", this, "OnRemoveDCCObject" ); + + m_pApplyChanges = new vgui::Button( this, "ApplyChangesButton", "Apply", this, "OnApplyChanges" ); + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettings( "resource/DmeSourceDCCFilePanel.res" ); +} + +CDmeSourceDCCFilePanel::~CDmeSourceDCCFilePanel() +{ +} + + +//----------------------------------------------------------------------------- +// Marks the file as dirty (or not) +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::SetDirty() +{ + PostActionSignal( new KeyValues( "DmeElementChanged" ) ); +} + + +//----------------------------------------------------------------------------- +// Refresh the source list +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::RefreshDCCObjectList( ) +{ + m_pRootDCCObjects->RemoveAll(); + if ( !m_hSourceDCCFile.Get() ) + return; + + int nCount = m_hSourceDCCFile->m_RootDCCObjects.Count(); + for ( int i = 0; i < nCount; ++i ) + { + KeyValues *pItemKeys = new KeyValues( "node", "dccobject", m_hSourceDCCFile->m_RootDCCObjects.Get(i) ); + pItemKeys->SetInt( "dccObjectIndex", i ); + m_pRootDCCObjects->AddItem( pItemKeys, 0, false, false ); + } + + m_pRootDCCObjects->SortList(); +} + + +//----------------------------------------------------------------------------- +// Resets the state +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::SetDmeElement( CDmeSourceDCCFile *pSourceDCCFile ) +{ + m_hSourceDCCFile = pSourceDCCFile; + + bool bEnabled = ( pSourceDCCFile != NULL ); + m_pDCCObjectBrowser->SetEnabled( bEnabled ); + m_pAddDCCObject->SetEnabled( bEnabled ); + m_pRemoveDCCObject->SetEnabled( bEnabled ); + m_pApplyChanges->SetEnabled( bEnabled ); + if ( !bEnabled ) + { + m_pRootDCCObjects->RemoveAll(); + m_pDCCObjectName->SetText( "" ); + return; + } + + RefreshDCCObjectList(); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnItemSelectionChanged( ) +{ + int nCount = m_pRootDCCObjects->GetSelectedItemsCount(); + bool bEnabled = ( nCount > 0 ); + bool bMultiselect = ( nCount > 1 ); + m_pDCCObjectBrowser->SetEnabled( bEnabled && !bMultiselect ); + m_pDCCObjectName->SetEnabled( bEnabled && !bMultiselect ); + m_pApplyChanges->SetEnabled( bEnabled && !bMultiselect ); + m_pRemoveDCCObject->SetEnabled( bEnabled ); + if ( !bEnabled || bMultiselect ) + { + m_pDCCObjectName->SetText( "" ); + return; + } + + int nItemID = m_pRootDCCObjects->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pRootDCCObjects->GetItem( nItemID ); + m_pDCCObjectName->SetText( pKeyValues->GetString( "dccobject" ) ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pRootDCCObjects ) + { + OnItemSelectionChanged(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnItemDeselected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pRootDCCObjects ) + { + OnItemSelectionChanged(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when return is hit in a text entry field +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnTextNewLine( KeyValues *kv ) +{ + if ( !m_hSourceDCCFile.Get() ) + return; + + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pDCCObjectName ) + { + OnDCCObjectNameChanged(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Selects a particular DCC object +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::SelectDCCObject( int nDCCObjectIndex ) +{ + if ( nDCCObjectIndex < 0 ) + { + m_pRootDCCObjects->ClearSelectedItems(); + return; + } + + int nItemID = m_pRootDCCObjects->FirstItem(); + for ( ; nItemID != m_pRootDCCObjects->InvalidItemID(); nItemID = m_pRootDCCObjects->NextItem( nItemID ) ) + { + KeyValues *kv = m_pRootDCCObjects->GetItem( nItemID ); + if ( kv->GetInt( "dccObjectIndex", -1 ) != nDCCObjectIndex ) + continue; + + m_pRootDCCObjects->SetSingleSelectedItem( nItemID ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Called when we're browsing for a DCC object and one was selected +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnDCCObjectAdded( const char *pDCCObjectName, KeyValues *pContextKeys ) +{ + if ( !m_hSourceDCCFile.Get() ) + return; + + if ( CheckForDuplicateNames( pDCCObjectName ) ) + return; + + int nIndex = -1; + { + CDisableUndoScopeGuard guard; + nIndex = m_hSourceDCCFile->m_RootDCCObjects.AddToTail( pDCCObjectName ); + } + SetDirty( ); + RefreshDCCObjectList( ); + SelectDCCObject( nIndex ); +} + + +//----------------------------------------------------------------------------- +// Called when the file open dialog for browsing source files selects something +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnInputCompleted( KeyValues *kv ) +{ + const char *pDCCObjectName = kv->GetString( "text", NULL ); + if ( !pDCCObjectName ) + return; + + KeyValues *pDialogKeys = kv->FindKey( "ChangeDCCObject" ); + if ( pDialogKeys ) + { + m_pDCCObjectName->SetText( pDCCObjectName ); + OnDCCObjectNameChanged(); + return; + } + + pDialogKeys = kv->FindKey( "AddDCCObject" ); + if ( pDialogKeys ) + { + OnDCCObjectAdded( pDCCObjectName, pDialogKeys ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Shows the DCC object browser (once we have one) +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::ShowDCCObjectBrowser( const char *pTitle, const char *pPrompt, KeyValues *pDialogKeys ) +{ + InputDialog *pInput = new InputDialog( this, pTitle, pPrompt ); + pInput->SetMultiline( false ); + pInput->DoModal( pDialogKeys ); +} + + +//----------------------------------------------------------------------------- +// Called when the button to add a file is clicked +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnAddDCCObject( ) +{ + if ( m_hSourceDCCFile.Get() ) + { + KeyValues *pDialogKeys = new KeyValues( "AddDCCObject" ); + ShowDCCObjectBrowser( "Add DCC Object", "Enter DCC object name to add", pDialogKeys ); + } +} + + +//----------------------------------------------------------------------------- +// Called when the button to browse for a source file is clicked +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnBrowseDCCObject( ) +{ + int nCount = m_pRootDCCObjects->GetSelectedItemsCount(); + if ( nCount == 0 || !m_hSourceDCCFile.Get() ) + return; + + int nItemID = m_pRootDCCObjects->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pRootDCCObjects->GetItem( nItemID ); + int nDCCObjectIndex = pKeyValues->GetInt( "dccObjectIndex", -1 ); + + KeyValues *pDialogKeys = new KeyValues( "ChangeDCCObject", "dccObjectIndex", nDCCObjectIndex ); + ShowDCCObjectBrowser( "Edit Maya/XSI Object", "Enter new name of Maya/XSI object", pDialogKeys ); +} + + +//----------------------------------------------------------------------------- +// Called when the source file name changes +//----------------------------------------------------------------------------- +bool CDmeSourceDCCFilePanel::CheckForDuplicateNames( const char *pDCCObjectName, int nDCCObjectSkipIndex ) +{ + // Look for the existence of this source already + if ( pDCCObjectName[0] ) + { + int nCount = m_hSourceDCCFile->m_RootDCCObjects.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( i == nDCCObjectSkipIndex ) + continue; + + if ( !Q_stricmp( pDCCObjectName, m_hSourceDCCFile->m_RootDCCObjects[i] ) ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "#DmeSourceDCCFile_DuplicateSourceTitle", "#DmeSourceDCCFile_DuplicateSourceText", GetParent() ); + pError->DoModal(); + return true; + } + } + } + return false; +} + + +//----------------------------------------------------------------------------- +// Called when the source file name changes +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnDCCObjectNameChanged() +{ + int nCount = m_pRootDCCObjects->GetSelectedItemsCount(); + if ( nCount == 0 || !m_hSourceDCCFile.Get() ) + return; + + int nItemID = m_pRootDCCObjects->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pRootDCCObjects->GetItem( nItemID ); + int nDCCObjectIndex = pKeyValues->GetInt( "dccObjectIndex", -1 ); + if ( nDCCObjectIndex < 0 ) + return; + + char pDCCObjectName[MAX_PATH]; + m_pDCCObjectName->GetText( pDCCObjectName, sizeof(pDCCObjectName) ); + + if ( CheckForDuplicateNames( pDCCObjectName, nDCCObjectIndex ) ) + return; + + { + CDisableUndoScopeGuard guard; + m_hSourceDCCFile->m_RootDCCObjects.Set( nDCCObjectIndex, pDCCObjectName ); + } + + pKeyValues->SetString( "dccobject", pDCCObjectName ); + m_pRootDCCObjects->ApplyItemChanges( nItemID ); + m_pRootDCCObjects->SortList(); + + SetDirty( ); +} + + +//----------------------------------------------------------------------------- +// Used for sorting below +//----------------------------------------------------------------------------- +static int IntCompare( const void *pSrc1, const void *pSrc2 ) +{ + int i1 = *(int*)pSrc1; + int i2 = *(int*)pSrc2; + return i1 - i2; +} + + +//----------------------------------------------------------------------------- +// Called when the button to remove a DCC object is clicked +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnRemoveDCCObject( ) +{ + int nCount = m_pRootDCCObjects->GetSelectedItemsCount(); + if ( nCount == 0 || !m_hSourceDCCFile.Get() ) + return; + + int nSelectedCount = 0; + int *pDCCObjectIndex = (int*)alloca( nCount*sizeof(int) ); + for ( int i = 0; i < nCount; ++i ) + { + int nItemID = m_pRootDCCObjects->GetSelectedItem( i ); + KeyValues *pKeyValues = m_pRootDCCObjects->GetItem( nItemID ); + int nDCCObjectIndex = pKeyValues->GetInt( "dccObjectIndex", -1 ); + if ( nDCCObjectIndex < 0 ) + continue; + pDCCObjectIndex[nSelectedCount++] = nDCCObjectIndex; + } + + if ( nSelectedCount == 0 ) + return; + + // Sort the object indices so we can remove them all + qsort( pDCCObjectIndex, nSelectedCount, sizeof(int), IntCompare ); + + // Update the selection to be reasonable after deletion + int nItemID = m_pRootDCCObjects->GetSelectedItem( 0 ); + int nRow = m_pRootDCCObjects->GetItemCurrentRow( nItemID ); + Assert( nRow >= 0 ); + + { + CDisableUndoScopeGuard guard; + // Because we sorted it above, removes will occur properly + for ( int i = nSelectedCount; --i >= 0; ) + { + m_hSourceDCCFile->m_RootDCCObjects.Remove( pDCCObjectIndex[i] ); + } + SetDirty( ); + } + RefreshDCCObjectList(); + + int nVisibleRowCount = m_pRootDCCObjects->GetItemCount(); + if ( nVisibleRowCount == 0 ) + return; + + if ( nRow >= nVisibleRowCount ) + { + nRow = nVisibleRowCount - 1; + } + + int nNewItemID = m_pRootDCCObjects->GetItemIDFromRow( nRow ); + m_pRootDCCObjects->SetSingleSelectedItem( nNewItemID ); +} + + +//----------------------------------------------------------------------------- +// Called when a key is typed +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnKeyCodeTyped( vgui::KeyCode code ) +{ + if ( code == KEY_DELETE ) + { + OnRemoveDCCObject(); + return; + } + + BaseClass::OnKeyCodeTyped( code ); +} + + +//----------------------------------------------------------------------------- +// Command handler +//----------------------------------------------------------------------------- +void CDmeSourceDCCFilePanel::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "OnBrowseDCCObject" ) ) + { + OnBrowseDCCObject(); + return; + } + + if ( !Q_stricmp( pCommand, "OnAddDCCObject" ) ) + { + OnAddDCCObject(); + return; + } + + if ( !Q_stricmp( pCommand, "OnRemoveDCCObject" ) ) + { + OnRemoveDCCObject(); + return; + } + + if ( !Q_stricmp( pCommand, "OnApplyChanges" ) ) + { + OnDCCObjectNameChanged(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + diff --git a/vgui2/dme_controls/DmeSourceSkinPanel.cpp b/vgui2/dme_controls/DmeSourceSkinPanel.cpp new file mode 100644 index 0000000..294fe05 --- /dev/null +++ b/vgui2/dme_controls/DmeSourceSkinPanel.cpp @@ -0,0 +1,143 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/DmeSourceSkinPanel.h" +#include "dme_controls/DmePanel.h" +#include "movieobjects/dmemdlmakefile.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/CheckButton.h" +#include "tier1/KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Hook into the dme panel editor system +// +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CDmeSourceSkinPanel, DmeSourceSkin, "DmeSourceSkinDefault", "MDL Skin Editor", true ); + + + +//----------------------------------------------------------------------------- +// Purpose: Constructor, destructor +//----------------------------------------------------------------------------- +CDmeSourceSkinPanel::CDmeSourceSkinPanel( vgui::Panel *pParent, const char *pPanelName ) : + BaseClass( pParent, pPanelName ) +{ + m_pSkinName = new vgui::TextEntry( this, "SkinName" ); + m_pSkinName->AddActionSignalTarget( this ); + + m_pScale = new vgui::TextEntry( this, "Scale" ); + m_pScale->AddActionSignalTarget( this ); + + m_pFlipTriangles = new vgui::CheckButton( this, "FlipTriangles", "" ); + m_pFlipTriangles->AddActionSignalTarget( this ); + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettings( "resource/DmeSourceSkinPanel.res" ); +} + +CDmeSourceSkinPanel::~CDmeSourceSkinPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Marks the file as dirty (or not) +//----------------------------------------------------------------------------- +void CDmeSourceSkinPanel::SetDirty() +{ + PostActionSignal( new KeyValues( "DmeElementChanged" ) ); +} + + +//----------------------------------------------------------------------------- +// Resets the state +//----------------------------------------------------------------------------- +void CDmeSourceSkinPanel::SetDmeElement( CDmeSourceSkin *pSourceSkin ) +{ + m_hSourceSkin = pSourceSkin; + + bool bEnabled = (pSourceSkin != NULL); + m_pSkinName->SetEnabled( bEnabled ); + m_pScale->SetEnabled( bEnabled ); + m_pFlipTriangles->SetEnabled( bEnabled ); + if ( !bEnabled ) + { + m_pSkinName->SetText( "" ); + m_pScale->SetText( "" ); + m_pFlipTriangles->SetSelected( false ); + return; + } + + char pBuf[32]; + Q_snprintf( pBuf, sizeof(pBuf), "%.3f", pSourceSkin->m_flScale.Get() ); + m_pSkinName->SetText( pSourceSkin->m_SkinName ); + m_pScale->SetText( pBuf ); + m_pFlipTriangles->SetSelected( pSourceSkin->m_bFlipTriangles ); +} + + +//----------------------------------------------------------------------------- +// Command handler +//----------------------------------------------------------------------------- +void CDmeSourceSkinPanel::OnCheckButtonChecked( int nChecked ) +{ + if ( !m_hSourceSkin.Get() ) + return; + + bool bFlipTriangles = ( nChecked != 0 ); + if ( bFlipTriangles != m_hSourceSkin->m_bFlipTriangles ) + { + m_hSourceSkin->m_bFlipTriangles = bFlipTriangles; + SetDirty(); + } +} + + +//----------------------------------------------------------------------------- +// Called when something is typed in a text entry field +//----------------------------------------------------------------------------- +void CDmeSourceSkinPanel::OnTextChanged( KeyValues *kv ) +{ + if ( !m_hSourceSkin.Get() ) + return; + + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pSkinName ) + { + char pTextBuf[256]; + m_pSkinName->GetText( pTextBuf, sizeof( pTextBuf) ); + if ( Q_stricmp( pTextBuf, m_hSourceSkin->m_SkinName ) ) + { + m_hSourceSkin->m_SkinName = pTextBuf; + SetDirty(); + } + return; + } + + if ( pPanel == m_pScale ) + { + char pTextBuf[256]; + m_pScale->GetText( pTextBuf, sizeof( pTextBuf) ); + float flScale = atoi( pTextBuf ); + if ( flScale != m_hSourceSkin->m_flScale ) + { + m_hSourceSkin->m_flScale = flScale; + SetDirty(); + } + return; + } +} + + + diff --git a/vgui2/dme_controls/ElementPropertiesTree.cpp b/vgui2/dme_controls/ElementPropertiesTree.cpp new file mode 100644 index 0000000..b953cd9 --- /dev/null +++ b/vgui2/dme_controls/ElementPropertiesTree.cpp @@ -0,0 +1,4496 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/elementpropertiestree.h" +#include "tier1/KeyValues.h" +#include "datamodel/dmelement.h" + +#include "vgui/IInput.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IVgui.h" +#include "vgui/Cursor.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/FileOpenDialog.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/MenuItem.h" +#include "vgui_controls/MenuButton.h" +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/ScrollBar.h" +#include "movieobjects/dmeeditortypedictionary.h" +#include "dme_controls/AttributeTextPanel.h" +#include "dme_controls/DmePanel.h" +#include "dme_controls/dmecontrols_utils.h" +#include "tier1/ConVar.h" +#include "tier2/fileutils.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CElementTreeViewListControl; + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// CElementTree +// +//----------------------------------------------------------------------------- +class CElementTree : public TreeView +{ + DECLARE_CLASS_SIMPLE( CElementTree, TreeView ); +public: + CElementTree( CElementPropertiesTreeInternal *parent, const char *panelName ); + ~CElementTree(); + + virtual void OnCommand( const char *cmd ); + virtual void ApplySchemeSettings( IScheme *pScheme ); + virtual void InvalidateLayout( bool layoutNow = false, bool reloadScheme = false ); + virtual void GenerateChildrenOfNode(int itemIndex); + // override to open a custom context menu on a node being selected and right-clicked + virtual void GenerateContextMenu( int itemIndex, int x, int y ); + + virtual void GenerateDragDataForItem( int itemIndex, KeyValues *msg ); + + virtual void OnLabelChanged( int itemIndex, const char *oldString, const char *newString ); + + virtual bool IsItemDroppable( int m_ItemIndex, CUtlVector< KeyValues * >& msglist ); + virtual void OnItemDropped( int m_ItemIndex, CUtlVector< KeyValues * >& msglist ); + virtual bool GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist ); + virtual HCursor GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist ); + + ScrollBar *GetScrollBar(); + +private: + Menu *m_pEditMenu; + CElementPropertiesTreeInternal *m_pParent; + ScrollBar *m_pVertSB; +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CElementTree::CElementTree( CElementPropertiesTreeInternal *parent, const char *panelName ) : + BaseClass( (Panel *)parent, panelName ), + m_pEditMenu( 0 ), + m_pParent( parent ) +{ + SetAllowLabelEditing( true ); + SetAllowMultipleSelections( true ); + + m_pVertSB = SetScrollBarExternal( true, this ); +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +CElementTree::~CElementTree() +{ + delete m_pEditMenu; +} + +ScrollBar *CElementTree::GetScrollBar() +{ + return m_pVertSB; +} + +bool CElementTree::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + return m_pParent->IsItemDroppable( itemIndex, msglist ); +} + +bool CElementTree::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + return m_pParent->GetItemDropContextMenu( itemIndex, menu, msglist ); +} + +void CElementTree::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + m_pParent->OnItemDropped( itemIndex, msglist ); +} + +HCursor CElementTree::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + return m_pParent->GetItemDropCursor( itemIndex, msglist ); +} + +void CElementTree::OnLabelChanged( int itemIndex, const char *oldString, const char *newString ) +{ + m_pParent->OnLabelChanged( itemIndex, oldString, newString ); +} + +void CElementTree::GenerateDragDataForItem( int itemIndex, KeyValues *msg ) +{ + m_pParent->GenerateDragDataForItem( itemIndex, msg ); +} + +// override to open a custom context menu on a node being selected and right-clicked +void CElementTree::GenerateContextMenu( int itemIndex, int x, int y ) +{ + m_pParent->GenerateContextMenu( itemIndex, x, y ); +} + + +void CElementTree::ApplySchemeSettings( IScheme *pScheme ) +{ + // Intentionally skip to Panel:: instead of BaseClass::!!! + Panel::ApplySchemeSettings( pScheme ); + + SetFont( pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ) ); +} + +void CElementTree::InvalidateLayout( bool layoutNow, bool reloadScheme ) +{ + BaseClass::InvalidateLayout( layoutNow, reloadScheme ); + if ( GetParent() && !reloadScheme ) + { + GetParent()->InvalidateLayout( layoutNow, false ); + } +} + +void CElementTree::OnCommand( const char *cmd ) +{ + // Relay to parent + GetParent()->OnCommand( cmd ); +} + +void CElementTree::GenerateChildrenOfNode(int itemIndex) +{ + m_pParent->GenerateChildrenOfNode( itemIndex ); +} + + +//----------------------------------------------------------------------------- +// +// Class: CElementTreeViewListControl +// +//----------------------------------------------------------------------------- +CElementTreeViewListControl::CElementTreeViewListControl( Panel *pParent, const char *pName ) + : BaseClass( pParent, pName ), m_Panels( 0, 0, PanelsLessFunc ) +{ + + m_iTreeColumnWidth = 200; + m_iFontSize = 1; + m_bMouseLeftIsDown = false; + m_bMouseIsDragging = false; + m_bDrawGrid = false; + + // why do this here? + SetScheme( vgui::scheme()->LoadSchemeFromFile( "Resource/BoxRocket.res", "BoxRocket" ) ); + + // the column lable font + vgui::IScheme *scheme = vgui::scheme()->GetIScheme( GetScheme() ); + HFont font = scheme->GetFont( "DefaultVerySmall", IsProportional() ); + + SetTitleBarInfo( font, 18 ); + + SetPostChildPaintEnabled( true ); + SetBorderColor( Color( 255, 255, 196, 64 ) ); + + SetKeyBoardInputEnabled( true ); +} + + +int CElementTreeViewListControl::AddItem( KeyValues *data, bool allowLabelEditing, int parentItemIndex, CUtlVector< vgui::Panel * >& columnPanels ) +{ + int itemIndex = GetTree()->AddItem( data, parentItemIndex ); + if ( allowLabelEditing ) + { + GetTree()->SetLabelEditingAllowed( itemIndex, allowLabelEditing ); + } + + GetTree()->SetItemFgColor( itemIndex, GetFgColor() ); + GetTree()->SetItemBgColor( itemIndex, GetBgColor() ); + + ColumnPanels_t search; + search.treeViewItem = itemIndex; + + int idx = m_Panels.Find( search ); + if ( idx == m_Panels.InvalidIndex() ) + { + ColumnPanels_t newInfo; + newInfo.treeViewItem = itemIndex; + idx = m_Panels.Insert( newInfo ); + } + + ColumnPanels_t& info = m_Panels[ idx ]; + + info.SetList( columnPanels ); + + int c = columnPanels.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( columnPanels[ i ] ) + { + columnPanels[ i ]->SetParent( this ); + } + } + +// GetTree()->InvalidateLayout( false, true ); + + return itemIndex; +} + + +//----------------------------------------------------------------------------- +// Removes an item recursively +//----------------------------------------------------------------------------- +void CElementTreeViewListControl::RemoveItem_R( int nItemIndex ) +{ + ColumnPanels_t search; + search.treeViewItem = nItemIndex; + int idx = m_Panels.Find( search ); + if ( idx != m_Panels.InvalidIndex() ) + { + ColumnPanels_t& info = m_Panels[ idx ]; + int nCount = info.m_Columns.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( info.m_Columns[i] ) + { + info.m_Columns[i]->SetParent( (Panel*)NULL ); + info.m_Columns[i]->MarkForDeletion(); + } + } + m_Panels.RemoveAt( idx ); + } + + int nCount = GetTree()->GetNumChildren( nItemIndex ); + for ( int i = 0; i < nCount; ++i ) + { + RemoveItem_R( GetTree()->GetChild( nItemIndex, i ) ); + } +} + + +//----------------------------------------------------------------------------- +// Removes an item +//----------------------------------------------------------------------------- +void CElementTreeViewListControl::RemoveItem( int nItemIndex ) +{ + RemoveItem_R( nItemIndex ); + GetTree()->RemoveItem( nItemIndex, false, true ); + RecalculateRows(); + InvalidateLayout(); +} + + +int CElementTreeViewListControl::GetTreeColumnWidth() +{ + return m_iTreeColumnWidth; +} + +void CElementTreeViewListControl::SetTreeColumnWidth(int w) +{ + m_iTreeColumnWidth = w; + SetColumnInfo( 0, "Tree", m_iTreeColumnWidth ); +} + +void CElementTreeViewListControl::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + GetTree()->SetFont( pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse drag resize of tree column (hack) +//----------------------------------------------------------------------------- +void CElementTreeViewListControl::OnCursorMoved(int x, int y) +{ + + if ( ( x > m_iTreeColumnWidth - 12 ) && + ( x < m_iTreeColumnWidth + 12 ) ) + { + SetCursor( dc_sizewe ); + if ( m_bMouseLeftIsDown ) + { + m_bMouseIsDragging = true; + } + } + else + { + SetCursor( dc_arrow ); + } + + if ( m_bMouseIsDragging ) + { + SetCursor( dc_sizewe ); + SetTreeColumnWidth( x ); + InvalidateLayout( true ); + } + +} + +void CElementTreeViewListControl::OnMousePressed( MouseCode code ) +{ + BaseClass::OnMousePressed( code ); + if ( code == MOUSE_LEFT ) + { + m_bMouseLeftIsDown = true; + } + input()->SetMouseCapture(GetVPanel()); + RequestFocus(); +} + +void CElementTreeViewListControl::OnMouseReleased( MouseCode code ) +{ + BaseClass::OnMouseReleased( code ); + if ( code == MOUSE_LEFT ) + { + m_bMouseLeftIsDown = false; + m_bMouseIsDragging = false; + } + input()->SetMouseCapture(NULL); +} + +void CElementTreeViewListControl::OnMouseDoublePressed( MouseCode code ) +{ + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + + // resize the column to the max width of the tree + if ( ( x > m_iTreeColumnWidth - 12 ) && + ( x < m_iTreeColumnWidth + 12 ) ) + { + ResizeTreeToExpandedWidth(); + } + + BaseClass::OnMouseDoublePressed( code ); + +} + +void CElementTreeViewListControl::ResizeTreeToExpandedWidth() +{ + int rows = GetNumRows(); + int vbarTop, nItemsVisible; + bool hbarVisible = false; + GetTree()->GetVBarInfo( vbarTop, nItemsVisible, hbarVisible ); + int vBarWidth = 0; + if ( nItemsVisible <= rows ) + { + vBarWidth = 27; + } + SetTreeColumnWidth( GetTree()->GetVisibleMaxWidth() + vBarWidth + 14 ); +} + +void CElementTreeViewListControl::OnMouseWheeled(int delta) +{ + + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + if ( ctrl ) + { + SetFontSize( GetFontSize() + delta ); + } + else if ( alt ) + { + ToggleDrawGrid(); + } + else + { + // scroll the treeview control + ScrollBar *sb = ((CElementTree *)GetTree())->GetScrollBar(); + sb->SetValue( sb->GetValue() + ( delta * -3 ) ); + } +} + +void CElementTreeViewListControl::ToggleDrawGrid() +{ + m_bDrawGrid = !m_bDrawGrid; +} + +bool CElementTreeViewListControl::IsDrawingGrid() +{ + return m_bDrawGrid; +} + +void CElementTreeViewListControl::PostChildPaint() +{ + // why isn't SetBorderColor doing the job??? + vgui::surface()->DrawSetColor( Color( 255, 255, 196, 32 ) ); + + int left, top, right, bottom; + int wide, tall; + GetSize( wide, tall ); + GetGridElementBounds( 0, 0, left, top, right, bottom ); + vgui::surface()->DrawFilledRect( right, 1, right+3, tall ); + + if ( m_bDrawGrid ) + { + int numColumns = GetNumColumns(); + int rows = GetNumRows(); + + int vbarTop, nItemsVisible; + bool hbarVisible = false; + GetTree()->GetVBarInfo( vbarTop, nItemsVisible, hbarVisible ); + + int vBarWidth = 0; + if ( nItemsVisible <= rows ) + { + vBarWidth = 21; + } + + if ( hbarVisible ) + { + --nItemsVisible; + } + + for ( int col = 0; col < numColumns; ++col ) + { + for ( int row = 0; row < rows; ++row ) + { + GetGridElementBounds( col, row, left, top, right, bottom ); + if (col == 0) + { + vgui::surface()->DrawLine( left+4, bottom, right, bottom ); + } + else + { + vgui::surface()->DrawLine( left-3, bottom, right-2, bottom ); + } + } + } + } +} + +int CElementTreeViewListControl::GetScrollBarSize() +{ + ScrollBar *sb = ((CElementTree *)GetTree())->GetScrollBar(); + if ( sb ) + { + return sb->GetWide(); + } + return 0; +} + +void CElementTreeViewListControl::PerformLayout() +{ + BaseClass::PerformLayout(); + + // Assume all invisible at first + HideAll(); + + GetTree()->PerformLayout(); + + ScrollBar *sb = ((CElementTree *)GetTree())->GetScrollBar(); + if ( sb && sb->GetParent() ) + { + sb->SetBounds( sb->GetParent()->GetWide() - sb->GetWide(), 0, sb->GetWide(), sb->GetParent()->GetTall() ); + } + + int rowheight = GetTree()->GetRowHeight(); + int treetop, visitems; + bool hbarVisible = false; + GetTree()->GetVBarInfo( treetop, visitems, hbarVisible ); + if ( hbarVisible ) + { + --visitems; + } + + int offset = -treetop * rowheight; + + int headerHeight = GetTitleBarHeight(); + + int numColumns = GetNumColumns(); + // Now position column panels into the correct spot + int rows = GetNumRows(); + int visItemCount = 0; + + for ( int row = 0; row < rows; ++row ) + { + int tvi = GetTreeItemAtRow( row ); + + for ( int col = 0; col < numColumns; ++col ) + { + int left, top, right, bottom; + GetGridElementBounds( col, row, left, top, right, bottom ); + + ColumnPanels_t search; + search.treeViewItem = tvi; + + int idx = m_Panels.Find( search ); + if ( idx != m_Panels.InvalidIndex() ) + { + ColumnPanels_t& info = m_Panels[ idx ]; + + if ( col >= info.m_Columns.Count() ) + continue; + + vgui::Panel *p = info.m_Columns[ col ]; + if ( !p ) + { + continue; + } + + bool vis = top + offset >= headerHeight; + if ( vis ) + { + ++visItemCount; + + if ( visItemCount > visitems ) + { + vis = false; + } + } + + p->SetVisible( vis ); + p->SetBounds( left + 4, top + offset, right - left, bottom - top ); + + p->InvalidateLayout(); + } + else + { + Assert( 0 ); + } + } + } +} + +void CElementTreeViewListControl::HideAll() +{ + for ( int i = m_Panels.FirstInorder(); i != m_Panels.InvalidIndex(); i = m_Panels.NextInorder( i ) ) + { + ColumnPanels_t& info = m_Panels[ i ]; + int c = info.m_Columns.Count(); + for ( int j = 0 ; j < c; ++j ) + { + Panel *panel = info.m_Columns[ j ]; + if ( !panel ) + { + continue; + } + panel->SetVisible( false ); + } + } +} + +void CElementTreeViewListControl::RemoveAll() +{ + GetTree()->RemoveAll(); + + for ( int i = m_Panels.FirstInorder(); i != m_Panels.InvalidIndex(); i = m_Panels.NextInorder( i ) ) + { + ColumnPanels_t& info = m_Panels[ i ]; + int c = info.m_Columns.Count(); + for ( int j = 0 ; j < c; ++j ) + { + delete info.m_Columns[ j ]; + } + info.m_Columns.RemoveAll(); + } + m_Panels.RemoveAll(); + InvalidateLayout(); +} + +HFont CElementTreeViewListControl::GetFont( int size ) +{ + vgui::IScheme *scheme = vgui::scheme()->GetIScheme( GetScheme() ); + + switch(size) + { + case 1: + return scheme->GetFont( "DmePropertyVerySmall", IsProportional() ); + case 2: + return scheme->GetFont( "DmePropertySmall", IsProportional() ); + case 3: + return scheme->GetFont( "DmeProperty", IsProportional() ); + case 4: + return scheme->GetFont( "DmePropertyLarge", IsProportional() ); + case 5: + return scheme->GetFont( "DmePropertyVeryLarge", IsProportional() ); + default: + return NULL; + } +} + +void CElementTreeViewListControl::SetFont( HFont font ) +{ + // set the font for the tree + GetTree()->SetFont( font ); + + // and now set the font on the data column... + for ( int i = m_Panels.FirstInorder(); i != m_Panels.InvalidIndex(); i = m_Panels.NextInorder( i ) ) + { + ColumnPanels_t& info = m_Panels[ i ]; + int c = info.m_Columns.Count(); + for ( int j = 0 ; j < c; ++j ) + { + Panel *panel = info.m_Columns[ j ]; + if ( !panel ) + { + continue; + } + + CBaseAttributePanel *attrPanel = dynamic_cast< CBaseAttributePanel * >( panel ); + if ( !attrPanel ) + { + continue; + } + attrPanel->SetFont( font ); + } + } +} + +int CElementTreeViewListControl::GetFontSize() +{ + return m_iFontSize; +} + +void CElementTreeViewListControl::SetFontSize( int size ) +{ + m_iFontSize = min( 5, max( 1, size ) ); + SetFont( GetFont( m_iFontSize ) ); +} + +void CElementTreeViewListControl::ExpandItem(int itemIndex, bool bExpand) +{ + GetTree()->ExpandItem( itemIndex, bExpand ); +} + +bool CElementTreeViewListControl::IsItemExpanded( int itemIndex ) +{ + return GetTree()->IsItemExpanded( itemIndex ); +} + +bool CElementTreeViewListControl::IsItemSelected( int itemIndex ) +{ + return GetTree()->IsItemSelected( itemIndex ); +} + + +KeyValues *CElementTreeViewListControl::GetItemData(int itemIndex) +{ + return GetTree()->GetItemData( itemIndex ); +} + + +class CHistoryMenuButton : public MenuButton +{ +DECLARE_CLASS_SIMPLE( CHistoryMenuButton, MenuButton ); +public: + CHistoryMenuButton( Panel *parent, const char *panelName, const char *text, CElementPropertiesTreeInternal *tree, int whichMenu ); + + virtual void OnShowMenu( Menu *menu ); + virtual int OnCheckMenuItemCount(); + + +private: + CElementPropertiesTreeInternal *m_pPropertiesTreeInternal; + int m_nWhichMenu; +}; + +CHistoryMenuButton::CHistoryMenuButton( Panel *parent, const char *panelName, const char *text, CElementPropertiesTreeInternal *tree, int whichMenu ) + : BaseClass( parent, panelName, text ), m_pPropertiesTreeInternal( tree ), m_nWhichMenu( whichMenu ) +{ + Assert( m_pPropertiesTreeInternal ); +} + +int CHistoryMenuButton::OnCheckMenuItemCount() +{ + Assert( m_pPropertiesTreeInternal ); + if ( !m_pPropertiesTreeInternal ) + return 0; + + return m_pPropertiesTreeInternal->GetHistoryMenuItemCount( m_nWhichMenu ); +} + +void CHistoryMenuButton::OnShowMenu( Menu *menu ) +{ + Assert( m_pPropertiesTreeInternal ); + if ( !m_pPropertiesTreeInternal ) + return; + + m_pPropertiesTreeInternal->PopulateHistoryMenu( m_nWhichMenu, menu ); +} + +class CSearchComboBox : public ComboBox +{ + DECLARE_CLASS_SIMPLE( CSearchComboBox, ComboBox ); +public: + + CSearchComboBox( CElementPropertiesTreeInternal *tree, vgui::Panel *parent, const char *panelName, int numLines, bool allowEdit ); + + virtual void OnMenuItemSelected(); + virtual void OnShowMenu(Menu *menu); + +private: + + CElementPropertiesTreeInternal *m_pTree; +}; + +CSearchComboBox::CSearchComboBox( CElementPropertiesTreeInternal *tree, vgui::Panel *parent, const char *panelName, int numLines, bool allowEdit ) + : BaseClass( parent, panelName, numLines, allowEdit ), m_pTree( tree ) +{ + Assert( m_pTree ); +} + +void CSearchComboBox::OnShowMenu(Menu *menu) +{ + menu->DeleteAllItems(); + Assert( m_pTree ); + if ( m_pTree ) + { + m_pTree->PopulateHistoryMenu( CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_SEARCHHSITORY, menu ); + } +} + +void CSearchComboBox::OnMenuItemSelected() +{ + BaseClass::OnMenuItemSelected(); + + int idx = GetActiveItem(); + if ( idx < 0 ) + return; + + char name[ 256 ]; + GetItemText( idx, name, sizeof( name ) ); + + Assert( m_pTree ); + if ( m_pTree && name[ 0 ] ) + { + m_pTree->OnNavSearch( name ); + } +} + +class CPropertiesTreeToolbar : public Panel +{ + DECLARE_CLASS_SIMPLE( CPropertiesTreeToolbar, Panel ); +public: + CPropertiesTreeToolbar( vgui::Panel *parent, const char *panelName, CElementPropertiesTreeInternal *tree ); + + virtual void ApplySchemeSettings( IScheme *scheme ); + + virtual void PerformLayout(); + + MESSAGE_FUNC( OnTextNewLine, "TextNewLine" ); + + virtual void OnKeyCodeTyped( KeyCode code ); + + void UpdateButtonState(); +private: + + CElementPropertiesTreeInternal *m_pTree; + + CHistoryMenuButton *m_pBack; + CHistoryMenuButton *m_pFwd; + Label *m_pSearchLabel; + CSearchComboBox *m_pSearch; + // Button *m_pShowSearchResults; +}; + +CPropertiesTreeToolbar::CPropertiesTreeToolbar( vgui::Panel *parent, const char *panelName, CElementPropertiesTreeInternal *tree ) : + BaseClass( parent, panelName ), m_pTree( tree ) +{ + Assert( m_pTree ); + + SetPaintBackgroundEnabled( false ); + + m_pBack = new CHistoryMenuButton( this, "Nav_Back", "#Dme_NavBack", tree, CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_BACKWARD ); + m_pBack->SetCommand( new KeyValues( "OnNavigateBack", "item", -1 ) ); + m_pBack->AddActionSignalTarget( parent ); + m_pBack->SetDropMenuButtonStyle( true ); + + m_pBack->SetMenu( new Menu( this, "Nav_BackMenu" ) ); + + m_pFwd = new CHistoryMenuButton( this, "Nav_Forward", "#Dme_NavForward", tree, CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_FORWARD ); + m_pFwd->SetCommand( new KeyValues( "OnNavigateForward", "item", -1 ) ); + m_pFwd->AddActionSignalTarget( parent ); + m_pFwd->SetDropMenuButtonStyle( true ); + m_pFwd->SetMenu( new Menu( this, "Nav_FwdMenu" ) ); + + m_pSearch = new CSearchComboBox( tree, this, "Nav_Search", 20, true ); + m_pSearch->SendNewLine( true ); + m_pSearch->SelectAllOnFocusAlways( true ); + m_pSearch->AddActionSignalTarget( this ); + + /* + m_pShowSearchResults = new Button( this, "Nav_ShowResults", "Show Results" ); + m_pShowSearchResults->SetCommand( new KeyValues( "OnShowSearchResults" ) ); + m_pShowSearchResults->AddActionSignalTarget( parent ); + */ + + m_pSearchLabel = new Label( this, "Nav_SearchLabel", "#Dme_NavSearch" ); +} + +void CPropertiesTreeToolbar::UpdateButtonState() +{ + m_pBack->SetEnabled( m_pTree->GetHistoryMenuItemCount( CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_BACKWARD ) > 0 ? true : false ); + m_pFwd->SetEnabled( m_pTree->GetHistoryMenuItemCount( CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_FORWARD ) > 0 ? true : false ); + + //m_pShowSearchResults->SetEnabled( m_pTree->GetHistoryMenuItemCount( CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_SEARCHHSITORY ) > 0 ? true : false ); +} + +void CPropertiesTreeToolbar::OnTextNewLine() +{ + Panel *parent = GetParent(); + Assert( parent ); + if ( !parent ) + return; + + char searchBuf[ 256 ]; + m_pSearch->GetText( searchBuf, sizeof( searchBuf ) ); + + KeyValues *msg = new KeyValues( "OnNavigateSearch", "text", searchBuf ); + + PostMessage( parent, msg ); +} + +void CPropertiesTreeToolbar::OnKeyCodeTyped( KeyCode code ) +{ + switch ( code ) + { + case KEY_F3: + { + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + + Panel *parent = GetParent(); + Assert( parent ); + if ( parent ) + { + KeyValues *msg = new KeyValues( "OnNavigateSearchAgain", "direction", shift ? -1 : 1 ); + PostMessage( parent, msg ); + } + } + break; + default: + BaseClass::OnKeyCodeTyped( code ); + break; + } +} + +void CPropertiesTreeToolbar::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + + m_pBack->SetFont( scheme->GetFont( "DefaultVerySmall" ) ); + m_pFwd->SetFont( scheme->GetFont( "DefaultVerySmall" ) ); + m_pSearch->SetFont( scheme->GetFont( "DefaultVerySmall" ) ); + m_pSearchLabel->SetFont( scheme->GetFont( "DefaultVerySmall" ) ); + //m_pShowSearchResults->SetFont( scheme->GetFont( "DefaultVerySmall" ) ); + + m_pSearch->SendNewLine( true ); + m_pSearch->SelectAllOnFocusAlways( true ); + + m_pBack->GetMenu()->SetFont( scheme->GetFont( "DefaultVerySmall" ) ); + m_pFwd->GetMenu()->SetFont( scheme->GetFont( "DefaultVerySmall" ) ); +} + +void CPropertiesTreeToolbar::PerformLayout() +{ + BaseClass::PerformLayout(); + int w, h; + GetSize( w, h ); + + int buttonw = 75; + int buttonh = h - 6; + + int x = 2; + + m_pBack->SetBounds( x, 3, buttonw, buttonh ); + + x += buttonw + 2; + + m_pFwd->SetBounds( x, 3, buttonw, buttonh ); + + x += buttonw + 15; + + m_pSearchLabel->SetBounds( x, 2, 50, buttonh ); + + x += 50 + 2; + + int textw = ( w - 2 ) - x; + + //textw -= 75; + + m_pSearch->SetBounds( x, 2, textw, buttonh ); + + //x += textw; + + //m_pShowSearchResults->SetBounds( x, 2, 75, buttonh ); + +} + +//----------------------------------------------------------------------------- +// +// CElementPropertiesTreeInternal +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CElementPropertiesTreeInternal::CElementPropertiesTreeInternal( + vgui::Panel *parent, IDmNotify *pNotify, CDmElement *pObject, bool autoApply /* = true */, CDmeEditorTypeDictionary *pDict /* = NULL */ ) : + BaseClass( parent, "ElementPropertiesTree" ), + m_pNotify( pNotify ), + m_hTypeDictionary( pDict ), + m_bAutoApply( autoApply ), m_bShowMemoryUsage( false ) +{ + m_hObject = pObject; + m_bSuppressHistoryUpdates = false; + m_nCurrentHistoryPosition = 0; + m_szSearchStr[ 0 ] = 0; + m_nCurrentSearchResult = 0; + + SetVisible( true ); + + Assert( m_pNotify ); + + CElementTree *dmeTree = new CElementTree( this, "ElementTree" ); + dmeTree->SetDragEnabledItems( true ); + + m_pTree = new CElementTreeViewListControl( this, "ElementTreeList" ); + m_pTree->SetTreeView( dmeTree ); + m_pTree->SetNumColumns( 2 ); + m_pTree->SetColumnInfo( 0, "Tree", m_pTree->GetTreeColumnWidth() ); + m_pTree->SetColumnInfo( 1, "Data", 1600 ); + + m_pToolBar = new CPropertiesTreeToolbar( this, "ElementTreeToolbar", this ); + // m_pToolBar->SetTreeView( dmeTree ); + + ScrollBar *sb = dmeTree->GetScrollBar(); + if ( sb ) + { + sb->SetParent( m_pTree ); + } + + SETUP_PANEL( dmeTree ); + SETUP_PANEL( m_pTree ); + SETUP_PANEL( m_pToolBar ); + + { + CDmElement *pResults = CreateElement< CDmElement >( "Search Results", DMFILEID_INVALID ); + Assert( pResults ); + pResults->AddAttributeElementArray< CDmElement >( "results" ); + m_SearchResultsRoot = pResults; + } + + LoadControlSettings( "resource/BxElementPropertiesTree.res" ); + + m_hDragCopyCursor = surface()->CreateCursorFromFile( "resource/drag_copy.cur" ); + m_hDragLinkCursor = surface()->CreateCursorFromFile( "resource/drag_link.cur" ); + m_hDragMoveCursor = surface()->CreateCursorFromFile( "resource/drag_move.cur" ); +} + + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +CElementPropertiesTreeInternal::~CElementPropertiesTreeInternal() +{ + if ( m_SearchResultsRoot.Get() ) + { + g_pDataModel->DestroyElement( m_SearchResultsRoot ); + } +} + +void CElementPropertiesTreeInternal::UpdateButtonState() +{ + m_pToolBar->UpdateButtonState(); +} + + +//----------------------------------------------------------------------------- +// Message sent when something changed the element you're looking at +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::OnElementChangedExternally( int valuesOnly ) +{ + Refresh( valuesOnly ? REFRESH_VALUES_ONLY : REFRESH_TREE_VIEW ); +} + + +//----------------------------------------------------------------------------- +// Sets the type dictionary +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::SetTypeDictionary( CDmeEditorTypeDictionary *pDict ) +{ + m_hTypeDictionary = pDict; +} + + +//----------------------------------------------------------------------------- +// Initialization of the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::Init( ) +{ + if ( !m_hObject.Get() ) + return; + + UpdateTree(); +} + + +//----------------------------------------------------------------------------- +// Applies changes to all attributes +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::ApplyChanges() +{ + Assert( !m_bAutoApply ); + + if ( !m_hObject.Get() ) + return; + + int nCount = m_AttributeWidgets.Count(); + for ( int i = 0; i < nCount; ++i ) + { + attributewidgetfactorylist->ApplyChanges( m_AttributeWidgets[i].m_pValueWidget, this ); + } +} + + +//----------------------------------------------------------------------------- +// Refreshes all attributes +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::Refresh( RefreshType_t rebuild /* = false */, bool preservePrevSelectedItem /*= false*/ ) +{ + if ( !m_hObject.Get() ) + return; + + if ( rebuild == REFRESH_REBUILD ) + { + SetObject( m_hObject.Get() ); + return; + } + + if ( rebuild != REFRESH_VALUES_ONLY ) + { + RefreshTreeView( preservePrevSelectedItem ); + } + else + { + RefreshTreeItemState( m_pTree->GetTree()->GetRootItemIndex() ); + } + int nCount = m_AttributeWidgets.Count(); + for ( int i = 0; i < nCount; ++i ) + { + attributewidgetfactorylist->Refresh( m_AttributeWidgets[i].m_pValueWidget, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Start editing label, in place +// Input : - +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::OnRename() +{ + if ( m_pTree->GetTree()->GetSelectedItemCount() != 1 ) + return; + + m_pTree->GetTree()->StartEditingLabel( m_pTree->GetTree()->GetFirstSelectedItem() ); +} + +void CElementPropertiesTreeInternal::OnCopy() +{ + CUtlVector< int > selected; + m_pTree->GetTree()->GetSelectedItems( selected ) ; + int c = selected.Count(); + if ( c <= 0 ) + return; + + // add in reverse order, since selection[0] is the last item selected + CUtlVector< KeyValues * > list; + for ( int i = c - 1; i >= 0; --i ) + { + KeyValues *data = new KeyValues( "Clipboard" ); + m_pTree->GetTree()->GenerateDragDataForItem( selected[ i ], data ); + list.AddToTail( data ); + } + + if ( list.Count() > 0 ) + { + g_pDataModel->SetClipboardData( list ); + } +} + +void CElementPropertiesTreeInternal::GetPathToItem( CUtlVector< TreeItem_t > &path, int itemIndex ) +{ + for ( int idx = itemIndex; idx != m_pTree->GetTree()->GetRootItemIndex(); idx = m_pTree->GetTree()->GetItemParent( idx ) ) + { + KeyValues *itemData = m_pTree->GetTree()->GetItemData( idx ); + bool isArrayElement = !itemData->IsEmpty( "arrayIndex" ); + + TreeItem_t treeitem; + treeitem.m_pElement = GetElementKeyValue< CDmElement >( itemData, "ownerelement" ); + treeitem.m_pAttributeName = itemData->GetString( "attributeName", "" ); + treeitem.m_pArrayElement = isArrayElement ? GetElementKeyValue< CDmElement >( itemData, "dmeelement" ) : NULL; + path.AddToTail( treeitem ); + } +} + +int CElementPropertiesTreeInternal::OpenPath( const CUtlVector< TreeItem_t > &path ) +{ + bool bFound = false; + + int itemIndex = m_pTree->GetTree()->GetRootItemIndex(); + int nPathItems = path.Count(); + for ( int i = 0; i < nPathItems; ++i ) + { + const TreeItem_t &childTreeItem = path[ i ]; + + bFound = false; + + int nChildren = m_pTree->GetTree()->GetNumChildren( itemIndex ); + for ( int i = 0; i < nChildren; ++i ) + { + int nChildIndex = m_pTree->GetTree()->GetChild( itemIndex, i ); + KeyValues *childData = m_pTree->GetTree()->GetItemData( nChildIndex ); + + bool isArrayElement = !childData->IsEmpty( "arrayIndex" ); + CDmElement *pOwnerElement = GetElementKeyValue< CDmElement >( childData, "ownerelement" ); + const char *pAttributeName = childData->GetString( "attributeName", "" ); + CDmAttribute *pAttribute = pOwnerElement->GetAttribute( pAttributeName ); + + if ( isArrayElement ) + { + Assert( childTreeItem.m_pArrayElement ); + Assert( !V_strcmp( childTreeItem.m_pAttributeName, pAttributeName ) ); + int nArrayIndex = childData->GetInt( "arrayIndex", -1 ); + const CDmrElementArray<> array( pAttribute ); + if ( nArrayIndex >= 0 && array[ nArrayIndex ] == childTreeItem.m_pArrayElement ) + { + bFound = true; + itemIndex = nChildIndex; + break; + } + } + else + { + Assert( !childTreeItem.m_pArrayElement ); + if ( !V_strcmp( childTreeItem.m_pAttributeName, pAttributeName ) ) + { + bFound = true; + itemIndex = nChildIndex; + break; + } + } + } + + if ( !bFound ) + return -1; + } + + return bFound ? itemIndex : -1; +} + +void CElementPropertiesTreeInternal::OnPaste_( bool reference ) +{ + CUtlVector< int > selected; + m_pTree->GetTree()->GetSelectedItems( selected ) ; + int c = selected.Count(); + if ( !c ) + return; + + // Just choose first item for now + int itemIndex = selected[ 0 ]; + KeyValues *itemData = m_pTree->GetItemData( itemIndex ); + if ( !itemData ) + return; + + const char *elementType = itemData->GetString( "droppableelementtype" ); + if ( !elementType || !elementType[ 0 ] ) + return; + + bool isArrayElement = !itemData->IsEmpty( "arrayIndex" ); + + //Check to see if this attribute refers to an element + CDmAttribute *pAttribute = ElementTree_GetAttribute( itemData ); + if ( !pAttribute ) + return; + + DmAttributeType_t attType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN; + bool isElementAttribute = attType == AT_ELEMENT || attType == AT_ELEMENT_ARRAY; + if ( !isElementAttribute ) + return; + + // get source data that will be pasted + CUtlVector< KeyValues * > msglist; + g_pDataModel->GetClipboardData( msglist ); + + CUtlVector< CDmElement * > list; + ElementTree_GetDroppableItems( msglist, "dmeelement", list ); + if ( !list.Count() ) + return; + + // Pasting after an element array item or at the end of an element array + if ( isArrayElement || attType == AT_ELEMENT_ARRAY ) + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, reference ? "Paste Reference" : "Paste" ); + + CDmrElementArray<> array( pAttribute ); + int nArrayIndex = isArrayElement ? itemData->GetInt( "arrayIndex" ) + 1 : array.Count(); + DropItemsIntoArray( array, msglist, list, nArrayIndex, reference ? DO_LINK : DO_COPY ); + } + // Pasting onto an element attribute + else + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, reference ? "Paste Reference" : "Paste" ); + + pAttribute->SetValue( reference ? list[ 0 ] : list[ 0 ]->Copy() ); + } + + CUtlVector< TreeItem_t > dropTargetPath; + if ( isArrayElement ) + { + itemIndex = m_pTree->GetTree()->GetItemParent( itemIndex ); // if we're an array element, start with the array itself + } + GetPathToItem( dropTargetPath, itemIndex ); + + // Does a forced refresh + Refresh( REFRESH_TREE_VIEW ); + + itemIndex = OpenPath( dropTargetPath ); + if ( attType == AT_ELEMENT_ARRAY ) + { + m_pTree->GetTree()->ExpandItem( itemIndex, true ); + } +} + +void CElementPropertiesTreeInternal::OnPaste() +{ + Warning( "CElementPropertiesTreeInternal::OnPaste\n" ); + OnPaste_( false ); +} + +void CElementPropertiesTreeInternal::OnPasteReference() +{ + Warning( "CElementPropertiesTreeInternal::OnPasteReference\n" ); + OnPaste_( true ); +} + +void CElementPropertiesTreeInternal::OnPasteInsert() +{ + Warning( "CElementPropertiesTreeInternal::OnPasteInsert\n" ); +} + +void RemoveAllReferencesToElement( CDmElement *pElement ) +{ + if ( pElement == NULL ) + return; + + for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement(); + hElement != DMELEMENT_HANDLE_INVALID; + hElement = g_pDataModel->NextAllocatedElement( hElement ) ) + { + CDmElement *pElt = g_pDataModel->GetElement( hElement ); + if ( pElt ) + { + pElt->RemoveAllReferencesToElement( pElement ); + } + } +} + +void CElementPropertiesTreeInternal::OnDeleteSelected() +{ + CUtlVector< KeyValues * > selection; + m_pTree->GetTree()->GetSelectedItemData( selection ); + int nSelected = selection.Count(); + if ( !nSelected ) + return; + + bool bChangeOccurred = false; + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Delete Elements" ); + + for ( int si = 0; si < nSelected; ++si ) + { + KeyValues *item = selection[ si ]; + Assert( item ); + + // Check to see if this attribute refers to an element + CDmElement *pOwner = GetElementKeyValue<CDmElement>( item, "ownerelement" ); + if ( pOwner == NULL ) + continue; + + CDmAttribute *pAttr = pOwner->GetAttribute( item->GetString( "attributeName" ) ); + if ( pAttr == NULL ) + continue; + + bChangeOccurred = true; + + DmAttributeType_t attrType = pAttr->GetType(); + if ( attrType == AT_ELEMENT ) + { + CDmElement *pElement = pAttr->GetValueElement<CDmElement>(); + RemoveAllReferencesToElement( pElement ); + } + else if ( attrType == AT_ELEMENT_ARRAY ) + { + const CDmrElementArray<> array( pAttr ); + int n = array.Count(); + int index = item->GetInt( "arrayIndex", -1 ); + if ( index >= 0 ) + { + CDmElement *pElement = array[ index ]; + RemoveAllReferencesToElement( pElement ); + } + else + { + for ( int i = 0; i < n; ++i ) + { + CDmElement *pElement = array[ i ]; + RemoveAllReferencesToElement( pElement ); + } + } + } + } + } + + // Does a forced refresh + if ( bChangeOccurred ) + { + Refresh( REFRESH_TREE_VIEW ); + } +} + +void CElementPropertiesTreeInternal::OnCut() +{ + OnCopy(); + OnRemove(); +} + +void CElementPropertiesTreeInternal::OnClear() +{ + bool bNeedRefresh = false; + CUtlVector< KeyValues * > data; + m_pTree->GetTree()->GetSelectedItemData( data ); + int c = data.Count(); + if ( !c ) + return; + + CElementTreeNotifyScopeGuard notify( "CElementPropertiesTreeInternal::OnClear", NOTIFY_SETDIRTYFLAG, m_pNotify ); + + for ( int i = 0; i < c; ++i ) + { + KeyValues *item = data[ i ]; + Assert( item ); + + //Check to see if this attribute refers to an element + CDmElement *pOwner = GetElementKeyValue<CDmElement>( item, "ownerelement" ); + const char *pAttributeName = item->GetString( "attributeName" ); + + if ( pOwner && pAttributeName[ 0 ] ) + { + CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName ); + DmAttributeType_t attType = pAttribute ? pAttribute->GetType( ) : AT_UNKNOWN; + switch ( attType ) + { + default: + break; + + case AT_ELEMENT: + { + bNeedRefresh = true; + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Element" ); + pAttribute->SetValue( DMELEMENT_HANDLE_INVALID ); + } + break; + + case AT_ELEMENT_ARRAY: + { + bNeedRefresh = true; + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Element" ); + CDmrGenericArray array( pAttribute ); + if ( array.IsValid() ) + { + array.RemoveAll(); + } + } + break; + } + } + } + + if ( bNeedRefresh ) + { + // Does a forced refresh + Refresh( REFRESH_TREE_VIEW ); + } +} + +// For each owner/attribute have an entry and them for each arrayIndex into any array type, need to sort by arrayIndex so we can remove them in reverse order + +static bool ArrayIndexLessFunc( KeyValues * const &lhs, KeyValues* const &rhs ) +{ + bool arrayItem1 = !lhs->IsEmpty( "arrayIndex" ) ? true : false; + int arrayIndex1 = lhs->GetInt( "arrayIndex" ); + bool arrayItem2 = !rhs->IsEmpty( "arrayIndex" ) ? true : false; + int arrayIndex2 = rhs->GetInt( "arrayIndex" ); + + if ( !arrayItem1 || !arrayItem2 ) + return lhs < rhs; + + return arrayIndex1 < arrayIndex2; +} + +struct OwnerAttribute_t +{ + OwnerAttribute_t() : sortedData( 0, 0, ArrayIndexLessFunc ) + { + } + + OwnerAttribute_t( const OwnerAttribute_t& src ) : sortedData( 0, 0, ArrayIndexLessFunc ) + { + pOwner = src.pOwner; + symAttribute = src.symAttribute; + for ( int i = src.sortedData.FirstInorder(); i != src.sortedData.InvalidIndex(); i = src.sortedData.NextInorder( i ) ) + { + sortedData.Insert( src.sortedData[ i ] ); + } + } + + static bool LessFunc( const OwnerAttribute_t& lhs, const OwnerAttribute_t& rhs ) + { + if ( lhs.pOwner != rhs.pOwner ) + return lhs.pOwner < rhs.pOwner; + + return Q_stricmp( lhs.symAttribute.String(), rhs.symAttribute.String() ) < 0; + } + + CDmElement *pOwner; + CUtlSymbol symAttribute; + + CUtlRBTree< KeyValues *, int > sortedData; +}; + +class CSortedElementData +{ +public: + CSortedElementData() : m_Sorted( 0, 0, OwnerAttribute_t::LessFunc ) + { + } + + void AddData( CDmElement *pOwner, const char *attribute, KeyValues *data ) + { + OwnerAttribute_t search; + search.pOwner = pOwner; + search.symAttribute = attribute; + + int idx = m_Sorted.Find( search ); + if ( idx == m_Sorted.InvalidIndex() ) + { + idx = m_Sorted.Insert( search ); + } + + OwnerAttribute_t *entry = &m_Sorted[ idx ]; + Assert( entry ); + + entry->sortedData.Insert( data ); + } + + CUtlRBTree< OwnerAttribute_t, int > m_Sorted; +}; + +bool CElementPropertiesTreeInternal::OnRemoveFromData( KeyValues *item ) +{ + Assert( item ); + + bool arrayItem = !item->IsEmpty( "arrayIndex" ); + int arrayIndex = item->GetInt( "arrayIndex" ); + + //Warning( " item[ %i ] (array? %s)\n", arrayIndex, arrayItem ? "yes" : "no" ); + + CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" ); + const char *pAttributeName = item->GetString( "attributeName" ); + if ( !pOwner || !pAttributeName[ 0 ] ) + return false; + + CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName ); + DmAttributeType_t attType = pAttribute ? pAttribute->GetType( ) : AT_UNKNOWN; + + if ( arrayItem && IsArrayType( attType ) ) + { + CDmrGenericArray array( pAttribute ); + if ( !array.IsValid() ) + return false; + + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Array element" ); + + array.Remove( arrayIndex ); + return true; + } + + if ( attType == AT_ELEMENT ) + { + if ( pOwner->GetValue< DmElementHandle_t >( pAttributeName ) != DMELEMENT_HANDLE_INVALID ) + { + // remove the referenced element from this attribute + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Element" ); + pAttribute->SetValue( DMELEMENT_HANDLE_INVALID ); + } + else if ( !pAttribute->IsFlagSet( FATTRIB_EXTERNAL ) && !pAttribute->IsFlagSet( FATTRIB_READONLY ) ) + { + // remove the attribute + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Attribute" ); + pOwner->RemoveAttribute( pAttributeName ); + } + return true; + } + + if ( attType == AT_ELEMENT_ARRAY ) + { + CDmrGenericArray array( pOwner, pAttributeName ); + if ( array.IsValid() && array.Count() > 0 ) + { + // remove the all the elements from the array + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Element Array Items" ); + array.RemoveAll(); + } + else if ( !pAttribute->IsFlagSet( FATTRIB_EXTERNAL ) && !pAttribute->IsFlagSet( FATTRIB_READONLY ) ) + { + // remove the attribute + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Attribute" ); + pOwner->RemoveAttribute( pAttributeName ); + } + return true; + } + + if ( !pAttribute->IsFlagSet( FATTRIB_EXTERNAL ) + && !pAttribute->IsFlagSet( FATTRIB_TOPOLOGICAL ) + && !pAttribute->IsFlagSet( FATTRIB_READONLY ) ) + { + if ( attType >= AT_FIRST_ARRAY_TYPE ) + { + CDmrGenericArray array( pOwner, pAttributeName ); + if ( array.IsValid() && array.Count() > 0 ) + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Array Items" ); + array.RemoveAll(); + } + else + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Attribute" ); + pOwner->RemoveAttribute( pAttributeName ); + } + } + else + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Attribute" ); + pOwner->RemoveAttribute( pAttributeName ); + } + return true; + } + return false; +} + +bool CElementPropertiesTreeInternal::OnRemoveFromData( CUtlVector< KeyValues * >& list ) +{ + CSortedElementData sorted; + int i; + int c = list.Count(); + for ( i = 0 ; i < c; ++i ) + { + KeyValues *item = list[ i ]; + //Check to see if this attribute refers to an element + CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" ); + const char *pAttributeName = item->GetString( "attributeName" ); + if ( !pOwner || !pAttributeName[ 0 ] ) + continue; + + sorted.AddData( pOwner, pAttributeName, item ); + } + + bool bRefreshRequired = false; + + // Now walk the data in reverse order + for ( i = sorted.m_Sorted.FirstInorder(); i != sorted.m_Sorted.InvalidIndex(); i = sorted.m_Sorted.NextInorder( i ) ) + { + OwnerAttribute_t& entry = sorted.m_Sorted[ i ]; + + // Walk it backward by array index... + for ( int j = entry.sortedData.LastInorder(); j != entry.sortedData.InvalidIndex(); j = entry.sortedData.PrevInorder( j ) ) + { + KeyValues *item = entry.sortedData[ j ]; + bRefreshRequired = OnRemoveFromData( item ) || bRefreshRequired; + } + } + + return bRefreshRequired; +} + +void CElementPropertiesTreeInternal::OnRemove() +{ + CElementTreeNotifyScopeGuard notify( "CElementPropertiesTreeInternal::OnRemove", NOTIFY_SETDIRTYFLAG, m_pNotify ); + + CUtlVector< KeyValues * > data; + m_pTree->GetTree()->GetSelectedItemData( data ); + bool bRefreshNeeded = OnRemoveFromData( data ); + if ( bRefreshNeeded ) + { + // Refresh the tree + Refresh( REFRESH_TREE_VIEW ); + } +} + + +//----------------------------------------------------------------------------- +// Sorts by name +//----------------------------------------------------------------------------- +int ElementNameSortFunc( const void *arg1, const void *arg2 ) +{ + CDmElement *pElement1 = *(CDmElement**)arg1; + CDmElement *pElement2 = *(CDmElement**)arg2; + + const char *pName1 = pElement1 ? pElement1->GetName() : ""; + const char *pName2 = pElement2 ? pElement2->GetName() : ""; + + return Q_stricmp( pName1, pName2 ); +} + +void CElementPropertiesTreeInternal::OnSortByName() +{ + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Sort Element Array Attribute" ); + + CUtlVector< KeyValues * > list; + m_pTree->GetTree()->GetSelectedItemData( list ); + + int c = list.Count(); + + bool bRefreshNeeded = false; + for ( int i = 0 ; i < c; ++i ) + { + KeyValues *item = list[ i ]; + + //Check to see if this attribute refers to an element + CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" ); + const char *pAttributeName = item->GetString( "attributeName" ); + + CDmrElementArray<> elementArray( pOwner, pAttributeName ); + if ( !elementArray.IsValid() ) + continue; + + int nCount = elementArray.Count(); + if ( nCount == 0 ) + continue; + + bRefreshNeeded = true; + CDmElement **pArray = ( CDmElement** )_alloca( nCount * sizeof( CDmElement* ) ); + for ( int i = 0; i < nCount; ++i ) + { + pArray[i] = elementArray[i]; + } + + qsort( pArray, nCount, sizeof( CDmElement* ), ElementNameSortFunc ); + + elementArray.RemoveAll(); + elementArray.AddMultipleToTail( nCount ); + + for ( int i = 0; i < nCount; ++i ) + { + elementArray.Set( i, pArray[i] ); + } + } + + if ( bRefreshNeeded ) + { + // Refresh the tree + Refresh( REFRESH_TREE_VIEW ); + } +} + +void CElementPropertiesTreeInternal::JumpToHistoryItem() +{ + if ( m_nCurrentHistoryPosition < 0 || m_nCurrentHistoryPosition >= m_hHistory.Count() ) + { + m_nCurrentHistoryPosition = 0; + } + + if ( !m_hHistory.Count() ) + return; + + CDmElement *element = m_hHistory[ m_nCurrentHistoryPosition ].Get(); + if ( !element ) + return; + + bool save = m_bSuppressHistoryUpdates; + m_bSuppressHistoryUpdates = true; + + SetObject( element ); + + m_bSuppressHistoryUpdates = save; + + // Does a forced refresh + Refresh( REFRESH_TREE_VIEW ); + + // Used by the Dme panel to refresh the combo boxes when we change objects + KeyValues *kv = new KeyValues( "NotifyViewedElementChanged" ); + SetElementKeyValue( kv, "dmeelement", element ); + PostActionSignal( kv ); +} + +void CElementPropertiesTreeInternal::OnShowSearchResults() +{ + if ( !m_SearchResults.Count() ) + return; + + if ( !m_SearchResultsRoot.Get() ) + return; + + SetObject( m_SearchResultsRoot.Get() ); + + // Used by the Dme panel to refresh the combo boxes when we change objects + KeyValues *kv = new KeyValues( "NotifyViewedElementChanged" ); + SetElementKeyValue( kv, "dmeelement", m_SearchResultsRoot.Get() ); + PostActionSignal( kv ); +} + +void CElementPropertiesTreeInternal::OnNavBack( int item ) +{ + int c = m_hHistory.Count(); + if ( c <= 1 ) + return; + + if ( item == -1 ) + { + if ( m_nCurrentHistoryPosition >= c - 1 ) + return; + + item = 1; + } + + m_nCurrentHistoryPosition += item; + Assert( m_nCurrentHistoryPosition < c ); + + JumpToHistoryItem(); + + UpdateButtonState(); +} + +void CElementPropertiesTreeInternal::OnNavForward( int item ) +{ + int c = m_hHistory.Count(); + if ( c <= 0 ) + return; + + if ( item == -1 ) + { + if ( m_nCurrentHistoryPosition <= 0 ) + return; + + item = 0; + } + + ++item; + + m_nCurrentHistoryPosition -= item; + Assert( m_nCurrentHistoryPosition >= 0 ); + + JumpToHistoryItem(); + + UpdateButtonState(); +} + +bool CElementPropertiesTreeInternal::BuildExpansionListToFindElement_R( + CUtlRBTree< CDmElement *, int >& visited, + int depth, + SearchResult_t &sr, + CDmElement *owner, + CDmElement *element, + const char *attributeName, + int arrayIndex, + CUtlVector< int >& expandIndices + ) +{ + if ( !element ) + return true; + + if ( visited.Find( element ) != visited.InvalidIndex() ) + return true; + + visited.Insert( element ); + + int nAttributes = element->AttributeCount(); + + if ( element == sr.handle.Get() ) + { + if ( sr.attributeName.Length() > 0 ) + { + int idx = nAttributes - 1; + for ( CDmAttribute *attribute = element->FirstAttribute(); attribute; attribute = attribute->NextAttribute(), --idx ) + { + const char *attributeName = attribute->GetName(); + if ( !Q_stricmp( attributeName, sr.attributeName.Get() ) ) + { + expandIndices.AddToTail( idx ); + break; + } + } + } + return false; + } + + int idx = nAttributes - 1; + for ( CDmAttribute *attribute = element->FirstAttribute(); attribute; attribute = attribute->NextAttribute(), --idx ) + { + const char *attributeName = attribute->GetName(); + if ( attribute->GetType() == AT_ELEMENT ) + { + if ( !BuildExpansionListToFindElement_R( visited, depth + 1, sr, element, attribute->GetValueElement<CDmElement>(), attributeName, -1, expandIndices ) ) + { + expandIndices.AddToTail( idx ); + return false; + } + } + else if ( attribute->GetType() == AT_ELEMENT_ARRAY ) + { + // Walk child objects + const CDmrElementArray<CDmElement> elementArray( attribute ); + int c = elementArray.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( !BuildExpansionListToFindElement_R( visited, depth + 1, sr, element, elementArray[ i ], attributeName, i, expandIndices ) ) + { + expandIndices.AddToTail( i ); + expandIndices.AddToTail( idx ); + return false; + } + } + } + } + + return true; +} + +static ConVar dme_properties_maxsearchresults( "dme_properties_maxsearchresults", "50", 0, "Max number of search results to track." ); + +void CElementPropertiesTreeInternal::FindMatchingElements_R( CUtlRBTree< CDmElement *, int >& visited, const char *searchstr, CDmElement *element, CUtlVector< SearchResult_t >& list ) +{ + if ( list.Count() >= dme_properties_maxsearchresults.GetInt() ) + return; + + if ( !element ) + return; + + if ( visited.Find( element ) != visited.InvalidIndex() ) + return; + + visited.Insert( element ); + + if ( Q_stristr( element->GetName(), searchstr ) ) + { + CDmeHandle< CDmElement > h; + h = element; + + SearchResult_t sr; + sr.handle = h; + sr.attributeName = ""; + + if ( list.Find( sr ) == list.InvalidIndex() ) + { + list.AddToTail( sr ); + } + } + + for ( CDmAttribute *attribute = element->FirstAttribute(); attribute; attribute = attribute->NextAttribute() ) + { + const char *attributeName = attribute->GetName(); + if ( Q_stristr( attributeName, searchstr ) ) + { + CDmeHandle< CDmElement > h; + h = element; + + SearchResult_t sr; + sr.handle = h; + sr.attributeName = attributeName; + + if ( list.Find( sr ) == list.InvalidIndex() ) + { + list.AddToTail( sr ); + } + } + + if ( attribute->GetType() == AT_ELEMENT ) + { + FindMatchingElements_R( visited, searchstr, attribute->GetValueElement<CDmElement>(), list ); + } + else if ( attribute->GetType() == AT_ELEMENT_ARRAY ) + { + // Walk child objects + const CDmrElementArray<CDmElement> elementArray( attribute ); + int c = elementArray.Count(); + for ( int i = 0; i < c; ++i ) + { + FindMatchingElements_R( visited, searchstr, elementArray[ i ], list ); + } + } + } +} + +void CElementPropertiesTreeInternal::OnNavigateSearchAgain( int direction ) +{ + if ( m_SearchResults.Count() <= 0 ) + { + surface()->PlaySound("common/warning.wav"); + return; + } + + if ( direction < 0 ) + { + direction = -1; + } + else if ( direction >= 0 ) + { + direction = 1; + } + + m_nCurrentSearchResult = m_nCurrentSearchResult + direction; + + if ( m_nCurrentSearchResult < 0 ) + { + m_nCurrentSearchResult = 0; + surface()->PlaySound("common/warning.wav"); + } + else if ( m_nCurrentSearchResult >= m_SearchResults.Count() ) + { + m_nCurrentSearchResult = m_SearchResults.Count() - 1; + surface()->PlaySound("common/warning.wav"); + } + + NavigateToSearchResult(); + + UpdateButtonState(); +} + +void CElementPropertiesTreeInternal::NavigateToSearchResult() +{ + if ( !m_SearchResults.Count() ) + return; + +// SetObject( m_SearchResultsRoot.Get() ); + + CUtlVector< int > expandIndices; + CUtlRBTree< CDmElement *, int > visited( 0, 0, DefLessFunc( CDmElement * ) ); + + BuildExpansionListToFindElement_R( + visited, + 0, + m_SearchResults[ m_nCurrentSearchResult ], + m_hObject.Get(), + m_hObject.Get(), + "name", + -1, + expandIndices ); + + expandIndices.AddToTail( 0 ); + + // Close the tree and re-create the root node only + UpdateTree(); + + // NOTE: Updating the tree could have changed the root item index + int nIndex = m_pTree->GetTree()->GetRootItemIndex(); + int c = expandIndices.Count(); + for ( int i = c - 2; i >= 0 ; --i ) + { + int idx = expandIndices[ i ]; + + // Expand the item + m_pTree->ExpandItem( nIndex, true ); + +#ifdef _DEBUG + int children = m_pTree->GetTree()->GetNumChildren( nIndex ); + if ( idx >= children ) + { + Assert( 0 ); + break; + } +#endif + int childIndex = m_pTree->GetTree()->GetChild( nIndex, idx ); + nIndex = childIndex; + } + + m_pTree->ExpandItem( nIndex, true ); + + // Add to selection, but don't request focus (3rd param) + m_pTree->GetTree()->AddSelectedItem( nIndex, true, false ); + m_pTree->GetTree()->MakeItemVisible( nIndex ); + + m_pTree->ResizeTreeToExpandedWidth(); + + DevMsg( "Displaying search result %d of %d\n", m_nCurrentSearchResult + 1, m_SearchResults.Count() ); +} + +void CElementPropertiesTreeInternal::OnNavSearch( const char *text ) +{ + Msg( "OnNavSearch(%s)\n", text); + if ( !text || !*text ) + { + UpdateButtonState(); + return; + } + + bool changed = Q_stricmp( text, m_szSearchStr ) != 0 ? true : false; + if ( changed ) + { + m_SearchResults.RemoveAll(); + Q_strncpy( m_szSearchStr, text, sizeof( m_szSearchStr ) ); + m_nCurrentSearchResult = 0; + + CUtlRBTree< CDmElement *, int > visited( 0, 0, DefLessFunc( CDmElement * ) ); + + FindMatchingElements_R( visited, m_szSearchStr, m_hObject.Get(), m_SearchResults ); + + AddToSearchHistory( text ); + + if ( m_SearchResultsRoot.Get() ) + { + CDisableUndoScopeGuard guard; + + int c = m_SearchResults.Count(); + + char sz[ 512 ]; + Q_snprintf( sz, sizeof( sz ), "Search Results [%d] for '%s'", c, m_szSearchStr ); + + m_SearchResultsRoot->SetName( sz ); + + CDmrElementArray<> array( m_SearchResultsRoot, "results" ); + if ( array.IsValid() ) + { + array.RemoveAll(); + for ( int i = 0; i < c; ++i ) + { + if ( m_SearchResults[ i ].handle.Get() ) + { + array.AddToTail( m_SearchResults[ i ].handle.GetHandle() ); + } + } + } + } + } + else + { + ++m_nCurrentSearchResult; + } + + if ( !m_SearchResults.Count() ) + { + // Close the tree and re-create the root node only + UpdateTree(); + + int nIndex = m_pTree->GetTree()->GetRootItemIndex(); + m_pTree->ExpandItem( nIndex, true ); + + m_pTree->ResizeTreeToExpandedWidth(); + + UpdateButtonState(); + return; + } + + m_nCurrentSearchResult = clamp( m_nCurrentSearchResult, 0, m_SearchResults.Count() - 1 ); + + NavigateToSearchResult(); + + UpdateButtonState(); +} + +int CElementPropertiesTreeInternal::GetHistoryMenuItemCount( int whichMenu ) +{ + int c = m_hHistory.Count(); + if ( !c ) + return 0; + + if ( m_nCurrentHistoryPosition == -1 ) + { + m_nCurrentHistoryPosition = 0; + } + + switch ( whichMenu ) + { + default: + Assert( 0 ); + break; + case DME_PROPERTIESTREE_MENU_BACKWARD: + { + return c - ( m_nCurrentHistoryPosition + 1 ); + } + break; + case DME_PROPERTIESTREE_MENU_FORWARD: + { + return m_nCurrentHistoryPosition; + } + break; + case DME_PROPERTIESTREE_MENU_SEARCHHSITORY: + { + return m_SearchHistory.Count(); + } + break; + } + + return 0; +} + +void CElementPropertiesTreeInternal::PopulateHistoryMenu( int whichMenu, Menu *menu ) +{ + ValidateHistory(); + + int c = m_hHistory.Count(); + + if ( m_nCurrentHistoryPosition == -1 ) + { + m_nCurrentHistoryPosition = 0; + } + + menu->DeleteAllItems(); + switch ( whichMenu ) + { + default: + Assert( 0 ); + break; + case DME_PROPERTIESTREE_MENU_BACKWARD: + { + for ( int i = m_nCurrentHistoryPosition + 1; i < c; ++i ) + { + CDmElement *element = m_hHistory[ i ].Get(); + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "%s < %s >", element->GetName(), element->GetTypeString() ); + menu->AddMenuItem( "backitem", sz, new KeyValues( "OnNavigateBack", "item", i ), this ); + } + } + break; + case DME_PROPERTIESTREE_MENU_FORWARD: + { + for ( int i = 0 ; i < m_nCurrentHistoryPosition; ++i ) + { + CDmElement *element = m_hHistory[ m_nCurrentHistoryPosition - i - 1 ].Get(); + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "%s < %s >", element->GetName(), element->GetTypeString() ); + menu->AddMenuItem( "fwditem", sz, new KeyValues( "OnNavigateForward", "item", i ), this ); + } + } + break; + case DME_PROPERTIESTREE_MENU_SEARCHHSITORY: + { + int c = m_SearchHistory.Count(); + for ( int i = 0; i < c; ++i ) + { + CUtlString& str = m_SearchHistory[ i ]; + menu->AddMenuItem( "search", str.Get(), new KeyValues( "OnNavSearch", "text", str.Get() ), this ); + } + } + break; + } +} + +void CElementPropertiesTreeInternal::AddToSearchHistory( const char *str ) +{ + CUtlString historyString; + historyString = str; + + int c = m_SearchHistory.Count(); + for ( int i = c - 1; i >= 0; --i ) + { + CUtlString& entry = m_SearchHistory[ i ]; + if ( entry == historyString ) + { + m_SearchHistory.Remove( i ); + break; + } + } + + while ( m_SearchHistory.Count() >= DME_PROPERTIESTREE_MAXSEARCHHISTORYITEMS ) + { + m_SearchHistory.Remove( m_SearchHistory.Count() - 1 ); + } + + // Newest item at head of list + m_SearchHistory.AddToHead( historyString ); +} + +void CElementPropertiesTreeInternal::AddToHistory( CDmElement *element ) +{ + if ( m_bSuppressHistoryUpdates ) + return; + + if ( !element ) + return; + + CDmeHandle< CDmElement > h; + h = element; + + // Purge the forward list + if ( m_nCurrentHistoryPosition > 0 ) + { + m_hHistory.RemoveMultiple( 0, m_nCurrentHistoryPosition ); + m_nCurrentHistoryPosition = 0; + } + + // Remove if it's already in the list + m_hHistory.FindAndRemove( h ); + + // Make sure there's room + while ( m_hHistory.Count() >= DME_PROPERTIESTREE_MAXHISTORYITEMS ) + { + m_hHistory.Remove( m_hHistory.Count() - 1 ); + } + + // Most recent is at head + m_hHistory.AddToHead( h ); + + ValidateHistory(); + + UpdateButtonState(); +} + +void CElementPropertiesTreeInternal::ValidateHistory() +{ + int i; + int c = m_hHistory.Count(); + for ( i = c - 1 ; i >= 0; --i ) + { + if ( !m_hHistory[ i ].Get() ) + { + m_hHistory.Remove( i ); + if ( i && i == m_nCurrentHistoryPosition ) + { + --m_nCurrentHistoryPosition; + } + } + } +} + +void CElementPropertiesTreeInternal::SpewHistory() +{ + int i; + int c = m_hHistory.Count(); + for ( i = 0 ; i < c; ++i ) + { + CDmElement *element = m_hHistory[ i ].Get(); + Assert( element ); + if ( !element ) + continue; + + Msg( "%s: [%02d] %s <%s>\n", + ( ( i < m_nCurrentHistoryPosition ) ? "Fwd" : ( i == m_nCurrentHistoryPosition ? "Current" : "Backward" ) ), + i, + element->GetName(), + element->GetTypeString() ); + } +} + + +void CElementPropertiesTreeInternal::AddAttribute( const char *pAttributeName, KeyValues *pContext ) +{ + if ( !pAttributeName || !pAttributeName[ 0 ] ) + { + Warning( "Can't add attribute with an empty name\n" ); + return; + } + + const char *pAttributeType = pContext->GetString( "attributeType" ); + CDmElement *pElement = GetElementKeyValue< CDmElement >( pContext, "element" ); + if ( !pAttributeType || !pAttributeType[0] || !pElement ) + return; + + DmAttributeType_t attributeType = g_pDataModel->GetAttributeTypeForName( pAttributeType ); + if ( attributeType == AT_UNKNOWN ) + { + Warning( "Can't add attribute '%s' because type '%s' is not known\n", pAttributeName, pAttributeType ); + return; + } + + // Make sure attribute name isn't taken already + if ( pElement->HasAttribute( pAttributeName ) ) + { + Warning( "Can't add attribute '%s', attribute with that name already exists\n", pAttributeName ); + return; + } + + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Add Attribute" ); + CDmAttribute *pAttribute = pElement->AddAttribute( pAttributeName, attributeType ); + if ( pAttribute ) + { + pAttribute->AddFlag( FATTRIB_USERDEFINED ); + } + Refresh( REFRESH_TREE_VIEW ); +} + +void CElementPropertiesTreeInternal::SetElementAttribute( const char *pElementName, KeyValues *pContext ) +{ + if ( !pElementName || !pElementName[ 0 ] ) + { + Warning( "Can't set an element attribute with an unnamed element!\n" ); + return; + } + + const char *pAttributeName = pContext->GetString( "attributeName" ); + const char *pElementType = pContext->GetString( "elementType" ); + CDmElement *pElement = GetElementKeyValue< CDmElement >( pContext, "element" ); + if ( !pElementType || !pElementType[0] || !pElement ) + return; + + bool bRefreshRequired = false; + + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Set Element" ); + DmElementHandle_t newElement = g_pDataModel->CreateElement( pElementType, pElementName, pElement->GetFileId() ); + if ( newElement == DMELEMENT_HANDLE_INVALID ) + return; + + CDmAttribute *pAttribute = pElement->GetAttribute( pAttributeName ); + DmAttributeType_t type = pAttribute ? pAttribute->GetType() : AT_UNKNOWN; + switch( type ) + { + case AT_ELEMENT: + pAttribute->SetValue( newElement ); + bRefreshRequired = true; + break; + + case AT_ELEMENT_ARRAY: + { + CDmrElementArray<> array( pAttribute ); + if ( !array.IsValid() ) + { + g_pDataModel->DestroyElement( newElement ); + return; + } + + int idx = pContext->GetInt( "index", -1 ); + + bRefreshRequired = true; + if ( idx == -1 ) + { + array.AddToTail( newElement ); + } + else + { + array.SetHandle( idx, newElement ); + } + } + break; + } + } + + if ( bRefreshRequired ) + { + Refresh( REFRESH_TREE_VIEW ); + } +} + + +//----------------------------------------------------------------------------- +// Called by the input dialog for add attribute + set element +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::OnInputCompleted( KeyValues *pParams ) +{ + KeyValues *pDlg = pParams->FindKey( "OnAddAttribute", false ); + if ( pDlg ) + { + const char *pAttributeName = pParams->GetString( "text" ); + AddAttribute( pAttributeName, pDlg ); + return; + } + + pDlg = pParams->FindKey( "OnSetElement", false ); + if ( pDlg ) + { + const char *pElementName = pParams->GetString( "text" ); + SetElementAttribute( pElementName, pDlg ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Forwards commands to parent +//----------------------------------------------------------------------------- +bool CElementPropertiesTreeInternal::ShowSetElementAttributeDialog( CDmElement *pOwner, + const char *pAttributeName, int nArrayIndex, const char *pElementType ) +{ + if ( !pOwner || !pAttributeName || !pAttributeName[ 0 ] || !pElementType || !pElementType[ 0 ] ) + return false; + + static int elemNum = 0; + char elemName[ 512 ]; + if ( elemNum++ == 0 ) + { + Q_snprintf( elemName, sizeof( elemName ), "newElement" ); + } + else + { + Q_snprintf( elemName, sizeof( elemName ), "newElement%i", elemNum ); + } + + KeyValues *kv = new KeyValues( "OnSetElement", "attributeName", pAttributeName ); + SetElementKeyValue( kv, "element", pOwner ); + kv->SetInt( "index", nArrayIndex ); + kv->SetString( "elementType", pElementType ); + + InputDialog *pSetAttributeDialog = new InputDialog( this, "Set Element", "Element Name:", elemName ); + pSetAttributeDialog->SetSmallCaption( true ); + pSetAttributeDialog->SetDeleteSelfOnClose( true ); + pSetAttributeDialog->DoModal( kv ); + return true; +} + + +bool CElementPropertiesTreeInternal::ShowAddAttributeDialog( CDmElement *pElement, const char *pAttributeType ) +{ + if ( !pElement || !pAttributeType || !pAttributeType[ 0 ] ) + return false; + + static int attrNum = 0; + char attrName[ 512 ]; + if ( attrNum++ == 0 ) + { + Q_snprintf( attrName, sizeof( attrName ), "newAttribute" ); + } + else + { + Q_snprintf( attrName, sizeof( attrName ), "newAttribute%i", attrNum ); + } + + KeyValues *kv = new KeyValues( "OnAddAttribute", "attributeType", pAttributeType ); + SetElementKeyValue( kv, "element", pElement ); + + InputDialog *pAddDialog = new InputDialog( this, "Add Attribute", "Attribute Name:", attrName ); + pAddDialog->SetSmallCaption( true ); + pAddDialog->SetDeleteSelfOnClose( true ); + pAddDialog->DoModal( kv ); + return true; +} + + +//----------------------------------------------------------------------------- +// Forwards commands to parent +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::OnCommand( const char *cmd ) +{ + CUtlVector< KeyValues * > data; + m_pTree->GetTree()->GetSelectedItemData( data ); + if ( !data.Count() ) + return; + + int c = data.Count(); + for ( int i = 0; i < c; ++i ) + { + KeyValues *item = data[ i ]; + Assert( item ); + + // Check to see if this attribute refers to an element + const char *pElementType = StringAfterPrefix( cmd, "element_" ); + if ( pElementType ) + { + CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" ); + const char *pAttributeName = item->GetString( "attributeName" ); + bool arrayItem = !item->IsEmpty( "arrayIndex" ); + int arrayIndex = item->GetInt( "arrayIndex" ); + if ( ShowSetElementAttributeDialog( pOwner, pAttributeName, arrayItem ? arrayIndex : -1, pElementType ) ) + return; + continue; + } + + const char *pAttributeType = StringAfterPrefix( cmd, "attribute_" ); + if ( pAttributeType ) + { + CDmElement *pElement = GetElementKeyValue< CDmElement >( item, "dmeelement" ); + if ( ShowAddAttributeDialog( pElement, pAttributeType ) ) + return; + continue; + } + } + + if ( GetParent() ) + { + GetParent()->OnCommand( cmd ); + } +} + +void CElementPropertiesTreeInternal::OnShowMemoryUsage() +{ + m_bShowMemoryUsage = !m_bShowMemoryUsage; + Refresh( REFRESH_TREE_VIEW, true ); +} + +void CElementPropertiesTreeInternal::OnAddItem() +{ + CUtlVector< KeyValues * > data; + m_pTree->GetTree()->GetSelectedItemData( data ); + int c = data.Count(); + if ( c == 0 ) + return; + + bool bRefreshRequired = false; + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Add Item(s)" ); + + for ( int i = 0; i < c; ++i ) + { + KeyValues *item = data[ i ]; + Assert( item ); + + CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" ); + const char *pAttributeName = item->GetString( "attributeName" ); + CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName ); + DmAttributeType_t attType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN; + + if ( attType == AT_ELEMENT_ARRAY ) + { + CDmrElementArray<> array( pAttribute ); + if ( !array.IsValid() ) + continue; + + CUtlSymbol typeSymbol = array.GetElementType(); + const char *pElementType = g_pDataModel->GetString( typeSymbol ); + const char *pElementTypeName = StringAfterPrefix( pElementType, "Dme" ); + if ( !pElementTypeName ) + { + Warning( "CElementPropertiesTreeInternal::OnAddItem: Unknown Element Type %s\n", pElementType ); + continue; + } + + // make up a unique name + static int elementNum = 0; + char elementName[ 256 ]; + if ( elementNum++ == 0 ) + { + Q_snprintf( elementName, sizeof( elementName ), "new%s", pElementTypeName ); + } + else + { + Q_snprintf( elementName, sizeof( elementName ), "new%s%i", pElementTypeName, elementNum ); + } + + DmElementHandle_t newElement = g_pDataModel->CreateElement( pElementType, elementName, pOwner->GetFileId() ); + if ( newElement != DMELEMENT_HANDLE_INVALID ) + { + array.AddToTail( newElement ); + bRefreshRequired = true; + } + continue; + } + + if ( attType >= AT_FIRST_ARRAY_TYPE ) + { + CDmrGenericArray arrayAttr( pAttribute ); + if ( arrayAttr.IsValid() ) + { + arrayAttr.AddToTail(); + bRefreshRequired = true; + } + continue; + } + } + + if ( !bRefreshRequired ) + { + guard.Abort(); + } + } + + if ( bRefreshRequired ) + { + // Does a forced refresh + Refresh( REFRESH_TREE_VIEW ); + } +} + +void CElementPropertiesTreeInternal::OnSetShared( KeyValues *params ) +{ + bool bShared = params->GetInt( "shared" ) != 0; + + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, bShared ? "Mark Shared" : "Mark Not Shared" ); + + CUtlVector< KeyValues* > selected; + m_pTree->GetTree()->GetSelectedItemData( selected ); + int nSelected = selected.Count(); + for ( int i = 0; i < nSelected; ++i ) + { + KeyValues *kv = selected[ i ]; + CDmElement *pElement = GetElementKeyValue<CDmElement>( kv, "dmeelement" ); + + // element attribute or element array item + if ( pElement ) + { + pElement->SetShared( bShared ); + continue; + } + + CDmElement *pOwner = GetElementKeyValue< CDmElement >( kv, "ownerelement" ); + const char *pAttributeName = kv->GetString( "attributeName" ); + + const CDmrElementArray<> array( pOwner, pAttributeName ); + if ( !array.IsValid() ) + continue; // value attribute, value array item, or value array + + // element array attribute + int nCount = array.Count(); + for ( int j = 0; j < nCount; ++j ) + { + CDmElement *pElement = array[ j ]; + if ( !pElement ) + continue; + + pElement->SetShared( bShared ); + } + } + + Refresh( REFRESH_TREE_VIEW, true ); +} + +void CElementPropertiesTreeInternal::OnChangeFile( KeyValues *params ) +{ + const char *pFileName = params->GetString( "filename" ); + DmFileId_t fileid = g_pDataModel->GetFileId( pFileName ); + + CUtlVector< KeyValues * > data; + m_pTree->GetTree()->GetSelectedItemData( data ); + int nSelected = data.Count(); + if ( !nSelected ) + return; + + CElementTreeNotifyScopeGuard notify( "CElementPropertiesTreeInternal::OnChangeFile", NOTIFY_SETDIRTYFLAG, m_pNotify ); + + bool bRefreshRequired = false; + for ( int i = 0; i < nSelected; ++i ) + { + KeyValues *item = data[ i ]; + Assert( item ); + + //Check to see if this attribute refers to an element + CDmElement *pElement = GetElementKeyValue< CDmElement >( item, "dmeelement" ); + if ( !pElement ) + continue; + + if ( fileid == DMFILEID_INVALID ) + { + fileid = g_pDataModel->FindOrCreateFileId( pElement->GetName() ); + g_pDataModel->SetFileRoot( fileid, pElement->GetHandle() ); + } + + pElement->SetFileId( fileid, TD_DEEP ); + bRefreshRequired = true; + } + + if ( bRefreshRequired ) + { + Refresh( REFRESH_REBUILD ); + } +} + +void CElementPropertiesTreeInternal::OnShowFileDialog( KeyValues *params ) +{ + const char *pTitle = params->GetString( "title" ); + bool bOpenOnly = params->GetInt( "openOnly" ) != 0; + KeyValues *pContext = params->FindKey( "context" ); + FileOpenDialog *pDialog = new FileOpenDialog( this, pTitle, bOpenOnly, pContext->MakeCopy() ); + + char pStartingDir[ MAX_PATH ]; + GetModSubdirectory( NULL, pStartingDir, sizeof( pStartingDir ) ); + Q_StripTrailingSlash( pStartingDir ); + + pDialog->SetStartDirectoryContext( pTitle, pStartingDir ); + pDialog->AddFilter( "*.*", "All Files (*.*)", false ); + pDialog->AddFilter( "*.dmx", "Generic MovieObjects File (*.dmx)", true, "movieobjects" ); // read/write generic movieobjects files + + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( true ); +} + +void CElementPropertiesTreeInternal::OnImportElement( const char *pFullPath, KeyValues *pContext ) +{ + CDmElement *pRoot = NULL; + DmFileId_t tempFileid; + { + CDisableUndoScopeGuard guard; + tempFileid = g_pDataModel->RestoreFromFile( pFullPath, NULL, NULL, &pRoot, CR_FORCE_COPY ); + } + if ( !pRoot ) + return; + + CDmElement *pParent = GetElementKeyValue<CDmElement>( pContext, "owner" ); + + pRoot->SetFileId( pParent->GetFileId(), TD_DEEP, true ); + g_pDataModel->RemoveFileId( tempFileid ); + + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Import Element" ); + + const char *pAttributeName = pContext->GetString( "attribute" ); + int nArrayIndex = pContext->GetInt( "index", -1 ); + DmElementHandle_t hRoot = pRoot->GetHandle(); + if ( nArrayIndex >= 0 ) + { + CDmrElementArray<> elemArrayAttr( pParent, pAttributeName ); + elemArrayAttr.SetHandle( nArrayIndex, hRoot ); + } + else + { + CDmAttribute *pAttribute = pParent->GetAttribute( pAttributeName ); + if ( pAttribute->GetType() == AT_ELEMENT ) + { + pAttribute->SetValue( hRoot ); + } + else if ( pAttribute->GetType() == AT_ELEMENT_ARRAY ) + { + CDmrElementArray<> elemArrayAttr( pAttribute ); + elemArrayAttr.AddToTail( hRoot ); + } + } + } + + Refresh( REFRESH_TREE_VIEW ); +} + +void CElementPropertiesTreeInternal::OnExportElement( const char *pFullPath, KeyValues *pContext ) +{ + CDmElement *pRoot = NULL; + + CUtlVector< KeyValues * > selection; + m_pTree->GetTree()->GetSelectedItemData( selection ); + int nSelected = selection.Count(); + if ( nSelected <= 1 ) + { + pRoot = GetElementKeyValue<CDmElement>( pContext, "element" ); + } + else + { + // HACK - this is just a temporary hack - we should really force serialization to traverse past fileid changes in this case + KeyValues *item = selection[ 0 ]; + CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" ); + DmFileId_t fileid = pOwner->GetFileId(); + + pRoot = CreateElement< CDmElement >( pFullPath, fileid ); + CDmrElementArray<> children( pRoot, "children", true ); + + for ( int si = 0; si < nSelected; ++si ) + { + KeyValues *item = selection[ si ]; + Assert( item ); + + //Check to see if this attribute refers to an element + CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" ); + if ( pOwner == NULL ) + continue; + + CDmAttribute *pAttr = pOwner->GetAttribute( item->GetString( "attributeName" ) ); + if ( pAttr == NULL ) + continue; + + DmAttributeType_t attrType = pAttr->GetType(); + if ( attrType == AT_ELEMENT ) + { + children.AddToTail( pAttr->GetValue< DmElementHandle_t >() ); + } + else if ( attrType == AT_ELEMENT_ARRAY ) + { + const CDmrElementArray<> arrayAttr( pAttr ); + int n = arrayAttr.Count(); + int index = item->GetInt( "arrayIndex", -1 ); + if ( index >= 0 ) + { + children.AddToTail( arrayAttr[ index ] ); + } + else + { + for ( int i = 0; i < n; ++i ) + { + children.AddToTail( arrayAttr[ i ] ); + } + } + } + } + } + + // if this control is ever moved to vgui_controls, change the default format to "dmx", the generic dmx format + const char *pFileFormat = "movieobjects"; + const char *pFileEncoding = g_pDataModel->GetDefaultEncoding( pFileFormat ); + g_pDataModel->SaveToFile( pFullPath, NULL, pFileEncoding, pFileFormat, pRoot ); + + if ( nSelected > 1 ) + { + DestroyElement( pRoot ); + } +} + +void CElementPropertiesTreeInternal::OnFileSelected( KeyValues *params ) +{ + const char *pFullPath = params->GetString( "fullpath" ); + KeyValues *pContext = params->FindKey( "context" ); + const char *pCommand = pContext->GetString( "command" ); + if ( V_strcmp( pCommand, "OnImportElement" ) == 0 ) + { + OnImportElement( pFullPath, pContext ); + } + else if ( V_strcmp( pCommand, "OnExportElement" ) == 0 ) + { + OnExportElement( pFullPath, pContext ); + } + else + { + Assert( 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Creates an attribute data widget using a specifically requested widget +//----------------------------------------------------------------------------- +vgui::Panel *CElementPropertiesTreeInternal::CreateAttributeDataWidget( CDmElement *pElement, + const char *pWidgetName, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex ) +{ + AttributeWidgetInfo_t info; + SetupWidgetInfo( &info, pElement, pAttribute, nArrayIndex ); + IAttributeWidgetFactory *pFactory = attributewidgetfactorylist->GetWidgetFactory( pWidgetName ); + if ( !pFactory ) + return NULL; + return pFactory->Create( NULL, info ); +} + + +// ------------------------------------------------------------------------------ + +void CElementPropertiesTreeInternal::UpdateTree() +{ + m_pTree->RemoveAll(); + if ( m_hObject.Get() ) + { + m_AttributeWidgets.RemoveAll(); + + char label[ 256 ]; + Q_snprintf( label, sizeof( label ), "%s", m_hObject->GetValueString( "name" ) ); + bool editableLabel = true; + + KeyValues *kv = new KeyValues( "item" ); + kv->SetString( "Text", label ); + kv->SetInt( "Expand", 1 ); + kv->SetInt( "dmeelement", m_hObject.Get() ? m_hObject.Get()->GetHandle() : DMELEMENT_HANDLE_INVALID ); + kv->SetInt( "ownerelement", m_hObject.Get() ? m_hObject.Get()->GetHandle() : DMELEMENT_HANDLE_INVALID ); + kv->SetString( "attributeName", "name" ); + kv->SetInt( "root", m_hObject.Get() ? m_hObject.Get()->GetHandle() : DMELEMENT_HANDLE_INVALID); + kv->SetInt( "editablelabel", editableLabel ? 1 : 0 ); + + CDmElement *pElement = m_hObject.Get(); + vgui::Panel *widget = CreateAttributeDataWidget( pElement, "element", pElement, NULL ); + + CUtlVector< Panel * > columns; + columns.AddToTail( NULL ); + columns.AddToTail( widget ); + int rootIndex = m_pTree->AddItem( kv, editableLabel, -1, columns ); + + m_pTree->GetTree()->SetItemFgColor( rootIndex, Color( 66, 196, 66, 255 ) ); + m_pTree->GetTree()->SetItemSelectionUnfocusedBgColor( rootIndex, Color( 255, 153, 35, 255 ) ); + + kv->deleteThis(); + + // open up the root item (for now) + m_pTree->ExpandItem(rootIndex, true); + + if ( m_SearchResultsRoot.Get() == m_hObject.Get() ) + { + // Expand "results" too + TreeItem_t item; + + item.m_pArrayElement = NULL; + item.m_pElement = m_SearchResultsRoot.Get(); + item.m_pAttributeName = "results"; + + // Look for a match + int nChildIndex = FindTreeItem( rootIndex, item ); + if ( nChildIndex >= 0 ) + { + m_pTree->ExpandItem( nChildIndex, true ); + } + } + } + m_pTree->InvalidateLayout(); +} + +void CElementPropertiesTreeInternal::GenerateDragDataForItem( int itemIndex, KeyValues *msg ) +{ + KeyValues *data = m_pTree->GetItemData( itemIndex ); + if ( !data || !msg ) + { + return; + } + + msg->SetInt( "dmeelement", data->GetInt( "dmeelement" ) ); + msg->SetInt( "ownerelement", data->GetInt( "ownerelement" ) ); + msg->SetString( "attributeName", data->GetString( "attributeName" ) ); + msg->SetInt( "arrayIndex", data->GetInt( "arrayIndex" ) ); + + msg->SetString( "text", data->GetString( "Text" ) ); +} + +struct DataModelFilenameArray +{ + int Count() const + { + return g_pDataModel->NumFileIds(); + } + const char *operator[]( int i ) const + { + return g_pDataModel->GetFileName( g_pDataModel->GetFileId( i ) ); + } +}; + +void CElementPropertiesTreeInternal::GenerateContextMenu( int itemIndex, int x, int y ) +{ + KeyValues *data = m_pTree->GetItemData( itemIndex ); + if ( !data ) + { + Assert( data ); + return; + } + + if ( m_hContextMenu.Get() ) + { + delete m_hContextMenu.Get(); + m_hContextMenu = NULL; + } + + m_hContextMenu = new Menu( this, "ActionMenu" ); + m_hContextMenu->SetFont( m_pTree->GetTree()->GetFont() ); + Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); + int id; + + // ---------------------------------------------------- + // What have we clicked on? + + // inspect the data + CDmElement *pElement = GetElementKeyValue< CDmElement >( data, "dmeelement" ); + CDmElement *pOwner = GetElementKeyValue< CDmElement >( data, "ownerelement" ); + const char *pAttributeName = data->GetString( "attributeName" ); + int nArrayIndex = data->GetInt( "arrayIndex", -1 ); + + // get the type + CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName ); + DmAttributeType_t attributeType = pAttribute->GetType(); + + // figure out the context + CDmrGenericArray array( pAttribute ); + + bool bIsAttribute = data->IsEmpty( "arrayIndex" ); + bool bIsArrayItem = !bIsAttribute; + bool bIsArrayAttribute = !bIsArrayItem && ( attributeType >= AT_FIRST_ARRAY_TYPE ); + bool bIsArrayAttributeEmpty = bIsArrayAttribute && ( array.Count() == 0 ); + bool bIsElementAttribute = bIsAttribute && ( attributeType == AT_ELEMENT ); + bool bIsElementArrayAttribute = bIsArrayAttribute && ( attributeType == AT_ELEMENT_ARRAY ); + bool bIsElementArrayItem = bIsArrayItem && ( attributeType == AT_ELEMENT_ARRAY ); + bool bIsElementAttributeNull = bIsElementAttribute && ( pElement == NULL ); + + // ---------------------------------------------------- + // menu title == what's my context? ( 3 x 2 ) + // 3: Item | Array | Attribute + // 2: Element | Not Element + + if ( bIsElementArrayItem ) + { + m_hContextMenu->AddCheckableMenuItem( "* Element Item Operations *", this ); + } + else if ( bIsElementArrayAttribute ) + { + m_hContextMenu->AddCheckableMenuItem( "* Element Array Operations *", this ); + } + else if ( bIsElementAttribute ) + { + m_hContextMenu->AddCheckableMenuItem( "* Element Attribute Operations *", this ); + } + else if ( bIsArrayItem ) + { + m_hContextMenu->AddCheckableMenuItem( "* Item Operations *", this ); + } + else if ( bIsArrayAttribute ) + { + m_hContextMenu->AddCheckableMenuItem( "* Array Operations *", this ); + } + else if ( bIsAttribute ) + { + m_hContextMenu->AddCheckableMenuItem( "* Attribute Operations *", this ); + } + + m_hContextMenu->AddSeparator(); + + // ---------------------------------------------------- + // basic ops: + + // cut / copy / paste + m_hContextMenu->AddMenuItem( "#DmeElementPropertiesCut", new KeyValues( "OnCut" ), this ); + m_hContextMenu->AddMenuItem( "#DmeElementPropertiesCopy", new KeyValues( "OnCopy" ), this ); + id = m_hContextMenu->AddMenuItem( "#DmeElementPropertiesPaste", new KeyValues( "OnPaste" ), this ); + m_hContextMenu->SetItemEnabled( id, vgui::system()->GetClipboardTextCount() > 0 ); + + // paste special + // Would have to get the clipboard contents and examine to enable a cascading "Paste Special" menu here + Menu *pasteSpecial = new Menu( this, "Paste Special" ); + pasteSpecial->SetFont( m_pTree->GetTree()->GetFont() ); + id = m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesPasteSpecial", this, pasteSpecial ); + m_hContextMenu->SetItemEnabled( id, vgui::system()->GetClipboardTextCount() > 0 ); + id = pasteSpecial->AddMenuItem( "Nothing Special", this ); + pasteSpecial->SetItemEnabled( id, false ); + + // clear or remove + int removeItemID; + if ( bIsArrayAttribute && !bIsArrayAttributeEmpty ) + { + removeItemID = m_hContextMenu->AddMenuItem( "#DmeElementPropertiesClear", new KeyValues( "OnRemove" ), this ); + } + else + { + removeItemID = m_hContextMenu->AddMenuItem( "#DmeElementPropertiesRemove", new KeyValues( "OnRemove" ), this ); + } + + // ---------------------------------------------------- + // other ops + + // Rename... + if ( data->GetInt( "editablelabel" ) ) + { + if ( bIsArrayItem ) + { + m_hContextMenu->AddMenuItem( "Rename Element...", new KeyValues( "OnRename" ), this ); + } + else + { + m_hContextMenu->AddMenuItem( "Rename Attribute...", new KeyValues( "OnRename" ), this ); + } + } + + // sort by name + if ( bIsElementArrayAttribute && !bIsArrayAttributeEmpty ) + { + m_hContextMenu->AddMenuItem( "#DmeElementPropertiesSortByName", new KeyValues( "OnSortByName" ), this ); + } + + // ---------------------------------------------------- + // Add item/attr/elem ops: + + // Add Item + if ( bIsArrayAttribute && !bIsElementArrayAttribute ) + { + m_hContextMenu->AddMenuItem( "#DmeElementPropertiesAddItem", new KeyValues( "OnAddItem" ), this ); + } + + // Add Attribute + if ( ( bIsElementAttribute && !bIsElementAttributeNull ) || m_pTree->GetTree()->GetRootItemIndex() == itemIndex || bIsElementArrayItem ) + { + Menu *addMenu = new Menu( this, "AddAttribute" ); + addMenu->SetFont( m_pTree->GetTree()->GetFont() ); + m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesAddAttribute", this, addMenu ); + { + for ( int i = AT_FIRST_VALUE_TYPE; i < AT_TYPE_COUNT; ++i ) + { + const char *typeName = g_pDataModel->GetAttributeNameForType( (DmAttributeType_t)i ); + if ( typeName && typeName[ 0 ] ) + { + char add_attribute[ 256 ]; + Q_snprintf( add_attribute, sizeof( add_attribute ), "attribute_%s", typeName ); + id = addMenu->AddMenuItem( typeName, new KeyValues( "Command", "command", add_attribute ), this ); + addMenu->GetMenuItem( id )->SetContentAlignment( Label::a_center ); + } + } + } + + } + + // New, Add or Replace Element + if ( bIsElementAttribute || bIsElementArrayAttribute || bIsElementArrayItem ) + { + Menu *addMenu = new Menu( this, "SetElement" ); + addMenu->SetFont( m_pTree->GetTree()->GetFont() ); + + if ( bIsElementArrayAttribute ) + { + m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesAddElement", this, addMenu ); + } + else if ( bIsElementAttributeNull ) + { + m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesNewElement", this, addMenu ); + } + else if ( bIsElementAttribute || bIsElementArrayItem ) + { + m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesReplaceElement", this, addMenu ); + } + + // Populate from factories + for ( int i = g_pDataModel->GetFirstFactory(); g_pDataModel->IsValidFactory( i ); i = g_pDataModel->GetNextFactory( i ) ) + { + const char *elementType = g_pDataModel->GetFactoryName( i ); + Assert( elementType && elementType[ 0 ] ); + + char add_element[ 256 ]; + Q_snprintf( add_element, sizeof( add_element ), "element_%s", elementType ); + id = addMenu->AddMenuItem( elementType, new KeyValues( "Command", "command", add_element ), this ); + addMenu->GetMenuItem( id )->SetContentAlignment( Label::a_center ); + } + } + + // sharing + if ( ( bIsElementAttribute && !bIsElementAttributeNull ) || m_pTree->GetTree()->GetRootItemIndex() == itemIndex || bIsElementArrayAttribute || bIsElementArrayItem ) + { + CUtlVector< KeyValues* > selected; + m_pTree->GetTree()->GetSelectedItemData( selected ); + int nElements = 0; + int nShared = 0; + int nSelected = selected.Count(); + for ( int i = 0; i < nSelected; ++i ) + { + KeyValues *kv = selected[ i ]; + CDmElement *pElement = GetElementKeyValue<CDmElement>( kv, "dmeelement" ); + + // element attribute or element array item + if ( pElement ) + { + ++nElements; + if ( pElement->IsShared() ) + { + ++nShared; + } + continue; + } + + CDmElement *pOwner = GetElementKeyValue< CDmElement >( kv, "ownerelement" ); + const char *pAttributeName = kv->GetString( "attributeName" ); + + const CDmrElementArray<> array( pOwner, pAttributeName ); + if ( !array.IsValid() ) + continue; // value attribute, value array item, or value array + + // element array attribute + int nCount = array.Count(); + for ( int j = 0; j < nCount; ++j ) + { + CDmElement *pElement = array[ j ]; + if ( !pElement ) + continue; + + ++nElements; + if ( pElement->IsShared() ) + { + ++nShared; + } + } + } + + if ( nShared < nElements ) + { + m_hContextMenu->AddMenuItem( "Mark Shared", new KeyValues( "OnSetShared", "shared", 1 ), this ); + } + if ( nShared > 0 ) + { + m_hContextMenu->AddMenuItem( "Mark Not Shared", new KeyValues( "OnSetShared", "shared", 0 ), this ); + } + } + + // import element + if ( bIsElementAttribute || bIsElementArrayAttribute || bIsElementArrayItem ) + { + KeyValues *pContext = new KeyValues( "context", "command", "OnImportElement" ); + pContext->SetInt( "owner", ( int )pOwner->GetHandle() ); + pContext->SetString( "attribute", pAttributeName ); + pContext->SetInt( "index", nArrayIndex ); + + KeyValues *kv = new KeyValues( "OnShowFileDialog", "title", "Import Element" ); + kv->SetInt( "openOnly", 1 ); + kv->AddSubKey( pContext ); + m_hContextMenu->AddMenuItem( "Import element...", kv, this ); + } + + // export element + if ( ( bIsElementAttribute && !bIsElementAttributeNull ) || m_pTree->GetTree()->GetRootItemIndex() == itemIndex || bIsElementArrayItem ) + { + KeyValues *pContext = new KeyValues( "context", "command", "OnExportElement" ); + pContext->SetInt( "element", ( int )pElement->GetHandle() ); + + KeyValues *kv = new KeyValues( "OnShowFileDialog", "title", "Export Element" ); + kv->SetInt( "openOnly", 0 ); + kv->AddSubKey( pContext ); + m_hContextMenu->AddMenuItem( "Export element...", kv, this ); + } + + if ( pElement ) + { + Menu *menu = new Menu( this, "ChangeFile" ); + menu->SetFont( m_pTree->GetTree()->GetFont() ); + + m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesChangeFileAssociation", this, menu ); + + int nFiles = g_pDataModel->NumFileIds(); + for ( int i = 0; i < nFiles; ++i ) + { + DmFileId_t fileid = g_pDataModel->GetFileId( i ); + const char *pFileName = g_pDataModel->GetFileName( fileid ); + + if ( !pFileName || !*pFileName ) + continue; // skip invalid and default fileids + + char cmd[ 256 ]; + Q_snprintf( cmd, sizeof( cmd ), "element_changefile %s", pFileName ); + + const char *pText = pFileName; + char text[ 256 ]; + if ( pElement->GetFileId() == fileid ) + { + Q_snprintf( text, sizeof( text ), "* %s", pFileName ); + pText = text; + } + + menu->AddMenuItem( pText, new KeyValues( "OnChangeFile", "filename", pFileName ), this ); + } + + char filename[ MAX_PATH ]; + V_GenerateUniqueName( filename, sizeof( filename ), "unnamed", DataModelFilenameArray() ); + + menu->AddMenuItem( "<new file>", new KeyValues( "OnChangeFile", "filename", filename ), this ); + } + + // ---------------------------------------------------- + // finally add a seperator after the "Remove" item, unless it's the last item + + if ( ( m_hContextMenu->GetItemCount() - 1 ) != removeItemID ) + { + m_hContextMenu->AddSeparatorAfterItem( removeItemID ); + } + + // ---------------------------------------------------- +} + +void CElementPropertiesTreeInternal::GenerateChildrenOfNode( int itemIndex ) +{ + KeyValues *data = m_pTree->GetItemData( itemIndex ); + if ( !data ) + { + Assert( data ); + return; + } + + //Check to see if this attribute refers to an element + CDmElement *obj = GetElementKeyValue<CDmElement>( data, "dmeelement" ); + if ( obj ) + { + InsertAttributes( itemIndex, obj ); + return; + } + + // Check to see if this node is an array entry, and then do nothing + if ( !data->IsEmpty( "arrayIndex" ) ) + return; + + // Check to see if this attribute is an array attribute + CDmElement *pOwner = GetElementKeyValue< CDmElement >( data, "ownerelement" ); + if ( pOwner ) + { + const char *pAttributeName = data->GetString( "attributeName" ); + CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName ); + if ( pAttribute && IsArrayType( pAttribute->GetType() ) ) + { + InsertAttributeArrayMembers( itemIndex, pOwner, pAttribute ); + return; + } + } +} + +void CElementPropertiesTreeInternal::OnLabelChanged( int itemIndex, const char *oldString, const char *newString ) +{ + KeyValues *data = m_pTree->GetItemData( itemIndex ); + if ( !data ) + { + Assert( data ); + return; + } + + // No change!!! + if ( !Q_stricmp( oldString, newString ) ) + return; + + CDmElement *pElement = GetElementKeyValue< CDmElement >( data, "dmeelement" ); + bool bEditableLabel = data->GetInt( "editablelabel" ); + + CDmElement *pOwner = GetElementKeyValue< CDmElement >( data, "ownerelement" ); + const char *pAttributeName = data->GetString( "attributeName" ); + + int bIsAttribute = data->GetInt( "isAttribute" ); + + int nNotifyFlags = 0; + if ( bEditableLabel ) + { + if ( pElement && !bIsAttribute ) + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Rename Object" ); + pElement->SetName( newString ); + nNotifyFlags = NOTIFY_CHANGE_ATTRIBUTE_VALUE; + } + else if ( pOwner && pAttributeName ) + { + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Rename Attribute" ); + pOwner->RenameAttribute( pAttributeName, newString ); + nNotifyFlags = NOTIFY_CHANGE_TOPOLOGICAL; + } + } + + if ( nNotifyFlags ) + { + Refresh( ( nNotifyFlags == NOTIFY_CHANGE_ATTRIBUTE_VALUE ) ? REFRESH_VALUES_ONLY : REFRESH_TREE_VIEW ); + } +} + +bool CElementPropertiesTreeInternal::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + KeyValues *itemData = m_pTree->GetItemData( itemIndex ); + if ( !itemData ) + return false; + + const char *elementType = itemData->GetString( "droppableelementtype" ); + if ( !elementType || !elementType[ 0 ] ) + return false; + + CUtlVector< CDmElement * > list; + return ElementTree_GetDroppableItems( msglist, elementType, list ); +} + +HCursor CElementPropertiesTreeInternal::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + DropOperation_t op = GetDropOperation( itemIndex, msglist ); + if ( op == DO_COPY ) + return m_hDragCopyCursor; + if ( op == DO_MOVE ) + return m_hDragMoveCursor; + Assert( op == DO_LINK ); + return m_hDragLinkCursor; +} + +struct ArrayItem_t +{ + ArrayItem_t( CDmAttribute *pAttr = NULL, int nIndex = -1 ) : m_pAttr( pAttr ), m_nIndex( nIndex ) {} + + static bool LessFunc( const ArrayItem_t &lhs, const ArrayItem_t &rhs ) + { + if ( lhs.m_pAttr != rhs.m_pAttr ) + return lhs.m_pAttr < rhs.m_pAttr; + return lhs.m_nIndex < rhs.m_nIndex; + } + + CDmAttribute *m_pAttr; + int m_nIndex; +}; + +void CElementPropertiesTreeInternal::DropItemsIntoArray( CDmrElementArray<> &array, CUtlVector< KeyValues* > &msglist, CUtlVector< CDmElement* > &list, int nArrayIndex, DropOperation_t op ) +{ + int nElements = list.Count(); + if ( op == DO_COPY ) + { + CUtlVector< CDmElement* > copylist; + CopyElements( list, copylist ); + list.Swap( copylist ); + } + else if ( op == DO_MOVE ) + { + m_pTree->GetTree()->ClearSelection(); + + CUtlRBTree< ArrayItem_t > arrayItemSorter( 0, msglist.Count(), ArrayItem_t::LessFunc ); + + // sort all element array items and set element attributes to NULL + int nMsgs = msglist.Count(); + for ( int i = 0; i < nMsgs; ++i ) + { + KeyValues *itemData = msglist[ i ]; + + CDmElement *pOwner = GetElementKeyValue< CDmElement >( itemData, "ownerelement" ); + const char *pAttributeName = itemData->GetString( "attributeName" ); + + if ( !pOwner || !pAttributeName || !*pAttributeName ) + continue; + + bool isArrayElement = !itemData->IsEmpty( "arrayIndex" ); + CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName, isArrayElement ? AT_ELEMENT_ARRAY : AT_ELEMENT ); + if ( !pAttribute ) + continue; + + if ( isArrayElement ) + { + int nIndex = itemData->GetInt( "arrayIndex", -1 ); + if ( nIndex < 0 ) + continue; + + arrayItemSorter.Insert( ArrayItem_t( pAttribute, nIndex ) ); + } + else + { + pAttribute->SetValue( DMELEMENT_HANDLE_INVALID ); + } + } + + // walk through all array items, back to front, so that removing won't mess up the indices + for ( int i = arrayItemSorter.LastInorder(); i != arrayItemSorter.InvalidIndex(); i = arrayItemSorter.PrevInorder( i ) ) + { + ArrayItem_t &arrayItem = arrayItemSorter[ i ]; + + CDmrElementArray<> srcArray( arrayItem.m_pAttr ); + srcArray.Remove( arrayItem.m_nIndex ); + + if ( arrayItem.m_pAttr == array.GetAttribute() && arrayItem.m_nIndex < nArrayIndex ) + { + --nArrayIndex; // update nArrayIndex when items before it are removed + } + } + } + + int base = array.InsertMultipleBefore( nArrayIndex, nElements ); + // array.SetMultiple( base, nElements, list.Base() ); + for ( int i = 0; i < nElements; ++i ) + { + array.Set( base + i, list[ i ] ); + if ( array[ base + i ] != list[ i ] ) + { + // if couldn't be dropped into array, skip it and merge remaining items down by one + Assert( array[ base + i ] == NULL ); + array.Remove( base + nElements - 1 ); + --base; + } + } +} + +CElementPropertiesTreeInternal::DropOperation_t CElementPropertiesTreeInternal::GetDropOperation( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + bool bCtrlDown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ); + bool bAltDown = input()->IsKeyDown( KEY_LALT ) || input()->IsKeyDown( KEY_RALT ); + bool bShiftDown = input()->IsKeyDown( KEY_LSHIFT ) || input()->IsKeyDown( KEY_RSHIFT ); + + if ( bAltDown || ( bShiftDown && bCtrlDown ) ) + return DO_LINK; + if ( bCtrlDown ) + return DO_COPY; + if ( bShiftDown ) + return DO_MOVE; + + KeyValues *itemData = m_pTree->GetItemData( itemIndex ); + Assert( itemData ); + if ( !itemData ) + return DO_LINK; + + if ( !ElementTree_IsArrayItem( itemData ) && ElementTree_GetAttributeType( itemData ) != AT_ELEMENT_ARRAY ) + return DO_LINK; // dropping to a non-array attribute + + DropOperation_t op = DO_UNKNOWN; + int nMsgs = msglist.Count(); + for ( int i = 0; i < nMsgs; ++i ) + { + KeyValues *pMsg = msglist[ i ]; + if ( !pMsg || !GetElementKeyValue< CDmElement >( pMsg , "dmeelement" ) ) + continue; // skip non-element drag/drop items + + if ( !ElementTree_IsArrayItem( pMsg ) || ElementTree_GetAttributeType( pMsg ) != AT_ELEMENT_ARRAY ) + return DO_LINK; // dragging from a non-array attribute + + op = DO_MOVE; // basically, op will only stay DO_MOVE if *every* item is a non-element or is an array (or array item) + } + + if ( op == DO_UNKNOWN ) + { + Assert( 0 ); + return DO_LINK; + } + + return op; +} + +void CElementPropertiesTreeInternal::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + if ( !msglist.Count() ) + return; + + KeyValues *itemData = m_pTree->GetItemData( itemIndex ); + if ( !itemData ) + return; + + const char *elementType = itemData->GetString( "droppableelementtype" ); + if ( !elementType || !elementType[ 0 ] ) + return; + + CUtlVector< CDmElement * > list; + ElementTree_GetDroppableItems( msglist, "dmeelement", list ); + if ( !list.Count() ) + return; + + bool isArrayElement = !itemData->IsEmpty( "arrayIndex" ); + //Check to see if this attribute refers to an element + CDmElement *pOwner = GetElementKeyValue< CDmElement >( itemData, "ownerelement" ); + const char *pAttributeName = itemData->GetString( "attributeName" ); + + if ( !pOwner ) + return; + + if ( !pAttributeName[ 0 ] ) + return; + + CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName ); + DmAttributeType_t attType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN; + bool isElementAttribute = attType == AT_ELEMENT || attType == AT_ELEMENT_ARRAY; + if ( !isElementAttribute ) + return; + + DropOperation_t op = GetDropOperation( itemIndex, msglist ); + + const char *cmd = msglist[ 0 ]->GetString( "command" ); + + // Mouse if over an array entry which is an element array type... + if ( isArrayElement ) + { + bool bReplace = Q_stricmp( cmd, "replace" ) == 0; + bool bBefore = Q_stricmp( cmd, "before" ) == 0; + bool bAfter = Q_stricmp( cmd, "after" ) == 0 || Q_stricmp( cmd, "default" ) == 0; + if ( !bReplace && !bBefore && !bAfter ) + { + Warning( "Unknown command '%s'\n", cmd ); + return; + } + + char str[ 128 ]; + V_snprintf( str, sizeof( str ), "%s %s element%s", + bReplace ? "Replace with" : "Insert", + op == DO_COPY ? "copied" : ( op == DO_MOVE ? "moved" : "referenced" ), + bBefore ? " before" : bAfter ? " after" : "" ); + + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, str ); + + int nArrayIndex = itemData->GetInt( "arrayIndex" ); + if ( bAfter ) + { + ++nArrayIndex; + } + + CDmrElementArray<> array( pAttribute ); + if ( bReplace ) + { + array.Remove( nArrayIndex ); + } + + DropItemsIntoArray( array, msglist, list, nArrayIndex, op ); + } + // Mouse is over an element attribute or element array attribute + else + { + // No head/tail stuff for AT_ELEMENT, just replace what's there + if ( attType == AT_ELEMENT ) + { + char str[ 128 ]; + V_snprintf( str, sizeof( str ), "Replace with %s element", + op == DO_COPY ? "copied" : ( op == DO_MOVE ? "moved" : "referenced" ) ); + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, str ); + + pAttribute->SetValue( op == DO_COPY ? list[ 0 ]->Copy() : list[ 0 ] ); + + if ( op == DO_MOVE ) + { + int c = msglist.Count(); + for ( int i = 0; i < c; ++i ) + { + KeyValues *data = msglist[ i ]; + CDmElement *e = GetElementKeyValue<CDmElement>( data, "dmeelement" ); + Assert( !e || e == list[ 0 ] ); + if ( e != list[ 0 ] ) + continue; + + OnRemoveFromData( data ); + break; + } + m_pTree->GetTree()->ClearSelection(); + } + } + else + { + bool bTail = !cmd[ 0 ] || !Q_stricmp( cmd, "default" ) || !Q_stricmp( cmd, "tail" ); + bool bHead = !bTail && !Q_stricmp( cmd, "head" ); + bool bReplace = !bTail && !bHead && !Q_stricmp( cmd, "replace" ); + if ( !bTail && !bHead && !bReplace ) + { + Warning( "Unknown command '%s'\n", cmd ); + return; + } + + char str[ 128 ]; + V_snprintf( str, sizeof( str ), "%s %s elements%s", + bReplace ? "Replace array with" : "Insert", + op == DO_COPY ? "copied" : ( op == DO_MOVE ? "moved" : "referenced" ), + bHead ? " at head" : bTail ? " at tail" : "" ); + + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, str ); + + CDmrElementArray<> array( pAttribute ); + if ( bReplace ) + { + array.RemoveAll(); + } + + DropItemsIntoArray( array, msglist, list, bTail ? array.Count() : 0, op ); + } + } + + CUtlVector< TreeItem_t > dropTargetPath; + if ( isArrayElement ) + { + itemIndex = m_pTree->GetTree()->GetItemParent( itemIndex ); // if we're an array element, start with the array itself + } + GetPathToItem( dropTargetPath, itemIndex ); + + // Does a forced refresh + Refresh( REFRESH_TREE_VIEW ); + + itemIndex = OpenPath( dropTargetPath ); + if ( attType == AT_ELEMENT_ARRAY ) + { + m_pTree->GetTree()->ExpandItem( itemIndex, true ); + } + + if ( op == DO_MOVE ) + { + if ( isArrayElement || attType == AT_ELEMENT_ARRAY ) + { + int nElements = list.Count(); + for ( int i = 0; i < nElements; ++i ) + { + int nChildren = m_pTree->GetTree()->GetNumChildren( itemIndex ); + for ( int ci = 0; ci < nChildren; ++ci ) + { + int nChildItem = m_pTree->GetTree()->GetChild( itemIndex, ci ); + KeyValues *pChildData = m_pTree->GetTree()->GetItemData( nChildItem ); + if ( list[ i ] == GetElementKeyValue< CDmElement >( pChildData, "dmeelement" ) ) + { + m_pTree->GetTree()->AddSelectedItem( nChildItem, false ); + } + } + } + } + else + { + m_pTree->GetTree()->AddSelectedItem( itemIndex, true ); + } + } +} + +bool CElementPropertiesTreeInternal::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + KeyValues *itemData = m_pTree->GetItemData( itemIndex ); + + bool isArrayElement = !itemData->IsEmpty( "arrayIndex" ); + //Check to see if this attribute refers to an element + CDmElement *pOwner = GetElementKeyValue<CDmElement>( itemData, "ownerelement" ); + const char *pAttributeName = itemData->GetString( "attributeName" ); + + if ( !pOwner ) + return false; + if ( !pAttributeName[ 0 ] ) + return false; + + bool isElementAttribute = false; + CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName ); + DmAttributeType_t attType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN; + switch ( attType ) + { + default: + break; + case AT_ELEMENT: + case AT_ELEMENT_ARRAY: + isElementAttribute = true; + break; + } + + if ( isArrayElement && isElementAttribute ) + { + menu->AddMenuItem( "After", "Insert after", "after", this ); + menu->AddMenuItem( "Before", "Insert before", "before", this ); + menu->AddMenuItem( "Replace", "Replace", "replace", this ); + return true; + } + else + { + if ( isElementAttribute && attType == AT_ELEMENT_ARRAY ) + { + CDmrGenericArray array( pAttribute ); + if ( array.IsValid() && array.Count() > 0 ) + { + menu->AddMenuItem( "Tail", "Insert at tail", "tail", this ); + menu->AddMenuItem( "Head", "Insert at head", "head", this ); + menu->AddMenuItem( "Replace", "Replace", "replace", this ); + return true; + } + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Set/get object +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::SetObject( CDmElement *object ) +{ + m_pTree->RemoveAll(); + m_AttributeWidgets.RemoveAll(); + + AddToHistory( object ); + + m_hObject = object; + + Init( ); +} + +CDmElement *CElementPropertiesTreeInternal::GetObject() +{ + return m_hObject.Get(); +} + + +//----------------------------------------------------------------------------- +// Gets tree view text +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::GetTreeViewText( CDmElement* obj, CDmAttribute *pAttribute, int nArrayIndex, char *pBuffer, int nMaxLen, bool& editableText ) +{ + pBuffer[0] = 0; + + editableText = false; + + if ( !obj ) + return; + + const char *pAttributeName = pAttribute->GetName(); + + if ( nArrayIndex < 0 ) + { + // non-array types + Q_strncpy( pBuffer, pAttributeName, nMaxLen ); + + editableText = !pAttribute->IsFlagSet( FATTRIB_EXTERNAL ) && !pAttribute->IsFlagSet( FATTRIB_READONLY ); + } + else + { + // array types + DmAttributeType_t type = pAttribute->GetType( ); + if ( type == AT_ELEMENT_ARRAY ) + { + const CDmrElementArray<> elementArray( pAttribute ); + CDmElement *pEntryElement = elementArray[nArrayIndex]; + if ( pEntryElement ) + { + Q_snprintf( pBuffer, nMaxLen, "%s", pEntryElement->GetValueString( "name" ) ); + editableText = true; + } + } + else + { + Q_snprintf( pBuffer, nMaxLen, "%s[%d]", pAttributeName, nArrayIndex ); + } + } +} + + +//----------------------------------------------------------------------------- +// Finds the tree index of a child matching the particular element + attribute +//----------------------------------------------------------------------------- +int CElementPropertiesTreeInternal::FindTreeItem( int nParentIndex, const TreeItem_t &info ) +{ + // Look for a match + int nCount = m_pTree->GetTree()->GetNumChildren( nParentIndex ); + for ( int i = nCount; --i >= 0; ) + { + int nChildIndex = m_pTree->GetTree()->GetChild( nParentIndex, i ); + KeyValues *data = m_pTree->GetItemData( nChildIndex ); + Assert( data ); + + CDmElement *pElement = GetElementKeyValue< CDmElement >( data, "ownerelement" ); + const char *pAttributeName = data->GetString( "attributeName" ); + CDmElement *pArrayElement = NULL; + if ( data->GetInt( "arrayIndex", -1 ) != -1 ) + { + // Only arrays of element pointers should refer to this + pArrayElement = GetElementKeyValue< CDmElement >( data, "dmeelement" ); + } + + if ( ( pElement == info.m_pElement ) && ( pArrayElement == info.m_pArrayElement ) && + !Q_stricmp( pAttributeName, info.m_pAttributeName ) ) + { + return nChildIndex; + } + } + return -1; +} + +void CElementPropertiesTreeInternal::SpewOpenItems( int depth, OpenItemTree_t &tree, int nOpenTreeIndex, int nItemIndex ) +{ + int i = tree.FirstChild( nOpenTreeIndex ); + if ( nOpenTreeIndex != tree.InvalidIndex() ) + { + TreeInfo_t& info = tree[ nOpenTreeIndex ]; + + if ( info.m_nFlags & EP_EXPANDED ) + { + Msg( "[%d] Marking %s <%s> %s array(%s) [expanded %i]\n", + depth, + info.m_Item.m_pElement->GetName(), + info.m_Item.m_pElement->GetTypeString(), + info.m_Item.m_pAttributeName.Get(), + info.m_Item.m_pArrayElement ? info.m_Item.m_pArrayElement->GetName() : "NULL", + info.m_nFlags & EP_EXPANDED ? 1 : 0 ); + } + } + + while ( i != tree.InvalidIndex() ) + { + TreeInfo_t& info = tree[ i ]; + // Look for a match + int nChildIndex = FindTreeItem( nItemIndex, info.m_Item ); + if ( nChildIndex != -1 ) + { + SpewOpenItems( depth + 1, tree, i, nChildIndex ); + } + else + { + } + i = tree.NextSibling( i ); + } +} + +//----------------------------------------------------------------------------- +// Expands all items in the open item tree if they exist +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::ExpandOpenItems( OpenItemTree_t &tree, int nOpenTreeIndex, int nItemIndex, bool makeVisible ) +{ + int i = tree.FirstChild( nOpenTreeIndex ); + if ( nOpenTreeIndex != tree.InvalidIndex() ) + { + TreeInfo_t& info = tree[ nOpenTreeIndex ]; + if ( info.m_nFlags & EP_EXPANDED ) + { + // Expand the item + m_pTree->ExpandItem( nItemIndex , true ); + } + if ( info.m_nFlags & EP_SELECTED ) + { + m_pTree->GetTree()->AddSelectedItem( nItemIndex, false, false ); + if ( makeVisible ) + { + m_pTree->GetTree()->MakeItemVisible( nItemIndex ); + } + } + } + + while ( i != tree.InvalidIndex() ) + { + TreeInfo_t& info = tree[ i ]; + // Look for a match + int nChildIndex = FindTreeItem( nItemIndex, info.m_Item ); + if ( nChildIndex != -1 ) + { + ExpandOpenItems( tree, i, nChildIndex, makeVisible ); + } + else + { + if ( info.m_nFlags & EP_SELECTED ) + { + // Look for preserved item + int nChildIndex = FindTreeItem( nItemIndex, info.m_Preserved ); + if ( nChildIndex != -1 ) + { + m_pTree->GetTree()->AddSelectedItem( nChildIndex, false, false ); + if ( makeVisible ) + { + m_pTree->GetTree()->MakeItemVisible( nChildIndex ); + } + } + } + } + i = tree.NextSibling( i ); + } +} + +void CElementPropertiesTreeInternal::FillInDataForItem( TreeItem_t &item, int nItemIndex ) +{ + KeyValues *data = m_pTree->GetItemData( nItemIndex ); + if ( !data ) + return; + + item.m_pElement = GetElementKeyValue< CDmElement >( data, "ownerelement" ); + item.m_pAttributeName = data->GetString( "attributeName" ); + if ( data->GetInt( "arrayIndex", -1 ) != -1 ) + { + // Only arrays of element pointers should refer to this + item.m_pArrayElement = GetElementKeyValue< CDmElement >( data, "dmeelement" ); + } + else + { + item.m_pArrayElement = NULL; + } +} + +//----------------------------------------------------------------------------- +// Builds a list of open items +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::BuildOpenItemList( OpenItemTree_t &tree, int nParent, int nItemIndex, bool preservePrevSelectedItem ) +{ + KeyValues *data = m_pTree->GetItemData( nItemIndex ); + if ( !data ) + return; + + bool expanded = m_pTree->IsItemExpanded( nItemIndex ); + bool selected = m_pTree->IsItemSelected( nItemIndex ); + + int flags = 0; + if ( expanded ) + { + flags |= EP_EXPANDED; + } + if ( selected ) + { + flags |= EP_SELECTED; + } + + int nChild = tree.InsertChildAfter( nParent, tree.InvalidIndex() ); + TreeInfo_t &info = tree[nChild]; + FillInDataForItem( info.m_Item, nItemIndex ); + info.m_nFlags = flags; + + if ( selected ) + { + // Set up prev an next item + int preserve = preservePrevSelectedItem + ? m_pTree->GetTree()->GetPrevChildItemIndex( nItemIndex ) : + m_pTree->GetTree()->GetNextChildItemIndex( nItemIndex ); + + if ( preserve != -1 ) + { + FillInDataForItem( info.m_Preserved, preserve ); + } + } + + // Deal with children + int nCount = m_pTree->GetTree()->GetNumChildren( nItemIndex ); + for ( int i = 0; i < nCount; ++i ) + { + int nChildIndex = m_pTree->GetTree()->GetChild( nItemIndex, i ); + BuildOpenItemList( tree, nChild, nChildIndex, preservePrevSelectedItem ); + } +} + + +//----------------------------------------------------------------------------- +// Builds a list of open items +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::RefreshTreeView( bool preservePrevSelectedItem /*= false*/ ) +{ + int nIndex = m_pTree->GetTree()->GetRootItemIndex(); + if ( nIndex >= 0 ) + { + // remember where the tree is scrolled to + ScrollBar *sBar = ((CElementTree *)m_pTree->GetTree())->GetScrollBar(); + int sBarPos = sBar->GetValue(); + + // Build a tree of every open item in the tree view + OpenItemTree_t openItems; + BuildOpenItemList( openItems, openItems.InvalidIndex(), nIndex, preservePrevSelectedItem ); + + // Close the tree and re-create the root node only + UpdateTree(); + + // NOTE: Updating the tree could have changed the root item index + nIndex = m_pTree->GetTree()->GetRootItemIndex(); + + // Iterate through all previously open items and expand them if they exist + if ( openItems.Root() != openItems.InvalidIndex() ) + { + ExpandOpenItems( openItems, openItems.Root(), nIndex, false ); + } + + // and now set the scroll pos back to where is was + // note: the layout needs to be re-Performed so that the + // scrollbars _range values are corrent or the SetValue will fail. + m_pTree->GetTree()->PerformLayout(); + sBar->SetValue( sBarPos ); + } +} + + +//----------------------------------------------------------------------------- +// Refreshes the color state of the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::SetTreeItemColor( int nItemID, CDmElement *pEntryElement, bool bIsElementArrayItem, bool bEditableLabel ) +{ + // dim any element tree items if they are muted or not visible + bool bIsDim = false; + int dimAlpha = 128; + if ( pEntryElement != NULL ) + { + if ( ( pEntryElement->HasAttribute( "visible" ) && !pEntryElement->GetValue< bool >( "visible" ) ) + || ( pEntryElement->HasAttribute( "mute" ) && pEntryElement->GetValue< bool >( "mute" ) ) ) + { + bIsDim = true; + } + } + + // the unfocused Bg color should match the focused one + // so that we can dim lables based on visibility and mute state. + // note: focus is unimportant in this context ( I'm pretty sure ) + m_pTree->GetTree()->SetItemSelectionBgColor( nItemID, Color( 255, 153, 35, bIsDim ? dimAlpha : 255 ) ); + m_pTree->GetTree()->SetItemSelectionUnfocusedBgColor( nItemID, Color( 255, 153, 35, bIsDim ? dimAlpha : 255 ) ); + + if ( bIsElementArrayItem ) + { + // element array items are green + m_pTree->GetTree()->SetItemFgColor( nItemID, Color( 66, 196, 66, bIsDim ? dimAlpha : 255 ) ); + } + else if ( bEditableLabel ) + { + // custom attributes are light yellow + m_pTree->GetTree()->SetItemFgColor( nItemID, Color( 190, 190, 105, bIsDim ? dimAlpha : 255 ) ); + } + else + { + // otherwise it's just light grey + m_pTree->GetTree()->SetItemFgColor( nItemID, Color( 160, 160, 160, bIsDim ? dimAlpha : 255 ) ); + } +} + + +//----------------------------------------------------------------------------- +// Refreshes the color state of the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::RefreshTreeItemState( int nItemID ) +{ + if ( nItemID < 0 ) + return; + + KeyValues *kv = m_pTree->GetTree()->GetItemData( nItemID ); + CDmElement *pEntryElement = GetElementKeyValue<CDmElement>( kv, "dmeelement" ); + bool bIsElementArrayItem = kv->GetInt( "elementArrayItem", 0 ); + bool bEditableLabel = kv->GetInt( "editablelabel", 0 ); + SetTreeItemColor( nItemID, pEntryElement, bIsElementArrayItem, bEditableLabel ); + + int nChildCount = m_pTree->GetTree()->GetNumChildren( nItemID ); + for ( int i = 0; i < nChildCount; ++i ) + { + int nChildID = m_pTree->GetTree()->GetChild( nItemID, i ); + RefreshTreeItemState( nChildID ); + } +} + + +/* +//----------------------------------------------------------------------------- +// Adds a single entry into the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::SetTreeEntryDimState( ) +{ + +} +*/ + + +//----------------------------------------------------------------------------- +// Adds a single entry into the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::CreateTreeEntry( int parentNodeIndex, CDmElement* obj, CDmAttribute *pAttribute, int nArrayIndex, AttributeWidgets_t &widgets ) +{ + char pText[ 512 ]; + bool bEditableLabel = false; + bool bIsExpandable = false; + CDmElement *pEntryElement = NULL; + + GetTreeViewText( obj, pAttribute, nArrayIndex, pText, sizeof(pText), bEditableLabel ); + + const char *pAttributeName = pAttribute->GetName(); + DmAttributeType_t type = pAttribute->GetType( ); + + bool bIsArrayItem = ( nArrayIndex > -1 ); + bool bIsArrayAttribute = ( type >= AT_FIRST_ARRAY_TYPE ) && ! bIsArrayItem; + bool bIsElementAttribute = ( type == AT_ELEMENT ) && ! bIsArrayItem; + bool bIsElementArrayItem = ( type == AT_ELEMENT_ARRAY ) && bIsArrayItem; + bool bIsElementArrayAttribute = ( type == AT_ELEMENT_ARRAY ) && ! bIsArrayItem; + + bool bIsDroppable = bIsElementArrayItem || bIsElementAttribute || bIsElementArrayAttribute; + + if ( bIsElementArrayItem ) + { + const CDmrElementArray<> elementArray( pAttribute ); + pEntryElement = elementArray[nArrayIndex]; + bIsExpandable = true; + } + else if ( bIsElementAttribute ) + { + pEntryElement = obj->GetValueElement< CDmElement>( pAttributeName ); + bIsExpandable = ( pEntryElement != NULL ); + } + else if ( bIsArrayAttribute ) + { + CDmrGenericArray array( pAttribute ); + bIsExpandable = array.Count() > 0; + } + + KeyValues *kv = new KeyValues( "item" ); + kv->SetString( "Text", pText ); + kv->SetInt( "Expand", bIsExpandable ); + SetElementKeyValue( kv, "dmeelement", pEntryElement ); + SetElementKeyValue( kv, "ownerelement", obj ); + kv->SetString( "attributeName", pAttributeName ); + kv->SetPtr( "widget", widgets.m_pValueWidget ); + kv->SetInt( "elementArrayItem", bIsElementArrayItem ? 1 : 0 ); + kv->SetInt( "editablelabel", bEditableLabel ? 1 : 0 ); + kv->SetInt( "isAttribute", bIsArrayItem ? 0 : 1 ); + kv->SetInt( "droppable", bIsDroppable ? 1 : 0 ); + kv->SetFloat( "drophoverdelay", 1.0f ); + + if ( bIsArrayItem ) + { + kv->SetInt( "arrayIndex", nArrayIndex ); + } + + if ( bIsDroppable ) + { + // Can always drop onto arrays + kv->SetString( "droppableelementtype", "dmeelement" ); // FIXME: Should be able to restrict to certain types!!! + } + + CUtlVector< vgui::Panel * > columns; + columns.AddToTail( NULL ); + columns.AddToTail( widgets.m_pValueWidget ); + int itemIndex = m_pTree->AddItem( kv, bEditableLabel, parentNodeIndex, columns ); + SetTreeItemColor( itemIndex, pEntryElement, bIsElementArrayItem, bEditableLabel ); + kv->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Sets up the attribute widget init info for a particular attribute +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::SetupWidgetInfo( AttributeWidgetInfo_t *pInfo, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex ) +{ + const char *pAttributeName = pAttribute ? pAttribute->GetName() : ""; + + pInfo->m_pNotify = m_pNotify; + pInfo->m_bAutoApply = m_bAutoApply; + pInfo->m_pElement = obj; + pInfo->m_pAttributeName = pAttributeName; + pInfo->m_nArrayIndex = nArrayIndex; + pInfo->m_pEditorTypeDictionary = m_hTypeDictionary; + pInfo->m_pEditorInfo = NULL; + pInfo->m_bShowMemoryUsage = m_bShowMemoryUsage; + if ( m_hTypeDictionary && pAttributeName ) + { + if ( nArrayIndex < 0 ) + { + pInfo->m_pEditorInfo = m_hTypeDictionary->GetAttributeInfo( obj, pAttributeName ); + } + else + { + pInfo->m_pEditorInfo = m_hTypeDictionary->GetAttributeArrayInfo( obj, pAttributeName ); + } + } +} + + +//----------------------------------------------------------------------------- +// Adds a single editable attributes of the element to the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::InsertSingleAttribute( int parentNodeIndex, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex ) +{ + const char *attributeName = pAttribute->GetName(); + NOTE_UNUSED( attributeName ); + + // Get information about the widget to create + + IAttributeWidgetFactory *pFactory = NULL; + if ( nArrayIndex >= 0 ) + { + pFactory = attributewidgetfactorylist->GetArrayWidgetFactory( obj, pAttribute, m_hTypeDictionary ); + } + else + { + pFactory = attributewidgetfactorylist->GetWidgetFactory( obj, pAttribute, m_hTypeDictionary ); + } + if ( !pFactory ) + return; + + // Create the widget + AttributeWidgetInfo_t info; + SetupWidgetInfo( &info, obj, pAttribute, nArrayIndex ); + + AttributeWidgets_t attributeWidget; + attributeWidget.m_pValueWidget = pFactory->Create( NULL, info ); + + // set it to the current font size + CBaseAttributePanel *attrPanel = dynamic_cast< CBaseAttributePanel * >( attributeWidget.m_pValueWidget ); + if ( attrPanel ) + { + attrPanel->SetFont( m_pTree->GetFont( m_pTree->GetFontSize() ) ); + } + + // Now create the tree-view entry + CreateTreeEntry( parentNodeIndex, obj, pAttribute, nArrayIndex, attributeWidget ); + + // Add the attribute to the list of them + m_AttributeWidgets.AddToTail( attributeWidget ); +} + + +//----------------------------------------------------------------------------- +// Used to insert attributes in alphabetical order +//----------------------------------------------------------------------------- +struct AttributeInfo_t +{ + CDmAttribute *m_pAttribute; + const char *m_pName; +}; + + +//----------------------------------------------------------------------------- +// Adds editable attributes of the element to the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::InsertAttributes( int parentNodeIndex, CDmElement *obj ) +{ + Assert( obj ); + + // Build a list of attributes for sorting + AttributeInfo_t *pInfo = (AttributeInfo_t*)_alloca( obj->AttributeCount() * sizeof(AttributeInfo_t) ); + int nCount = 0; + for ( CDmAttribute *pAttribute = obj->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() ) + { + pInfo[nCount].m_pAttribute = pAttribute; + pInfo[nCount].m_pName = pAttribute->GetName(); + ++nCount; + } + + // Iterate over each element and create a widget and tree entry for it + for ( int i = nCount - 1; i >= 0; --i ) + { + InsertSingleAttribute( parentNodeIndex, obj, pInfo[i].m_pAttribute ); + } +} + + +//----------------------------------------------------------------------------- +// Removes an item from the tree recursively +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::RemoveItem_R( int nItemIndex ) +{ + KeyValues *data = m_pTree->GetItemData( nItemIndex ); + if ( data ) + { + AttributeWidgets_t search; + search.m_pValueWidget = static_cast<vgui::Panel*>( data->GetPtr( "widget", NULL ) ); + if ( search.m_pValueWidget ) + { + m_AttributeWidgets.FindAndRemove( search ); + } + } + + int nCount = m_pTree->GetTree()->GetNumChildren( nItemIndex ); + for ( int i = 0; i < nCount; ++i ) + { + RemoveItem_R( m_pTree->GetTree()->GetChild( nItemIndex, i ) ); + } +} + + +//----------------------------------------------------------------------------- +// Removes an item from the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::RemoveItem( int nItemIndex ) +{ + RemoveItem_R( nItemIndex ); + m_pTree->RemoveItem( nItemIndex ); +} + + +//----------------------------------------------------------------------------- +// Adds editable attribute array entries to the tree +//----------------------------------------------------------------------------- +void CElementPropertiesTreeInternal::InsertAttributeArrayMembers( int parentNodeIndex, CDmElement *obj, CDmAttribute *pAttribute ) +{ + Assert( obj ); + + // Iterate over each element and create a widget and tree entry for it + CDmrGenericArray array( pAttribute ); + int c = array.Count(); + for ( int i = 0; i < c; i++ ) + { + InsertSingleAttribute( parentNodeIndex, obj, pAttribute, i ); + } +} + +void CElementPropertiesTreeInternal::OnKeyDelete() +{ + RemoveSelected( false ); +} + +void CElementPropertiesTreeInternal::OnKeyBackspace() +{ + RemoveSelected( true ); +} + +void CElementPropertiesTreeInternal::RemoveSelected( bool selectLeft ) +{ + CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Delete Items" ); + + CUtlVector< KeyValues * > itemData; + m_pTree->GetTree()->GetSelectedItemData( itemData ); + bool bRefreshNeeded = OnRemoveFromData( itemData ); + + if ( itemData.Count() > 1 ) + { + // dont try to maintain the selection if multiple items are selected + m_pTree->GetTree()->ClearSelection(); + } + + if ( bRefreshNeeded ) + { + // Refresh the tree + Refresh( REFRESH_TREE_VIEW, selectLeft ); + } +} + +bool CElementPropertiesTreeInternal::IsLabelBeingEdited() const +{ + return m_pTree->GetTree()->IsLabelBeingEdited(); +} + +bool CElementPropertiesTreeInternal::HasItemsSelected() const +{ + return m_pTree->GetTree()->GetSelectedItemCount() > 0 ? true : false; +} + + +//----------------------------------------------------------------------------- +// protected accessors +//----------------------------------------------------------------------------- +KeyValues *CElementPropertiesTreeInternal::GetTreeItemData( int itemIndex ) +{ + return m_pTree->GetItemData( itemIndex ); +} + + +void CElementPropertiesTreeInternal::OnRefresh() +{ + // Does a forced refresh + Refresh( REFRESH_TREE_VIEW ); + + CElementTreeNotifyScopeGuard notify( "CElementPropertiesTreeInternal::OnRefresh", NOTIFY_SETDIRTYFLAG | NOTIFY_CHANGE_TOPOLOGICAL, m_pNotify ); +} + + +//----------------------------------------------------------------------------- +// +// CElementPropertiesTree methods +// +//----------------------------------------------------------------------------- +CElementPropertiesTree::CElementPropertiesTree( vgui::Panel *parent, IDmNotify *pNotify, CDmElement *pObject, CDmeEditorTypeDictionary *pDict ) + : BaseClass( parent, "ElementPropertiesTreeFrame" ) +{ + SetTitle( "#BxElementPropertiesTree", true ); + + SetSizeable( true ); + SetCloseButtonVisible( false ); + SetMinimumSize( 600, 200 ); + + m_pProperties = new CElementPropertiesTreeInternal( this, pNotify, pObject, false, pDict ); + + m_pOK = new Button( this, "OK", "OK", this, "close" ); + m_pApply = new Button( this, "Apply", "Apply", this, "apply" ); + m_pCancel = new Button( this, "Cancel", "Cancel", this, "cancel" ); + + SetScheme( vgui::scheme()->LoadSchemeFromFile( "Resource/BoxRocket.res", "BoxRocket" ) ); + LoadControlSettings( "resource/BxElementPropertiesTreeFrame.res" ); +} + +void CElementPropertiesTree::Init( ) +{ + m_pProperties->Init( ); +} + +void CElementPropertiesTree::ActivateBuildMode() +{ + BaseClass::ActivateBuildMode(); + m_pProperties->ActivateBuildMode(); +} + +void CElementPropertiesTree::Refresh( CElementPropertiesTreeInternal::RefreshType_t rebuild /* = REFRESH_REBUILD */, bool preservePrevSelectedItem /*= false*/ ) +{ + m_pProperties->Refresh( rebuild, preservePrevSelectedItem ); +} + +void CElementPropertiesTree::GenerateChildrenOfNode(int itemIndex) +{ + m_pProperties->GenerateChildrenOfNode( itemIndex ); +} + +void CElementPropertiesTree::SetObject( CDmElement *object ) +{ + m_pProperties->SetObject( object ); +} + +void CElementPropertiesTree::OnCommand( const char *cmd ) +{ + if ( !Q_stricmp( cmd, "close" ) ) + { + m_pProperties->ApplyChanges(); + MarkForDeletion(); + } + else if ( !Q_stricmp( cmd, "apply" ) ) + { + m_pProperties->ApplyChanges(); + m_pProperties->Refresh(); + } + else if ( !Q_stricmp( cmd, "cancel" ) ) + { + MarkForDeletion(); + } + else + { + BaseClass::OnCommand( cmd ); + } +} + + +//----------------------------------------------------------------------------- +// +// Hook this into the DmePanel editing system +// +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CDmeElementPanel, DmElement, "DmeElementDefault", "Dme Element Editor", true ); + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +#pragma warning (disable:4355) +CDmeElementPanel::CDmeElementPanel( vgui::Panel *pParent, const char *pPanelName ) : + BaseClass( pParent, this, NULL ) +{ +} +#pragma warning (default:4355) + + +//----------------------------------------------------------------------------- +// Called when the panel changes something +//----------------------------------------------------------------------------- +void CDmeElementPanel::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags ) +{ + if ( nNotifyFlags & ( NOTIFY_CHANGE_TOPOLOGICAL | NOTIFY_CHANGE_ATTRIBUTE_VALUE | NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE ) ) + { + KeyValues *pKeyValues = new KeyValues( "DmeElementChanged", "notifyFlags", nNotifyFlags ); + pKeyValues->SetString( "reason", pReason ); + pKeyValues->SetInt( "source", nNotifySource ); + PostActionSignal( pKeyValues ); + } +} + + +//----------------------------------------------------------------------------- +// Called by the DmePanel framework to hook an element to this +//----------------------------------------------------------------------------- +void CDmeElementPanel::SetDmeElement( CDmElement *pElement ) +{ + SetObject( pElement ); +} diff --git a/vgui2/dme_controls/FactoryOverloads.cpp b/vgui2/dme_controls/FactoryOverloads.cpp new file mode 100644 index 0000000..5bc785c --- /dev/null +++ b/vgui2/dme_controls/FactoryOverloads.cpp @@ -0,0 +1,58 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "KeyValues.h" +#include "dme_controls/ElementPropertiesTree.h" +#include "datamodel/dmelement.h" + +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/PanelListPanel.h" + +#include "FactoryOverloads.h" + +void CFactoryOverloads::AddOverload( + char const *attributeName, + IAttributeWidgetFactory *newFactory, + IAttributeElementChoiceList *newChoiceList ) +{ + Assert( attributeName ); + Assert( newFactory || newChoiceList ); + + if ( !newFactory ) + { + return; + } + + Entry_t e; + e.factory = newFactory; + e.choices = newChoiceList; + + m_Overloads.Insert( attributeName, e ); +} + +int CFactoryOverloads::Count() +{ + return m_Overloads.Count(); +} + +char const *CFactoryOverloads::Name( int index ) +{ + return m_Overloads.GetElementName( index ); +} + +IAttributeWidgetFactory *CFactoryOverloads::Factory( int index ) +{ + return m_Overloads[ index ].factory; +} + +IAttributeElementChoiceList *CFactoryOverloads::ChoiceList( int index ) +{ + return m_Overloads[ index ].choices; +}
\ No newline at end of file diff --git a/vgui2/dme_controls/FactoryOverloads.h b/vgui2/dme_controls/FactoryOverloads.h new file mode 100644 index 0000000..d378226 --- /dev/null +++ b/vgui2/dme_controls/FactoryOverloads.h @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef FACTORYOVERLOADS_H +#define FACTORYOVERLOADS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utldict.h" + +class IAttributeWidgetFactory; +class IAttributeElementChoiceList; + +class CFactoryOverloads : public IFactoryOverloads +{ +public: + virtual void AddOverload + ( + char const *attributeName, + IAttributeWidgetFactory *newFactory, + IAttributeElementChoiceList *newChoiceList + ); + int Count(); + char const *Name( int index ); + IAttributeWidgetFactory *Factory( int index ); + IAttributeElementChoiceList *ChoiceList( int index ); + +private: + struct Entry_t + { + Entry_t() : + factory( 0 ), + choices( 0 ) + { + } + + IAttributeWidgetFactory *factory; + IAttributeElementChoiceList *choices; + }; + + CUtlDict< Entry_t, int > m_Overloads; +}; + +#endif // FACTORYOVERLOADS_H
\ No newline at end of file diff --git a/vgui2/dme_controls/FileListManager.cpp b/vgui2/dme_controls/FileListManager.cpp new file mode 100644 index 0000000..de01163 --- /dev/null +++ b/vgui2/dme_controls/FileListManager.cpp @@ -0,0 +1,618 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/filelistmanager.h" +#include "vgui_controls/FileOpenDialog.h" +#include "vgui_controls/menu.h" +#include "vgui_controls/messagebox.h" +#include "datamodel/idatamodel.h" +#include "datamodel/dmelement.h" +#include "datamodel/dmattribute.h" +#include "datamodel/dmattributevar.h" +#include "vgui/ISurface.h" +#include <vgui/IInput.h> +#include "vgui/mousecode.h" +#include "tier1/strtools.h" +#include "tier1/KeyValues.h" +#include "tier2/tier2.h" +#include "p4lib/ip4.h" +#include "filesystem.h" +#include "dme_controls/INotifyUI.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +template < int C > +int ListPanelStringSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ); + +struct ColumnInfo_t +{ + char const *columnName; + char const *columnText; + int startingWidth; + int flags; + vgui::SortFunc *pfnSort; + vgui::Label::Alignment alignment; +}; + +enum ColumnIndex_t +{ + CI_FILENAME, + CI_PATH, + CI_LOADED, + CI_NUMELEMENTS, + CI_CHANGED, + CI_INPERFORCE, + CI_OPENFOREDIT, +}; + +const int baseflags = vgui::ListPanel::COLUMN_UNHIDABLE; +const int fixedflags = vgui::ListPanel::COLUMN_UNHIDABLE | vgui::ListPanel::COLUMN_FIXEDSIZE; + +static ColumnInfo_t g_ColInfo[] = +{ + { "filename", "#BxFileManager_Filename", 150, baseflags, ListPanelStringSortFunc< CI_FILENAME >, vgui::Label::a_west }, + { "path", "#BxFileManager_Path", 240, baseflags, ListPanelStringSortFunc< CI_PATH >, vgui::Label::a_west }, + { "loaded", "#BxFileManager_Loaded", 40, fixedflags, ListPanelStringSortFunc< CI_LOADED >, vgui::Label::a_center }, + { "numelements", "#BxFileManager_NumElements", 60, fixedflags, ListPanelStringSortFunc< CI_NUMELEMENTS >, vgui::Label::a_east }, + { "changed", "#BxFileManager_Changed", 50, fixedflags, ListPanelStringSortFunc< CI_CHANGED >, vgui::Label::a_center }, + { "in_perforce", "#BxFileManager_P4Exists", 35, fixedflags, ListPanelStringSortFunc< CI_INPERFORCE >, vgui::Label::a_center }, + { "open_for_edit", "#BxFileManager_P4Edit", 40, fixedflags, ListPanelStringSortFunc< CI_OPENFOREDIT >, vgui::Label::a_center }, +}; + +const char *GetKey( ColumnIndex_t ci ) +{ + return g_ColInfo[ ci ].columnName; +} + +template < int C > +int ListPanelStringSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + NOTE_UNUSED( pPanel ); + + const char *pKey = GetKey( ( ColumnIndex_t )C ); + const char *string1 = item1.kv->GetString( pKey ); + const char *string2 = item2.kv->GetString( pKey ); + + return Q_stricmp( string1, string2 ); +} + +void AddColumn( CFileListManager *pFileManager, ColumnIndex_t ci ) +{ + pFileManager->AddColumnHeader( ci, g_ColInfo[ ci ].columnName, g_ColInfo[ ci ].columnText, g_ColInfo[ ci ].startingWidth, g_ColInfo[ ci ].flags ); + pFileManager->SetSortFunc( ci, g_ColInfo[ ci ].pfnSort ); + pFileManager->SetColumnTextAlignment( ci, g_ColInfo[ ci ].alignment ); +} + + +CFileListManager::CFileListManager( vgui::Panel *parent ) : BaseClass( parent, "FileListManager" ) +{ + SetMultiselectEnabled( true ); + SetVisible( true ); + m_bRefreshRequired = false; + + SetSize( 800, 200 ); + SetPos( 100, 100 ); + + AddColumn( this, CI_FILENAME ); + AddColumn( this, CI_PATH ); + AddColumn( this, CI_LOADED ); + AddColumn( this, CI_NUMELEMENTS ); + AddColumn( this, CI_CHANGED ); + AddColumn( this, CI_INPERFORCE ); + AddColumn( this, CI_OPENFOREDIT ); + + SetSortColumn( 0 ); + + Refresh(); + + SetScheme( vgui::scheme()->LoadSchemeFromFile( "Resource/BoxRocket.res", "BoxRocket" ) ); +// LoadControlSettings( "resource/BxFileListManager.res" ); +} + +int CFileListManager::AddItem( DmFileId_t fileid, const char *pFilename, const char *pPath, bool bLoaded, int nElements, bool bChanged, bool bInPerforce, bool bOpenForEdit ) +{ + KeyValues *kv = new KeyValues( "", GetKey( CI_FILENAME ), pFilename, GetKey( CI_PATH ), pPath ); + kv->SetInt ( GetKey( CI_NUMELEMENTS ), nElements ); + kv->SetString( GetKey( CI_LOADED ), bLoaded ? "Y" : "N" ); + kv->SetString( GetKey( CI_CHANGED ), bChanged ? "Y" : "N" ); + kv->SetString( GetKey( CI_INPERFORCE ), bInPerforce ? "Y" : "N" ); + kv->SetString( GetKey( CI_OPENFOREDIT ), bOpenForEdit ? "Y" : "N" ); + int itemID = BaseClass::AddItem( kv, fileid, false, false ); + kv->deleteThis(); + return itemID; +} + +void CFileListManager::SetLoaded( DmFileId_t fileid, bool bLoaded ) +{ + CNotifyScopeGuard notify( "CFileListManager::SetLoaded", NOTIFY_SOURCE_FILE_LIST_MANAGER, NOTIFY_SETDIRTYFLAG ); + + if ( bLoaded ) + { + const char *pFilename = g_pDataModel->GetFileName( fileid ); + Assert( pFilename ); + if ( !pFilename ) + return; + + CDisableUndoScopeGuard guard; + CDmElement *pRoot = NULL; + g_pDataModel->RestoreFromFile( pFilename, NULL, NULL, &pRoot, CR_DELETE_NEW ); + } + else + { + CDisableUndoScopeGuard guard; + g_pDataModel->UnloadFile( fileid ); + } +} + +void CFileListManager::OnMousePressed( vgui::MouseCode code ) +{ + // determine where we were pressed + int x, y, row, column; + vgui::input()->GetCursorPos( x, y ); + GetCellAtPos( x, y, row, column ); + + if ( code == MOUSE_LEFT ) + { + bool bIsFakeToggleButton = column == CI_LOADED; + if ( bIsFakeToggleButton && row >= 0 && row < GetItemCount() ) + { + int itemID = GetItemIDFromRow( row ); + KeyValues *kv = GetItem( itemID ); + + const char *pStr = kv->GetString( GetKey( ( ColumnIndex_t )column ), "" ); + Assert( *pStr == 'Y' || *pStr == 'N' ); + bool bSet = *pStr == 'N'; // bSet is the NEW state, not the old one + kv->SetString( GetKey( ( ColumnIndex_t )column ), bSet ? "Y" : "N" ); + + SetLoaded( ( DmFileId_t )GetItemUserData( itemID ), bSet ); + + // get the key focus + RequestFocus(); + return; + } + } + else if ( code == MOUSE_RIGHT ) + { + int itemID = -1; + if ( row >= 0 && row < GetItemCount() ) + { + itemID = GetItemIDFromRow( row ); + + if ( !IsItemSelected( itemID ) ) + { + SetSingleSelectedItem( itemID ); + } + } + + KeyValues *kv = new KeyValues( "OpenContextMenu", "itemID", itemID ); + OnOpenContextMenu( kv ); + kv->deleteThis(); + return; + } + + BaseClass::OnMousePressed( code ); +} + +int AddMenuItemHelper( vgui::Menu *pMenu, const char *pItemName, const char *pKVName, vgui::Panel *pTarget, bool bEnabled ) +{ + int id = pMenu->AddMenuItem( pItemName, new KeyValues( pKVName ), pTarget ); + pMenu->SetItemEnabled( id, bEnabled ); + return id; +} + +void CFileListManager::OnOpenContextMenu( KeyValues *pParams ) +{ + if ( m_hContextMenu.Get() ) + { + delete m_hContextMenu.Get(); + m_hContextMenu = NULL; + } + + m_hContextMenu = new vgui::Menu( this, "ContextMenu" ); + + int itemID = pParams->GetInt( "itemID", -1 ); + if ( itemID < 0 ) + { + AddMenuItemHelper( m_hContextMenu, "Open File...", "open", this, true ); // Is this how we should load other files??? + } + else + { + bool bP4Connected = p4->IsConnectedToServer(); + + int nSelected = GetSelectedItemsCount(); + int nLoaded = 0; + int nChanged = 0; + int nOnDisk = 0; + int nInPerforce = 0; + int nOpenForEdit = 0; + for ( int i = 0; i < nSelected; ++i ) + { + int itemId = GetSelectedItem( i ); + DmFileId_t fileid = ( DmFileId_t )GetItemUserData( itemId ); + if ( g_pDataModel->IsFileLoaded( fileid ) ) + { + ++nLoaded; + ++nChanged; // TODO - find out for real + } + const char *pFilename = g_pDataModel->GetFileName( fileid ); + if ( g_pFullFileSystem->FileExists( pFilename ) ) + { + ++nOnDisk; + } + + if ( bP4Connected ) + { + if ( p4->IsFileInPerforce( pFilename ) ) + { + ++nInPerforce; + if ( p4->GetFileState( pFilename ) != P4FILE_UNOPENED ) + { + ++nOpenForEdit; + } + } + } + } + + AddMenuItemHelper( m_hContextMenu, "Load", "load", this, nLoaded < nSelected && nOnDisk > 0 ); + AddMenuItemHelper( m_hContextMenu, "Unload", "unload", this, nLoaded > 0 ); + AddMenuItemHelper( m_hContextMenu, "Save", "save", this, nChanged > 0 && nOnDisk == nSelected ); + AddMenuItemHelper( m_hContextMenu, "Save As...", "saveas", this, nLoaded == 1 && nSelected == 1 ); + AddMenuItemHelper( m_hContextMenu, "Add To Perforce", "p4add", this, nInPerforce < nSelected && nOnDisk > 0 ); + AddMenuItemHelper( m_hContextMenu, "Open For Edit", "p4edit", this, nOpenForEdit < nSelected && nOnDisk > 0 ); + } + + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + +void CFileListManager::OnLoadFiles( KeyValues *pParams ) +{ + CNotifyScopeGuard notify( "CFileListManager::OnLoadFiles", NOTIFY_SOURCE_FILE_LIST_MANAGER, NOTIFY_SETDIRTYFLAG ); + + int nSelected = GetSelectedItemsCount(); + for ( int i = 0; i < nSelected; ++i ) + { + int itemId = GetSelectedItem( i ); + DmFileId_t fileid = ( DmFileId_t )GetItemUserData( itemId ); + if ( !g_pDataModel->IsFileLoaded( fileid ) ) + { + SetLoaded( fileid, true ); + } + } + + Refresh(); +} + +void CFileListManager::OnUnloadFiles( KeyValues *pParams ) +{ + CNotifyScopeGuard notify( "CFileListManager::OnUnloadFiles", NOTIFY_SOURCE_FILE_LIST_MANAGER, NOTIFY_SETDIRTYFLAG ); + + int nSelected = GetSelectedItemsCount(); + for ( int i = 0; i < nSelected; ++i ) + { + int itemId = GetSelectedItem( i ); + DmFileId_t fileid = ( DmFileId_t )GetItemUserData( itemId ); + if ( g_pDataModel->IsFileLoaded( fileid ) ) + { + SetLoaded( fileid, false ); + } + } + + Refresh(); +} + +void CFileListManager::OnSaveFiles( KeyValues *pParams ) +{ + int nSelected = GetSelectedItemsCount(); + for ( int i = 0; i < nSelected; ++i ) + { + int itemId = GetSelectedItem( i ); + DmFileId_t fileid = ( DmFileId_t )GetItemUserData( itemId ); + if ( !g_pDataModel->IsFileLoaded( fileid ) ) + continue; + + const char *pFilename = g_pDataModel->GetFileName( fileid ); + Assert( pFilename ); + if ( !pFilename ) + continue; + + CDmElement *pRoot = GetElement< CDmElement >( g_pDataModel->GetFileRoot( fileid ) ); + Assert( pRoot ); + if ( !pRoot ) + continue; + + const char *pFileFormat = g_pDataModel->GetFileFormat( fileid ); + const char *pEncoding = g_pDataModel->GetDefaultEncoding( pFileFormat ); + g_pDataModel->SaveToFile( pFilename, NULL, pEncoding, pFileFormat, pRoot ); + } + + Refresh(); +} + +void CFileListManager::OnOpenFile( KeyValues *pParams ) +{ + KeyValues *pContextKeyValues = new KeyValues( "OnOpen" ); + vgui::FileOpenDialog *pFileOpenDialog = new vgui::FileOpenDialog( this, "Save .dmx File As", false, pContextKeyValues ); + pFileOpenDialog->AddFilter( "*.dmx", "DmElements File (*.dmx)", true ); + pFileOpenDialog->AddActionSignalTarget( this ); + pFileOpenDialog->SetDeleteSelfOnClose( true ); + pFileOpenDialog->DoModal( false ); +} + +void CFileListManager::OnSaveFileAs( KeyValues *pParams ) +{ + int nSelected = GetSelectedItemsCount(); + Assert( nSelected == 1 ); + if ( nSelected != 1 ) + return; + + KeyValues *pContextKeyValues = new KeyValues( "OnSaveAs" ); + pContextKeyValues->SetInt( "itemId", GetSelectedItem( 0 ) ); + DmFileId_t fileid = ( DmFileId_t )GetItemUserData( GetSelectedItem( 0 ) ); + const char *pFileFormat = g_pDataModel->GetFileFormat( fileid ); + + vgui::FileOpenDialog *pFileOpenDialog = new vgui::FileOpenDialog( this, "Save .dmx File As", false, pContextKeyValues ); + // if this control is moved to vgui_controls, change the default format to "dmx", the generic dmx format + pFileOpenDialog->AddFilter( "*.dmx", "Generic MovieObjects File (*.dmx)", false, "movieobjects" ); + if ( V_strcmp( pFileFormat, "movieobjects" ) != 0 ) + { + char description[ 256 ]; + V_snprintf( description, sizeof( description ), "%s (*.dmx)", g_pDataModel->GetFormatDescription( pFileFormat ) ); + pFileOpenDialog->AddFilter( "*.dmx", description, true, pFileFormat ); + } + pFileOpenDialog->AddActionSignalTarget( this ); + pFileOpenDialog->SetDeleteSelfOnClose( true ); + pFileOpenDialog->DoModal( false ); +} + +void CFileListManager::OnFileSelected( KeyValues *pParams ) +{ + const char *pFullPath = pParams->GetString( "fullpath" ); + if ( !pFullPath || !pFullPath[ 0 ] ) + return; + + KeyValues *pSaveAsKey = pParams->FindKey( "OnSaveAs" ); + if ( pSaveAsKey ) + { + int itemId = pSaveAsKey->GetInt( "itemId", -1 ); + Assert( itemId != -1 ); + if ( itemId == -1 ) + return; + + DmFileId_t fileid = ( DmFileId_t )GetItemUserData( itemId ); + Assert( fileid != DMFILEID_INVALID ); + if ( fileid == DMFILEID_INVALID ) + return; + + CDmElement *pRoot = GetElement< CDmElement >( g_pDataModel->GetFileRoot( fileid ) ); + Assert( pRoot ); + if ( !pRoot ) + return; + + const char *pFormat = pParams->GetString( "filterinfo" ); + Assert( pFormat ); + if ( !pFormat ) + return; + + g_pDataModel->SetFileName( fileid, pFullPath ); + g_pDataModel->SaveToFile( pFullPath, NULL, g_pDataModel->GetDefaultEncoding( pFormat ), pFormat, pRoot ); + + Refresh(); + return; + } + + KeyValues *pOpenKey = pParams->FindKey( "OnOpen" ); + if ( pOpenKey ) + { + CDmElement *pRoot = NULL; + g_pDataModel->RestoreFromFile( pFullPath, NULL, NULL, &pRoot ); + + Refresh(); + return; + } +} + +void CFileListManager::OnAddToPerforce( KeyValues *pParams ) +{ + int nFileCount = 0; + int nSelected = GetSelectedItemsCount(); + const char **ppFileNames = ( const char** )_alloca( nSelected * sizeof( char* ) ); + for ( int i = 0; i < nSelected; ++i ) + { + int itemId = GetSelectedItem( i ); + DmFileId_t fileid = ( DmFileId_t )GetItemUserData( itemId ); + const char *pFilename = g_pDataModel->GetFileName( fileid ); + Assert( pFilename ); + if ( !pFilename ) + continue; + + ++nFileCount; + ppFileNames[ i ] = pFilename; + } + + bool bSuccess = p4->OpenFilesForAdd( nFileCount, ppFileNames ); + if ( !bSuccess ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "Perforce Error!", p4->GetLastError(), GetParent() ); + pError->SetSmallCaption( true ); + pError->DoModal(); + } + + Refresh(); +} + +void CFileListManager::OnOpenForEdit( KeyValues *pParams ) +{ + int nFileCount = 0; + int nSelected = GetSelectedItemsCount(); + const char **ppFileNames = ( const char** )_alloca( nSelected * sizeof( char* ) ); + for ( int i = 0; i < nSelected; ++i ) + { + int itemId = GetSelectedItem( i ); + DmFileId_t fileid = ( DmFileId_t )GetItemUserData( itemId ); + const char *pFilename = g_pDataModel->GetFileName( fileid ); + Assert( pFilename ); + if ( !pFilename ) + continue; + + ++nFileCount; + ppFileNames[ i ] = pFilename; + } + + bool bSuccess = p4->OpenFilesForEdit( nFileCount, ppFileNames ); + if ( !bSuccess ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "Perforce Error!", p4->GetLastError(), GetParent() ); + pError->SetSmallCaption( true ); + pError->DoModal(); + } + + Refresh(); +} + +void CFileListManager::OnDataChanged( KeyValues *pParams ) +{ + int nNotifyFlags = pParams->GetInt( "notifyFlags" ); + if ( ( nNotifyFlags & NOTIFY_CHANGE_TOPOLOGICAL ) == 0 ) + return; + + int nNotifySource = pParams->GetInt( "source" ); + if ( nNotifySource == NOTIFY_SOURCE_FILE_LIST_MANAGER ) + return; + + if ( !IsVisible() ) + { + m_bRefreshRequired = true; + return; + } + + int nCount = GetItemCount(); + int nFiles = g_pDataModel->NumFileIds(); + bool bPerformFullRefresh = ( nCount != nFiles ); + if ( !bPerformFullRefresh ) + { + const char *pNameKey = GetKey( CI_FILENAME ); + + for ( int i = 0; i < nCount; ++i ) + { + DmFileId_t fileid = g_pDataModel->GetFileId( i ); + const char *pFileName = g_pDataModel->GetFileName( fileid ); + if ( !pFileName || !*pFileName ) + { + bPerformFullRefresh = true; + break; + } + pFileName = V_UnqualifiedFileName( pFileName ); + + KeyValues *pKeyValues = GetItem( i ); + bPerformFullRefresh = ( fileid != (DmFileId_t)GetItemUserData(i) ) || Q_stricmp( pFileName, pKeyValues->GetString( pNameKey ) ); + if ( bPerformFullRefresh ) + break; + + pKeyValues->SetInt ( GetKey( CI_NUMELEMENTS ), g_pDataModel->NumElementsInFile( fileid ) ); + pKeyValues->SetString( GetKey( CI_LOADED ), g_pDataModel->IsFileLoaded( fileid ) ? "Y" : "N" ); + pKeyValues->SetString( GetKey( CI_CHANGED ), false ? "Y" : "N" ); + ApplyItemChanges( i ); + } + } + + if ( bPerformFullRefresh ) + { + Refresh(); + return; + } +} + +void CFileListManager::Refresh() +{ + m_bRefreshRequired = false; + RemoveAll(); + + const bool bP4Connected = p4 ? p4->IsConnectedToServer() : false; + + int nFiles = g_pDataModel->NumFileIds(); + for ( int i = 0; i < nFiles; ++i ) + { + DmFileId_t fileid = g_pDataModel->GetFileId( i ); + const char *pFileName = g_pDataModel->GetFileName( fileid ); + if ( !pFileName || !*pFileName ) + continue; // skip DMFILEID_INVALID and the default fileid "" + + bool bLoaded = g_pDataModel->IsFileLoaded( fileid ); + int nElements = g_pDataModel->NumElementsInFile( fileid ); + bool bChanged = false; // TODO - find out for real + bool bInPerforce = bP4Connected && p4->IsFileInPerforce( pFileName ); + bool bOpenForEdit = bInPerforce && p4->GetFileState( pFileName ) != P4FILE_UNOPENED; + + char path[ 256 ]; + V_ExtractFilePath( pFileName, path, sizeof( path ) ); + + AddItem( fileid, V_UnqualifiedFileName( pFileName ), path, bLoaded, nElements, bChanged, bInPerforce, bOpenForEdit ); + } +} + +void CFileListManager::OnThink( ) +{ + BaseClass::OnThink(); + if ( m_bRefreshRequired && IsVisible() ) + { + Refresh(); + } +} + +void CFileListManager::OnCommand( const char *cmd ) +{ + // if ( !Q_stricmp( cmd, "foo" ) ) ... + BaseClass::OnCommand( cmd ); +} + + +//----------------------------------------------------------------------------- +// +// CFileManagerFrame methods +// +//----------------------------------------------------------------------------- +CFileManagerFrame::CFileManagerFrame( vgui::Panel *parent ) : BaseClass( parent, "FileManagerFrame" ) +{ + SetTitle( "#BxFileManagerFrame", true ); + + SetSizeable( true ); + SetCloseButtonVisible( false ); + SetMinimumSize( 200, 200 ); + + SetVisible( true ); + + SetSize( 800, 200 ); + SetPos( 100, 100 ); + + m_pFileListManager = new CFileListManager( this ); + Refresh(); + + SetScheme( vgui::scheme()->LoadSchemeFromFile( "Resource/BoxRocket.res", "BoxRocket" ) ); +} + +void CFileManagerFrame::Refresh() +{ + m_pFileListManager->Refresh(); +} + +void CFileManagerFrame::OnCommand( const char *cmd ) +{ + BaseClass::OnCommand( cmd ); + m_pFileListManager->OnCommand( cmd ); +} + +void CFileManagerFrame::PerformLayout() +{ + BaseClass::PerformLayout(); + + int iWidth, iHeight; + GetSize( iWidth, iHeight ); + m_pFileListManager->SetPos( 0, GetCaptionHeight() ); + m_pFileListManager->SetSize( iWidth, iHeight - GetCaptionHeight() ); +} diff --git a/vgui2/dme_controls/attributeassetpickerpanel.cpp b/vgui2/dme_controls/attributeassetpickerpanel.cpp new file mode 100644 index 0000000..b7f27c8 --- /dev/null +++ b/vgui2/dme_controls/attributeassetpickerpanel.cpp @@ -0,0 +1,69 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeAssetPickerPanel.h" +#include "dme_controls/AttributeTextEntry.h" +#include "matsys_controls/AssetPicker.h" +#include "matsys_controls/VtfPicker.h" +#include "matsys_controls/VMTPicker.h" +#include "tier1/KeyValues.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Assets +//----------------------------------------------------------------------------- +IMPLEMENT_ATTRIBUTE_ASSET_PICKER( CAttributeBspPickerPanel, "Select .BSP file", "BSP Files", "bsp", "maps", "bspName" ); +IMPLEMENT_ATTRIBUTE_ASSET_PREVIEW_PICKER( CAttributeVmtPickerPanel, CVMTPickerFrame, "Select .VMT file" ); +IMPLEMENT_ATTRIBUTE_ASSET_PREVIEW_PICKER( CAttributeVtfPickerPanel, CVTFPickerFrame, "Select .VTF file" ); + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeAssetPickerPanel::CAttributeAssetPickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + +CAttributeAssetPickerPanel::~CAttributeAssetPickerPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the BSP picker +//----------------------------------------------------------------------------- +void CAttributeAssetPickerPanel::ShowPickerDialog() +{ + CBaseAssetPickerFrame *pAssetPickerDialog = CreateAssetPickerFrame( ); + pAssetPickerDialog->AddActionSignalTarget( this ); + pAssetPickerDialog->DoModal( ); +} + + +//----------------------------------------------------------------------------- +// Called by the asset picker dialog if a asset was selected +//----------------------------------------------------------------------------- +void CAttributeAssetPickerPanel::OnAssetSelected( KeyValues *pKeyValues ) +{ + // Get the asset name back + const char *pAssetName = pKeyValues->GetString( "asset", NULL ); + if ( !pAssetName || !pAssetName[ 0 ] ) + return; + + // Apply to text panel + m_pData->SetText( pAssetName ); + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +} diff --git a/vgui2/dme_controls/attributedetailtypepickerpanel.cpp b/vgui2/dme_controls/attributedetailtypepickerpanel.cpp new file mode 100644 index 0000000..38801fb --- /dev/null +++ b/vgui2/dme_controls/attributedetailtypepickerpanel.cpp @@ -0,0 +1,88 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeDetailTypePickerPanel.h" +#include "dme_controls/AttributeTextEntry.h" +#include "tier1/KeyValues.h" +#include "filesystem.h" + + +using namespace vgui; + + +const char *DETAILTYPE_FILE = "detail.vbsp"; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeDetailTypePickerPanel::CAttributeDetailTypePickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + +CAttributeDetailTypePickerPanel::~CAttributeDetailTypePickerPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Reads the detail types +//----------------------------------------------------------------------------- +void CAttributeDetailTypePickerPanel::AddDetailTypesToList( PickerList_t &list ) +{ + KeyValues *pDetailTypes = new KeyValues( DETAILTYPE_FILE ); + if ( pDetailTypes->LoadFromFile( g_pFullFileSystem, DETAILTYPE_FILE, "GAME" ) ) + { + for ( KeyValues *sub = pDetailTypes->GetFirstTrueSubKey(); sub != NULL; sub = sub->GetNextTrueSubKey() ) + { + int i = list.AddToTail( ); + list[i].m_pChoiceString = sub->GetName(); + list[i].m_pChoiceValue = sub->GetName(); + } + } + else + { + Warning( "Unable to load detail prop file '%s'\n", DETAILTYPE_FILE ); + } + + pDetailTypes->deleteThis(); +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the picker +//----------------------------------------------------------------------------- +void CAttributeDetailTypePickerPanel::ShowPickerDialog() +{ + CPickerFrame *pDetailTypePickerDialog = new CPickerFrame( this, "Select Detail Type", "Detail Type", "detailTypeName" ); + PickerList_t detailTypeList; + AddDetailTypesToList( detailTypeList ); + pDetailTypePickerDialog->AddActionSignalTarget( this ); + pDetailTypePickerDialog->DoModal( detailTypeList ); +} + + +//----------------------------------------------------------------------------- +// Called by the picker dialog if a asset was selected +//----------------------------------------------------------------------------- +void CAttributeDetailTypePickerPanel::OnPicked( KeyValues *pKeyValues ) +{ + // Get the detail type name back + const char *pDetailTypeName = pKeyValues->GetString( "choice", NULL ); + if ( !pDetailTypeName || !pDetailTypeName[ 0 ] ) + return; + + // Apply to text panel + m_pData->SetText( pDetailTypeName ); + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +} diff --git a/vgui2/dme_controls/attributeshaderpickerpanel.cpp b/vgui2/dme_controls/attributeshaderpickerpanel.cpp new file mode 100644 index 0000000..56730a1 --- /dev/null +++ b/vgui2/dme_controls/attributeshaderpickerpanel.cpp @@ -0,0 +1,77 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeShaderPickerPanel.h" +#include "dme_controls/AttributeTextEntry.h" +#include "matsys_controls/Picker.h" +#include "tier1/KeyValues.h" +#include "matsys_controls/matsyscontrols.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/IShader.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeShaderPickerPanel::CAttributeShaderPickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + +CAttributeShaderPickerPanel::~CAttributeShaderPickerPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the picker +//----------------------------------------------------------------------------- +void CAttributeShaderPickerPanel::ShowPickerDialog() +{ + CPickerFrame *pShaderPickerDialog = new CPickerFrame( this, "Select Shader", "Shader", "shaderName" ); + + int nCount = vgui::MaterialSystem()->ShaderCount(); + IShader** ppShaderList = (IShader**)_alloca( nCount * sizeof(IShader) ); + vgui::MaterialSystem()->GetShaders( 0, nCount, ppShaderList ); + PickerList_t shaderList( 0, nCount ); + for ( int i = 0; i < nCount; ++i ) + { + if ( ( ppShaderList[i]->GetFlags() & SHADER_NOT_EDITABLE ) == 0 ) + { + int j = shaderList.AddToTail( ); + shaderList[j].m_pChoiceString = ppShaderList[i]->GetName(); + shaderList[j].m_pChoiceValue = ppShaderList[i]->GetName(); + } + } + + pShaderPickerDialog->AddActionSignalTarget( this ); + pShaderPickerDialog->DoModal( shaderList ); +} + + +//----------------------------------------------------------------------------- +// Called by the picker dialog if a asset was selected +//----------------------------------------------------------------------------- +void CAttributeShaderPickerPanel::OnPicked( KeyValues *pKeyValues ) +{ + // Get the asset name back + const char *pShaderName = pKeyValues->GetString( "choice", NULL ); + if ( !pShaderName || !pShaderName[ 0 ] ) + return; + + // Apply to text panel + m_pData->SetText( pShaderName ); + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +} diff --git a/vgui2/dme_controls/attributeslider.cpp b/vgui2/dme_controls/attributeslider.cpp new file mode 100644 index 0000000..4597e4a --- /dev/null +++ b/vgui2/dme_controls/attributeslider.cpp @@ -0,0 +1,1350 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/attributeslider.h" +#include "materialsystem/imesh.h" +#include "movieobjects/dmeanimationset.h" +#include "vgui/IInput.h" +#include "vgui/ISurface.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/TextImage.h" +#include "vgui_controls/subrectimage.h" +#include "vgui_controls/CheckButton.h" +#include "dme_controls/BaseAnimSetAttributeSliderPanel.h" +#include "dme_controls/BaseAnimationSetEditor.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Enums +//----------------------------------------------------------------------------- +#define SLIDER_PIXEL_SPACING 3 +#define CIRCULAR_CONTROL_RADIUS 6.0f +#define UNDO_CHAIN_MOUSEWHEEL_ATTRIBUTE_SLIDER 9876 +#define FRAC_PER_PIXEL 0.0025f +#define ANIM_SET_ATTRIBUTE_SLIDER_BALANCE_INSET 30 +#define ANIM_SET_ATTRIBUTE_SLIDER_LEFT_BORDER 5 +#define ANIM_SET_ATTRIBUTE_SLIDER_GRAPH_BUTTON_WIDTH 16 +#define ANIM_SET_ATTRIBUTE_SLIDER_MULTILEVEL_INSET 30 + + +static ConVar ifm_attributeslider_sensitivity( "ifm_attributeslider_sensitivity", "3.0", 0 ); + + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +static Color s_TextColor( 200, 200, 200, 192 ); +static Color s_TextColorFocus( 208, 143, 40, 192 ); + +// NOTE: Index with [preview][selected] +static Color s_BarColor[2][2] = +{ + { Color( 45, 45, 45, 255 ), Color( 150, 80, 0, 255 ) }, + { Color( 30, 255, 255, 80 ), Color( 30, 180, 255, 255 ) } +}; + +static Color s_ZeroColor[2][2] = +{ + { Color( 33, 33, 33, 255 ), Color( 0, 255, 255, 60 ) }, + { Color( 100, 80, 0, 255 ), Color( 0, 180, 255, 255 ) } +}; + +static Color s_DraggingBarColor( 142, 142, 142, 255 ); +static Color s_PreviewTickColor( 255, 164, 8, 255 ); +static Color s_OldValueTickColor( 100, 100, 100, 63 ); + +static Color s_MidpointColor( 115, 115, 115, 255 ); + +//----------------------------------------------------------------------------- +// Blends flex values in left-right space instead of balance/value space +//----------------------------------------------------------------------------- +static void BlendFlexValues( AttributeValue_t *pResult, const AttributeValue_t &src, const AttributeValue_t &dest, float flBlend, float flBalanceFilter = 0.5f ) +{ + // Apply the left-right balance to the target + float flLeftFilter, flRightFilter; + ValueBalanceToLeftRight( &flLeftFilter, &flRightFilter, flBlend, flBalanceFilter ); + + // Do the math in 'left-right' space because we filter in that space + float flSrcLeft, flSrcRight; + ValueBalanceToLeftRight( &flSrcLeft, &flSrcRight, src.m_pValue[ANIM_CONTROL_VALUE], src.m_pValue[ANIM_CONTROL_BALANCE] ); + + float flDestLeft, flDestRight; + ValueBalanceToLeftRight( &flDestLeft, &flDestRight, dest.m_pValue[ANIM_CONTROL_VALUE], dest.m_pValue[ANIM_CONTROL_BALANCE] ); + + float flTargetLeft = flSrcLeft + flLeftFilter * ( flDestLeft - flSrcLeft ); + float flTargetRight = flSrcRight + flRightFilter * ( flDestRight - flSrcRight ); + + LeftRightToValueBalance( &pResult->m_pValue[ANIM_CONTROL_VALUE], &pResult->m_pValue[ANIM_CONTROL_BALANCE], flTargetLeft, flTargetRight, + ( flBlend <= 0.5f ) ? src.m_pValue[ANIM_CONTROL_BALANCE] : dest.m_pValue[ANIM_CONTROL_BALANCE] ); + + pResult->m_pValue[ANIM_CONTROL_MULTILEVEL] = src.m_pValue[ANIM_CONTROL_MULTILEVEL] + ( dest.m_pValue[ANIM_CONTROL_MULTILEVEL] - src.m_pValue[ANIM_CONTROL_MULTILEVEL] ) * flBlend; +} + +//----------------------------------------------------------------------------- +// The panel used to do text entry when double-clicking in the slider +//----------------------------------------------------------------------------- +class CAttributeSliderTextEntry : public TextEntry +{ + DECLARE_CLASS_SIMPLE( CAttributeSliderTextEntry, TextEntry ); + +public: + CAttributeSliderTextEntry( CAttributeSlider *slider, const char *panelName ) : + BaseClass( (Panel *)slider, panelName ), m_pSlider( slider ) + { + Assert( m_pSlider ); + } + + MESSAGE_FUNC_PARAMS( OnKillFocus, "KillFocus", kv ); + virtual void OnMouseWheeled( int delta ); + +private: + CAttributeSlider *m_pSlider; +}; + + + +//----------------------------------------------------------------------------- +// +// CAttributeSlider begins here +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CAttributeSlider::CAttributeSlider( CBaseAnimSetAttributeSliderPanel *parent, const char *panelName, CDmElement *pControl ) : + BaseClass( (Panel *)parent, panelName ), + m_pParent( parent ), + m_pWhite( NULL ), + m_bPreviewEnabled( false ), + m_bSimplePreviewOnly( true ), + m_bCursorInsidePanel( false ), + m_flPreviewGoalTime( -1.0f ), + m_bRampUp( false ), + m_bFaderBeingDragged( false ), + m_flFaderAmount( 1.0f ), + m_bIsLogPreviewControl( false ), + m_bSelected( false ), + m_pRightTextField( 0 ) +{ + m_SliderMode = SLIDER_MODE_NONE; + m_hControl = pControl; + + // Cache off control information since this state should never change + // NOTE: If it ever does, just change the implementations of + // IsTransform + GetMidpoint to always read these values from the attributes + m_bTransform = pControl->GetValue< bool >( "transform" ); + + m_nDragStartPosition[ 0 ] = m_nDragStartPosition[ 1 ] = 0; + m_nAccum[ 0 ] = m_nAccum[ 1 ] = 0; + m_flDragStartValue = 1.0f; + m_flDragStartBalance = 0.5f; + + SetPaintBackgroundEnabled( true ); + + m_pName = new TextImage( panelName ); + m_pValues[ 0 ] = new TextImage( "" ); + m_pValues[ 1 ] = new TextImage( "" ); + m_pValues[ 2 ] = new TextImage( "" ); + + m_pCircleImage = new CSubRectImage( "tools/ifm/icon_balance", false, 7, 8, 19, 15 ); + + // Allocate a white material + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetInt( "$vertexcolor", 1 ); + pVMTKeyValues->SetInt( "$vertexalpha", 1 ); + pVMTKeyValues->SetInt( "$ignorez", 1 ); + pVMTKeyValues->SetInt( "$no_fullbright", 1 ); + pVMTKeyValues->SetInt( "$nocull", 1 ); + m_pWhite.Init( "AttributeSlider_White", NULL, pVMTKeyValues ); + + SetBgColor( Color( 42, 42, 42, 255 ) ); + + m_bIsControlActive[ANIM_CONTROL_VALUE] = true; + m_bIsControlActive[ANIM_CONTROL_BALANCE] = false; + m_bIsControlActive[ANIM_CONTROL_MULTILEVEL] = false; + + m_pTextField = new CAttributeSliderTextEntry( this, panelName ); + m_pTextField->SetVisible( false ); + m_pTextField->SetEnabled( false ); + m_pTextField->SelectAllOnFocusAlways( true ); + + SetPaintBorderEnabled( false ); +} + +CAttributeSlider::~CAttributeSlider() +{ + m_pWhite.Shutdown(); + delete m_pCircleImage; + delete m_pName; + delete m_pValues[ 0 ]; + delete m_pValues[ 1 ]; + delete m_pValues[ 2 ]; +} + + +//----------------------------------------------------------------------------- +// Scheme +//----------------------------------------------------------------------------- +void CAttributeSlider::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + + m_pName->SetFont( scheme->GetFont( "Default" ) ); + m_pName->SetColor( s_TextColor ); + m_pName->ResizeImageToContent(); + + m_pValues[ 0 ]->SetColor( s_TextColor ); + m_pValues[ 0 ]->SetFont( scheme->GetFont( "Default" ) ); + m_pValues[ 1 ]->SetColor( s_TextColorFocus ); + m_pValues[ 1 ]->SetFont( scheme->GetFont( "Default" ) ); + m_pValues[ 2 ]->SetColor( s_TextColor ); + m_pValues[ 2 ]->SetFont( scheme->GetFont( "Default" ) ); + + m_pCircleImage->SetColor( Color( 255, 255, 255, 255 ) ); + + SetBgColor( Color( 42, 42, 42, 255 ) ); + SetFgColor( Color( 194, 120, 0, 255 ) ); +} + + +//----------------------------------------------------------------------------- +// Gets/sets the slider value. +// NOTE: This may not match the value pushed into the control because of fading +//----------------------------------------------------------------------------- +static const char *s_pChangeMessage[ANIM_CONTROL_COUNT] = +{ + "SliderMoved", + "BalanceChanged", + "MultiLevelChanged", +}; + +static const char *s_pChangeKeyValue[ANIM_CONTROL_COUNT] = +{ + "position", + "balance", + "level", +}; + +void CAttributeSlider::ActivateControl( AnimationControlType_t type, bool bActive ) +{ + if ( m_bIsControlActive[type] != bActive ) + { + m_bIsControlActive[type] = bActive; + if ( bActive ) + { + PostActionSignal( new KeyValues( s_pChangeMessage[type], s_pChangeKeyValue[type], m_Control.m_pValue[type] ) ); + } + } +} + +bool CAttributeSlider::IsControlActive( AnimationControlType_t type ) +{ + return m_bIsControlActive[type]; +} + +void CAttributeSlider::SetValue( AnimationControlType_t type, float flValue ) +{ + if ( m_Control.m_pValue[type] != flValue ) + { + m_Control.m_pValue[type] = flValue; + if ( m_bIsControlActive[type] ) + { + PostActionSignal( new KeyValues( s_pChangeMessage[type], s_pChangeKeyValue[type], flValue ) ); + } + } +} + +void CAttributeSlider::SetValue( const AttributeValue_t& value ) +{ + for ( int i = 0; i < ANIM_CONTROL_COUNT; ++i ) + { + SetValue( (AnimationControlType_t)i, value.m_pValue[i] ); + } +} + +float CAttributeSlider::GetValue( AnimationControlType_t type ) const +{ + return m_Control.m_pValue[type]; +} + +const AttributeValue_t& CAttributeSlider::GetValue() const +{ + return m_Control; +} + + +//----------------------------------------------------------------------------- +// Returns the default value for the control +//----------------------------------------------------------------------------- +float CAttributeSlider::GetControlDefaultValue( AnimationControlType_t type ) const +{ + if ( IsTransform() ) + return 0.0f; + + Assert( m_hControl.Get() ); + if ( !m_hControl.Get() ) + return 0.0f; + + switch ( type ) + { + case ANIM_CONTROL_VALUE: + return m_hControl->GetValue<float>( "defaultValue" ); + case ANIM_CONTROL_BALANCE: + return m_hControl->GetValue<float>( "defaultBalance" ); + case ANIM_CONTROL_MULTILEVEL: + return m_hControl->GetValue<float>( "defaultMultilevel" ); + } + return 0.0f; +} + + +//----------------------------------------------------------------------------- +// Given a mouse position in (x,y) in local coordinates, which animation control is it over? +//----------------------------------------------------------------------------- +AnimationControlType_t CAttributeSlider::DetermineControl( int x, int y ) +{ + if ( IsControlActive( ANIM_CONTROL_MULTILEVEL ) ) + { + Rect_t rect; + GetControlRect( &rect, ANIM_CONTROL_MULTILEVEL ); + if ( x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height ) + return ANIM_CONTROL_MULTILEVEL; + } + + return ANIM_CONTROL_VALUE; +} + + +void CAttributeSlider::SetSelected( bool state ) +{ + m_bSelected = state; +} + +bool CAttributeSlider::IsSelected() const +{ + return m_bSelected; +} + +void CAttributeSlider::SetIsLogPreviewControl( bool state ) +{ + m_bIsLogPreviewControl = state; +} + +void CAttributeSlider::OnCursorEntered() +{ + BaseClass::OnCursorEntered(); + m_bCursorInsidePanel = true; +} + +void CAttributeSlider::OnCursorExited() +{ + BaseClass::OnCursorExited(); + m_bCursorInsidePanel = false; +} + + +//----------------------------------------------------------------------------- +// Mouse event handlers +//----------------------------------------------------------------------------- +void CAttributeSlider::OnMousePressed( MouseCode code ) +{ + if ( !IsEnabled() || IsInTextEntry() || IsDragging() ) + return; + + // Deal with transform sliders + if ( m_bTransform ) + { + bool bCtrlDown = ( input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ) ); + m_pParent->SetLogPreviewControl( m_hControl ); + if ( !bCtrlDown ) + { + m_pParent->ClearSelectedControls(); + } + m_pParent->SetControlSelected( this, !IsSelected() ); + return; + } + + // Determine which control we clicked on + int x,y; + input()->GetCursorPosition( x, y ); + ScreenToLocal( x, y ); + AnimationControlType_t type = DetermineControl( x, y ); + + // Right click sets the value to match the default value + if ( code == MOUSE_RIGHT ) + { + SetValue( type, GetControlDefaultValue( type ) ); + + CUndoScopeGuard guard( "Set Slider Value To Default" ); + + StampValueIntoLogs( type, GetControlDefaultValue( type ) ); + return; + } + + if ( code != MOUSE_LEFT ) + return; + + // Cache off the value at the click point + // in case we end up receiving a double-click + m_InitialTextEntryValue = m_Control; + + // Enter drag mode + m_SliderMode = (SliderMode_t)( SLIDER_MODE_FIRST_DRAG_MODE + type ); + m_nDragStartPosition[ 0 ] = x; + m_nDragStartPosition[ 1 ] = y; + m_nAccum[ 0 ] = m_nAccum[ 1 ] = 0; + m_flDragStartValue = GetValue( type ); + m_flDragStartBalance = GetValue( ANIM_CONTROL_BALANCE ); + input()->SetMouseCapture( GetVPanel() ); + SetCursor( dc_blank ); + m_pParent->RecomputePreview(); +} + + +void CAttributeSlider::OnCursorMoved( int x, int y ) +{ + if ( !IsEnabled() || !IsDragging() || m_bTransform ) + return; + + // NOTE: This works because we always slam the mouse to be back at the start position + // at the end of this function + + // Accumulate the total mouse movement + int dx = x - m_nDragStartPosition[ 0 ]; + m_nAccum[ 0 ] += dx; + float flFactor = FRAC_PER_PIXEL * ifm_attributeslider_sensitivity.GetFloat(); + + bool bInRecordMode = m_pParent->GetEditor()->GetRecordingState() == AS_RECORD; + float flMinVal = bInRecordMode ? -1.0f : 0.0f; + float flMaxVal = bInRecordMode ? 2.0f : 1.0f; + + // Clamp accum so we never generate values < -1 or > 2 + int nMinVal = floor( ( -m_flDragStartValue + flMinVal ) / flFactor ); + int nMaxVal = ceil( ( -m_flDragStartValue + flMaxVal ) / flFactor ); + m_nAccum[ 0 ] = clamp( m_nAccum[ 0 ], nMinVal, nMaxVal ); + + float flDelta = flFactor * m_nAccum[ 0 ]; + if ( GetDragControl() == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) ) + { + // do the hacky conversion from the ui's left/right to the underlying value/balance + float flLeftValue, flRightValue; + ValueBalanceToLeftRight( &flLeftValue, &flRightValue, m_flDragStartValue, m_flDragStartBalance ); + + float flLeftDelta, flRightDelta; + ValueBalanceToLeftRight( &flLeftDelta, &flRightDelta, flDelta, m_pParent->GetBalanceSliderValue() ); + + flLeftValue = clamp( flLeftValue + flLeftDelta, flMinVal, flMaxVal ); + flRightValue = clamp( flRightValue + flRightDelta, flMinVal, flMaxVal ); + + float flValue, flBalance; + LeftRightToValueBalance( &flValue, &flBalance, flLeftValue, flRightValue ); + + SetValue( GetDragControl(), flValue ); + SetValue( ANIM_CONTROL_BALANCE, flBalance ); // TODO - add balance for multi control as well + } + else + { + float flValue = clamp( m_flDragStartValue + flDelta, flMinVal, flMaxVal ); + SetValue( GetDragControl(), flValue ); + } + + // Slam the cursor back to the drag start point + if ( x != m_nDragStartPosition[ 0 ] || y != m_nDragStartPosition[ 1 ] ) + { + x = m_nDragStartPosition[ 0 ]; + y = m_nDragStartPosition[ 1 ]; + LocalToScreen( x, y ); + input()->SetCursorPos( x, y ); + } +} + +void CAttributeSlider::OnMouseReleased( MouseCode code ) +{ + if ( !IsEnabled() || !IsDragging() || m_bTransform ) + return; + + m_SliderMode = SLIDER_MODE_NONE; + input()->SetMouseCapture( NULL ); + SetCursor( dc_arrow ); + m_pParent->RecomputePreview(); +} + + + +//----------------------------------------------------------------------------- +// +// Methods related to text entry mode +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Called by the text entry code to enter the value into the logs +//----------------------------------------------------------------------------- +void CAttributeSlider::StampValueIntoLogs( AnimationControlType_t type, float flValue ) +{ + Assert( !m_bTransform ); + m_pParent->StampValueIntoLogs( m_hControl, type, flValue ); +} + + +//----------------------------------------------------------------------------- +// Key typed key handler +//----------------------------------------------------------------------------- +void CAttributeSlider::OnKeyCodeTyped( KeyCode code ) +{ + if ( !IsInTextEntry() ) + { + BaseClass::OnKeyCodeTyped( code ); + return; + } + + switch ( code ) + { + default: + BaseClass::OnKeyCodeTyped( code ); + break; + + case KEY_ESCAPE: + DiscardTextEntryValue(); + break; + + case KEY_ENTER: + AcceptTextEntryValue(); + break; + } +} + + +//----------------------------------------------------------------------------- +// Methods to entry text entry mode +//----------------------------------------------------------------------------- +void CAttributeSlider::EnterTextEntryMode( AnimationControlType_t type, bool bRelatchValues ) +{ + if ( m_bTransform ) + return; + + m_SliderMode = (SliderMode_t)( SLIDER_MODE_FIRST_TEXT_MODE + type ); + + // For double-clicking, ignore the value set by the first single mouse click + if ( !bRelatchValues ) + { + SetValue( m_InitialTextEntryValue ); + } + + m_pTextField->SetVisible( true ); + m_pTextField->SetEnabled( true ); + + if ( type == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) ) + { + if ( !m_pRightTextField ) + { + m_pRightTextField = new CAttributeSliderTextEntry( this, GetName() ); + m_pRightTextField->SetVisible( false ); + m_pRightTextField->SetEnabled( false ); + m_pRightTextField->SelectAllOnFocusAlways( true ); + InvalidateLayout(); + } + m_pRightTextField->SetVisible( true ); + m_pRightTextField->SetEnabled( true ); + + float flValue = m_InitialTextEntryValue.m_pValue[ ANIM_CONTROL_VALUE ]; + float flBalance = m_InitialTextEntryValue.m_pValue[ ANIM_CONTROL_BALANCE ]; + float flLeftValue, flRightValue; + ValueBalanceToLeftRight( &flLeftValue, &flRightValue, flValue, flBalance ); + + char val[ 64 ]; + V_snprintf( val, sizeof( val ), "%f", flLeftValue ); + m_pTextField->SetText( val ); + V_snprintf( val, sizeof( val ), "%f", flRightValue ); + m_pRightTextField->SetText( val ); + + m_pRightTextField->GotoTextEnd(); + m_pRightTextField->RequestFocus(); + } + else + { + char val[ 64 ]; + Q_snprintf( val, sizeof( val ), "%f", m_InitialTextEntryValue.m_pValue[ type ] ); + m_pTextField->SetText( val ); + } + + m_pTextField->GotoTextEnd(); + m_pTextField->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Methods to accept or discard the value in the text entry field +//----------------------------------------------------------------------------- +void CAttributeSlider::AcceptTextEntryValue() +{ + if ( !IsInTextEntry() ) + return; + + Assert( !m_bTransform ); + + // Get the value in the text entry field + char buf[ 64 ]; + m_pTextField->GetText( buf, sizeof( buf ) ); + float flValue = Q_atof( buf ); + + // Hide the text entry + m_pTextField->SetVisible( false ); + m_pTextField->SetEnabled( false ); + + if ( m_pRightTextField && GetTextEntryControl() == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) ) + { + float flLeftValue = flValue; + + // Get the value in the text entry field + buf[0] = 0; + m_pRightTextField->GetText( buf, sizeof( buf ) ); + float flRightValue = Q_atof( buf ); + + // Hide the text entry + m_pRightTextField->SetVisible( false ); + m_pRightTextField->SetEnabled( false ); + + float flBalance; + LeftRightToValueBalance( &flValue, &flBalance, flLeftValue, flRightValue ); + + SetValue( ANIM_CONTROL_BALANCE, flBalance ); + StampValueIntoLogs( ANIM_CONTROL_BALANCE, flBalance ); + } + + // Apply the change + AnimationControlType_t type = GetTextEntryControl(); + SetValue( type, flValue ); + StampValueIntoLogs( type, flValue ); + + m_SliderMode = SLIDER_MODE_NONE; + RequestFocus(); +} + +void CAttributeSlider::DiscardTextEntryValue() +{ + if ( !IsInTextEntry() ) + return; + + Assert( !m_bTransform ); + + // Hide the text entry + m_pTextField->SetVisible( false ); + m_pTextField->SetEnabled( false ); + + if ( m_pRightTextField && GetTextEntryControl() == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) ) + { + m_pRightTextField->SetVisible( false ); + m_pRightTextField->SetEnabled( false ); + } + + m_SliderMode = SLIDER_MODE_NONE; + RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Methods of the text entry widget +//----------------------------------------------------------------------------- +void CAttributeSliderTextEntry::OnKillFocus( KeyValues *pParams ) +{ + Assert( m_pSlider ); + + SelectNone(); + + VPANEL hPanel = (VPANEL)pParams->GetPtr( "newPanel" ); + if ( hPanel != INVALID_PANEL && vgui::ipanel()->GetParent( hPanel ) == m_pSlider->GetVPanel() ) + return; + + m_pSlider->AcceptTextEntryValue(); +} + +void CAttributeSliderTextEntry::OnMouseWheeled( int delta ) +{ + if ( m_pSlider->m_bTransform ) + return; + + float deltaFactor; + if ( input()->IsKeyDown(KEY_LSHIFT) ) + { + deltaFactor = ((float)delta) * 10.0f; + } + else if ( input()->IsKeyDown(KEY_LCONTROL) ) + { + deltaFactor = ((float)delta) / 100.0; + } + else + { + deltaFactor = ((float)delta) / 10.0; + } + + char sz[ 64 ]; + GetText( sz, sizeof( sz ) ); + + float val = Q_atof( sz ) + deltaFactor; + if ( input()->IsKeyDown(KEY_LALT) ) + { + val = clamp( val, 0.0f, 1.0f ); + } + + Q_snprintf( sz, sizeof( sz ), "%f", val ); + + SetText( sz ); + m_pSlider->SetValue( ANIM_CONTROL_VALUE, val ); + + CUndoScopeGuard guard( UNDO_CHAIN_MOUSEWHEEL_ATTRIBUTE_SLIDER, "Set Slider Value" ); + + m_pSlider->StampValueIntoLogs( m_pSlider->GetTextEntryControl(), val ); +} + +void CAttributeSlider::OnMouseDoublePressed( MouseCode code ) +{ + if ( !IsEnabled() || IsDragging() ) + return; + + if ( code != MOUSE_LEFT ) + return; + + int x,y; + input()->GetCursorPosition( x, y ); + ScreenToLocal( x, y ); + AnimationControlType_t type = DetermineControl( x, y ); + EnterTextEntryMode( type, false ); +} + + +//----------------------------------------------------------------------------- +// +// Methods related to preview +// +//----------------------------------------------------------------------------- +void CAttributeSlider::EnablePreview( bool state, bool simple, bool faderdrag ) +{ + m_bPreviewEnabled = state; + m_bSimplePreviewOnly = simple; + m_bFaderBeingDragged = faderdrag; +} + +bool CAttributeSlider::IsPreviewEnabled() const +{ + return m_bPreviewEnabled; +} + +bool CAttributeSlider::IsSimplePreview() const +{ + return m_bSimplePreviewOnly; +} + +#define ATTRIBUTE_SLIDER_RAMP_TIME 0.5f + +bool CAttributeSlider::IsRampingTowardPreview() const +{ + if ( m_flPreviewGoalTime == -1.0f ) + return false; + + return true; +} + +void CAttributeSlider::RampDown() +{ + if ( m_flPreviewGoalTime == -1.0f ) + return; + + m_Previous.m_Current = GetValue(); + m_Previous.m_Full = GetValue( ); + m_bRampUp = false; +} + +void CAttributeSlider::UpdateFaderAmount( float flAmount ) +{ + m_flFaderAmount = flAmount; + AttributeValue_t current = GetValue(); + if ( m_flPreviewGoalTime == -1.0f ) + { + BlendFlexValues( &m_Preview.m_Current, current, m_Preview.m_Full, flAmount ); + return; + } + + BlendFlexValues( &m_Next.m_Current, current, m_Next.m_Full, flAmount ); +} + +void CAttributeSlider::UpdateTime( float dt ) +{ + if ( m_flPreviewGoalTime == -1.0f ) + return; + + // Move toward goal + if ( m_bRampUp ) + { + if ( m_flPreviewGoalTime < ATTRIBUTE_SLIDER_RAMP_TIME ) + { + m_flPreviewGoalTime += dt; + } + } + else + { + m_flPreviewGoalTime -= dt; + } + + if ( m_flPreviewGoalTime >= ATTRIBUTE_SLIDER_RAMP_TIME ) + { + m_Preview = m_Next; + m_flPreviewGoalTime = ATTRIBUTE_SLIDER_RAMP_TIME; + + } + else if ( m_flPreviewGoalTime <= 0.0f ) + { + m_flPreviewGoalTime = -1.0f; + m_Preview = m_Previous; + } + else + { + float frac = m_flPreviewGoalTime / ATTRIBUTE_SLIDER_RAMP_TIME; + BlendFlexValues( &m_Preview.m_Current, m_Previous.m_Current, m_Next.m_Current, frac ); + BlendFlexValues( &m_Preview.m_Full, m_Previous.m_Full, m_Next.m_Full, frac ); + } +} + +void CAttributeSlider::SetPreview( const AttributeValue_t &value, const AttributeValue_t &full, bool instantaneous, bool startfromcurrent ) +{ + m_bRampUp = true; + + if ( instantaneous ) + { + m_Next.m_Current = value; + m_Next.m_Full = full; + + m_Preview = m_Previous = m_Next; + m_flPreviewGoalTime = -1.0f; + } + else + { + // Current becomes previous, next becomes goal and preview starts moving toward that goal + if ( startfromcurrent ) + { + m_Previous.m_Current = GetValue( ); + m_Previous.m_Full = GetValue( ); + } + else + { + m_Previous = m_Preview; + } + + m_Next.m_Current = value; + m_Next.m_Full = full; + m_flPreviewGoalTime = 0.0f; + } +} + +const AttributeValue_t &CAttributeSlider::GetPreview() const +{ + return m_Preview.m_Current; +} + +float CAttributeSlider::GetPreview( AnimationControlType_t type ) const +{ + return m_Preview.m_Current.m_pValue[type]; +} + +// Estimates the value of the control given a local coordinate +float CAttributeSlider::EstimateValueAtPos( int nLocalX, int nLocalY ) const +{ + Rect_t rect; + GetControlRect( &rect, ANIM_CONTROL_VALUE ); + + float flFactor = rect.width > 1 ? (float)( nLocalX - rect.x ) / (float)( rect.width - 1 ) : 0.5f; + flFactor = clamp( flFactor, 0.0f, 1.0f ); + return flFactor; +} + + +//----------------------------------------------------------------------------- +// Layout +//----------------------------------------------------------------------------- +void CAttributeSlider::PerformLayout() +{ + BaseClass::PerformLayout(); + + Rect_t rect; + GetControlRect( &rect, ANIM_CONTROL_VALUE ); + + // Place the text entry along the main attribute track rectangle + if ( m_pRightTextField && GetTextEntryControl() == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) ) + { + m_pTextField ->SetBounds( rect.x, rect.y, rect.width / 2, rect.height ); + m_pRightTextField->SetBounds( rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height ); + } + else + { + m_pTextField->SetBounds( rect.x, rect.y, rect.width, rect.height ); + } +} + +void CAttributeSlider::GetControlRect( Rect_t *pRect, AnimationControlType_t type ) const +{ + int sw, sh; + const_cast<CAttributeSlider*>( this )->GetSize( sw, sh ); + + int cw, ch; + m_pCircleImage->GetSize( cw, ch ); + + switch ( type ) + { + case ANIM_CONTROL_VALUE: + pRect->x = 2 * SLIDER_PIXEL_SPACING + cw; + pRect->y = SLIDER_PIXEL_SPACING; + pRect->width = sw - pRect->x * 2; + pRect->height = max( 0, sh - SLIDER_PIXEL_SPACING * 2 ); + break; +/* + case ANIM_CONTROL_BALANCE: + pRect->x = SLIDER_PIXEL_SPACING; + pRect->y = max( 0, sh - ch ) / 2; + pRect->width = cw; + pRect->height = min( ch, sh ); + break; +*/ + case ANIM_CONTROL_MULTILEVEL: + pRect->x = sw - SLIDER_PIXEL_SPACING - cw; + pRect->y = max( 0, sh - ch ) / 2; + pRect->width = cw; + pRect->height = min( ch, sh ); + break; + } +} + +bool CAttributeSlider::IsFaderBeingDragged() +{ + return IsPreviewEnabled() && m_bFaderBeingDragged; +} + + +//----------------------------------------------------------------------------- +// +// Methods related to painting start here +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Used to control how fader-driven ticks look +//----------------------------------------------------------------------------- +float CAttributeSlider::GetPreviewAlphaScale() const +{ + return max( m_flFaderAmount, 0.1f ); +} + + +//----------------------------------------------------------------------------- +// Draws a tick on the main control +//----------------------------------------------------------------------------- +void CAttributeSlider::DrawTick( const Color& clr, float frac, int width, int inset ) +{ + // Get the control position + Rect_t rect; + GetControlRect( &rect, ANIM_CONTROL_VALUE ); + + // Inset by 1 pixel + rect.x++; rect.y++; rect.width -= 2; rect.height -= 2; + + surface()->DrawSetColor( clr ); + + int previewx = (int)( frac * (float)rect.width + 0.5f ) + rect.x; + int previewtall = rect.height - 2 * inset; + int ypos = rect.y + ( rect.height - previewtall ) / 2; + + int xpos = previewx - width / 2; + xpos = clamp( xpos, rect.x, rect.x + rect.width - width ); + surface()->DrawFilledRect( xpos, ypos, xpos + width, ypos + previewtall ); +} + + +//----------------------------------------------------------------------------- +// Draws a preview tick on the main control +//----------------------------------------------------------------------------- +void CAttributeSlider::DrawPreviewTick( bool bMainTick ) +{ + Color col = s_PreviewTickColor; + col[ 3 ] *= bMainTick ? GetPreviewAlphaScale() : 0.5f; + DrawTick( col, m_Next.m_Full.m_pValue[ ANIM_CONTROL_VALUE ], 2, 2 ); +} + + +//----------------------------------------------------------------------------- +// Draws a tick on a circular control +//----------------------------------------------------------------------------- +void CAttributeSlider::DrawCircularTick( const Color& clr, float flValue, int nCenterX, int nCenterY, float flRadius ) +{ + float flFraction = 1.0f; + float flAngle = 0.0f; + if ( flValue < 0.5f ) + { + flFraction = ( flValue / 0.5f ); + flAngle = 180.0f + flFraction * 180.0f; + } + else + { + flFraction = ( flValue - 0.5f ) * 2.0f; + flAngle = flFraction * 180.0f; + } + + float flRadians = DEG2RAD( flAngle ); + float ca = cos( flRadians ); + float sa = sin( flRadians ); + + int nEndX = nCenterX + flRadius * sa; + int nEndY = nCenterY - flRadius * ca; + + surface()->DrawSetColor( clr ); + surface()->DrawLine( nCenterX, nCenterY, nEndX, nEndY ); +} + + +//----------------------------------------------------------------------------- +// Draws a preview of a circular control +//----------------------------------------------------------------------------- +void CAttributeSlider::DrawCircularPreview( AnimationControlType_t type, bool bMainTick, float flRadius ) +{ + Rect_t rect; + GetControlRect( &rect, type ); + + // Fill left from top + float flPreview = m_Next.m_Full.m_pValue[type]; + float flCurrent = GetValue( type ); + + Color clr = s_PreviewTickColor; + clr[ 3 ] *= bMainTick ? GetPreviewAlphaScale() : 0.5f; + + int nCenterX = rect.x + rect.width / 2; + int nCenterY = rect.y + rect.height / 2; + DrawCircularTick( clr, flPreview, nCenterX, nCenterY, flRadius ); + + if ( m_bSimplePreviewOnly && !m_bFaderBeingDragged ) + return; + + clr = s_OldValueTickColor; + if ( !bMainTick ) + { + clr[ 3 ] *= 0.5f; + } + + DrawCircularTick( clr, flCurrent, nCenterX, nCenterY, flRadius ); +} + + +//----------------------------------------------------------------------------- +// Paints ticks +//----------------------------------------------------------------------------- +void CAttributeSlider::Paint() +{ + DrawTick( s_OldValueTickColor, GetValue( ANIM_CONTROL_VALUE ), 1, 0 ); + if ( m_bPreviewEnabled ) + { + DrawPreviewTick( true ); + if ( IsControlActive( ANIM_CONTROL_BALANCE ) ) + { + DrawCircularPreview( ANIM_CONTROL_BALANCE, true, CIRCULAR_CONTROL_RADIUS ); + } + if ( IsControlActive( ANIM_CONTROL_MULTILEVEL ) ) + { + DrawCircularPreview( ANIM_CONTROL_MULTILEVEL, true, CIRCULAR_CONTROL_RADIUS ); + } + } +} + + +//----------------------------------------------------------------------------- +// Draws the min, current, and max values for the slider +//----------------------------------------------------------------------------- +void CAttributeSlider::DrawValueLabel( float flValue ) +{ + float flMinVal = 0.0f; + float flMaxVal = 1.0f; + flValue = clamp( flValue, flMinVal, flMaxVal ); + + Rect_t rect; + GetControlRect( &rect, ANIM_CONTROL_VALUE ); + + int cw, ch; + char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%.1f", flMinVal ); + m_pValues[ 0 ]->SetText( sz ); + m_pValues[ 0 ]->ResizeImageToContent(); + m_pValues[ 0 ]->GetContentSize( cw, ch ); + m_pValues[ 0 ]->SetPos( rect.x + 5, rect.y + ( rect.height - ch ) * 0.5f ); + m_pValues[ 0 ]->Paint(); + + Q_snprintf( sz, sizeof( sz ), "%.1f", flMaxVal ); + m_pValues[ 2 ]->SetText( sz ); + m_pValues[ 2 ]->ResizeImageToContent(); + m_pValues[ 2 ]->GetContentSize( cw, ch ); + m_pValues[ 2 ]->SetPos( rect.x + rect.width - cw - 5, rect.y + ( rect.height - ch ) * 0.5f ); + m_pValues[ 2 ]->Paint(); + + Q_snprintf( sz, sizeof( sz ), "%.3f", flValue ); + m_pValues[ 1 ]->SetText( sz ); + m_pValues[ 1 ]->ResizeImageToContent(); + m_pValues[ 1 ]->GetContentSize( cw, ch ); + m_pValues[ 1 ]->SetPos( rect.x + ( rect.width - cw ) * 0.5f, rect.y + ( rect.height - ch ) * 0.5f ); + m_pValues[ 1 ]->Paint(); +} + + +//----------------------------------------------------------------------------- +// Draws the text for the slider. It's either the slider name, or its value if dragging is happening +//----------------------------------------------------------------------------- +void CAttributeSlider::DrawNameLabel() +{ + if ( IsDragging() ) + { + float flValue = GetValue( GetDragControl() ); + DrawValueLabel( flValue ); + return; + } + + if ( IsInTextEntry() ) + return; + + int w, h; + GetSize( w, h ); + + int cw, ch; + Color clr = m_bCursorInsidePanel ? s_TextColorFocus : s_TextColor; + m_pName->SetColor( clr ); + m_pName->GetContentSize( cw, ch ); + + Rect_t rect; + GetControlRect( &rect, ANIM_CONTROL_VALUE ); + + m_pName->SetPos( rect.x + ( rect.width - cw ) * 0.5f, rect.y + ( rect.height - ch ) * 0.5f ); + m_pName->Paint(); +} + + +//----------------------------------------------------------------------------- +// Draws the midpoint value for the slider +//----------------------------------------------------------------------------- +void CAttributeSlider::DrawMidpoint( int x, int ty, int ttall ) +{ + surface()->DrawSetColor( s_MidpointColor ); + surface()->DrawFilledRect( x, ty, x + 1, ty + ttall ); +} + + +//----------------------------------------------------------------------------- +// Paints circular controls used for balance + multilevel controls +//----------------------------------------------------------------------------- +void CAttributeSlider::PaintCircularControl( float flValue, const Rect_t& rect ) +{ + flValue = clamp( flValue, 0.0f, 1.0f ); + + m_pCircleImage->SetPos( rect.x, rect.y ); + m_pCircleImage->Paint(); + + int ofs[ 2 ] = { 0 }; + LocalToScreen( ofs[ 0 ], ofs[ 1 ] ); + + int nCenterX = ofs[ 0 ] + rect.x + rect.width / 2; + int nCenterY = ofs[ 1 ] + rect.y + rect.height / 2; + + float maxTrianges = 36.0f; + + float frac = 0.0f; + float step = 180.0f / (float)( maxTrianges ); + float ang = 0.0f; + float clamp = 360.0f; + int numTriangles = 0; + float radius = CIRCULAR_CONTROL_RADIUS; + + float zpos = vgui::surface()->GetZPos(); + + Vector centerVert( nCenterX, nCenterY, zpos ); + + Vector top; + top = centerVert; + top.y -= radius; + + // Fill left from top + if ( flValue < 0.5f ) + { + frac = 1.0f - ( flValue / 0.5f ); + numTriangles = (int)( frac * ( maxTrianges ) + 0.5f ); + clamp = 180.0f; + step = -step; + ang = 360.0f; + } + else + { + frac = ( flValue - 0.5f ) / 0.5f; + numTriangles = (int)( frac * ( maxTrianges ) + 0.5f ); + clamp = 180.0f; + } + + if ( numTriangles == 0 ) + return; + + CMatRenderContextPtr pRenderContext( materials ); + IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_pWhite ); + + Color clr( 102, 102, 102, 255 ); + + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numTriangles ); + + Vector next; + next.Init(); + + for ( int j = 0; j < numTriangles; j++ ) + { + ang += step; + //ang = min( ang, clamp ); + + float flRadians = DEG2RAD( ang ); + + float ca = cos( flRadians ); + float sa = sin( flRadians ); + + meshBuilder.Position3fv( centerVert.Base() ); + meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() ); + meshBuilder.TexCoord2f( 0, 0, 0 ); + meshBuilder.AdvanceVertex(); + + next.Init(); + next.x = radius * sa; + next.y = -radius * ca; + next += centerVert; + + if ( step > 0 ) + { + meshBuilder.Position3fv( top.Base() ); + meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() ); + meshBuilder.TexCoord2f( 0, 0, 1 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3fv( next.Base() ); + meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() ); + meshBuilder.TexCoord2f( 0, 1, 0 ); + meshBuilder.AdvanceVertex(); + } + else + { + meshBuilder.Position3fv( next.Base() ); + meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() ); + meshBuilder.TexCoord2f( 0, 0, 1 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3fv( top.Base() ); + meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() ); + meshBuilder.TexCoord2f( 0, 1, 0 ); + meshBuilder.AdvanceVertex(); + } + + top = next; + } + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +// Paints the slider +//----------------------------------------------------------------------------- +void CAttributeSlider::PaintBackground() +{ + Rect_t rect; + GetControlRect( &rect, ANIM_CONTROL_VALUE ); + + // Paint the border + surface()->DrawSetColor( Color( 24, 24, 24, 255 ) ); + // top and left + surface()->DrawOutlinedRect( rect.x, rect.y, rect.x + rect.width, rect.y + 1 ); + surface()->DrawOutlinedRect( rect.x, rect.y, rect.x + 1, rect.y + rect.height ); + // right + surface()->DrawSetColor( Color( 33, 33, 33, 255 ) ); + surface()->DrawOutlinedRect( rect.x + rect.width - 1, rect.y, rect.x + rect.width, rect.y + rect.height ); + // bottom + surface()->DrawSetColor( Color( 56, 56, 56, 255 ) ); + surface()->DrawOutlinedRect( rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height ); + + // Inset the rect by 1 pixel + ++rect.x; ++rect.y; rect.width -= 2; rect.height -= 2; + + int y0 = rect.y; + int y1 = rect.y + rect.height / 2; + int y2 = rect.y + rect.height; + + // Draw the main bar background + surface()->DrawSetColor( s_ZeroColor[ m_bIsLogPreviewControl ][ IsSelected() ] ); + surface()->DrawFilledRect( rect.x, y0, rect.x + rect.width, y2 ); + + AnimationControlType_t viewType = ANIM_CONTROL_VALUE; + if ( IsDragging() ) + { + viewType = GetDragControl(); + } + else if ( IsInTextEntry() ) + { + viewType = GetTextEntryControl(); + } + + bool bUsePreview = m_bPreviewEnabled && ( !m_bSimplePreviewOnly || m_bFaderBeingDragged ); + + float flMidPoint = GetControlDefaultValue( viewType ); + int nMidPoint = (int)( (float)rect.width * clamp( flMidPoint, 0.0f, 1.0f ) + 0.5f ); + + float flValue = bUsePreview ? m_Preview.m_Current.m_pValue[viewType] : GetValue( viewType ); + if ( viewType == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) ) + { + float flBalance = bUsePreview ? m_Preview.m_Current.m_pValue[ ANIM_CONTROL_BALANCE ] : GetValue( ANIM_CONTROL_BALANCE ); + float flLeftValue, flRightValue; + ValueBalanceToLeftRight( &flLeftValue, &flRightValue, flValue, flBalance ); + + int nLeftValue = (int)( (float)rect.width * clamp( flLeftValue, 0.0f, 1.0f ) + 0.5f ); + int nRightValue = (int)( (float)rect.width * clamp( flRightValue, 0.0f, 1.0f ) + 0.5f ); + + // Draw the current value as a bar from the midpoint + surface()->DrawSetColor( IsDragging() ? s_DraggingBarColor : s_BarColor[ m_bIsLogPreviewControl ][ IsSelected() ] ); + surface()->DrawFilledRect( rect.x + min( nLeftValue, nMidPoint ), y0, rect.x + max( nLeftValue, nMidPoint ), y1 ); + surface()->DrawFilledRect( rect.x + min( nRightValue, nMidPoint ), y1, rect.x + max( nRightValue, nMidPoint ), y2 ); + } + else + { + Assert( viewType != ANIM_CONTROL_BALANCE ); + + int nValue = (int)( (float)rect.width * clamp( flValue, 0.0f, 1.0f ) + 0.5f ); + + // Draw the current value as a bar from the midpoint + surface()->DrawSetColor( IsDragging() ? s_DraggingBarColor : s_BarColor[ m_bIsLogPreviewControl ][ IsSelected() ] ); + surface()->DrawFilledRect( rect.x + min( nValue, nMidPoint ), y0, rect.x + max( nValue, nMidPoint ), y2 ); + } + + // Draw the midpoint over the top of the current value + DrawMidpoint( rect.x + nMidPoint, rect.y, rect.height ); + + // Draw the name or value over the top of that + DrawNameLabel(); + + // Paints the circular controls + if ( IsControlActive( ANIM_CONTROL_MULTILEVEL ) ) + { + float flMultiValue = bUsePreview ? m_Preview.m_Current.m_pValue[ANIM_CONTROL_MULTILEVEL] : GetValue( ANIM_CONTROL_MULTILEVEL ); + GetControlRect( &rect, ANIM_CONTROL_MULTILEVEL ); + PaintCircularControl( flMultiValue, rect ); + + // Draws the midpoint for the circular controls + int nCenterX = rect.x + rect.width / 2; + int nCenterY = rect.y + rect.height / 2; + DrawCircularTick( s_MidpointColor, GetControlDefaultValue( ANIM_CONTROL_MULTILEVEL ), nCenterX, nCenterY, CIRCULAR_CONTROL_RADIUS ); + } +}
\ No newline at end of file diff --git a/vgui2/dme_controls/attributesurfacepropertypickerpanel.cpp b/vgui2/dme_controls/attributesurfacepropertypickerpanel.cpp new file mode 100644 index 0000000..0de7868 --- /dev/null +++ b/vgui2/dme_controls/attributesurfacepropertypickerpanel.cpp @@ -0,0 +1,103 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dme_controls/AttributeSurfacePropertyPickerPanel.h" +#include "dme_controls/AttributeTextEntry.h" +#include "tier1/KeyValues.h" +#include "filesystem.h" + + +using namespace vgui; + + +const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt"; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAttributeSurfacePropertyPickerPanel::CAttributeSurfacePropertyPickerPanel( vgui::Panel *parent, const AttributeWidgetInfo_t &info ) : + BaseClass( parent, info ) +{ +} + +CAttributeSurfacePropertyPickerPanel::~CAttributeSurfacePropertyPickerPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Reads the surface properties +//----------------------------------------------------------------------------- +void CAttributeSurfacePropertyPickerPanel::AddSurfacePropertiesToList( PickerList_t &list ) +{ + KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE ); + if ( manifest->LoadFromFile( g_pFullFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) ) + { + for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() ) + { + if ( Q_stricmp( sub->GetName(), "file" ) ) + continue; + + KeyValues *file = new KeyValues( SURFACEPROP_MANIFEST_FILE ); + if ( file->LoadFromFile( g_pFullFileSystem, sub->GetString(), "GAME" ) ) + { + for ( KeyValues *pTrav = file; pTrav; pTrav = pTrav->GetNextKey() ) + { + int i = list.AddToTail(); + list[i].m_pChoiceString = pTrav->GetName(); + list[i].m_pChoiceValue = pTrav->GetName(); + } + } + else + { + Warning( "Unable to load surface properties file '%s'\n", sub->GetString() ); + } + file->deleteThis(); + } + } + else + { + Warning( "Unable to load manifest file '%s'\n", SURFACEPROP_MANIFEST_FILE ); + } + + manifest->deleteThis(); +} + + +//----------------------------------------------------------------------------- +// Called when it's time to show the picker +//----------------------------------------------------------------------------- +void CAttributeSurfacePropertyPickerPanel::ShowPickerDialog() +{ + CPickerFrame *pSurfacePropPickerDialog = new CPickerFrame( this, "Select Surface Property", "Surface Property", "surfacePropertyName" ); + PickerList_t surfacePropList; + AddSurfacePropertiesToList( surfacePropList ); + pSurfacePropPickerDialog->AddActionSignalTarget( this ); + pSurfacePropPickerDialog->DoModal( surfacePropList ); +} + + +//----------------------------------------------------------------------------- +// Called by the picker dialog if a asset was selected +//----------------------------------------------------------------------------- +void CAttributeSurfacePropertyPickerPanel::OnPicked( KeyValues *pKeyValues ) +{ + // Get the asset name back + const char *pSurfacePropertyName = pKeyValues->GetString( "choice", NULL ); + if ( !pSurfacePropertyName || !pSurfacePropertyName[ 0 ] ) + return; + + // Apply to text panel + m_pData->SetText( pSurfacePropertyName ); + SetDirty(true); + if ( IsAutoApply() ) + { + Apply(); + } +} diff --git a/vgui2/dme_controls/dme_controls.vpc b/vgui2/dme_controls/dme_controls.vpc new file mode 100644 index 0000000..42841b7 --- /dev/null +++ b/vgui2/dme_controls/dme_controls.vpc @@ -0,0 +1,133 @@ +//----------------------------------------------------------------------------- +// DME_CONTROLS.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc" + +$Configuration +{ + $Compiler + { + $PreprocessorDefinitions "$BASE;DMECONTROLS_LIB" + $PrecompiledHeaderFile "Debug/dme_controls.pch" + } +} + +$Project "Dme_controls" +{ + $Folder "Header Files" + { + $File "$SRCDIR\public\dme_controls\AttributeSlider.h" + $File "$SRCDIR\public\dme_controls\AssetBuilder.h" + $File "$SRCDIR\public\dme_controls\attributeassetpickerpanel.h" + $File "$SRCDIR\public\dme_controls\AttributeBasePickerPanel.h" + $File "$SRCDIR\public\dme_controls\AttributeBoolChoicePanel.h" + $File "$SRCDIR\public\dme_controls\AttributeColorPickerPanel.h" + $File "$SRCDIR\public\dme_controls\attributedetailtypepickerpanel.h" + $File "$SRCDIR\public\dme_controls\AttributeElementPanel.h" + $File "$SRCDIR\public\dme_controls\AttributeElementPickerPanel.h" + $File "$SRCDIR\public\dme_controls\AttributeFilePickerPanel.h" + $File "$SRCDIR\public\dme_controls\AttributeIntChoicePanel.h" + $File "$SRCDIR\public\dme_controls\AttributeInterpolatorChoicePanel.h" + $File "$SRCDIR\public\dme_controls\AttributeMDLPickerPanel.h" + $File "$SRCDIR\public\dme_controls\AttributeSequencePickerPanel.h" + $File "$SRCDIR\public\dme_controls\attributeshaderpickerpanel.h" + $File "$SRCDIR\public\dme_controls\AttributeSoundPickerPanel.h" + $File "$SRCDIR\public\dme_controls\AttributeStringChoicePanel.h" + $File "$SRCDIR\public\dme_controls\attributesurfacepropertypickerpanel.h" + $File "$SRCDIR\public\dme_controls\AttributeTextEntry.h" + $File "$SRCDIR\public\dme_controls\AttributeTextPanel.h" + $File "$SRCDIR\public\dme_controls\AttributeWidgetFactory.h" + $File "$SRCDIR\public\dme_controls\BaseAttributeChoicePanel.h" + $File "$SRCDIR\public\dme_controls\BaseAttributeDoubleChoicePanel.h" + $File "$SRCDIR\public\dme_controls\BaseAttributePanel.h" + $File "$SRCDIR\public\dme_controls\ChannelGraphPanel.h" + $File "$SRCDIR\public\dme_controls\dmecombinationsystemeditorpanel.h" + $File "$SRCDIR\public\dme_controls\dmecontrols.h" + $File "$SRCDIR\public\dme_controls\dmecontrols_utils.h" + $File "$SRCDIR\public\dme_controls\dmedageditpanel.h" + $File "$SRCDIR\public\dme_controls\dmedagrenderpanel.h" + $File "$SRCDIR\public\dme_controls\dmemdlpanel.h" + $File "$SRCDIR\public\dme_controls\dmepanel.h" + $File "$SRCDIR\public\dme_controls\dmepicker.h" + $File "$SRCDIR\public\dme_controls\dmepresetgroupeditorpanel.h" + $File "$SRCDIR\public\dme_controls\DmeSourceDCCFilePanel.h" + $File "$SRCDIR\public\dme_controls\DmeSourceSkinPanel.h" + $File "$SRCDIR\public\dme_controls\ElementPropertiesTree.h" + $File "$SRCDIR\public\dme_controls\FileListManager.h" + $File "$SRCDIR\public\dme_controls\filtercombobox.h" + $File "$SRCDIR\public\dme_controls\inotifyui.h" + $File "$SRCDIR\public\dme_controls\particlesystempanel.h" + $File "$SRCDIR\public\dme_controls\particlesystempropertiespanel.h" + $File "$SRCDIR\public\dme_controls\soundpicker.h" + $File "$SRCDIR\public\dme_controls\soundrecordpanel.h" + } + + $Folder "Source Files" + { + $File "AttributeSlider.cpp" + $File "AssetBuilder.cpp" + $File "attributeassetpickerpanel.cpp" + $File "AttributeBasePickerPanel.cpp" + $File "AttributeBoolChoicePanel.cpp" + $File "AttributeColorPickerPanel.cpp" + $File "attributedetailtypepickerpanel.cpp" + $File "AttributeElementPanel.cpp" + $File "AttributeElementPickerPanel.cpp" + $File "AttributeFilePickerPanel.cpp" + $File "AttributeIntChoicePanel.cpp" + $File "AttributeInterpolatorChoicePanel.cpp" + $File "AttributeMDLPickerPanel.cpp" + $File "AttributeSequencePickerPanel.cpp" + $File "attributeshaderpickerpanel.cpp" + $File "AttributeSoundPickerPanel.cpp" + $File "AttributeStringChoicePanel.cpp" + $File "attributesurfacepropertypickerpanel.cpp" + $File "AttributeTextEntry.cpp" + $File "AttributeTextPanel.cpp" + $File "AttributeWidgetFactory.cpp" + $File "BaseAttributeChoicePanel.cpp" + $File "BaseAttributeDoubleChoicePanel.cpp" + $File "BaseAttributePanel.cpp" + $File "ChannelGraphPanel.cpp" + $File "dmecombinationsystemeditorpanel.cpp" + $File "dmecontrols.cpp" + $File "dmedageditpanel.cpp" + $File "dmedagrenderpanel.cpp" + $File "dmelogeditpanel.cpp" + $File "dmemdlpanel.cpp" + $File "dmepanel.cpp" + $File "dmepicker.cpp" + $File "dmepresetgroupeditorpanel.cpp" + $File "DmeSourceDCCFilePanel.cpp" + $File "DmeSourceSkinPanel.cpp" + $File "ElementPropertiesTree.cpp" + $File "FileListManager.cpp" + $File "filtercombobox.cpp" + $File "particlesystempanel.cpp" + $File "particlesystempropertiespanel.cpp" + $File "soundpicker.cpp" + $File "soundrecordpanel.cpp" + + $Folder "Animation Set Editor" + { + $File "$SRCDIR\public\dme_controls\AnimSetAttributeValue.h" + $File "BaseAnimationSetEditor.cpp" + $File "$SRCDIR\public\dme_controls\BaseAnimationSetEditor.h" + $File "BaseAnimSetAttributeSliderPanel.cpp" + $File "$SRCDIR\public\dme_controls\BaseAnimSetAttributeSliderPanel.h" + $File "BaseAnimSetControlGroupPanel.cpp" + $File "$SRCDIR\public\dme_controls\BaseAnimSetControlGroupPanel.h" + $File "BaseAnimSetPresetFaderPanel.cpp" + $File "$SRCDIR\public\dme_controls\BaseAnimSetPresetFaderPanel.h" + $File "$SRCDIR\public\dme_controls\LogPreview.h" + $File "presetpicker.cpp" + $File "$SRCDIR\public\dme_controls\presetpicker.h" + $File "$SRCDIR\public\dme_controls\RecordingState.h" + $File "$SRCDIR\public\phonemeconverter.cpp" + } + } +} diff --git a/vgui2/dme_controls/dmecombinationsystemeditorpanel.cpp b/vgui2/dme_controls/dmecombinationsystemeditorpanel.cpp new file mode 100644 index 0000000..5d6efaf --- /dev/null +++ b/vgui2/dme_controls/dmecombinationsystemeditorpanel.cpp @@ -0,0 +1,2150 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "dme_controls/dmecombinationsystemeditorpanel.h" +#include "dme_controls/dmepanel.h" +#include "dme_controls/elementpropertiestree.h" +#include "dme_controls/dmecontrols_utils.h" +#include "movieobjects/dmecombinationoperator.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/InputDialog.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/FileOpenDialog.h" +#include "vgui_controls/perforcefilelistframe.h" +#include "vgui/MouseCode.h" +#include "vgui/IInput.h" +#include "tier1/KeyValues.h" +#include "tier2/fileutils.h" + + +//----------------------------------------------------------------------------- +// +// Hook into the dme panel editor system +// +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CDmeCombinationSystemEditorPanel, DmeCombinationOperator, "DmeCombinationOperatorEditor", "Combination Operator Editor", true ); + + +// Forward declaration +class CDmeCombinationControlsPanel; + + +//----------------------------------------------------------------------------- +// Import combination rules from this operator +//----------------------------------------------------------------------------- +static void ImportCombinationControls( CDmeCombinationOperator *pDestComboOp, CDmeCombinationOperator *pSrcComboOp, COperationFileListFrame *pStatusFrame ) +{ + pDestComboOp->RemoveAllControls(); + + // Iterate through all controls in the imported operator. + // For each control that contains at least 1 raw controls + // that also exist in this combination op, create a control here also. + int nCount = pSrcComboOp->GetControlCount(); + for ( int i = 0; i < nCount; ++i ) + { + const char *pControlName = pSrcComboOp->GetControlName( i ); + + int nRawControls = pSrcComboOp->GetRawControlCount( i ); + int nMatchCount = 0; + bool *pFoundMatch = (bool*)_alloca( nRawControls * sizeof(bool) ); + for ( int j = 0; j < nRawControls; ++j ) + { + const char *pRawControl = pSrcComboOp->GetRawControlName( i, j ); + pFoundMatch[j] = pDestComboOp->DoesTargetContainDeltaState( pRawControl ); + nMatchCount += pFoundMatch[j]; + } + + // No match? Don't import + if ( nMatchCount == 0 ) + { + pStatusFrame->AddOperation( pControlName, "No raw controls found!" ); + continue; + } + + bool bPartialMatch = ( nMatchCount != nRawControls ); + pStatusFrame->AddOperation( pControlName, bPartialMatch ? "Partial rule match" : "Successful" ); + + // Found a match! Let's create the control and potentially raw control + bool bIsStereo = pSrcComboOp->IsStereoControl( i ); + bool bIsEyelid = pSrcComboOp->IsEyelidControl( i ); + ControlIndex_t index = pDestComboOp->FindOrCreateControl( pControlName, bIsStereo ); + pDestComboOp->SetEyelidControl( index, bIsEyelid ); + for ( int j = 0; j < nRawControls; ++j ) + { + if ( pFoundMatch[j] ) + { + const char *pRawControl = pSrcComboOp->GetRawControlName( i, j ); + float flWrinkleScale = pSrcComboOp->GetRawControlWrinkleScale( i, j ); + + pDestComboOp->AddRawControl( index, pRawControl ); + pDestComboOp->SetWrinkleScale( index, pRawControl, flWrinkleScale ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Import dominance rules from this operator +//----------------------------------------------------------------------------- +static void ImportDominationRules( CDmeCombinationOperator *pDestComboOp, CDmeCombinationOperator *pSrcComboOp, COperationFileListFrame *pStatusFrame ) +{ + pDestComboOp->RemoveAllDominationRules(); + + // Now deal with dominance rules + int nRuleCount = pSrcComboOp->DominationRuleCount(); + for ( int i = 0; i < nRuleCount; ++i ) + { + bool bMismatch = false; + + // Only add dominance rule if *all* raw controls are present + CDmeCombinationDominationRule *pSrcRule = pSrcComboOp->GetDominationRule( i ); + int nDominatorCount = pSrcRule->DominatorCount(); + for ( int j = 0; j < nDominatorCount; ++j ) + { + const char *pDominatorName = pSrcRule->GetDominator( j ); + if ( !pDestComboOp->HasRawControl( pDominatorName ) ) + { + bMismatch = true; + pStatusFrame->AddOperation( pDominatorName, "Missing raw control for dominance rule" ); + break; + } + } + + int nSuppressedCount = pSrcRule->SuppressedCount(); + for ( int j = 0; j < nSuppressedCount; ++j ) + { + const char *pSuppressedName = pSrcRule->GetSuppressed( j ); + if ( !pDestComboOp->HasRawControl( pSuppressedName ) ) + { + bMismatch = true; + pStatusFrame->AddOperation( pSuppressedName, "Missing raw control for dominance rule" ); + break; + } + } + + if ( bMismatch ) + continue; + + pDestComboOp->AddDominationRule( pSrcRule ); + } +} + + +//----------------------------------------------------------------------------- +// Called when the file open dialog for browsing source files selects something +//----------------------------------------------------------------------------- +static bool ImportCombinationData( vgui::Panel* pParent, CDmeCombinationOperator *pDestComboOp, KeyValues *kv ) +{ + const char *pFileName = kv->GetString( "fullpath", NULL ); + if ( !pFileName ) + return false; + + CDmElement *pRoot; + + { + CDisableUndoScopeGuard sg; + g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pRoot, CR_FORCE_COPY ); + } + + if ( !pRoot ) + return false; + + // Try to find a combination system in the file + CDmeCombinationOperator *pComboOp = CastElement<CDmeCombinationOperator>( pRoot ); + if ( !pComboOp ) + { + pComboOp = pRoot->GetValueElement< CDmeCombinationOperator >( "combinationOperator" ); + } + + if ( pComboOp ) + { + // Actually rename the files, build an error dialog if necessary + COperationFileListFrame *pStatusFrame = new COperationFileListFrame( pParent, + "Import Status", "Status", false, true ); + pStatusFrame->SetOperationColumnHeaderText( "Control Name" ); + + CUndoScopeGuard sg( "Import Combination Rules" ); + if ( kv->FindKey( "ImportControls" ) ) + { + ImportCombinationControls( pDestComboOp, pComboOp, pStatusFrame ); + } + ImportDominationRules( pDestComboOp, pComboOp, pStatusFrame ); + sg.Release(); + + pStatusFrame->DoModal(); + } + + CDisableUndoScopeGuard sg; + g_pDataModel->UnloadFile( pRoot->GetFileId() ); + sg.Release(); + + return true; +} + + +//----------------------------------------------------------------------------- +// +// +// CDmeInputControlListPanel +// +// Implementation below because of scoping issues +// +// +//----------------------------------------------------------------------------- +class CDmeInputControlListPanel : public vgui::ListPanel +{ + DECLARE_CLASS_SIMPLE( CDmeInputControlListPanel, vgui::ListPanel ); + +public: + // constructor, destructor + CDmeInputControlListPanel( vgui::Panel *pParent, const char *pName, CDmeCombinationControlsPanel *pComboPanel ); + + virtual void OnCreateDragData( KeyValues *msg ); + virtual bool IsDroppable( CUtlVector< KeyValues * >& msgList ); + virtual void OnPanelDropped( CUtlVector< KeyValues * >& msgList ); + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + +private: + CDmeCombinationControlsPanel *m_pComboPanel; +}; + + +//----------------------------------------------------------------------------- +// +// +// CDmeRawControlListPanel +// +// Implementation below because of scoping issues +// +// +//----------------------------------------------------------------------------- +class CDmeRawControlListPanel : public vgui::ListPanel +{ + DECLARE_CLASS_SIMPLE( CDmeRawControlListPanel, vgui::ListPanel ); + +public: + // constructor, destructor + CDmeRawControlListPanel( vgui::Panel *pParent, const char *pName, CDmeCombinationControlsPanel *pComboPanel ); + + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + virtual void OnMouseDoublePressed( vgui::MouseCode code ); + +private: + MESSAGE_FUNC( OnNewWrinkleText, "TextNewLine" ); + + CDmeCombinationControlsPanel *m_pComboPanel; + vgui::TextEntry *m_pWrinkleEdit; + bool m_bIsWrinkle; +}; + + +//----------------------------------------------------------------------------- +// +// +// Slider panel +// +// +//----------------------------------------------------------------------------- +class CDmeCombinationControlsPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CDmeCombinationControlsPanel, vgui::EditablePanel ); + +public: + // constructor, destructor + CDmeCombinationControlsPanel( vgui::Panel *pParent, const char *pName ); + virtual ~CDmeCombinationControlsPanel(); + + void SetCombinationOperator( CDmeCombinationOperator *pOp ); + CDmeCombinationOperator* GetCombinationOperator(); + void RefreshCombinationOperator(); + void NotifyDataChanged(); + + const char *GetSelectedControlName(); + void MoveControlInFrontOf( const char *pDragControl, const char *pDropControl ); + + void SetRawControlWrinkleValue( float flWrinkleValue ); + + int GetSelectedInputControlItemId(); + void SelectedInputControlByItemId( int ); + + MESSAGE_FUNC( OnMoveUpInputControl, "MoveUpInputControl" ); + MESSAGE_FUNC( OnMoveDownInputControl, "MoveDownInputControl" ); + MESSAGE_FUNC( OnMoveUp, "MoveUp" ); + MESSAGE_FUNC( OnMoveDown, "MoveDown" ); + +private: + MESSAGE_FUNC_PARAMS( OnOpenContextMenu, "OpenContextMenu", kv ); + MESSAGE_FUNC_PARAMS( OnInputCompleted, "InputCompleted", kv ); + MESSAGE_FUNC( OnGroupControls, "GroupControls" ); + MESSAGE_FUNC( OnUngroupControls, "UngroupControls" ); + MESSAGE_FUNC( OnRenameControl, "RenameControl" ); + MESSAGE_FUNC( OnImportCombination, "ImportCombination" ); + MESSAGE_FUNC_PARAMS( OnFileSelected, "FileSelected", kv ); + MESSAGE_FUNC( OnToggleStereoControl, "ToggleStereoControl" ); + MESSAGE_FUNC( OnToggleEyelidControl, "ToggleEyelidControl" ); + MESSAGE_FUNC( OnToggleWrinkleType, "ToggleWrinkleType" ); + MESSAGE_FUNC_PARAMS( OnItemSelected, "ItemSelected", kv ); + MESSAGE_FUNC_PARAMS( OnItemDeselected, "ItemDeselected", kv ); + + // Cleans up the context menu + void CleanupContextMenu(); + + // Builds a list of selected control + raw control names, returns true if any control is stereo + void BuildSelectedControlLists( bool bOnlyGroupedControls, CUtlVector< CUtlString >& controlNames, CUtlVector< CUtlString >& rawControlNames, bool *pbStereo = NULL, bool *pbEyelid = NULL ); + + // If it finds a duplicate control name, reports an error message and returns it found one + bool HasDuplicateControlName( const char *pControlName, CUtlVector< CUtlString >& retiredControlNames ); + + // Refreshes the list of raw controls + void RefreshRawControlNames(); + + // Called by OnGroupControls and OnRenameControl after we get a new group name + void PerformGroupControls( const char *pGroupedControlName ); + + // Called by OnGroupControls after we get a new group name + void PerformRenameControl( const char *pNewControlName ); + + // Called to open a context-sensitive menu for a particular menu item + void OnOpenRawControlsContextMenu( ); + + // Called to open a context-sensitive menu for a particular menu item + const char* GetSelectedRawControl( ControlIndex_t &nControlIndex ); + + CDmeHandle< CDmeCombinationOperator > m_hCombinationOperator; + vgui::Splitter *m_pSplitter; + CDmeInputControlListPanel *m_pControlList; + CDmeRawControlListPanel *m_pRawControlList; + vgui::DHANDLE< vgui::Menu > m_hContextMenu; +}; + + +//----------------------------------------------------------------------------- +// Sort functions for list panel +//----------------------------------------------------------------------------- +static int __cdecl ControlNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("name"); + const char *string2 = item2.kv->GetString("name"); + return Q_stricmp( string1, string2 ); +} + +static int __cdecl PeakSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + float flPeak1 = item1.kv->GetFloat("peak"); + float flPeak2 = item2.kv->GetFloat("peak"); + if ( flPeak1 < flPeak2 ) + return -1; + if ( flPeak1 > flPeak2 ) + return 1; + return 0; +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CDmeCombinationControlsPanel::CDmeCombinationControlsPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_pSplitter = new vgui::Splitter( this, "ControlsSplitter", vgui::SPLITTER_MODE_VERTICAL, 1 ); + vgui::Panel *pSplitterLeftSide = m_pSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pSplitter->GetChild( 1 ); + + m_pControlList = new CDmeInputControlListPanel( pSplitterLeftSide, "ControlList", this ); + m_pControlList->AddColumnHeader( 0, "name", "Control Name", 150, 0 ); + m_pControlList->AddColumnHeader( 1, "stereo", "Stereo", 70, 0 ); + m_pControlList->AddColumnHeader( 2, "eyelid", "Eyelid", 70, 0 ); + m_pControlList->AddColumnHeader( 3, "default", "Default", 52, 0 ); + m_pControlList->SetSelectIndividualCells( false ); + m_pControlList->SetMultiselectEnabled( true ); + m_pControlList->SetEmptyListText( "No controls" ); + m_pControlList->AddActionSignalTarget( this ); + m_pControlList->SetSortFunc( 0, NULL ); + m_pControlList->SetColumnSortable( 0, false ); + m_pControlList->SetSortFunc( 1, NULL ); + m_pControlList->SetColumnSortable( 1, false ); + m_pControlList->SetSortFunc( 2, NULL ); + m_pControlList->SetColumnSortable( 2, false ); + m_pControlList->SetSortFunc( 3, NULL ); + m_pControlList->SetColumnSortable( 3, false ); + m_pControlList->SetDragEnabled( true ); + m_pControlList->SetDragEnabled( true ); + m_pControlList->SetDropEnabled( true ); + + m_pRawControlList = new CDmeRawControlListPanel( pSplitterRightSide, "RawControlList", this ); + m_pRawControlList->AddColumnHeader( 0, "name", "Raw Control Name", 150, 0 ); + m_pRawControlList->AddColumnHeader( 1, "peak", "Peak", 52, 0 ); + m_pRawControlList->AddColumnHeader( 2, "wrinkletype", "Wrinkle Type", 100, 0 ); + m_pRawControlList->AddColumnHeader( 3, "wrinkle", "Wrinkle Amount", 100, 0 ); + m_pRawControlList->SetSelectIndividualCells( false ); + m_pRawControlList->SetEmptyListText( "No raw controls" ); + m_pRawControlList->AddActionSignalTarget( this ); + m_pRawControlList->SetSortFunc( 0, ControlNameSortFunc ); + m_pRawControlList->SetSortFunc( 1, PeakSortFunc ); + m_pRawControlList->SetSortFunc( 2, NULL ); + m_pRawControlList->SetColumnSortable( 2, false ); + m_pRawControlList->SetSortFunc( 3, NULL ); + m_pRawControlList->SetColumnSortable( 3, false ); + m_pRawControlList->SetSortColumn( 1 ); +} + + +CDmeCombinationControlsPanel::~CDmeCombinationControlsPanel() +{ + CleanupContextMenu(); + SaveUserConfig(); +} + + +//----------------------------------------------------------------------------- +// Cleans up the context menu +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::CleanupContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + m_hContextMenu->MarkForDeletion(); + m_hContextMenu = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Sets the combination operator +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::SetCombinationOperator( CDmeCombinationOperator *pOp ) +{ + if ( pOp != m_hCombinationOperator.Get() ) + { + m_hCombinationOperator = pOp; + RefreshCombinationOperator(); + } +} + +CDmeCombinationOperator* CDmeCombinationControlsPanel::GetCombinationOperator() +{ + return m_hCombinationOperator; +} + + +//----------------------------------------------------------------------------- +// Builds the control list for the combination operator +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::RefreshCombinationOperator() +{ + const CUtlString controlName = GetSelectedControlName(); + + m_pControlList->RemoveAll(); + if ( !m_hCombinationOperator.Get() ) + return; + + int nCount = m_hCombinationOperator->GetControlCount(); + for ( int i = 0; i < nCount; ++i ) + { + bool bIsMultiControl = m_hCombinationOperator->GetRawControlCount(i) > 1; + float flDefault = m_hCombinationOperator->GetRawControlCount(i) == 2 ? 0.5f : 0.0f; + const char *pName = m_hCombinationOperator->GetControlName( i ); + KeyValues *kv = new KeyValues( "node", "name", pName ); + kv->SetString( "stereo", m_hCombinationOperator->IsStereoControl(i) ? "On" : "Off" ); + kv->SetString( "eyelid", m_hCombinationOperator->IsEyelidControl(i) ? "On" : "Off" ); + kv->SetFloat( "default", flDefault ); + kv->SetColor( "cellcolor", bIsMultiControl ? Color( 192, 192, 0, 255 ) : Color( 255, 255, 255, 255 ) ); + const int nItemId = m_pControlList->AddItem( kv, 0, false, false ); + + if ( !Q_strcmp( controlName.Get(), pName ) ) + { + m_pControlList->SetSingleSelectedItem( nItemId ); + } + } + + RefreshRawControlNames(); +} + + +//----------------------------------------------------------------------------- +// Tells any class that cares that the data in this thing has changed +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::NotifyDataChanged() +{ + PostActionSignal( new KeyValues( "DmeElementChanged", "DmeCombinationControlsPanel", 1 ) ); +} + + +//----------------------------------------------------------------------------- +// Refreshes the list of raw controls +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::RefreshRawControlNames() +{ + m_pRawControlList->RemoveAll(); + if ( !m_hCombinationOperator.Get() ) + return; + + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + int nItemID = m_pControlList->GetSelectedItem( 0 ); + + KeyValues *pKeyValues = m_pControlList->GetItem( nItemID ); + const char *pControlName = pKeyValues->GetString( "name" ); + ControlIndex_t nControlIndex = m_hCombinationOperator->FindControlIndex( pControlName ); + if ( nControlIndex < 0 ) + return; + + int nCount = m_hCombinationOperator->GetRawControlCount( nControlIndex ); + for ( int i = 0; i < nCount; ++i ) + { + KeyValues *kv = new KeyValues( "node", "name", m_hCombinationOperator->GetRawControlName( nControlIndex, i ) ); + switch( nCount ) + { + case 0: + case 1: + kv->SetFloat( "peak", 1.0f ); + break; + + case 2: + kv->SetFloat( "peak", i == 0 ? 0.0f : 1.0f ); + break; + + default: + kv->SetFloat( "peak", (float)i / (nCount - 1) ); + break; + } + + float flWrinkleScale = m_hCombinationOperator->GetRawControlWrinkleScale( nControlIndex, i ); + kv->SetString( "wrinkletype", ( flWrinkleScale < 0.0f ) ? "Compress" : "Stretch" ); + kv->SetFloat( "wrinkle", fabs( flWrinkleScale ) ); + m_pRawControlList->AddItem( kv, 0, false, false ); + } + + m_pRawControlList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +const char* CDmeCombinationControlsPanel::GetSelectedRawControl( ControlIndex_t &nControlIndex ) +{ + if ( !m_hCombinationOperator.Get() ) + return NULL; + + nControlIndex = -1; + + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return NULL; + + int nSelectedRawItemCount = m_pRawControlList->GetSelectedItemsCount(); + if ( nSelectedRawItemCount != 1 ) + return NULL; + + int nItemID = m_pControlList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pControlList->GetItem( nItemID ); + const char *pControlName = pKeyValues->GetString( "name" ); + nControlIndex = m_hCombinationOperator->FindControlIndex( pControlName ); + + nItemID = m_pRawControlList->GetSelectedItem( 0 ); + pKeyValues = m_pRawControlList->GetItem( nItemID ); + return pKeyValues->GetString( "name" ); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +const char *CDmeCombinationControlsPanel::GetSelectedControlName() +{ + if ( !m_hCombinationOperator.Get() ) + return NULL; + + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return NULL; + + const int nItemId = m_pControlList->GetSelectedItem( 0 ); + if ( !m_pControlList->IsValidItemID( nItemId ) ) + return NULL; + + KeyValues *pKeyValues = m_pControlList->GetItem( nItemId ); + if ( !pKeyValues ) + return NULL; + + return pKeyValues->GetString( "name" ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CDmeCombinationControlsPanel::GetSelectedInputControlItemId() +{ + return m_pControlList->GetSelectedItem( 0 ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::SelectedInputControlByItemId( int nItemId ) +{ + m_pControlList->SetSingleSelectedItem( nItemId ); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnMoveUp() +{ + ControlIndex_t nControlIndex; + const char *pRawControlName = GetSelectedRawControl( nControlIndex ); + if ( !pRawControlName ) + return; + + m_hCombinationOperator->MoveRawControlUp( nControlIndex, pRawControlName ); + RefreshRawControlNames(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnMoveDown() +{ + ControlIndex_t nControlIndex; + const char *pRawControlName = GetSelectedRawControl( nControlIndex ); + if ( !pRawControlName ) + return; + + m_hCombinationOperator->MoveRawControlDown( nControlIndex, pRawControlName ); + RefreshRawControlNames(); + + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnMoveUpInputControl() +{ + const char *pControlName = GetSelectedControlName(); + if ( !pControlName ) + return; + + m_hCombinationOperator->MoveControlUp( pControlName ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnMoveDownInputControl() +{ + const char *pControlName = GetSelectedControlName(); + if ( !pControlName ) + return; + + m_hCombinationOperator->MoveControlDown( pControlName ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::MoveControlInFrontOf( + const char *pDragControl, + const char *pDropControl ) +{ + m_hCombinationOperator->MoveControlBefore( pDragControl, pDropControl ); + RefreshCombinationOperator(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Toggles the wrinkle type +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnToggleWrinkleType() +{ + ControlIndex_t nControlIndex; + const char *pRawControlName = GetSelectedRawControl( nControlIndex ); + if ( !pRawControlName ) + return; + + float flWrinkleScale = m_hCombinationOperator->GetRawControlWrinkleScale( nControlIndex, pRawControlName ); + m_hCombinationOperator->SetWrinkleScale( nControlIndex, pRawControlName, -flWrinkleScale ); + RefreshRawControlNames(); + m_hCombinationOperator->GenerateWrinkleDeltas(); +} + + +void CDmeCombinationControlsPanel::SetRawControlWrinkleValue( float flWrinkleValue ) +{ + ControlIndex_t nControlIndex; + const char *pRawControlName = GetSelectedRawControl( nControlIndex ); + if ( !pRawControlName ) + return; + + m_hCombinationOperator->SetWrinkleScale( nControlIndex, pRawControlName, flWrinkleValue ); + RefreshRawControlNames(); + m_hCombinationOperator->GenerateWrinkleDeltas(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnOpenRawControlsContextMenu( ) +{ + if ( !m_hCombinationOperator.Get() ) + return; + + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + int nSelectedRawItemCount = m_pRawControlList->GetSelectedItemsCount(); + if ( nSelectedRawItemCount != 1 ) + return; + + m_hContextMenu = new vgui::Menu( this, "ActionMenu" ); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_MoveUp", new KeyValues( "MoveUp" ), this ); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_MoveDown", new KeyValues( "MoveDown" ), this ); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_ToggleWrinkleType", new KeyValues( "ToggleWrinkleType" ), this ); + + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnOpenContextMenu( KeyValues *kv ) +{ + CleanupContextMenu(); + if ( !m_hCombinationOperator.Get() ) + return; + + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pRawControlList ) + { + OnOpenRawControlsContextMenu(); + return; + } + + if ( pPanel != m_pControlList ) + return; + + bool bGroupedControls = false; + bool bStereoControls = false; + bool bEyelidControls = false; + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + for ( int i = 0; i < nSelectedItemCount; ++i ) + { + int nItemID = m_pControlList->GetSelectedItem( i ); + + KeyValues *pKeyValues = m_pControlList->GetItem( nItemID ); + const char *pControlName = pKeyValues->GetString( "name" ); + ControlIndex_t nControlIndex = m_hCombinationOperator->FindControlIndex( pControlName ); + if ( nControlIndex < 0 ) + continue; + + if ( m_hCombinationOperator->GetRawControlCount( nControlIndex ) > 1 ) + { + bGroupedControls = true; + } + + if ( m_hCombinationOperator->IsStereoControl( nControlIndex ) ) + { + bStereoControls = true; + } + + if ( m_hCombinationOperator->IsEyelidControl( nControlIndex ) ) + { + bEyelidControls = true; + } + } + + m_hContextMenu = new vgui::Menu( this, "ActionMenu" ); + + if ( nSelectedItemCount > 1 ) + { + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_GroupControls", new KeyValues( "GroupControls" ), this ); + } + + if ( bGroupedControls ) + { + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_UngroupControls", new KeyValues( "UngroupControls" ), this ); + } + + if ( nSelectedItemCount >= 1 ) + { + int nMenuItemID = m_hContextMenu->AddCheckableMenuItem( "#DmeCombinationSystemEditor_StereoControl", new KeyValues( "ToggleStereoControl" ), this ); + m_hContextMenu->SetMenuItemChecked( nMenuItemID, bStereoControls ); + } + + if ( nSelectedItemCount >= 1 ) + { + int nMenuItemID = m_hContextMenu->AddCheckableMenuItem( "#DmeCombinationSystemEditor_EyelidControl", new KeyValues( "ToggleEyelidControl" ), this ); + m_hContextMenu->SetMenuItemChecked( nMenuItemID, bEyelidControls ); + } + + if ( nSelectedItemCount == 1 ) + { + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_RenameControl", new KeyValues( "RenameControl" ), this ); + } + + if ( nSelectedItemCount >= 1 ) + { + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_MoveUp", new KeyValues( "MoveUpInputControl" ), this ); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_MoveDown", new KeyValues( "MoveDownInputControl" ), this ); + } + + if ( nSelectedItemCount >= 1 || bGroupedControls ) + { + m_hContextMenu->AddSeparator(); + } + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_Import", new KeyValues( "ImportCombination" ), this ); + + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pControlList ) + { + RefreshRawControlNames(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnItemDeselected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pControlList ) + { + RefreshRawControlNames(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Builds a list of selected control + raw control names, returns true if any control is stereo +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::BuildSelectedControlLists( + bool bOnlyGroupedControls, + CUtlVector< CUtlString >& controlNames, CUtlVector< CUtlString >& rawControlNames, + bool *pbStereo, bool *pbEyelid ) +{ + bool bIsStereo = false; + bool bIsEyelid = false; + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + for ( int i = 0; i < nSelectedItemCount; ++i ) + { + int nItemID = m_pControlList->GetSelectedItem( i ); + + KeyValues *pKeyValues = m_pControlList->GetItem( nItemID ); + const char *pControlName = pKeyValues->GetString( "name" ); + ControlIndex_t nControlIndex = m_hCombinationOperator->FindControlIndex( pControlName ); + if ( nControlIndex < 0 ) + continue; + + int nRawControlCount = m_hCombinationOperator->GetRawControlCount( nControlIndex ); + if ( bOnlyGroupedControls && ( nRawControlCount <= 1 ) ) + continue; + + if ( m_hCombinationOperator->IsStereoControl( nControlIndex ) ) + { + bIsStereo = true; + } + + if ( m_hCombinationOperator->IsEyelidControl( nControlIndex ) ) + { + bIsEyelid = true; + } + + controlNames.AddToTail( pControlName ); + for ( int j = 0; j < nRawControlCount; ++j ) + { + rawControlNames.AddToTail( m_hCombinationOperator->GetRawControlName( nControlIndex, j ) ); + } + } + + if ( pbStereo ) + { + *pbStereo = bIsStereo; + } + + if ( pbEyelid ) + { + *pbEyelid = bIsEyelid; + } +} + + +//----------------------------------------------------------------------------- +// If it finds a duplicate control name, reports an error message and returns it found one +//----------------------------------------------------------------------------- +bool CDmeCombinationControlsPanel::HasDuplicateControlName( const char *pControlName, CUtlVector< CUtlString >& retiredControlNames ) +{ + int i; + int nRetiredControlNameCount = retiredControlNames.Count(); + for ( i = 0; i < nRetiredControlNameCount; ++i ) + { + if ( !Q_stricmp( retiredControlNames[i], pControlName ) ) + break; + } + if ( i == nRetiredControlNameCount ) + { + // no match + if ( m_hCombinationOperator->FindControlIndex( pControlName ) >= 0 ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "#DmeCombinationSystemEditor_DuplicateNameTitle", "#DmeCombinationSystemEditor_DuplicateNameText", this ); + pError->DoModal(); + return true; + } + } + return false; +} + + +//----------------------------------------------------------------------------- +// Called by OnGroupControls and OnRenameControl after we get a new group name +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::PerformGroupControls( const char *pGroupedControlName ) +{ + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + if ( nSelectedItemCount <= 1 ) + return; + + // Build lists of selected controls + raw controls + CUtlVector< CUtlString > controlNames; + CUtlVector< CUtlString > rawControlNames; + bool bIsStereo = false; + bool bIsEyelid = false; + BuildSelectedControlLists( false, controlNames, rawControlNames, &bIsStereo, &bIsEyelid ); + + // NOTE: It's illegal to use a grouped control name which already exists + // assuming it's not in the list of grouped control names we're going to group together + if ( HasDuplicateControlName( pGroupedControlName, controlNames ) ) + return; + + // Delete old controls + int nRetiredControlNameCount = controlNames.Count(); + for ( int i = 0; i < nRetiredControlNameCount; ++i ) + { + m_hCombinationOperator->RemoveControl( controlNames[i] ); + } + + // Create new control + ControlIndex_t nNewControl = m_hCombinationOperator->FindOrCreateControl( pGroupedControlName, bIsStereo ); + m_hCombinationOperator->SetEyelidControl( nNewControl, bIsEyelid ); + int nGroupedControlCount = rawControlNames.Count(); + for ( int i = 0; i < nGroupedControlCount; ++i ) + { + m_hCombinationOperator->AddRawControl( nNewControl, rawControlNames[i] ); + } + + RefreshCombinationOperator(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called by OnGroupControls after we get a new group name +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::PerformRenameControl( const char *pNewControlName ) +{ + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + int nItemID = m_pControlList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pControlList->GetItem( nItemID ); + const char *pControlName = pKeyValues->GetString( "name" ); + ControlIndex_t nControlIndex = m_hCombinationOperator->FindControlIndex( pControlName ); + if ( nControlIndex < 0 ) + return; + + // NOTE: It's illegal to use a grouped control name which already exists + // assuming it's not in the list of grouped control names we're going to group together + ControlIndex_t nFoundIndex = m_hCombinationOperator->FindControlIndex( pNewControlName ); + if ( nFoundIndex >= 0 && nFoundIndex != nControlIndex ) + return; + + m_hCombinationOperator->SetControlName( nControlIndex, pNewControlName ); + + RefreshCombinationOperator(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called by OnGroupControls after we get a new group name +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnInputCompleted( KeyValues *pKeyValues ) +{ + const char *pControlName = pKeyValues->GetString( "text", NULL ); + if ( !pControlName || !pControlName[0] ) + return; + + if ( pKeyValues->FindKey( "OnGroupControls" ) ) + { + PerformGroupControls( pControlName ); + return; + } + + if ( pKeyValues->FindKey( "OnRenameControl" ) ) + { + PerformRenameControl( pControlName ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Group controls together +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnGroupControls( ) +{ + vgui::InputDialog *pInput = new vgui::InputDialog( this, "Group Controls", "Enter name of grouped control" ); + pInput->SetMultiline( false ); + pInput->DoModal( new KeyValues( "OnGroupControls" ) ); +} + + +//----------------------------------------------------------------------------- +// Ungroup controls from each other +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnUngroupControls( ) +{ + // Build lists of selected controls + raw controls + CUtlVector< CUtlString > controlNames; + CUtlVector< CUtlString > rawControlNames; + bool bIsStereo = false; + bool bIsEyelid = false; + BuildSelectedControlLists( true, controlNames, rawControlNames, &bIsStereo, &bIsEyelid ); + + // NOTE: It's illegal to use a grouped control name which already exists + // assuming it's not in the list of grouped control names we're going to group together + int nRawControlCount = rawControlNames.Count(); + for ( int i = 0; i < nRawControlCount; ++i ) + { + if ( HasDuplicateControlName( rawControlNames[i], controlNames ) ) + return; + } + + // Delete old controls + int nRetiredControlNameCount = controlNames.Count(); + for ( int i = 0; i < nRetiredControlNameCount; ++i ) + { + m_hCombinationOperator->RemoveControl( controlNames[i] ); + } + + // Create new control (this will also create raw controls with the same name) + int nGroupedControlCount = rawControlNames.Count(); + for ( int i = 0; i < nGroupedControlCount; ++i ) + { + const int nControlIndex = m_hCombinationOperator->FindOrCreateControl( rawControlNames[i], bIsStereo, true ); + m_hCombinationOperator->SetEyelidControl( nControlIndex, bIsEyelid ); + } + + RefreshCombinationOperator(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Ungroup controls from each other +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnToggleStereoControl( ) +{ + // Build lists of selected controls + raw controls + // Yeah, this isn't super efficient, but this UI is not going to be super-polished + CUtlVector< CUtlString > controlNames; + CUtlVector< CUtlString > rawControlNames; + + bool bIsStereo = false; + BuildSelectedControlLists( false, controlNames, rawControlNames, &bIsStereo ); + + int nControlCount = controlNames.Count(); + for ( int i = 0; i < nControlCount; ++i ) + { + ControlIndex_t nControlIndex = m_hCombinationOperator->FindControlIndex( controlNames[i] ); + m_hCombinationOperator->SetStereoControl( nControlIndex, !bIsStereo ); + } + + RefreshCombinationOperator(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Toggle Eyelid-Ness +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnToggleEyelidControl() +{ + // Build lists of selected controls + raw controls + // Yeah, this isn't super efficient, but this UI is not going to be super-polished + CUtlVector< CUtlString > controlNames; + CUtlVector< CUtlString > rawControlNames; + + bool bIsEyelid = false; + BuildSelectedControlLists( false, controlNames, rawControlNames, NULL, &bIsEyelid ); + + int nControlCount = controlNames.Count(); + for ( int i = 0; i < nControlCount; ++i ) + { + ControlIndex_t nControlIndex = m_hCombinationOperator->FindControlIndex( controlNames[i] ); + m_hCombinationOperator->SetEyelidControl( nControlIndex, !bIsEyelid ); + } + + RefreshCombinationOperator(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Rename a control +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnRenameControl() +{ + int nSelectedItemCount = m_pControlList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + vgui::InputDialog *pInput = new vgui::InputDialog( this, "Rename Control", "Enter new name of control" ); + pInput->SetMultiline( false ); + pInput->DoModal( new KeyValues( "OnRenameControl" ) ); +} + + +//----------------------------------------------------------------------------- +// Called when the file open dialog for browsing source files selects something +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnFileSelected( KeyValues *kv ) +{ + if ( ImportCombinationData( this, m_hCombinationOperator, kv ) ) + { + RefreshCombinationOperator(); + NotifyDataChanged(); + } +} + + +//----------------------------------------------------------------------------- +// Import combination controls + domination rules +//----------------------------------------------------------------------------- +void CDmeCombinationControlsPanel::OnImportCombination() +{ + char pStartingDir[MAX_PATH]; + GetModContentSubdirectory( "models", pStartingDir, sizeof(pStartingDir) ); + + vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( this, "Select File to Import", true, new KeyValues( "ImportControls" ) ); + pDialog->SetStartDirectoryContext( "combination_system_import", pStartingDir ); + pDialog->AddFilter( "*.dmx", "Exported model file (*.dmx)", true ); + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( false ); +} + + +//----------------------------------------------------------------------------- +// +// +// CDmeInputControlListPanel +// +// Declaration above because of scoping issues +// +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CDmeInputControlListPanel::CDmeInputControlListPanel( vgui::Panel *pParent, const char *pName, CDmeCombinationControlsPanel *pComboPanel ) : + BaseClass( pParent, pName ), m_pComboPanel( pComboPanel ) +{ +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmeInputControlListPanel::OnCreateDragData( KeyValues *msg ) +{ + const char *const pControlName = m_pComboPanel->GetSelectedControlName(); + if ( pControlName ) + { + msg->SetString( "inputControl", pControlName ); + msg->SetInt( "selfDroppable", 1 ); + } +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +bool CDmeInputControlListPanel::IsDroppable( CUtlVector< KeyValues * >& msgList ) +{ + if ( msgList.Count() > 0 ) + { + KeyValues *pData( msgList[ 0 ] ); + if ( pData->GetPtr( "panel", NULL ) == this && m_pComboPanel ) + { + if ( pData->GetString( "inputControl" ) && pData->GetInt( "selfDroppable" ) ) + { + return true; + } + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmeInputControlListPanel::OnPanelDropped( CUtlVector< KeyValues * >& msgList ) +{ + if ( msgList.Count() > 0 ) + { + KeyValues *pData( msgList[ 0 ] ); + if ( pData->GetPtr( "panel", NULL ) == this && m_pComboPanel ) + { + const char *const pDragControl( pData->GetString( "inputControl" ) ); + if ( pDragControl ) + { + int x; + int y; + + vgui::input()->GetCursorPos( x, y ); + + int row; + int column; + GetCellAtPos( x, y, row, column ); + + KeyValues *pKeyValues = GetItem( GetItemIDFromRow( row ) ); + if ( pKeyValues ) + { + const char *pDropControl = pKeyValues->GetString( "name" ); + if ( pDropControl ) + { + m_pComboPanel->MoveControlInFrontOf( pDragControl, pDropControl ); + } + } + } + } + } +} + + +void CDmeInputControlListPanel::OnKeyCodeTyped( vgui::KeyCode code ) +{ + // Not sure how to handle 'edit' mode... the relevant stuff is private + if ( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) ) + { + if ( code == KEY_UP ) + { + const int nItemId = m_pComboPanel->GetSelectedInputControlItemId(); + m_pComboPanel->OnMoveUpInputControl(); + vgui::ListPanel::OnKeyCodeTyped( code ); + m_pComboPanel->SelectedInputControlByItemId( nItemId ); + return; + } + else if ( code == KEY_DOWN ) + { + const int nItemId = m_pComboPanel->GetSelectedInputControlItemId(); + m_pComboPanel->OnMoveDownInputControl(); + vgui::ListPanel::OnKeyCodeTyped( code ); + m_pComboPanel->SelectedInputControlByItemId( nItemId ); + return; + } + } + + vgui::ListPanel::OnKeyCodeTyped( code ); +} + + +//----------------------------------------------------------------------------- +// +// +// CDmeRawControlListPanel +// +// Declaration above because of scoping issues +// +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CDmeRawControlListPanel::CDmeRawControlListPanel( vgui::Panel *pParent, const char *pName, CDmeCombinationControlsPanel *pComboPanel ) : + BaseClass( pParent, pName ), m_pComboPanel( pComboPanel ) +{ + m_pWrinkleEdit = new vgui::TextEntry( this, "WrinkleEdit" ); + m_pWrinkleEdit->SetVisible( false ); + m_pWrinkleEdit->AddActionSignalTarget( this ); + m_pWrinkleEdit->SetAllowNumericInputOnly( true ); +} + +void CDmeRawControlListPanel::OnKeyCodeTyped( vgui::KeyCode code ) +{ + // Not sure how to handle 'edit' mode... the relevant stuff is private + if ( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) ) + { + if ( code == KEY_UP ) + { + m_pComboPanel->OnMoveUp(); + } + else if ( code == KEY_DOWN ) + { + m_pComboPanel->OnMoveDown(); + } + } + + vgui::ListPanel::OnKeyCodeTyped( code ); +} + +void CDmeRawControlListPanel::OnMouseDoublePressed( vgui::MouseCode code ) +{ + if ( code != MOUSE_LEFT ) + { + BaseClass::OnMouseDoublePressed( code ); + return; + } + + int nNumSelected = GetSelectedItemsCount(); + if ( IsInEditMode() || nNumSelected != 1 ) + return; + + m_pWrinkleEdit->SetVisible( true ); + m_pWrinkleEdit->SendNewLine( true ); + + // Always edit column 3, which contains the wrinkle amount + int nEditingItem = GetSelectedItem( 0 ); + KeyValues *pKeyValues = GetItem( nEditingItem ); + float flWrinkleValue = pKeyValues->GetFloat( "wrinkle" ); + + m_bIsWrinkle = !Q_stricmp( pKeyValues->GetString( "wrinkletype" ), "Wrinkle" ); + + char buf[64]; + Q_snprintf( buf, sizeof(buf), "%f", flWrinkleValue ); + m_pWrinkleEdit->SetText( buf ); + + EnterEditMode( nEditingItem, 3, m_pWrinkleEdit ); +} + +void CDmeRawControlListPanel::OnNewWrinkleText() +{ + LeaveEditMode(); + + char szEditText[MAX_PATH]; + m_pWrinkleEdit->GetText( szEditText, MAX_PATH ); + m_pWrinkleEdit->SetVisible( false ); + + float flWrinkleScale = atof( szEditText ); + if ( m_bIsWrinkle ) + { + flWrinkleScale = -flWrinkleScale; + } + m_pComboPanel->SetRawControlWrinkleValue( flWrinkleScale ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Multiselect for raw controls +// +//----------------------------------------------------------------------------- +class CRawControlPickerFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CRawControlPickerFrame, vgui::Frame ); + +public: + CRawControlPickerFrame( vgui::Panel *pParent, const char *pTitle ); + ~CRawControlPickerFrame(); + + // Sets the current scene + animation list + void DoModal( CDmeCombinationOperator *pCombinationOperator, CDmeCombinationDominationRule *pRule, bool bSuppressed, KeyValues *pContextKeyValues ); + + // Inherited from Frame + virtual void OnCommand( const char *pCommand ); + +private: + // Refreshes the list of raw controls + void RefreshRawControlNames( CDmeCombinationOperator *pCombinationOperator, CDmeCombinationDominationRule *pRule, bool bSuppressed ); + void CleanUpMessage(); + + vgui::ListPanel *m_pRawControlList; + vgui::Button *m_pOpenButton; + vgui::Button *m_pCancelButton; + KeyValues *m_pContextKeyValues; +}; + +CRawControlPickerFrame::CRawControlPickerFrame( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "RawControlPickerFrame" ) +{ + SetDeleteSelfOnClose( true ); + m_pContextKeyValues = NULL; + + m_pRawControlList = new vgui::ListPanel( this, "RawControlList" ); + m_pRawControlList->AddColumnHeader( 0, "name", "Raw Control Name", 52, 0 ); + m_pRawControlList->SetSelectIndividualCells( false ); + m_pRawControlList->SetEmptyListText( "No raw controls" ); + m_pRawControlList->AddActionSignalTarget( this ); + m_pRawControlList->SetSortFunc( 0, ControlNameSortFunc ); + m_pRawControlList->SetSortColumn( 0 ); + + m_pOpenButton = new vgui::Button( this, "OkButton", "#MessageBox_OK", this, "Ok" ); + m_pCancelButton = new vgui::Button( this, "CancelButton", "#MessageBox_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/dmecombinationsystemeditor_rawcontrolpickerframe.res" ); + + SetTitle( pTitle, false ); +} + +CRawControlPickerFrame::~CRawControlPickerFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Refreshes the list of raw controls +//----------------------------------------------------------------------------- +void CRawControlPickerFrame::RefreshRawControlNames( CDmeCombinationOperator *pCombinationOperator, CDmeCombinationDominationRule *pRule, bool bChooseSuppressed ) +{ + m_pRawControlList->RemoveAll(); + if ( !pCombinationOperator ) + return; + + int nCount = pCombinationOperator->GetRawControlCount( ); + for ( int i = 0; i < nCount; ++i ) + { + const char *pRawControl = pCombinationOperator->GetRawControlName( i ); + + // Hide controls that are in the other part of the rule + bool bIsDominator = pRule->HasDominatorControl( pRawControl ); + bool bIsSuppressed = pRule->HasSuppressedControl( pRawControl ); + Assert( !bIsDominator || !bIsSuppressed ); + if ( ( bChooseSuppressed && bIsDominator ) || ( !bChooseSuppressed && bIsSuppressed ) ) + continue; + + KeyValues *kv = new KeyValues( "node", "name", pCombinationOperator->GetRawControlName( i ) ); + int nItemID = m_pRawControlList->AddItem( kv, 0, false, false ); + if ( ( bChooseSuppressed && bIsSuppressed ) || ( !bChooseSuppressed && bIsDominator ) ) + { + m_pRawControlList->AddSelectedItem( nItemID ); + } + } + + m_pRawControlList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CRawControlPickerFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Sets the current scene + animation list +//----------------------------------------------------------------------------- +void CRawControlPickerFrame::DoModal( CDmeCombinationOperator *pCombinationOperator, + CDmeCombinationDominationRule *pRule, bool bSuppressed, KeyValues *pContextKeyValues ) +{ + CleanUpMessage(); + RefreshRawControlNames( pCombinationOperator, pRule, bSuppressed ); + m_pContextKeyValues = pContextKeyValues; + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CRawControlPickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Ok" ) ) + { + KeyValues *pActionKeys = new KeyValues( "RawControlPicked" ); + KeyValues *pControlList = pActionKeys->FindKey( "rawControls", true ); + + int nSelectedItemCount = m_pRawControlList->GetSelectedItemsCount(); + for ( int i = 0; i < nSelectedItemCount; ++i ) + { + int nItemID = m_pRawControlList->GetSelectedItem( i ); + KeyValues *pKeyValues = m_pRawControlList->GetItem( nItemID ); + const char *pControlName = pKeyValues->GetString( "name" ); + + pControlList->SetString( pControlName, pControlName ); + } + + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + + // This prevents them from being deleted later + m_pContextKeyValues = NULL; + } + + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// Domination rules panel +//----------------------------------------------------------------------------- +class CDmeCombinationDominationRulesPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CDmeCombinationDominationRulesPanel, vgui::EditablePanel ); + +public: + // constructor, destructor + CDmeCombinationDominationRulesPanel( vgui::Panel *pParent, const char *pName ); + virtual ~CDmeCombinationDominationRulesPanel(); + + void SetCombinationOperator( CDmeCombinationOperator *pOp ); + void RefreshCombinationOperator(); + void NotifyDataChanged(); + +private: + MESSAGE_FUNC_PARAMS( OnOpenContextMenu, "OpenContextMenu", kv ); + MESSAGE_FUNC_PARAMS( OnRawControlPicked, "RawControlPicked", kv ); + MESSAGE_FUNC( OnAddDominationRule, "AddDominationRule" ); + MESSAGE_FUNC( OnRemoveDominationRule, "RemoveDominationRule" ); + MESSAGE_FUNC( OnDuplicateSuppressed, "DuplicateSuppressed" ); + MESSAGE_FUNC( OnDuplicateDominators, "DuplicateDominators" ); + MESSAGE_FUNC( OnSelectDominators, "SelectDominators" ); + MESSAGE_FUNC( OnSelectSuppressed, "SelectSuppressed" ); + MESSAGE_FUNC( OnMoveUp, "MoveUp" ); + MESSAGE_FUNC( OnMoveDown, "MoveDown" ); + MESSAGE_FUNC( OnImportDominationRules, "ImportDominationRules" ); + MESSAGE_FUNC_PARAMS( OnFileSelected, "FileSelected", kv ); + + // Cleans up the context menu + void CleanupContextMenu(); + + // Selects a particular domination rule + void SelectRule( CDmeCombinationDominationRule* pRule ); + + // Returns the currently selected rule + CDmeCombinationDominationRule* GetSelectedRule( ); + + CDmeHandle< CDmeCombinationOperator > m_hCombinationOperator; + vgui::ListPanel *m_pDominationRulesList; + vgui::DHANDLE< vgui::Menu > m_hContextMenu; +}; + + +static int __cdecl DominatorNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + int i1 = item1.kv->GetInt("index"); + int i2 = item2.kv->GetInt("index"); + return i1 - i2; +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CDmeCombinationDominationRulesPanel::CDmeCombinationDominationRulesPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_pDominationRulesList = new vgui::ListPanel( this, "DominationRulesList" ); + m_pDominationRulesList->AddColumnHeader( 0, "suppressed", "Suppress", 100, 0 ); + m_pDominationRulesList->AddColumnHeader( 1, "dominator", "Dominate", 100, 0 ); + m_pDominationRulesList->AddActionSignalTarget( this ); + m_pDominationRulesList->SetSortFunc( 0, DominatorNameSortFunc ); + m_pDominationRulesList->SetSortFunc( 1, DominatorNameSortFunc ); + m_pDominationRulesList->SetSortColumn( 0 ); + m_pDominationRulesList->SetEmptyListText("No domination rules."); + m_pDominationRulesList->SetMultiselectEnabled( false ); +} + +CDmeCombinationDominationRulesPanel::~CDmeCombinationDominationRulesPanel() +{ + CleanupContextMenu(); + SaveUserConfig(); +} + + +//----------------------------------------------------------------------------- +// Cleans up the context menu +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::CleanupContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + m_hContextMenu->MarkForDeletion(); + m_hContextMenu = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Sets the combination operator +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::SetCombinationOperator( CDmeCombinationOperator *pOp ) +{ + if ( pOp != m_hCombinationOperator.Get() ) + { + m_hCombinationOperator = pOp; + RefreshCombinationOperator(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Basic sort function, for use in qsort +//----------------------------------------------------------------------------- +static int __cdecl ControlNameSortFunc(const void *elem1, const void *elem2) +{ + const char *pItem1 = *((const char **) elem1); + const char *pItem2 = *((const char **) elem2); + return Q_stricmp( pItem1, pItem2 ); +} + + +//----------------------------------------------------------------------------- +// Builds the list of animations +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::RefreshCombinationOperator() +{ + m_pDominationRulesList->RemoveAll(); + if ( !m_hCombinationOperator.Get() ) + return; + + char pTemp[1024]; + int nCount = m_hCombinationOperator->DominationRuleCount(); + for ( int i = 0; i < nCount; ++i ) + { + CDmeCombinationDominationRule *pRule = m_hCombinationOperator->GetDominationRule( i ); + + KeyValues *pItemKeys = new KeyValues( "node" ); + + int nLen = 0; + int nControlCount = pRule->DominatorCount(); + pTemp[0] = 0; + const char **ppStrings = (const char**)_alloca( nControlCount * sizeof(const char*) ); + for ( int j = 0; j < nControlCount; ++j ) + { + ppStrings[j] = pRule->GetDominator(j); + } + qsort( ppStrings, (size_t)nControlCount, (size_t)sizeof(char*), ControlNameSortFunc ); + for ( int j = 0; j < nControlCount; ++j ) + { + nLen += Q_snprintf( &pTemp[nLen], sizeof(pTemp) - nLen, "%s ", ppStrings[j] ); + } + + pItemKeys->SetString( "dominator", pTemp ); + + nLen = 0; + nControlCount = pRule->SuppressedCount(); + pTemp[0] = 0; + ppStrings = (const char**)_alloca( nControlCount * sizeof(const char*) ); + for ( int j = 0; j < nControlCount; ++j ) + { + ppStrings[j] = pRule->GetSuppressed(j); + } + qsort( ppStrings, (size_t)nControlCount, (size_t)sizeof(char*), ControlNameSortFunc ); + for ( int j = 0; j < nControlCount; ++j ) + { + nLen += Q_snprintf( &pTemp[nLen], sizeof(pTemp) - nLen, "%s ", ppStrings[j] ); + } + pItemKeys->SetString( "suppressed", pTemp ); + pItemKeys->SetInt( "index", i ); + SetElementKeyValue( pItemKeys, "rule", pRule ); + + m_pDominationRulesList->AddItem( pItemKeys, 0, false, false ); + } + + m_pDominationRulesList->SortList(); +} + + +void CDmeCombinationDominationRulesPanel::NotifyDataChanged() +{ + PostActionSignal( new KeyValues( "DmeElementChanged", "DmeCombinationDominationRulesPanel", 2 ) ); +} + + +//----------------------------------------------------------------------------- +// Returns the currently selected domination rule +//----------------------------------------------------------------------------- +CDmeCombinationDominationRule* CDmeCombinationDominationRulesPanel::GetSelectedRule( ) +{ + if ( !m_hCombinationOperator.Get() ) + return NULL; + + int nSelectedItemCount = m_pDominationRulesList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return NULL; + + int nItemID = m_pDominationRulesList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pDominationRulesList->GetItem( nItemID ); + return GetElementKeyValue<CDmeCombinationDominationRule>( pKeyValues, "rule" ); +} + + +//----------------------------------------------------------------------------- +// Reorder rules +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnMoveUp( ) +{ + CDmeCombinationDominationRule* pRule = GetSelectedRule( ); + if ( !pRule ) + return; + + m_hCombinationOperator->MoveDominationRuleUp( pRule ); + RefreshCombinationOperator(); + NotifyDataChanged(); +} + +void CDmeCombinationDominationRulesPanel::OnMoveDown( ) +{ + CDmeCombinationDominationRule* pRule = GetSelectedRule( ); + if ( !pRule ) + return; + + m_hCombinationOperator->MoveDominationRuleDown( pRule ); + RefreshCombinationOperator(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called when the file open dialog for browsing source files selects something +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnFileSelected( KeyValues *kv ) +{ + if ( ImportCombinationData( this, m_hCombinationOperator, kv ) ) + { + RefreshCombinationOperator(); + NotifyDataChanged(); + } +} + + +//----------------------------------------------------------------------------- +// Import domination rules +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnImportDominationRules() +{ + char pStartingDir[MAX_PATH]; + GetModContentSubdirectory( "models", pStartingDir, sizeof(pStartingDir) ); + + vgui::FileOpenDialog *pDialog = new vgui::FileOpenDialog( this, "Select File to Import", true, new KeyValues( "ImportDominationRules" ) ); + pDialog->SetStartDirectoryContext( "combination_system_import", pStartingDir ); + pDialog->AddFilter( "*.dmx", "Exported model file (*.dmx)", true ); + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( false ); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnOpenContextMenu( KeyValues *kv ) +{ + CleanupContextMenu(); + if ( !m_hCombinationOperator.Get() ) + return; + + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel != m_pDominationRulesList ) + return; + + int nSelectedItemCount = m_pDominationRulesList->GetSelectedItemsCount(); + + m_hContextMenu = new vgui::Menu( this, "ActionMenu" ); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_AddDominationRule", new KeyValues( "AddDominationRule" ), this ); + if ( nSelectedItemCount > 0 ) + { + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_RemoveDominationRule", new KeyValues( "RemoveDominationRule" ), this ); + } + + if ( nSelectedItemCount == 1 ) + { + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_DuplicateSuppressed", new KeyValues( "DuplicateSuppressed" ), this ); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_DuplicateDominators", new KeyValues( "DuplicateDominators" ), this ); + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_SelectSuppressed", new KeyValues( "SelectSuppressed" ), this ); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_SelectDominators", new KeyValues( "SelectDominators" ), this ); + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_MoveUp", new KeyValues( "MoveUp" ), this ); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_MoveDown", new KeyValues( "MoveDown" ), this ); + } + + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmeCombinationSystemEditor_ImportDomination", new KeyValues( "ImportDominationRules" ), this ); + + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Selects a particular domination rule +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::SelectRule( CDmeCombinationDominationRule* pRule ) +{ + for ( int nItemID = m_pDominationRulesList->FirstItem(); nItemID != m_pDominationRulesList->InvalidItemID(); nItemID = m_pDominationRulesList->NextItem( nItemID ) ) + { + KeyValues *pKeyValues = m_pDominationRulesList->GetItem( nItemID ); + if ( pRule == GetElementKeyValue<CDmeCombinationDominationRule>( pKeyValues, "rule" ) ) + { + m_pDominationRulesList->SetSingleSelectedItem( nItemID ); + break; + } + } +} + +//----------------------------------------------------------------------------- +// Called by the context menu to add dominator rules +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnAddDominationRule( ) +{ + CDmeCombinationDominationRule* pRule = m_hCombinationOperator->AddDominationRule(); + RefreshCombinationOperator(); + SelectRule( pRule ); + OnSelectSuppressed(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Duplicate suppressed +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnDuplicateSuppressed() +{ + CDmeCombinationDominationRule *pSrcRule = GetSelectedRule(); + if ( !pSrcRule ) + return; + + CDmeCombinationDominationRule* pRule = m_hCombinationOperator->AddDominationRule(); + RefreshCombinationOperator(); + SelectRule( pRule ); + for ( int i = 0; i < pSrcRule->SuppressedCount(); ++i ) + { + pRule->AddSuppressed( pSrcRule->GetSuppressed( i ) ); + } + OnSelectDominators(); + NotifyDataChanged(); +} + +void CDmeCombinationDominationRulesPanel::OnDuplicateDominators() +{ + CDmeCombinationDominationRule *pSrcRule = GetSelectedRule(); + if ( !pSrcRule ) + return; + + CDmeCombinationDominationRule* pRule = m_hCombinationOperator->AddDominationRule(); + RefreshCombinationOperator(); + SelectRule( pRule ); + for ( int i = 0; i < pSrcRule->DominatorCount(); ++i ) + { + pRule->AddDominator( pSrcRule->GetDominator( i ) ); + } + OnSelectSuppressed(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called by the context menu to remove dominator rules +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnRemoveDominationRule( ) +{ + int nSelectedItemCount = m_pDominationRulesList->GetSelectedItemsCount(); + for ( int i = 0; i < nSelectedItemCount; ++i ) + { + int nItemID = m_pDominationRulesList->GetSelectedItem( i ); + KeyValues *pKeyValues = m_pDominationRulesList->GetItem( nItemID ); + + CDmeCombinationDominationRule *pRule = GetElementKeyValue<CDmeCombinationDominationRule>( pKeyValues, "rule" ); + if ( pRule ) + { + m_hCombinationOperator->RemoveDominationRule( pRule ); + } + } + + RefreshCombinationOperator(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called by the pickers made in OnSelectDominators + OnSelectSuppressed +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnRawControlPicked( KeyValues *pKeyValues ) +{ + KeyValues *pControlList = pKeyValues->FindKey( "rawControls" ); + if ( !pControlList ) + return; + + KeyValues *pContextKeys = pKeyValues->FindKey( "OnSelectDominators" ); + if ( pContextKeys ) + { + CDmeCombinationDominationRule *pRule = GetElementKeyValue<CDmeCombinationDominationRule>( pContextKeys, "rule" ); + if ( !pRule ) + return; + + pRule->RemoveAllDominators(); + for ( KeyValues *pKey = pControlList->GetFirstValue(); pKey; pKey = pKey->GetNextValue() ) + { + pRule->AddDominator( pKey->GetName() ); + } + RefreshCombinationOperator(); + NotifyDataChanged(); + return; + } + + pContextKeys = pKeyValues->FindKey( "OnSelectSuppressed" ); + if ( pContextKeys ) + { + CDmeCombinationDominationRule *pRule = GetElementKeyValue<CDmeCombinationDominationRule>( pContextKeys, "rule" ); + if ( !pRule ) + return; + + pRule->RemoveAllSuppressed(); + for ( KeyValues *pKey = pControlList->GetFirstValue(); pKey; pKey = pKey->GetNextValue() ) + { + pRule->AddSuppressed( pKey->GetName() ); + } + RefreshCombinationOperator(); + NotifyDataChanged(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called by the context menu to change dominator rules +//----------------------------------------------------------------------------- +void CDmeCombinationDominationRulesPanel::OnSelectDominators( ) +{ + CDmeCombinationDominationRule *pRule = GetSelectedRule(); + if ( !pRule ) + return; + + KeyValues *pContextKeyValues = new KeyValues( "OnSelectDominators" ); + SetElementKeyValue( pContextKeyValues, "rule", pRule ); + + CRawControlPickerFrame *pPicker = new CRawControlPickerFrame( this, "Select Dominator(s)" ); + pPicker->DoModal( m_hCombinationOperator, pRule, false, pContextKeyValues ); +} + +void CDmeCombinationDominationRulesPanel::OnSelectSuppressed( ) +{ + CDmeCombinationDominationRule *pRule = GetSelectedRule(); + if ( !pRule ) + return; + + KeyValues *pContextKeyValues = new KeyValues( "OnSelectSuppressed" ); + SetElementKeyValue( pContextKeyValues, "rule", pRule ); + + CRawControlPickerFrame *pPicker = new CRawControlPickerFrame( this, "Select Suppressed" ); + pPicker->DoModal( m_hCombinationOperator, pRule, true, pContextKeyValues ); +} + + +//----------------------------------------------------------------------------- +// +// Combination system editor panel +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CDmeCombinationSystemEditorPanel::CDmeCombinationSystemEditorPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + m_pEditorSheet = new vgui::PropertySheet( this, "EditorSheet" ); + m_pEditorSheet->AddActionSignalTarget( this ); + + m_pControlsPage = new vgui::PropertyPage( m_pEditorSheet, "ControlsPage" ); + m_pDominationRulesPage = new vgui::PropertyPage( m_pEditorSheet, "DominationRulesPage" ); + m_pPropertiesPage = new vgui::PropertyPage( m_pEditorSheet, "PropertiesPage" ); + + m_pControlsPanel = new CDmeCombinationControlsPanel( (vgui::Panel*)NULL, "CombinationControls" ); + m_pControlsPanel->AddActionSignalTarget( this ); + + m_pDominationRulesPanel = new CDmeCombinationDominationRulesPanel( (vgui::Panel*)NULL, "DominationRules" ); + m_pDominationRulesPanel->AddActionSignalTarget( this ); + + m_pPropertiesPanel = new CDmeElementPanel( (vgui::Panel*)NULL, "PropertiesPanel" ); + m_pPropertiesPanel->AddActionSignalTarget( this ); + + m_pControlsPanel->LoadControlSettingsAndUserConfig( "resource/dmecombinationsystemeditorpanel_controlspage.res" ); + m_pDominationRulesPanel->LoadControlSettingsAndUserConfig( "resource/dmecombinationsystemeditorpanel_dominationpage.res" ); + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettingsAndUserConfig( "resource/dmecombinationsystemeditorpanel.res" ); + + // NOTE: Page adding happens *after* LoadControlSettingsAndUserConfig + // because the layout of the sheet is correct at this point. + m_pEditorSheet->AddPage( m_pControlsPanel, "Controls" ); + m_pEditorSheet->AddPage( m_pDominationRulesPanel, "Domination" ); + m_pEditorSheet->AddPage( m_pPropertiesPanel, "Properties" ); +} + +CDmeCombinationSystemEditorPanel::~CDmeCombinationSystemEditorPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Set the scene + +//----------------------------------------------------------------------------- +void CDmeCombinationSystemEditorPanel::SetDmeElement( CDmeCombinationOperator *pComboOp ) +{ + m_pControlsPanel->SetCombinationOperator( pComboOp ); + m_pDominationRulesPanel->SetCombinationOperator( pComboOp ); + m_pPropertiesPanel->SetDmeElement( pComboOp ); +} + +CDmeCombinationOperator *CDmeCombinationSystemEditorPanel::GetDmeElement() +{ + return m_pControlsPanel->GetCombinationOperator( ); +} + + +//----------------------------------------------------------------------------- +// Called when the page changes +//----------------------------------------------------------------------------- +void CDmeCombinationSystemEditorPanel::OnPageChanged( ) +{ + if ( m_pEditorSheet->GetActivePage() == m_pControlsPage ) + { + return; + } + + if ( m_pEditorSheet->GetActivePage() == m_pDominationRulesPage ) + { + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when the property editor has made a change +//----------------------------------------------------------------------------- +void CDmeCombinationSystemEditorPanel::OnDmeElementChanged( KeyValues *kv ) +{ + m_pControlsPanel->RefreshCombinationOperator(); + m_pDominationRulesPanel->RefreshCombinationOperator(); + m_pPropertiesPanel->Refresh(); + + PostActionSignal( new KeyValues( "DmeElementChanged" ) ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Combination system editor frame +// +//----------------------------------------------------------------------------- +CDmeCombinationSystemEditorFrame::CDmeCombinationSystemEditorFrame( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "DmeCombinationSystemEditorFrame" ) +{ + SetDeleteSelfOnClose( true ); + m_pEditor = new CDmeCombinationSystemEditorPanel( this, "DmeCombinationSystemEditorPanel" ); + m_pEditor->AddActionSignalTarget( this ); + m_pOpenButton = new vgui::Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" ); + m_pCancelButton = new vgui::Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/dmecombinationsystemeditorframe.res" ); + + SetTitle( pTitle, false ); +} + +CDmeCombinationSystemEditorFrame::~CDmeCombinationSystemEditorFrame() +{ +} + + +//----------------------------------------------------------------------------- +// Sets the current scene + animation list +//----------------------------------------------------------------------------- +void CDmeCombinationSystemEditorFrame::SetCombinationOperator( CDmeCombinationOperator *pComboSystem ) +{ + m_pEditor->SetDmeElement( pComboSystem ); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CDmeCombinationSystemEditorFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Open" ) ) + { + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CDmeCombinationSystemEditorFrame::OnDmeElementChanged() +{ + PostActionSignal( new KeyValues( "CombinationOperatorChanged" ) ); +}
\ No newline at end of file diff --git a/vgui2/dme_controls/dmecontrols.cpp b/vgui2/dme_controls/dmecontrols.cpp new file mode 100644 index 0000000..1e4ee78 --- /dev/null +++ b/vgui2/dme_controls/dmecontrols.cpp @@ -0,0 +1,102 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "dme_controls/dmecontrols.h" +#include "soundemittersystem/isoundemittersystembase.h" +#include "matsys_controls/matsyscontrols.h" +#include "toolframework/ienginetool.h" +#include "vphysics_interface.h" +#include "dme_controls/inotifyui.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +namespace vgui +{ + +ISoundEmitterSystemBase *g_pSoundEmitterSystem = NULL; +ISoundEmitterSystemBase *SoundEmitterSystem() +{ + return g_pSoundEmitterSystem; +} + +IEngineTool *enginetools = NULL; +IEngineTool *EngineTool() +{ + return enginetools; +} + +IPhysicsCollision *g_pPhysicsCollision = NULL; +IPhysicsCollision *PhysicsCollision() +{ + return g_pPhysicsCollision; +} + +class CDefaultElementPropertiesChoices : public CBaseElementPropertiesChoices +{ +public: +}; + +static CDefaultElementPropertiesChoices s_DefaultChoices; +IElementPropertiesChoices *g_pElementPropertiesChoices = &s_DefaultChoices; +IElementPropertiesChoices *ElementPropertiesChoices() +{ + return g_pElementPropertiesChoices; +} + +void SetElementPropertiesChoices( IElementPropertiesChoices *pElementPropertiesChoices ) +{ + g_pElementPropertiesChoices = pElementPropertiesChoices ? pElementPropertiesChoices : &s_DefaultChoices; +} + + +//----------------------------------------------------------------------------- +// Purpose: finds a particular interface in the factory set +//----------------------------------------------------------------------------- +static void *InitializeInterface( char const *interfaceName, CreateInterfaceFn *factoryList, int numFactories ) +{ + void *retval; + + for ( int i = 0; i < numFactories; i++ ) + { + CreateInterfaceFn factory = factoryList[ i ]; + if ( !factory ) + continue; + + retval = factory( interfaceName, NULL ); + if ( retval ) + return retval; + } + + // No provider for requested interface!!! + // Assert( !"No provider for requested interface!!!" ); + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Initializes the controls +//----------------------------------------------------------------------------- +bool VGui_InitDmeInterfacesList( const char *moduleName, CreateInterfaceFn *factoryList, int numFactories ) +{ + if ( !vgui::VGui_InitMatSysInterfacesList( moduleName, factoryList, numFactories ) ) + return false; + + g_pSoundEmitterSystem = (ISoundEmitterSystemBase*)InitializeInterface( SOUNDEMITTERSYSTEM_INTERFACE_VERSION, factoryList, numFactories ); + enginetools = (IEngineTool*)InitializeInterface( VENGINETOOL_INTERFACE_VERSION, factoryList, numFactories ); + g_pPhysicsCollision = (IPhysicsCollision*)InitializeInterface( VPHYSICS_COLLISION_INTERFACE_VERSION, factoryList, numFactories ); + + // Can function without either of these + return true; +} + + +} // namespace vgui + + + diff --git a/vgui2/dme_controls/dmedageditpanel.cpp b/vgui2/dme_controls/dmedageditpanel.cpp new file mode 100644 index 0000000..0703627 --- /dev/null +++ b/vgui2/dme_controls/dmedageditpanel.cpp @@ -0,0 +1,1011 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "vstdlib/random.h" +#include "dme_controls/dmedageditpanel.h" +#include "dme_controls/dmedagrenderpanel.h" +#include "dme_controls/dmepanel.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/ScrollBar.h" +#include "vgui_controls/Slider.h" +#include "vgui_controls/ScrollableEditablePanel.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/Image.h" +#include "vgui_controls/TextImage.h" +#include "vgui/ISurface.h" +#include "vgui/ischeme.h" +#include "vgui/iinput.h" +#include "vgui/IVGui.h" +#include "vgui/cursor.h" +#include "movieobjects/dmemakefile.h" +#include "movieobjects/dmemdlmakefile.h" +#include "movieobjects/dmedccmakefile.h" +#include "movieobjects/dmedag.h" +#include "movieobjects/dmeclip.h" +#include "movieobjects/dmeanimationlist.h" +#include "movieobjects/dmecombinationoperator.h" +#include "movieobjects/dmeanimationset.h" + +#include "tier1/KeyValues.h" +#include "materialsystem/imesh.h" +#include "dme_controls/BaseAnimationSetEditor.h" +#include "dme_controls/BaseAnimSetAttributeSliderPanel.h" +//----------------------------------------------------------------------------- +// +// Hook into the dme panel editor system +// +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeDag, "DmeDagPreview2", "DmeDag Previewer 2", false ); +IMPLEMENT_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeSourceSkin, "DmeSourceSkinPreview2", "MDL Skin Previewer 2", false ); +IMPLEMENT_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeSourceAnimation, "DmeSourceAnimationPreview2", "MDL Animation Previewer 2", false ); +IMPLEMENT_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeDCCMakefile, "DmeMakeFileOutputPreview2", "DCC MakeFile Output Previewer 2", false ); + + +//----------------------------------------------------------------------------- +// Slider panel +//----------------------------------------------------------------------------- +class CDmeAnimationListPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CDmeAnimationListPanel, vgui::EditablePanel ); + +public: + // constructor, destructor + CDmeAnimationListPanel( vgui::Panel *pParent, const char *pName ); + virtual ~CDmeAnimationListPanel(); + + void SetAnimationList( CDmeAnimationList *pList ); + void RefreshAnimationList(); + const char *GetSelectedAnimation() const; + +private: + // Called when the selection changes moves + MESSAGE_FUNC( OnItemSelected, "ItemSelected" ); + MESSAGE_FUNC( OnItemDeselected, "ItemDeselected" ); + + vgui::ListPanel *m_pAnimationList; + CDmeHandle< CDmeAnimationList > m_hAnimationList; +}; + + +static int __cdecl AnimationNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("name"); + const char *string2 = item2.kv->GetString("name"); + return Q_stricmp( string1, string2 ); +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CDmeAnimationListPanel::CDmeAnimationListPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_pAnimationList = new vgui::ListPanel( this, "AnimationList" ); + m_pAnimationList->AddColumnHeader( 0, "name", "name", 100, 0 ); + m_pAnimationList->AddActionSignalTarget( this ); + m_pAnimationList->SetSortFunc( 0, AnimationNameSortFunc ); + m_pAnimationList->SetSortColumn( 0 ); + m_pAnimationList->SetEmptyListText("No animations"); + m_pAnimationList->SetMultiselectEnabled( false ); + + LoadControlSettingsAndUserConfig( "resource/dmeanimationlistpanel.res" ); +} + +CDmeAnimationListPanel::~CDmeAnimationListPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Sets the combination operator +//----------------------------------------------------------------------------- +void CDmeAnimationListPanel::SetAnimationList( CDmeAnimationList *pList ) +{ + if ( pList != m_hAnimationList.Get() ) + { + m_hAnimationList = pList; + RefreshAnimationList(); + } +} + + +//----------------------------------------------------------------------------- +// Builds the list of animations +//----------------------------------------------------------------------------- +void CDmeAnimationListPanel::RefreshAnimationList() +{ + m_pAnimationList->RemoveAll(); + if ( !m_hAnimationList.Get() ) + return; + + int nCount = m_hAnimationList->GetAnimationCount(); + for ( int i = 0; i < nCount; ++i ) + { + CDmeChannelsClip *pAnimation = m_hAnimationList->GetAnimation( i ); + KeyValues *pItemKeys = new KeyValues( "node", "name", pAnimation->GetName() ); + m_pAnimationList->AddItem( pItemKeys, 0, false, false ); + } + + m_pAnimationList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Returns the selected animation +//----------------------------------------------------------------------------- +const char *CDmeAnimationListPanel::GetSelectedAnimation() const +{ + if ( m_pAnimationList->GetSelectedItemsCount() == 0 ) + return ""; + + int nIndex = m_pAnimationList->GetSelectedItem( 0 ); + KeyValues *pItemKeyValues = m_pAnimationList->GetItem( nIndex ); + return pItemKeyValues->GetString( "name" ); +} + + +//----------------------------------------------------------------------------- +// Called when an animation is selected or deselected +//----------------------------------------------------------------------------- +void CDmeAnimationListPanel::OnItemSelected( ) +{ + const char *pAnimationName = GetSelectedAnimation(); + PostActionSignal( new KeyValues( "AnimationSelected", "animationName", pAnimationName ) ); +} + +void CDmeAnimationListPanel::OnItemDeselected( ) +{ + PostActionSignal( new KeyValues( "AnimationDeselected" ) ); +} + +class CDmeCombinationOperatorPanel : public CBaseAnimationSetEditor +{ + DECLARE_CLASS_SIMPLE( CDmeCombinationOperatorPanel, CBaseAnimationSetEditor ); +public: + CDmeCombinationOperatorPanel( vgui::Panel *parent, const char *panelName ); + virtual ~CDmeCombinationOperatorPanel(); + + virtual void OnTick(); + + void SetCombinationOperator( CDmeCombinationOperator *pOp ); + void RefreshCombinationOperator(); + +private: + void CreateFakeAnimationSet( ); + void DestroyFakeAnimationSet( ); + + // Adds new controls to the animation set + void AddNewAnimationSetControls(); + + // Removes a controls from all presets + void RemoveAnimationControlFromPresets( const char *pControlName ); + + // Removes a controls from all presets + void RemoveAnimationControlFromSelectionGroups( const char *pControlName ); + + // Removes controls from the animation set that aren't used + void RemoveUnusedAnimationSetControls(); + + // Modify controls in the presets which have had stereo or multilevel settings changed + void ModifyExistingAnimationSetControls(); + + // Modify controls in the presets which have had stereo or multilevel settings changed + void ModifyExistingAnimationSetPresets( CDmElement *pControlElement ); + + // Modify controls which have had stereo or multilevel settings changed + void ModifyExistingAnimationSetControl( CDmElement *pControlElement ); + + // Creates the procedural presets + void ComputeProceduralPresets(); + + void RefreshAnimationSet(); + + // Sort control names to match the combination controls + void SortAnimationSetControls(); + + // Sets preset values + void GenerateProceduralPresetValues( CDmElement *pPreset, CDmElement *pControl, bool bIdentity, float flForceValue ); + void GenerateProceduralPresetValues( CDmElement *pPreset, const CDmrElementArray< CDmElement > &controls, bool bIdentity, float flForceValue = -1.0f ); + + CDmeHandle< CDmeCombinationOperator > m_hCombinationOperator; +}; + + +//----------------------------------------------------------------------------- +// Constructor/destructor +//----------------------------------------------------------------------------- +CDmeCombinationOperatorPanel::CDmeCombinationOperatorPanel( vgui::Panel *parent, const char *panelName ) : + BaseClass( parent, panelName, false ) +{ + CreateFakeAnimationSet(); + vgui::ivgui()->AddTickSignal( GetVPanel(), 0 ); +} + +CDmeCombinationOperatorPanel::~CDmeCombinationOperatorPanel() +{ + DestroyFakeAnimationSet(); +} + + +//----------------------------------------------------------------------------- +// Create, destroy fake animation sets +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::CreateFakeAnimationSet( ) +{ + m_AnimSet = CreateElement< CDmeAnimationSet >( "fakeSet", DMFILEID_INVALID ); + m_AnimSet->SetValue( "gameModel", DMELEMENT_HANDLE_INVALID ); + g_pDataModel->DontAutoDelete( m_AnimSet->GetHandle() ); +} + +void CDmeCombinationOperatorPanel::DestroyFakeAnimationSet( ) +{ + DestroyElement( m_AnimSet, TD_DEEP ); +} + + +//----------------------------------------------------------------------------- +// Sets combination ops + animation sets +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::SetCombinationOperator( CDmeCombinationOperator *pOp ) +{ + // The Animation Set Editor Doesn't Handle Passing The Same Animation Set With + // Different Data In It Very Well + DestroyFakeAnimationSet(); + CreateFakeAnimationSet(); + + if ( pOp != m_hCombinationOperator.Get() ) + { + m_hCombinationOperator = pOp; + RefreshCombinationOperator(); + } +} + + +//----------------------------------------------------------------------------- +// Sets preset values +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::GenerateProceduralPresetValues( CDmElement *pPreset, CDmElement *ctrl, bool bForceValue, float flForceValue ) +{ + bool combo = ctrl->GetValue< bool >( "combo" ); + bool multi = ctrl->GetValue< bool >( "multi" ); + + float flValue, flBalance, flMultilevel; + if ( !bForceValue ) + { + flValue = RandomFloat( 0.0f, 1.0f ); + flBalance = RandomFloat( 0.25f, 0.75f ); + flMultilevel = RandomFloat( 0.0f, 1.0f ); + } + else + { + flValue = flBalance = flMultilevel = flForceValue; + } + + pPreset->SetValue< float >( "value", flValue ); + if ( combo ) + { + pPreset->SetValue< float >( "balance", flBalance ); + } + else + { + pPreset->RemoveAttribute( "balance" ); + } + + if ( multi ) + { + pPreset->SetValue< float >( "multilevel", flMultilevel ); + } + else + { + pPreset->RemoveAttribute( "multilevel" ); + } +} + + +void CDmeCombinationOperatorPanel::GenerateProceduralPresetValues( CDmElement *pPreset, const CDmrElementArray< CDmElement > &controls, bool bIdentity, float flForceValue /*= -1.0f*/ ) +{ + CDmrElementArray<> values( pPreset, "controlValues" ); + Assert( values.IsValid() ); + values.RemoveAll(); + int c = controls.Count(); + for ( int i = 0; i < c ; ++i ) + { + CDmElement *pControl = controls[ i ]; + CDmElement *pControlValue = CreateElement< CDmElement >( pControl->GetName(), pPreset->GetFileId() ); + GenerateProceduralPresetValues( pControlValue, pControl, bIdentity, flForceValue ); + values.AddToTail( pControlValue ); + } +} + + +//----------------------------------------------------------------------------- +// Removes a controls from all presets +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::RemoveAnimationControlFromPresets( const char *pControlName ) +{ + CDmeAnimationSet *pAnimationSet = m_AnimSet.Get(); + CDmrElementArray< CDmePresetGroup > presetGroupList = pAnimationSet->GetPresetGroups(); + + int nPresetGroupCount = presetGroupList.Count(); + for ( int i = 0; i < nPresetGroupCount; ++i ) + { + CDmePresetGroup *pPresetGroup = presetGroupList[ i ]; + + CDmrElementArray< CDmePreset > presetList = pPresetGroup->GetPresets(); + int nPresetCount = presetList.Count(); + for ( int j = 0; j < nPresetCount; ++j ) + { + CDmePreset *pPreset = presetList[ j ]; + CDmrElementArray<> controlValues = pPreset->GetControlValues(); + + int nControlCount = controlValues.Count(); + for ( int k = 0; k < nControlCount; ++k ) + { + CDmElement *v = controlValues[ k ]; + if ( !Q_stricmp( v->GetName(), pControlName ) ) + { + controlValues.FastRemove( k ); + break; + } + } + } + } +} + + +//----------------------------------------------------------------------------- +// Removes a controls from all presets +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::RemoveAnimationControlFromSelectionGroups( const char *pControlName ) +{ + CDmeAnimationSet *pAnimationSet = m_AnimSet.Get(); + CDmrElementArray< > selectionGroupList = pAnimationSet->GetSelectionGroups(); + + int nGroupCount = selectionGroupList.Count(); + for ( int i = 0; i < nGroupCount; ++i ) + { + CDmElement *pSelectionGroup = selectionGroupList[ i ]; + CDmrStringArray selectedControls( pSelectionGroup, "selectedControls" ); + int nControlCount = selectedControls.Count(); + for ( int j = 0; j < nControlCount; ++j ) + { + if ( !Q_stricmp( selectedControls[ j ], pControlName ) ) + { + selectedControls.FastRemove( j ); + break; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Removes controls from the animation set that aren't used +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::RemoveUnusedAnimationSetControls() +{ + CDmeAnimationSet *pAnimationSet = m_AnimSet.Get(); + CDmrElementArray< > controls = pAnimationSet->GetControls(); + + // Remove all controls in the animation set and in presets that don't exist in the combination system + int nAnimSetCount = controls.Count(); + for ( int i = nAnimSetCount; --i >= 0; ) + { + CDmElement *pControlElement = controls[i]; + if ( !pControlElement ) + continue; + + // Don't do any of this work for transforms + if ( pControlElement->GetValue< bool >( "transform" ) ) + continue; + + const char *pControlName = pControlElement->GetName(); + + // Look for a match + if ( m_hCombinationOperator.Get() && m_hCombinationOperator->FindControlIndex( pControlName ) >= 0 ) + continue; + + // No match, blow the control away. + RemoveAnimationControlFromPresets( pControlName ); + RemoveAnimationControlFromSelectionGroups( pControlName ); + controls.FastRemove( i ); + } +} + + +//----------------------------------------------------------------------------- +// Modify controls in the presets which have had stereo or multilevel settings changed +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::ModifyExistingAnimationSetControl( CDmElement *pControlElement ) +{ + const char *pControlName = pControlElement->GetName(); + + // Look for a match + int nControlIndex = m_hCombinationOperator->FindControlIndex( pControlName ); + Assert( nControlIndex >= 0 ); + + bool bIsStereoControl = m_hCombinationOperator->IsStereoControl( nControlIndex ); + bool bIsAnimControlStereo = pControlElement->GetValue< bool >( "combo" ); + if ( bIsAnimControlStereo != bIsStereoControl ) + { + pControlElement->SetValue< bool >( "combo", bIsStereoControl ); + if ( !bIsStereoControl ) + { + pControlElement->RemoveAttribute( "balance" ); + } + else + { + const Vector2D &value = m_hCombinationOperator->GetStereoControlValue( nControlIndex ); + pControlElement->SetValue< float >( "balance", value.y ); + } + } + + bool bIsMultiControl = m_hCombinationOperator->IsMultiControl( nControlIndex ); + bool bIsAnimMultiControl = pControlElement->GetValue< bool >( "multi" ); + if ( bIsAnimMultiControl != bIsMultiControl ) + { + pControlElement->SetValue< bool >( "multi", bIsMultiControl ); + if ( !bIsMultiControl ) + { + pControlElement->RemoveAttribute( "multilevel" ); + } + else + { + pControlElement->SetValue< float >( "multilevel", m_hCombinationOperator->GetMultiControlLevel( nControlIndex ) ); + } + } + + float flDefaultValue = m_hCombinationOperator->GetRawControlCount( nControlIndex ) == 2 ? 0.5f : 0.0f; + pControlElement->SetValue( "defaultValue", flDefaultValue ); + pControlElement->SetValue( "defaultBalance", 0.5f ); + pControlElement->SetValue( "defaultMultilevel", 0.5f ); +} + + +//----------------------------------------------------------------------------- +// Modify controls in the presets which have had stereo or multilevel settings changed +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::ModifyExistingAnimationSetPresets( CDmElement *pControlElement ) +{ + const char *pControlName = pControlElement->GetName(); + + CDmeAnimationSet *pAnimationSet = m_AnimSet.Get(); + const CDmrElementArray< CDmePresetGroup > &presetGroupList = pAnimationSet->GetPresetGroups(); + + int nPresetGroupCount = presetGroupList.Count(); + for ( int g = 0; g < nPresetGroupCount; ++g ) + { + CDmePresetGroup *pPresetGroup = presetGroupList[ g ]; + const CDmrElementArray< CDmePreset > &presetList = pPresetGroup->GetPresets(); + + int nPresetCount = presetList.Count(); + for ( int i = 0; i < nPresetCount; ++i ) + { + CDmePreset *pPreset = presetList[ i ]; + const CDmrElementArray< CDmElement > &controlValues = pPreset->GetControlValues( ); + + int nControlCount = controlValues.Count(); + for ( int j = 0; j < nControlCount; ++j ) + { + CDmElement *v = controlValues[ j ]; + if ( Q_stricmp( v->GetName(), pControlName ) ) + continue; + + bool bIsAnimControlStereo = pControlElement->GetValue< bool >( "combo" ); + if ( bIsAnimControlStereo ) + { + if ( !v->HasAttribute( "balance" ) ) + { + v->SetValue( "balance", 0.5f ); + } + } + else + { + v->RemoveAttribute( "balance" ); + } + + bool bIsAnimMultiControl = pControlElement->GetValue< bool >( "multi" ); + if ( bIsAnimMultiControl ) + { + if ( !v->HasAttribute( "multilevel" ) ) + { + v->SetValue( "multilevel", 0.5f ); + } + } + else + { + v->RemoveAttribute( "multilevel" ); + } + } + } + } +} + + +//----------------------------------------------------------------------------- +// Modify controls which have had stereo or multilevel settings changed +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::ModifyExistingAnimationSetControls() +{ + CDmeAnimationSet *pAnimationSet = m_AnimSet.Get(); + const CDmrElementArray< CDmElement > &controls = pAnimationSet->GetControls(); + + // Update the main controls; update defaults and add or remove combo + multi data + int nAnimSetCount = controls.Count(); + for ( int i = nAnimSetCount; --i >= 0; ) + { + CDmElement *pControlElement = controls[i]; + if ( !pControlElement ) + continue; + + // Don't do any of this work for transforms + if ( pControlElement->GetValue< bool >( "transform" ) ) + continue; + + ModifyExistingAnimationSetControl( pControlElement ); + ModifyExistingAnimationSetPresets( pControlElement ); + } +} + + +//----------------------------------------------------------------------------- +// Adds new controls to the animation set +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::AddNewAnimationSetControls() +{ + if ( !m_hCombinationOperator.Get() ) + return; + + CDmeAnimationSet *pAnimationSet = m_AnimSet.Get(); + CDmaElementArray< > &controls = pAnimationSet->GetControls(); + + // Remove all controls in the animation set and in presets that don't exist in the combination system + int nFirstControl = controls.Count(); + int nCombinationControlCount = m_hCombinationOperator->GetControlCount(); + for ( int i = 0; i < nCombinationControlCount; ++i ) + { + const char *pControlName = m_hCombinationOperator->GetControlName( i ); + if ( pAnimationSet->FindControl( pControlName ) ) + continue; + + bool bIsStereoControl = m_hCombinationOperator->IsStereoControl(i); + bool bIsMultiControl = m_hCombinationOperator->IsMultiControl(i); + float flDefaultValue = m_hCombinationOperator->GetRawControlCount(i) == 2 ? 0.5f : 0.0f; + + // Add the control to the controls group + CDmElement *pControl = CreateElement< CDmElement >( pControlName, pAnimationSet->GetFileId() ); + Assert( pControl ); + controls.InsertBefore( i, pControl ); + + pControl->SetValue( "combo", bIsStereoControl ); + pControl->SetValue( "multi", bIsMultiControl ); + + if ( bIsStereoControl ) + { + const Vector2D &value = m_hCombinationOperator->GetStereoControlValue(i); + + pControl->SetValue( "value", value.x ); + pControl->SetValue( "balance", value.y ); + } + else + { + pControl->SetValue( "value", m_hCombinationOperator->GetControlValue(i) ); + } + + if ( bIsMultiControl ) + { + pControl->SetValue( "multilevel", m_hCombinationOperator->GetMultiControlLevel(i) ); + } + + pControl->SetValue( "defaultValue", flDefaultValue ); + pControl->SetValue( "defaultBalance", 0.5f ); + pControl->SetValue( "defaultMultilevel", 0.5f ); + } + + int nLastControl = controls.Count(); + if ( nLastControl == nFirstControl ) + return; + + // Add new controls to the root group + CDmElement *pGroup = pAnimationSet->FindOrAddSelectionGroup( "Root" ); + + // Fill in members + CDmrStringArray groups( pGroup, "selectedControls" ); + for ( int i = nFirstControl; i < nLastControl; ++i ) + { + groups.AddToTail( controls[ i ]->GetName() ); + } +} + + +//----------------------------------------------------------------------------- +// Creates the procedural presets +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::ComputeProceduralPresets() +{ + CDmeAnimationSet *pAnimationSet = m_AnimSet.Get(); + + // Now create some presets + CDmePresetGroup* pPresetGroup = pAnimationSet->FindOrAddPresetGroup( "procedural" ); + pPresetGroup->m_bIsReadOnly = true; + + // NOTE: Default needs no values set into it since it's the default + CDmElement *pPreset = pPresetGroup->FindOrAddPreset( "Default" ); + + pPreset = pPresetGroup->FindOrAddPreset( "Zero" ); + GenerateProceduralPresetValues( pPreset, pAnimationSet->GetControls(), true, 0.0f ); + + pPreset = pPresetGroup->FindOrAddPreset( "Half" ); + GenerateProceduralPresetValues( pPreset, pAnimationSet->GetControls(), true, 0.5f ); + + pPreset = pPresetGroup->FindOrAddPreset( "One" ); + GenerateProceduralPresetValues( pPreset, pAnimationSet->GetControls(), true, 1.0f ); + + pPreset = pPresetGroup->FindOrAddPreset( "Random" ); + GenerateProceduralPresetValues( pPreset, pAnimationSet->GetControls(), false ); + + pAnimationSet->EnsureProceduralPresets(); +} + + +//----------------------------------------------------------------------------- +// Sort control names to match the combination controls +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::SortAnimationSetControls() +{ + CDmaElementArray<> &controls = m_AnimSet->GetControls(); + int nControlCount = controls.Count(); + if ( nControlCount == 0 ) + return; + + int nCombinationControlCount = m_hCombinationOperator->GetControlCount(); + Assert( nControlCount == nCombinationControlCount ); + + DmElementHandle_t *pElements = (DmElementHandle_t*)_alloca( nControlCount * sizeof(DmElementHandle_t) ); + for ( int i = 0; i < nCombinationControlCount; ++i ) + { + const char *pControlName = m_hCombinationOperator->GetControlName( i ); + CDmElement *pControl = m_AnimSet->FindControl( pControlName ); + pElements[i] = pControl->GetHandle(); + } + + controls.SetMultiple( 0, nControlCount, pElements ); + +#ifdef _DEBUG + for ( int i = 0; i < nCombinationControlCount; ++i ) + { + const char *pControlName = controls[i]->GetName(); + const char *pComboName = m_hCombinationOperator->GetControlName( i ); + Assert( !Q_stricmp( pControlName, pComboName ) ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Called when the animation set has to be made to match the combination operator +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::RefreshAnimationSet() +{ + if ( !m_AnimSet.Get() ) + return; + + CDisableUndoScopeGuard sg; + + // Remove all controls in the animation set and in presets that don't exist in the combination system + RemoveUnusedAnimationSetControls(); + + // Modify controls in the presets which have had stereo or multilevel settings changed + ModifyExistingAnimationSetControls(); + + // Add all controls not in the animation set but which do exist in the combination system + AddNewAnimationSetControls(); + + // Resets the procedural presets + ComputeProceduralPresets(); + + // Sort control names to match the combination controls + SortAnimationSetControls(); + + // Set that as the current set + BaseClass::ChangeAnimationSet( m_AnimSet ); +} + +void CDmeCombinationOperatorPanel::RefreshCombinationOperator() +{ + RefreshAnimationSet(); +} + + +//----------------------------------------------------------------------------- +// Simulate +//----------------------------------------------------------------------------- +void CDmeCombinationOperatorPanel::OnTick() +{ + BaseClass::OnTick(); + + if ( m_hCombinationOperator ) + { + { + CDisableUndoScopeGuard sg; + + int c = m_hCombinationOperator->GetControlCount(); + for ( int i = 0; i < c; ++i ) + { + AttributeValue_t value; + + bool bVisible = GetAttributeSlider()->GetSliderValues( &value, i ); + if ( !bVisible ) + continue; + + if ( m_hCombinationOperator->IsStereoControl( i ) ) + { + m_hCombinationOperator->SetControlValue( i, value.m_pValue[ANIM_CONTROL_VALUE], value.m_pValue[ANIM_CONTROL_BALANCE] ); + } + else + { + m_hCombinationOperator->SetControlValue( i, value.m_pValue[ANIM_CONTROL_VALUE] ); + } + if ( m_hCombinationOperator->IsMultiControl( i ) ) + { + m_hCombinationOperator->SetMultiControlLevel( i, value.m_pValue[ANIM_CONTROL_MULTILEVEL] ); + } + } + } + + // FIXME: Shouldn't this happen at the application level? + // run the machinery - apply, resolve, dependencies, operate, resolve + CUtlVector< IDmeOperator* > operators; + operators.AddToTail( m_hCombinationOperator ); + + CDisableUndoScopeGuard guard; + g_pDmElementFramework->SetOperators( operators ); + g_pDmElementFramework->Operate( true ); + } + + // allow elements and attributes to be edited again + g_pDmElementFramework->BeginEdit(); +} + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CDmeDagEditPanel::CDmeDagEditPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + m_pPropertiesSplitter = new vgui::Splitter( this, "PropertiesSplitter", vgui::SPLITTER_MODE_VERTICAL, 1 ); + vgui::Panel *pSplitterLeftSide = m_pPropertiesSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pPropertiesSplitter->GetChild( 1 ); + + m_pDagRenderPanel = new CDmeDagRenderPanel( pSplitterRightSide, "DagRenderPanel" ); + + m_pEditorSheet = new vgui::PropertySheet( pSplitterLeftSide, "EditorSheet" ); + m_pEditorSheet->AddActionSignalTarget( this ); + + m_pAnimationPage = new vgui::PropertyPage( m_pEditorSheet, "AnimationPage" ); + m_pCombinationPage = new vgui::PropertyPage( m_pEditorSheet, "AnimationSetEditor" ); + m_pVertexAnimationPage = new vgui::PropertyPage( m_pEditorSheet, "VertexAnimationPage" ); + + m_pCombinationPanel = new CDmeCombinationOperatorPanel( m_pCombinationPage, "AnimationSetEditorPanel" ); + m_pCombinationPanel->CreateToolsSubPanels(); + + m_pAnimationListPanel = new CDmeAnimationListPanel( m_pAnimationPage, "AnimationListPanel" ); + m_pAnimationListPanel->AddActionSignalTarget( this ); + m_pVertexAnimationListPanel = new CDmeAnimationListPanel( m_pVertexAnimationPage, "VertexAnimationListPanel" ); + m_pVertexAnimationListPanel->AddActionSignalTarget( this ); + + m_pCombinationPage->LoadControlSettingsAndUserConfig( "resource/dmedageditpanel_animationseteditorpage.res" ); + m_pAnimationPage->LoadControlSettingsAndUserConfig( "resource/dmedageditpanel_animationpage.res" ); + m_pVertexAnimationPage->LoadControlSettingsAndUserConfig( "resource/dmedageditpanel_vertexanimationpage.res" ); + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettingsAndUserConfig( "resource/dmedageditpanel.res" ); + + // NOTE: Page adding happens *after* LoadControlSettingsAndUserConfig + // because the layout of the sheet is correct at this point. + m_pEditorSheet->AddPage( m_pAnimationPage, "Animation" ); + m_pEditorSheet->AddPage( m_pCombinationPage, "Combination" ); + m_pEditorSheet->AddPage( m_pVertexAnimationPage, "Vertex Animation" ); + +} + +CDmeDagEditPanel::~CDmeDagEditPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Set the scene + +//----------------------------------------------------------------------------- +void CDmeDagEditPanel::SetDmeElement( CDmeDag *pScene ) +{ + m_pDagRenderPanel->SetDmeElement( pScene ); +} + + +//----------------------------------------------------------------------------- +// Sets up the various panels in the dag editor +//----------------------------------------------------------------------------- +void CDmeDagEditPanel::SetMakefileRootElement( CDmElement *pRoot ) +{ + CDmeAnimationList *pAnimationList = pRoot->GetValueElement< CDmeAnimationList >( "animationList" ); + SetAnimationList( pAnimationList ); + + CDmeAnimationList *pVertexAnimationList = pRoot->GetValueElement< CDmeAnimationList >( "vertexAnimationList" ); + SetVertexAnimationList( pVertexAnimationList ); + + CDmeCombinationOperator *pComboOp = pRoot->GetValueElement< CDmeCombinationOperator >( "combinationOperator" ); + SetCombinationOperator( pComboOp ); +} + + +//----------------------------------------------------------------------------- +// Other methods which hook into DmePanel +//----------------------------------------------------------------------------- +void CDmeDagEditPanel::SetDmeElement( CDmeSourceSkin *pSkin ) +{ + m_pDagRenderPanel->SetDmeElement( pSkin ); + + // First, try to grab the dependent makefile + CDmeMakefile *pSourceMakefile = pSkin->GetDependentMakefile(); + if ( !pSourceMakefile ) + return; + + // Next, try to grab the output of that makefile + CDmElement *pOutput = pSourceMakefile->GetOutputElement( true ); + if ( !pOutput ) + return; + + SetMakefileRootElement( pOutput ); +} + +void CDmeDagEditPanel::SetDmeElement( CDmeSourceAnimation *pAnimation ) +{ + m_pDagRenderPanel->SetDmeElement( pAnimation ); + + // First, try to grab the dependent makefile + CDmeMakefile *pSourceMakefile = pAnimation->GetDependentMakefile(); + if ( !pSourceMakefile ) + return; + + // Next, try to grab the output of that makefile + CDmElement *pOutput = pSourceMakefile->GetOutputElement( true ); + if ( !pOutput ) + return; + + SetMakefileRootElement( pOutput ); + +// if ( pAnimationList->FindAnimation( pAnimation->m_SourceAnimationName ) < 0 ) +// return; +} + +void CDmeDagEditPanel::SetDmeElement( CDmeDCCMakefile *pDCCMakefile ) +{ + m_pDagRenderPanel->SetDmeElement( pDCCMakefile ); + + // First, try to grab the dependent makefile + CDmElement *pOutput = pDCCMakefile->GetOutputElement( true ); + if ( !pOutput ) + return; + + SetMakefileRootElement( pOutput ); +} + +CDmeDag *CDmeDagEditPanel::GetDmeElement() +{ + return m_pDagRenderPanel->GetDmeElement(); +} + + +//----------------------------------------------------------------------------- +// paint it! +//----------------------------------------------------------------------------- +void CDmeDagEditPanel::Paint() +{ + BaseClass::Paint(); +} + + +//----------------------------------------------------------------------------- +// Sets animation +//----------------------------------------------------------------------------- +void CDmeDagEditPanel::SetAnimationList( CDmeAnimationList *pAnimationList ) +{ + m_pDagRenderPanel->SetAnimationList( pAnimationList ); + m_pDagRenderPanel->SelectAnimation( "" ); + m_pAnimationListPanel->SetAnimationList( pAnimationList ); +} + + +void CDmeDagEditPanel::SetVertexAnimationList( CDmeAnimationList *pAnimationList ) +{ + m_pDagRenderPanel->SetVertexAnimationList( pAnimationList ); + m_pDagRenderPanel->SelectVertexAnimation( "" ); + m_pVertexAnimationListPanel->SetAnimationList( pAnimationList ); +} + +void CDmeDagEditPanel::SetCombinationOperator( CDmeCombinationOperator *pComboOp ) +{ + m_pCombinationPanel->SetCombinationOperator( pComboOp ); +} + +void CDmeDagEditPanel::RefreshCombinationOperator() +{ + m_pCombinationPanel->RefreshCombinationOperator(); +} + + +//----------------------------------------------------------------------------- +// Called when the page changes +//----------------------------------------------------------------------------- +void CDmeDagEditPanel::OnPageChanged( ) +{ + if ( m_pEditorSheet->GetActivePage() == m_pCombinationPage ) + { + m_pDagRenderPanel->SelectAnimation( "" ); + m_pDagRenderPanel->SelectVertexAnimation( "" ); + return; + } + + if ( m_pEditorSheet->GetActivePage() == m_pAnimationPage ) + { + m_pDagRenderPanel->SelectAnimation( m_pAnimationListPanel->GetSelectedAnimation() ); + m_pDagRenderPanel->SelectVertexAnimation( "" ); + return; + } + + if ( m_pEditorSheet->GetActivePage() == m_pVertexAnimationPage ) + { + m_pDagRenderPanel->SelectAnimation( "" ); + m_pDagRenderPanel->SelectVertexAnimation( m_pVertexAnimationListPanel->GetSelectedAnimation() ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when new animations are selected +//----------------------------------------------------------------------------- +void CDmeDagEditPanel::OnAnimationSelected( KeyValues *pKeyValues ) +{ + vgui::Panel *pPanel = (vgui::Panel *)pKeyValues->GetPtr( "panel", NULL ); + const char *pAnimationName = pKeyValues->GetString( "animationName", "" ); + + if ( pPanel == m_pAnimationListPanel ) + { + m_pDagRenderPanel->SelectAnimation( pAnimationName ); + return; + } + + if ( pPanel == m_pVertexAnimationListPanel ) + { + m_pDagRenderPanel->SelectVertexAnimation( pAnimationName ); + return; + } +} + + +void CDmeDagEditPanel::OnAnimationDeselected( KeyValues *pKeyValues ) +{ + vgui::Panel *pPanel = (vgui::Panel *)pKeyValues->GetPtr( "panel", NULL ); + if ( pPanel == m_pAnimationListPanel ) + { + m_pDagRenderPanel->SelectAnimation( "" ); + return; + } + + if ( pPanel == m_pVertexAnimationListPanel ) + { + m_pDagRenderPanel->SelectVertexAnimation( "" ); + return; + } +} diff --git a/vgui2/dme_controls/dmedagrenderpanel.cpp b/vgui2/dme_controls/dmedagrenderpanel.cpp new file mode 100644 index 0000000..4d6e138 --- /dev/null +++ b/vgui2/dme_controls/dmedagrenderpanel.cpp @@ -0,0 +1,739 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "dme_controls/dmedagrenderpanel.h" +#include "movieobjects/dmedag.h" +#include "movieobjects/dmemodel.h" +#include "movieobjects/timeutils.h" +#include "movieobjects/dmeanimationlist.h" +#include "movieobjects/dmeclip.h" +#include "movieobjects/dmechannel.h" +#include "movieobjects/dmemdlmakefile.h" +#include "movieobjects/dmedccmakefile.h" +#include "movieobjects/dmemesh.h" +#include "movieobjects/dmedrawsettings.h" +#include "dme_controls/dmepanel.h" +#include "tier1/KeyValues.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "tier3/tier3.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "materialsystem/imesh.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/MenuBar.h" +#include "vgui_controls/MenuButton.h" +#include "vgui/IVGui.h" + + +//----------------------------------------------------------------------------- +// +// Hook into the dme panel editor system +// +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CDmeDagRenderPanel, DmeDag, "DmeDagRenderer", "DmeDag Preview Renderer", false ); +IMPLEMENT_DMEPANEL_FACTORY( CDmeDagRenderPanel, DmeSourceSkin, "DmeSourceSkinPreview", "MDL Skin Previewer", false ); +IMPLEMENT_DMEPANEL_FACTORY( CDmeDagRenderPanel, DmeSourceAnimation, "DmeSourceAnimationPreview", "MDL Animation Previewer", false ); +IMPLEMENT_DMEPANEL_FACTORY( CDmeDagRenderPanel, DmeDCCMakefile, "DmeMakeFileOutputPreview", "DCC MakeFile Output Preview", false ); + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CDmeDagRenderPanel::CDmeDagRenderPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + // Used to poll input + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + m_bDrawJointNames = false; + m_bDrawJoints = false; + m_bDrawGrid = true; + + // Deal with the default cubemap + ITexture *pCubemapTexture = g_pMaterialSystem->FindTexture( "editor/cubemap", NULL, true ); + m_DefaultEnvCubemap.Init( pCubemapTexture ); + pCubemapTexture = g_pMaterialSystem->FindTexture( "editor/cubemap.hdr", NULL, true ); + m_DefaultHDREnvCubemap.Init( pCubemapTexture ); + + m_pDrawSettings = CreateElement< CDmeDrawSettings >( "drawSettings", g_pDataModel->FindOrCreateFileId( "DagRenderPanelDrawSettings" ) ); + m_hDrawSettings = m_pDrawSettings; + + m_pMenuBar = new vgui::MenuBar( this, "Dag Render Panel Menu Bar" ); + + m_pShadingMenu = new vgui::Menu( NULL, "Shading Menu" ); + m_nMenuSmoothShade = m_pShadingMenu->AddCheckableMenuItem( "&Smooth Shade", new KeyValues( "SmoothShade" ), this ); + m_nMenuFlatShade = m_pShadingMenu->AddCheckableMenuItem( "&Flat Shade", new KeyValues( "FlatShade" ), this ); + m_nMenuWireframe = m_pShadingMenu->AddCheckableMenuItem( "&Wireframe", new KeyValues( "Wireframe" ), this ); + m_pShadingMenu->AddSeparator(); + m_nMenuBoundingBox = m_pShadingMenu->AddCheckableMenuItem( "&Bounding Box", new KeyValues( "BoundingBox" ), this ); + m_pShadingMenu->AddSeparator(); // Bug is visibility + m_nMenuNormals = m_pShadingMenu->AddCheckableMenuItem( "&Normals", new KeyValues( "Normals" ), this ); + m_nMenuWireframeOnShaded = m_pShadingMenu->AddCheckableMenuItem( "WireFrame &On Shaded", new KeyValues( "WireframeOnShaded" ), this ); + m_nMenuBackfaceCulling = m_pShadingMenu->AddCheckableMenuItem( "&Backface Culling", new KeyValues( "BackfaceCulling" ), this ); + m_nMenuXRay = m_pShadingMenu->AddCheckableMenuItem( "&X-Ray", new KeyValues( "XRay" ), this ); + m_nMenuGrayShade = m_pShadingMenu->AddCheckableMenuItem( "&Gray Shade", new KeyValues( "GrayShade" ), this ); + + // For now... + m_pShadingMenu->SetItemVisible( m_nMenuFlatShade, false ); + m_pShadingMenu->SetItemEnabled( m_nMenuFlatShade, false ); + m_pShadingMenu->SetItemVisible( m_nMenuBoundingBox, false ); + m_pShadingMenu->SetItemEnabled( m_nMenuBoundingBox, false ); + m_pShadingMenu->SetItemVisible( m_nMenuBackfaceCulling, false ); + m_pShadingMenu->SetItemEnabled( m_nMenuBackfaceCulling, false ); + m_pShadingMenu->SetItemVisible( m_nMenuXRay, false ); + m_pShadingMenu->SetItemEnabled( m_nMenuXRay, false ); + + m_pMenuBar->AddMenu( "&Shading", m_pShadingMenu ); + + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CDmeDagRenderPanel::~CDmeDagRenderPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Scheme settings +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + SetBorder( pScheme->GetBorder( "MenuBorder") ); + m_hFont = pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ); +} + + +//----------------------------------------------------------------------------- +// Set the scene +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::SetDmeElement( CDmeDag *pScene ) +{ + m_hDag = pScene; + ComputeDefaultTangentData( m_hDag, false ); +} + + +//----------------------------------------------------------------------------- +// Other methods which hook into DmePanel +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::SetDmeElement( CDmeSourceSkin *pSkin ) +{ + // First, try to grab the dependent makefile + CDmeMakefile *pSourceMakefile = pSkin->GetDependentMakefile(); + if ( !pSourceMakefile ) + { + m_hDag = NULL; + return; + } + + // Next, try to grab the output of that makefile + CDmElement *pOutput = pSourceMakefile->GetOutputElement( true ); + if ( !pOutput ) + { + m_hDag = NULL; + return; + } + + // Finally, grab the 'skin' attribute of that makefile + m_hDag = pOutput->GetValueElement< CDmeDag >( "model" ); + ComputeDefaultTangentData( m_hDag, false ); + DrawJoints( false ); + DrawJointNames( false ); +} + +void CDmeDagRenderPanel::SetDmeElement( CDmeSourceAnimation *pAnimation ) +{ + // First, try to grab the dependent makefile + CDmeMakefile *pSourceMakefile = pAnimation->GetDependentMakefile(); + if ( !pSourceMakefile ) + { + m_hDag = NULL; + return; + } + + // Next, try to grab the output of that makefile + CDmElement *pOutput = pSourceMakefile->GetOutputElement( true ); + if ( !pOutput ) + { + m_hDag = NULL; + return; + } + + // Finally, grab the 'model' or 'skeleton' attribute of that makefile + CDmeDag *pDag = pOutput->GetValueElement< CDmeDag >( "model" ); + if ( !pDag ) + { + pDag = pOutput->GetValueElement< CDmeDag >( "skeleton" ); + } + if ( !pDag ) + return; + + CDmeAnimationList *pAnimationList = pOutput->GetValueElement< CDmeAnimationList >( "animationList" ); + if ( !pAnimationList ) + return; + + if ( pAnimationList->FindAnimation( pAnimation->m_SourceAnimationName ) < 0 ) + return; + + m_hDag = pDag; + ComputeDefaultTangentData( m_hDag, false ); + m_hAnimationList = pAnimationList; + SelectAnimation( pAnimation->m_SourceAnimationName ); + DrawJoints( true ); + DrawJointNames( true ); +} + +void CDmeDagRenderPanel::SetDmeElement( CDmeDCCMakefile *pDCCMakefile ) +{ + // First, try to grab the dependent makefile + CDmElement *pOutputElement = pDCCMakefile->GetOutputElement( true ); + if ( !pOutputElement ) + { + m_hDag = NULL; + return; + } + + // Finally, grab the 'model' or 'skeleton' attribute of that makefile + CDmeDag *pDag = pOutputElement->GetValueElement< CDmeDag >( "model" ); + if ( !pDag ) + { + pDag = pOutputElement->GetValueElement< CDmeDag >( "skeleton" ); + } + if ( !pDag ) + return; + + CDmeAnimationList *pAnimationList = pOutputElement->GetValueElement< CDmeAnimationList >( "animationList" ); + + m_hDag = pDag; + ComputeDefaultTangentData( m_hDag, false ); + m_hAnimationList = pAnimationList; + SelectAnimation( 0 ); + DrawJoints( pAnimationList != NULL ); + DrawJointNames( pAnimationList != NULL ); +} + +CDmeDag *CDmeDagRenderPanel::GetDmeElement() +{ + return m_hDag; +} + + +//----------------------------------------------------------------------------- +// Draw joint names +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::DrawJointNames( CDmeDag *pRoot, CDmeDag *pDag, const matrix3x4_t& parentToWorld ) +{ + CDmeTransform *pJointTransform = pDag->GetTransform(); + int nJointIndex = -1; + + CDmeModel *pRootModel = CastElement<CDmeModel>( pRoot ); + if ( pRootModel ) + { + nJointIndex = pRootModel->GetJointTransformIndex( pJointTransform ); + if ( nJointIndex < 0 && pRootModel != pDag ) + return; + } + + matrix3x4_t jointToParent, jointToWorld; + pJointTransform->GetTransform( jointToParent ); + ConcatTransforms( parentToWorld, jointToParent, jointToWorld ); + + CDmeJoint *pJoint = CastElement< CDmeJoint >( pDag ); + if ( pJoint ) + { + Vector vecJointOrigin; + MatrixGetColumn( jointToWorld, 3, &vecJointOrigin ); + + Vector2D vecPanelPos; + ComputePanelPosition( vecJointOrigin, &vecPanelPos ); + + char pJointName[512]; + if ( nJointIndex >= 0 ) + { + Q_snprintf( pJointName, sizeof(pJointName), "%d : %s", nJointIndex, pJoint->GetName() ); + } + else + { + Q_snprintf( pJointName, sizeof(pJointName), "%s", pJoint->GetName() ); + } + g_pMatSystemSurface->DrawColoredText( m_hFont, vecPanelPos.x + 5, vecPanelPos.y, 255, 255, 255, 255, pJointName ); + } + + int nCount = pDag->GetChildCount(); + for ( int i = 0; i < nCount; ++i ) + { + CDmeDag *pChild = pDag->GetChild(i); + if ( !pChild ) + continue; + + DrawJointNames( pRoot, pChild, jointToWorld ); + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnSmoothShade() +{ + if ( m_pShadingMenu->IsChecked( m_nMenuSmoothShade ) ) + { + m_pDrawSettings->SetDrawType( CDmeDrawSettings::DRAW_SMOOTH ); + } + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnFlatShade() +{ + if ( m_pShadingMenu->IsChecked( m_nMenuFlatShade ) ) + { + m_pDrawSettings->SetDrawType( CDmeDrawSettings::DRAW_FLAT ); + } + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnWireframe() +{ + if ( m_pShadingMenu->IsChecked( m_nMenuWireframe ) ) + { + m_pDrawSettings->SetDrawType( CDmeDrawSettings::DRAW_WIREFRAME ); + } + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnBoundingBox() +{ + if ( m_pShadingMenu->IsChecked( m_nMenuBoundingBox ) ) + { + m_pDrawSettings->SetDrawType( CDmeDrawSettings::DRAW_BOUNDINGBOX ); + } + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnNormals() +{ + m_pDrawSettings->SetNormals( m_pShadingMenu->IsChecked( m_nMenuNormals ) ); + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnWireframeOnShaded() +{ + m_pDrawSettings->SetWireframeOnShaded( m_pShadingMenu->IsChecked( m_nMenuWireframeOnShaded ) ); + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnBackfaceCulling() +{ + m_pDrawSettings->SetBackfaceCulling( m_pShadingMenu->IsChecked( m_nMenuBackfaceCulling ) ); + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnXRay() +{ + m_pDrawSettings->SetXRay( m_pShadingMenu->IsChecked( m_nMenuXRay ) ); + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnGrayShade() +{ + m_pDrawSettings->SetGrayShade( m_pShadingMenu->IsChecked( m_nMenuGrayShade ) ); + UpdateMenu(); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnFrame() +{ + if ( !m_hDag ) + return; + + float flRadius; + Vector vecCenter, vecWorldCenter; + m_hDag->GetBoundingSphere( vecCenter, flRadius ); + + matrix3x4_t dmeToEngine; + CDmeDag::DmeToEngineMatrix( dmeToEngine ); + VectorTransform( vecCenter, dmeToEngine, vecWorldCenter ); + LookAt( vecWorldCenter, flRadius ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( m_pMenuBar->IsVisible() ) + { + int iWidth; + int iHeight; + GetSize( iWidth, iHeight ); + + int iMenuWidth; // Unused + int iMenuHeight; + m_pMenuBar->GetSize( iMenuWidth, iMenuHeight ); + m_pMenuBar->SetSize( iWidth, iMenuHeight ); + } +} + + +//----------------------------------------------------------------------------- +// paint it! +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::Paint() +{ + if ( m_hCurrentAnimation.Get() ) + { + DmeTime_t currentTime( Plat_FloatTime() - m_flStartTime ); + if ( m_hCurrentAnimation->GetDuration() != DMETIME_ZERO ) + { + currentTime = currentTime % m_hCurrentAnimation->GetDuration(); + } + else + { + currentTime = DMETIME_ZERO; + } + currentTime += m_hCurrentAnimation->GetStartTime(); + DmeTime_t mediaTime = m_hCurrentAnimation->ToChildMediaTime( currentTime, true ); + + int nChannelCount = m_hCurrentAnimation->m_Channels.Count(); + for ( int i = 0; i < nChannelCount; ++i ) + { + m_hCurrentAnimation->m_Channels[i]->SetCurrentTime( mediaTime ); + } + } + + if ( m_hCurrentVertexAnimation.Get() ) + { + DmeTime_t currentTime( Plat_FloatTime() - m_flStartTime ); + currentTime = currentTime % m_hCurrentVertexAnimation->GetDuration(); + currentTime += m_hCurrentVertexAnimation->GetStartTime(); + DmeTime_t mediaTime = m_hCurrentVertexAnimation->ToChildMediaTime( currentTime, true ); + + int nChannelCount = m_hCurrentVertexAnimation->m_Channels.Count(); + for ( int i = 0; i < nChannelCount; ++i ) + { + m_hCurrentVertexAnimation->m_Channels[i]->SetCurrentTime( mediaTime ); + } + } + + // FIXME: Shouldn't this happen at the application level? + // run the machinery - apply, resolve, dependencies, operate, resolve + { + CDisableUndoScopeGuard guard; + g_pDmElementFramework->SetOperators( m_operators ); + g_pDmElementFramework->Operate( true ); + } + + // allow elements and attributes to be edited again + g_pDmElementFramework->BeginEdit(); + + BaseClass::Paint(); + + // Overlay the joint names + if ( m_bDrawJointNames && m_hDag ) + { + matrix3x4_t modelToWorld; + CDmeDag::DmeToEngineMatrix( modelToWorld ); + DrawJointNames( m_hDag, m_hDag, modelToWorld ); + } +} + + +//----------------------------------------------------------------------------- +// Indicate we should draw joint names +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::DrawJointNames( bool bDrawJointNames ) +{ + m_bDrawJointNames = bDrawJointNames; +} + + +//----------------------------------------------------------------------------- +// Indicate we should draw joints +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::DrawJoints( bool bDrawJoint ) +{ + m_bDrawJoints = bDrawJoint; +} + + +//----------------------------------------------------------------------------- +// Indicate we should draw the grid +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::DrawGrid( bool bDrawGrid ) +{ + m_bDrawGrid = bDrawGrid; +} + + +//----------------------------------------------------------------------------- +// paint it! +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnPaint3D() +{ + CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); + ITexture *pLocalCube = pRenderContext->GetLocalCubemap(); + if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) + { + pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap ); + } + else + { + pRenderContext->BindLocalCubemap( m_DefaultHDREnvCubemap ); + } + + if ( m_bDrawGrid ) + { + BaseClass::DrawGrid(); + } + + if ( m_bDrawJoints ) + { + CDmeJoint::DrawJointHierarchy( true ); + } + + pRenderContext->CullMode( MATERIAL_CULLMODE_CW ); + CDmeDag::DrawUsingEngineCoordinates( true ); + m_pDrawSettings->DrawDag( m_hDag ); + CDmeDag::DrawUsingEngineCoordinates( false ); + + pRenderContext->Flush(); + pRenderContext->BindLocalCubemap( pLocalCube ); +} + + +//----------------------------------------------------------------------------- +// input +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnMouseDoublePressed( vgui::MouseCode code ) +{ + OnFrame(); + + BaseClass::OnMouseDoublePressed( code ); +} + + +//----------------------------------------------------------------------------- +// TODO: Have a whole groovy keybinding thingy like SFM +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::OnKeyCodePressed( vgui::KeyCode code ) +{ + BaseClass::OnKeyCodePressed( code ); + + if ( code == KEY_F ) + { + OnFrame(); + } +} + + +//----------------------------------------------------------------------------- +// Rebuilds the list of operators +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::RebuildOperatorList( ) +{ + m_operators.RemoveAll(); + + if ( m_hCurrentAnimation.Get() ) + { + int nChannelCount = m_hCurrentAnimation->m_Channels.Count(); + for ( int i = 0; i < nChannelCount; ++i ) + { + m_hCurrentAnimation->m_Channels[i]->SetMode( CM_PLAY ); + m_operators.AddToTail( m_hCurrentAnimation->m_Channels[i] ); + } + } + + if ( m_hCurrentVertexAnimation.Get() ) + { + int nChannelCount = m_hCurrentVertexAnimation->m_Channels.Count(); + for ( int i = 0; i < nChannelCount; ++i ) + { + m_hCurrentVertexAnimation->m_Channels[i]->SetMode( CM_PLAY ); + m_operators.AddToTail( m_hCurrentVertexAnimation->m_Channels[i] ); + } + } + + m_flStartTime = Plat_FloatTime(); +} + +//----------------------------------------------------------------------------- +// Select animation by index +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::SelectAnimation( int nIndex ) +{ + m_hCurrentAnimation = NULL; + if ( m_hAnimationList.Get() && ( nIndex >= 0 ) ) + { + // FIXME: How is this actually going to work? + m_hCurrentAnimation = m_hAnimationList->GetAnimation( nIndex ); + } + RebuildOperatorList(); +} + +void CDmeDagRenderPanel::SelectVertexAnimation( int nIndex ) +{ + m_hCurrentVertexAnimation = NULL; + if ( m_hVertexAnimationList.Get() && ( nIndex >= 0 ) ) + { + // FIXME: How is this actually going to work? + m_hCurrentVertexAnimation = m_hVertexAnimationList->GetAnimation( nIndex ); + } + RebuildOperatorList(); +} + + +//----------------------------------------------------------------------------- +// Select animation by name +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::SelectAnimation( const char *pAnimName ) +{ + if ( !pAnimName[0] ) + { + SelectAnimation( -1 ); + return; + } + + if ( m_hAnimationList ) + { + int nIndex = m_hAnimationList->FindAnimation( pAnimName ); + if ( nIndex >= 0 ) + { + SelectAnimation( nIndex ); + } + } +} + +void CDmeDagRenderPanel::SelectVertexAnimation( const char *pAnimName ) +{ + if ( !pAnimName[0] ) + { + SelectVertexAnimation( -1 ); + return; + } + + if ( m_hVertexAnimationList ) + { + int nIndex = m_hVertexAnimationList->FindAnimation( pAnimName ); + if ( nIndex >= 0 ) + { + SelectVertexAnimation( nIndex ); + } + } +} + + +//----------------------------------------------------------------------------- +// Sets animation +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::SetAnimationList( CDmeAnimationList *pAnimationList ) +{ + m_hAnimationList = pAnimationList; + int nCount = pAnimationList ? pAnimationList->GetAnimationCount() : 0; + if ( nCount == 0 ) + { + m_hCurrentAnimation = NULL; + return; + } + + SelectAnimation( 0 ); +} + + +void CDmeDagRenderPanel::SetVertexAnimationList( CDmeAnimationList *pAnimationList ) +{ + m_hVertexAnimationList = pAnimationList; + int nCount = pAnimationList ? pAnimationList->GetAnimationCount() : 0; + if ( nCount == 0 ) + { + m_hCurrentVertexAnimation = NULL; + return; + } + + SelectVertexAnimation( 0 ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmeDagRenderPanel::UpdateMenu() +{ + switch ( m_pDrawSettings->GetDrawType() ) + { + case CDmeDrawSettings::DRAW_FLAT: + m_pShadingMenu->SetMenuItemChecked( m_nMenuSmoothShade, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuFlatShade, true ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuWireframe, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuBoundingBox, false ); + break; + case CDmeDrawSettings::DRAW_WIREFRAME: + m_pShadingMenu->SetMenuItemChecked( m_nMenuSmoothShade, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuFlatShade, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuWireframe, true ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuBoundingBox, false ); + break; + case CDmeDrawSettings::DRAW_BOUNDINGBOX: + m_pShadingMenu->SetMenuItemChecked( m_nMenuSmoothShade, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuFlatShade, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuWireframe, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuBoundingBox, true ); + break; + default: + m_pShadingMenu->SetMenuItemChecked( m_nMenuSmoothShade, true ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuFlatShade, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuWireframe, false ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuBoundingBox, false ); + break; + } + + m_pShadingMenu->SetMenuItemChecked( m_nMenuNormals, m_pDrawSettings->GetNormals() ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuWireframeOnShaded, m_pDrawSettings->GetWireframeOnShaded() ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuBackfaceCulling, m_pDrawSettings->GetBackfaceCulling() ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuXRay, m_pDrawSettings->GetXRay() ); + m_pShadingMenu->SetMenuItemChecked( m_nMenuGrayShade, m_pDrawSettings->GetGrayShade() ); +} diff --git a/vgui2/dme_controls/dmelogeditpanel.cpp b/vgui2/dme_controls/dmelogeditpanel.cpp new file mode 100644 index 0000000..52117f8 --- /dev/null +++ b/vgui2/dme_controls/dmelogeditpanel.cpp @@ -0,0 +1,542 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "dme_controls/dmelogeditpanel.h" +#include "movieobjects/dmelog.h" +#include "vgui_controls/button.h" +#include "vgui_controls/combobox.h" +#include "tier1/KeyValues.h" + +using namespace vgui; + + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CDmeLogEditPanel::CDmeLogEditPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + SetVisible( false ); + m_flMinVertical = 0; + m_flMaxVertical = 256; +} + +CDmeLogEditPanel::~CDmeLogEditPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Converts normalized values to int time +//----------------------------------------------------------------------------- +DmeTime_t CDmeLogEditPanel::NormalizedToTime( float flIn ) +{ + return m_minTime + NormalizedToDuration( flIn ); +} + +DmeTime_t CDmeLogEditPanel::NormalizedToDuration( float flDuration ) +{ + flDuration = clamp( flDuration, 0.0f, 1.0f ); + return flDuration * ( m_maxTime - m_minTime ); +} + +float CDmeLogEditPanel::TimeToNormalized( DmeTime_t time ) +{ + if ( m_maxTime == m_minTime ) + return 0.0f; + return GetFractionOfTimeBetween( time, m_minTime, m_maxTime, true ); +} + +float CDmeLogEditPanel::NormalizedToValue( float flValue ) +{ + return Lerp( flValue, m_flMinVertical, m_flMaxVertical ); +} + +float CDmeLogEditPanel::ValueToNormalized( float flNormalized ) +{ + if ( m_flMaxVertical == m_flMinVertical ) + return 0.0f; + return (flNormalized - m_flMinVertical) / ( m_flMaxVertical - m_flMinVertical ); +} + + +//----------------------------------------------------------------------------- +// Control points + values... +//----------------------------------------------------------------------------- +int CDmeLogEditPanel::FindOrAddControlPoint( float flIn, float flTolerance, float flOut ) +{ + Assert( m_hLog.Get() ); + DmeTime_t time = NormalizedToTime( flIn ); + DmeTime_t tolerance = ( flTolerance >= 0 ) ? NormalizedToDuration( flTolerance ) : DmeTime_t( 0 ); + float flValue = NormalizedToValue( flOut ); + + int nKeyIndex = -1; + + Assert( m_hLog.Get() ); + switch( m_hLog->GetDataType() ) + { + case AT_BOOL: + nKeyIndex = CastElement<CDmeBoolLog >( m_hLog )->FindOrAddKey( time, tolerance, (bool)(flValue >= 0.5f) ); + break; + + case AT_INT: + nKeyIndex = CastElement<CDmeIntLog >( m_hLog )->FindOrAddKey( time, tolerance, (int)(flValue + 0.5f) ); + break; + + case AT_FLOAT: + nKeyIndex = CastElement<CDmeFloatLog >( m_hLog )->FindOrAddKey( time, tolerance, flValue ); + break; + + case AT_COLOR: + { + Color c = CastElement<CDmeColorLog >( m_hLog )->GetValue( time ); + int nComp = (int)( flValue + 0.5f ); + nComp = clamp( nComp, 0, 255 ); + for ( int i = 0; i < 4; ++i ) + { + if ( m_LogFieldMask & (1 << i) ) + { + c[i] = (unsigned char)nComp; + } + } + nKeyIndex = CastElement<CDmeColorLog >( m_hLog )->FindOrAddKey( time, tolerance, c ); + } + break; + + case AT_VECTOR2: + nKeyIndex = FindOrAddKey< Vector2D >( time, tolerance, 2, flValue ); + break; + + case AT_VECTOR3: + nKeyIndex = FindOrAddKey< Vector >( time, tolerance, 3, flValue ); + break; + + case AT_VECTOR4: + nKeyIndex = FindOrAddKey< Vector4D >( time, tolerance, 4, flValue ); + break; + + case AT_QANGLE: + nKeyIndex = FindOrAddKey< QAngle >( time, tolerance, 3, flValue ); + break; + + case AT_QUATERNION: + nKeyIndex = FindOrAddKey< Quaternion >( time, tolerance, 4, flValue ); + break; + } + return nKeyIndex; +} + + +//----------------------------------------------------------------------------- +// Finds a control point within tolerance +//----------------------------------------------------------------------------- +int CDmeLogEditPanel::FindControlPoint( float flIn, float flTolerance ) +{ + Assert( m_hLog.Get() ); + DmeTime_t time = NormalizedToTime( flIn ); + DmeTime_t tolerance = NormalizedToDuration( flTolerance ); + return m_hLog->FindKeyWithinTolerance( time, tolerance ); +} + + +//----------------------------------------------------------------------------- +// Modifies an existing control point +//----------------------------------------------------------------------------- +int CDmeLogEditPanel::ModifyControlPoint( int nPoint, float flIn, float flOut ) +{ + Assert( m_hLog.Get() ); + DmeTime_t time = NormalizedToTime( flIn ); + DmeTime_t initialTime = m_hLog->GetKeyTime( nPoint ); + float flValue = NormalizedToValue( flOut ); + + int nKeyIndex = -1; + + Assert( m_hLog.Get() ); + switch( m_hLog->GetDataType() ) + { + case AT_BOOL: + RemoveControlPoint( nPoint ); + nKeyIndex = CastElement<CDmeBoolLog >( m_hLog )->FindOrAddKey( time, DmeTime_t( 0 ), (bool)(flValue >= 0.5f) ); + break; + + case AT_INT: + RemoveControlPoint( nPoint ); + nKeyIndex = CastElement<CDmeIntLog >( m_hLog )->FindOrAddKey( time, DmeTime_t( 0 ), (int)(flValue + 0.5f) ); + break; + + case AT_FLOAT: + RemoveControlPoint( nPoint ); + nKeyIndex = CastElement<CDmeFloatLog >( m_hLog )->FindOrAddKey( time, DmeTime_t( 0 ), flValue ); + break; + + case AT_COLOR: + { + Color c = CastElement<CDmeColorLog >( m_hLog )->GetValue( initialTime ); + int nComp = (int)( flValue + 0.5f ); + nComp = clamp( nComp, 0, 255 ); + for ( int i = 0; i < 4; ++i ) + { + if ( m_LogFieldMask & (1 << i) ) + { + c[i] = (unsigned char)nComp; + } + } + RemoveControlPoint( nPoint ); + nKeyIndex = CastElement<CDmeColorLog >( m_hLog )->FindOrAddKey( time, DmeTime_t( 0 ), c ); + } + break; + + case AT_VECTOR2: + nKeyIndex = ModifyKey< Vector2D >( nPoint, initialTime, time, 2, flValue ); + break; + + case AT_VECTOR3: + nKeyIndex = ModifyKey< Vector >( nPoint, initialTime, time, 3, flValue ); + break; + + case AT_VECTOR4: + nKeyIndex = ModifyKey< Vector4D >( nPoint, initialTime, time, 4, flValue ); + break; + + case AT_QANGLE: + nKeyIndex = ModifyKey< QAngle >( nPoint, initialTime, time, 3, flValue ); + break; + + case AT_QUATERNION: + nKeyIndex = ModifyKey< Quaternion >( nPoint, initialTime, time, 4, flValue ); + break; + } + return nKeyIndex; +} + + +//----------------------------------------------------------------------------- +// Removes a single control point +//----------------------------------------------------------------------------- +void CDmeLogEditPanel::RemoveControlPoint( int nPoint ) +{ + Assert( m_hLog.Get() ); + m_hLog->RemoveKey( nPoint ); +} + + +//----------------------------------------------------------------------------- +// Gets the interpolated value of the log based on normalized time +//----------------------------------------------------------------------------- +float CDmeLogEditPanel::GetValue( float flIn ) +{ + DmeTime_t time = NormalizedToTime( flIn ); + + float flValue = 0.0f; + + Assert( m_hLog.Get() ); + switch( m_hLog->GetDataType() ) + { + case AT_BOOL: + flValue = CastElement<CDmeBoolLog >( m_hLog )->GetValue( time ); + break; + + case AT_INT: + flValue = CastElement<CDmeIntLog >( m_hLog )->GetValue( time ); + break; + + case AT_FLOAT: + flValue = CastElement<CDmeFloatLog >( m_hLog )->GetValue( time ); + break; + + case AT_COLOR: + { + Color c = CastElement<CDmeColorLog >( m_hLog )->GetValue( time ); + flValue = c[m_nFieldIndex]; + } + break; + + case AT_VECTOR2: + flValue = CastElement<CDmeVector2Log >( m_hLog )->GetValue( time )[m_nFieldIndex]; + break; + + case AT_VECTOR3: + flValue = CastElement<CDmeVector3Log >( m_hLog )->GetValue( time )[m_nFieldIndex]; + break; + + case AT_VECTOR4: + flValue = CastElement<CDmeVector2Log >( m_hLog )->GetValue( time )[m_nFieldIndex]; + break; + + case AT_QANGLE: + flValue = CastElement<CDmeQAngleLog >( m_hLog )->GetValue( time )[m_nFieldIndex]; + break; + + case AT_QUATERNION: + flValue = CastElement<CDmeQuaternionLog >( m_hLog )->GetValue( time )[m_nFieldIndex]; + break; + } + + return ValueToNormalized( flValue ); +} + +int CDmeLogEditPanel::ControlPointCount() +{ + Assert( m_hLog.Get() ); + return m_hLog->GetKeyCount( ); +} + + +//----------------------------------------------------------------------------- +// Gets a particular control point's value +//----------------------------------------------------------------------------- +void CDmeLogEditPanel::GetControlPoint( int nPoint, float *pIn, float *pOut ) +{ + Assert( m_hLog.Get() ); + DmeTime_t time = m_hLog->GetKeyTime( nPoint ); + *pIn = TimeToNormalized( time ); + + float flValue = 0.0f; + + Assert( m_hLog.Get() ); + switch( m_hLog->GetDataType() ) + { + case AT_BOOL: + flValue = CastElement<CDmeBoolLog >( m_hLog )->GetKeyValue( nPoint ); + break; + + case AT_INT: + flValue = CastElement<CDmeIntLog >( m_hLog )->GetKeyValue( nPoint ); + break; + + case AT_FLOAT: + flValue = CastElement<CDmeFloatLog >( m_hLog )->GetKeyValue( nPoint ); + break; + + case AT_COLOR: + { + Color c = CastElement<CDmeColorLog >( m_hLog )->GetKeyValue( nPoint ); + flValue = c[m_nFieldIndex]; + } + break; + + case AT_VECTOR2: + flValue = CastElement<CDmeVector2Log >( m_hLog )->GetKeyValue( nPoint )[m_nFieldIndex]; + break; + + case AT_VECTOR3: + flValue = CastElement<CDmeVector3Log >( m_hLog )->GetKeyValue( nPoint )[m_nFieldIndex]; + break; + + case AT_VECTOR4: + flValue = CastElement<CDmeVector2Log >( m_hLog )->GetKeyValue( nPoint )[m_nFieldIndex]; + break; + + case AT_QANGLE: + flValue = CastElement<CDmeQAngleLog >( m_hLog )->GetKeyValue( nPoint )[m_nFieldIndex]; + break; + + case AT_QUATERNION: + flValue = CastElement<CDmeQuaternionLog >( m_hLog )->GetKeyValue( nPoint )[m_nFieldIndex]; + break; + } + + *pOut = ValueToNormalized( flValue ); +} + + +//----------------------------------------------------------------------------- +// Sets the log to edit +//----------------------------------------------------------------------------- +void CDmeLogEditPanel::SetDmeLog( CDmeLog *pLog ) +{ + bool bValid = pLog && ( pLog->GetDataType() == AT_INT || pLog->GetDataType() == AT_FLOAT || pLog->GetDataType() == AT_COLOR ); + if ( bValid ) + { + m_hLog = pLog; + } + else + { + m_minTime.SetSeconds( 0.0f ); + m_maxTime.SetSeconds( 0.0f ); + } + SetVisible( bValid ); +} + + +void CDmeLogEditPanel::SetMask( int nMask ) +{ + m_LogFieldMask = nMask; + m_nFieldIndex = 0; + for ( int i = 0; i < 4; ++i ) + { + if ( m_LogFieldMask & (1 << i) ) + { + m_nFieldIndex = i; + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Sets the time range on the view in ms +//----------------------------------------------------------------------------- +void CDmeLogEditPanel::SetTimeRange( DmeTime_t startTime, DmeTime_t endTime ) +{ + m_minTime = startTime; + m_maxTime = endTime; +} + + +//----------------------------------------------------------------------------- +// Sets the vertical range on the view +//----------------------------------------------------------------------------- +void CDmeLogEditPanel::SetVerticalRange( float flMin, float flMax ) +{ + m_flMinVertical = flMin; + m_flMaxVertical = flMax; +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CDmeLogEditFrame::CDmeLogEditFrame( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "DmeLogEditFrame" ) +{ + m_pContextKeyValues = NULL; + SetDeleteSelfOnClose( true ); + m_pCurveEditor = new CDmeLogEditPanel( this, "DmeLogEditPanel" ); + m_pOkButton = new Button( this, "OkButton", "#GameUI_OK", this, "Ok" ); + m_pCancelButton = new Button( this, "CancelButton", "#GameUI_Cancel", this, "Cancel" ); + m_pFilter = new ComboBox( this, "LogFilter", 5, false ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/dmelogeditframe.res" ); + + SetTitle( pTitle, false ); +} + +CDmeLogEditFrame::~CDmeLogEditFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CDmeLogEditFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when the combo box changes +//----------------------------------------------------------------------------- +void CDmeLogEditFrame::OnTextChanged( ) +{ + KeyValues *pKeyValues = m_pFilter->GetActiveItemUserData(); + int nMask = pKeyValues->GetInt( "Value", CDmeLogEditPanel::FIELD_ALL ); + m_pCurveEditor->SetMask( nMask ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void CDmeLogEditFrame::DoModal( CDmeLog *pLog, DmeTime_t startTime, DmeTime_t endTime, KeyValues *pKeyValues ) +{ + CleanUpMessage(); + m_pContextKeyValues = pKeyValues; + m_pCurveEditor->SetDmeLog( pLog ); + m_pCurveEditor->SetTimeRange( startTime, endTime ); + + m_pFilter->SetVisible( true ); + m_pFilter->RemoveAll(); + + switch( pLog->GetDataType() ) + { + case AT_BOOL: + case AT_INT: + case AT_FLOAT: + m_pFilter->SetVisible( false ); + break; + + case AT_COLOR: + m_pFilter->AddItem( "RGB Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_R | CDmeLogEditPanel::FIELD_G | CDmeLogEditPanel::FIELD_B ) ); + m_pFilter->AddItem( "Red Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_R ) ); + m_pFilter->AddItem( "Green Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_G ) ); + m_pFilter->AddItem( "Blue Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_B ) ); + m_pFilter->AddItem( "Alpha Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_A ) ); + break; + + case AT_VECTOR2: + m_pFilter->AddItem( "X Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_X ) ); + m_pFilter->AddItem( "Y Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_Y ) ); + break; + + case AT_VECTOR3: + case AT_QANGLE: + m_pFilter->AddItem( "X Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_X ) ); + m_pFilter->AddItem( "Y Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_Y ) ); + m_pFilter->AddItem( "Z Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_Z ) ); + break; + + case AT_VECTOR4: + case AT_QUATERNION: + m_pFilter->AddItem( "X Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_X ) ); + m_pFilter->AddItem( "Y Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_Y ) ); + m_pFilter->AddItem( "Z Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_Z ) ); + m_pFilter->AddItem( "W Channel", new KeyValues( "Mask", "Value", CDmeLogEditPanel::FIELD_W ) ); + break; + } + + if ( m_pFilter->IsVisible() ) + { + // Will cause the mask to be set + m_pFilter->ActivateItemByRow( 0 ); + } + else + { + m_pCurveEditor->SetMask( CDmeLogEditPanel::FIELD_ALL ); + } + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CDmeLogEditFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Ok" ) ) + { + KeyValues *pActionKeys = new KeyValues( "LogEdited" ); + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + + // This prevents them from being deleted later + m_pContextKeyValues = NULL; + } + + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +}
\ No newline at end of file diff --git a/vgui2/dme_controls/dmemdlpanel.cpp b/vgui2/dme_controls/dmemdlpanel.cpp new file mode 100644 index 0000000..389d86e --- /dev/null +++ b/vgui2/dme_controls/dmemdlpanel.cpp @@ -0,0 +1,47 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/dmemdlpanel.h" +#include "dme_controls/dmecontrols.h" +#include "dme_controls/dmepanel.h" +#include "movieobjects/dmemdl.h" +#include "movieobjects/dmemdlmakefile.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +IMPLEMENT_DMEPANEL_FACTORY( CDmeMDLPanel, DmeMDLMakefile, "DmeMakeFileOutputPreview", "MDL MakeFile Output Preview", false ); + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CDmeMDLPanel::CDmeMDLPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ +} + +CDmeMDLPanel::~CDmeMDLPanel() +{ +} + + +//----------------------------------------------------------------------------- +// DMEPanel.. +//----------------------------------------------------------------------------- +void CDmeMDLPanel::SetDmeElement( CDmeMDLMakefile *pMDLMakefile ) +{ + if ( pMDLMakefile != NULL ) + { + CDmeMDL *pMDL = CastElement< CDmeMDL >( pMDLMakefile->GetOutputElement( true ) ); + if ( pMDL ) + { + SetMDL( pMDL->GetMDL() ); + } + } +} diff --git a/vgui2/dme_controls/dmepanel.cpp b/vgui2/dme_controls/dmepanel.cpp new file mode 100644 index 0000000..b653a1f --- /dev/null +++ b/vgui2/dme_controls/dmepanel.cpp @@ -0,0 +1,656 @@ +//========= 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; +} + diff --git a/vgui2/dme_controls/dmepicker.cpp b/vgui2/dme_controls/dmepicker.cpp new file mode 100644 index 0000000..749ea9e --- /dev/null +++ b/vgui2/dme_controls/dmepicker.cpp @@ -0,0 +1,280 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/DmePicker.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/Button.h" +#include "datamodel/dmelement.h" +#include "vgui/ISurface.h" +#include "vgui/iinput.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; + + +//----------------------------------------------------------------------------- +// +// Dme Picker +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Sort by MDL name +//----------------------------------------------------------------------------- +static int __cdecl DmeBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("dme"); + const char *string2 = item2.kv->GetString("dme"); + return stricmp( string1, string2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CDmePicker::CDmePicker( vgui::Panel *pParent ) : BaseClass( pParent, "DmePicker" ) +{ + // FIXME: Make this an image browser + m_pDmeBrowser = new vgui::ListPanel( this, "DmeBrowser" ); + m_pDmeBrowser->AddColumnHeader( 0, "dme", "Dme Elements", 52, 0 ); + m_pDmeBrowser->SetSelectIndividualCells( true ); + m_pDmeBrowser->SetEmptyListText( "No Dme Elements" ); + m_pDmeBrowser->SetDragEnabled( true ); + m_pDmeBrowser->AddActionSignalTarget( this ); + m_pDmeBrowser->SetSortFunc( 0, DmeBrowserSortFunc ); + m_pDmeBrowser->SetSortColumn( 0 ); + + // filter selection + m_pFilterList = new TextEntry( this, "FilterList" ); + m_pFilterList->AddActionSignalTarget( this ); + m_pFilterList->RequestFocus(); + + LoadControlSettingsAndUserConfig( "resource/dmepicker.res" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CDmePicker::~CDmePicker() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: called to open +//----------------------------------------------------------------------------- +void CDmePicker::Activate( const CUtlVector< DmePickerInfo_t >&vec ) +{ + m_pDmeBrowser->RemoveAll(); + + int nCount = vec.Count(); + for ( int i = 0; i < nCount; ++i ) + { + CDmElement *pElement = GetElement<CDmElement>( vec[i].m_hElement ); + const char *pElementName = pElement ? pElement->GetName() : "<null element>"; + const char *pItemName = vec[i].m_pChoiceString ? vec[i].m_pChoiceString : pElementName; + + KeyValues *kv = new KeyValues( "node", "dme", pItemName ); + kv->SetInt( "dmeHandle", vec[i].m_hElement ); + int nItemID = m_pDmeBrowser->AddItem( kv, 0, false, false ); + + KeyValues *pDrag = new KeyValues( "drag", "text", pElementName ); + pDrag->SetString( "texttype", "dmeName" ); + pDrag->SetInt( "dmeelement", vec[i].m_hElement ); + m_pDmeBrowser->SetItemDragData( nItemID, pDrag ); + } + + RefreshDmeList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDmePicker::OnKeyCodePressed( KeyCode code ) +{ + if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN )) + { + KeyValues *pMsg = new KeyValues("KeyCodePressed", "code", code); + vgui::ipanel()->SendMessage( m_pDmeBrowser->GetVPanel(), pMsg, GetVPanel()); + pMsg->deleteThis(); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes the file list +//----------------------------------------------------------------------------- +void CDmePicker::RefreshDmeList() +{ + // Check the filter matches + int nMatchingElements = 0; + int nTotalCount = 0; + for ( int nItemID = m_pDmeBrowser->FirstItem(); nItemID != m_pDmeBrowser->InvalidItemID(); nItemID = m_pDmeBrowser->NextItem( nItemID ) ) + { + KeyValues *kv = m_pDmeBrowser->GetItem( nItemID ); + const char *pElementName = kv->GetString( "dme" ); + bool bIsVisible = !m_Filter.Length() || Q_stristr( pElementName, m_Filter.Get() ); + m_pDmeBrowser->SetItemVisible( nItemID, bIsVisible ); + if ( bIsVisible ) + { + ++nMatchingElements; + } + ++nTotalCount; + } + m_pDmeBrowser->SortList(); + + char pColumnTitle[512]; + Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)", + "Dme Elements", nMatchingElements, nTotalCount ); + m_pDmeBrowser->SetColumnHeaderText( 0, pColumnTitle ); + + if ( ( m_pDmeBrowser->GetItemCount() > 0 ) && ( m_pDmeBrowser->GetSelectedItemsCount() == 0 ) ) + { + int nItemID = m_pDmeBrowser->GetItemIDFromRow( 0 ); + m_pDmeBrowser->SetSelectedCell( nItemID, 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on text changing +//----------------------------------------------------------------------------- +void CDmePicker::OnTextChanged( ) +{ + int nLength = m_pFilterList->GetTextLength(); + m_Filter.SetLength( nLength ); + if ( nLength > 0 ) + { + m_pFilterList->GetText( m_Filter.GetForModify(), nLength+1 ); + } + RefreshDmeList(); +} + + +//----------------------------------------------------------------------------- +// Returns the selceted model name +//----------------------------------------------------------------------------- +CDmElement *CDmePicker::GetSelectedDme( ) +{ + if ( m_pDmeBrowser->GetSelectedItemsCount() == 0 ) + return NULL; + + int nIndex = m_pDmeBrowser->GetSelectedItem( 0 ); + KeyValues *pItemKeyValues = m_pDmeBrowser->GetItem( nIndex ); + return GetElementKeyValue< CDmElement >( pItemKeyValues, "dmeHandle" ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CDmePickerFrame::CDmePickerFrame( vgui::Panel *pParent, const char *pTitle ) : +BaseClass( pParent, "DmePickerFrame" ) +{ + m_pContextKeyValues = NULL; + SetDeleteSelfOnClose( true ); + m_pPicker = new CDmePicker( this ); + m_pPicker->AddActionSignalTarget( this ); + m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" ); + m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/dmepickerframe.res" ); + + SetTitle( pTitle, false ); +} + +CDmePickerFrame::~CDmePickerFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CDmePickerFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void CDmePickerFrame::DoModal( const CUtlVector< DmePickerInfo_t >& vec, KeyValues *pKeyValues ) +{ + CleanUpMessage(); + m_pContextKeyValues = pKeyValues; + m_pPicker->Activate( vec ); + m_pOpenButton->SetEnabled( vec.Count() != 0 ); + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CDmePickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Open" ) ) + { + CDmElement *pElement = m_pPicker->GetSelectedDme( ); + + KeyValues *pActionKeys = new KeyValues( "DmeSelected" ); + SetElementKeyValue( pActionKeys, "dme", pElement ); + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + + // This prevents them from being deleted later + m_pContextKeyValues = NULL; + } + + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + KeyValues *pActionKeys = new KeyValues( "DmeSelectionCancelled" ); + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + + // This prevents them from being deleted later + m_pContextKeyValues = NULL; + } + + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + diff --git a/vgui2/dme_controls/dmepresetgroupeditorpanel.cpp b/vgui2/dme_controls/dmepresetgroupeditorpanel.cpp new file mode 100644 index 0000000..a2e9f34 --- /dev/null +++ b/vgui2/dme_controls/dmepresetgroupeditorpanel.cpp @@ -0,0 +1,2332 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "dme_controls/dmepresetgroupeditorpanel.h" +#include "dme_controls/dmecontrols_utils.h" +#include "movieobjects/dmeanimationset.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/InputDialog.h" +#include "vgui_controls/TextEntry.h" +#include "vgui/MouseCode.h" +#include "vgui/IInput.h" +#include "vgui/ISurface.h" +#include "tier1/KeyValues.h" +#include "tier1/utldict.h" +#include "dme_controls/presetpicker.h" +#include "vgui_controls/FileOpenDialog.h" +#include "tier2/fileutils.h" +#include "tier1/utlbuffer.h" +#include "dme_controls/inotifyui.h" +#include "../game/shared/iscenetokenprocessor.h" +#include "movieobjects/dmx_to_vcd.h" +#include "studio.h" +#include "phonemeconverter.h" + +// Forward declaration +class CDmePresetGroupEditorPanel; + + +//----------------------------------------------------------------------------- +// Utility scope guards +//----------------------------------------------------------------------------- +DEFINE_SOURCE_UNDO_SCOPE_GUARD( PresetGroup, NOTIFY_SOURCE_PRESET_GROUP_EDITOR ); +DEFINE_SOURCE_NOTIFY_SCOPE_GUARD( PresetGroup, NOTIFY_SOURCE_PRESET_GROUP_EDITOR ); + +#define PRESET_FILE_FORMAT "preset" + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// CDmePresetRemapPanel +// +// Implementation below because of scoping issues +// +//----------------------------------------------------------------------------- +class CDmePresetRemapPanel : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CDmePresetRemapPanel, vgui::Frame ); + +public: + CDmePresetRemapPanel( vgui::Panel *pParent, const char *pTitle ); + ~CDmePresetRemapPanel(); + + // Shows the modal dialog + void DoModal( CDmeAnimationSet *pAnimationSet, CDmePresetGroup *pDestGroup ); + + // Inherited from Frame + virtual void OnCommand( const char *pCommand ); + + virtual void OnKeyCodeTyped( KeyCode code ); + +private: + MESSAGE_FUNC( OnTextChanged, "TextChanged" ); + MESSAGE_FUNC( OnSelectPreset, "SelectPreset" ); + MESSAGE_FUNC( OnRemovePreset, "RemovePreset" ); + MESSAGE_FUNC_PARAMS( OnPresetPicked, "PresetPicked", params ); + MESSAGE_FUNC_PARAMS( OnOpenContextMenu, "OpenContextMenu", kv ); + + // Refreshes the list of presets + void RefreshPresetList( ); + + // Applies changes to the preset remap + void ApplyChangesToPresetRemap(); + + // Cleans up the context menu + void CleanupContextMenu(); + + vgui::ListPanel *m_pPresetRemapList; + vgui::ComboBox *m_pSourcePresetGroup; + CDmeHandle< CDmePresetGroup > m_hSourceGroup; + CDmeHandle< CDmePresetGroup > m_hDestGroup; + vgui::DHANDLE< vgui::Menu > m_hContextMenu; +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +static int __cdecl DestPresetNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString( "dest" ); + const char *string2 = item2.kv->GetString( "dest" ); + return Q_stricmp( string1, string2 ); +} + +static int __cdecl SrcPresetNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString( "src" ); + const char *string2 = item2.kv->GetString( "src" ); + return Q_stricmp( string1, string2 ); +} + +CDmePresetRemapPanel::CDmePresetRemapPanel( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "DmePresetRemapPanel" ) +{ + m_pSourcePresetGroup = new vgui::ComboBox( this, "SourcePresetGroup", 8, true ); + SetDeleteSelfOnClose( true ); + + m_pPresetRemapList = new vgui::ListPanel( this, "PresetRemapList" ); + m_pPresetRemapList->AddColumnHeader( 0, "dest", "Dest Preset", 100, 0 ); + m_pPresetRemapList->AddColumnHeader( 1, "src", "Source Preset", 100, 0 ); + m_pPresetRemapList->SetSelectIndividualCells( false ); + m_pPresetRemapList->SetMultiselectEnabled( true ); + m_pPresetRemapList->SetEmptyListText( "No presets" ); + m_pPresetRemapList->AddActionSignalTarget( this ); + m_pPresetRemapList->SetSortFunc( 0, DestPresetNameSortFunc ); + m_pPresetRemapList->SetSortFunc( 1, SrcPresetNameSortFunc ); + m_pPresetRemapList->SetSortColumn( 0 ); + + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/presetremappanel.res" ); + + SetTitle( pTitle, false ); +} + +CDmePresetRemapPanel::~CDmePresetRemapPanel() +{ + CleanupContextMenu(); +} + + +//----------------------------------------------------------------------------- +// Cleans up the context menu +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::CleanupContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + m_hContextMenu->MarkForDeletion(); + m_hContextMenu = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Refreshes the list of presets +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::RefreshPresetList( ) +{ + m_pPresetRemapList->RemoveAll(); + + CDmaElementArray< CDmePreset > *pPresetList = m_hDestGroup.Get() ? &m_hDestGroup->GetPresets() : NULL; + if ( !pPresetList ) + return; + + int nCount = pPresetList->Count(); + if ( nCount == 0 ) + return; + + CDmePresetRemap *pRemap = m_hDestGroup->GetPresetRemap(); + bool bUseRemap = ( pRemap && m_hSourceGroup.Get() && !Q_stricmp( pRemap->m_SourcePresetGroup, m_hSourceGroup->GetName() ) ); + + for ( int i = 0; i < nCount; ++i ) + { + CDmePreset *pPreset = pPresetList->Get(i); + + const char *pName = pPreset->GetName(); + if ( !pName || !pName[0] ) + { + pName = "<no name>"; + } + + KeyValues *kv = new KeyValues( "node" ); + kv->SetString( "dest", pName ); + SetElementKeyValue( kv, "destPreset", pPreset ); + if ( bUseRemap ) + { + const char *pSource = pRemap->FindSourcePreset( pName ); + CDmePreset *pSrcPreset = pSource ? m_hSourceGroup->FindPreset( pSource ) : NULL; + kv->SetString( "src", pSrcPreset ? pSrcPreset->GetName() : "" ); + SetElementKeyValue( kv, "srcPreset", pSrcPreset ); + } + else + { + kv->SetString( "src", "" ); + SetElementKeyValue( kv, "srcPreset", NULL ); + } + + m_pPresetRemapList->AddItem( kv, 0, false, false ); + } + m_pPresetRemapList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Called by the preset picker when a preset is picked +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::OnPresetPicked( KeyValues *pParams ) +{ + int nSelectedItemCount = m_pPresetRemapList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + CDmePreset *pPreset = GetElementKeyValue< CDmePreset >( pParams, "preset" ); + int nItemID = m_pPresetRemapList->GetSelectedItem( 0 ); + KeyValues *kv = m_pPresetRemapList->GetItem( nItemID ); + kv->SetString( "src", pPreset ? pPreset->GetName() : "" ); + SetElementKeyValue( kv, "srcPreset", pPreset ); + m_pPresetRemapList->ApplyItemChanges( nItemID ); +} + + +//----------------------------------------------------------------------------- +// Called when double-clicking on a list entry +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::OnKeyCodeTyped( KeyCode code ) +{ + if ( code == KEY_ENTER ) + { + OnSelectPreset(); + return; + } + + if ( code == KEY_DELETE || code == KEY_BACKSPACE ) + { + OnRemovePreset(); + return; + } + + BaseClass::OnKeyCodeTyped( code ); +} + + +//----------------------------------------------------------------------------- +// Called by the context menu +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::OnSelectPreset() +{ + int nSelectedItemCount = m_pPresetRemapList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + CPresetPickerFrame *pPresetPicker = new CPresetPickerFrame( this, "Select Source Preset", false ); + pPresetPicker->AddActionSignalTarget( this ); + pPresetPicker->DoModal( m_hSourceGroup, false, NULL ); +} + +void CDmePresetRemapPanel::OnRemovePreset() +{ + int nSelectedItemCount = m_pPresetRemapList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + int nItemID = m_pPresetRemapList->GetSelectedItem( 0 ); + KeyValues *kv = m_pPresetRemapList->GetItem( nItemID ); + kv->SetString( "src", "" ); + SetElementKeyValue( kv, "srcPreset", NULL ); + m_pPresetRemapList->ApplyItemChanges( nItemID ); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::OnOpenContextMenu( KeyValues *kv ) +{ + CleanupContextMenu(); + + int nSelectedItemCount = m_pPresetRemapList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + m_hContextMenu = new vgui::Menu( this, "ActionMenu" ); + m_hContextMenu->AddMenuItem( "#DmePresetRemapPanel_SelectPreset", new KeyValues( "SelectPreset" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetRemapPanel_RemovePreset", new KeyValues( "RemovePreset" ), this ); + + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Called when the dest combo list changes +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::OnTextChanged() +{ + KeyValues *pCurrentGroup = m_pSourcePresetGroup->GetActiveItemUserData(); + m_hSourceGroup = pCurrentGroup ? GetElementKeyValue<CDmePresetGroup>( pCurrentGroup, "presetGroup" ) : NULL; + RefreshPresetList(); +} + + +void CDmePresetRemapPanel::DoModal( CDmeAnimationSet *pAnimationSet, CDmePresetGroup *pDestGroup ) +{ + m_hDestGroup = pDestGroup; + + m_pSourcePresetGroup->DeleteAllItems(); + + bool bSelected = false; + + CDmePresetRemap* pRemap = m_hDestGroup->GetPresetRemap(); + + // Populate the combo box with preset group names + const CDmaElementArray< CDmePresetGroup > &presetGroupList = pAnimationSet->GetPresetGroups(); + int nCount = presetGroupList.Count(); + for ( int i = 0; i < nCount; ++i ) + { + CDmePresetGroup *pPresetGroup = presetGroupList[i]; + if ( pPresetGroup == m_hDestGroup.Get() ) + continue; + + KeyValues *kv = new KeyValues( "entry" ); + SetElementKeyValue( kv, "presetGroup", pPresetGroup ); + int nItemID = m_pSourcePresetGroup->AddItem( pPresetGroup->GetName(), kv ); + if ( !bSelected || ( pRemap && !Q_stricmp( pRemap->m_SourcePresetGroup, pPresetGroup->GetName() ) ) ) + { + m_pSourcePresetGroup->ActivateItem( nItemID ); + bSelected = true; + } + } + + BaseClass::DoModal( ); + + m_pSourcePresetGroup->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Applies changes to the preset remap +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::ApplyChangesToPresetRemap() +{ + int nTextLength = m_pSourcePresetGroup->GetTextLength() + 1; + char* pSourceName = (char*)_alloca( nTextLength * sizeof(char) ); + m_pSourcePresetGroup->GetText( pSourceName, nTextLength ); + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Change Preset Remap" ); + CDmePresetRemap *pPresetRemap = m_hDestGroup->GetOrAddPresetRemap(); + pPresetRemap->m_SourcePresetGroup = pSourceName; + pPresetRemap->RemoveAll(); + for ( int nItemID = m_pPresetRemapList->FirstItem(); + nItemID != m_pPresetRemapList->InvalidItemID(); + nItemID = m_pPresetRemapList->NextItem( nItemID ) ) + { + KeyValues* pKeyValues = m_pPresetRemapList->GetItem( nItemID ); + CDmePreset *pSrcPreset = GetElementKeyValue< CDmePreset >( pKeyValues, "srcPreset" ); + CDmePreset *pDestPreset = GetElementKeyValue< CDmePreset >( pKeyValues, "destPreset" ); + if ( pSrcPreset && pDestPreset ) + { + pPresetRemap->AddRemap( pSrcPreset->GetName(), pDestPreset->GetName() ); + } + } +} + + +//----------------------------------------------------------------------------- +// command handler +//----------------------------------------------------------------------------- +void CDmePresetRemapPanel::OnCommand( const char *command ) +{ + if ( !Q_stricmp( command, "Ok") ) + { + ApplyChangesToPresetRemap(); + CloseModal(); + return; + } + + if ( !Q_stricmp( command, "Cancel") ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( command ); +} + + +//----------------------------------------------------------------------------- +// +// CDmePresetGroupListPanel +// +// Implementation below because of scoping issues +// +//----------------------------------------------------------------------------- +class CDmePresetGroupListPanel : public vgui::ListPanel +{ + DECLARE_CLASS_SIMPLE( CDmePresetGroupListPanel, vgui::ListPanel ); + +public: + // constructor, destructor + CDmePresetGroupListPanel( vgui::Panel *pParent, const char *pName, CDmePresetGroupEditorPanel *pComboPanel ); + + virtual void OnCreateDragData( KeyValues *msg ); + virtual bool IsDroppable( CUtlVector< KeyValues * >& msgList ); + virtual void OnPanelDropped( CUtlVector< KeyValues * >& msgList ); + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + virtual void OnMouseDoublePressed( vgui::MouseCode code ); + virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ); + +private: + CDmePresetGroupEditorPanel *m_pPresetGroupPanel; +}; + + +//----------------------------------------------------------------------------- +// +// CDmePresetListPanel +// +// Implementation below because of scoping issues +// +//----------------------------------------------------------------------------- +class CDmePresetListPanel : public vgui::ListPanel +{ + DECLARE_CLASS_SIMPLE( CDmePresetListPanel, vgui::ListPanel ); + +public: + // constructor, destructor + CDmePresetListPanel( vgui::Panel *pParent, const char *pName, CDmePresetGroupEditorPanel *pComboPanel ); + + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + virtual void OnCreateDragData( KeyValues *msg ); + virtual bool IsDroppable( CUtlVector< KeyValues * >& msgList ); + virtual void OnPanelDropped( CUtlVector< KeyValues * >& msgList ); + virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ); + +private: + + CDmePresetGroupEditorPanel *m_pPresetGroupPanel; +}; + + +//----------------------------------------------------------------------------- +// Sort functions for list panel +//----------------------------------------------------------------------------- +static int __cdecl IndexSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + int nIndex1 = item1.kv->GetInt("index"); + int nIndex2 = item2.kv->GetInt("index"); + return nIndex1 - nIndex2; +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CDmePresetGroupEditorPanel::CDmePresetGroupEditorPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_pSplitter = new vgui::Splitter( this, "PresetGroupSplitter", vgui::SPLITTER_MODE_VERTICAL, 1 ); + vgui::Panel *pSplitterLeftSide = m_pSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pSplitter->GetChild( 1 ); + + m_pPresetGroupList = new CDmePresetGroupListPanel( pSplitterLeftSide, "PresetGroupList", this ); + m_pPresetGroupList->AddColumnHeader( 0, "name", "Preset Group Name", 150, 0 ); + m_pPresetGroupList->AddColumnHeader( 1, "visible", "Visible", 70, 0 ); + m_pPresetGroupList->AddColumnHeader( 2, "shared", "Shared", 52, 0 ); + m_pPresetGroupList->AddColumnHeader( 3, "readonly", "Read Only", 52, 0 ); + m_pPresetGroupList->SetSelectIndividualCells( false ); + m_pPresetGroupList->SetMultiselectEnabled( false ); + m_pPresetGroupList->SetEmptyListText( "No preset groups" ); + m_pPresetGroupList->AddActionSignalTarget( this ); + m_pPresetGroupList->SetSortFunc( 0, IndexSortFunc ); + m_pPresetGroupList->SetSortFunc( 1, NULL ); + m_pPresetGroupList->SetColumnSortable( 1, false ); + m_pPresetGroupList->SetSortFunc( 2, NULL ); + m_pPresetGroupList->SetColumnSortable( 2, false ); + m_pPresetGroupList->SetSortFunc( 3, NULL ); + m_pPresetGroupList->SetColumnSortable( 3, false ); + m_pPresetGroupList->SetDropEnabled( true ); + m_pPresetGroupList->SetSortColumn( 0 ); + m_pPresetGroupList->SetDragEnabled( true ); + m_pPresetGroupList->SetDropEnabled( true ); + m_pPresetGroupList->SetIgnoreDoubleClick( true ); + + m_pPresetList = new CDmePresetListPanel( pSplitterRightSide, "PresetList", this ); + m_pPresetList->AddColumnHeader( 0, "name", "Preset Name", 150, 0 ); + m_pPresetList->SetSelectIndividualCells( false ); + m_pPresetList->SetEmptyListText( "No presets" ); + m_pPresetList->AddActionSignalTarget( this ); + m_pPresetList->SetSortFunc( 0, IndexSortFunc ); + m_pPresetList->SetSortColumn( 0 ); + m_pPresetList->SetDragEnabled( true ); + m_pPresetList->SetDropEnabled( true ); + m_pPresetList->SetIgnoreDoubleClick( true ); + + LoadControlSettingsAndUserConfig( "resource/dmepresetgroupeditorpanel.res" ); + + m_hFileOpenStateMachine = new vgui::FileOpenStateMachine( this, this ); + m_hFileOpenStateMachine->AddActionSignalTarget( this ); +} + + +CDmePresetGroupEditorPanel::~CDmePresetGroupEditorPanel() +{ + CleanupContextMenu(); + SaveUserConfig(); +} + + +//----------------------------------------------------------------------------- +// Cleans up the context menu +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::CleanupContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + m_hContextMenu->MarkForDeletion(); + m_hContextMenu = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Sets the combination operator +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::SetAnimationSet( CDmeAnimationSet *pAnimationSet ) +{ + m_hAnimationSet = pAnimationSet; + RefreshAnimationSet(); +} + +CDmeAnimationSet* CDmePresetGroupEditorPanel::GetAnimationSet() +{ + return m_hAnimationSet; +} + + +//----------------------------------------------------------------------------- +// Builds the preset group list for the animation set +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::RefreshAnimationSet() +{ + CDmePresetGroup *pSelectedPresetGroup = GetSelectedPresetGroup(); + + m_pPresetGroupList->RemoveAll(); + if ( !m_hAnimationSet.Get() ) + return; + + const CDmaElementArray< CDmePresetGroup > &presetGroupList = m_hAnimationSet->GetPresetGroups(); + int nCount = presetGroupList.Count(); + for ( int i = 0; i < nCount; ++i ) + { + CDmePresetGroup *pPresetGroup = presetGroupList[i]; + Assert( pPresetGroup ); + if ( !pPresetGroup ) + continue; + + bool bIsVisible = pPresetGroup->m_bIsVisible; + KeyValues *kv = new KeyValues( "node", "name", pPresetGroup->GetName() ); + kv->SetString( "visible", bIsVisible ? "Yes" : "No" ); + kv->SetString( "shared", pPresetGroup->IsShared() ? "Yes" : "No" ); + kv->SetString( "readonly", pPresetGroup->m_bIsReadOnly ? "Yes" : "No" ); + SetElementKeyValue( kv, "presetGroup", pPresetGroup ); + kv->SetColor( "cellcolor", pPresetGroup->m_bIsReadOnly ? Color( 255, 0, 0, 255 ) : Color( 255, 255, 255, 255 ) ); + kv->SetInt( "index", i ); + int nItemID = m_pPresetGroupList->AddItem( kv, 0, false, false ); + + if ( pSelectedPresetGroup == pPresetGroup ) + { + m_pPresetGroupList->AddSelectedItem( nItemID ); + } + } + + m_pPresetGroupList->SortList(); + + RefreshPresetNames(); +} + + +//----------------------------------------------------------------------------- +// Tells any class that cares that the data in this thing has changed +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::NotifyDataChanged() +{ + PostActionSignal( new KeyValues( "PresetsChanged" ) ); +} + + +//----------------------------------------------------------------------------- +// Refreshes the list of presets in the selected preset group +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::RefreshPresetNames() +{ + CDmePreset *pSelectedPreset = GetSelectedPreset(); + + m_pPresetList->RemoveAll(); + if ( !m_hAnimationSet.Get() ) + return; + + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + const CDmaElementArray< CDmePreset > &presetList = pPresetGroup->GetPresets(); + int nCount = presetList.Count( ); + for ( int i = 0; i < nCount; ++i ) + { + CDmePreset *pPreset = presetList[i]; + KeyValues *kv = new KeyValues( "node", "name", pPreset->GetName() ); + SetElementKeyValue( kv, "preset", pPreset ); + kv->SetInt( "index", i ); + int nItemID = m_pPresetList->AddItem( kv, 0, false, false ); + if ( pSelectedPreset == pPreset ) + { + m_pPresetList->AddSelectedItem( nItemID ); + } + } + + m_pPresetList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +CDmePreset* CDmePresetGroupEditorPanel::GetSelectedPreset() +{ + if ( !m_hAnimationSet.Get() ) + return NULL; + + int nSelectedPresetCount = m_pPresetList->GetSelectedItemsCount(); + if ( nSelectedPresetCount != 1 ) + return NULL; + + int nItemID = m_pPresetList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pPresetList->GetItem( nItemID ); + + CDmePreset *pPreset = GetElementKeyValue< CDmePreset >( pKeyValues, "preset" ); + return pPreset; +} + + +//----------------------------------------------------------------------------- +// Selects a particular preset +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::SetSelectedPreset( CDmePreset* pPreset ) +{ + m_pPresetList->ClearSelectedItems(); + for ( int nItemID = m_pPresetList->FirstItem(); + nItemID != m_pPresetList->InvalidItemID(); + nItemID = m_pPresetList->NextItem( nItemID ) ) + { + KeyValues* pKeyValues = m_pPresetList->GetItem( nItemID ); + CDmePreset *pItemPreset = GetElementKeyValue< CDmePreset >( pKeyValues, "preset" ); + if ( pItemPreset == pPreset ) + { + m_pPresetList->AddSelectedItem( nItemID ); + } + } +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +CDmePresetGroup* CDmePresetGroupEditorPanel::GetSelectedPresetGroup() +{ + if ( !m_hAnimationSet.Get() ) + return NULL; + + int nSelectedItemCount = m_pPresetGroupList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return NULL; + + int nItemID = m_pPresetGroupList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pPresetGroupList->GetItem( nItemID ); + CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pKeyValues, "presetGroup" ); + return pPresetGroup; +} + + +//----------------------------------------------------------------------------- +// Selects a particular preset group +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::SetSelectedPresetGroup( CDmePresetGroup* pPresetGroup ) +{ + m_pPresetGroupList->ClearSelectedItems(); + for ( int nItemID = m_pPresetGroupList->FirstItem(); + nItemID != m_pPresetGroupList->InvalidItemID(); + nItemID = m_pPresetGroupList->NextItem( nItemID ) ) + { + KeyValues* pKeyValues = m_pPresetGroupList->GetItem( nItemID ); + CDmePresetGroup *pItemPresetGroup = GetElementKeyValue< CDmePresetGroup >( pKeyValues, "presetGroup" ); + if ( pItemPresetGroup == pPresetGroup ) + { + m_pPresetGroupList->AddSelectedItem( nItemID ); + } + } +} + + +//----------------------------------------------------------------------------- +// If it finds a duplicate preset name, reports an error message and returns it found one +//----------------------------------------------------------------------------- +bool CDmePresetGroupEditorPanel::HasDuplicatePresetName( const char *pPresetName, CDmePreset *pIgnorePreset ) +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return false; + + CDmePreset *pMatch = pPresetGroup->FindPreset( pPresetName ); + if ( pMatch && pMatch != pIgnorePreset ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "#DmePresetGroupEditor_DuplicatePresetNameTitle", "#DmePresetGroupEditor_DuplicatePresetNameText", this ); + pError->DoModal(); + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Called by OnInputCompleted after we get a new group name +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::PerformAddPreset( const char *pNewPresetName ) +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + if ( HasDuplicatePresetName( pNewPresetName ) ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Add Preset" ); + CDmePreset *pPreset = pPresetGroup->FindOrAddPreset( pNewPresetName ); + sg.Release(); + + RefreshPresetNames(); + SetSelectedPreset( pPreset ); + NotifyDataChanged(); + + KeyValues *pKeyValues = new KeyValues( "AddNewPreset" ); + SetElementKeyValue( pKeyValues, "preset", pPreset ); + PostActionSignal( pKeyValues ); +} + + +//----------------------------------------------------------------------------- +// Called by OnInputCompleted after we get a new group name +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::PerformRenamePreset( const char *pNewPresetName ) +{ + CDmePreset *pPreset = GetSelectedPreset(); + if ( !pPreset ) + return; + + if ( HasDuplicatePresetName( pNewPresetName, pPreset ) ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Rename Preset" ); + pPreset->SetName( pNewPresetName ); + sg.Release(); + + RefreshPresetNames(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Add a preset +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnAddPreset() +{ + vgui::InputDialog *pInput = new vgui::InputDialog( this, "Add Preset", "Enter name of new preset" ); + pInput->SetMultiline( false ); + pInput->DoModal( new KeyValues( "OnAddPreset" ) ); +} + + +//----------------------------------------------------------------------------- +// Rename a preset +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnRenamePreset() +{ + CDmePreset *pPreset = GetSelectedPreset(); + if ( !pPreset ) + return; + + vgui::InputDialog *pInput = new vgui::InputDialog( this, "Rename Preset", "Enter new name of preset" ); + pInput->SetMultiline( false ); + pInput->DoModal( new KeyValues( "OnRenamePreset" ) ); +} + + +//----------------------------------------------------------------------------- +// Remove a preset +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnRemovePreset() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + CDmePreset *pPreset = GetSelectedPreset(); + if ( !pPreset ) + return; + + int nItemID = m_pPresetList->GetSelectedItem( 0 ); + int nCurrentRow = m_pPresetList->GetItemCurrentRow( nItemID ); + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Remove Preset" ); + pPresetGroup->RemovePreset( pPreset ); + sg.Release(); + + RefreshPresetNames(); + if ( nCurrentRow >= m_pPresetList->GetItemCount() ) + { + --nCurrentRow; + } + if ( nCurrentRow >= 0 ) + { + nItemID = m_pPresetList->GetItemIDFromRow( nCurrentRow ); + m_pPresetList->ClearSelectedItems(); + m_pPresetList->AddSelectedItem( nItemID ); + } + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnMovePresetUp() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + CDmePreset *pPreset = GetSelectedPreset(); + if ( !pPresetGroup || !pPreset ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Presets" ); + pPresetGroup->MovePresetUp( pPreset ); + sg.Release(); + + RefreshPresetNames(); + SetSelectedPreset( pPreset ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnMovePresetDown() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + CDmePreset *pPreset = GetSelectedPreset(); + if ( !pPresetGroup || !pPreset ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Presets" ); + pPresetGroup->MovePresetDown( pPreset ); + sg.Release(); + + RefreshPresetNames(); + SetSelectedPreset( pPreset ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Drag/drop reordering of presets +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::MovePresetInFrontOf( CDmePreset *pDragPreset, CDmePreset *pDropPreset ) +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Presets" ); + pPresetGroup->MovePresetInFrontOf( pDragPreset, pDropPreset ); + sg.Release(); + + RefreshPresetNames(); + SetSelectedPreset( pDragPreset ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Fileopen state machine +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnFileStateMachineFinished( KeyValues *pParams ) +{ + KeyValues *pContextKeyValues = pParams->GetFirstTrueSubKey(); + if ( Q_stricmp( pContextKeyValues->GetName(), "ImportPresets" ) ) + return; + + CDmElement *pRoot = GetElementKeyValue<CDmElement>( pContextKeyValues, "presets" ); + if ( !pRoot ) + return; + + if ( pParams->GetInt( "completionState", 0 ) != 0 ) + { + CPresetPickerFrame *pPresetPicker = new CPresetPickerFrame( this, "Select Preset(s) to Import" ); + pPresetPicker->AddActionSignalTarget( this ); + KeyValues *pContextKeyValuesImport = new KeyValues( "ImportPicked" ); + SetElementKeyValue( pContextKeyValuesImport, "presets", pRoot ); + pPresetPicker->DoModal( pRoot, true, pContextKeyValuesImport ); + } + else + { + // Clean up the read-in file + CDisableUndoScopeGuard sg; + g_pDataModel->RemoveFileId( pRoot->GetFileId() ); + } +} + + +//----------------------------------------------------------------------------- +// Finds a control index +//----------------------------------------------------------------------------- +struct ExportedControl_t +{ + CUtlString m_Name; + bool m_bIsStereo; + bool m_bIsMulti; + int m_nFirstIndex; +}; + +static int FindExportedControlIndex( const char *pControlName, CUtlVector< ExportedControl_t > &uniqueControls ) +{ + int nCount = uniqueControls.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( !Q_stricmp( pControlName, uniqueControls[i].m_Name ) ) + return i; + } + return -1; +} + + +//----------------------------------------------------------------------------- +// Builds a unique list of controls found in the presets +//----------------------------------------------------------------------------- +static int BuildExportedControlList( CDmeAnimationSet *pAnimationSet, CDmePresetGroup *pPresetGroup, CUtlVector< ExportedControl_t > &uniqueControls ) +{ + int nGlobalIndex = 0; + const CDmrElementArray< CDmePreset > &presets = pPresetGroup->GetPresets(); + int nPresetCount = presets.Count(); + for ( int iPreset = 0; iPreset < nPresetCount; ++iPreset ) + { + CDmePreset *pPreset = presets[iPreset]; + const CDmrElementArray< CDmElement > &controls = pPreset->GetControlValues(); + + int nControlCount = controls.Count(); + for ( int i = 0; i < nControlCount; ++i ) + { + const char *pControlName = controls[i]->GetName(); + int nIndex = FindExportedControlIndex( pControlName, uniqueControls ); + if ( nIndex >= 0 ) + continue; + CDmAttribute *pValueAttribute = controls[i]->GetAttribute( "value" ); + if ( !pValueAttribute || pValueAttribute->GetType() != AT_FLOAT ) + continue; + + CDmElement *pControl = pAnimationSet->FindControl( pControlName ); + if ( !pControl ) + continue; + + int j = uniqueControls.AddToTail(); + ExportedControl_t &control = uniqueControls[j]; + control.m_Name = pControlName; + control.m_bIsStereo = pControl->GetValue<bool>( "combo" ); + control.m_bIsMulti = pControl->GetValue<bool>( "multi" ); + control.m_nFirstIndex = nGlobalIndex; + nGlobalIndex += 1 + control.m_bIsStereo + control.m_bIsMulti; + } + } + return nGlobalIndex; +} + + +//----------------------------------------------------------------------------- +// Fileopen state machine +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::SetupFileOpenDialog( vgui::FileOpenDialog *pDialog, bool bOpenFile, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + if ( bOpenFile ) + { + pDialog->SetTitle( "Import Preset File", true ); + } + else + { + pDialog->SetTitle( "Export Preset File", true ); + } + + char pPresetPath[MAX_PATH]; + if ( !Q_stricmp( pFileFormat, PRESET_FILE_FORMAT ) ) + { + GetModSubdirectory( "models", pPresetPath, sizeof(pPresetPath) ); + pDialog->SetStartDirectoryContext( "preset_importexport", pPresetPath ); + pDialog->AddFilter( "*.*", "All Files (*.*)", false ); + pDialog->AddFilter( "*.pre", "Preset File (*.pre)", true, PRESET_FILE_FORMAT ); + } + else if ( !Q_stricmp( pFileFormat, "vfe" ) ) + { + GetModSubdirectory( "expressions", pPresetPath, sizeof(pPresetPath) ); + pDialog->SetStartDirectoryContext( "preset_exportvfe", pPresetPath ); + pDialog->AddFilter( "*.*", "All Files (*.*)", false ); + pDialog->AddFilter( "*.vfe", "Expression File (*.vfe)", true, "vfe" ); + } + else if ( !Q_stricmp( pFileFormat, "txt" ) ) + { + GetModSubdirectory( "expressions", pPresetPath, sizeof(pPresetPath) ); + pDialog->SetStartDirectoryContext( "preset_exportvfe", pPresetPath ); + pDialog->AddFilter( "*.*", "All Files (*.*)", false ); + pDialog->AddFilter( "*.txt", "Faceposer Expression File (*.txt)", true, "txt" ); + } +} + +bool CDmePresetGroupEditorPanel::OnReadFileFromDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + CDmElement *pRoot; + CDisableUndoScopeGuard sgRestore; + DmFileId_t fileId = g_pDataModel->RestoreFromFile( pFileName, NULL, pFileFormat, &pRoot, CR_FORCE_COPY ); + sgRestore.Release(); + + if ( fileId == DMFILEID_INVALID ) + return false; + + // When importing an entire group, we can do it all right here + if ( !Q_stricmp( pContextKeyValues->GetName(), "ImportPresetGroup" ) ) + { + CDmePresetGroup *pPresetGroup = CastElement< CDmePresetGroup >( pRoot ); + if ( !pPresetGroup ) + return false; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Import Preset Group" ); + pPresetGroup->SetFileId( m_hAnimationSet->GetFileId(), TD_DEEP ); + m_hAnimationSet->GetPresetGroups( ).AddToTail( pPresetGroup ); + sg.Release(); + + // Warn if we import a remap which doesn't exist + CDmePresetRemap *pPresetRemap = pPresetGroup->GetPresetRemap(); + if ( pPresetRemap ) + { + if ( m_hAnimationSet->FindPresetGroup( pPresetRemap->m_SourcePresetGroup ) == NULL ) + { + char pBuf[512]; + Q_snprintf( pBuf, sizeof(pBuf), + "Import contains a remap which refers to an unknown preset group \"%s\"!\n", + pPresetRemap->m_SourcePresetGroup.Get() ); + vgui::MessageBox *pError = new vgui::MessageBox( "Bad source remap name!", pBuf, this ); + pError->DoModal(); + } + } + + RefreshAnimationSet(); + NotifyDataChanged(); + return true; + } + + CDmAttribute* pPresets = pRoot->GetAttribute( "presets", AT_ELEMENT_ARRAY ); + if ( !pPresets ) + return false; + + SetElementKeyValue( pContextKeyValues, "presets", pRoot ); + return true; +} + +bool CDmePresetGroupEditorPanel::OnWriteFileToDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues ) +{ + // Used when exporting an entire preset group + if ( !Q_stricmp( pContextKeyValues->GetName(), "ExportPresetGroup" ) ) + { + CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pContextKeyValues, "presetGroup" ); + if ( !pPresetGroup ) + return false; + + bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, g_pDataModel->GetDefaultEncoding( pFileFormat ), pFileFormat, pPresetGroup ); + return bOk; + } + + // Used when exporting an entire preset group + if ( !Q_stricmp( pContextKeyValues->GetName(), "ExportPresetGroupToVFE" ) ) + { + CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pContextKeyValues, "presetGroup" ); + if ( !pPresetGroup ) + return false; + + bool bOk = pPresetGroup->ExportToVFE( pFileName, m_hAnimationSet ); + return bOk; + } + + // Used when exporting an entire preset group + if ( !Q_stricmp( pContextKeyValues->GetName(), "ExportPresetGroupToTXT" ) ) + { + CDmePresetGroup *pPresetGroup = GetElementKeyValue<CDmePresetGroup>( pContextKeyValues, "presetGroup" ); + if ( !pPresetGroup ) + return false; + + bool bOk = pPresetGroup->ExportToTXT( pFileName, m_hAnimationSet ); + return bOk; + } + + // Used when exporting a subset of a preset group + int nCount = pContextKeyValues->GetInt( "count" ); + if ( nCount == 0 ) + return true; + + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + const char *pPresetGroupName = pPresetGroup ? pPresetGroup->GetName() : "root"; + + CDisableUndoScopeGuard sg; + CDmePresetGroup *pRoot = CreateElement< CDmePresetGroup >( pPresetGroupName, DMFILEID_INVALID ); + CDmaElementArray< CDmePreset >& presets = pRoot->GetPresets( ); + + // Build list of selected presets + for ( int i = 0; i < nCount; ++i ) + { + char pBuf[32]; + Q_snprintf( pBuf, sizeof(pBuf), "%d", i ); + CDmePreset *pPreset = GetElementKeyValue<CDmePreset>( pContextKeyValues, pBuf ); + presets.AddToTail( pPreset ); + } + + bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, g_pDataModel->GetDefaultEncoding( pFileFormat ), pFileFormat, pRoot ); + g_pDataModel->DestroyElement( pRoot->GetHandle() ); + return bOk; +} + + +//----------------------------------------------------------------------------- +// Called when preset picking is cancelled +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnPresetPickCancelled( KeyValues *pParams ) +{ + KeyValues *pContextKeyValues = pParams->FindKey( "ImportPicked" ); + if ( pContextKeyValues ) + { + // Clean up the read-in file + CDisableUndoScopeGuard sg; + CDmElement *pRoot = GetElementKeyValue<CDmElement>( pContextKeyValues, "presets" ); + g_pDataModel->RemoveFileId( pRoot->GetFileId() ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Actually imports the presets from a file +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::ImportPresets( const CUtlVector< CDmePreset * >& presets ) +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Import Presets" ); + + int nPresetCount = presets.Count(); + for ( int i = 0; i < nPresetCount; ++i ) + { + CDmePreset *pPreset = pPresetGroup->FindOrAddPreset( presets[i]->GetName() ); + const CDmaElementArray< CDmElement > &srcValues = presets[i]->GetControlValues( ); + CDmaElementArray< CDmElement > &values = pPreset->GetControlValues( ); + values.RemoveAll(); + + int nValueCount = srcValues.Count(); + for ( int j = 0; j < nValueCount; ++j ) + { + CDmElement *pSrcControlValue = srcValues[j]; + CDmElement *pControlValue = pSrcControlValue->Copy( ); + pControlValue->SetFileId( pPresetGroup->GetFileId(), TD_DEEP ); + values.AddToTail( pControlValue ); + } + } + + RefreshAnimationSet(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// The 'export presets' context menu option +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnPresetPicked( KeyValues *pParams ) +{ + CUtlVector< CDmePreset * > presets; + int nCount = pParams->GetInt( "count" ); + if ( nCount == 0 ) + return; + + // Build list of selected presets + for ( int i = 0; i < nCount; ++i ) + { + char pBuf[32]; + Q_snprintf( pBuf, sizeof(pBuf), "%d", i ); + CDmePreset *pPreset = GetElementKeyValue<CDmePreset>( pParams, pBuf ); + presets.AddToTail( pPreset ); + } + + if ( pParams->FindKey( "ExportPicked" ) ) + { + KeyValues *pContextKeyValues = new KeyValues( "ExportPresets" ); + pContextKeyValues->SetInt( "count", nCount ); + for ( int i = 0; i < nCount; ++i ) + { + char pBuf[32]; + Q_snprintf( pBuf, sizeof(pBuf), "%d", i ); + SetElementKeyValue( pContextKeyValues, pBuf, presets[i] ); + } + + m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, PRESET_FILE_FORMAT, vgui::FOSM_SHOW_PERFORCE_DIALOGS ); + return; + } + + KeyValues *pContextKeyValues = pParams->FindKey( "ImportPicked" ); + if ( pContextKeyValues ) + { + ImportPresets( presets ); + + // Clean up the read-in file + { + CDisableUndoScopeGuard sg; + CDmElement *pRoot = GetElementKeyValue<CDmElement>( pContextKeyValues, "presets" ); + g_pDataModel->RemoveFileId( pRoot->GetFileId() ); + return; + } + } +} + + +//----------------------------------------------------------------------------- +// The 'export presets' context menu option +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnExportPresets() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + CPresetPickerFrame *pPresetPicker = new CPresetPickerFrame( this, "Select Preset(s) to Export" ); + pPresetPicker->AddActionSignalTarget( this ); + pPresetPicker->DoModal( pPresetGroup, true, new KeyValues( "ExportPicked" ) ); +} + + +//----------------------------------------------------------------------------- +// The 'import presets' context menu option +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnImportPresets() +{ + KeyValues *pContextKeyValues = new KeyValues( "ImportPresets" ); + m_hFileOpenStateMachine->OpenFile( PRESET_FILE_FORMAT, pContextKeyValues ); +} + + +//----------------------------------------------------------------------------- +// The 'export preset groups to VFE' context menu option +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnExportPresetGroupToVFE() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + KeyValues *pContextKeyValues = new KeyValues( "ExportPresetGroupToVFE" ); + SetElementKeyValue( pContextKeyValues, "presetGroup", pPresetGroup ); + m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, "vfe", vgui::FOSM_SHOW_PERFORCE_DIALOGS ); +} + + +//----------------------------------------------------------------------------- +// The 'export preset groups to TXT' context menu option +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnExportPresetGroupToTXT() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + KeyValues *pContextKeyValues = new KeyValues( "ExportPresetGroupToTXT" ); + SetElementKeyValue( pContextKeyValues, "presetGroup", pPresetGroup ); + m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, "txt", vgui::FOSM_SHOW_PERFORCE_DIALOGS ); +} + + +//----------------------------------------------------------------------------- +// The 'export preset groups' context menu option +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnExportPresetGroups() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + KeyValues *pContextKeyValues = new KeyValues( "ExportPresetGroup" ); + SetElementKeyValue( pContextKeyValues, "presetGroup", pPresetGroup ); + m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, PRESET_FILE_FORMAT, vgui::FOSM_SHOW_PERFORCE_DIALOGS ); +} + + +//----------------------------------------------------------------------------- +// The 'import preset groups' context menu option +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnImportPresetGroups() +{ + KeyValues *pContextKeyValues = new KeyValues( "ImportPresetGroup" ); + m_hFileOpenStateMachine->OpenFile( PRESET_FILE_FORMAT, pContextKeyValues ); +} + + +//----------------------------------------------------------------------------- +// Preset remap editor +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnRemoveDefaultControls() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Remove Default Controls" ); + CDmrElementArray< CDmePreset > presets = pPresetGroup->GetPresets(); + int nPresetCount = presets.Count(); + for ( int i = 0; i < nPresetCount; ++i ) + { + CDmePreset *pPreset = presets[i]; + CDmrElementArray< CDmElement > controls = pPreset->GetControlValues(); + int nControlCount = controls.Count(); + for ( int j = nControlCount; --j >= 0; ) + { + CDmElement *pControlValue = controls[j]; + CDmElement *pControl = m_hAnimationSet->FindControl( pControlValue->GetName() ); + if ( !pControl ) + { + controls.Remove( j ); + continue; + } + + bool bIsDefault = true; + if ( pControl->GetValue<float>( "defaultValue" ) != pControlValue->GetValue<float>( "value" ) ) + { + bIsDefault = false; + } + + bool bIsStereo = pControl->GetValue<bool>( "combo" ); + if ( bIsStereo ) + { + if ( pControl->GetValue<float>( "defaultBalance" ) != pControlValue->GetValue<float>( "balance" ) ) + { + bIsDefault = false; + } + } + + bool bIsMulti = pControl->GetValue<bool>( "multi" ); + if ( bIsMulti ) + { + if ( pControl->GetValue<float>( "defaultMultilevel" ) != pControlValue->GetValue<float>( "multilevel" ) ) + { + bIsDefault = false; + } + } + + if ( bIsDefault ) + { + controls.Remove( j ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Preset remap editor +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnEditPresetRemapping() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + CDmePresetRemapPanel *pPresetRemapPanel = new CDmePresetRemapPanel( this, "Manage Preset Remapping" ); + pPresetRemapPanel->DoModal( m_hAnimationSet, pPresetGroup ); +} + + +//----------------------------------------------------------------------------- +// Perform preset remap +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnRemapPresets() +{ + CDmePresetGroup *pDestPresetGroup = GetSelectedPresetGroup(); + if ( !pDestPresetGroup || pDestPresetGroup->m_bIsReadOnly ) + return; + + CDmePresetRemap *pPresetRemap = pDestPresetGroup->GetPresetRemap(); + if ( !pPresetRemap ) + return; + + CDmePresetGroup *pSourcePresetGroup = m_hAnimationSet->FindPresetGroup( pPresetRemap->m_SourcePresetGroup ); + if ( !pSourcePresetGroup ) + { + char pBuf[512]; + Q_snprintf( pBuf, sizeof(pBuf), "Unable to find preset group name %s in animation set %s!\n", + pPresetRemap->m_SourcePresetGroup.Get(), m_hAnimationSet->GetName() ); + vgui::MessageBox *pError = new vgui::MessageBox( "Bad source remap name!", pBuf, this ); + pError->DoModal(); + return; + } + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Remap Presets" ); + + int nCount = pPresetRemap->GetRemapCount(); + for ( int i = 0; i < nCount; ++i ) + { + const char *pSourceName = pPresetRemap->GetRemapSource( i ); + CDmePreset *pSourcePreset = pSourcePresetGroup->FindPreset( pSourceName ); + + const char *pDestName = pPresetRemap->GetRemapDest( i ); + CDmePreset *pDestPreset = pDestPresetGroup->FindPreset( pDestName ); + + if ( !pSourcePreset || !pDestPreset ) + continue; + + pDestPreset->CopyControlValuesFrom( pSourcePreset ); + } + + sg.Release(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular preset +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnOpenPresetContextMenu( ) +{ + if ( !m_hAnimationSet.Get() ) + return; + + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + CDmePreset *pPreset = GetSelectedPreset(); + + m_hContextMenu = new vgui::Menu( this, "ActionMenu" ); + + // Can only export from read-only groups + if ( pPresetGroup->m_bIsReadOnly ) + { + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresets", new KeyValues( "ExportPresets" ), this ); + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); + return; + } + + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_AddPreset", new KeyValues( "AddPreset" ), this ); + if ( pPreset ) + { + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RenamePreset", new KeyValues( "RenamePreset" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RemovePreset", new KeyValues( "RemovePreset" ), this ); + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_MoveUp", new KeyValues( "MovePresetUp" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_MoveDown", new KeyValues( "MovePresetDown" ), this ); + } + + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ImportPresets", new KeyValues( "ImportPresets" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresets", new KeyValues( "ExportPresets" ), this ); + + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnOpenContextMenu( KeyValues *kv ) +{ + CleanupContextMenu(); + if ( !m_hAnimationSet.Get() ) + return; + + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pPresetList ) + { + OnOpenPresetContextMenu(); + return; + } + + if ( pPanel != m_pPresetGroupList ) + return; + + m_hContextMenu = new vgui::Menu( this, "ActionMenu" ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_AddGroup", new KeyValues( "AddGroup" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_AddPhonemeGroup", new KeyValues( "AddPhonemeGroup" ), this ); + + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( pPresetGroup ) + { + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RenameGroup", new KeyValues( "RenameGroup" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RemoveGroup", new KeyValues( "RemoveGroup" ), this ); + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ToggleVisibility", new KeyValues( "ToggleGroupVisibility" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ToggleSharing", new KeyValues( "ToggleGroupSharing" ), this ); + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_MoveUp", new KeyValues( "MoveGroupUp" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_MoveDown", new KeyValues( "MoveGroupDown" ), this ); + + CDmePresetRemap *pPresetRemap = pPresetGroup->GetPresetRemap(); + bool bUseSeparator = !pPresetGroup->m_bIsReadOnly || pPresetRemap; + if ( bUseSeparator ) + { + m_hContextMenu->AddSeparator(); + } + if ( !pPresetGroup->m_bIsReadOnly ) + { + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RemoveDefaultControls", new KeyValues( "RemoveDefaultControls" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_EditPresetRemapping", new KeyValues( "EditPresetRemapping" ), this ); + } + if ( pPresetRemap ) + { + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_RemapPresets", new KeyValues( "RemapPresets" ), this ); + } + } + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ImportPresets", new KeyValues( "ImportPresetGroups" ), this ); + if ( pPresetGroup ) + { + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresets", new KeyValues( "ExportPresetGroups" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresetsToFaceposer", new KeyValues( "ExportPresetGroupsToTXT" ), this ); + m_hContextMenu->AddMenuItem( "#DmePresetGroupEditor_ExportPresetsToExpression", new KeyValues( "ExportPresetGroupsToVFE" ), this ); + } + + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pPresetGroupList ) + { + RefreshPresetNames(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnItemDeselected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pPresetGroupList ) + { + RefreshPresetNames(); + return; + } +} + + +//----------------------------------------------------------------------------- +// If it finds a duplicate control name, reports an error message and returns it found one +//----------------------------------------------------------------------------- +bool CDmePresetGroupEditorPanel::HasDuplicateGroupName( const char *pGroupName, CDmePresetGroup *pIgnorePreset ) +{ + if ( !m_hAnimationSet ) + return false; + + CDmePresetGroup *pMatch = m_hAnimationSet->FindPresetGroup( pGroupName ); + if ( pMatch && pMatch != pIgnorePreset ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "#DmePresetGroupEditor_DuplicateNameTitle", "#DmePresetGroupEditor_DuplicateNameText", this ); + pError->DoModal(); + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Called by OnInputCompleted after we get a new group name +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::PerformAddGroup( const char *pNewGroupName ) +{ + if ( !m_hAnimationSet ) + return; + + if ( HasDuplicateGroupName( pNewGroupName ) ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Add Preset Group" ); + CDmePresetGroup *pPresetGroup = m_hAnimationSet->FindOrAddPresetGroup( pNewGroupName ); + sg.Release(); + + RefreshAnimationSet(); + SetSelectedPresetGroup( pPresetGroup ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called by OnInputCompleted after we get a new group name +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::PerformAddPhonemeGroup( const char *pNewGroupName ) +{ + if ( !m_hAnimationSet ) + return; + + if ( HasDuplicateGroupName( pNewGroupName ) ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Add Phoneme Preset Group" ); + CDmePresetGroup *pPresetGroup = m_hAnimationSet->FindOrAddPresetGroup( pNewGroupName ); + + int nPhonemeCount = NumPhonemes(); + for ( int i = 0; i < nPhonemeCount; ++i ) + { + if ( !IsStandardPhoneme( i ) ) + continue; + + char pTempBuf[256]; + const char *pPhonemeName = NameForPhonemeByIndex( i ); + if ( !Q_stricmp( pPhonemeName, "<sil>" ) ) + { + pPhonemeName = "silence"; + } + Q_snprintf( pTempBuf, sizeof(pTempBuf), "p_%s", pPhonemeName ); + + pPresetGroup->FindOrAddPreset( pTempBuf ); + } + + sg.Release(); + + RefreshAnimationSet(); + SetSelectedPresetGroup( pPresetGroup ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called by OnInputCompleted after we get a new group name +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::PerformRenameGroup( const char *pNewGroupName ) +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + if ( HasDuplicateGroupName( pNewGroupName, pPresetGroup ) ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Rename Preset Group" ); + pPresetGroup->SetName( pNewGroupName ); + sg.Release(); + + RefreshAnimationSet(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called by OnGroupControls after we get a new group name +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnInputCompleted( KeyValues *pKeyValues ) +{ + const char *pName = pKeyValues->GetString( "text", NULL ); + if ( !pName || !pName[0] ) + return; + + if ( pKeyValues->FindKey( "OnAddGroup" ) ) + { + PerformAddGroup( pName ); + return; + } + + if ( pKeyValues->FindKey( "OnAddPhonemeGroup" ) ) + { + PerformAddPhonemeGroup( pName ); + return; + } + + if ( pKeyValues->FindKey( "OnRenameGroup" ) ) + { + PerformRenameGroup( pName ); + return; + } + + if ( pKeyValues->FindKey( "OnAddPreset" ) ) + { + PerformAddPreset( pName ); + return; + } + + if ( pKeyValues->FindKey( "OnRenamePreset" ) ) + { + PerformRenamePreset( pName ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Toggle group visibility +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::ToggleGroupVisibility( CDmePresetGroup *pPresetGroup ) +{ + if ( pPresetGroup ) + { + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Toggle Preset Group Visibility" ); + + pPresetGroup->m_bIsVisible = !pPresetGroup->m_bIsVisible; + } + + RefreshAnimationSet(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Ungroup controls from each other +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnToggleGroupVisibility( ) +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + ToggleGroupVisibility( pPresetGroup ); +} + + +//----------------------------------------------------------------------------- +// Ungroup controls from each other +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnToggleGroupSharing( ) +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( pPresetGroup ) + { + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Toggle Preset Group Sharing" ); + + pPresetGroup->SetShared( !pPresetGroup->IsShared() ); + } + + RefreshAnimationSet(); +} + + +//----------------------------------------------------------------------------- +// Add a preset group +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnAddGroup() +{ + vgui::InputDialog *pInput = new vgui::InputDialog( this, "Add Preset Group", "Enter name of new preset group" ); + pInput->SetMultiline( false ); + pInput->DoModal( new KeyValues( "OnAddGroup" ) ); +} + + +//----------------------------------------------------------------------------- +// Add a preset group +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnAddPhonemeGroup() +{ + vgui::InputDialog *pInput = new vgui::InputDialog( this, "Add Phoneme Preset Group", "Enter name of new preset group", "phoneme" ); + pInput->SetMultiline( false ); + pInput->DoModal( new KeyValues( "OnAddPhonemeGroup" ) ); +} + + +//----------------------------------------------------------------------------- +// Rename a preset group +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnRenameGroup() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + vgui::InputDialog *pInput = new vgui::InputDialog( this, "Rename Preset Group", "Enter new name of preset group" ); + pInput->SetMultiline( false ); + pInput->DoModal( new KeyValues( "OnRenameGroup" ) ); +} + + +//----------------------------------------------------------------------------- +// Remove a preset group +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnRemoveGroup() +{ + if ( !m_hAnimationSet.Get() ) + return; + + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + if ( !Q_stricmp( pPresetGroup->GetName(), "procedural" ) ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "#DmePresetGroupEditor_CannotRemovePresetGroupTitle", "#DmePresetGroupEditor_CannotRemovePresetGroupText", this ); + pError->DoModal(); + return; + } + + int nItemID = m_pPresetGroupList->GetSelectedItem( 0 ); + int nCurrentRow = m_pPresetGroupList->GetItemCurrentRow( nItemID ); + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Remove Preset Group" ); + m_hAnimationSet->RemovePresetGroup( pPresetGroup ); + sg.Release(); + + RefreshAnimationSet(); + if ( nCurrentRow >= m_pPresetGroupList->GetItemCount() ) + { + --nCurrentRow; + } + if ( nCurrentRow >= 0 ) + { + nItemID = m_pPresetGroupList->GetItemIDFromRow( nCurrentRow ); + m_pPresetGroupList->ClearSelectedItems(); + m_pPresetGroupList->AddSelectedItem( nItemID ); + } + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnMoveGroupUp() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup || !m_hAnimationSet.Get() ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Preset Groups" ); + m_hAnimationSet->MovePresetGroupUp( pPresetGroup ); + sg.Release(); + + RefreshAnimationSet(); + SetSelectedPresetGroup( pPresetGroup ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::OnMoveGroupDown() +{ + CDmePresetGroup *pPresetGroup = GetSelectedPresetGroup(); + if ( !pPresetGroup || !m_hAnimationSet.Get() ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Preset Groups" ); + m_hAnimationSet->MovePresetGroupDown( pPresetGroup ); + sg.Release(); + + RefreshAnimationSet(); + SetSelectedPresetGroup( pPresetGroup ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Drag/drop reordering of preset groups +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::MovePresetGroupInFrontOf( CDmePresetGroup *pDragGroup, CDmePresetGroup *pDropGroup ) +{ + if ( !m_hAnimationSet.Get() ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Reorder Preset Groups" ); + m_hAnimationSet->MovePresetGroupInFrontOf( pDragGroup, pDropGroup ); + sg.Release(); + + RefreshAnimationSet(); + SetSelectedPresetGroup( pDragGroup ); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// Drag/drop preset moving +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorPanel::MovePresetIntoGroup( CDmePreset *pPreset, CDmePresetGroup *pGroup ) +{ + if ( !m_hAnimationSet.Get() || !pPreset || !pGroup ) + return; + + CPresetGroupUndoScopeGuard sg( NOTIFY_SETDIRTYFLAG, "Change Preset Group" ); + + m_hAnimationSet->RemovePreset( pPreset ); + pGroup->GetPresets().AddToTail( pPreset ); + sg.Release(); + + RefreshPresetNames(); + NotifyDataChanged(); +} + + +//----------------------------------------------------------------------------- +// +// +// CDmePresetGroupListPanel +// +// Declaration above because of scoping issues +// +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CDmePresetGroupListPanel::CDmePresetGroupListPanel( vgui::Panel *pParent, const char *pName, CDmePresetGroupEditorPanel *pComboPanel ) : + BaseClass( pParent, pName ), m_pPresetGroupPanel( pComboPanel ) +{ +} + + +//----------------------------------------------------------------------------- +// Handle keypresses +//----------------------------------------------------------------------------- +void CDmePresetGroupListPanel::OnMouseDoublePressed( vgui::MouseCode code ) +{ + if ( code == MOUSE_LEFT ) + { + int x, y, row, column; + vgui::input()->GetCursorPos( x, y ); + GetCellAtPos( x, y, row, column ); + int itemId = GetItemIDFromRow( row ); + KeyValues *pKeyValues = GetItem( itemId ); + CDmePresetGroup *pPresetGroup = GetElementKeyValue< CDmePresetGroup >( pKeyValues, "presetGroup" ); + m_pPresetGroupPanel->ToggleGroupVisibility( pPresetGroup ); + return; + } + + BaseClass::OnMouseDoublePressed( code ); +} + + +//----------------------------------------------------------------------------- +// Handle keypresses +//----------------------------------------------------------------------------- +void CDmePresetGroupListPanel::OnKeyCodeTyped( vgui::KeyCode code ) +{ + if ( code == KEY_DELETE || code == KEY_BACKSPACE ) + { + m_pPresetGroupPanel->OnRemoveGroup(); + return; + } + + if ( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) ) + { + if ( code == KEY_UP ) + { + m_pPresetGroupPanel->OnMoveGroupUp(); + return; + } + + if ( code == KEY_DOWN ) + { + m_pPresetGroupPanel->OnMoveGroupDown(); + return; + } + } + + vgui::ListPanel::OnKeyCodeTyped( code ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmePresetGroupListPanel::OnCreateDragData( KeyValues *msg ) +{ + CDmePresetGroup *pPresetGroup = m_pPresetGroupPanel->GetSelectedPresetGroup(); + if ( !pPresetGroup ) + return; + + SetElementKeyValue( msg, "presetGroup", pPresetGroup ); + msg->SetInt( "selfDroppable", 1 ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +bool CDmePresetGroupListPanel::IsDroppable( CUtlVector< KeyValues * >& msgList ) +{ + if ( msgList.Count() > 0 ) + { + KeyValues *pData( msgList[ 0 ] ); + if ( m_pPresetGroupPanel ) + { + CDmePresetGroup *pPresetGroup = GetElementKeyValue< CDmePresetGroup >( pData, "presetGroup" ); + if ( pPresetGroup ) + return true; + + CDmePreset *pPreset = GetElementKeyValue< CDmePreset >( pData, "preset" ); + if ( pPreset ) + { + // Can't drop presets onto read-only preset groups + int x, y, row, column; + vgui::input()->GetCursorPos( x, y ); + GetCellAtPos( x, y, row, column ); + KeyValues *pKeyValues = GetItem( row ); + CDmePresetGroup *pDropGroup = pKeyValues ? GetElementKeyValue<CDmePresetGroup>( pKeyValues, "presetGroup" ) : NULL; + + if ( pDropGroup && !pDropGroup->m_bIsReadOnly ) + return true; + } + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmePresetGroupListPanel::OnPanelDropped( CUtlVector< KeyValues * >& msgList ) +{ + if ( msgList.Count() == 0 ) + return; + + KeyValues *pData = msgList[ 0 ]; + if ( !m_pPresetGroupPanel ) + return; + + // Discover the cell the panel is over + int x, y, row, column; + vgui::input()->GetCursorPos( x, y ); + GetCellAtPos( x, y, row, column ); + KeyValues *pKeyValues = GetItem( row ); + + CDmePresetGroup *pDragGroup = GetElementKeyValue<CDmePresetGroup>( pData, "presetGroup" ); + if ( pDragGroup ) + { + CDmePresetGroup *pDropGroup = pKeyValues ? GetElementKeyValue<CDmePresetGroup>( pKeyValues, "presetGroup" ) : NULL; + m_pPresetGroupPanel->MovePresetGroupInFrontOf( pDragGroup, pDropGroup ); + return; + } + + CDmePreset *pDragPreset = GetElementKeyValue<CDmePreset>( pData, "preset" ); + if ( pDragPreset ) + { + CDmePresetGroup *pDropGroup = pKeyValues ? GetElementKeyValue<CDmePresetGroup>( pKeyValues, "presetGroup" ) : NULL; + if ( pDropGroup ) + { + m_pPresetGroupPanel->MovePresetIntoGroup( pDragPreset, pDropGroup ); + } + return; + } +} + + +//----------------------------------------------------------------------------- +// Mouse is now over a droppable panel +//----------------------------------------------------------------------------- +void CDmePresetGroupListPanel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) +{ + // Discover the cell the panel is over + int x, y, w, h, row, column; + vgui::input()->GetCursorPos( x, y ); + GetCellAtPos( x, y, row, column ); + GetCellBounds( row, 0, x, y, w, h ); + + int x2, y2, w2, h2; + GetCellBounds( row, 3, x2, y2, w2, h2 ); + w = x2 + w2 - x; + + LocalToScreen( x, y ); + + surface()->DrawSetColor( GetDropFrameColor() ); + + // Draw insertion point + surface()->DrawFilledRect( x, y, x + w, y + 2 ); + surface()->DrawFilledRect( x, y + h - 2, x + w, y + h ); + surface()->DrawFilledRect( x, y, x + 2, y + h ); + surface()->DrawFilledRect( x + w - 2, y, x + w, y + h ); +} + + +//----------------------------------------------------------------------------- +// +// +// CDmePresetListPanel +// +// Declaration above because of scoping issues +// +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CDmePresetListPanel::CDmePresetListPanel( vgui::Panel *pParent, const char *pName, CDmePresetGroupEditorPanel *pComboPanel ) : + BaseClass( pParent, pName ), m_pPresetGroupPanel( pComboPanel ) +{ +} + +void CDmePresetListPanel::OnKeyCodeTyped( vgui::KeyCode code ) +{ + CDmePresetGroup *pPresetGroup = m_pPresetGroupPanel->GetSelectedPresetGroup(); + if ( pPresetGroup && !pPresetGroup->m_bIsReadOnly ) + { + if ( code == KEY_DELETE || code == KEY_BACKSPACE ) + { + m_pPresetGroupPanel->OnRemovePreset(); + return; + } + + // Not sure how to handle 'edit' mode... the relevant stuff is private + if ( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) ) + { + if ( code == KEY_UP ) + { + m_pPresetGroupPanel->OnMovePresetUp(); + return; + } + + if ( code == KEY_DOWN ) + { + m_pPresetGroupPanel->OnMovePresetDown(); + return; + } + } + } + + vgui::ListPanel::OnKeyCodeTyped( code ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmePresetListPanel::OnCreateDragData( KeyValues *msg ) +{ + CDmePresetGroup *pPresetGroup = m_pPresetGroupPanel->GetSelectedPresetGroup(); + if ( pPresetGroup->m_bIsReadOnly ) + return; + + CDmePreset *pPreset = m_pPresetGroupPanel->GetSelectedPreset(); + if ( !pPreset ) + return; + + SetElementKeyValue( msg, "preset", pPreset ); + msg->SetInt( "selfDroppable", 1 ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +bool CDmePresetListPanel::IsDroppable( CUtlVector< KeyValues * >& msgList ) +{ + if ( msgList.Count() > 0 ) + { + KeyValues *pData( msgList[ 0 ] ); + if ( pData->GetPtr( "panel", NULL ) == this && m_pPresetGroupPanel ) + { + CDmePreset *pPreset = GetElementKeyValue< CDmePreset >( pData, "preset" ); + if ( pPreset ) + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CDmePresetListPanel::OnPanelDropped( CUtlVector< KeyValues * >& msgList ) +{ + if ( msgList.Count() == 0 ) + return; + + KeyValues *pData = msgList[ 0 ]; + if ( pData->GetPtr( "panel", NULL ) != this || !m_pPresetGroupPanel ) + return; + + // Discover the cell the panel is over + int x, y, row, column; + vgui::input()->GetCursorPos( x, y ); + GetCellAtPos( x, y, row, column ); + + KeyValues *pKeyValues = GetItem( row ); + + CDmePreset *pDragPreset = GetElementKeyValue<CDmePreset>( pData, "preset" ); + if ( pDragPreset ) + { + CDmePreset *pDropPreset = pKeyValues ? GetElementKeyValue<CDmePreset>( pKeyValues, "preset" ) : NULL; + m_pPresetGroupPanel->MovePresetInFrontOf( pDragPreset, pDropPreset ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Mouse is now over a droppable panel +//----------------------------------------------------------------------------- +void CDmePresetListPanel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) +{ + // Discover the cell the panel is over + int x, y, w, h, row, column; + vgui::input()->GetCursorPos( x, y ); + GetCellAtPos( x, y, row, column ); + GetCellBounds( row, column, x, y, w, h ); + LocalToScreen( x, y ); + + surface()->DrawSetColor( GetDropFrameColor() ); + + // Draw insertion point + surface()->DrawFilledRect( x, y, x + w, y + 2 ); + surface()->DrawFilledRect( x, y + h - 2, x + w, y + h ); + surface()->DrawFilledRect( x, y, x + 2, y + h ); + surface()->DrawFilledRect( x + w - 2, y, x + w, y + h ); +} + + + +//----------------------------------------------------------------------------- +// +// Purpose: Combination system editor frame +// +//----------------------------------------------------------------------------- +CDmePresetGroupEditorFrame::CDmePresetGroupEditorFrame( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "DmePresetGroupEditorFrame" ) +{ + SetDeleteSelfOnClose( true ); + m_pEditor = new CDmePresetGroupEditorPanel( this, "DmePresetGroupEditorPanel" ); + m_pEditor->AddActionSignalTarget( this ); + m_pOkButton = new vgui::Button( this, "OkButton", "#VGui_OK", this, "Ok" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/dmepresetgroupeditorframe.res" ); + + SetTitle( pTitle, false ); + g_pDataModel->InstallNotificationCallback( this ); +} + +CDmePresetGroupEditorFrame::~CDmePresetGroupEditorFrame() +{ + g_pDataModel->RemoveNotificationCallback( this ); +} + + +//----------------------------------------------------------------------------- +// Sets the current scene + animation list +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorFrame::SetAnimationSet( CDmeAnimationSet *pComboSystem ) +{ + m_pEditor->SetAnimationSet( pComboSystem ); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Ok" ) ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// Inherited from IDmNotify +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorFrame::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags ) +{ + if ( !IsVisible() ) + return; + + if ( nNotifySource == NOTIFY_SOURCE_PRESET_GROUP_EDITOR ) + return; + + m_pEditor->RefreshAnimationSet(); +} + + +//----------------------------------------------------------------------------- +// Chains notification messages from the contained panel to external clients +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorFrame::OnPresetsChanged() +{ + PostActionSignal( new KeyValues( "PresetsChanged" ) ); +} + +void CDmePresetGroupEditorFrame::OnAddNewPreset( KeyValues *pKeyValues ) +{ + PostActionSignal( pKeyValues->MakeCopy() ); +} + + +//----------------------------------------------------------------------------- +// Various command handlers related to the Edit menu +//----------------------------------------------------------------------------- +void CDmePresetGroupEditorFrame::OnUndo() +{ + if ( g_pDataModel->CanUndo() ) + { + CDisableUndoScopeGuard guard; + g_pDataModel->Undo(); + } +} + +void CDmePresetGroupEditorFrame::OnRedo() +{ + if ( g_pDataModel->CanRedo() ) + { + CDisableUndoScopeGuard guard; + g_pDataModel->Redo(); + } +} diff --git a/vgui2/dme_controls/filtercombobox.cpp b/vgui2/dme_controls/filtercombobox.cpp new file mode 100644 index 0000000..24f51c4 --- /dev/null +++ b/vgui2/dme_controls/filtercombobox.cpp @@ -0,0 +1,53 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/filtercombobox.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CFilterComboBox::CFilterComboBox( Panel *parent, const char *panelName, int numLines, bool allowEdit ) : + BaseClass( parent, panelName, numLines, allowEdit ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: panel lost focus message +//----------------------------------------------------------------------------- +void CFilterComboBox::OnKillFocus() +{ + int nLength = GetTextLength(); + char *pFilterText = (char*)_alloca( (nLength+1) * sizeof(char) ); + GetText( pFilterText, nLength+1 ); + + // Remove the existing version in the list + char pItemText[512]; + int nItemCount = GetItemCount(); + int i; + for ( i = 0; i < nItemCount; ++i ) + { + GetItemText( i, pItemText, sizeof(pItemText) ); + if ( !Q_stricmp( pFilterText, pItemText ) ) + break; + } + + if ( i != nItemCount ) + { + // Remove the existing copy + DeleteItem( i ); + } + + AddItem( pFilterText, NULL ); + + BaseClass::OnKillFocus( ); +} + + + diff --git a/vgui2/dme_controls/particlesystempanel.cpp b/vgui2/dme_controls/particlesystempanel.cpp new file mode 100644 index 0000000..e03f290 --- /dev/null +++ b/vgui2/dme_controls/particlesystempanel.cpp @@ -0,0 +1,676 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "dme_controls/particlesystempanel.h" +#include "dme_controls/dmepanel.h" +#include "movieobjects/dmeparticlesystemdefinition.h" +#include "materialsystem/imesh.h" +#include "materialsystem/imaterial.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "matsys_controls/matsyscontrols.h" +#include "vgui/IVGui.h" +#include "vgui_controls/propertypage.h" +#include "vgui_controls/propertysheet.h" +#include "vgui_controls/textentry.h" +#include "vgui_controls/splitter.h" +#include "vgui_controls/checkbutton.h" +#include "matsys_controls/colorpickerpanel.h" +#include "particles/particles.h" +#include "tier1/KeyValues.h" +#include "tier1/utlbuffer.h" +#include "tier2/renderutils.h" + + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Enums +//----------------------------------------------------------------------------- +enum +{ + SCROLLBAR_SIZE=18, // the width of a scrollbar + WINDOW_BORDER_WIDTH=2 // the width of the window's border +}; + +#define SPHERE_RADIUS 10.0f + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CParticleSystemPanel::CParticleSystemPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + m_pParticleSystem = NULL; + m_flLastTime = FLT_MAX; + m_bRenderBounds = false; + m_bRenderCullBounds = false; + m_bRenderHelpers = false; + m_bPerformNameBasedLookup = true; + m_ParticleSystemName = NULL; + InvalidateUniqueId( &m_ParticleSystemId ); + InvalidateUniqueId( &m_RenderHelperId ); + + LookAt( SPHERE_RADIUS ); + + m_pLightmapTexture.Init( "//platform/materials/debug/defaultlightmap", "editor" ); + m_DefaultEnvCubemap.Init( "editor/cubemap", "editor", true ); + for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) + { + SetControlPointValue( i, Vector( 0, 0, 10.0f * i ) ); + } +} + +CParticleSystemPanel::~CParticleSystemPanel() +{ + m_pLightmapTexture.Shutdown(); + m_DefaultEnvCubemap.Shutdown(); +} + + +//----------------------------------------------------------------------------- +// Scheme +//----------------------------------------------------------------------------- +void CParticleSystemPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + SetBorder( pScheme->GetBorder( "MenuBorder") ); +} + + +//----------------------------------------------------------------------------- +// Indicates that bounds should be drawn +//----------------------------------------------------------------------------- +void CParticleSystemPanel::RenderBounds( bool bEnable ) +{ + m_bRenderBounds = bEnable; +} + + +//----------------------------------------------------------------------------- +// Indicates that cull sphere should be drawn +//----------------------------------------------------------------------------- +void CParticleSystemPanel::RenderCullBounds( bool bEnable ) +{ + m_bRenderCullBounds = bEnable; +} + + +//----------------------------------------------------------------------------- +// Indicates that bounds should be drawn +//----------------------------------------------------------------------------- +void CParticleSystemPanel::RenderHelpers( bool bEnable ) +{ + m_bRenderHelpers = bEnable; +} + + +//----------------------------------------------------------------------------- +// Indicates which helper to draw +//----------------------------------------------------------------------------- +void CParticleSystemPanel::SetRenderedHelper( CDmeParticleFunction *pOp ) +{ + if ( !pOp ) + { + InvalidateUniqueId( &m_RenderHelperId ); + } + else + { + CopyUniqueId( pOp->GetId(), &m_RenderHelperId ); + } +} + + + +static bool IsValidHierarchy( CParticleCollection *pCollection ) +{ + if ( !pCollection->IsValid() ) + return false; + + for( CParticleCollection *pChild = pCollection->m_Children.m_pHead; pChild; pChild = pChild->m_pNext ) + { + if ( !IsValidHierarchy( pChild ) ) + return false; + } + return true; +} + + +//----------------------------------------------------------------------------- +// Simulate the particle system +//----------------------------------------------------------------------------- +void CParticleSystemPanel::OnTick() +{ + BaseClass::OnTick(); + if ( !m_pParticleSystem ) + return; + + float flTime = Plat_FloatTime(); + if ( m_flLastTime == FLT_MAX ) + { + m_flLastTime = flTime; + } + + float flDt = flTime - m_flLastTime; + m_flLastTime = flTime; + + for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) + { + if ( !m_pParticleSystem->ReadsControlPoint( i ) ) + continue; + + m_pParticleSystem->SetControlPoint( i, m_pControlPointValue[i] ); + m_pParticleSystem->SetControlPointOrientation( i, Vector( 1, 0, 0 ), Vector( 0, -1, 0 ), Vector( 0, 0, 1 ) ); + m_pParticleSystem->SetControlPointParent( i, i ); + } + + // Restart the particle system if it's finished + bool bIsInvalid = !IsValidHierarchy( m_pParticleSystem ); + + if ( !bIsInvalid ) + { + m_pParticleSystem->Simulate( flDt, false ); + } + + if ( m_pParticleSystem->IsFinished() || bIsInvalid ) + { + delete m_pParticleSystem; + m_pParticleSystem = NULL; + + if ( m_bPerformNameBasedLookup ) + { + if ( m_ParticleSystemName.Length() ) + { + CParticleCollection *pNewParticleSystem = g_pParticleSystemMgr->CreateParticleCollection( m_ParticleSystemName ); + m_pParticleSystem = pNewParticleSystem; + } + } + else + { + if ( IsUniqueIdValid( m_ParticleSystemId ) ) + { + CParticleCollection *pNewParticleSystem = g_pParticleSystemMgr->CreateParticleCollection( m_ParticleSystemId ); + m_pParticleSystem = pNewParticleSystem; + } + } + + if ( bIsInvalid ) + { + PostActionSignal( new KeyValues( "ParticleSystemReconstructed" ) ); + } + m_flLastTime = FLT_MAX; + } +} + + +//----------------------------------------------------------------------------- +// Startup, shutdown particle collection +//----------------------------------------------------------------------------- +void CParticleSystemPanel::StartupParticleCollection() +{ + if ( m_pParticleSystem ) + { + vgui::ivgui()->AddTickSignal( GetVPanel(), 0 ); + } + m_flLastTime = FLT_MAX; +} + +void CParticleSystemPanel::ShutdownParticleCollection() +{ + if ( m_pParticleSystem ) + { + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + delete m_pParticleSystem; + m_pParticleSystem = NULL; + } +} + +//----------------------------------------------------------------------------- +// Set the particle system to draw +//----------------------------------------------------------------------------- +void CParticleSystemPanel::SetParticleSystem( CDmeParticleSystemDefinition *pDef ) +{ + ShutdownParticleCollection(); + if ( pDef ) + { + m_bPerformNameBasedLookup = pDef->UseNameBasedLookup(); + if ( m_bPerformNameBasedLookup ) + { + m_ParticleSystemName = pDef->GetName(); + Assert( g_pParticleSystemMgr->IsParticleSystemDefined( m_ParticleSystemName ) ); + m_pParticleSystem = g_pParticleSystemMgr->CreateParticleCollection( m_ParticleSystemName ); + } + else + { + CopyUniqueId( pDef->GetId(), &m_ParticleSystemId ); + Assert( g_pParticleSystemMgr->IsParticleSystemDefined( m_ParticleSystemId ) ); + m_pParticleSystem = g_pParticleSystemMgr->CreateParticleCollection( m_ParticleSystemId ); + } + PostActionSignal( new KeyValues( "ParticleSystemReconstructed" ) ); + } + StartupParticleCollection(); +} + +void CParticleSystemPanel::SetDmeElement( CDmeParticleSystemDefinition *pDef ) +{ + SetParticleSystem( pDef ); +} + +CParticleCollection *CParticleSystemPanel::GetParticleSystem() +{ + return m_pParticleSystem; +} + + +//----------------------------------------------------------------------------- +// Draw bounds +//----------------------------------------------------------------------------- +void CParticleSystemPanel::DrawBounds() +{ + Vector vecMins, vecMaxs; + m_pParticleSystem->GetBounds( &vecMins, &vecMaxs ); + RenderWireframeBox( vec3_origin, vec3_angle, vecMins, vecMaxs, Color( 0, 255, 255, 255 ), true ); +} + + +//----------------------------------------------------------------------------- +// Draw cull bounds +//----------------------------------------------------------------------------- +void CParticleSystemPanel::DrawCullBounds() +{ + Vector vecCenter; + m_pParticleSystem->GetControlPointAtTime( m_pParticleSystem->m_pDef->GetCullControlPoint(), m_pParticleSystem->m_flCurTime, &vecCenter ); + RenderWireframeSphere( vecCenter, m_pParticleSystem->m_pDef->GetCullRadius(), 32, 16, Color( 0, 255, 255, 255 ), true ); +} + + +//----------------------------------------------------------------------------- +// paint it! +//----------------------------------------------------------------------------- +#define AXIS_SIZE 5.0f + +void CParticleSystemPanel::OnPaint3D() +{ + if ( !m_pParticleSystem ) + return; + + // This needs calling to reset various counters. + g_pParticleSystemMgr->SetLastSimulationTime( m_pParticleSystem->m_flCurTime ); + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + pRenderContext->BindLightmapTexture( m_pLightmapTexture ); + pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap ); + + // Draw axes + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity( ); + + if ( m_bRenderBounds ) + { + DrawBounds(); + Vector vP1; + Vector vP2; + m_pParticleSystem->GetControlPointAtTime( 0, m_pParticleSystem->m_flCurTime, &vP1 ); + m_pParticleSystem->GetControlPointAtTime( 1, m_pParticleSystem->m_flCurTime, &vP2 ); + RenderLine( vP1, vP2, Color( 0, 255, 255, 255 ), true ); + } + + if ( m_bRenderCullBounds ) + { + DrawCullBounds(); + } + + if ( m_bRenderHelpers && IsUniqueIdValid( m_RenderHelperId ) ) + { + m_pParticleSystem->VisualizeOperator( &m_RenderHelperId ); + } + m_pParticleSystem->Render( pRenderContext ); + m_pParticleSystem->VisualizeOperator( ); + RenderAxes( vec3_origin, AXIS_SIZE, true ); + + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PopMatrix(); +} + + +//----------------------------------------------------------------------------- +// +// Control point page +// +//----------------------------------------------------------------------------- +class CControlPointPage : public vgui::PropertyPage +{ + DECLARE_CLASS_SIMPLE( CControlPointPage, vgui::PropertyPage ); + +public: + // constructor, destructor + CControlPointPage( vgui::Panel *pParent, const char *pName, CParticleSystemPanel *pParticleSystemPanel ); + + virtual void PerformLayout(); + + void CreateControlPointControls( ); + +private: + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", params ); + MESSAGE_FUNC_PARAMS( OnNewLine, "TextNewLine", params ); + + void LayoutControlPointControls(); + void CleanUpControlPointControls(); + + vgui::Label *m_pControlPointName[MAX_PARTICLE_CONTROL_POINTS]; + vgui::TextEntry *m_pControlPointValue[MAX_PARTICLE_CONTROL_POINTS]; + CParticleSystemPanel *m_pParticleSystemPanel; +}; + + +//----------------------------------------------------------------------------- +// Contstructor +//----------------------------------------------------------------------------- +CControlPointPage::CControlPointPage( vgui::Panel *pParent, const char *pName, CParticleSystemPanel *pParticleSystemPanel ) : + BaseClass( pParent, pName ) +{ + for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) + { + m_pControlPointName[i] = NULL; + m_pControlPointValue[i] = NULL; + } + + m_pParticleSystemPanel = pParticleSystemPanel; +} + + +//----------------------------------------------------------------------------- +// Called when the text entry for a control point is changed +//----------------------------------------------------------------------------- +void CControlPointPage::OnTextChanged( KeyValues *pParams ) +{ + vgui::Panel *pPanel = (vgui::Panel *)pParams->GetPtr( "panel" ); + for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) + { + if ( pPanel != m_pControlPointValue[i] ) + continue; + + char pBuf[512]; + m_pControlPointValue[i]->GetText( pBuf, sizeof(pBuf) ); + + Vector vecValue( 0, 0, 0 ); + sscanf( pBuf, "%f %f %f", &vecValue.x, &vecValue.y, &vecValue.z ); + m_pParticleSystemPanel->SetControlPointValue( i, vecValue ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Called when the text entry for a control point is changed +//----------------------------------------------------------------------------- +void CControlPointPage::OnNewLine( KeyValues *pParams ) +{ + vgui::Panel *pPanel = (vgui::Panel *)pParams->GetPtr( "panel" ); + for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) + { + if ( pPanel != m_pControlPointValue[i] ) + continue; + + char pBuf[512]; + m_pControlPointValue[i]->GetText( pBuf, sizeof(pBuf) ); + + Vector vecValue( 0, 0, 0 ); + sscanf( pBuf, "%f %f %f", &vecValue.x, &vecValue.y, &vecValue.z ); + m_pParticleSystemPanel->SetControlPointValue( i, vecValue ); + + vecValue = m_pParticleSystemPanel->GetControlPointValue( i ); + Q_snprintf( pBuf, sizeof(pBuf), "%.3f %.3f %.3f", vecValue.x, vecValue.y, vecValue.z ); + m_pControlPointValue[i]->SetText( pBuf ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Called when the particle system changes +//----------------------------------------------------------------------------- +void CControlPointPage::PerformLayout() +{ + BaseClass::PerformLayout(); + LayoutControlPointControls(); +} + + +//----------------------------------------------------------------------------- +// Creates controls used to modify control point values +//----------------------------------------------------------------------------- +void CControlPointPage::CreateControlPointControls() +{ + CleanUpControlPointControls(); + CParticleCollection* pParticleSystem = m_pParticleSystemPanel->GetParticleSystem(); + if ( !pParticleSystem ) + return; + + for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) + { + if ( !pParticleSystem->ReadsControlPoint( i ) ) + continue; + + char pName[512]; + Q_snprintf( pName, sizeof(pName), "Pt #%d:", i ); + m_pControlPointName[i] = new Label( this, pName, pName ); + + Q_snprintf( pName, sizeof(pName), "Entry #%d:", i ); + m_pControlPointValue[i] = new TextEntry( this, pName ); + m_pControlPointValue[i]->AddActionSignalTarget( this ); + m_pControlPointValue[i]->SendNewLine( true ); + m_pControlPointValue[i]->SetMultiline( false ); + + const Vector &vecValue = m_pParticleSystemPanel->GetControlPointValue( i ); + Q_snprintf( pName, sizeof(pName), "%.3f %.3f %.3f", vecValue.x, vecValue.y, vecValue.z ); + m_pControlPointValue[i]->SetText( pName ); + } + + LayoutControlPointControls(); +} + + +//----------------------------------------------------------------------------- +// Lays out the controls +//----------------------------------------------------------------------------- +void CControlPointPage::LayoutControlPointControls() +{ + int nFoundControlCount = 0; + for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) + { + if ( !m_pControlPointName[i] ) + continue; + + int yVal = 8 + nFoundControlCount * 28; + m_pControlPointName[i]->SetBounds( 8, yVal, 48, 24 ); + m_pControlPointValue[i]->SetBounds( 64, yVal, 160, 24 ); + ++nFoundControlCount; + } +} + + +//----------------------------------------------------------------------------- +// Cleans up controls used to modify control point values +//----------------------------------------------------------------------------- +void CControlPointPage::CleanUpControlPointControls( ) +{ + for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) + { + if ( m_pControlPointName[i] ) + { + delete m_pControlPointName[i]; + m_pControlPointName[i] = NULL; + } + + if ( m_pControlPointValue[i] ) + { + delete m_pControlPointValue[i]; + m_pControlPointValue[i] = NULL; + } + } +} + + +//----------------------------------------------------------------------------- +// +// CParticleSystemPreviewPanel +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Dme panel connection +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CParticleSystemPreviewPanel, DmeParticleSystemDefinition, "DmeParticleSystemDefinitionViewer", "Particle System Viewer", false ); + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CParticleSystemPreviewPanel::CParticleSystemPreviewPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_Splitter = new vgui::Splitter( this, "Splitter", SPLITTER_MODE_VERTICAL, 1 ); + vgui::Panel *pSplitterLeftSide = m_Splitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_Splitter->GetChild( 1 ); + + m_pParticleSystemPanel = new CParticleSystemPanel( pSplitterRightSide, "ParticlePreview" ); + m_pParticleSystemPanel->AddActionSignalTarget( this ); + m_pParticleSystemPanel->SetBackgroundColor( 0, 0, 0 ); + + m_pParticleCount = new vgui::Label( pSplitterRightSide, "ParticleCountLabel", "" ); + m_pParticleCount->SetZPos( 1 ); + + m_pControlSheet = new vgui::PropertySheet( pSplitterLeftSide, "ControlSheet" ); + + m_pRenderPage = new vgui::PropertyPage( m_pControlSheet, "RenderPage" ); + + m_pRenderBounds = new vgui::CheckButton( m_pRenderPage, "RenderBounds", "Render Bounding Box" ); + m_pRenderBounds->AddActionSignalTarget( this ); + + m_pRenderCullBounds = new vgui::CheckButton( m_pRenderPage, "RenderCullBounds", "Render Culling Bounds" ); + m_pRenderCullBounds->AddActionSignalTarget( this ); + + m_pRenderHelpers = new vgui::CheckButton( m_pRenderPage, "RenderHelpers", "Render Helpers" ); + m_pRenderHelpers->AddActionSignalTarget( this ); + + m_pBackgroundColor = new CColorPickerButton( m_pRenderPage, "BackgroundColor", this ); + m_pBackgroundColor->SetColor( m_pParticleSystemPanel->GetBackgroundColor() ); + + m_pRenderPage->LoadControlSettingsAndUserConfig( "resource/particlesystempreviewpanel_renderpage.res" ); + + m_pControlPointPage = new CControlPointPage( m_pControlSheet, "ControlPointPage", m_pParticleSystemPanel ); + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettingsAndUserConfig( "resource/particlesystempreviewpanel.res" ); + + // NOTE: Page adding happens *after* LoadControlSettingsAndUserConfig + // because the layout of the sheet is correct at this point. + m_pControlSheet->AddPage( m_pRenderPage, "Render" ); + m_pControlSheet->AddPage( m_pControlPointPage, "Ctrl Pts" ); +} + +CParticleSystemPreviewPanel::~CParticleSystemPreviewPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Set the particle system to draw +//----------------------------------------------------------------------------- +void CParticleSystemPreviewPanel::OnThink() +{ + BaseClass::OnThink(); + CParticleCollection* pParticleSystem = m_pParticleSystemPanel->GetParticleSystem(); + if ( !pParticleSystem ) + { + m_pParticleCount->SetText( "" ); + } + else + { + char buf[256]; + Q_snprintf( buf, sizeof(buf), "Particle Count: %5d/%5d", + pParticleSystem->m_nActiveParticles, pParticleSystem->m_nAllocatedParticles ); + m_pParticleCount->SetText( buf ); + } +} + + +//----------------------------------------------------------------------------- +// Called when the particle system changes +//----------------------------------------------------------------------------- +void CParticleSystemPreviewPanel::OnParticleSystemReconstructed() +{ + m_pControlPointPage->CreateControlPointControls(); +} + + +//----------------------------------------------------------------------------- +// Set the particle system to draw +//----------------------------------------------------------------------------- +void CParticleSystemPreviewPanel::SetParticleSystem( CDmeParticleSystemDefinition *pDef ) +{ + m_pParticleSystemPanel->SetParticleSystem( pDef ); +} + +void CParticleSystemPreviewPanel::SetDmeElement( CDmeParticleSystemDefinition *pDef ) +{ + m_pParticleSystemPanel->SetDmeElement( pDef ); +} + + +//----------------------------------------------------------------------------- +// Indicates which helper to draw +//----------------------------------------------------------------------------- +void CParticleSystemPreviewPanel::SetParticleFunction( CDmeParticleFunction *pFunction ) +{ + m_pParticleSystemPanel->SetRenderedHelper( pFunction ); +} + + +//----------------------------------------------------------------------------- +// Called when the check button is checked +//----------------------------------------------------------------------------- +void CParticleSystemPreviewPanel::OnCheckButtonChecked( KeyValues *pParams ) +{ + int state = pParams->GetInt( "state", 0 ); + vgui::Panel *pPanel = (vgui::Panel*)pParams->GetPtr( "panel" ); + if ( pPanel == m_pRenderBounds ) + { + m_pParticleSystemPanel->RenderBounds( state ); + return; + } + if ( pPanel == m_pRenderCullBounds ) + { + m_pParticleSystemPanel->RenderCullBounds( state ); + return; + } + if ( pPanel == m_pRenderHelpers ) + { + m_pParticleSystemPanel->RenderHelpers( state ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when a new background color is picked +//----------------------------------------------------------------------------- +void CParticleSystemPreviewPanel::OnBackgroundColorChanged( KeyValues *pParams ) +{ + m_pParticleSystemPanel->SetBackgroundColor( pParams->GetColor( "color" ) ); +} + +void CParticleSystemPreviewPanel::OnBackgroundColorPreview( KeyValues *pParams ) +{ + m_pParticleSystemPanel->SetBackgroundColor( pParams->GetColor( "color" ) ); +} + +void CParticleSystemPreviewPanel::OnBackgroundColorCancel( KeyValues *pParams ) +{ + m_pParticleSystemPanel->SetBackgroundColor( pParams->GetColor( "startingColor" ) ); +} diff --git a/vgui2/dme_controls/particlesystempropertiespanel.cpp b/vgui2/dme_controls/particlesystempropertiespanel.cpp new file mode 100644 index 0000000..fb795e7 --- /dev/null +++ b/vgui2/dme_controls/particlesystempropertiespanel.cpp @@ -0,0 +1,1149 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Dialog used to edit properties of a particle system definition +// +//===========================================================================// + +#include "dme_controls/ParticleSystemPropertiesPanel.h" +#include "tier1/KeyValues.h" +#include "tier1/utlbuffer.h" +#include "vgui/IVGui.h" +#include "vgui_controls/button.h" +#include "vgui_controls/listpanel.h" +#include "vgui_controls/splitter.h" +#include "vgui_controls/messagebox.h" +#include "vgui_controls/combobox.h" +#include "datamodel/dmelement.h" +#include "movieobjects/dmeparticlesystemdefinition.h" +#include "dme_controls/elementpropertiestree.h" +#include "matsys_controls/picker.h" +#include "dme_controls/dmecontrols_utils.h" +#include "dme_controls/particlesystempanel.h" +#include "dme_controls/dmepanel.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Purpose: Picker for particle functions +// +//----------------------------------------------------------------------------- +class CParticleFunctionPickerFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CParticleFunctionPickerFrame, vgui::Frame ); + +public: + CParticleFunctionPickerFrame( vgui::Panel *pParent, const char *pTitle ); + ~CParticleFunctionPickerFrame(); + + // Sets the current scene + animation list + void DoModal( CDmeParticleSystemDefinition *pParticleSystem, ParticleFunctionType_t type, KeyValues *pContextKeyValues ); + + // Inherited from Frame + virtual void OnCommand( const char *pCommand ); + +private: + // Refreshes the list of particle functions + void RefreshParticleFunctions( CDmeParticleSystemDefinition *pDefinition, ParticleFunctionType_t type ); + void CleanUpMessage(); + + vgui::ListPanel *m_pFunctionList; + vgui::Button *m_pOpenButton; + vgui::Button *m_pCancelButton; + KeyValues *m_pContextKeyValues; +}; + +static int __cdecl ParticleFunctionNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString( "name" ); + const char *string2 = item2.kv->GetString( "name" ); + return Q_stricmp( string1, string2 ); +} + +CParticleFunctionPickerFrame::CParticleFunctionPickerFrame( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "ParticleFunctionPickerFrame" ) +{ + SetDeleteSelfOnClose( true ); + m_pContextKeyValues = NULL; + + m_pFunctionList = new vgui::ListPanel( this, "ParticleFunctionList" ); + m_pFunctionList->AddColumnHeader( 0, "name", "Particle Function Name", 52, 0 ); + m_pFunctionList->SetSelectIndividualCells( false ); + m_pFunctionList->SetMultiselectEnabled( false ); + m_pFunctionList->SetEmptyListText( "No particle functions" ); + m_pFunctionList->AddActionSignalTarget( this ); + m_pFunctionList->SetSortFunc( 0, ParticleFunctionNameSortFunc ); + m_pFunctionList->SetSortColumn( 0 ); + + m_pOpenButton = new vgui::Button( this, "OkButton", "#MessageBox_OK", this, "Ok" ); + m_pCancelButton = new vgui::Button( this, "CancelButton", "#MessageBox_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/particlefunctionpicker.res" ); + + SetTitle( pTitle, false ); +} + +CParticleFunctionPickerFrame::~CParticleFunctionPickerFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Refreshes the list of raw controls +//----------------------------------------------------------------------------- +void CParticleFunctionPickerFrame::RefreshParticleFunctions( CDmeParticleSystemDefinition *pParticleSystem, ParticleFunctionType_t type ) +{ + m_pFunctionList->RemoveAll(); + if ( !pParticleSystem ) + return; + + CUtlVector< IParticleOperatorDefinition *> &list = g_pParticleSystemMgr->GetAvailableParticleOperatorList( type ); + int nCount = list.Count(); + + // Build a list of used operator IDs + bool pUsedIDs[OPERATOR_ID_COUNT]; + memset( pUsedIDs, 0, sizeof(pUsedIDs) ); + + int nFunctionCount = pParticleSystem->GetParticleFunctionCount( type ); + for ( int i = 0; i < nFunctionCount; ++i ) + { + const char *pFunctionName = pParticleSystem->GetParticleFunction( type, i )->GetName(); + for ( int j = 0; j < nCount; ++j ) + { + if ( Q_stricmp( pFunctionName, list[j]->GetName() ) ) + continue; + + if ( list[j]->GetId() >= 0 ) + { + pUsedIDs[ list[j]->GetId() ] = true; + } + break; + } + } + + for ( int i = 0; i < nCount; ++i ) + { + const char *pFunctionName = list[i]->GetName(); + + // Look to see if this is in a special operator group + if ( list[i]->GetId() >= 0 ) + { + // Don't display ones that are already in the particle system + if ( pUsedIDs[ list[i]->GetId() ] ) + continue; + } + + if ( list[i]->GetId() == OPERATOR_SINGLETON ) + { + // Don't display ones that are already in the particle system + if ( pParticleSystem->FindFunction( type, pFunctionName ) >= 0 ) + continue; + } + + // Don't display obsolete operators + if ( list[i]->IsObsolete() ) + continue; + + KeyValues *kv = new KeyValues( "node", "name", pFunctionName ); + m_pFunctionList->AddItem( kv, 0, false, false ); + } + + m_pFunctionList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CParticleFunctionPickerFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Sets the current scene + animation list +//----------------------------------------------------------------------------- +void CParticleFunctionPickerFrame::DoModal( CDmeParticleSystemDefinition *pParticleSystem, ParticleFunctionType_t type, KeyValues *pContextKeyValues ) +{ + CleanUpMessage(); + RefreshParticleFunctions( pParticleSystem, type ); + m_pContextKeyValues = pContextKeyValues; + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CParticleFunctionPickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Ok" ) ) + { + int nSelectedItemCount = m_pFunctionList->GetSelectedItemsCount(); + if ( nSelectedItemCount == 0 ) + return; + + Assert( nSelectedItemCount == 1 ); + int nItemID = m_pFunctionList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pFunctionList->GetItem( nItemID ); + + KeyValues *pActionKeys = new KeyValues( "ParticleFunctionPicked" ); + pActionKeys->SetString( "name", pKeyValues->GetString( "name" ) ); + + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + + // This prevents them from being deleted later + m_pContextKeyValues = NULL; + } + + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + + +//----------------------------------------------------------------------------- +// +// Purpose: Picker for child particle systems +// +//----------------------------------------------------------------------------- +class CParticleChildrenPickerFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CParticleChildrenPickerFrame, vgui::Frame ); + +public: + CParticleChildrenPickerFrame( vgui::Panel *pParent, const char *pTitle, IParticleSystemPropertiesPanelQuery *pQuery ); + ~CParticleChildrenPickerFrame(); + + // Sets the current scene + animation list + void DoModal( CDmeParticleSystemDefinition *pParticleSystem, KeyValues *pContextKeyValues ); + + // Inherited from Frame + virtual void OnCommand( const char *pCommand ); + +private: + // Refreshes the list of children particle systems + void RefreshChildrenList( CDmeParticleSystemDefinition *pDefinition ); + void CleanUpMessage(); + + IParticleSystemPropertiesPanelQuery *m_pQuery; + vgui::ListPanel *m_pChildrenList; + vgui::Button *m_pOpenButton; + vgui::Button *m_pCancelButton; + KeyValues *m_pContextKeyValues; +}; + +static int __cdecl ParticleChildrenNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString( "name" ); + const char *string2 = item2.kv->GetString( "name" ); + return Q_stricmp( string1, string2 ); +} + +CParticleChildrenPickerFrame::CParticleChildrenPickerFrame( vgui::Panel *pParent, const char *pTitle, IParticleSystemPropertiesPanelQuery *pQuery ) : + BaseClass( pParent, "ParticleChildrenPickerFrame" ), m_pQuery( pQuery ) +{ + SetDeleteSelfOnClose( true ); + m_pContextKeyValues = NULL; + + m_pChildrenList = new vgui::ListPanel( this, "ParticleChildrenList" ); + m_pChildrenList->AddColumnHeader( 0, "name", "Particle System Name", 52, 0 ); + m_pChildrenList->SetSelectIndividualCells( false ); + m_pChildrenList->SetMultiselectEnabled( false ); + m_pChildrenList->SetEmptyListText( "No particle systems" ); + m_pChildrenList->AddActionSignalTarget( this ); + m_pChildrenList->SetSortFunc( 0, ParticleChildrenNameSortFunc ); + m_pChildrenList->SetSortColumn( 0 ); + + m_pOpenButton = new vgui::Button( this, "OkButton", "#MessageBox_OK", this, "Ok" ); + m_pCancelButton = new vgui::Button( this, "CancelButton", "#MessageBox_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/particlechildrenpicker.res" ); + + SetTitle( pTitle, false ); +} + +CParticleChildrenPickerFrame::~CParticleChildrenPickerFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Refreshes the list of raw controls +//----------------------------------------------------------------------------- +void CParticleChildrenPickerFrame::RefreshChildrenList( CDmeParticleSystemDefinition *pCurrentParticleSystem ) +{ + m_pChildrenList->RemoveAll(); + + CUtlVector< CDmeParticleSystemDefinition* > definitions; + if ( m_pQuery ) + { + m_pQuery->GetKnownParticleDefinitions( definitions ); + } + int nCount = definitions.Count(); + if ( nCount == 0 ) + return; + + for ( int i = 0; i < nCount; ++i ) + { + CDmeParticleSystemDefinition *pParticleSystem = definitions[i]; + if ( pParticleSystem == pCurrentParticleSystem ) + continue; + + const char *pName = pParticleSystem->GetName(); + if ( !pName || !pName[0] ) + { + pName = "<no name>"; + } + + KeyValues *kv = new KeyValues( "node" ); + kv->SetString( "name", pName ); + SetElementKeyValue( kv, "particleSystem", pParticleSystem ); + + m_pChildrenList->AddItem( kv, 0, false, false ); + } + m_pChildrenList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CParticleChildrenPickerFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Sets the current scene + animation list +//----------------------------------------------------------------------------- +void CParticleChildrenPickerFrame::DoModal( CDmeParticleSystemDefinition *pParticleSystem, KeyValues *pContextKeyValues ) +{ + CleanUpMessage(); + RefreshChildrenList( pParticleSystem ); + m_pContextKeyValues = pContextKeyValues; + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CParticleChildrenPickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Ok" ) ) + { + int nSelectedItemCount = m_pChildrenList->GetSelectedItemsCount(); + if ( nSelectedItemCount == 0 ) + return; + + Assert( nSelectedItemCount == 1 ); + int nItemID = m_pChildrenList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pChildrenList->GetItem( nItemID ); + CDmeParticleSystemDefinition *pParticleSystem = GetElementKeyValue<CDmeParticleSystemDefinition>( pKeyValues, "particleSystem" ); + + KeyValues *pActionKeys = new KeyValues( "ParticleChildPicked" ); + SetElementKeyValue( pActionKeys, "particleSystem", pParticleSystem ); + + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + + // This prevents them from being deleted later + m_pContextKeyValues = NULL; + } + + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// Browser of various particle functions +//----------------------------------------------------------------------------- +class CParticleFunctionBrowser : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CParticleFunctionBrowser, vgui::EditablePanel ); + +public: + // constructor, destructor + CParticleFunctionBrowser( vgui::Panel *pParent, const char *pName, ParticleFunctionType_t type, IParticleSystemPropertiesPanelQuery *pQuery ); + virtual ~CParticleFunctionBrowser(); + + // Inherited from Panel + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + + void SetParticleFunctionProperties( CDmeElementPanel *pPanel ); + void SetParticleSystem( CDmeParticleSystemDefinition *pOp ); + void RefreshParticleFunctionList(); + void SelectDefaultFunction(); + + // Called when a list panel's selection changes + void RefreshParticleFunctionProperties( ); + +private: + MESSAGE_FUNC_PARAMS( OnOpenContextMenu, "OpenContextMenu", kv ); + MESSAGE_FUNC_PARAMS( OnItemSelected, "ItemSelected", kv ); + MESSAGE_FUNC_PARAMS( OnItemDeselected, "ItemDeselected", kv ); + MESSAGE_FUNC( OnAdd, "Add" ); + MESSAGE_FUNC( OnRemove, "Remove" ); + MESSAGE_FUNC( OnRename, "Rename" ); + MESSAGE_FUNC( OnMoveUp, "MoveUp" ); + MESSAGE_FUNC( OnMoveDown, "MoveDown" ); + MESSAGE_FUNC_PARAMS( OnInputCompleted, "InputCompleted", kv ); + MESSAGE_FUNC_PARAMS( OnParticleFunctionPicked, "ParticleFunctionPicked", kv ); + MESSAGE_FUNC_PARAMS( OnParticleChildPicked, "ParticleChildPicked", kv ); + + // Cleans up the context menu + void CleanupContextMenu(); + + // Returns the selected particle function + CDmeParticleFunction* GetSelectedFunction( ); + + // Returns the selected particle function + CDmeParticleFunction* GetSelectedFunction( int nIndex ); + + // Select a particular particle function + void SelectParticleFunction( CDmeParticleFunction *pFind ); + + IParticleSystemPropertiesPanelQuery *m_pQuery; + CDmeHandle< CDmeParticleSystemDefinition > m_hParticleSystem; + vgui::ListPanel *m_pFunctionList; + ParticleFunctionType_t m_FuncType; + vgui::DHANDLE< vgui::Menu > m_hContextMenu; + CDmeElementPanel *m_pParticleFunctionProperties; +}; + + +//----------------------------------------------------------------------------- +// Sort functions for list panel +//----------------------------------------------------------------------------- +static int __cdecl ParticleFunctionSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + int i1 = item1.kv->GetInt( "index" ); + int i2 = item2.kv->GetInt( "index" ); + return i1 - i2; +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CParticleFunctionBrowser::CParticleFunctionBrowser( vgui::Panel *pParent, const char *pName, ParticleFunctionType_t type, IParticleSystemPropertiesPanelQuery *pQuery ) : + BaseClass( pParent, pName ), m_pQuery( pQuery ) +{ + SetKeyBoardInputEnabled( true ); + + m_FuncType = type; + + m_pFunctionList = new vgui::ListPanel( this, "FunctionList" ); + if ( m_FuncType != FUNCTION_CHILDREN ) + { + m_pFunctionList->AddColumnHeader( 0, "name", "Function Name", 150, 0 ); + m_pFunctionList->AddColumnHeader( 1, "type", "Function Type", 150, 0 ); + m_pFunctionList->SetEmptyListText( "No particle functions" ); + } + else + { + m_pFunctionList->AddColumnHeader( 0, "name", "Label", 150, 0 ); + m_pFunctionList->AddColumnHeader( 1, "type", "Child Name", 150, 0 ); + m_pFunctionList->SetEmptyListText( "No children" ); + } + m_pFunctionList->SetSelectIndividualCells( false ); + m_pFunctionList->SetMultiselectEnabled( true ); + m_pFunctionList->AddActionSignalTarget( this ); + m_pFunctionList->SetSortFunc( 0, ParticleFunctionSortFunc ); + m_pFunctionList->SetSortColumn( 0 ); + m_pFunctionList->AddActionSignalTarget( this ); + + LoadControlSettings( "resource/particlefunctionbrowser.res" ); +} + +CParticleFunctionBrowser::~CParticleFunctionBrowser() +{ + CleanupContextMenu(); + SaveUserConfig(); +} + + +//----------------------------------------------------------------------------- +// Cleans up the context menu +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::CleanupContextMenu() +{ + if ( m_hContextMenu.Get() ) + { + m_hContextMenu->MarkForDeletion(); + m_hContextMenu = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Selects a particular function by default +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::SelectDefaultFunction() +{ + if ( m_pFunctionList->GetSelectedItemsCount() == 0 && m_pFunctionList->GetItemCount() > 0 ) + { + int nItemID = m_pFunctionList->GetItemIDFromRow( 0 ); + m_pFunctionList->SetSingleSelectedItem( nItemID ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the particle system properties panel +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::SetParticleFunctionProperties( CDmeElementPanel *pPanel ) +{ + m_pParticleFunctionProperties = pPanel; +} + + +//----------------------------------------------------------------------------- +// Sets/gets the particle system +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::SetParticleSystem( CDmeParticleSystemDefinition *pParticleSystem ) +{ + if ( pParticleSystem != m_hParticleSystem.Get() ) + { + m_hParticleSystem = pParticleSystem; + RefreshParticleFunctionList(); + SelectDefaultFunction(); + } +} + + +//----------------------------------------------------------------------------- +// Builds the particle function list for the particle system +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::RefreshParticleFunctionList() +{ + if ( !m_hParticleSystem.Get() ) + return; + + // Maintain selection if possible + CUtlVector< CDmeParticleFunction* > selectedItems; + int nCount = m_pFunctionList->GetSelectedItemsCount(); + for ( int i = 0; i < nCount; ++i ) + { + selectedItems.AddToTail( GetSelectedFunction( i ) ); + } + + m_pFunctionList->RemoveAll(); + + int nSelectedCount = selectedItems.Count(); + nCount = m_hParticleSystem->GetParticleFunctionCount( m_FuncType ); + for ( int i = 0; i < nCount; ++i ) + { + CDmeParticleFunction *pFunction = m_hParticleSystem->GetParticleFunction( m_FuncType, i ); + KeyValues *kv = new KeyValues( "node", "name", pFunction->GetName() ); + kv->SetString( "type", pFunction->GetFunctionType() ); + kv->SetInt( "index", i ); + SetElementKeyValue( kv, "particleFunction", pFunction ); + int nItemID = m_pFunctionList->AddItem( kv, 0, false, false ); + + for ( int j = 0; j < nSelectedCount; ++j ) + { + if ( selectedItems[j] == pFunction ) + { + m_pFunctionList->AddSelectedItem( nItemID ); + selectedItems.FastRemove( j ); + --nSelectedCount; + break; + } + } + } + + m_pFunctionList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Returns the selected particle function +//----------------------------------------------------------------------------- +CDmeParticleFunction* CParticleFunctionBrowser::GetSelectedFunction( int nIndex ) +{ + if ( !m_hParticleSystem.Get() ) + return NULL; + + int nSelectedItemCount = m_pFunctionList->GetSelectedItemsCount(); + if ( nSelectedItemCount <= nIndex ) + return NULL; + + int nItemID = m_pFunctionList->GetSelectedItem( nIndex ); + KeyValues *pKeyValues = m_pFunctionList->GetItem( nItemID ); + return GetElementKeyValue<CDmeParticleFunction>( pKeyValues, "particleFunction" ); +} + + +//----------------------------------------------------------------------------- +// Returns the selected particle function +//----------------------------------------------------------------------------- +CDmeParticleFunction* CParticleFunctionBrowser::GetSelectedFunction( ) +{ + if ( !m_hParticleSystem.Get() ) + return NULL; + + int nSelectedItemCount = m_pFunctionList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return NULL; + + int nItemID = m_pFunctionList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pFunctionList->GetItem( nItemID ); + return GetElementKeyValue<CDmeParticleFunction>( pKeyValues, "particleFunction" ); +} + +void CParticleFunctionBrowser::OnMoveUp( ) +{ + CDmeParticleFunction* pFunction = GetSelectedFunction( ); + if ( !pFunction ) + return; + + { + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Move Function Up", "Move Function Up" ); + m_hParticleSystem->MoveFunctionUp( m_FuncType, pFunction ); + } + PostActionSignal( new KeyValues( "ParticleSystemModified" ) ); +} + +void CParticleFunctionBrowser::OnMoveDown( ) +{ + CDmeParticleFunction* pFunction = GetSelectedFunction( ); + if ( !pFunction ) + return; + + { + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Move Function Down", "Move Function Down" ); + m_hParticleSystem->MoveFunctionDown( m_FuncType, pFunction ); + } + PostActionSignal( new KeyValues( "ParticleSystemModified" ) ); +} + + +//----------------------------------------------------------------------------- +// Select a particular particle function +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::SelectParticleFunction( CDmeParticleFunction *pFind ) +{ + m_pFunctionList->ClearSelectedItems(); + for ( int nItemID = m_pFunctionList->FirstItem(); nItemID != m_pFunctionList->InvalidItemID(); nItemID = m_pFunctionList->NextItem( nItemID ) ) + { + KeyValues *kv = m_pFunctionList->GetItem( nItemID ); + CDmeParticleFunction *pFunction = GetElementKeyValue<CDmeParticleFunction>( kv, "particleFunction" ); + if ( pFunction == pFind ) + { + m_pFunctionList->AddSelectedItem( nItemID ); + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Add/remove functions +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::OnParticleChildPicked( KeyValues *pKeyValues ) +{ + CDmeParticleSystemDefinition *pParticleSystem = GetElementKeyValue<CDmeParticleSystemDefinition>( pKeyValues, "particleSystem" ); + if ( !pParticleSystem || ( m_FuncType != FUNCTION_CHILDREN ) ) + return; + + CDmeParticleFunction *pFunction; + { + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Add Particle System Child", "Add Particle System Child" ); + pFunction = m_hParticleSystem->AddChild( pParticleSystem ); + } + PostActionSignal( new KeyValues( "ParticleSystemModified" ) ); + SelectParticleFunction( pFunction ); +} + +void CParticleFunctionBrowser::OnParticleFunctionPicked( KeyValues *pKeyValues ) +{ + if ( m_FuncType == FUNCTION_CHILDREN ) + return; + + const char *pParticleFuncName = pKeyValues->GetString( "name" ); + CDmeParticleFunction *pFunction; + { + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Add Particle Function", "Add Particle Function" ); + pFunction = m_hParticleSystem->AddOperator( m_FuncType, pParticleFuncName ); + } + PostActionSignal( new KeyValues( "ParticleSystemModified" ) ); + SelectParticleFunction( pFunction ); +} + +void CParticleFunctionBrowser::OnAdd( ) +{ + if ( m_FuncType != FUNCTION_CHILDREN ) + { + CParticleFunctionPickerFrame *pPicker = new CParticleFunctionPickerFrame( this, "Select Particle Function" ); + pPicker->DoModal( m_hParticleSystem, m_FuncType, NULL ); + } + else + { + CParticleChildrenPickerFrame *pPicker = new CParticleChildrenPickerFrame( this, "Select Child Particle Systems", m_pQuery ); + pPicker->DoModal( m_hParticleSystem, NULL ); + } +} + +void CParticleFunctionBrowser::OnRemove( ) +{ + int iSel = m_pFunctionList->GetSelectedItem( 0 ); + int nRow = m_pFunctionList->GetItemCurrentRow( iSel ) - 1; + { + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Remove Particle Function", "Remove Particle Function" ); + + // + // Build a list of objects to delete. + // + CUtlVector< CDmeParticleFunction* > itemsToDelete; + int nCount = m_pFunctionList->GetSelectedItemsCount(); + for (int i = 0; i < nCount; i++) + { + CDmeParticleFunction *pParticleFunction = GetSelectedFunction( i ); + if ( pParticleFunction ) + { + itemsToDelete.AddToTail( pParticleFunction ); + } + } + + nCount = itemsToDelete.Count(); + for ( int i = 0; i < nCount; ++i ) + { + m_hParticleSystem->RemoveFunction( m_FuncType, itemsToDelete[i] ); + } + } + + PostActionSignal( new KeyValues( "ParticleSystemModified" ) ); + + // Update the list box selection. + if ( m_pFunctionList->GetItemCount() > 0 ) + { + if ( nRow < 0 ) + { + nRow = 0; + } + else if ( nRow >= m_pFunctionList->GetItemCount() ) + { + nRow = m_pFunctionList->GetItemCount() - 1; + } + + iSel = m_pFunctionList->GetItemIDFromRow( nRow ); + m_pFunctionList->SetSingleSelectedItem( iSel ); + } + else + { + m_pFunctionList->ClearSelectedItems(); + } +} + + +//----------------------------------------------------------------------------- +// Rename a particle function +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::OnInputCompleted( KeyValues *pKeyValues ) +{ + const char *pName = pKeyValues->GetString( "text" ); + CDmeParticleFunction *pFunction = GetSelectedFunction(); + { + CUndoScopeGuard guard( 0, NOTIFY_SETDIRTYFLAG, "Rename Particle Function", "Rename Particle Function" ); + pFunction->SetName( pName ); + } + PostActionSignal( new KeyValues( "ParticleSystemModified" ) ); +} + + +//----------------------------------------------------------------------------- +// Rename a particle function +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::OnRename() +{ + int nSelectedItemCount = m_pFunctionList->GetSelectedItemsCount(); + if ( nSelectedItemCount != 1 ) + return; + + vgui::InputDialog *pInput = new vgui::InputDialog( this, "Rename Particle Function", "Enter new name of particle function" ); + pInput->SetMultiline( false ); + pInput->DoModal( ); +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::RefreshParticleFunctionProperties( ) +{ + if ( IsVisible() ) + { + CDmeParticleFunction *pFunction = GetSelectedFunction(); + if ( pFunction ) + { + m_pParticleFunctionProperties->SetTypeDictionary( pFunction->GetEditorTypeDictionary() ); + } + m_pParticleFunctionProperties->SetDmeElement( pFunction ); + + // Notify the outside world so we can get helpers to render correctly in the preview + KeyValues *pMessage = new KeyValues( "ParticleFunctionSelChanged" ); + SetElementKeyValue( pMessage, "function", pFunction ); + PostActionSignal( pMessage ); + } +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pFunctionList ) + { + RefreshParticleFunctionProperties(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when a list panel's selection changes +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::OnItemDeselected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pFunctionList ) + { + RefreshParticleFunctionProperties(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called to open a context-sensitive menu for a particular menu item +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::OnOpenContextMenu( KeyValues *kv ) +{ + CleanupContextMenu(); + if ( !m_hParticleSystem.Get() ) + return; + + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel != m_pFunctionList ) + return; + + int nSelectedItemCount = m_pFunctionList->GetSelectedItemsCount(); + m_hContextMenu = new vgui::Menu( this, "ActionMenu" ); + m_hContextMenu->AddMenuItem( "#ParticleFunctionBrowser_Add", new KeyValues( "Add" ), this ); + if ( nSelectedItemCount >= 1 ) + { + m_hContextMenu->AddMenuItem( "#ParticleFunctionBrowser_Remove", new KeyValues( "Remove" ), this ); + } + if ( nSelectedItemCount == 1 ) + { + m_hContextMenu->AddMenuItem( "#ParticleFunctionBrowser_Rename", new KeyValues( "Rename" ), this ); + m_hContextMenu->AddSeparator(); + m_hContextMenu->AddMenuItem( "#ParticleFunctionBrowser_MoveUp", new KeyValues( "MoveUp" ), this ); + m_hContextMenu->AddMenuItem( "#ParticleFunctionBrowser_MoveDown", new KeyValues( "MoveDown" ), this ); + } + + vgui::Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CParticleFunctionBrowser::OnKeyCodeTyped( vgui::KeyCode code ) +{ + if ( code == KEY_DELETE ) + { + OnRemove(); + } + else + { + BaseClass::OnKeyCodeTyped( code ); + } +} + + + +//----------------------------------------------------------------------------- +// Strings +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CParticleSystemPropertiesPanel::CParticleSystemPropertiesPanel( IParticleSystemPropertiesPanelQuery *pQuery, vgui::Panel* pParent ) + : BaseClass( pParent, "ParticleSystemPropertiesPanel" ), m_pQuery( pQuery ) +{ + SetKeyBoardInputEnabled( true ); + + m_pSplitter = new vgui::Splitter( this, "Splitter", vgui::SPLITTER_MODE_VERTICAL, 1 ); + vgui::Panel *pSplitterLeftSide = m_pSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pSplitter->GetChild( 1 ); + + m_pFunctionBrowserArea = new vgui::EditablePanel( pSplitterLeftSide, "FunctionBrowserArea" ); + m_pFunctionTypeCombo = new vgui::ComboBox( pSplitterLeftSide, "FunctionTypeCombo", PARTICLE_FUNCTION_COUNT+1, false ); + m_pFunctionTypeCombo->AddItem( "Properties", new KeyValues( "choice", "index", -1 ) ); + m_pFunctionTypeCombo->AddActionSignalTarget( this ); + + m_pParticleFunctionProperties = new CDmeElementPanel( pSplitterRightSide, "FunctionProperties" ); + m_pParticleFunctionProperties->SetAutoResize( vgui::Panel::PIN_TOPLEFT, vgui::Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -6, -6 ); + m_pParticleFunctionProperties->AddActionSignalTarget( this ); + + for ( int i = 0; i < PARTICLE_FUNCTION_COUNT; ++i ) + { + const char *pTypeName = GetParticleFunctionTypeName( (ParticleFunctionType_t)i ); + + m_pFunctionTypeCombo->AddItem( pTypeName, new KeyValues( "choice", "index", i ) ); + m_pParticleFunctionBrowser[i] = new CParticleFunctionBrowser( m_pFunctionBrowserArea, "FunctionBrowser", (ParticleFunctionType_t)i, m_pQuery ); + m_pParticleFunctionBrowser[i]->SetParticleFunctionProperties( m_pParticleFunctionProperties ); + m_pParticleFunctionBrowser[i]->AddActionSignalTarget( this ); + } + + m_pFunctionTypeCombo->ActivateItemByRow( 0 ); + LoadControlSettings( "resource/particlesystempropertiespanel.res" ); +} + + +//----------------------------------------------------------------------------- +// Sets the particle system to look at +//----------------------------------------------------------------------------- +void CParticleSystemPropertiesPanel::SetParticleSystem( CDmeParticleSystemDefinition *pParticleSystem ) +{ + m_hParticleSystem = pParticleSystem; + for ( int i = 0; i < PARTICLE_FUNCTION_COUNT; ++i ) + { + m_pParticleFunctionBrowser[i]->SetParticleSystem( pParticleSystem ); + } + + KeyValues *pKeyValues = m_pFunctionTypeCombo->GetActiveItemUserData(); + int nPage = pKeyValues->GetInt( "index" ); + if ( nPage < 0 ) + { + if ( m_hParticleSystem.Get() ) + { + m_pParticleFunctionProperties->SetTypeDictionary( m_hParticleSystem->GetEditorTypeDictionary() ); + } + m_pParticleFunctionProperties->SetDmeElement( m_hParticleSystem ); + } +} + + +//----------------------------------------------------------------------------- +// Called when something changes in the dmeelement panel +//----------------------------------------------------------------------------- +void CParticleSystemPropertiesPanel::OnDmeElementChanged( KeyValues *pKeyValues ) +{ + int nNotifyFlags = pKeyValues->GetInt( "notifyFlags" ); + OnParticleSystemModified(); + + if ( nNotifyFlags & ( NOTIFY_CHANGE_TOPOLOGICAL | NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE ) ) + { + for ( int i = 0; i < PARTICLE_FUNCTION_COUNT; ++i ) + { + m_pParticleFunctionBrowser[i]->RefreshParticleFunctionList(); + } + } + PostActionSignal( new KeyValues( "ParticleSystemModified" ) ); +} + +void CParticleSystemPropertiesPanel::OnParticleSystemModifiedInternal() +{ + OnParticleSystemModified(); + Refresh( false ); + PostActionSignal( new KeyValues( "ParticleSystemModified" ) ); +} + + +void CParticleSystemPropertiesPanel::OnParticleFunctionSelChanged( KeyValues *pParams ) +{ + // Notify the outside world so we can get helpers to render correctly in the preview + CDmeParticleFunction *pFunction = GetElementKeyValue<CDmeParticleFunction>( pParams, "function" ); + KeyValues *pMessage = new KeyValues( "ParticleFunctionSelChanged" ); + SetElementKeyValue( pMessage, "function", pFunction ); + PostActionSignal( pMessage ); +} + + +//----------------------------------------------------------------------------- +// Refreshes display +//----------------------------------------------------------------------------- +void CParticleSystemPropertiesPanel::Refresh( bool bValuesOnly ) +{ + for ( int i = 0; i < PARTICLE_FUNCTION_COUNT; ++i ) + { + m_pParticleFunctionBrowser[i]->RefreshParticleFunctionList(); + } + m_pParticleFunctionProperties->Refresh( bValuesOnly ? CElementPropertiesTreeInternal::REFRESH_VALUES_ONLY : + CElementPropertiesTreeInternal::REFRESH_TREE_VIEW ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when a page is shown +//----------------------------------------------------------------------------- +void CParticleSystemPropertiesPanel::OnTextChanged( ) +{ + for ( int i = 0; i < PARTICLE_FUNCTION_COUNT; ++i ) + { + m_pParticleFunctionBrowser[i]->SetVisible( false ); + } + + KeyValues *pKeyValues = m_pFunctionTypeCombo->GetActiveItemUserData(); + int nPage = pKeyValues->GetInt( "index" ); + if ( nPage < 0 ) + { + if ( m_hParticleSystem.Get() ) + { + m_pParticleFunctionProperties->SetTypeDictionary( m_hParticleSystem->GetEditorTypeDictionary() ); + } + m_pParticleFunctionProperties->SetDmeElement( m_hParticleSystem ); + return; + } + + m_pParticleFunctionBrowser[nPage]->SetVisible( true ); + m_pParticleFunctionBrowser[nPage]->SelectDefaultFunction(); + m_pParticleFunctionBrowser[nPage]->RefreshParticleFunctionProperties(); +} + + + + +//----------------------------------------------------------------------------- +// A little panel used as a DmePanel for particle systems +//----------------------------------------------------------------------------- +class CParticleSystemDmePanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CParticleSystemDmePanel, vgui::EditablePanel ); + +public: + // constructor, destructor + CParticleSystemDmePanel( vgui::Panel *pParent, const char *pName ); + virtual ~CParticleSystemDmePanel(); + + // Set the material to draw + void SetDmeElement( CDmeParticleSystemDefinition *pDef ); + +private: + MESSAGE_FUNC_INT( OnElementChangedExternally, "ElementChangedExternally", valuesOnly ); + MESSAGE_FUNC( OnParticleSystemModified, "ParticleSystemModified" ); + MESSAGE_FUNC_PARAMS( OnParticleFunctionSelChanged, "ParticleFunctionSelChanged", params ); + + vgui::Splitter *m_Splitter; + CParticleSystemPropertiesPanel *m_pProperties; + CParticleSystemPreviewPanel *m_pPreview; +}; + + +//----------------------------------------------------------------------------- +// Dme panel connection +//----------------------------------------------------------------------------- +IMPLEMENT_DMEPANEL_FACTORY( CParticleSystemDmePanel, DmeParticleSystemDefinition, "DmeParticleSystemDefinitionEditor", "Particle System Editor", true ); + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CParticleSystemDmePanel::CParticleSystemDmePanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_Splitter = new vgui::Splitter( this, "Splitter", SPLITTER_MODE_HORIZONTAL, 1 ); + vgui::Panel *pSplitterTopSide = m_Splitter->GetChild( 0 ); + vgui::Panel *pSplitterBottomSide = m_Splitter->GetChild( 1 ); + m_Splitter->SetAutoResize( PIN_TOPLEFT, AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); + + m_pProperties = new CParticleSystemPropertiesPanel( NULL, pSplitterBottomSide ); + m_pProperties->AddActionSignalTarget( this ); + m_pProperties->SetAutoResize( PIN_TOPLEFT, AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); + + m_pPreview = new CParticleSystemPreviewPanel( pSplitterTopSide, "Preview" ); + m_pPreview->SetAutoResize( PIN_TOPLEFT, AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); +} + +CParticleSystemDmePanel::~CParticleSystemDmePanel() +{ +} + + +//----------------------------------------------------------------------------- +// Layout +//----------------------------------------------------------------------------- +void CParticleSystemDmePanel::SetDmeElement( CDmeParticleSystemDefinition *pDef ) +{ + m_pProperties->SetParticleSystem( pDef ); + m_pPreview->SetParticleSystem( pDef ); +} + + +//----------------------------------------------------------------------------- +// Particle system modified externally to the editor +//----------------------------------------------------------------------------- +void CParticleSystemDmePanel::OnElementChangedExternally( int valuesOnly ) +{ + m_pProperties->Refresh( valuesOnly != 0 ); +} + + +//----------------------------------------------------------------------------- +// Called when the selected particle function changes +//----------------------------------------------------------------------------- +void CParticleSystemDmePanel::OnParticleFunctionSelChanged( KeyValues *pParams ) +{ + CDmeParticleFunction *pFunction = GetElementKeyValue<CDmeParticleFunction>( pParams, "function" ); + m_pPreview->SetParticleFunction( pFunction ); +} + + +//----------------------------------------------------------------------------- +// Communicate to the owning DmePanel +//----------------------------------------------------------------------------- +void CParticleSystemDmePanel::OnParticleSystemModified() +{ + PostActionSignal( new KeyValues( "DmeElementChanged" ) ); +} diff --git a/vgui2/dme_controls/presetpicker.cpp b/vgui2/dme_controls/presetpicker.cpp new file mode 100644 index 0000000..5e85211 --- /dev/null +++ b/vgui2/dme_controls/presetpicker.cpp @@ -0,0 +1,194 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Dialog allowing users to select presets from within a preset group +// +//===========================================================================// + +#include "dme_controls/presetpicker.h" +#include "tier1/KeyValues.h" +#include "tier1/utlbuffer.h" +#include "vgui/IVGui.h" +#include "vgui_controls/button.h" +#include "vgui_controls/listpanel.h" +#include "vgui_controls/splitter.h" +#include "vgui_controls/messagebox.h" +#include "movieobjects/dmeanimationset.h" +#include "datamodel/dmelement.h" +#include "matsys_controls/picker.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; + +static int __cdecl PresetNameSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString( "name" ); + const char *string2 = item2.kv->GetString( "name" ); + return Q_stricmp( string1, string2 ); +} + +CPresetPickerFrame::CPresetPickerFrame( vgui::Panel *pParent, const char *pTitle, bool bAllowMultiSelect ) : + BaseClass( pParent, "PresetPickerFrame" ) +{ + SetDeleteSelfOnClose( true ); + m_pContextKeyValues = NULL; + + m_pPresetList = new vgui::ListPanel( this, "PresetList" ); + m_pPresetList->AddColumnHeader( 0, "name", "Preset Name", 52, 0 ); + m_pPresetList->SetSelectIndividualCells( false ); + m_pPresetList->SetMultiselectEnabled( bAllowMultiSelect ); + m_pPresetList->SetEmptyListText( "No presets" ); + m_pPresetList->AddActionSignalTarget( this ); + m_pPresetList->SetSortFunc( 0, PresetNameSortFunc ); + m_pPresetList->SetSortColumn( 0 ); + + m_pOpenButton = new vgui::Button( this, "OkButton", "#MessageBox_OK", this, "Ok" ); + m_pCancelButton = new vgui::Button( this, "CancelButton", "#MessageBox_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/presetpicker.res" ); + + SetTitle( pTitle, false ); +} + +CPresetPickerFrame::~CPresetPickerFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Refreshes the list of presets +//----------------------------------------------------------------------------- +void CPresetPickerFrame::RefreshPresetList( CDmElement *pPresetGroup, bool bSelectAll ) +{ + m_pPresetList->RemoveAll(); + + const CDmrElementArray< CDmePreset > presets( pPresetGroup, "presets" ); + if ( !presets.IsValid() ) + return; + + int nCount = presets.Count(); + if ( nCount == 0 ) + return; + + for ( int i = 0; i < nCount; ++i ) + { + CDmePreset *pPreset = presets[i]; + + const char *pName = pPreset->GetName(); + if ( !pName || !pName[0] ) + { + pName = "<no name>"; + } + + KeyValues *kv = new KeyValues( "node" ); + kv->SetString( "name", pName ); + SetElementKeyValue( kv, "preset", pPreset ); + + int nItemID = m_pPresetList->AddItem( kv, 0, false, false ); + if ( bSelectAll ) + { + m_pPresetList->AddSelectedItem( nItemID ); + } + } + m_pPresetList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CPresetPickerFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Sets the current scene + animation list +//----------------------------------------------------------------------------- +void CPresetPickerFrame::DoModal( CDmElement *pPresetGroup, bool bSelectAll, KeyValues *pContextKeyValues ) +{ + CleanUpMessage(); + RefreshPresetList( pPresetGroup, bSelectAll ); + m_pContextKeyValues = pContextKeyValues; + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CPresetPickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Ok" ) ) + { + int nSelectedItemCount = m_pPresetList->GetSelectedItemsCount(); + if ( nSelectedItemCount == 0 ) + return; + + KeyValues *pActionKeys = new KeyValues( "PresetPicked" ); + + if ( m_pPresetList->IsMultiselectEnabled() ) + { + pActionKeys->SetInt( "count", nSelectedItemCount ); + + // Adds them in selection order + for ( int i = 0; i < nSelectedItemCount; ++i ) + { + char pBuf[32]; + Q_snprintf( pBuf, sizeof(pBuf), "%d", i ); + + int nItemID = m_pPresetList->GetSelectedItem( i ); + KeyValues *pKeyValues = m_pPresetList->GetItem( nItemID ); + CDmePreset *pPreset = GetElementKeyValue<CDmePreset>( pKeyValues, "preset" ); + + SetElementKeyValue( pActionKeys, pBuf, pPreset ); + } + } + else + { + int nItemID = m_pPresetList->GetSelectedItem( 0 ); + KeyValues *pKeyValues = m_pPresetList->GetItem( nItemID ); + CDmePreset *pPreset = GetElementKeyValue<CDmePreset>( pKeyValues, "preset" ); + SetElementKeyValue( pActionKeys, "preset", pPreset ); + } + + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + + // This prevents them from being deleted later + m_pContextKeyValues = NULL; + } + + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + KeyValues *pActionKeys = new KeyValues( "PresetPickCancelled" ); + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + + // This prevents them from being deleted later + m_pContextKeyValues = NULL; + } + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + diff --git a/vgui2/dme_controls/soundpicker.cpp b/vgui2/dme_controls/soundpicker.cpp new file mode 100644 index 0000000..19dc478 --- /dev/null +++ b/vgui2/dme_controls/soundpicker.cpp @@ -0,0 +1,602 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include <windows.h> +#undef PropertySheet + +#include "filesystem.h" +#include "dme_controls/soundpicker.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/PropertyPage.h" +#include "dme_controls/filtercombobox.h" +#include "vgui/ISurface.h" +#include "vgui/iinput.h" +#include "dme_controls/dmecontrols.h" +#include "soundemittersystem/isoundemittersystembase.h" +#include "mathlib/mathlib.h" + +// FIXME: Move sound code out of the engine + into a library! +#include "toolframework/ienginetool.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Sound Picker +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Sort by sound name +//----------------------------------------------------------------------------- +static int __cdecl GameSoundSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + bool bRoot1 = item1.kv->GetInt("root") != 0; + bool bRoot2 = item2.kv->GetInt("root") != 0; + if ( bRoot1 != bRoot2 ) + return bRoot1 ? -1 : 1; + const char *string1 = item1.kv->GetString("gamesound"); + const char *string2 = item2.kv->GetString("gamesound"); + return Q_stricmp( string1, string2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSoundPicker::CSoundPicker( vgui::Panel *pParent, int nFlags ) : + BaseClass( pParent, "Sound Files", "wav", "sound", "wavName" ) +{ + m_nSoundSuppressionCount = 0; + m_nPlayingSound = 0; + + // Connection problem if this failed + Assert( SoundEmitterSystem() ); + + m_pViewsSheet = new vgui::PropertySheet( this, "ViewsSheet" ); + m_pViewsSheet->AddActionSignalTarget( this ); + + // game sounds + m_pGameSoundPage = NULL; + m_pGameSoundList = NULL; + if ( nFlags & PICK_GAMESOUNDS ) + { + m_pGameSoundPage = new PropertyPage( m_pViewsSheet, "GameSoundPage" ); + m_pGameSoundList = new ListPanel( m_pGameSoundPage, "GameSoundsList" ); + m_pGameSoundList->AddColumnHeader( 0, "GameSound", "Game Sound", 52, 0 ); + m_pGameSoundList->AddActionSignalTarget( this ); + m_pGameSoundList->SetSelectIndividualCells( true ); + m_pGameSoundList->SetEmptyListText("No game sounds"); + m_pGameSoundList->SetDragEnabled( true ); + m_pGameSoundList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); + m_pGameSoundList->SetSortFunc( 0, GameSoundSortFunc ); + m_pGameSoundList->SetSortColumn( 0 ); + m_pGameSoundList->SetMultiselectEnabled( ( nFlags & ALLOW_MULTISELECT ) != 0 ); + + // filter selection + m_pGameSoundFilter = new TextEntry( m_pGameSoundPage, "GameSoundFilter" ); + m_pGameSoundFilter->AddActionSignalTarget( this ); + + m_pGameSoundPage->LoadControlSettings( "resource/soundpickergamesoundpage.res" ); + + m_pViewsSheet->AddPage( m_pGameSoundPage, "Game Sounds" ); + } + + // wav files + m_pWavPage = NULL; + if ( nFlags & PICK_WAVFILES ) + { + m_pWavPage = new PropertyPage( m_pViewsSheet, "WavPage" ); + bool bAllowMultiselect = ( nFlags & ALLOW_MULTISELECT ) != 0; + CreateStandardControls( m_pWavPage, bAllowMultiselect ); + AddExtension( "mp3" ); + + m_pWavPage->LoadControlSettings( "resource/soundpickerwavpage.res" ); + m_pViewsSheet->AddPage( m_pWavPage, "WAVs" ); + } + + LoadControlSettings( "resource/soundpicker.res" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CSoundPicker::~CSoundPicker() +{ + StopSoundPreview(); +} + + +//----------------------------------------------------------------------------- +// Purpose: called to open +//----------------------------------------------------------------------------- +void CSoundPicker::Activate() +{ + BaseClass::Activate(); + if ( m_pGameSoundPage ) + { + BuildGameSoundList(); + } +} + + +//----------------------------------------------------------------------------- +// Sets the current sound choice +//----------------------------------------------------------------------------- +void CSoundPicker::SetSelectedSound( PickType_t type, const char *pSoundName ) +{ + if ( type == PICK_NONE || !pSoundName ) + return; + + if ( m_pGameSoundPage && ( type == PICK_GAMESOUNDS ) ) + { + m_pViewsSheet->SetActivePage( m_pGameSoundPage ); + m_pGameSoundFilter->SetText( pSoundName ); + } + + if ( m_pWavPage && ( type == PICK_WAVFILES ) ) + { + m_pViewsSheet->SetActivePage( m_pWavPage ); + SetInitialSelection( pSoundName ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSoundPicker::OnKeyCodePressed( KeyCode code ) +{ + if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) ) + { + if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN )) + { + KeyValues *pMsg = new KeyValues( "KeyCodePressed", "code", code ); + vgui::ipanel()->SendMessage( m_pGameSoundList->GetVPanel(), pMsg, GetVPanel() ); + pMsg->deleteThis(); + return; + } + } + + BaseClass::OnKeyCodePressed( code ); +} + + +//----------------------------------------------------------------------------- +// Purpose: builds the gamesound list +//----------------------------------------------------------------------------- +bool CSoundPicker::IsGameSoundVisible( int hGameSound ) +{ + const char *pSoundName = SoundEmitterSystem()->GetSoundName( hGameSound ); + return ( !m_GameSoundFilter.Length() || Q_stristr( pSoundName, m_GameSoundFilter.Get() ) ); +} + + +//----------------------------------------------------------------------------- +// Updates the column header in the chooser +//----------------------------------------------------------------------------- +void CSoundPicker::UpdateGameSoundColumnHeader( int nMatchCount, int nTotalCount ) +{ + char pColumnTitle[512]; + Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)", + "Game Sound", nMatchCount, nTotalCount ); + m_pGameSoundList->SetColumnHeaderText( 0, pColumnTitle ); +} + + +//----------------------------------------------------------------------------- +// Purpose: builds the gamesound list +//----------------------------------------------------------------------------- +void CSoundPicker::BuildGameSoundList() +{ + if ( !m_pGameSoundList ) + return; + + m_pGameSoundList->RemoveAll(); + + int nTotalCount = 0; + int i = SoundEmitterSystem()->First(); + while ( i != SoundEmitterSystem()->InvalidIndex() ) + { + const char *pSoundName = SoundEmitterSystem()->GetSoundName( i ); + + bool bInRoot = !strchr( pSoundName, '\\' ) && !strchr( pSoundName, '/' ); + KeyValues *kv = new KeyValues( "node", "gamesound", pSoundName ); + kv->SetInt( "gameSoundHandle", i ); + kv->SetInt( "root", bInRoot ); + + int nItemID = m_pGameSoundList->AddItem( kv, 0, false, false ); + m_pGameSoundList->SetItemVisible( nItemID, IsGameSoundVisible( i ) ); + KeyValues *pDrag = new KeyValues( "drag", "text", pSoundName ); + pDrag->SetString( "texttype", "gamesoundName" ); + m_pGameSoundList->SetItemDragData( nItemID, pDrag ); + ++nTotalCount; + + i = SoundEmitterSystem()->Next( i ); + } + + m_pGameSoundList->SortList(); + if ( m_pGameSoundList->GetItemCount() > 0 ) + { + int nItemID = m_pGameSoundList->GetItemIDFromRow( 0 ); + + // This prevents the refreshing of the sound list from playing the sound + ++m_nSoundSuppressionCount; + m_pGameSoundList->SetSelectedCell( nItemID, 0 ); + } + + UpdateGameSoundColumnHeader( nTotalCount, nTotalCount ); +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes the gamesound list +//----------------------------------------------------------------------------- +void CSoundPicker::RefreshGameSoundList() +{ + if ( !m_pGameSoundList ) + return; + + // Check the filter matches + int nMatchingGameSounds = 0; + int nTotalCount = 0; + for ( int nItemID = m_pGameSoundList->FirstItem(); nItemID != m_pGameSoundList->InvalidItemID(); nItemID = m_pGameSoundList->NextItem( nItemID ) ) + { + KeyValues *kv = m_pGameSoundList->GetItem( nItemID ); + int hGameSound = kv->GetInt( "gameSoundHandle", SoundEmitterSystem()->InvalidIndex() ); + if ( hGameSound == SoundEmitterSystem()->InvalidIndex() ) + continue; + bool bIsVisible = IsGameSoundVisible( hGameSound ); + m_pGameSoundList->SetItemVisible( nItemID, bIsVisible ); + if ( bIsVisible ) + { + ++nMatchingGameSounds; + } + ++nTotalCount; + } + + UpdateGameSoundColumnHeader( nMatchingGameSounds, nTotalCount ); + + if ( ( m_pGameSoundList->GetSelectedItemsCount() == 0 ) && ( m_pGameSoundList->GetItemCount() > 0 ) ) + { + int nItemID = m_pGameSoundList->GetItemIDFromRow( 0 ); + // This prevents the refreshing of the sound list from playing the sound + ++m_nSoundSuppressionCount; + m_pGameSoundList->SetSelectedCell( nItemID, 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Update filter when text changes +//----------------------------------------------------------------------------- +void CSoundPicker::OnGameSoundFilterTextChanged( ) +{ + int nLength = m_pGameSoundFilter->GetTextLength(); + m_GameSoundFilter.SetLength( nLength ); + if ( nLength > 0 ) + { + m_pGameSoundFilter->GetText( m_GameSoundFilter.GetForModify(), nLength+1 ); + } + RefreshGameSoundList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on filter changing +//----------------------------------------------------------------------------- +void CSoundPicker::OnTextChanged( KeyValues *pKeyValues ) +{ + vgui::Panel *pSource = (vgui::Panel*)pKeyValues->GetPtr( "panel" ); + if ( pSource == m_pGameSoundFilter ) + { + OnGameSoundFilterTextChanged(); + return; + } + + BaseClass::OnTextChanged( pKeyValues ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when a page is shown +//----------------------------------------------------------------------------- +void CSoundPicker::RequestGameSoundFilterFocus( ) +{ + m_pGameSoundFilter->SelectAllOnFirstFocus( true ); + m_pGameSoundFilter->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when a page is shown +//----------------------------------------------------------------------------- +void CSoundPicker::OnPageChanged( ) +{ + StopSoundPreview(); + if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) ) + { + RequestGameSoundFilterFocus(); + } + if ( m_pWavPage && ( m_pViewsSheet->GetActivePage() == m_pWavPage ) ) + { + RequestFilterFocus(); + } +} + + +//----------------------------------------------------------------------------- +// Stop sound preview +//----------------------------------------------------------------------------- +void CSoundPicker::StopSoundPreview( ) +{ + if ( m_nPlayingSound != 0 ) + { + EngineTool()->StopSoundByGuid( m_nPlayingSound ); + m_nPlayingSound = 0; + } +} + + +//----------------------------------------------------------------------------- +// Plays a gamesound +//----------------------------------------------------------------------------- +void CSoundPicker::PlayGameSound( const char *pSoundName ) +{ + StopSoundPreview(); + + CSoundParameters params; + if ( SoundEmitterSystem()->GetParametersForSound( pSoundName, params, GENDER_NONE ) ) + { + m_nPlayingSound = EngineTool()->StartSound( 0, true, -1, CHAN_STATIC, params.soundname, + params.volume, params.soundlevel, vec3_origin, vec3_origin, 0, + params.pitch, false, params.delay_msec / 1000.0f ); + } +} + + +//----------------------------------------------------------------------------- +// Plays a wav file +//----------------------------------------------------------------------------- +void CSoundPicker::PlayWavSound( const char *pSoundName ) +{ + StopSoundPreview(); + m_nPlayingSound = EngineTool()->StartSound( 0, true, -1, CHAN_STATIC, pSoundName, + VOL_NORM, SNDLVL_NONE, vec3_origin, vec3_origin, 0, PITCH_NORM, false, 0 ); +} + + +//----------------------------------------------------------------------------- +// Don't play a sound when the next selection is a default selection +//----------------------------------------------------------------------------- +void CSoundPicker::OnNextSelectionIsDefault() +{ + ++m_nSoundSuppressionCount; +} + + +//----------------------------------------------------------------------------- +// Derived classes have this called when the previewed asset changes +//----------------------------------------------------------------------------- +void CSoundPicker::OnSelectedAssetPicked( const char *pAssetName ) +{ + bool bPlaySounds = true; + if ( m_nSoundSuppressionCount > 0 ) + { + --m_nSoundSuppressionCount; + bPlaySounds = false; + } + + if ( pAssetName && bPlaySounds ) + { + PlayWavSound( pAssetName ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on text changing +//----------------------------------------------------------------------------- +void CSoundPicker::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( m_pGameSoundList && (pPanel == m_pGameSoundList ) ) + { + bool bPlaySounds = true; + if ( m_nSoundSuppressionCount > 0 ) + { + --m_nSoundSuppressionCount; + bPlaySounds = false; + } + + const char *pGameSoundName = GetSelectedSoundName(); + if ( pGameSoundName && bPlaySounds ) + { + int len = V_strlen( pGameSoundName ); + char *soundname = ( char* )stackalloc( len + 2 ); + soundname[ 0 ] = '#'; // mark sound to bypass the dsp + V_strncpy( soundname + 1, pGameSoundName, len + 1 ); + + PlayGameSound( soundname ); + } + return; + } + + BaseClass::OnItemSelected( kv ); +} + + +//----------------------------------------------------------------------------- +// Gets the selected sound type +//----------------------------------------------------------------------------- +CSoundPicker::PickType_t CSoundPicker::GetSelectedSoundType( ) +{ + if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) ) + return PICK_GAMESOUNDS; + if ( m_pWavPage && ( m_pViewsSheet->GetActivePage() == m_pWavPage ) ) + return PICK_WAVFILES; + return PICK_NONE; +} + + +//----------------------------------------------------------------------------- +// Returns the selected sound count +//----------------------------------------------------------------------------- +int CSoundPicker::GetSelectedSoundCount() +{ + if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) ) + return m_pGameSoundList->GetSelectedItemsCount(); + + if ( m_pWavPage && ( m_pViewsSheet->GetActivePage() == m_pWavPage ) ) + return GetSelectedAssetCount(); + + return 0; +} + + +//----------------------------------------------------------------------------- +// Returns the selected sound +//----------------------------------------------------------------------------- +const char *CSoundPicker::GetSelectedSoundName( int nSelectionIndex ) +{ + if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) ) + { + int nCount = m_pGameSoundList->GetSelectedItemsCount(); + if ( nCount == 0 ) + return NULL; + + if ( nSelectionIndex < 0 ) + { + nSelectionIndex = nCount - 1; + } + int nIndex = m_pGameSoundList->GetSelectedItem( nSelectionIndex ); + if ( nIndex >= 0 ) + { + KeyValues *pkv = m_pGameSoundList->GetItem( nIndex ); + return pkv->GetString( "gamesound", NULL ); + } + return NULL; + } + + if ( m_pWavPage && ( m_pViewsSheet->GetActivePage() == m_pWavPage ) ) + return GetSelectedAsset( nSelectionIndex ); + + return NULL; +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CSoundPickerFrame::CSoundPickerFrame( vgui::Panel *pParent, const char *pTitle, int nFlags ) : + BaseClass( pParent ) +{ + SetAssetPicker( new CSoundPicker( this, nFlags ) ); + LoadControlSettingsAndUserConfig( "resource/soundpickerframe.res" ); + SetTitle( pTitle, false ); +} + +CSoundPickerFrame::~CSoundPickerFrame() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void CSoundPickerFrame::DoModal( CSoundPicker::PickType_t initialType, const char *pInitialValue, KeyValues *pContextKeyValues ) +{ + vgui::surface()->SetCursor( dc_hourglass ); + CSoundPicker *pPicker = static_cast <CSoundPicker*>( GetAssetPicker() ); + if ( initialType != CSoundPicker::PICK_NONE && pInitialValue ) + { + pPicker->SetSelectedSound( initialType, pInitialValue ); + } + BaseClass::DoModal( pContextKeyValues ); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CSoundPickerFrame::OnCommand( const char *pCommand ) +{ + CSoundPicker *pPicker = static_cast <CSoundPicker*>( GetAssetPicker() ); + if ( !Q_stricmp( pCommand, "Open" ) ) + { + CSoundPicker::PickType_t type = pPicker->GetSelectedSoundType( ); + if (( type == CSoundPicker::PICK_GAMESOUNDS ) || ( type == CSoundPicker::PICK_WAVFILES )) + { + const char *pSoundName = pPicker->GetSelectedSoundName(); + + int len = V_strlen( pSoundName ); + char *soundname = ( char* )stackalloc( len + 2 ); + soundname[ 0 ] = '#'; // mark sound to bypass the dsp + V_strncpy( soundname + 1, pSoundName, len + 1 ); + + int nSoundCount = pPicker->GetSelectedSoundCount(); + + KeyValues *pActionKeys = new KeyValues( "SoundSelected" ); + pActionKeys->SetInt( "count", nSoundCount ); + KeyValues *pSoundList = NULL; + if ( type == CSoundPicker::PICK_GAMESOUNDS ) + { + pActionKeys->SetString( "gamesound", soundname ); + if ( pPicker->IsMultiselectEnabled() ) + { + pSoundList = pActionKeys->FindKey( "gamesounds", true ); + } + } + else + { + pActionKeys->SetString( "wav", soundname ); + if ( pPicker->IsMultiselectEnabled() ) + { + pSoundList = pActionKeys->FindKey( "wavs", true ); + } + } + + if ( pSoundList ) + { + // Adds them in selection order + for ( int i = 0; i < nSoundCount; ++i ) + { + char pBuf[32]; + Q_snprintf( pBuf, sizeof(pBuf), "%d", i ); + pSoundName = pPicker->GetSelectedSoundName( i ); + + len = V_strlen( pSoundName ); + soundname = ( char* )malloc( len + 2 ); + soundname[ 0 ] = '#'; // mark sound to bypass the dsp + V_strncpy( soundname + 1, pSoundName, len + 1 ); + + pSoundList->SetString( pBuf, soundname ); + free( soundname ); + } + } + + PostMessageAndClose( pActionKeys ); + CloseModal(); + } + return; + } + + BaseClass::OnCommand( pCommand ); +} + + diff --git a/vgui2/dme_controls/soundrecordpanel.cpp b/vgui2/dme_controls/soundrecordpanel.cpp new file mode 100644 index 0000000..b2feea7 --- /dev/null +++ b/vgui2/dme_controls/soundrecordpanel.cpp @@ -0,0 +1,212 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "dme_controls/soundrecordpanel.h" +#include "filesystem.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/TextEntry.h" +#include "dme_controls/dmecontrols.h" +#include "vgui_controls/messagebox.h" +#include "soundemittersystem/isoundemittersystembase.h" +#include "vgui/IVGui.h" +#include "mathlib/mathlib.h" + +// FIXME: Move sound code out of the engine + into a library! +#include "toolframework/ienginetool.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Sound Record Dialog +// +//----------------------------------------------------------------------------- +CSoundRecordPanel::CSoundRecordPanel( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "SoundRecordPanel" ) +{ + m_bIsRecording = false; + m_nPlayingSound = 0; + SetDeleteSelfOnClose( true ); + m_pOkButton = new Button( this, "OkButton", "#FileOpenDialog_Open", this, "Ok" ); + m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" ); + m_pPlayButton = new Button( this, "PlayButton", "Play", this, "Play" ); + m_pRecordButton = new Button( this, "Record", "Record", this, "ToggleRecord" ); + m_pRecordTime = new TextEntry( this, "RecordTime" ); + m_pFileName = new TextEntry( this, "FileName" ); + + LoadControlSettingsAndUserConfig( "resource/soundrecordpanel.res" ); + + SetTitle( pTitle, false ); +} + +CSoundRecordPanel::~CSoundRecordPanel() +{ + StopSoundPreview(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void CSoundRecordPanel::DoModal( const char *pFileName ) +{ + Assert( EngineTool() ); + + char pRelativeWAVPath[MAX_PATH]; + g_pFullFileSystem->FullPathToRelativePath( pFileName, pRelativeWAVPath, sizeof(pRelativeWAVPath) ); + + // Check to see if this file is not hidden owing to search paths + bool bBadDirectory = false; + char pRelativeDir[MAX_PATH]; + Q_strncpy( pRelativeDir, pRelativeWAVPath, sizeof( pRelativeDir ) ); + Q_StripFilename( pRelativeDir ); + char pFoundFullPath[MAX_PATH]; + g_pFullFileSystem->RelativePathToFullPath( pRelativeDir, "MOD", pFoundFullPath, sizeof( pFoundFullPath ) ); + if ( StringHasPrefix( pFileName, pFoundFullPath ) ) + { + // Strip 'sound/' prefix + m_FileName = pRelativeWAVPath; + const char *pSoundName = StringAfterPrefix( pRelativeWAVPath, "sound\\" ); + if ( !pSoundName ) + { + pSoundName = pRelativeWAVPath; + bBadDirectory = true; + } + m_EngineFileName = pSoundName; + } + else + { + bBadDirectory = true; + } + + if ( bBadDirectory ) + { + char pBuf[1024]; + Q_snprintf( pBuf, sizeof(pBuf), "File %s is in a bad directory!\nAudio must be recorded into your mod's sound/ directory.\n", pFileName ); + vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Bad Save Directory!\n", pBuf, GetParent() ); + pMessageBox->DoModal( ); + return; + } + + m_pFileName->SetText( pFileName ); + m_pOkButton->SetEnabled( false ); + m_pPlayButton->SetEnabled( false ); + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// Stop sound preview +//----------------------------------------------------------------------------- +void CSoundRecordPanel::StopSoundPreview( ) +{ + if ( m_nPlayingSound != 0 ) + { + EngineTool()->StopSoundByGuid( m_nPlayingSound ); + m_nPlayingSound = 0; + } +} + + +//----------------------------------------------------------------------------- +// Plays a wav file +//----------------------------------------------------------------------------- +void CSoundRecordPanel::PlaySoundPreview( ) +{ + StopSoundPreview(); + m_nPlayingSound = EngineTool()->StartSound( 0, true, -1, CHAN_STATIC, m_EngineFileName, + VOL_NORM, SNDLVL_NONE, vec3_origin, vec3_origin, 0, PITCH_NORM, false, 0 ); +} + + +//----------------------------------------------------------------------------- +// Updates sound record time during recording +//----------------------------------------------------------------------------- +void CSoundRecordPanel::UpdateTimeRecorded() +{ + float flTime = Plat_FloatTime() - m_flRecordStartTime; + char pTimeBuf[64]; + Q_snprintf( pTimeBuf, sizeof(pTimeBuf), "%.3f", flTime ); + m_pRecordTime->SetText( pTimeBuf ); +} + + +//----------------------------------------------------------------------------- +// Updates sound record time during recording +//----------------------------------------------------------------------------- +void CSoundRecordPanel::OnTick() +{ + BaseClass::OnTick(); + + // Update the amount of time recorded + UpdateTimeRecorded(); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CSoundRecordPanel::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "ToggleRecord" ) ) + { + if ( !m_bIsRecording ) + { + StopSoundPreview(); + g_pFullFileSystem->RemoveFile( m_FileName, "MOD" ); + EngineTool()->StartRecordingVoiceToFile( m_FileName, "MOD" ); + m_pRecordButton->SetText( "Stop Recording" ); + m_pPlayButton->SetEnabled( false ); + m_pOkButton->SetEnabled( false ); + m_pCancelButton->SetEnabled( true ); + m_flRecordStartTime = Plat_FloatTime(); + vgui::ivgui()->AddTickSignal( GetVPanel(), 0 ); + } + else + { + EngineTool()->StopRecordingVoiceToFile(); + EngineTool()->ReloadSound( m_EngineFileName ); + ivgui()->RemoveTickSignal( GetVPanel() ); + UpdateTimeRecorded(); + m_pOkButton->SetEnabled( true ); + m_pCancelButton->SetEnabled( true ); + m_pPlayButton->SetEnabled( true ); + m_pRecordButton->SetText( "Record" ); + } + m_bIsRecording = !m_bIsRecording; + return; + } + + if ( !Q_stricmp( pCommand, "Play" ) ) + { + PlaySoundPreview(); + return; + } + + if ( !Q_stricmp( pCommand, "Ok" ) ) + { + PostActionSignal( new KeyValues( "SoundRecorded", "relativepath", m_EngineFileName.Get() ) ); + CloseModal(); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + g_pFullFileSystem->RemoveFile( m_FileName, "MOD" ); + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + |