//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #include "filesystem.h" #include "matsys_controls/baseassetpicker.h" #include "tier1/KeyValues.h" #include "tier1/utlntree.h" #include "tier1/utlrbtree.h" #include "vgui_controls/ListPanel.h" #include "vgui_controls/TextEntry.h" #include "vgui_controls/ComboBox.h" #include "vgui_controls/Button.h" #include "vgui_controls/Splitter.h" #include "vgui_controls/TreeView.h" #include "vgui_controls/ImageList.h" #include "vgui_controls/CheckButton.h" #include "vgui/ISurface.h" #include "vgui/IInput.h" #include "vgui/IVGui.h" #include "vgui/Cursor.h" using namespace vgui; #define ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME 0.25f #define ASSET_LIST_DIRECTORY_SEARCH_TIME 0.025f //----------------------------------------------------------------------------- // sorting function, should return true if node1 should be displayed before node2 //----------------------------------------------------------------------------- bool AssetTreeViewSortFunc( KeyValues *node1, KeyValues *node2 ) { const char *pDir1 = node1->GetString( "text", NULL ); const char *pDir2 = node2->GetString( "text", NULL ); return Q_stricmp( pDir1, pDir2 ) < 0; } //----------------------------------------------------------------------------- // // Tree view for assets // //----------------------------------------------------------------------------- class CAssetTreeView : public vgui::TreeView { DECLARE_CLASS_SIMPLE( CAssetTreeView, vgui::TreeView ); public: CAssetTreeView( vgui::Panel *parent, const char *name, const char *pRootFolderName, const char *pRootDir ); // Inherited from base classes virtual void GenerateChildrenOfNode( int itemIndex ); virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); // Opens and selects the root folder void OpenRoot(); // Purpose: Refreshes the active file list void RefreshFileList(); // Adds a subdirectory DirHandle_t GetRootDirectory( ); DirHandle_t AddSubDirectory( DirHandle_t hParent, const char *pDirName ); void ClearDirectories(); // Selects a folder void SelectFolder( const char *pSubDir, const char *pPath ); private: // Allocates the root node void AllocateRootNode( ); // Purpose: Refreshes the active file list DirHandle_t RefreshTreeViewItem( int nItemIndex ); // Sets an item to be colored as if its a menu void SetItemColorForDirectories( int nItemID ); // Add a directory into the treeview void AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath ); // Selects an item in the tree bool SelectFolder_R( int nItemID, const char *pPath ); CUtlString m_RootFolderName; CUtlString m_RootDirectory; vgui::ImageList m_Images; CUtlNTree< CUtlString, DirHandle_t > m_DirectoryStructure; }; //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- CAssetTreeView::CAssetTreeView( Panel *pParent, const char *pName, const char *pRootFolderName, const char *pRootDir ) : BaseClass(pParent, pName), m_Images( false ) { SetSortFunc( AssetTreeViewSortFunc ); m_RootFolderName = pRootFolderName; m_RootDirectory = pRootDir; AllocateRootNode(); // build our list of images m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) ); SetImageList( &m_Images, false ); SETUP_PANEL( this ); } //----------------------------------------------------------------------------- // Purpose: Refreshes the active file list //----------------------------------------------------------------------------- void CAssetTreeView::OpenRoot() { RemoveAll(); // add the base node const char *pRootDir = m_DirectoryStructure[ m_DirectoryStructure.Root() ]; KeyValues *pkv = new KeyValues( "root" ); pkv->SetString( "text", m_RootFolderName.Get() ); pkv->SetInt( "root", 1 ); pkv->SetInt( "expand", 1 ); pkv->SetInt( "dirHandle", m_DirectoryStructure.Root() ); pkv->SetString( "path", pRootDir ); int iRoot = AddItem( pkv, GetRootItemIndex() ); pkv->deleteThis(); ExpandItem( iRoot, true ); } //----------------------------------------------------------------------------- // Allocates the root node //----------------------------------------------------------------------------- void CAssetTreeView::AllocateRootNode( ) { DirHandle_t hRoot = m_DirectoryStructure.Alloc(); m_DirectoryStructure.SetRoot( hRoot ); m_DirectoryStructure[hRoot] = m_RootDirectory; } //----------------------------------------------------------------------------- // Adds a subdirectory (maintains sorted order) //----------------------------------------------------------------------------- DirHandle_t CAssetTreeView::GetRootDirectory( ) { return m_DirectoryStructure.Root(); } DirHandle_t CAssetTreeView::AddSubDirectory( DirHandle_t hParent, const char *pDirName ) { DirHandle_t hSubdir = m_DirectoryStructure.Alloc(); m_DirectoryStructure[hSubdir] = pDirName; m_DirectoryStructure[hSubdir].ToLower(); DirHandle_t hChild = m_DirectoryStructure.FirstChild( hParent ); m_DirectoryStructure.LinkChildBefore( hParent, hChild, hSubdir ); return hSubdir; } void CAssetTreeView::ClearDirectories() { m_DirectoryStructure.RemoveAll(); AllocateRootNode(); } //----------------------------------------------------------------------------- // Sets an item to be colored as if its a menu //----------------------------------------------------------------------------- void CAssetTreeView::SetItemColorForDirectories( int nItemID ) { // mark directories in orange SetItemFgColor( nItemID, Color(224, 192, 0, 255) ); } //----------------------------------------------------------------------------- // Add a directory into the treeview //----------------------------------------------------------------------------- void CAssetTreeView::AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath ) { const char *pDirName = m_DirectoryStructure[hPath].Get(); KeyValues *kv = new KeyValues( "node", "text", pDirName ); char pFullPath[MAX_PATH]; Q_snprintf( pFullPath, sizeof( pFullPath ), "%s/%s", pFullParentPath, pDirName ); Q_FixSlashes( pFullPath ); Q_strlower( pFullPath ); bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex(); kv->SetString( "path", pFullPath ); kv->SetInt( "expand", bHasSubdirectories ); kv->SetInt( "image", 0 ); kv->SetInt( "dirHandle", hPath ); int nItemID = AddItem( kv, nParentItemIndex ); kv->deleteThis(); // mark directories in orange SetItemColorForDirectories( nItemID ); } //----------------------------------------------------------------------------- // override to incremental request and show p4 directories //----------------------------------------------------------------------------- void CAssetTreeView::GenerateChildrenOfNode( int nItemIndex ) { KeyValues *pkv = GetItemData( nItemIndex ); const char *pFullParentPath = pkv->GetString( "path", NULL ); if ( !pFullParentPath ) return; DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() ); if ( hPath == m_DirectoryStructure.InvalidIndex() ) return; DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath ); while ( hChild != m_DirectoryStructure.InvalidIndex() ) { AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild ); hChild = m_DirectoryStructure.NextSibling( hChild ); } } //----------------------------------------------------------------------------- // Purpose: Refreshes the active file list //----------------------------------------------------------------------------- DirHandle_t CAssetTreeView::RefreshTreeViewItem( int nItemIndex ) { if ( nItemIndex < 0 ) return m_DirectoryStructure.InvalidIndex(); // Make sure the expand icons are set correctly KeyValues *pkv = GetItemData( nItemIndex ); DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() ); const char *pFullParentPath = pkv->GetString( "path", NULL ); bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex(); if ( bHasSubdirectories != ( pkv->GetInt( "expand" ) != 0 ) ) { pkv->SetInt( "expand", bHasSubdirectories ); ModifyItem( nItemIndex, pkv ); } bool bIsExpanded = IsItemExpanded( nItemIndex ); if ( !bIsExpanded ) return hPath; // Check all children + build a list of children we've already got int nChildCount = GetNumChildren( nItemIndex ); DirHandle_t *pFoundHandles = (DirHandle_t*)_alloca( nChildCount * sizeof(DirHandle_t) ); memset( pFoundHandles, 0xFF, nChildCount * sizeof(DirHandle_t) ); for ( int i = 0; i < nChildCount; ++i ) { int nChildItemIndex = GetChild( nItemIndex, i ); pFoundHandles[i] = RefreshTreeViewItem( nChildItemIndex ); } // Check directory structure to see if other directories were added DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath ); for ( ; hChild != m_DirectoryStructure.InvalidIndex(); hChild = m_DirectoryStructure.NextSibling( hChild ) ) { // Search for existence of this child already bool bFound = false; for ( int j = 0; j < nChildCount; ++j ) { if ( pFoundHandles[j] == hChild ) { pFoundHandles[j] = pFoundHandles[nChildCount-1]; --nChildCount; bFound = true; break; } } if ( bFound ) continue; // Child is new, add it AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild ); } return hPath; } void CAssetTreeView::RefreshFileList() { // Make sure the expand icons are set correctly RefreshTreeViewItem( GetRootItemIndex() ); InvalidateLayout(); } //----------------------------------------------------------------------------- // Selects a folder //----------------------------------------------------------------------------- bool CAssetTreeView::SelectFolder_R( int nItemID, const char *pPath ) { if ( nItemID < 0 ) return false; KeyValues *kv = GetItemData( nItemID ); const char *pTestPath = kv->GetString( "path" ); if ( !Q_stricmp( pTestPath, pPath ) ) { AddSelectedItem( nItemID, true, false, true ); return true; } // Substring match.. CUtlString str = pTestPath; str += '\\'; if ( Q_strnicmp( str, pPath, str.Length() ) ) return false; ExpandItem( nItemID, true ); int nChildCount = GetNumChildren( nItemID ); for ( int i = 0; i < nChildCount; ++i ) { int nChildItemID = GetChild( nItemID, i ); if ( SelectFolder_R( nChildItemID, pPath ) ) return true; } return false; } void CAssetTreeView::SelectFolder( const char *pSubDir, const char *pPath ) { char pTemp[MAX_PATH]; Q_snprintf( pTemp, sizeof(pTemp), "%s\\%s", pSubDir, pPath ); Q_StripTrailingSlash( pTemp ); int nItem = GetRootItemIndex(); SelectFolder_R( nItem, pTemp ); } //----------------------------------------------------------------------------- // setup a smaller font //----------------------------------------------------------------------------- void CAssetTreeView::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); SetFont( pScheme->GetFont("DefaultSmall") ); SetFgColor( Color(216, 222, 211, 255) ); } //----------------------------------------------------------------------------- // // Cache of asset data so we don't need to rebuild all the time // //----------------------------------------------------------------------------- DECLARE_POINTER_HANDLE( AssetList_t ); #define ASSET_LIST_INVALID ((AssetList_t)(0xFFFF)) class CAssetCache { public: struct CachedAssetInfo_t { CUtlString m_AssetName; int m_nModIndex; }; struct ModInfo_t { CUtlString m_ModName; CUtlString m_Path; }; CAssetCache(); // Mod iteration int ModCount() const; const ModInfo_t& ModInfo( int nIndex ) const; // Building the mod list void BuildModList(); AssetList_t FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt ); bool BeginAssetScan( AssetList_t hList, bool bForceRescan = false ); CAssetTreeView* GetFileTree( AssetList_t hList ); int GetAssetCount( AssetList_t hList ) const; const CachedAssetInfo_t& GetAsset( AssetList_t hList, int nIndex ) const; void AddAsset( AssetList_t hList, const CachedAssetInfo_t& info ); bool ContinueSearchForAssets( AssetList_t hList, float flDuration ); private: struct DirToCheck_t { CUtlString m_DirName; DirHandle_t m_hDirHandle; }; struct CachedAssetList_t { CachedAssetList_t() {} CachedAssetList_t( const char *pSearchSubDir, int nExtCount, const char **ppSearchExt ) : m_pSubDir( pSearchSubDir, Q_strlen( pSearchSubDir ) + 1 ) { m_Ext.AddMultipleToTail( nExtCount, ppSearchExt ); } CachedAssetList_t( const CachedAssetList_t& ) { // Only used during insertion; do nothing } CUtlVector< CachedAssetInfo_t > m_AssetList; CAssetTreeView *m_pFileTree; CUtlString m_pSubDir; CUtlVector< const char * > m_Ext; CUtlLinkedList< DirToCheck_t > m_DirectoriesToCheck; FileFindHandle_t m_hFind; bool m_bAssetScanComplete; }; private: bool AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hDirHandle, float flStartTime, float flDuration ); bool DoesExtensionMatch( CachedAssetList_t& list, const char *pFileName ); void AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex ); private: // List of known mods CUtlVector< ModInfo_t > m_ModList; // List of cached assets CUtlRBTree< CachedAssetList_t > m_CachedAssets; // Have we built the mod list? bool m_bBuiltModList; static bool CachedAssetLessFunc( const CachedAssetList_t& src1, const CachedAssetList_t& src2 ); }; //----------------------------------------------------------------------------- // Static instance of the asset cache //----------------------------------------------------------------------------- static CAssetCache s_AssetCache; //----------------------------------------------------------------------------- // Map sort func //----------------------------------------------------------------------------- bool CAssetCache::CachedAssetLessFunc( const CAssetCache::CachedAssetList_t& src1, const CAssetCache::CachedAssetList_t& src2 ) { int nRetVal = Q_stricmp( src1.m_pSubDir, src2.m_pSubDir ) > 0; if ( nRetVal != 0 ) return nRetVal > 0; int nCount = src1.m_Ext.Count(); int nDiff = nCount - src2.m_Ext.Count(); if ( nDiff != 0 ) return nDiff > 0; for ( int i = 0; i < nCount; ++i ) { nRetVal = Q_stricmp( src1.m_Ext[i], src2.m_Ext[i] ); if ( nRetVal != 0 ) return nRetVal > 0; } return false; } //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- CAssetCache::CAssetCache() : m_CachedAssets( 0, 0, CachedAssetLessFunc ) { m_bBuiltModList = false; } //----------------------------------------------------------------------------- // Mod iteration //----------------------------------------------------------------------------- int CAssetCache::ModCount() const { return m_ModList.Count(); } const CAssetCache::ModInfo_t& CAssetCache::ModInfo( int nIndex ) const { return m_ModList[nIndex]; } //----------------------------------------------------------------------------- // Building the mod list //----------------------------------------------------------------------------- void CAssetCache::BuildModList() { if ( m_bBuiltModList ) return; m_bBuiltModList = true; m_ModList.RemoveAll(); // Add all mods int nLen = g_pFullFileSystem->GetSearchPath( "GAME", false, NULL, 0 ); char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) ); g_pFullFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen ); char *pPath = pSearchPath; while( pPath ) { char *pSemiColon = strchr( pPath, ';' ); if ( pSemiColon ) { *pSemiColon = 0; } Q_StripTrailingSlash( pPath ); Q_FixSlashes( pPath ); char pModName[ MAX_PATH ]; Q_FileBase( pPath, pModName, sizeof( pModName ) ); // Always start in an asset-specific directory // char pAssetPath[MAX_PATH]; // Q_snprintf( pAssetPath, MAX_PATH, "%s\\%s", pPath, m_pAssetSubDir ); // Q_FixSlashes( pPath ); int i = m_ModList.AddToTail( ); m_ModList[i].m_ModName.Set( pModName ); m_ModList[i].m_Path.Set( pPath ); pPath = pSemiColon ? pSemiColon + 1 : NULL; } } //----------------------------------------------------------------------------- // Adds an asset to the list of assets of this type //----------------------------------------------------------------------------- void CAssetCache::AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex ) { int i = list.m_AssetList.AddToTail( ); CachedAssetInfo_t& info = list.m_AssetList[i]; info.m_AssetName.Set( pAssetName ); info.m_nModIndex = nModIndex; } //----------------------------------------------------------------------------- // Extension matches? //----------------------------------------------------------------------------- bool CAssetCache::DoesExtensionMatch( CachedAssetList_t& info, const char *pFileName ) { char pChildExt[MAX_PATH]; Q_ExtractFileExtension( pFileName, pChildExt, sizeof(pChildExt) ); // Check the extension matches int nCount = info.m_Ext.Count(); for ( int i = 0; i < nCount; ++i ) { if ( !Q_stricmp( info.m_Ext[i], pChildExt ) ) return true; } return false; } //----------------------------------------------------------------------------- // Recursively add all files matching the wildcard under this directory //----------------------------------------------------------------------------- bool CAssetCache::AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hCurrentDir, float flStartTime, float flDuration ) { // Indicates no files found if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE ) return true; // generate children // add all the items int nModCount = m_ModList.Count(); int nSubDirLen = list.m_pSubDir ? Q_strlen(list.m_pSubDir) : 0; const char *pszFileName = pStartingFile; while ( pszFileName ) { char pRelativeChildPath[MAX_PATH]; Q_snprintf( pRelativeChildPath, MAX_PATH, "%s\\%s", pFilePath, pszFileName ); if ( g_pFullFileSystem->FindIsDirectory( list.m_hFind ) ) { // If .svn is in the name, don't add this directory!! if ( strstr (pszFileName, ".svn") ) { pszFileName = g_pFullFileSystem->FindNext( list.m_hFind ); continue; } if ( Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) ) { DirHandle_t hDirHandle = list.m_pFileTree->AddSubDirectory( hCurrentDir, pszFileName ); int i = list.m_DirectoriesToCheck.AddToTail(); list.m_DirectoriesToCheck[i].m_DirName = pRelativeChildPath; list.m_DirectoriesToCheck[i].m_hDirHandle = hDirHandle; } } else { // Check the extension matches if ( DoesExtensionMatch( list, pszFileName ) ) { char pFullAssetPath[MAX_PATH]; g_pFullFileSystem->RelativePathToFullPath( pRelativeChildPath, "GAME", pFullAssetPath, sizeof(pFullAssetPath) ); int nModIndex = -1; for ( int i = 0; i < nModCount; ++i ) { if ( !Q_strnicmp( pFullAssetPath, m_ModList[i].m_Path, m_ModList[i].m_Path.Length() ) ) { nModIndex = i; break; } } if ( nModIndex >= 0 ) { // Strip 'subdir/' prefix char *pAssetName = pRelativeChildPath; if ( list.m_pSubDir ) { if ( !Q_strnicmp( list.m_pSubDir, pAssetName, nSubDirLen ) ) { if ( pAssetName[nSubDirLen] == '\\' ) { pAssetName += nSubDirLen + 1; } } } strlwr( pAssetName ); AddAssetToList( list, pAssetName, nModIndex ); } } } // Don't let the search go for too long at a time if ( Plat_FloatTime() - flStartTime >= flDuration ) return false; pszFileName = g_pFullFileSystem->FindNext( list.m_hFind ); } return true; } //----------------------------------------------------------------------------- // Recursively add all files matching the wildcard under this directory //----------------------------------------------------------------------------- bool CAssetCache::ContinueSearchForAssets( AssetList_t hList, float flDuration ) { CachedAssetList_t& list = m_CachedAssets[ (int)hList ]; float flStartTime = Plat_FloatTime(); while ( list.m_DirectoriesToCheck.Count() ) { const char *pFilePath = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_DirName; DirHandle_t hCurrentDir = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_hDirHandle; const char *pStartingFile; if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE ) { char pSearchString[MAX_PATH]; Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath ); // get the list of files pStartingFile = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &list.m_hFind ); } else { pStartingFile = g_pFullFileSystem->FindNext( list.m_hFind ); } if ( !AddFilesInDirectory( list, pStartingFile, pFilePath, hCurrentDir, flStartTime, flDuration ) ) return false; g_pFullFileSystem->FindClose( list.m_hFind ); list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE; list.m_DirectoriesToCheck.Remove( list.m_DirectoriesToCheck.Head() ); } list.m_bAssetScanComplete = true; return true; } //----------------------------------------------------------------------------- // Asset cache iteration //----------------------------------------------------------------------------- bool CAssetCache::BeginAssetScan( AssetList_t hList, bool bForceRescan ) { CachedAssetList_t& list = m_CachedAssets[ (int)hList ]; if ( bForceRescan ) { list.m_bAssetScanComplete = false; if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE ) { g_pFullFileSystem->FindClose( list.m_hFind ); list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE; } list.m_DirectoriesToCheck.RemoveAll(); } if ( list.m_bAssetScanComplete ) return false; // This case occurs if we stopped the picker previously while in the middle of a scan if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE ) return true; list.m_AssetList.RemoveAll(); list.m_pFileTree->ClearDirectories(); // Add all files, determine which mod they are in. int i = list.m_DirectoriesToCheck.AddToTail(); list.m_DirectoriesToCheck[i].m_DirName = list.m_pSubDir; list.m_DirectoriesToCheck[i].m_hDirHandle = list.m_pFileTree->GetRootDirectory(); return true; } //----------------------------------------------------------------------------- // Asset cache iteration //----------------------------------------------------------------------------- AssetList_t CAssetCache::FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt ) { CachedAssetList_t search( pSubDir, nExtCount, ppExt ); int nIndex = m_CachedAssets.Find( search ); if ( nIndex == m_CachedAssets.InvalidIndex() ) { nIndex = m_CachedAssets.Insert( search ); CachedAssetList_t &list = m_CachedAssets[nIndex]; list.m_pSubDir = pSubDir; list.m_Ext.AddMultipleToTail( nExtCount, ppExt ); list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE; list.m_bAssetScanComplete = false; list.m_pFileTree = new CAssetTreeView( NULL, "FolderFilter", pAssetType, pSubDir ); } return (AssetList_t)nIndex; } CAssetTreeView* CAssetCache::GetFileTree( AssetList_t hList ) { if ( hList == ASSET_LIST_INVALID ) return NULL; return m_CachedAssets[ (int)hList ].m_pFileTree; } int CAssetCache::GetAssetCount( AssetList_t hList ) const { if ( hList == ASSET_LIST_INVALID ) return 0; return m_CachedAssets[ (int)hList ].m_AssetList.Count(); } const CAssetCache::CachedAssetInfo_t& CAssetCache::GetAsset( AssetList_t hList, int nIndex ) const { Assert( nIndex < GetAssetCount(hList) ); return m_CachedAssets[ (int)hList ].m_AssetList[ nIndex ]; } //----------------------------------------------------------------------------- // // Base asset Picker // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Sort by asset name //----------------------------------------------------------------------------- static int __cdecl AssetBrowserSortFunc( 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 *pString1 = item1.kv->GetString("asset"); const char *pString2 = item2.kv->GetString("asset"); return Q_stricmp( pString1, pString2 ); } static int __cdecl AssetBrowserModSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) { int nMod1 = item1.kv->GetInt("modIndex", -1); int nMod2 = item2.kv->GetInt("modIndex", -1); if ( nMod1 != nMod2 ) return nMod1 - nMod2; return AssetBrowserSortFunc( pPanel, item1, item2 ); } //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CBaseAssetPicker::CBaseAssetPicker( vgui::Panel *pParent, const char *pAssetType, const char *pExt, const char *pSubDir, const char *pTextType ) : BaseClass( pParent, "AssetPicker" ) { m_bBuiltAssetList = false; m_pAssetType = pAssetType; m_pAssetTextType = pTextType; m_pAssetExt = pExt; m_pAssetSubDir = pSubDir; m_bFinishedAssetListScan = false; m_bFirstAssetScan = false; m_nMatchingAssets = 0; m_bSubDirCheck = true; m_hAssetList = ASSET_LIST_INVALID; } //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CBaseAssetPicker::~CBaseAssetPicker() { SaveUserConfig(); // Detach! m_pFileTree->RemoveActionSignalTarget( this ); m_pFileTree->SetParent( (Panel*)NULL ); m_pFileTree = NULL; } //----------------------------------------------------------------------------- // Creates standard controls //----------------------------------------------------------------------------- void CBaseAssetPicker::CreateStandardControls( vgui::Panel *pParent, bool bAllowMultiselect ) { int nExtCount = 1 + m_ExtraAssetExt.Count(); const char **ppExt = (const char **)_alloca( nExtCount * sizeof(const char *) ); ppExt[0] = m_pAssetExt; if ( nExtCount > 1 ) { memcpy( ppExt + 1, m_ExtraAssetExt.Base(), nExtCount - 1 ); } m_hAssetList = s_AssetCache.FindAssetList( m_pAssetType, m_pAssetSubDir, nExtCount, ppExt ); m_pAssetSplitter = new vgui::Splitter( pParent, "AssetSplitter", SPLITTER_MODE_HORIZONTAL, 1 ); vgui::Panel *pSplitterTopSide = m_pAssetSplitter->GetChild( 0 ); vgui::Panel *pSplitterBottomSide = m_pAssetSplitter->GetChild( 1 ); // Combo box for mods m_pModSelector = new ComboBox( pSplitterTopSide, "ModFilter", 5, false ); m_pModSelector->AddActionSignalTarget( this ); // Rescan button m_pRescanButton = new Button( pSplitterTopSide, "RescanButton", "Rescan", this, "AssetRescan" ); // file browser tree controls m_pFileTree = s_AssetCache.GetFileTree( m_hAssetList ); m_pFileTree->SetParent( pSplitterTopSide ); m_pFileTree->AddActionSignalTarget( this ); m_pSubDirCheck = new CheckButton( pSplitterTopSide, "SubDirCheck", "Check subfolders for files?" ); m_pSubDirCheck->SetSelected( true ); m_pSubDirCheck->SetEnabled( false ); m_pSubDirCheck->SetVisible( false ); m_pSubDirCheck->AddActionSignalTarget( this ); char pTemp[512]; Q_snprintf( pTemp, sizeof(pTemp), "No .%s files", m_pAssetExt ); m_pAssetBrowser = new vgui::ListPanel( pSplitterBottomSide, "AssetBrowser" ); m_pAssetBrowser->AddColumnHeader( 0, "mod", "Mod", 52, 0 ); m_pAssetBrowser->AddColumnHeader( 1, "asset", m_pAssetType, 128, ListPanel::COLUMN_RESIZEWITHWINDOW ); m_pAssetBrowser->SetSelectIndividualCells( false ); m_pAssetBrowser->SetMultiselectEnabled( bAllowMultiselect ); m_pAssetBrowser->SetEmptyListText( pTemp ); m_pAssetBrowser->SetDragEnabled( true ); m_pAssetBrowser->AddActionSignalTarget( this ); m_pAssetBrowser->SetSortFunc( 0, AssetBrowserModSortFunc ); m_pAssetBrowser->SetSortFunc( 1, AssetBrowserSortFunc ); m_pAssetBrowser->SetSortColumn( 1 ); // filter selection m_pFilter = new TextEntry( pSplitterBottomSide, "FilterList" ); m_pFilter->AddActionSignalTarget( this ); // full path m_pFullPath = new TextEntry( pSplitterBottomSide, "FullPath" ); m_pFullPath->SetEnabled( false ); m_pFullPath->SetEditable( false ); m_nCurrentModFilter = -1; } //----------------------------------------------------------------------------- // Reads user config settings //----------------------------------------------------------------------------- void CBaseAssetPicker::ApplyUserConfigSettings( KeyValues *pUserConfig ) { BaseClass::ApplyUserConfigSettings( pUserConfig ); // Populates the mod list names RefreshAssetList(); const char *pFilter = pUserConfig->GetString( "filter", "" ); m_FolderFilter = pUserConfig->GetString( "folderfilter", "" ); const char *pMod = pUserConfig->GetString( "mod", "" ); SetFilter( pFilter ); m_nCurrentModFilter = -1; if ( pMod && pMod[0] ) { int nCount = s_AssetCache.ModCount(); for ( int i = 0; i < nCount; ++i ) { const CAssetCache::ModInfo_t& modInfo = s_AssetCache.ModInfo( i ); if ( Q_stricmp( pMod, modInfo.m_ModName ) ) continue; int nItemCount = m_pModSelector->GetItemCount(); for ( int j = 0; j < nItemCount; ++j ) { int nItemID = m_pModSelector->GetItemIDFromRow( j ); KeyValues *kv = m_pModSelector->GetItemUserData( nItemID ); int nModIndex = kv->GetInt( "mod" ); if ( nModIndex == i ) { m_nCurrentModFilter = i; m_pModSelector->ActivateItem( nItemID ); break; } } break; } } } //----------------------------------------------------------------------------- // Purpose: returns user config settings for this control //----------------------------------------------------------------------------- void CBaseAssetPicker::GetUserConfigSettings( KeyValues *pUserConfig ) { BaseClass::GetUserConfigSettings( pUserConfig ); pUserConfig->SetString( "filter", m_Filter ); pUserConfig->SetString( "folderfilter", m_FolderFilter ); pUserConfig->SetString( "mod", ( m_nCurrentModFilter >= 0 ) ? s_AssetCache.ModInfo( m_nCurrentModFilter ).m_ModName : "" ); } //----------------------------------------------------------------------------- // Purpose: optimization, return true if this control has any user config settings //----------------------------------------------------------------------------- bool CBaseAssetPicker::HasUserConfigSettings() { return true; } //----------------------------------------------------------------------------- // Allows the picker to browse multiple asset types //----------------------------------------------------------------------------- void CBaseAssetPicker::AddExtension( const char *pExtension ) { m_ExtraAssetExt.AddToTail( pExtension ); } //----------------------------------------------------------------------------- // Is multiselect enabled? //----------------------------------------------------------------------------- bool CBaseAssetPicker::IsMultiselectEnabled() const { return m_pAssetBrowser->IsMultiselectEnabled(); } //----------------------------------------------------------------------------- // Sets the initial selected asset //----------------------------------------------------------------------------- void CBaseAssetPicker::SetInitialSelection( const char *pAssetName ) { // This makes it so the background list filling code will automatically select this asset when it gets to it. m_SelectedAsset = pAssetName; if ( pAssetName ) { // Sometimes we've already refreshed our list with a bunch of cached resources and the item is already in the list, // so in that case just select it here. int cnt = m_pAssetBrowser->GetItemCount(); for ( int i=0; i < cnt; i++ ) { KeyValues *kv = m_pAssetBrowser->GetItem( i ); if ( !kv ) continue; const char *pTestAssetName = kv->GetString( "asset" ); if ( !pTestAssetName ) continue; if ( Q_stricmp( pTestAssetName, pAssetName ) == 0 ) { m_pAssetBrowser->SetSelectedCell( i, 0 ); break; } } } } //----------------------------------------------------------------------------- // Set/get the filter //----------------------------------------------------------------------------- void CBaseAssetPicker::SetFilter( const char *pFilter ) { m_Filter = pFilter; m_pFilter->SetText( pFilter ); } const char *CBaseAssetPicker::GetFilter() { return m_Filter; } //----------------------------------------------------------------------------- // Purpose: called to open //----------------------------------------------------------------------------- void CBaseAssetPicker::Activate() { RefreshAssetList(); RequestFilterFocus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAssetPicker::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_pAssetBrowser->GetVPanel(), pMsg, GetVPanel()); pMsg->deleteThis(); } else { BaseClass::OnKeyCodePressed( code ); } } //----------------------------------------------------------------------------- // Is a particular asset visible? //----------------------------------------------------------------------------- bool CBaseAssetPicker::IsAssetVisible( int nAssetIndex ) { const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex ); // Filter based on active mod int nModIndex = info.m_nModIndex; if ( ( m_nCurrentModFilter >= 0 ) && ( m_nCurrentModFilter != nModIndex ) ) return false; // Filter based on name const char *pAssetName = info.m_AssetName; if ( !Q_strcmp( pAssetName, m_SelectedAsset ) ) return true; if ( m_Filter.Length() && !Q_stristr( pAssetName, m_Filter.Get() ) ) return false; // Filter based on folder if ( m_FolderFilter.Length() && Q_strnicmp( pAssetName, m_FolderFilter.Get(), m_FolderFilter.Length() ) ) return false; // Filter based on subdirectory check if ( !m_bSubDirCheck && strchr( pAssetName + m_FolderFilter.Length(), '\\' ) ) return false; return true; } //----------------------------------------------------------------------------- // Adds an asset from the cache to the list //----------------------------------------------------------------------------- void CBaseAssetPicker::AddAssetToList( int nAssetIndex ) { const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex ); bool bInRootDir = !strchr( info.m_AssetName, '\\' ) && !strchr( info.m_AssetName, '/' ); KeyValues *kv = new KeyValues( "node", "asset", info.m_AssetName ); kv->SetString( "mod", s_AssetCache.ModInfo( info.m_nModIndex ).m_ModName ); kv->SetInt( "modIndex", info.m_nModIndex ); kv->SetInt( "root", bInRootDir ); int nItemID = m_pAssetBrowser->AddItem( kv, 0, false, false ); kv->deleteThis(); if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 && !Q_strcmp( m_SelectedAsset, info.m_AssetName ) ) { m_pAssetBrowser->SetSelectedCell( nItemID, 0 ); } KeyValues *pDrag = new KeyValues( "drag", "text", info.m_AssetName ); if ( m_pAssetTextType ) { pDrag->SetString( "texttype", m_pAssetTextType ); } m_pAssetBrowser->SetItemDragData( nItemID, pDrag ); int i = m_AssetList.AddToTail( ); m_AssetList[i].m_nAssetIndex = nAssetIndex; m_AssetList[i].m_nItemId = nItemID; bool bIsVisible = IsAssetVisible( i ); m_pAssetBrowser->SetItemVisible( nItemID, bIsVisible ); if ( bIsVisible ) { ++m_nMatchingAssets; } } //----------------------------------------------------------------------------- // Continues to build the asset list //----------------------------------------------------------------------------- void CBaseAssetPicker::OnTick() { BaseClass::OnTick(); int nPreAssetCount = s_AssetCache.GetAssetCount( m_hAssetList ); // Stop getting called back once all assets have been found float flTime = m_bFirstAssetScan ? ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME : ASSET_LIST_DIRECTORY_SEARCH_TIME; bool bFinished = s_AssetCache.ContinueSearchForAssets( m_hAssetList, flTime ); if ( m_bFirstAssetScan ) { m_pFileTree->OpenRoot(); } m_bFirstAssetScan = false; int nPostAssetCount = s_AssetCache.GetAssetCount( m_hAssetList ); for ( int i = nPreAssetCount; i < nPostAssetCount; ++i ) { AddAssetToList( i ); } if ( bFinished ) { vgui::ivgui()->RemoveTickSignal( GetVPanel() ); m_bFinishedAssetListScan = true; // Copy the current folder filter.. this is necessary // to finally select the folder loaded from the user config settings // in the free view (since it's finally populated at this point) // NOTE: if a user has changed the folder filter between startup // and this point, this should still work since m_FolderFilter should be updated m_pFileTree->SelectFolder( m_pAssetSubDir, m_FolderFilter ); RefreshAssetList( ); return; } UpdateAssetColumnHeader(); } //----------------------------------------------------------------------------- // Builds the Bsp name list //----------------------------------------------------------------------------- void CBaseAssetPicker::BuildAssetNameList( ) { if ( m_bBuiltAssetList ) return; m_bBuiltAssetList = true; m_nMatchingAssets = 0; m_nCurrentModFilter = -1; // Build the list of known mods if we haven't s_AssetCache.BuildModList(); m_pModSelector->RemoveAll(); m_pModSelector->AddItem( "All Mods", new KeyValues( "Mod", "mod", -1 ) ); int nModCount = s_AssetCache.ModCount(); for ( int i = 0; i < nModCount; ++i ) { const char *pModName = s_AssetCache.ModInfo( i ).m_ModName; m_pModSelector->AddItem( pModName, new KeyValues( "Mod", "mod", i ) ); } m_pModSelector->ActivateItemByRow( 0 ); // If we've already read in if ( s_AssetCache.BeginAssetScan( m_hAssetList ) ) { m_bFirstAssetScan = true; m_bFinishedAssetListScan = false; vgui::ivgui()->AddTickSignal( GetVPanel(), 10 ); } else { m_bFirstAssetScan = false; m_bFinishedAssetListScan = true; } int nAssetCount = s_AssetCache.GetAssetCount( m_hAssetList ); for ( int i = 0; i < nAssetCount; ++i ) { AddAssetToList( i ); } } //----------------------------------------------------------------------------- // Rescan assets //----------------------------------------------------------------------------- void CBaseAssetPicker::RescanAssets() { m_pAssetBrowser->RemoveAll(); m_AssetList.RemoveAll(); s_AssetCache.BeginAssetScan( m_hAssetList, true ); m_bFirstAssetScan = true; m_nMatchingAssets = 0; if ( m_bFinishedAssetListScan ) { m_bFinishedAssetListScan = false; vgui::ivgui()->AddTickSignal( GetVPanel(), 10 ); } } //----------------------------------------------------------------------------- // Returns the mod path to the item index //----------------------------------------------------------------------------- const char *CBaseAssetPicker::GetModPath( int nModIndex ) { return s_AssetCache.ModInfo( nModIndex ).m_Path.Get(); } //----------------------------------------------------------------------------- // Command handler //----------------------------------------------------------------------------- void CBaseAssetPicker::OnCommand( const char *pCommand ) { if ( !Q_stricmp( pCommand, "AssetRescan" ) ) { RescanAssets(); return; } BaseClass::OnCommand( pCommand ); } //----------------------------------------------------------------------------- // Update column headers //----------------------------------------------------------------------------- void CBaseAssetPicker::UpdateAssetColumnHeader( ) { char pColumnTitle[512]; Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)%s", m_pAssetType, m_nMatchingAssets, m_AssetList.Count(), m_bFinishedAssetListScan ? "" : " ..." ); m_pAssetBrowser->SetColumnHeaderText( 1, pColumnTitle ); } //----------------------------------------------------------------------------- // Request focus of the filter box //----------------------------------------------------------------------------- void CBaseAssetPicker::RequestFilterFocus() { if ( m_Filter.Length() ) { m_pFilter->SelectAllOnFirstFocus( true ); } m_pFilter->RequestFocus(); } //----------------------------------------------------------------------------- // Purpose: refreshes the asset list //----------------------------------------------------------------------------- void CBaseAssetPicker::RefreshAssetList( ) { BuildAssetNameList(); // Check the filter matches int nCount = m_AssetList.Count(); m_nMatchingAssets = 0; for ( int i = 0; i < nCount; ++i ) { // Filter based on active mod bool bIsVisible = IsAssetVisible( i ); m_pAssetBrowser->SetItemVisible( m_AssetList[i].m_nItemId, bIsVisible ); if ( bIsVisible ) { ++m_nMatchingAssets; } } UpdateAssetColumnHeader(); m_pAssetBrowser->SortList(); if ( ( m_pAssetBrowser->GetSelectedItemsCount() == 0 ) && ( m_pAssetBrowser->GetItemCount() > 0 ) ) { // Invoke a callback if the next selection will be a 'default' selection OnNextSelectionIsDefault(); int nItemID = m_pAssetBrowser->GetItemIDFromRow( 0 ); m_pAssetBrowser->SetSelectedCell( nItemID, 0 ); } m_pFileTree->RefreshFileList(); } //----------------------------------------------------------------------------- // Purpose: refreshes dialog on file folder changing //----------------------------------------------------------------------------- void CBaseAssetPicker::OnFileSelected() { // update list const char *pFolderFilter = ""; int iItem = m_pFileTree->GetFirstSelectedItem(); if ( iItem >= 0 ) { KeyValues *pkv = m_pFileTree->GetItemData( iItem ); pFolderFilter = pkv->GetString( "path" ); // The first keys are always the subdir pFolderFilter += Q_strlen( m_pAssetSubDir ); if ( *pFolderFilter ) { ++pFolderFilter; } } if ( Q_stricmp( pFolderFilter, m_FolderFilter.Get() ) ) { int nLen = Q_strlen( pFolderFilter ); m_FolderFilter = pFolderFilter; if ( nLen > 0 ) { m_FolderFilter += '\\'; } RefreshAssetList(); } } //----------------------------------------------------------------------------- // Purpose: refreshes dialog on text changing //----------------------------------------------------------------------------- void CBaseAssetPicker::OnTextChanged( KeyValues *pKeyValues ) { vgui::Panel *pSource = (vgui::Panel*)pKeyValues->GetPtr( "panel" ); if ( pSource == m_pFilter ) { int nLength = m_pFilter->GetTextLength(); char *pNewFilter = (char*)_alloca( (nLength+1) * sizeof(char) ); if ( nLength > 0 ) { m_pFilter->GetText( pNewFilter, nLength+1 ); } else { pNewFilter[0] = 0; } if ( Q_stricmp( pNewFilter, m_Filter.Get() ) ) { m_Filter.SetLength( nLength ); m_Filter = pNewFilter; RefreshAssetList(); } return; } if ( pSource == m_pModSelector ) { KeyValues *pKeyValuesActive = m_pModSelector->GetActiveItemUserData(); if ( pKeyValuesActive ) { m_nCurrentModFilter = pKeyValuesActive->GetInt( "mod", -1 ); RefreshAssetList(); } return; } } //----------------------------------------------------------------------------- // Purpose: Updates preview when an item is selected //----------------------------------------------------------------------------- void CBaseAssetPicker::OnItemSelected( KeyValues *kv ) { Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); if ( pPanel == m_pAssetBrowser ) { int nCount = GetSelectedAssetCount(); Assert( nCount > 0 ); const char *pSelectedAsset = GetSelectedAsset( nCount - 1 ); // Fill in the full path int nModIndex = GetSelectedAssetModIndex(); char pBuf[MAX_PATH]; Q_snprintf( pBuf, sizeof(pBuf), "%s\\%s\\%s", s_AssetCache.ModInfo( nModIndex ).m_Path.Get(), m_pAssetSubDir, pSelectedAsset ); Q_FixSlashes( pBuf ); m_pFullPath->SetText( pBuf ); surface()->SetCursor( dc_waitarrow ); OnSelectedAssetPicked( pSelectedAsset ); return; } } void CBaseAssetPicker::OnCheckButtonChecked( KeyValues *kv ) { vgui::Panel *pSource = (vgui::Panel*)kv->GetPtr( "panel" ); if ( pSource == m_pSubDirCheck ) { m_bSubDirCheck = m_pSubDirCheck->IsSelected(); RefreshAssetList(); } } //----------------------------------------------------------------------------- // Returns the selceted asset count //----------------------------------------------------------------------------- int CBaseAssetPicker::GetSelectedAssetCount() { return m_pAssetBrowser->GetSelectedItemsCount(); } //----------------------------------------------------------------------------- // Returns the selceted asset name //----------------------------------------------------------------------------- const char *CBaseAssetPicker::GetSelectedAsset( int nAssetIndex ) { int nSelectedAssetCount = m_pAssetBrowser->GetSelectedItemsCount(); if ( nAssetIndex < 0 ) { nAssetIndex = nSelectedAssetCount - 1; } if ( nSelectedAssetCount <= nAssetIndex || nAssetIndex < 0 ) return NULL; int nIndex = m_pAssetBrowser->GetSelectedItem( nAssetIndex ); KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex ); return pItemKeyValues->GetString( "asset" ); } //----------------------------------------------------------------------------- // Returns the selceted asset mod index //----------------------------------------------------------------------------- int CBaseAssetPicker::GetSelectedAssetModIndex( ) { if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 ) return 0; int nIndex = m_pAssetBrowser->GetSelectedItem( 0 ); KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex ); return pItemKeyValues->GetInt( "modIndex" ); } //----------------------------------------------------------------------------- // // Purpose: Modal picker frame // //----------------------------------------------------------------------------- CBaseAssetPickerFrame::CBaseAssetPickerFrame( vgui::Panel *pParent ) : BaseClass( pParent, "AssetPickerFrame" ) { m_pContextKeyValues = NULL; SetDeleteSelfOnClose( true ); m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" ); m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" ); SetBlockDragChaining( true ); } CBaseAssetPickerFrame::~CBaseAssetPickerFrame() { CleanUpMessage(); } //----------------------------------------------------------------------------- // Allows the derived class to create the picker //----------------------------------------------------------------------------- void CBaseAssetPickerFrame::SetAssetPicker( CBaseAssetPicker* pPicker ) { m_pPicker = pPicker; m_pPicker->AddActionSignalTarget( this ); } //----------------------------------------------------------------------------- // Deletes the message //----------------------------------------------------------------------------- void CBaseAssetPickerFrame::CleanUpMessage() { if ( m_pContextKeyValues ) { m_pContextKeyValues->deleteThis(); m_pContextKeyValues = NULL; } } //----------------------------------------------------------------------------- // Sets the initial selected asset //----------------------------------------------------------------------------- void CBaseAssetPickerFrame::SetInitialSelection( const char *pAssetName ) { m_pPicker->SetInitialSelection( pAssetName ); } //----------------------------------------------------------------------------- // Set/get the filter //----------------------------------------------------------------------------- void CBaseAssetPickerFrame::SetFilter( const char *pFilter ) { m_pPicker->SetFilter( pFilter ); } const char *CBaseAssetPickerFrame::GetFilter() { return m_pPicker->GetFilter( ); } //----------------------------------------------------------------------------- // Purpose: Activate the dialog //----------------------------------------------------------------------------- void CBaseAssetPickerFrame::DoModal( KeyValues *pKeyValues ) { BaseClass::DoModal(); CleanUpMessage(); m_pContextKeyValues = pKeyValues; m_pPicker->Activate(); } //----------------------------------------------------------------------------- // Posts a message (passing the key values) //----------------------------------------------------------------------------- void CBaseAssetPickerFrame::PostMessageAndClose( KeyValues *pKeyValues ) { if ( m_pContextKeyValues ) { pKeyValues->AddSubKey( m_pContextKeyValues ); m_pContextKeyValues = NULL; } CloseModal(); PostActionSignal( pKeyValues ); } //----------------------------------------------------------------------------- // On command //----------------------------------------------------------------------------- void CBaseAssetPickerFrame::OnCommand( const char *pCommand ) { if ( !Q_stricmp( pCommand, "Open" ) ) { KeyValues *pActionKeys = new KeyValues( "AssetSelected" ); if ( !m_pPicker->IsMultiselectEnabled() ) { const char *pAssetName = m_pPicker->GetSelectedAsset( ); pActionKeys->SetString( "asset", pAssetName ); } else { char pBuf[512]; KeyValues *pAssetKeys = pActionKeys->FindKey( "assets", true ); int nCount = m_pPicker->GetSelectedAssetCount(); for ( int i = 0; i < nCount; ++i ) { Q_snprintf( pBuf, sizeof(pBuf), "asset%d", i ); pAssetKeys->SetString( pBuf, m_pPicker->GetSelectedAsset( i ) ); } } PostMessageAndClose( pActionKeys ); return; } if ( !Q_stricmp( pCommand, "Cancel" ) ) { CloseModal(); return; } BaseClass::OnCommand( pCommand ); }