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.

602 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include <windows.h>
  7. #undef PropertySheet
  8. #include "filesystem.h"
  9. #include "dme_controls/soundpicker.h"
  10. #include "tier1/KeyValues.h"
  11. #include "vgui_controls/ListPanel.h"
  12. #include "vgui_controls/Button.h"
  13. #include "vgui_controls/PropertySheet.h"
  14. #include "vgui_controls/PropertyPage.h"
  15. #include "dme_controls/filtercombobox.h"
  16. #include "vgui/ISurface.h"
  17. #include "vgui/iinput.h"
  18. #include "dme_controls/dmecontrols.h"
  19. #include "soundemittersystem/isoundemittersystembase.h"
  20. #include "mathlib/mathlib.h"
  21. // FIXME: Move sound code out of the engine + into a library!
  22. #include "toolframework/ienginetool.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. using namespace vgui;
  26. //-----------------------------------------------------------------------------
  27. //
  28. // Sound Picker
  29. //
  30. //-----------------------------------------------------------------------------
  31. //-----------------------------------------------------------------------------
  32. // Sort by sound name
  33. //-----------------------------------------------------------------------------
  34. static int __cdecl GameSoundSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  35. {
  36. bool bRoot1 = item1.kv->GetInt("root") != 0;
  37. bool bRoot2 = item2.kv->GetInt("root") != 0;
  38. if ( bRoot1 != bRoot2 )
  39. return bRoot1 ? -1 : 1;
  40. const char *string1 = item1.kv->GetString("gamesound");
  41. const char *string2 = item2.kv->GetString("gamesound");
  42. return Q_stricmp( string1, string2 );
  43. }
  44. //-----------------------------------------------------------------------------
  45. // Purpose: Constructor
  46. //-----------------------------------------------------------------------------
  47. CSoundPicker::CSoundPicker( vgui::Panel *pParent, int nFlags ) :
  48. BaseClass( pParent, "Sound Files", "wav", "sound", "wavName" )
  49. {
  50. m_nSoundSuppressionCount = 0;
  51. m_nPlayingSound = 0;
  52. // Connection problem if this failed
  53. Assert( SoundEmitterSystem() );
  54. m_pViewsSheet = new vgui::PropertySheet( this, "ViewsSheet" );
  55. m_pViewsSheet->AddActionSignalTarget( this );
  56. // game sounds
  57. m_pGameSoundPage = NULL;
  58. m_pGameSoundList = NULL;
  59. if ( nFlags & PICK_GAMESOUNDS )
  60. {
  61. m_pGameSoundPage = new PropertyPage( m_pViewsSheet, "GameSoundPage" );
  62. m_pGameSoundList = new ListPanel( m_pGameSoundPage, "GameSoundsList" );
  63. m_pGameSoundList->AddColumnHeader( 0, "GameSound", "Game Sound", 52, 0 );
  64. m_pGameSoundList->AddActionSignalTarget( this );
  65. m_pGameSoundList->SetSelectIndividualCells( true );
  66. m_pGameSoundList->SetEmptyListText("No game sounds");
  67. m_pGameSoundList->SetDragEnabled( true );
  68. m_pGameSoundList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 );
  69. m_pGameSoundList->SetSortFunc( 0, GameSoundSortFunc );
  70. m_pGameSoundList->SetSortColumn( 0 );
  71. m_pGameSoundList->SetMultiselectEnabled( ( nFlags & ALLOW_MULTISELECT ) != 0 );
  72. // filter selection
  73. m_pGameSoundFilter = new TextEntry( m_pGameSoundPage, "GameSoundFilter" );
  74. m_pGameSoundFilter->AddActionSignalTarget( this );
  75. m_pGameSoundPage->LoadControlSettings( "resource/soundpickergamesoundpage.res" );
  76. m_pViewsSheet->AddPage( m_pGameSoundPage, "Game Sounds" );
  77. }
  78. // wav files
  79. m_pWavPage = NULL;
  80. if ( nFlags & PICK_WAVFILES )
  81. {
  82. m_pWavPage = new PropertyPage( m_pViewsSheet, "WavPage" );
  83. bool bAllowMultiselect = ( nFlags & ALLOW_MULTISELECT ) != 0;
  84. CreateStandardControls( m_pWavPage, bAllowMultiselect );
  85. AddExtension( "mp3" );
  86. m_pWavPage->LoadControlSettings( "resource/soundpickerwavpage.res" );
  87. m_pViewsSheet->AddPage( m_pWavPage, "WAVs" );
  88. }
  89. LoadControlSettings( "resource/soundpicker.res" );
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose: Destructor
  93. //-----------------------------------------------------------------------------
  94. CSoundPicker::~CSoundPicker()
  95. {
  96. StopSoundPreview();
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose: called to open
  100. //-----------------------------------------------------------------------------
  101. void CSoundPicker::Activate()
  102. {
  103. BaseClass::Activate();
  104. if ( m_pGameSoundPage )
  105. {
  106. BuildGameSoundList();
  107. }
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Sets the current sound choice
  111. //-----------------------------------------------------------------------------
  112. void CSoundPicker::SetSelectedSound( PickType_t type, const char *pSoundName )
  113. {
  114. if ( type == PICK_NONE || !pSoundName )
  115. return;
  116. if ( m_pGameSoundPage && ( type == PICK_GAMESOUNDS ) )
  117. {
  118. m_pViewsSheet->SetActivePage( m_pGameSoundPage );
  119. m_pGameSoundFilter->SetText( pSoundName );
  120. }
  121. if ( m_pWavPage && ( type == PICK_WAVFILES ) )
  122. {
  123. m_pViewsSheet->SetActivePage( m_pWavPage );
  124. SetInitialSelection( pSoundName );
  125. }
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Purpose:
  129. //-----------------------------------------------------------------------------
  130. void CSoundPicker::OnKeyCodePressed( KeyCode code )
  131. {
  132. if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) )
  133. {
  134. if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN ))
  135. {
  136. KeyValues *pMsg = new KeyValues( "KeyCodePressed", "code", code );
  137. vgui::ipanel()->SendMessage( m_pGameSoundList->GetVPanel(), pMsg, GetVPanel() );
  138. pMsg->deleteThis();
  139. return;
  140. }
  141. }
  142. BaseClass::OnKeyCodePressed( code );
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Purpose: builds the gamesound list
  146. //-----------------------------------------------------------------------------
  147. bool CSoundPicker::IsGameSoundVisible( int hGameSound )
  148. {
  149. const char *pSoundName = SoundEmitterSystem()->GetSoundName( hGameSound );
  150. return ( !m_GameSoundFilter.Length() || Q_stristr( pSoundName, m_GameSoundFilter.Get() ) );
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Updates the column header in the chooser
  154. //-----------------------------------------------------------------------------
  155. void CSoundPicker::UpdateGameSoundColumnHeader( int nMatchCount, int nTotalCount )
  156. {
  157. char pColumnTitle[512];
  158. Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)",
  159. "Game Sound", nMatchCount, nTotalCount );
  160. m_pGameSoundList->SetColumnHeaderText( 0, pColumnTitle );
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Purpose: builds the gamesound list
  164. //-----------------------------------------------------------------------------
  165. void CSoundPicker::BuildGameSoundList()
  166. {
  167. if ( !m_pGameSoundList )
  168. return;
  169. m_pGameSoundList->RemoveAll();
  170. int nTotalCount = 0;
  171. int i = SoundEmitterSystem()->First();
  172. while ( i != SoundEmitterSystem()->InvalidIndex() )
  173. {
  174. const char *pSoundName = SoundEmitterSystem()->GetSoundName( i );
  175. bool bInRoot = !strchr( pSoundName, '\\' ) && !strchr( pSoundName, '/' );
  176. KeyValues *kv = new KeyValues( "node", "gamesound", pSoundName );
  177. kv->SetInt( "gameSoundHandle", i );
  178. kv->SetInt( "root", bInRoot );
  179. int nItemID = m_pGameSoundList->AddItem( kv, 0, false, false );
  180. m_pGameSoundList->SetItemVisible( nItemID, IsGameSoundVisible( i ) );
  181. KeyValues *pDrag = new KeyValues( "drag", "text", pSoundName );
  182. pDrag->SetString( "texttype", "gamesoundName" );
  183. m_pGameSoundList->SetItemDragData( nItemID, pDrag );
  184. ++nTotalCount;
  185. i = SoundEmitterSystem()->Next( i );
  186. }
  187. m_pGameSoundList->SortList();
  188. if ( m_pGameSoundList->GetItemCount() > 0 )
  189. {
  190. int nItemID = m_pGameSoundList->GetItemIDFromRow( 0 );
  191. // This prevents the refreshing of the sound list from playing the sound
  192. ++m_nSoundSuppressionCount;
  193. m_pGameSoundList->SetSelectedCell( nItemID, 0 );
  194. }
  195. UpdateGameSoundColumnHeader( nTotalCount, nTotalCount );
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose: refreshes the gamesound list
  199. //-----------------------------------------------------------------------------
  200. void CSoundPicker::RefreshGameSoundList()
  201. {
  202. if ( !m_pGameSoundList )
  203. return;
  204. // Check the filter matches
  205. int nMatchingGameSounds = 0;
  206. int nTotalCount = 0;
  207. for ( int nItemID = m_pGameSoundList->FirstItem(); nItemID != m_pGameSoundList->InvalidItemID(); nItemID = m_pGameSoundList->NextItem( nItemID ) )
  208. {
  209. KeyValues *kv = m_pGameSoundList->GetItem( nItemID );
  210. int hGameSound = kv->GetInt( "gameSoundHandle", SoundEmitterSystem()->InvalidIndex() );
  211. if ( hGameSound == SoundEmitterSystem()->InvalidIndex() )
  212. continue;
  213. bool bIsVisible = IsGameSoundVisible( hGameSound );
  214. m_pGameSoundList->SetItemVisible( nItemID, bIsVisible );
  215. if ( bIsVisible )
  216. {
  217. ++nMatchingGameSounds;
  218. }
  219. ++nTotalCount;
  220. }
  221. UpdateGameSoundColumnHeader( nMatchingGameSounds, nTotalCount );
  222. if ( ( m_pGameSoundList->GetSelectedItemsCount() == 0 ) && ( m_pGameSoundList->GetItemCount() > 0 ) )
  223. {
  224. int nItemID = m_pGameSoundList->GetItemIDFromRow( 0 );
  225. // This prevents the refreshing of the sound list from playing the sound
  226. ++m_nSoundSuppressionCount;
  227. m_pGameSoundList->SetSelectedCell( nItemID, 0 );
  228. }
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: Update filter when text changes
  232. //-----------------------------------------------------------------------------
  233. void CSoundPicker::OnGameSoundFilterTextChanged( )
  234. {
  235. int nLength = m_pGameSoundFilter->GetTextLength();
  236. m_GameSoundFilter.SetLength( nLength );
  237. if ( nLength > 0 )
  238. {
  239. m_pGameSoundFilter->GetText( m_GameSoundFilter.GetForModify(), nLength+1 );
  240. }
  241. RefreshGameSoundList();
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose: refreshes dialog on filter changing
  245. //-----------------------------------------------------------------------------
  246. void CSoundPicker::OnTextChanged( KeyValues *pKeyValues )
  247. {
  248. vgui::Panel *pSource = (vgui::Panel*)pKeyValues->GetPtr( "panel" );
  249. if ( pSource == m_pGameSoundFilter )
  250. {
  251. OnGameSoundFilterTextChanged();
  252. return;
  253. }
  254. BaseClass::OnTextChanged( pKeyValues );
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: Called when a page is shown
  258. //-----------------------------------------------------------------------------
  259. void CSoundPicker::RequestGameSoundFilterFocus( )
  260. {
  261. m_pGameSoundFilter->SelectAllOnFirstFocus( true );
  262. m_pGameSoundFilter->RequestFocus();
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose: Called when a page is shown
  266. //-----------------------------------------------------------------------------
  267. void CSoundPicker::OnPageChanged( )
  268. {
  269. StopSoundPreview();
  270. if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) )
  271. {
  272. RequestGameSoundFilterFocus();
  273. }
  274. if ( m_pWavPage && ( m_pViewsSheet->GetActivePage() == m_pWavPage ) )
  275. {
  276. RequestFilterFocus();
  277. }
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Stop sound preview
  281. //-----------------------------------------------------------------------------
  282. void CSoundPicker::StopSoundPreview( )
  283. {
  284. if ( m_nPlayingSound != 0 )
  285. {
  286. EngineTool()->StopSoundByGuid( m_nPlayingSound );
  287. m_nPlayingSound = 0;
  288. }
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Plays a gamesound
  292. //-----------------------------------------------------------------------------
  293. void CSoundPicker::PlayGameSound( const char *pSoundName )
  294. {
  295. StopSoundPreview();
  296. CSoundParameters params;
  297. if ( SoundEmitterSystem()->GetParametersForSound( pSoundName, params, GENDER_NONE ) )
  298. {
  299. m_nPlayingSound = EngineTool()->StartSound( 0, true, -1, CHAN_STATIC, params.soundname,
  300. params.volume, params.soundlevel, vec3_origin, vec3_origin, 0,
  301. params.pitch, false, params.delay_msec / 1000.0f );
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Plays a wav file
  306. //-----------------------------------------------------------------------------
  307. void CSoundPicker::PlayWavSound( const char *pSoundName )
  308. {
  309. StopSoundPreview();
  310. m_nPlayingSound = EngineTool()->StartSound( 0, true, -1, CHAN_STATIC, pSoundName,
  311. VOL_NORM, SNDLVL_NONE, vec3_origin, vec3_origin, 0, PITCH_NORM, false, 0 );
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Don't play a sound when the next selection is a default selection
  315. //-----------------------------------------------------------------------------
  316. void CSoundPicker::OnNextSelectionIsDefault()
  317. {
  318. ++m_nSoundSuppressionCount;
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Derived classes have this called when the previewed asset changes
  322. //-----------------------------------------------------------------------------
  323. void CSoundPicker::OnSelectedAssetPicked( const char *pAssetName )
  324. {
  325. bool bPlaySounds = true;
  326. if ( m_nSoundSuppressionCount > 0 )
  327. {
  328. --m_nSoundSuppressionCount;
  329. bPlaySounds = false;
  330. }
  331. if ( pAssetName && bPlaySounds )
  332. {
  333. PlayWavSound( pAssetName );
  334. }
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Purpose: refreshes dialog on text changing
  338. //-----------------------------------------------------------------------------
  339. void CSoundPicker::OnItemSelected( KeyValues *kv )
  340. {
  341. Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL );
  342. if ( m_pGameSoundList && (pPanel == m_pGameSoundList ) )
  343. {
  344. bool bPlaySounds = true;
  345. if ( m_nSoundSuppressionCount > 0 )
  346. {
  347. --m_nSoundSuppressionCount;
  348. bPlaySounds = false;
  349. }
  350. const char *pGameSoundName = GetSelectedSoundName();
  351. if ( pGameSoundName && bPlaySounds )
  352. {
  353. int len = V_strlen( pGameSoundName );
  354. char *soundname = ( char* )stackalloc( len + 2 );
  355. soundname[ 0 ] = '#'; // mark sound to bypass the dsp
  356. V_strncpy( soundname + 1, pGameSoundName, len + 1 );
  357. PlayGameSound( soundname );
  358. }
  359. return;
  360. }
  361. BaseClass::OnItemSelected( kv );
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Gets the selected sound type
  365. //-----------------------------------------------------------------------------
  366. CSoundPicker::PickType_t CSoundPicker::GetSelectedSoundType( )
  367. {
  368. if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) )
  369. return PICK_GAMESOUNDS;
  370. if ( m_pWavPage && ( m_pViewsSheet->GetActivePage() == m_pWavPage ) )
  371. return PICK_WAVFILES;
  372. return PICK_NONE;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Returns the selected sound count
  376. //-----------------------------------------------------------------------------
  377. int CSoundPicker::GetSelectedSoundCount()
  378. {
  379. if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) )
  380. return m_pGameSoundList->GetSelectedItemsCount();
  381. if ( m_pWavPage && ( m_pViewsSheet->GetActivePage() == m_pWavPage ) )
  382. return GetSelectedAssetCount();
  383. return 0;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Returns the selected sound
  387. //-----------------------------------------------------------------------------
  388. const char *CSoundPicker::GetSelectedSoundName( int nSelectionIndex )
  389. {
  390. if ( m_pGameSoundPage && ( m_pViewsSheet->GetActivePage() == m_pGameSoundPage ) )
  391. {
  392. int nCount = m_pGameSoundList->GetSelectedItemsCount();
  393. if ( nCount == 0 )
  394. return NULL;
  395. if ( nSelectionIndex < 0 )
  396. {
  397. nSelectionIndex = nCount - 1;
  398. }
  399. int nIndex = m_pGameSoundList->GetSelectedItem( nSelectionIndex );
  400. if ( nIndex >= 0 )
  401. {
  402. KeyValues *pkv = m_pGameSoundList->GetItem( nIndex );
  403. return pkv->GetString( "gamesound", NULL );
  404. }
  405. return NULL;
  406. }
  407. if ( m_pWavPage && ( m_pViewsSheet->GetActivePage() == m_pWavPage ) )
  408. return GetSelectedAsset( nSelectionIndex );
  409. return NULL;
  410. }
  411. //-----------------------------------------------------------------------------
  412. //
  413. // Purpose: Modal picker frame
  414. //
  415. //-----------------------------------------------------------------------------
  416. CSoundPickerFrame::CSoundPickerFrame( vgui::Panel *pParent, const char *pTitle, int nFlags ) :
  417. BaseClass( pParent )
  418. {
  419. SetAssetPicker( new CSoundPicker( this, nFlags ) );
  420. LoadControlSettingsAndUserConfig( "resource/soundpickerframe.res" );
  421. SetTitle( pTitle, false );
  422. }
  423. CSoundPickerFrame::~CSoundPickerFrame()
  424. {
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Purpose: Activate the dialog
  428. //-----------------------------------------------------------------------------
  429. void CSoundPickerFrame::DoModal( CSoundPicker::PickType_t initialType, const char *pInitialValue, KeyValues *pContextKeyValues )
  430. {
  431. vgui::surface()->SetCursor( dc_hourglass );
  432. CSoundPicker *pPicker = static_cast <CSoundPicker*>( GetAssetPicker() );
  433. if ( initialType != CSoundPicker::PICK_NONE && pInitialValue )
  434. {
  435. pPicker->SetSelectedSound( initialType, pInitialValue );
  436. }
  437. BaseClass::DoModal( pContextKeyValues );
  438. }
  439. //-----------------------------------------------------------------------------
  440. // On command
  441. //-----------------------------------------------------------------------------
  442. void CSoundPickerFrame::OnCommand( const char *pCommand )
  443. {
  444. CSoundPicker *pPicker = static_cast <CSoundPicker*>( GetAssetPicker() );
  445. if ( !Q_stricmp( pCommand, "Open" ) )
  446. {
  447. CSoundPicker::PickType_t type = pPicker->GetSelectedSoundType( );
  448. if (( type == CSoundPicker::PICK_GAMESOUNDS ) || ( type == CSoundPicker::PICK_WAVFILES ))
  449. {
  450. const char *pSoundName = pPicker->GetSelectedSoundName();
  451. int len = V_strlen( pSoundName );
  452. char *soundname = ( char* )stackalloc( len + 2 );
  453. soundname[ 0 ] = '#'; // mark sound to bypass the dsp
  454. V_strncpy( soundname + 1, pSoundName, len + 1 );
  455. int nSoundCount = pPicker->GetSelectedSoundCount();
  456. KeyValues *pActionKeys = new KeyValues( "SoundSelected" );
  457. pActionKeys->SetInt( "count", nSoundCount );
  458. KeyValues *pSoundList = NULL;
  459. if ( type == CSoundPicker::PICK_GAMESOUNDS )
  460. {
  461. pActionKeys->SetString( "gamesound", soundname );
  462. if ( pPicker->IsMultiselectEnabled() )
  463. {
  464. pSoundList = pActionKeys->FindKey( "gamesounds", true );
  465. }
  466. }
  467. else
  468. {
  469. pActionKeys->SetString( "wav", soundname );
  470. if ( pPicker->IsMultiselectEnabled() )
  471. {
  472. pSoundList = pActionKeys->FindKey( "wavs", true );
  473. }
  474. }
  475. if ( pSoundList )
  476. {
  477. // Adds them in selection order
  478. for ( int i = 0; i < nSoundCount; ++i )
  479. {
  480. char pBuf[32];
  481. Q_snprintf( pBuf, sizeof(pBuf), "%d", i );
  482. pSoundName = pPicker->GetSelectedSoundName( i );
  483. len = V_strlen( pSoundName );
  484. soundname = ( char* )malloc( len + 2 );
  485. soundname[ 0 ] = '#'; // mark sound to bypass the dsp
  486. V_strncpy( soundname + 1, pSoundName, len + 1 );
  487. pSoundList->SetString( pBuf, soundname );
  488. free( soundname );
  489. }
  490. }
  491. PostMessageAndClose( pActionKeys );
  492. CloseModal();
  493. }
  494. return;
  495. }
  496. BaseClass::OnCommand( pCommand );
  497. }