Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1625 lines
49 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "filesystem.h"
  7. #include "matsys_controls/baseassetpicker.h"
  8. #include "tier1/KeyValues.h"
  9. #include "tier1/utlntree.h"
  10. #include "tier1/utlrbtree.h"
  11. #include "vgui_controls/ListPanel.h"
  12. #include "vgui_controls/TextEntry.h"
  13. #include "vgui_controls/ComboBox.h"
  14. #include "vgui_controls/Button.h"
  15. #include "vgui_controls/Splitter.h"
  16. #include "vgui_controls/TreeView.h"
  17. #include "vgui_controls/ImageList.h"
  18. #include "vgui_controls/CheckButton.h"
  19. #include "vgui/ISurface.h"
  20. #include "vgui/IInput.h"
  21. #include "vgui/IVGui.h"
  22. #include "vgui/Cursor.h"
  23. using namespace vgui;
  24. #define ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME 0.25f
  25. #define ASSET_LIST_DIRECTORY_SEARCH_TIME 0.025f
  26. //-----------------------------------------------------------------------------
  27. // sorting function, should return true if node1 should be displayed before node2
  28. //-----------------------------------------------------------------------------
  29. bool AssetTreeViewSortFunc( KeyValues *node1, KeyValues *node2 )
  30. {
  31. const char *pDir1 = node1->GetString( "text", NULL );
  32. const char *pDir2 = node2->GetString( "text", NULL );
  33. return Q_stricmp( pDir1, pDir2 ) < 0;
  34. }
  35. //-----------------------------------------------------------------------------
  36. //
  37. // Tree view for assets
  38. //
  39. //-----------------------------------------------------------------------------
  40. class CAssetTreeView : public vgui::TreeView
  41. {
  42. DECLARE_CLASS_SIMPLE( CAssetTreeView, vgui::TreeView );
  43. public:
  44. CAssetTreeView( vgui::Panel *parent, const char *name, const char *pRootFolderName, const char *pRootDir );
  45. // Inherited from base classes
  46. virtual void GenerateChildrenOfNode( int itemIndex );
  47. virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
  48. // Opens and selects the root folder
  49. void OpenRoot();
  50. // Purpose: Refreshes the active file list
  51. void RefreshFileList();
  52. // Adds a subdirectory
  53. DirHandle_t GetRootDirectory( );
  54. DirHandle_t AddSubDirectory( DirHandle_t hParent, const char *pDirName );
  55. void ClearDirectories();
  56. // Selects a folder
  57. void SelectFolder( const char *pSubDir, const char *pPath );
  58. private:
  59. // Allocates the root node
  60. void AllocateRootNode( );
  61. // Purpose: Refreshes the active file list
  62. DirHandle_t RefreshTreeViewItem( int nItemIndex );
  63. // Sets an item to be colored as if its a menu
  64. void SetItemColorForDirectories( int nItemID );
  65. // Add a directory into the treeview
  66. void AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath );
  67. // Selects an item in the tree
  68. bool SelectFolder_R( int nItemID, const char *pPath );
  69. CUtlString m_RootFolderName;
  70. CUtlString m_RootDirectory;
  71. vgui::ImageList m_Images;
  72. CUtlNTree< CUtlString, DirHandle_t > m_DirectoryStructure;
  73. };
  74. //-----------------------------------------------------------------------------
  75. // Constructor
  76. //-----------------------------------------------------------------------------
  77. CAssetTreeView::CAssetTreeView( Panel *pParent, const char *pName, const char *pRootFolderName, const char *pRootDir ) : BaseClass(pParent, pName), m_Images( false )
  78. {
  79. SetSortFunc( AssetTreeViewSortFunc );
  80. m_RootFolderName = pRootFolderName;
  81. m_RootDirectory = pRootDir;
  82. AllocateRootNode();
  83. // build our list of images
  84. m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) );
  85. SetImageList( &m_Images, false );
  86. SETUP_PANEL( this );
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Purpose: Refreshes the active file list
  90. //-----------------------------------------------------------------------------
  91. void CAssetTreeView::OpenRoot()
  92. {
  93. RemoveAll();
  94. // add the base node
  95. const char *pRootDir = m_DirectoryStructure[ m_DirectoryStructure.Root() ];
  96. KeyValues *pkv = new KeyValues( "root" );
  97. pkv->SetString( "text", m_RootFolderName.Get() );
  98. pkv->SetInt( "root", 1 );
  99. pkv->SetInt( "expand", 1 );
  100. pkv->SetInt( "dirHandle", m_DirectoryStructure.Root() );
  101. pkv->SetString( "path", pRootDir );
  102. int iRoot = AddItem( pkv, GetRootItemIndex() );
  103. pkv->deleteThis();
  104. ExpandItem( iRoot, true );
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Allocates the root node
  108. //-----------------------------------------------------------------------------
  109. void CAssetTreeView::AllocateRootNode( )
  110. {
  111. DirHandle_t hRoot = m_DirectoryStructure.Alloc();
  112. m_DirectoryStructure.SetRoot( hRoot );
  113. m_DirectoryStructure[hRoot] = m_RootDirectory;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Adds a subdirectory (maintains sorted order)
  117. //-----------------------------------------------------------------------------
  118. DirHandle_t CAssetTreeView::GetRootDirectory( )
  119. {
  120. return m_DirectoryStructure.Root();
  121. }
  122. DirHandle_t CAssetTreeView::AddSubDirectory( DirHandle_t hParent, const char *pDirName )
  123. {
  124. DirHandle_t hSubdir = m_DirectoryStructure.Alloc();
  125. m_DirectoryStructure[hSubdir] = pDirName;
  126. m_DirectoryStructure[hSubdir].ToLower();
  127. DirHandle_t hChild = m_DirectoryStructure.FirstChild( hParent );
  128. m_DirectoryStructure.LinkChildBefore( hParent, hChild, hSubdir );
  129. return hSubdir;
  130. }
  131. void CAssetTreeView::ClearDirectories()
  132. {
  133. m_DirectoryStructure.RemoveAll();
  134. AllocateRootNode();
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Sets an item to be colored as if its a menu
  138. //-----------------------------------------------------------------------------
  139. void CAssetTreeView::SetItemColorForDirectories( int nItemID )
  140. {
  141. // mark directories in orange
  142. SetItemFgColor( nItemID, Color(224, 192, 0, 255) );
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Add a directory into the treeview
  146. //-----------------------------------------------------------------------------
  147. void CAssetTreeView::AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath )
  148. {
  149. const char *pDirName = m_DirectoryStructure[hPath].Get();
  150. KeyValues *kv = new KeyValues( "node", "text", pDirName );
  151. char pFullPath[MAX_PATH];
  152. Q_snprintf( pFullPath, sizeof( pFullPath ), "%s/%s", pFullParentPath, pDirName );
  153. Q_FixSlashes( pFullPath );
  154. Q_strlower( pFullPath );
  155. bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex();
  156. kv->SetString( "path", pFullPath );
  157. kv->SetInt( "expand", bHasSubdirectories );
  158. kv->SetInt( "image", 0 );
  159. kv->SetInt( "dirHandle", hPath );
  160. int nItemID = AddItem( kv, nParentItemIndex );
  161. kv->deleteThis();
  162. // mark directories in orange
  163. SetItemColorForDirectories( nItemID );
  164. }
  165. //-----------------------------------------------------------------------------
  166. // override to incremental request and show p4 directories
  167. //-----------------------------------------------------------------------------
  168. void CAssetTreeView::GenerateChildrenOfNode( int nItemIndex )
  169. {
  170. KeyValues *pkv = GetItemData( nItemIndex );
  171. const char *pFullParentPath = pkv->GetString( "path", NULL );
  172. if ( !pFullParentPath )
  173. return;
  174. DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() );
  175. if ( hPath == m_DirectoryStructure.InvalidIndex() )
  176. return;
  177. DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath );
  178. while ( hChild != m_DirectoryStructure.InvalidIndex() )
  179. {
  180. AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild );
  181. hChild = m_DirectoryStructure.NextSibling( hChild );
  182. }
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: Refreshes the active file list
  186. //-----------------------------------------------------------------------------
  187. DirHandle_t CAssetTreeView::RefreshTreeViewItem( int nItemIndex )
  188. {
  189. if ( nItemIndex < 0 )
  190. return m_DirectoryStructure.InvalidIndex();
  191. // Make sure the expand icons are set correctly
  192. KeyValues *pkv = GetItemData( nItemIndex );
  193. DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() );
  194. const char *pFullParentPath = pkv->GetString( "path", NULL );
  195. bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex();
  196. if ( bHasSubdirectories != ( pkv->GetInt( "expand" ) != 0 ) )
  197. {
  198. pkv->SetInt( "expand", bHasSubdirectories );
  199. ModifyItem( nItemIndex, pkv );
  200. }
  201. bool bIsExpanded = IsItemExpanded( nItemIndex );
  202. if ( !bIsExpanded )
  203. return hPath;
  204. // Check all children + build a list of children we've already got
  205. int nChildCount = GetNumChildren( nItemIndex );
  206. DirHandle_t *pFoundHandles = (DirHandle_t*)_alloca( nChildCount * sizeof(DirHandle_t) );
  207. memset( pFoundHandles, 0xFF, nChildCount * sizeof(DirHandle_t) );
  208. for ( int i = 0; i < nChildCount; ++i )
  209. {
  210. int nChildItemIndex = GetChild( nItemIndex, i );
  211. pFoundHandles[i] = RefreshTreeViewItem( nChildItemIndex );
  212. }
  213. // Check directory structure to see if other directories were added
  214. DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath );
  215. for ( ; hChild != m_DirectoryStructure.InvalidIndex(); hChild = m_DirectoryStructure.NextSibling( hChild ) )
  216. {
  217. // Search for existence of this child already
  218. bool bFound = false;
  219. for ( int j = 0; j < nChildCount; ++j )
  220. {
  221. if ( pFoundHandles[j] == hChild )
  222. {
  223. pFoundHandles[j] = pFoundHandles[nChildCount-1];
  224. --nChildCount;
  225. bFound = true;
  226. break;
  227. }
  228. }
  229. if ( bFound )
  230. continue;
  231. // Child is new, add it
  232. AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild );
  233. }
  234. return hPath;
  235. }
  236. void CAssetTreeView::RefreshFileList()
  237. {
  238. // Make sure the expand icons are set correctly
  239. RefreshTreeViewItem( GetRootItemIndex() );
  240. InvalidateLayout();
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Selects a folder
  244. //-----------------------------------------------------------------------------
  245. bool CAssetTreeView::SelectFolder_R( int nItemID, const char *pPath )
  246. {
  247. if ( nItemID < 0 )
  248. return false;
  249. KeyValues *kv = GetItemData( nItemID );
  250. const char *pTestPath = kv->GetString( "path" );
  251. if ( !Q_stricmp( pTestPath, pPath ) )
  252. {
  253. AddSelectedItem( nItemID, true, false, true );
  254. return true;
  255. }
  256. // Substring match..
  257. CUtlString str = pTestPath;
  258. str += '\\';
  259. if ( Q_strnicmp( str, pPath, str.Length() ) )
  260. return false;
  261. ExpandItem( nItemID, true );
  262. int nChildCount = GetNumChildren( nItemID );
  263. for ( int i = 0; i < nChildCount; ++i )
  264. {
  265. int nChildItemID = GetChild( nItemID, i );
  266. if ( SelectFolder_R( nChildItemID, pPath ) )
  267. return true;
  268. }
  269. return false;
  270. }
  271. void CAssetTreeView::SelectFolder( const char *pSubDir, const char *pPath )
  272. {
  273. char pTemp[MAX_PATH];
  274. Q_snprintf( pTemp, sizeof(pTemp), "%s\\%s", pSubDir, pPath );
  275. Q_StripTrailingSlash( pTemp );
  276. int nItem = GetRootItemIndex();
  277. SelectFolder_R( nItem, pTemp );
  278. }
  279. //-----------------------------------------------------------------------------
  280. // setup a smaller font
  281. //-----------------------------------------------------------------------------
  282. void CAssetTreeView::ApplySchemeSettings( IScheme *pScheme )
  283. {
  284. BaseClass::ApplySchemeSettings( pScheme );
  285. SetFont( pScheme->GetFont("DefaultSmall") );
  286. SetFgColor( Color(216, 222, 211, 255) );
  287. }
  288. //-----------------------------------------------------------------------------
  289. //
  290. // Cache of asset data so we don't need to rebuild all the time
  291. //
  292. //-----------------------------------------------------------------------------
  293. DECLARE_POINTER_HANDLE( AssetList_t );
  294. #define ASSET_LIST_INVALID ((AssetList_t)(0xFFFF))
  295. class CAssetCache
  296. {
  297. public:
  298. struct CachedAssetInfo_t
  299. {
  300. CUtlString m_AssetName;
  301. int m_nModIndex;
  302. };
  303. struct ModInfo_t
  304. {
  305. CUtlString m_ModName;
  306. CUtlString m_Path;
  307. };
  308. CAssetCache();
  309. // Mod iteration
  310. int ModCount() const;
  311. const ModInfo_t& ModInfo( int nIndex ) const;
  312. // Building the mod list
  313. void BuildModList();
  314. AssetList_t FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt );
  315. bool BeginAssetScan( AssetList_t hList, bool bForceRescan = false );
  316. CAssetTreeView* GetFileTree( AssetList_t hList );
  317. int GetAssetCount( AssetList_t hList ) const;
  318. const CachedAssetInfo_t& GetAsset( AssetList_t hList, int nIndex ) const;
  319. void AddAsset( AssetList_t hList, const CachedAssetInfo_t& info );
  320. bool ContinueSearchForAssets( AssetList_t hList, float flDuration );
  321. private:
  322. struct DirToCheck_t
  323. {
  324. CUtlString m_DirName;
  325. DirHandle_t m_hDirHandle;
  326. };
  327. struct CachedAssetList_t
  328. {
  329. CachedAssetList_t() {}
  330. CachedAssetList_t( const char *pSearchSubDir, int nExtCount, const char **ppSearchExt ) :
  331. m_pSubDir( pSearchSubDir, Q_strlen( pSearchSubDir ) + 1 )
  332. {
  333. m_Ext.AddMultipleToTail( nExtCount, ppSearchExt );
  334. }
  335. CachedAssetList_t( const CachedAssetList_t& )
  336. {
  337. // Only used during insertion; do nothing
  338. }
  339. CUtlVector< CachedAssetInfo_t > m_AssetList;
  340. CAssetTreeView *m_pFileTree;
  341. CUtlString m_pSubDir;
  342. CUtlVector< const char * > m_Ext;
  343. CUtlLinkedList< DirToCheck_t > m_DirectoriesToCheck;
  344. FileFindHandle_t m_hFind;
  345. bool m_bAssetScanComplete;
  346. };
  347. private:
  348. bool AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hDirHandle, float flStartTime, float flDuration );
  349. bool DoesExtensionMatch( CachedAssetList_t& list, const char *pFileName );
  350. void AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex );
  351. private:
  352. // List of known mods
  353. CUtlVector< ModInfo_t > m_ModList;
  354. // List of cached assets
  355. CUtlRBTree< CachedAssetList_t > m_CachedAssets;
  356. // Have we built the mod list?
  357. bool m_bBuiltModList;
  358. static bool CachedAssetLessFunc( const CachedAssetList_t& src1, const CachedAssetList_t& src2 );
  359. };
  360. //-----------------------------------------------------------------------------
  361. // Static instance of the asset cache
  362. //-----------------------------------------------------------------------------
  363. static CAssetCache s_AssetCache;
  364. //-----------------------------------------------------------------------------
  365. // Map sort func
  366. //-----------------------------------------------------------------------------
  367. bool CAssetCache::CachedAssetLessFunc( const CAssetCache::CachedAssetList_t& src1, const CAssetCache::CachedAssetList_t& src2 )
  368. {
  369. int nRetVal = Q_stricmp( src1.m_pSubDir, src2.m_pSubDir ) > 0;
  370. if ( nRetVal != 0 )
  371. return nRetVal > 0;
  372. int nCount = src1.m_Ext.Count();
  373. int nDiff = nCount - src2.m_Ext.Count();
  374. if ( nDiff != 0 )
  375. return nDiff > 0;
  376. for ( int i = 0; i < nCount; ++i )
  377. {
  378. nRetVal = Q_stricmp( src1.m_Ext[i], src2.m_Ext[i] );
  379. if ( nRetVal != 0 )
  380. return nRetVal > 0;
  381. }
  382. return false;
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Constructor
  386. //-----------------------------------------------------------------------------
  387. CAssetCache::CAssetCache() : m_CachedAssets( 0, 0, CachedAssetLessFunc )
  388. {
  389. m_bBuiltModList = false;
  390. }
  391. //-----------------------------------------------------------------------------
  392. // Mod iteration
  393. //-----------------------------------------------------------------------------
  394. int CAssetCache::ModCount() const
  395. {
  396. return m_ModList.Count();
  397. }
  398. const CAssetCache::ModInfo_t& CAssetCache::ModInfo( int nIndex ) const
  399. {
  400. return m_ModList[nIndex];
  401. }
  402. //-----------------------------------------------------------------------------
  403. // Building the mod list
  404. //-----------------------------------------------------------------------------
  405. void CAssetCache::BuildModList()
  406. {
  407. if ( m_bBuiltModList )
  408. return;
  409. m_bBuiltModList = true;
  410. m_ModList.RemoveAll();
  411. // Add all mods
  412. int nLen = g_pFullFileSystem->GetSearchPath( "GAME", false, NULL, 0 );
  413. char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) );
  414. g_pFullFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen );
  415. char *pPath = pSearchPath;
  416. while( pPath )
  417. {
  418. char *pSemiColon = strchr( pPath, ';' );
  419. if ( pSemiColon )
  420. {
  421. *pSemiColon = 0;
  422. }
  423. Q_StripTrailingSlash( pPath );
  424. Q_FixSlashes( pPath );
  425. char pModName[ MAX_PATH ];
  426. Q_FileBase( pPath, pModName, sizeof( pModName ) );
  427. // Always start in an asset-specific directory
  428. // char pAssetPath[MAX_PATH];
  429. // Q_snprintf( pAssetPath, MAX_PATH, "%s\\%s", pPath, m_pAssetSubDir );
  430. // Q_FixSlashes( pPath );
  431. int i = m_ModList.AddToTail( );
  432. m_ModList[i].m_ModName.Set( pModName );
  433. m_ModList[i].m_Path.Set( pPath );
  434. pPath = pSemiColon ? pSemiColon + 1 : NULL;
  435. }
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Adds an asset to the list of assets of this type
  439. //-----------------------------------------------------------------------------
  440. void CAssetCache::AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex )
  441. {
  442. int i = list.m_AssetList.AddToTail( );
  443. CachedAssetInfo_t& info = list.m_AssetList[i];
  444. info.m_AssetName.Set( pAssetName );
  445. info.m_nModIndex = nModIndex;
  446. }
  447. //-----------------------------------------------------------------------------
  448. // Extension matches?
  449. //-----------------------------------------------------------------------------
  450. bool CAssetCache::DoesExtensionMatch( CachedAssetList_t& info, const char *pFileName )
  451. {
  452. char pChildExt[MAX_PATH];
  453. Q_ExtractFileExtension( pFileName, pChildExt, sizeof(pChildExt) );
  454. // Check the extension matches
  455. int nCount = info.m_Ext.Count();
  456. for ( int i = 0; i < nCount; ++i )
  457. {
  458. if ( !Q_stricmp( info.m_Ext[i], pChildExt ) )
  459. return true;
  460. }
  461. return false;
  462. }
  463. //-----------------------------------------------------------------------------
  464. // Recursively add all files matching the wildcard under this directory
  465. //-----------------------------------------------------------------------------
  466. bool CAssetCache::AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hCurrentDir, float flStartTime, float flDuration )
  467. {
  468. // Indicates no files found
  469. if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE )
  470. return true;
  471. // generate children
  472. // add all the items
  473. int nModCount = m_ModList.Count();
  474. int nSubDirLen = list.m_pSubDir ? Q_strlen(list.m_pSubDir) : 0;
  475. const char *pszFileName = pStartingFile;
  476. while ( pszFileName )
  477. {
  478. char pRelativeChildPath[MAX_PATH];
  479. Q_snprintf( pRelativeChildPath, MAX_PATH, "%s\\%s", pFilePath, pszFileName );
  480. if ( g_pFullFileSystem->FindIsDirectory( list.m_hFind ) )
  481. {
  482. // If .svn is in the name, don't add this directory!!
  483. if ( strstr (pszFileName, ".svn") )
  484. {
  485. pszFileName = g_pFullFileSystem->FindNext( list.m_hFind );
  486. continue;
  487. }
  488. if ( Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) )
  489. {
  490. DirHandle_t hDirHandle = list.m_pFileTree->AddSubDirectory( hCurrentDir, pszFileName );
  491. int i = list.m_DirectoriesToCheck.AddToTail();
  492. list.m_DirectoriesToCheck[i].m_DirName = pRelativeChildPath;
  493. list.m_DirectoriesToCheck[i].m_hDirHandle = hDirHandle;
  494. }
  495. }
  496. else
  497. {
  498. // Check the extension matches
  499. if ( DoesExtensionMatch( list, pszFileName ) )
  500. {
  501. char pFullAssetPath[MAX_PATH];
  502. g_pFullFileSystem->RelativePathToFullPath( pRelativeChildPath, "GAME", pFullAssetPath, sizeof(pFullAssetPath) );
  503. int nModIndex = -1;
  504. for ( int i = 0; i < nModCount; ++i )
  505. {
  506. if ( !Q_strnicmp( pFullAssetPath, m_ModList[i].m_Path, m_ModList[i].m_Path.Length() ) )
  507. {
  508. nModIndex = i;
  509. break;
  510. }
  511. }
  512. if ( nModIndex >= 0 )
  513. {
  514. // Strip 'subdir/' prefix
  515. char *pAssetName = pRelativeChildPath;
  516. if ( list.m_pSubDir )
  517. {
  518. if ( !Q_strnicmp( list.m_pSubDir, pAssetName, nSubDirLen ) )
  519. {
  520. if ( pAssetName[nSubDirLen] == '\\' )
  521. {
  522. pAssetName += nSubDirLen + 1;
  523. }
  524. }
  525. }
  526. strlwr( pAssetName );
  527. AddAssetToList( list, pAssetName, nModIndex );
  528. }
  529. }
  530. }
  531. // Don't let the search go for too long at a time
  532. if ( Plat_FloatTime() - flStartTime >= flDuration )
  533. return false;
  534. pszFileName = g_pFullFileSystem->FindNext( list.m_hFind );
  535. }
  536. return true;
  537. }
  538. //-----------------------------------------------------------------------------
  539. // Recursively add all files matching the wildcard under this directory
  540. //-----------------------------------------------------------------------------
  541. bool CAssetCache::ContinueSearchForAssets( AssetList_t hList, float flDuration )
  542. {
  543. CachedAssetList_t& list = m_CachedAssets[ (int)hList ];
  544. float flStartTime = Plat_FloatTime();
  545. while ( list.m_DirectoriesToCheck.Count() )
  546. {
  547. const char *pFilePath = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_DirName;
  548. DirHandle_t hCurrentDir = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_hDirHandle;
  549. const char *pStartingFile;
  550. if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE )
  551. {
  552. char pSearchString[MAX_PATH];
  553. Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath );
  554. // get the list of files
  555. pStartingFile = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &list.m_hFind );
  556. }
  557. else
  558. {
  559. pStartingFile = g_pFullFileSystem->FindNext( list.m_hFind );
  560. }
  561. if ( !AddFilesInDirectory( list, pStartingFile, pFilePath, hCurrentDir, flStartTime, flDuration ) )
  562. return false;
  563. g_pFullFileSystem->FindClose( list.m_hFind );
  564. list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
  565. list.m_DirectoriesToCheck.Remove( list.m_DirectoriesToCheck.Head() );
  566. }
  567. list.m_bAssetScanComplete = true;
  568. return true;
  569. }
  570. //-----------------------------------------------------------------------------
  571. // Asset cache iteration
  572. //-----------------------------------------------------------------------------
  573. bool CAssetCache::BeginAssetScan( AssetList_t hList, bool bForceRescan )
  574. {
  575. CachedAssetList_t& list = m_CachedAssets[ (int)hList ];
  576. if ( bForceRescan )
  577. {
  578. list.m_bAssetScanComplete = false;
  579. if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE )
  580. {
  581. g_pFullFileSystem->FindClose( list.m_hFind );
  582. list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
  583. }
  584. list.m_DirectoriesToCheck.RemoveAll();
  585. }
  586. if ( list.m_bAssetScanComplete )
  587. return false;
  588. // This case occurs if we stopped the picker previously while in the middle of a scan
  589. if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE )
  590. return true;
  591. list.m_AssetList.RemoveAll();
  592. list.m_pFileTree->ClearDirectories();
  593. // Add all files, determine which mod they are in.
  594. int i = list.m_DirectoriesToCheck.AddToTail();
  595. list.m_DirectoriesToCheck[i].m_DirName = list.m_pSubDir;
  596. list.m_DirectoriesToCheck[i].m_hDirHandle = list.m_pFileTree->GetRootDirectory();
  597. return true;
  598. }
  599. //-----------------------------------------------------------------------------
  600. // Asset cache iteration
  601. //-----------------------------------------------------------------------------
  602. AssetList_t CAssetCache::FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt )
  603. {
  604. CachedAssetList_t search( pSubDir, nExtCount, ppExt );
  605. int nIndex = m_CachedAssets.Find( search );
  606. if ( nIndex == m_CachedAssets.InvalidIndex() )
  607. {
  608. nIndex = m_CachedAssets.Insert( search );
  609. CachedAssetList_t &list = m_CachedAssets[nIndex];
  610. list.m_pSubDir = pSubDir;
  611. list.m_Ext.AddMultipleToTail( nExtCount, ppExt );
  612. list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE;
  613. list.m_bAssetScanComplete = false;
  614. list.m_pFileTree = new CAssetTreeView( NULL, "FolderFilter", pAssetType, pSubDir );
  615. }
  616. return (AssetList_t)nIndex;
  617. }
  618. CAssetTreeView* CAssetCache::GetFileTree( AssetList_t hList )
  619. {
  620. if ( hList == ASSET_LIST_INVALID )
  621. return NULL;
  622. return m_CachedAssets[ (int)hList ].m_pFileTree;
  623. }
  624. int CAssetCache::GetAssetCount( AssetList_t hList ) const
  625. {
  626. if ( hList == ASSET_LIST_INVALID )
  627. return 0;
  628. return m_CachedAssets[ (int)hList ].m_AssetList.Count();
  629. }
  630. const CAssetCache::CachedAssetInfo_t& CAssetCache::GetAsset( AssetList_t hList, int nIndex ) const
  631. {
  632. Assert( nIndex < GetAssetCount(hList) );
  633. return m_CachedAssets[ (int)hList ].m_AssetList[ nIndex ];
  634. }
  635. //-----------------------------------------------------------------------------
  636. //
  637. // Base asset Picker
  638. //
  639. //-----------------------------------------------------------------------------
  640. //-----------------------------------------------------------------------------
  641. // Sort by asset name
  642. //-----------------------------------------------------------------------------
  643. static int __cdecl AssetBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  644. {
  645. bool bRoot1 = item1.kv->GetInt("root") != 0;
  646. bool bRoot2 = item2.kv->GetInt("root") != 0;
  647. if ( bRoot1 != bRoot2 )
  648. return bRoot1 ? -1 : 1;
  649. const char *pString1 = item1.kv->GetString("asset");
  650. const char *pString2 = item2.kv->GetString("asset");
  651. return Q_stricmp( pString1, pString2 );
  652. }
  653. static int __cdecl AssetBrowserModSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  654. {
  655. int nMod1 = item1.kv->GetInt("modIndex", -1);
  656. int nMod2 = item2.kv->GetInt("modIndex", -1);
  657. if ( nMod1 != nMod2 )
  658. return nMod1 - nMod2;
  659. return AssetBrowserSortFunc( pPanel, item1, item2 );
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Purpose: Constructor
  663. //-----------------------------------------------------------------------------
  664. CBaseAssetPicker::CBaseAssetPicker( vgui::Panel *pParent, const char *pAssetType,
  665. const char *pExt, const char *pSubDir, const char *pTextType ) :
  666. BaseClass( pParent, "AssetPicker" )
  667. {
  668. m_bBuiltAssetList = false;
  669. m_pAssetType = pAssetType;
  670. m_pAssetTextType = pTextType;
  671. m_pAssetExt = pExt;
  672. m_pAssetSubDir = pSubDir;
  673. m_bFinishedAssetListScan = false;
  674. m_bFirstAssetScan = false;
  675. m_nMatchingAssets = 0;
  676. m_bSubDirCheck = true;
  677. m_hAssetList = ASSET_LIST_INVALID;
  678. }
  679. //-----------------------------------------------------------------------------
  680. // Purpose: Destructor
  681. //-----------------------------------------------------------------------------
  682. CBaseAssetPicker::~CBaseAssetPicker()
  683. {
  684. SaveUserConfig();
  685. // Detach!
  686. m_pFileTree->RemoveActionSignalTarget( this );
  687. m_pFileTree->SetParent( (Panel*)NULL );
  688. m_pFileTree = NULL;
  689. }
  690. //-----------------------------------------------------------------------------
  691. // Creates standard controls
  692. //-----------------------------------------------------------------------------
  693. void CBaseAssetPicker::CreateStandardControls( vgui::Panel *pParent, bool bAllowMultiselect )
  694. {
  695. int nExtCount = 1 + m_ExtraAssetExt.Count();
  696. const char **ppExt = (const char **)_alloca( nExtCount * sizeof(const char *) );
  697. ppExt[0] = m_pAssetExt;
  698. if ( nExtCount > 1 )
  699. {
  700. memcpy( ppExt + 1, m_ExtraAssetExt.Base(), nExtCount - 1 );
  701. }
  702. m_hAssetList = s_AssetCache.FindAssetList( m_pAssetType, m_pAssetSubDir, nExtCount, ppExt );
  703. m_pAssetSplitter = new vgui::Splitter( pParent, "AssetSplitter", SPLITTER_MODE_HORIZONTAL, 1 );
  704. vgui::Panel *pSplitterTopSide = m_pAssetSplitter->GetChild( 0 );
  705. vgui::Panel *pSplitterBottomSide = m_pAssetSplitter->GetChild( 1 );
  706. // Combo box for mods
  707. m_pModSelector = new ComboBox( pSplitterTopSide, "ModFilter", 5, false );
  708. m_pModSelector->AddActionSignalTarget( this );
  709. // Rescan button
  710. m_pRescanButton = new Button( pSplitterTopSide, "RescanButton", "Rescan", this, "AssetRescan" );
  711. // file browser tree controls
  712. m_pFileTree = s_AssetCache.GetFileTree( m_hAssetList );
  713. m_pFileTree->SetParent( pSplitterTopSide );
  714. m_pFileTree->AddActionSignalTarget( this );
  715. m_pSubDirCheck = new CheckButton( pSplitterTopSide, "SubDirCheck", "Check subfolders for files?" );
  716. m_pSubDirCheck->SetSelected( true );
  717. m_pSubDirCheck->SetEnabled( false );
  718. m_pSubDirCheck->SetVisible( false );
  719. m_pSubDirCheck->AddActionSignalTarget( this );
  720. char pTemp[512];
  721. Q_snprintf( pTemp, sizeof(pTemp), "No .%s files", m_pAssetExt );
  722. m_pAssetBrowser = new vgui::ListPanel( pSplitterBottomSide, "AssetBrowser" );
  723. m_pAssetBrowser->AddColumnHeader( 0, "mod", "Mod", 52, 0 );
  724. m_pAssetBrowser->AddColumnHeader( 1, "asset", m_pAssetType, 128, ListPanel::COLUMN_RESIZEWITHWINDOW );
  725. m_pAssetBrowser->SetSelectIndividualCells( false );
  726. m_pAssetBrowser->SetMultiselectEnabled( bAllowMultiselect );
  727. m_pAssetBrowser->SetEmptyListText( pTemp );
  728. m_pAssetBrowser->SetDragEnabled( true );
  729. m_pAssetBrowser->AddActionSignalTarget( this );
  730. m_pAssetBrowser->SetSortFunc( 0, AssetBrowserModSortFunc );
  731. m_pAssetBrowser->SetSortFunc( 1, AssetBrowserSortFunc );
  732. m_pAssetBrowser->SetSortColumn( 1 );
  733. // filter selection
  734. m_pFilter = new TextEntry( pSplitterBottomSide, "FilterList" );
  735. m_pFilter->AddActionSignalTarget( this );
  736. // full path
  737. m_pFullPath = new TextEntry( pSplitterBottomSide, "FullPath" );
  738. m_pFullPath->SetEnabled( false );
  739. m_pFullPath->SetEditable( false );
  740. m_nCurrentModFilter = -1;
  741. }
  742. //-----------------------------------------------------------------------------
  743. // Reads user config settings
  744. //-----------------------------------------------------------------------------
  745. void CBaseAssetPicker::ApplyUserConfigSettings( KeyValues *pUserConfig )
  746. {
  747. BaseClass::ApplyUserConfigSettings( pUserConfig );
  748. // Populates the mod list names
  749. RefreshAssetList();
  750. const char *pFilter = pUserConfig->GetString( "filter", "" );
  751. m_FolderFilter = pUserConfig->GetString( "folderfilter", "" );
  752. const char *pMod = pUserConfig->GetString( "mod", "" );
  753. SetFilter( pFilter );
  754. m_nCurrentModFilter = -1;
  755. if ( pMod && pMod[0] )
  756. {
  757. int nCount = s_AssetCache.ModCount();
  758. for ( int i = 0; i < nCount; ++i )
  759. {
  760. const CAssetCache::ModInfo_t& modInfo = s_AssetCache.ModInfo( i );
  761. if ( Q_stricmp( pMod, modInfo.m_ModName ) )
  762. continue;
  763. int nItemCount = m_pModSelector->GetItemCount();
  764. for ( int j = 0; j < nItemCount; ++j )
  765. {
  766. int nItemID = m_pModSelector->GetItemIDFromRow( j );
  767. KeyValues *kv = m_pModSelector->GetItemUserData( nItemID );
  768. int nModIndex = kv->GetInt( "mod" );
  769. if ( nModIndex == i )
  770. {
  771. m_nCurrentModFilter = i;
  772. m_pModSelector->ActivateItem( nItemID );
  773. break;
  774. }
  775. }
  776. break;
  777. }
  778. }
  779. }
  780. //-----------------------------------------------------------------------------
  781. // Purpose: returns user config settings for this control
  782. //-----------------------------------------------------------------------------
  783. void CBaseAssetPicker::GetUserConfigSettings( KeyValues *pUserConfig )
  784. {
  785. BaseClass::GetUserConfigSettings( pUserConfig );
  786. pUserConfig->SetString( "filter", m_Filter );
  787. pUserConfig->SetString( "folderfilter", m_FolderFilter );
  788. pUserConfig->SetString( "mod", ( m_nCurrentModFilter >= 0 ) ?
  789. s_AssetCache.ModInfo( m_nCurrentModFilter ).m_ModName : "" );
  790. }
  791. //-----------------------------------------------------------------------------
  792. // Purpose: optimization, return true if this control has any user config settings
  793. //-----------------------------------------------------------------------------
  794. bool CBaseAssetPicker::HasUserConfigSettings()
  795. {
  796. return true;
  797. }
  798. //-----------------------------------------------------------------------------
  799. // Allows the picker to browse multiple asset types
  800. //-----------------------------------------------------------------------------
  801. void CBaseAssetPicker::AddExtension( const char *pExtension )
  802. {
  803. m_ExtraAssetExt.AddToTail( pExtension );
  804. }
  805. //-----------------------------------------------------------------------------
  806. // Is multiselect enabled?
  807. //-----------------------------------------------------------------------------
  808. bool CBaseAssetPicker::IsMultiselectEnabled() const
  809. {
  810. return m_pAssetBrowser->IsMultiselectEnabled();
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Sets the initial selected asset
  814. //-----------------------------------------------------------------------------
  815. void CBaseAssetPicker::SetInitialSelection( const char *pAssetName )
  816. {
  817. // This makes it so the background list filling code will automatically select this asset when it gets to it.
  818. m_SelectedAsset = pAssetName;
  819. if ( pAssetName )
  820. {
  821. // Sometimes we've already refreshed our list with a bunch of cached resources and the item is already in the list,
  822. // so in that case just select it here.
  823. int cnt = m_pAssetBrowser->GetItemCount();
  824. for ( int i=0; i < cnt; i++ )
  825. {
  826. KeyValues *kv = m_pAssetBrowser->GetItem( i );
  827. if ( !kv )
  828. continue;
  829. const char *pTestAssetName = kv->GetString( "asset" );
  830. if ( !pTestAssetName )
  831. continue;
  832. if ( Q_stricmp( pTestAssetName, pAssetName ) == 0 )
  833. {
  834. m_pAssetBrowser->SetSelectedCell( i, 0 );
  835. break;
  836. }
  837. }
  838. }
  839. }
  840. //-----------------------------------------------------------------------------
  841. // Set/get the filter
  842. //-----------------------------------------------------------------------------
  843. void CBaseAssetPicker::SetFilter( const char *pFilter )
  844. {
  845. m_Filter = pFilter;
  846. m_pFilter->SetText( pFilter );
  847. }
  848. const char *CBaseAssetPicker::GetFilter()
  849. {
  850. return m_Filter;
  851. }
  852. //-----------------------------------------------------------------------------
  853. // Purpose: called to open
  854. //-----------------------------------------------------------------------------
  855. void CBaseAssetPicker::Activate()
  856. {
  857. RefreshAssetList();
  858. RequestFilterFocus();
  859. }
  860. //-----------------------------------------------------------------------------
  861. // Purpose:
  862. //-----------------------------------------------------------------------------
  863. void CBaseAssetPicker::OnKeyCodePressed( KeyCode code )
  864. {
  865. if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN ))
  866. {
  867. KeyValues *pMsg = new KeyValues("KeyCodePressed", "code", code);
  868. vgui::ipanel()->SendMessage( m_pAssetBrowser->GetVPanel(), pMsg, GetVPanel());
  869. pMsg->deleteThis();
  870. }
  871. else
  872. {
  873. BaseClass::OnKeyCodePressed( code );
  874. }
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Is a particular asset visible?
  878. //-----------------------------------------------------------------------------
  879. bool CBaseAssetPicker::IsAssetVisible( int nAssetIndex )
  880. {
  881. const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex );
  882. // Filter based on active mod
  883. int nModIndex = info.m_nModIndex;
  884. if ( ( m_nCurrentModFilter >= 0 ) && ( m_nCurrentModFilter != nModIndex ) )
  885. return false;
  886. // Filter based on name
  887. const char *pAssetName = info.m_AssetName;
  888. if ( !Q_strcmp( pAssetName, m_SelectedAsset ) )
  889. return true;
  890. if ( m_Filter.Length() && !Q_stristr( pAssetName, m_Filter.Get() ) )
  891. return false;
  892. // Filter based on folder
  893. if ( m_FolderFilter.Length() && Q_strnicmp( pAssetName, m_FolderFilter.Get(), m_FolderFilter.Length() ) )
  894. return false;
  895. // Filter based on subdirectory check
  896. if ( !m_bSubDirCheck && strchr( pAssetName + m_FolderFilter.Length(), '\\' ) )
  897. return false;
  898. return true;
  899. }
  900. //-----------------------------------------------------------------------------
  901. // Adds an asset from the cache to the list
  902. //-----------------------------------------------------------------------------
  903. void CBaseAssetPicker::AddAssetToList( int nAssetIndex )
  904. {
  905. const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex );
  906. bool bInRootDir = !strchr( info.m_AssetName, '\\' ) && !strchr( info.m_AssetName, '/' );
  907. KeyValues *kv = new KeyValues( "node", "asset", info.m_AssetName );
  908. kv->SetString( "mod", s_AssetCache.ModInfo( info.m_nModIndex ).m_ModName );
  909. kv->SetInt( "modIndex", info.m_nModIndex );
  910. kv->SetInt( "root", bInRootDir );
  911. int nItemID = m_pAssetBrowser->AddItem( kv, 0, false, false );
  912. kv->deleteThis();
  913. if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 && !Q_strcmp( m_SelectedAsset, info.m_AssetName ) )
  914. {
  915. m_pAssetBrowser->SetSelectedCell( nItemID, 0 );
  916. }
  917. KeyValues *pDrag = new KeyValues( "drag", "text", info.m_AssetName );
  918. if ( m_pAssetTextType )
  919. {
  920. pDrag->SetString( "texttype", m_pAssetTextType );
  921. }
  922. m_pAssetBrowser->SetItemDragData( nItemID, pDrag );
  923. int i = m_AssetList.AddToTail( );
  924. m_AssetList[i].m_nAssetIndex = nAssetIndex;
  925. m_AssetList[i].m_nItemId = nItemID;
  926. bool bIsVisible = IsAssetVisible( i );
  927. m_pAssetBrowser->SetItemVisible( nItemID, bIsVisible );
  928. if ( bIsVisible )
  929. {
  930. ++m_nMatchingAssets;
  931. }
  932. }
  933. //-----------------------------------------------------------------------------
  934. // Continues to build the asset list
  935. //-----------------------------------------------------------------------------
  936. void CBaseAssetPicker::OnTick()
  937. {
  938. BaseClass::OnTick();
  939. int nPreAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );
  940. // Stop getting called back once all assets have been found
  941. float flTime = m_bFirstAssetScan ? ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME : ASSET_LIST_DIRECTORY_SEARCH_TIME;
  942. bool bFinished = s_AssetCache.ContinueSearchForAssets( m_hAssetList, flTime );
  943. if ( m_bFirstAssetScan )
  944. {
  945. m_pFileTree->OpenRoot();
  946. }
  947. m_bFirstAssetScan = false;
  948. int nPostAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );
  949. for ( int i = nPreAssetCount; i < nPostAssetCount; ++i )
  950. {
  951. AddAssetToList( i );
  952. }
  953. if ( bFinished )
  954. {
  955. vgui::ivgui()->RemoveTickSignal( GetVPanel() );
  956. m_bFinishedAssetListScan = true;
  957. // Copy the current folder filter.. this is necessary
  958. // to finally select the folder loaded from the user config settings
  959. // in the free view (since it's finally populated at this point)
  960. // NOTE: if a user has changed the folder filter between startup
  961. // and this point, this should still work since m_FolderFilter should be updated
  962. m_pFileTree->SelectFolder( m_pAssetSubDir, m_FolderFilter );
  963. RefreshAssetList( );
  964. return;
  965. }
  966. UpdateAssetColumnHeader();
  967. }
  968. //-----------------------------------------------------------------------------
  969. // Builds the Bsp name list
  970. //-----------------------------------------------------------------------------
  971. void CBaseAssetPicker::BuildAssetNameList( )
  972. {
  973. if ( m_bBuiltAssetList )
  974. return;
  975. m_bBuiltAssetList = true;
  976. m_nMatchingAssets = 0;
  977. m_nCurrentModFilter = -1;
  978. // Build the list of known mods if we haven't
  979. s_AssetCache.BuildModList();
  980. m_pModSelector->RemoveAll();
  981. m_pModSelector->AddItem( "All Mods", new KeyValues( "Mod", "mod", -1 ) );
  982. int nModCount = s_AssetCache.ModCount();
  983. for ( int i = 0; i < nModCount; ++i )
  984. {
  985. const char *pModName = s_AssetCache.ModInfo( i ).m_ModName;
  986. m_pModSelector->AddItem( pModName, new KeyValues( "Mod", "mod", i ) );
  987. }
  988. m_pModSelector->ActivateItemByRow( 0 );
  989. // If we've already read in
  990. if ( s_AssetCache.BeginAssetScan( m_hAssetList ) )
  991. {
  992. m_bFirstAssetScan = true;
  993. m_bFinishedAssetListScan = false;
  994. vgui::ivgui()->AddTickSignal( GetVPanel(), 10 );
  995. }
  996. else
  997. {
  998. m_bFirstAssetScan = false;
  999. m_bFinishedAssetListScan = true;
  1000. }
  1001. int nAssetCount = s_AssetCache.GetAssetCount( m_hAssetList );
  1002. for ( int i = 0; i < nAssetCount; ++i )
  1003. {
  1004. AddAssetToList( i );
  1005. }
  1006. }
  1007. //-----------------------------------------------------------------------------
  1008. // Rescan assets
  1009. //-----------------------------------------------------------------------------
  1010. void CBaseAssetPicker::RescanAssets()
  1011. {
  1012. m_pAssetBrowser->RemoveAll();
  1013. m_AssetList.RemoveAll();
  1014. s_AssetCache.BeginAssetScan( m_hAssetList, true );
  1015. m_bFirstAssetScan = true;
  1016. m_nMatchingAssets = 0;
  1017. if ( m_bFinishedAssetListScan )
  1018. {
  1019. m_bFinishedAssetListScan = false;
  1020. vgui::ivgui()->AddTickSignal( GetVPanel(), 10 );
  1021. }
  1022. }
  1023. //-----------------------------------------------------------------------------
  1024. // Returns the mod path to the item index
  1025. //-----------------------------------------------------------------------------
  1026. const char *CBaseAssetPicker::GetModPath( int nModIndex )
  1027. {
  1028. return s_AssetCache.ModInfo( nModIndex ).m_Path.Get();
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Command handler
  1032. //-----------------------------------------------------------------------------
  1033. void CBaseAssetPicker::OnCommand( const char *pCommand )
  1034. {
  1035. if ( !Q_stricmp( pCommand, "AssetRescan" ) )
  1036. {
  1037. RescanAssets();
  1038. return;
  1039. }
  1040. BaseClass::OnCommand( pCommand );
  1041. }
  1042. //-----------------------------------------------------------------------------
  1043. // Update column headers
  1044. //-----------------------------------------------------------------------------
  1045. void CBaseAssetPicker::UpdateAssetColumnHeader( )
  1046. {
  1047. char pColumnTitle[512];
  1048. Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)%s",
  1049. m_pAssetType, m_nMatchingAssets, m_AssetList.Count(), m_bFinishedAssetListScan ? "" : " ..." );
  1050. m_pAssetBrowser->SetColumnHeaderText( 1, pColumnTitle );
  1051. }
  1052. //-----------------------------------------------------------------------------
  1053. // Request focus of the filter box
  1054. //-----------------------------------------------------------------------------
  1055. void CBaseAssetPicker::RequestFilterFocus()
  1056. {
  1057. if ( m_Filter.Length() )
  1058. {
  1059. m_pFilter->SelectAllOnFirstFocus( true );
  1060. }
  1061. m_pFilter->RequestFocus();
  1062. }
  1063. //-----------------------------------------------------------------------------
  1064. // Purpose: refreshes the asset list
  1065. //-----------------------------------------------------------------------------
  1066. void CBaseAssetPicker::RefreshAssetList( )
  1067. {
  1068. BuildAssetNameList();
  1069. // Check the filter matches
  1070. int nCount = m_AssetList.Count();
  1071. m_nMatchingAssets = 0;
  1072. for ( int i = 0; i < nCount; ++i )
  1073. {
  1074. // Filter based on active mod
  1075. bool bIsVisible = IsAssetVisible( i );
  1076. m_pAssetBrowser->SetItemVisible( m_AssetList[i].m_nItemId, bIsVisible );
  1077. if ( bIsVisible )
  1078. {
  1079. ++m_nMatchingAssets;
  1080. }
  1081. }
  1082. UpdateAssetColumnHeader();
  1083. m_pAssetBrowser->SortList();
  1084. if ( ( m_pAssetBrowser->GetSelectedItemsCount() == 0 ) && ( m_pAssetBrowser->GetItemCount() > 0 ) )
  1085. {
  1086. // Invoke a callback if the next selection will be a 'default' selection
  1087. OnNextSelectionIsDefault();
  1088. int nItemID = m_pAssetBrowser->GetItemIDFromRow( 0 );
  1089. m_pAssetBrowser->SetSelectedCell( nItemID, 0 );
  1090. }
  1091. m_pFileTree->RefreshFileList();
  1092. }
  1093. //-----------------------------------------------------------------------------
  1094. // Purpose: refreshes dialog on file folder changing
  1095. //-----------------------------------------------------------------------------
  1096. void CBaseAssetPicker::OnFileSelected()
  1097. {
  1098. // update list
  1099. const char *pFolderFilter = "";
  1100. int iItem = m_pFileTree->GetFirstSelectedItem();
  1101. if ( iItem >= 0 )
  1102. {
  1103. KeyValues *pkv = m_pFileTree->GetItemData( iItem );
  1104. pFolderFilter = pkv->GetString( "path" );
  1105. // The first keys are always the subdir
  1106. pFolderFilter += Q_strlen( m_pAssetSubDir );
  1107. if ( *pFolderFilter )
  1108. {
  1109. ++pFolderFilter;
  1110. }
  1111. }
  1112. if ( Q_stricmp( pFolderFilter, m_FolderFilter.Get() ) )
  1113. {
  1114. int nLen = Q_strlen( pFolderFilter );
  1115. m_FolderFilter = pFolderFilter;
  1116. if ( nLen > 0 )
  1117. {
  1118. m_FolderFilter += '\\';
  1119. }
  1120. RefreshAssetList();
  1121. }
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. // Purpose: refreshes dialog on text changing
  1125. //-----------------------------------------------------------------------------
  1126. void CBaseAssetPicker::OnTextChanged( KeyValues *pKeyValues )
  1127. {
  1128. vgui::Panel *pSource = (vgui::Panel*)pKeyValues->GetPtr( "panel" );
  1129. if ( pSource == m_pFilter )
  1130. {
  1131. int nLength = m_pFilter->GetTextLength();
  1132. char *pNewFilter = (char*)_alloca( (nLength+1) * sizeof(char) );
  1133. if ( nLength > 0 )
  1134. {
  1135. m_pFilter->GetText( pNewFilter, nLength+1 );
  1136. }
  1137. else
  1138. {
  1139. pNewFilter[0] = 0;
  1140. }
  1141. if ( Q_stricmp( pNewFilter, m_Filter.Get() ) )
  1142. {
  1143. m_Filter.SetLength( nLength );
  1144. m_Filter = pNewFilter;
  1145. RefreshAssetList();
  1146. }
  1147. return;
  1148. }
  1149. if ( pSource == m_pModSelector )
  1150. {
  1151. KeyValues *pKeyValuesActive = m_pModSelector->GetActiveItemUserData();
  1152. if ( pKeyValuesActive )
  1153. {
  1154. m_nCurrentModFilter = pKeyValuesActive->GetInt( "mod", -1 );
  1155. RefreshAssetList();
  1156. }
  1157. return;
  1158. }
  1159. }
  1160. //-----------------------------------------------------------------------------
  1161. // Purpose: Updates preview when an item is selected
  1162. //-----------------------------------------------------------------------------
  1163. void CBaseAssetPicker::OnItemSelected( KeyValues *kv )
  1164. {
  1165. Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL );
  1166. if ( pPanel == m_pAssetBrowser )
  1167. {
  1168. int nCount = GetSelectedAssetCount();
  1169. Assert( nCount > 0 );
  1170. const char *pSelectedAsset = GetSelectedAsset( nCount - 1 );
  1171. // Fill in the full path
  1172. int nModIndex = GetSelectedAssetModIndex();
  1173. char pBuf[MAX_PATH];
  1174. Q_snprintf( pBuf, sizeof(pBuf), "%s\\%s\\%s",
  1175. s_AssetCache.ModInfo( nModIndex ).m_Path.Get(), m_pAssetSubDir, pSelectedAsset );
  1176. Q_FixSlashes( pBuf );
  1177. m_pFullPath->SetText( pBuf );
  1178. surface()->SetCursor( dc_waitarrow );
  1179. OnSelectedAssetPicked( pSelectedAsset );
  1180. return;
  1181. }
  1182. }
  1183. void CBaseAssetPicker::OnCheckButtonChecked( KeyValues *kv )
  1184. {
  1185. vgui::Panel *pSource = (vgui::Panel*)kv->GetPtr( "panel" );
  1186. if ( pSource == m_pSubDirCheck )
  1187. {
  1188. m_bSubDirCheck = m_pSubDirCheck->IsSelected();
  1189. RefreshAssetList();
  1190. }
  1191. }
  1192. //-----------------------------------------------------------------------------
  1193. // Returns the selceted asset count
  1194. //-----------------------------------------------------------------------------
  1195. int CBaseAssetPicker::GetSelectedAssetCount()
  1196. {
  1197. return m_pAssetBrowser->GetSelectedItemsCount();
  1198. }
  1199. //-----------------------------------------------------------------------------
  1200. // Returns the selceted asset name
  1201. //-----------------------------------------------------------------------------
  1202. const char *CBaseAssetPicker::GetSelectedAsset( int nAssetIndex )
  1203. {
  1204. int nSelectedAssetCount = m_pAssetBrowser->GetSelectedItemsCount();
  1205. if ( nAssetIndex < 0 )
  1206. {
  1207. nAssetIndex = nSelectedAssetCount - 1;
  1208. }
  1209. if ( nSelectedAssetCount <= nAssetIndex || nAssetIndex < 0 )
  1210. return NULL;
  1211. int nIndex = m_pAssetBrowser->GetSelectedItem( nAssetIndex );
  1212. KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex );
  1213. return pItemKeyValues->GetString( "asset" );
  1214. }
  1215. //-----------------------------------------------------------------------------
  1216. // Returns the selceted asset mod index
  1217. //-----------------------------------------------------------------------------
  1218. int CBaseAssetPicker::GetSelectedAssetModIndex( )
  1219. {
  1220. if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 )
  1221. return 0;
  1222. int nIndex = m_pAssetBrowser->GetSelectedItem( 0 );
  1223. KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex );
  1224. return pItemKeyValues->GetInt( "modIndex" );
  1225. }
  1226. //-----------------------------------------------------------------------------
  1227. //
  1228. // Purpose: Modal picker frame
  1229. //
  1230. //-----------------------------------------------------------------------------
  1231. CBaseAssetPickerFrame::CBaseAssetPickerFrame( vgui::Panel *pParent ) :
  1232. BaseClass( pParent, "AssetPickerFrame" )
  1233. {
  1234. m_pContextKeyValues = NULL;
  1235. SetDeleteSelfOnClose( true );
  1236. m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" );
  1237. m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" );
  1238. SetBlockDragChaining( true );
  1239. }
  1240. CBaseAssetPickerFrame::~CBaseAssetPickerFrame()
  1241. {
  1242. CleanUpMessage();
  1243. }
  1244. //-----------------------------------------------------------------------------
  1245. // Allows the derived class to create the picker
  1246. //-----------------------------------------------------------------------------
  1247. void CBaseAssetPickerFrame::SetAssetPicker( CBaseAssetPicker* pPicker )
  1248. {
  1249. m_pPicker = pPicker;
  1250. m_pPicker->AddActionSignalTarget( this );
  1251. }
  1252. //-----------------------------------------------------------------------------
  1253. // Deletes the message
  1254. //-----------------------------------------------------------------------------
  1255. void CBaseAssetPickerFrame::CleanUpMessage()
  1256. {
  1257. if ( m_pContextKeyValues )
  1258. {
  1259. m_pContextKeyValues->deleteThis();
  1260. m_pContextKeyValues = NULL;
  1261. }
  1262. }
  1263. //-----------------------------------------------------------------------------
  1264. // Sets the initial selected asset
  1265. //-----------------------------------------------------------------------------
  1266. void CBaseAssetPickerFrame::SetInitialSelection( const char *pAssetName )
  1267. {
  1268. m_pPicker->SetInitialSelection( pAssetName );
  1269. }
  1270. //-----------------------------------------------------------------------------
  1271. // Set/get the filter
  1272. //-----------------------------------------------------------------------------
  1273. void CBaseAssetPickerFrame::SetFilter( const char *pFilter )
  1274. {
  1275. m_pPicker->SetFilter( pFilter );
  1276. }
  1277. const char *CBaseAssetPickerFrame::GetFilter()
  1278. {
  1279. return m_pPicker->GetFilter( );
  1280. }
  1281. //-----------------------------------------------------------------------------
  1282. // Purpose: Activate the dialog
  1283. //-----------------------------------------------------------------------------
  1284. void CBaseAssetPickerFrame::DoModal( KeyValues *pKeyValues )
  1285. {
  1286. BaseClass::DoModal();
  1287. CleanUpMessage();
  1288. m_pContextKeyValues = pKeyValues;
  1289. m_pPicker->Activate();
  1290. }
  1291. //-----------------------------------------------------------------------------
  1292. // Posts a message (passing the key values)
  1293. //-----------------------------------------------------------------------------
  1294. void CBaseAssetPickerFrame::PostMessageAndClose( KeyValues *pKeyValues )
  1295. {
  1296. if ( m_pContextKeyValues )
  1297. {
  1298. pKeyValues->AddSubKey( m_pContextKeyValues );
  1299. m_pContextKeyValues = NULL;
  1300. }
  1301. CloseModal();
  1302. PostActionSignal( pKeyValues );
  1303. }
  1304. //-----------------------------------------------------------------------------
  1305. // On command
  1306. //-----------------------------------------------------------------------------
  1307. void CBaseAssetPickerFrame::OnCommand( const char *pCommand )
  1308. {
  1309. if ( !Q_stricmp( pCommand, "Open" ) )
  1310. {
  1311. KeyValues *pActionKeys = new KeyValues( "AssetSelected" );
  1312. if ( !m_pPicker->IsMultiselectEnabled() )
  1313. {
  1314. const char *pAssetName = m_pPicker->GetSelectedAsset( );
  1315. pActionKeys->SetString( "asset", pAssetName );
  1316. }
  1317. else
  1318. {
  1319. char pBuf[512];
  1320. KeyValues *pAssetKeys = pActionKeys->FindKey( "assets", true );
  1321. int nCount = m_pPicker->GetSelectedAssetCount();
  1322. for ( int i = 0; i < nCount; ++i )
  1323. {
  1324. Q_snprintf( pBuf, sizeof(pBuf), "asset%d", i );
  1325. pAssetKeys->SetString( pBuf, m_pPicker->GetSelectedAsset( i ) );
  1326. }
  1327. }
  1328. PostMessageAndClose( pActionKeys );
  1329. return;
  1330. }
  1331. if ( !Q_stricmp( pCommand, "Cancel" ) )
  1332. {
  1333. CloseModal();
  1334. return;
  1335. }
  1336. BaseClass::OnCommand( pCommand );
  1337. }