Counter Strike : Global Offensive Source Code
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.

609 lines
19 KiB

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