summaryrefslogtreecommitdiff
path: root/vgui2/dme_controls
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vgui2/dme_controls
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'vgui2/dme_controls')
-rw-r--r--vgui2/dme_controls/AssetBuilder.cpp1725
-rw-r--r--vgui2/dme_controls/AttributeBasePickerPanel.cpp59
-rw-r--r--vgui2/dme_controls/AttributeBoolChoicePanel.cpp159
-rw-r--r--vgui2/dme_controls/AttributeColorPickerPanel.cpp170
-rw-r--r--vgui2/dme_controls/AttributeElementPanel.cpp104
-rw-r--r--vgui2/dme_controls/AttributeElementPickerPanel.cpp174
-rw-r--r--vgui2/dme_controls/AttributeFilePickerPanel.cpp73
-rw-r--r--vgui2/dme_controls/AttributeIntChoicePanel.cpp170
-rw-r--r--vgui2/dme_controls/AttributeInterpolatorChoicePanel.cpp91
-rw-r--r--vgui2/dme_controls/AttributeMDLPickerPanel.cpp62
-rw-r--r--vgui2/dme_controls/AttributeSequencePickerPanel.cpp109
-rw-r--r--vgui2/dme_controls/AttributeSoundPickerPanel.cpp85
-rw-r--r--vgui2/dme_controls/AttributeStringChoicePanel.cpp169
-rw-r--r--vgui2/dme_controls/AttributeTextEntry.cpp500
-rw-r--r--vgui2/dme_controls/AttributeTextPanel.cpp119
-rw-r--r--vgui2/dme_controls/AttributeWidgetFactory.cpp370
-rw-r--r--vgui2/dme_controls/BaseAnimSetAttributeSliderPanel.cpp1534
-rw-r--r--vgui2/dme_controls/BaseAnimSetControlGroupPanel.cpp523
-rw-r--r--vgui2/dme_controls/BaseAnimSetPresetFaderPanel.cpp1504
-rw-r--r--vgui2/dme_controls/BaseAnimationSetEditor.cpp1076
-rw-r--r--vgui2/dme_controls/BaseAttributeChoicePanel.cpp91
-rw-r--r--vgui2/dme_controls/BaseAttributeDoubleChoicePanel.cpp127
-rw-r--r--vgui2/dme_controls/BaseAttributePanel.cpp365
-rw-r--r--vgui2/dme_controls/ChannelGraphPanel.cpp368
-rw-r--r--vgui2/dme_controls/DmeSourceDCCFilePanel.cpp488
-rw-r--r--vgui2/dme_controls/DmeSourceSkinPanel.cpp143
-rw-r--r--vgui2/dme_controls/ElementPropertiesTree.cpp4496
-rw-r--r--vgui2/dme_controls/FactoryOverloads.cpp58
-rw-r--r--vgui2/dme_controls/FactoryOverloads.h49
-rw-r--r--vgui2/dme_controls/FileListManager.cpp618
-rw-r--r--vgui2/dme_controls/attributeassetpickerpanel.cpp69
-rw-r--r--vgui2/dme_controls/attributedetailtypepickerpanel.cpp88
-rw-r--r--vgui2/dme_controls/attributeshaderpickerpanel.cpp77
-rw-r--r--vgui2/dme_controls/attributeslider.cpp1350
-rw-r--r--vgui2/dme_controls/attributesurfacepropertypickerpanel.cpp103
-rw-r--r--vgui2/dme_controls/dme_controls.vpc133
-rw-r--r--vgui2/dme_controls/dmecombinationsystemeditorpanel.cpp2150
-rw-r--r--vgui2/dme_controls/dmecontrols.cpp102
-rw-r--r--vgui2/dme_controls/dmedageditpanel.cpp1011
-rw-r--r--vgui2/dme_controls/dmedagrenderpanel.cpp739
-rw-r--r--vgui2/dme_controls/dmelogeditpanel.cpp542
-rw-r--r--vgui2/dme_controls/dmemdlpanel.cpp47
-rw-r--r--vgui2/dme_controls/dmepanel.cpp656
-rw-r--r--vgui2/dme_controls/dmepicker.cpp280
-rw-r--r--vgui2/dme_controls/dmepresetgroupeditorpanel.cpp2332
-rw-r--r--vgui2/dme_controls/filtercombobox.cpp53
-rw-r--r--vgui2/dme_controls/particlesystempanel.cpp676
-rw-r--r--vgui2/dme_controls/particlesystempropertiespanel.cpp1149
-rw-r--r--vgui2/dme_controls/presetpicker.cpp194
-rw-r--r--vgui2/dme_controls/soundpicker.cpp602
-rw-r--r--vgui2/dme_controls/soundrecordpanel.cpp212
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", &params );
+
+ 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 );
+}
+
+