summaryrefslogtreecommitdiff
path: root/vgui2/matsys_controls/baseassetpicker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vgui2/matsys_controls/baseassetpicker.cpp')
-rw-r--r--vgui2/matsys_controls/baseassetpicker.cpp1625
1 files changed, 1625 insertions, 0 deletions
diff --git a/vgui2/matsys_controls/baseassetpicker.cpp b/vgui2/matsys_controls/baseassetpicker.cpp
new file mode 100644
index 0000000..99e6eca
--- /dev/null
+++ b/vgui2/matsys_controls/baseassetpicker.cpp
@@ -0,0 +1,1625 @@
+//========= 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 );
+}
+
+