source-engine/vgui2/matsys_controls/baseassetpicker.cpp

1626 lines
49 KiB
C++

//========= 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() = default;
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[ (intp)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[ (intp)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)(intp)nIndex;
}
CAssetTreeView* CAssetCache::GetFileTree( AssetList_t hList )
{
if ( hList == ASSET_LIST_INVALID )
return NULL;
return m_CachedAssets[ (intp)hList ].m_pFileTree;
}
int CAssetCache::GetAssetCount( AssetList_t hList ) const
{
if ( hList == ASSET_LIST_INVALID )
return 0;
return m_CachedAssets[ (intp)hList ].m_AssetList.Count();
}
const CAssetCache::CachedAssetInfo_t& CAssetCache::GetAsset( AssetList_t hList, int nIndex ) const
{
Assert( nIndex < GetAssetCount(hList) );
return m_CachedAssets[ (intp)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 );
}