source-engine/utils/hlfaceposer/vcdbrowser.cpp

882 lines
18 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include <windows.h>
#include "resource.h"
#include "vcdbrowser.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "filesystem.h"
#include "tabwindow.h"
#include "inputproperties.h"
#include "choreowidgetdrawhelper.h"
#include "UtlBuffer.h"
#include "ChoreoEvent.h"
#include "ChoreoView.h"
CVCDBrowser *g_pVCDBrowser = NULL;
enum
{
// Controls
IDC_VB_LISTVIEW = 101,
IDC_VB_FILETREE,
// Messages
IDC_VB_OPENVCD = 1000,
};
enum
{
COL_VCD = 0,
};
class CVCDList : public mxListView
{
public:
CVCDList( mxWindow *parent, int id = 0 )
: mxListView( parent, 0, 0, 0, 0, id )
{
// Add column headers
insertTextColumn( COL_VCD, 700, "VCD" );
}
};
class CUtlSymbolTree : public mxTreeView
{
public:
CUtlSymbolTree( mxWindow *parent, int id = 0 ) : mxTreeView( parent, 0, 0, 0, 0, id ),
m_Paths( 0, 0, FileTreeLessFunc )
{
}
void Clear()
{
removeAll();
m_Paths.RemoveAll();
}
void FindOrAddSubdirectory( char const *subdir )
{
FileTreePath fp;
Q_strcpy( fp.path, subdir );
if ( m_Paths.Find( fp ) != m_Paths.InvalidIndex() )
return;
m_Paths.Insert( fp );
}
mxTreeViewItem *FindOrAddChildItem( mxTreeViewItem *parent, char const *child )
{
mxTreeViewItem *p = getFirstChild( parent );
if ( !p )
{
return add( parent, child );
}
while ( p )
{
if ( !Q_stricmp( getLabel( p ), child ) )
return p;
p = getNextChild( p );
}
return add( parent, child );
}
void _PopulateTree( int pathId, char const *path )
{
char sz[ 512 ];
Q_strcpy( sz, path );
char *p = sz;
// Start at root
mxTreeViewItem *cur = NULL;
// Tokenize path
while ( p && p[0] )
{
char *slash = Q_strstr( p, "/" );
if ( !slash )
{
slash = Q_strstr( p, "\\" );
}
char *check = p;
if ( slash )
{
*slash = 0;
// see if a child of current already exists with this name
p = slash + 1;
}
else
{
p = NULL;
}
Assert( check );
cur = FindOrAddChildItem( cur, check );
}
setUserData( cur, (void *)pathId );
}
char const *GetSelectedPath( void )
{
mxTreeViewItem *tvi = getSelectedItem();
unsigned int id = (unsigned int)getUserData( tvi );
if ( id < 0 || id >= m_Paths.Count() )
{
Assert( 0 );
return "";
}
return m_Paths[ id ].path;
}
void PopulateTree()
{
int i;
for ( i = m_Paths.FirstInorder(); i != m_Paths.InvalidIndex(); i = m_Paths.NextInorder( i ) )
{
_PopulateTree( i, m_Paths[ i ].path );
}
mxTreeViewItem *p = getFirstChild( NULL );
setOpen( p, true );
}
struct FileTreePath
{
char path[ MAX_PATH ];
};
static bool FileTreeLessFunc( const FileTreePath &lhs, const FileTreePath &rhs )
{
return Q_stricmp( lhs.path, rhs.path ) < 0;
}
CUtlRBTree< FileTreePath, int > m_Paths;
};
#pragma optimize( "", off )
class CVCDOptionsWindow : public mxWindow
{
typedef mxWindow BaseClass;
public:
enum
{
IDC_OPENFILE = 1000,
IDC_SEARCH,
IDC_CANCELSEARCH,
};
CVCDOptionsWindow( CVCDBrowser *browser ) : BaseClass( browser, 0, 0, 0, 0 ), m_pBrowser( browser )
{
FacePoser_AddWindowStyle( this, WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
m_szSearchString[0]=0;
m_pOpen = new mxButton( this, 0, 0, 0, 0, "Open", IDC_OPENFILE );
m_pSearch = new mxLineEdit( this, 0, 0, 0, 0, "", IDC_SEARCH );
m_pCancelSearch = new mxButton( this, 0, 0, 0, 0, "Cancel", IDC_CANCELSEARCH );
}
bool PaintBackground( void )
{
redraw();
return false;
}
virtual void redraw()
{
CChoreoWidgetDrawHelper drawHelper( this, GetSysColor( COLOR_BTNFACE ) );
}
virtual int handleEvent( mxEvent *event )
{
int iret = 0;
switch ( event->event )
{
default:
break;
case mxEvent::Size:
{
iret = 1;
int split = 120;
int x = 1;
m_pOpen->setBounds( x, 1, split, h2() - 2 );
x += split + 10;
m_pCancelSearch->setBounds( x, 1, split, h2() - 2 );
x += split + 10;
m_pSearch->setBounds( x, 0, split * 3, h2() - 1 );
x += split * 3 + 10;
}
break;
case mxEvent::KeyDown:
switch ( event->action )
{
default:
break;
case IDC_SEARCH:
{
if ( event->event == mxEvent::KeyDown )
{
OnSearch();
}
iret = 1;
};
break;
}
break;
case mxEvent::Action:
{
switch ( event->action )
{
case IDC_SEARCH:
iret = 1;
break;
case IDC_OPENFILE:
{
iret = 1;
m_pBrowser->OnOpen();
}
break;
case IDC_CANCELSEARCH:
{
iret = 1;
OnCancelSearch();
}
break;
default:
break;
}
}
break;
}
return iret;
}
char const *GetSearchString()
{
return m_szSearchString;
}
void OnSearch()
{
m_pSearch->getText( m_szSearchString, sizeof( m_szSearchString ) );
m_pBrowser->OnSearch();
}
void OnCancelSearch()
{
m_szSearchString[ 0 ] = 0;
m_pSearch->clear();
m_pBrowser->OnCancelSearch();
}
private:
mxButton *m_pOpen;
mxLineEdit *m_pSearch;
mxButton *m_pCancelSearch;
CVCDBrowser *m_pBrowser;
char m_szSearchString[ 256 ];
};
#pragma optimize( "", on )
//-----------------------------------------------------------------------------
// Purpose:
// Input : *parent -
//-----------------------------------------------------------------------------
CVCDBrowser::CVCDBrowser( mxWindow *parent )
: IFacePoserToolWindow( "VCDBrowser", "VCDs" ), mxWindow( parent, 0, 0, 0, 0 )
{
SetAutoProcess( false );
m_bTextSearch = false;
m_nPrevProcessed = -1;
m_pListView = new CVCDList( this, IDC_VB_LISTVIEW );
m_pOptions = new CVCDOptionsWindow( this );
m_pFileTree = new CUtlSymbolTree( this, IDC_VB_FILETREE );
//HIMAGELIST list = CreateImageList();
// Associate the image list with the tree-view control.
//m_pListView->setImageList( (void *)list );
LoadAllSounds();
PopulateTree( NULL );
}
#define CX_ICON 16
#define CY_ICON 16
HIMAGELIST CVCDBrowser::CreateImageList()
{
HIMAGELIST list;
list = ImageList_Create( CX_ICON, CY_ICON,
FALSE, VCD_NUM_IMAGES, 0 );
// Load the icon resources, and add the icons to the image list.
HICON hicon;
int slot;
#if defined( DBGFLAG_ASSERT )
int c = 0;
#endif
hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_VCD));
slot = ImageList_AddIcon(list, hicon);
Assert( slot == c++ );
DeleteObject( hicon );
return list;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CVCDBrowser::OnDelete()
{
RemoveAllSounds();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *event -
// Output : int
//-----------------------------------------------------------------------------
int CVCDBrowser::handleEvent( mxEvent *event )
{
int iret = 0;
if ( HandleToolEvent( event ) )
{
return iret;
}
switch ( event->event )
{
default:
break;
case mxEvent::Action:
{
iret = 1;
switch ( event->action )
{
default:
{
iret = 0;
}
break;
case IDC_VB_LISTVIEW:
{
SetActiveTool( this );
bool rightmouse = ( event->flags == mxEvent::RightClicked ) ? true : false;
bool doubleclicked = ( event->flags == mxEvent::DoubleClicked ) ? true : false;
if ( rightmouse )
{
ShowContextMenu();
}
else if ( doubleclicked )
{
if ( m_pListView->getNumSelected() == 1 )
{
int index = m_pListView->getNextSelectedItem( -1 );
if ( index >= 0 )
{
FileNameHandle_t vcd = (FileNameHandle_t)m_pListView->getUserData( index, 0 );
OpenVCD( vcd );
}
}
}
}
break;
case IDC_VB_FILETREE:
{
SetActiveTool( this );
PopulateTree( m_pFileTree->GetSelectedPath() );
}
break;
case IDC_VB_OPENVCD:
{
OnOpen();
}
break;
}
}
break;
case mxEvent::Size:
{
int optionsh = 20;
m_pOptions->setBounds( 0, 0, w2(), optionsh );
int filetreewidth = 175;
m_pFileTree->setBounds( 0, optionsh, filetreewidth, h2() - optionsh );
m_pListView->setBounds( filetreewidth, optionsh, w2() - filetreewidth, h2() - optionsh );
iret = 1;
}
break;
case mxEvent::Close:
{
iret = 1;
}
break;
}
return iret;
}
bool CVCDBrowser::CNameLessFunc::Less( const FileNameHandle_t &name1, const FileNameHandle_t &name2, void *pContext )
{
if ( name1 < name2 )
return true;
return false;
}
void CVCDBrowser::OpenVCD( const FileNameHandle_t& handle )
{
char fn[ 512 ];
if ( filesystem->String( handle, fn, sizeof( fn ) ) )
{
char pFullPath[MAX_PATH];
const char *pFileName = filesystem->RelativePathToFullPath( fn, "GAME", pFullPath, sizeof(pFullPath) );
if ( !pFileName )
{
pFileName = fn;
}
g_pChoreoView->LoadSceneFromFile( pFileName );
}
}
#define SCENES_PREFIX_LEN 0
//-----------------------------------------------------------------------------
// Finds all .vcd files in a particular directory
//-----------------------------------------------------------------------------
bool CVCDBrowser::LoadVCDsFilesInDirectory( CUtlSortVector< FileNameHandle_t, CNameLessFunc >& soundlist, char const* pDirectoryName, int nDirectoryNameLen )
{
char *pWildCard;
pWildCard = ( char * )stackalloc( nDirectoryNameLen + 7 );
Q_snprintf( pWildCard, nDirectoryNameLen + 7, "%s/*.vcd", pDirectoryName );
if ( !filesystem )
{
return false;
}
FileFindHandle_t findHandle;
const char *pFileName = filesystem->FindFirst( pWildCard, &findHandle );
while( pFileName )
{
if( !filesystem->FindIsDirectory( findHandle ) )
{
// Strip off the 'sound/' part of the name.
char *pFileNameWithPath;
int nAllocSize = nDirectoryNameLen + Q_strlen(pFileName) + 2;
pFileNameWithPath = (char *)stackalloc( nAllocSize );
Q_snprintf( pFileNameWithPath, nAllocSize, "%s/%s", &pDirectoryName[ SCENES_PREFIX_LEN ], pFileName );
Q_strnlwr( pFileNameWithPath, nAllocSize );
FileNameHandle_t vcd;
vcd = filesystem->FindOrAddFileName( pFileNameWithPath );
soundlist.InsertNoSort( vcd );
}
pFileName = filesystem->FindNext( findHandle );
}
m_pFileTree->FindOrAddSubdirectory( &pDirectoryName[ SCENES_PREFIX_LEN ] );
filesystem->FindClose( findHandle );
return true;
}
bool CVCDBrowser::InitDirectoryRecursive( CUtlSortVector< FileNameHandle_t, CNameLessFunc >& soundlist, char const* pDirectoryName )
{
// Compute directory name length
int nDirectoryNameLen = Q_strlen( pDirectoryName );
if (!LoadVCDsFilesInDirectory( soundlist, pDirectoryName, nDirectoryNameLen ) )
return false;
char *pWildCard = ( char * )stackalloc( nDirectoryNameLen + 4 );
strcpy(pWildCard, pDirectoryName);
strcat(pWildCard, "/*.");
int nPathStrLen = nDirectoryNameLen + 1;
FileFindHandle_t findHandle;
const char *pFileName = filesystem->FindFirst( pWildCard, &findHandle );
while( pFileName )
{
if ((pFileName[0] != '.') || (pFileName[1] != '.' && pFileName[1] != 0))
{
if( filesystem->FindIsDirectory( findHandle ) )
{
int fileNameStrLen = Q_strlen( pFileName );
char *pFileNameWithPath = ( char * )stackalloc( nPathStrLen + fileNameStrLen + 1 );
memcpy( pFileNameWithPath, pWildCard, nPathStrLen );
pFileNameWithPath[nPathStrLen] = '\0';
strcat( pFileNameWithPath, pFileName );
if (!InitDirectoryRecursive( soundlist, pFileNameWithPath ))
return false;
}
}
pFileName = filesystem->FindNext( findHandle );
}
return true;
}
void CVCDBrowser::LoadAllSounds()
{
RemoveAllSounds();
Con_Printf( "Building list of all .vcds in sound/ folder\n" );
InitDirectoryRecursive( m_AllVCDs, "scenes" );
m_AllVCDs.RedoSort();
m_pFileTree->PopulateTree();
}
void CVCDBrowser::RemoveAllSounds()
{
m_AllVCDs.Purge();
m_Scripts.RemoveAll();
m_CurrentSelection.RemoveAll();
m_pFileTree->Clear();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CVCDBrowser::PopulateTree( char const *subdirectory )
{
char subdir[ 512 ];
subdir[ 0 ] = 0;
int i;
CUtlSortVector< FileNameHandle_t, CNameLessFunc > sorted( 0, 0 );
char const *texttofind = NULL;
if ( m_bTextSearch )
{
subdirectory = NULL;
texttofind = GetSearchString();
}
int len = 0;
if ( subdirectory )
{
len = Q_strlen( subdirectory );
Q_strncpy( subdir, subdirectory, sizeof( subdir ) );
Q_strlower( subdir );
Q_FixSlashes( subdir );
}
int c = m_AllVCDs.Count();
for ( i = 0; i < c; i++ )
{
const FileNameHandle_t &vcd = m_AllVCDs[ i ];
char name[ 512 ];
if ( !filesystem->String( vcd, name, sizeof( name ) ) )
continue;
if ( subdirectory )
{
if ( Q_strnicmp( subdir, name, len ) )
continue;
}
if ( m_bTextSearch && texttofind )
{
if ( !Q_stristr( name, texttofind ) )
continue;
}
sorted.InsertNoSort( vcd );
}
sorted.RedoSort();
char prevSelectedName[ 512 ];
prevSelectedName[ 0 ] = 0;
if ( m_pListView->getNumSelected() == 1 )
{
int selectedItem = m_pListView->getNextSelectedItem( 0 );
if ( selectedItem >= 0 )
{
// Grab name of previously selected item
Q_strcpy( prevSelectedName, m_pListView->getLabel( selectedItem, 0 ) );
}
}
// Repopulate tree
m_pListView->removeAll();
int loadcount = 0;
m_pListView->setDrawingEnabled( false );
int selectedSlot = -1;
for ( i = 0; i < sorted.Count(); ++i )
{
const FileNameHandle_t &vcd = sorted[ i ];
char name[ 512 ];
if ( !filesystem->String( vcd, name, sizeof( name ) ) )
continue;
int slot = m_pListView->add( name );
m_pListView->setUserData( slot, COL_VCD, (void *)vcd );
if ( !Q_stricmp( prevSelectedName, name ) )
{
selectedSlot = slot;
}
++loadcount;
}
m_pListView->setDrawingEnabled( true );
if ( selectedSlot != -1 )
{
m_pListView->setSelected( selectedSlot, true );
m_pListView->scrollToItem( selectedSlot );
}
}
void CVCDBrowser::RepopulateTree()
{
PopulateTree( m_pFileTree->GetSelectedPath() );
}
void CVCDBrowser::BuildSelectionList( CUtlVector< FileNameHandle_t >& selected )
{
selected.RemoveAll();
int idx = -1;
do
{
idx = m_pListView->getNextSelectedItem( idx );
if ( idx != -1 )
{
FileNameHandle_t vcd = (FileNameHandle_t)m_pListView->getUserData( idx, 0 );
selected.AddToTail( vcd );
}
} while ( idx != -1 );
}
void CVCDBrowser::ShowContextMenu( void )
{
SetActiveTool( this );
BuildSelectionList( m_CurrentSelection );
if ( m_CurrentSelection.Count() <= 0 )
return;
POINT pt;
GetCursorPos( &pt );
ScreenToClient( (HWND)getHandle(), &pt );
mxPopupMenu *pop = new mxPopupMenu();
if ( m_CurrentSelection.Count() == 1 && m_CurrentSelection[ 0 ] )
{
char sz[ 512 ];
char name[ 512 ];
if ( filesystem->String( m_CurrentSelection[ 0 ], name, sizeof( name ) ) )
{
Q_snprintf( sz, sizeof( sz ), "&Open '%s'", name );
pop->add ( sz, IDC_VB_OPENVCD );
}
}
pop->popup( this, pt.x, pt.y );
}
void CVCDBrowser::OnOpen()
{
SetActiveTool( this );
BuildSelectionList( m_CurrentSelection );
if ( m_CurrentSelection.Count() == 1 )
{
FileNameHandle_t& vcd = m_CurrentSelection[ 0 ];
OpenVCD( vcd );
}
}
static void SplitFileName( char const *in, char *path, int maxpath, char *filename, int maxfilename )
{
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
_splitpath( in, drive, dir, fname, ext );
if ( dir[0] )
{
Q_snprintf( path, maxpath, "\\%s", dir );
}
else
{
path[0] = 0;
}
Q_snprintf( filename, maxfilename, "%s%s", fname, ext );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *se -
//-----------------------------------------------------------------------------
void CVCDBrowser::JumpToItem( const FileNameHandle_t& vcd )
{
SetActiveTool( this );
char path[ 256 ];
char filename[ 256 ];
char vcdfile[ 512 ];
if ( !filesystem->String( vcd, vcdfile, sizeof( vcdfile ) ) )
return;
SplitFileName( vcdfile, path, sizeof( path ), filename, sizeof( filename ) );
char *usepath = path + Q_strlen( "/scenes/" );
PopulateTree( usepath );
int idx = 0;
int c = m_pListView->getItemCount();
for ( ; idx < c; idx++ )
{
FileNameHandle_t item = (FileNameHandle_t)m_pListView->getUserData( idx, 0 );
if ( item == vcd )
{
break;
}
}
if ( idx < c )
{
m_pListView->scrollToItem( idx );
}
}
int CVCDBrowser::GetVCDCount() const
{
return m_AllVCDs.Count();
}
FileNameHandle_t CVCDBrowser::GetVCD( int index )
{
if ( index < 0 || index >= (int)m_AllVCDs.Count() )
return NULL;
return m_AllVCDs[ index ];
}
void CVCDBrowser::OnSearch()
{
if ( !GetSearchString()[ 0 ] )
{
OnCancelSearch();
return;
}
SetActiveTool( this );
m_bTextSearch = true;
PopulateTree( GetSearchString());
}
void CVCDBrowser::OnCancelSearch()
{
SetActiveTool( this );
m_bTextSearch = false;
PopulateTree( m_pFileTree->GetSelectedPath() );
}
char const *CVCDBrowser::GetSearchString()
{
return m_pOptions->GetSearchString();
}
void CVCDBrowser::SetCurrent( char const *filename )
{
// Get sound name and look up .vcd from it
char const *p = filename;
if ( p &&
( !Q_strnicmp( p, "sound/", 6 ) || !Q_strnicmp( p, "sound\\", 6 ) ) )
{
p += 6;
}
char fn[ 512 ];
Q_strncpy( fn, p, sizeof( fn ) );
Q_FixSlashes( fn );
int i;
int c = m_pListView->getItemCount();
for ( i = 0; i < c; ++i )
{
FileNameHandle_t vcd = (FileNameHandle_t)( m_pListView->getUserData( i, COL_VCD ) );
char fixed[ 512 ];
if ( !filesystem->String( vcd, fixed, sizeof( fixed ) ) )
continue;
Q_FixSlashes( fixed );
if ( !Q_stricmp( fixed, fn ) )
{
m_pListView->scrollToItem( i );
m_pListView->setSelected( i, true );
}
else
{
m_pListView->setSelected( i, false );
}
}
}