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.

1950 lines
59 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #if defined( REPLAY_ENABLED )
  8. #include "replaybrowserdetailspanel.h"
  9. #include "replaybrowsermainpanel.h"
  10. #include "replaybrowseritemmanager.h"
  11. #include "replaybrowsermovieplayerpanel.h"
  12. #include "replaybrowserrenderdialog.h"
  13. #include "vgui/IVGui.h"
  14. #include "vgui/ISurface.h"
  15. #include "vgui/IInput.h"
  16. #include "vgui/ILocalize.h"
  17. #include "vgui_controls/FileOpenDialog.h"
  18. #include "vgui_controls/PanelListPanel.h"
  19. #include "vgui_controls/ScrollBar.h"
  20. #include "vgui_controls/ScrollBarSlider.h"
  21. #include "vgui_controls/TextEntry.h"
  22. #include "vgui_controls/TextImage.h"
  23. #include "vgui_avatarimage.h"
  24. #include "gamestringpool.h"
  25. #include "replay/genericclassbased_replay.h"
  26. #include "replaybrowserlistitempanel.h"
  27. #include "confirm_dialog.h"
  28. #include "replay/ireplaymoviemanager.h"
  29. #include "replay/ireplaymanager.h"
  30. #include "replay/ireplayrenderqueue.h"
  31. #include "replay/screenshot.h"
  32. #include "replay/ireplayperformancemanager.h"
  33. #include "replay/performance.h"
  34. #include "vgui/ISystem.h"
  35. #include "youtubeapi.h"
  36. #include "replay/replayyoutubeapi.h"
  37. #include "ienginevgui.h"
  38. #include <filesystem.h>
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include <tier0/memdbgon.h>
  41. //-----------------------------------------------------------------------------
  42. extern IClientReplayContext *g_pClientReplayContext;
  43. extern IReplayMovieManager *g_pReplayMovieManager;
  44. extern IReplayPerformanceManager *g_pReplayPerformanceManager;
  45. //-----------------------------------------------------------------------------
  46. ConVar replay_movie_reveal_warning( "replay_movie_reveal_warning", "1", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_HIDDEN | FCVAR_DONTRECORD );
  47. ConVar replay_movie_export_last_dir( "replay_movie_export_last_dir", "", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_HIDDEN | FCVAR_DONTRECORD );
  48. ConVar replay_renderqueue_first_add( "replay_renderqueue_first_add", "0", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_HIDDEN | FCVAR_DONTRECORD );
  49. //-----------------------------------------------------------------------------
  50. using namespace vgui;
  51. //-----------------------------------------------------------------------------
  52. class CConfirmDisconnectFromServerDialog : public CConfirmDialog
  53. {
  54. DECLARE_CLASS_SIMPLE( CConfirmDisconnectFromServerDialog, CConfirmDialog );
  55. public:
  56. CConfirmDisconnectFromServerDialog( Panel *pParent )
  57. : BaseClass( pParent )
  58. {
  59. surface()->PlaySound( "replay\\replaydialog_warn.wav" );
  60. }
  61. const wchar_t *GetText() { return g_pVGuiLocalize->Find( "#Replay_ConfirmDisconnectFromServer" ); }
  62. void ApplySchemeSettings( vgui::IScheme *pScheme )
  63. {
  64. BaseClass::ApplySchemeSettings( pScheme );
  65. SetTall( YRES( 170 ) );
  66. }
  67. };
  68. //-----------------------------------------------------------------------------
  69. CKeyValueLabelPanel::CKeyValueLabelPanel( Panel *pParent, const char *pKey, const char *pValue )
  70. : EditablePanel( pParent, "KeyValueLabelPanel" )
  71. {
  72. SetScheme( "ClientScheme" );
  73. m_pLabels[ 0 ] = new CExLabel( this, "KeyLabel", pKey );
  74. m_pLabels[ 1 ] = new CExLabel( this, "ValueLabel", pValue );
  75. }
  76. CKeyValueLabelPanel::CKeyValueLabelPanel( Panel *pParent, const char *pKey, const wchar_t *pValue )
  77. : EditablePanel( pParent, "KeyValueLabelPanel" )
  78. {
  79. SetScheme( "ClientScheme" );
  80. m_pLabels[ 0 ] = new CExLabel( this, "KeyLabel", pKey );
  81. m_pLabels[ 1 ] = new CExLabel( this, "ValueLabel", pValue );
  82. }
  83. void CKeyValueLabelPanel::ApplySchemeSettings( IScheme *pScheme )
  84. {
  85. BaseClass::ApplySchemeSettings( pScheme );
  86. HFont hFont = scheme()->GetIScheme( GetScheme() )->GetFont( "ReplayBrowserSmallest", true );
  87. m_pLabels[ 0 ]->SetFont( hFont );
  88. m_pLabels[ 1 ]->SetFont( hFont );
  89. m_pLabels[ 0 ]->SetFgColor( Color( 119, 107, 95, 255 ) );
  90. m_pLabels[ 1 ]->SetFgColor( Color( 255, 255, 255, 255 ) );
  91. }
  92. void CKeyValueLabelPanel::PerformLayout()
  93. {
  94. BaseClass::PerformLayout();
  95. int nContentWidth, nContentHeight;
  96. m_pLabels[0]->GetContentSize( nContentWidth, nContentHeight );
  97. int iMidX = GetParent()->GetWide() * 0.55f;
  98. m_pLabels[0]->SetBounds( 0, 0, iMidX, nContentHeight );
  99. m_pLabels[1]->GetContentSize( nContentWidth, nContentHeight );
  100. m_pLabels[1]->SetBounds( iMidX, 0, iMidX, nContentHeight );
  101. }
  102. int CKeyValueLabelPanel::GetHeight() const
  103. {
  104. int nWidth, nHeight;
  105. m_pLabels[ 0 ]->GetContentSize( nWidth, nHeight );
  106. return nHeight;
  107. }
  108. int CKeyValueLabelPanel::GetValueHeight() const
  109. {
  110. int nWidth, nHeight;
  111. m_pLabels[ 1 ]->GetContentSize( nWidth, nHeight );
  112. return nHeight;
  113. }
  114. void CKeyValueLabelPanel::SetValue( const wchar_t *pValue )
  115. {
  116. m_pLabels[ 1 ]->SetText( pValue );
  117. InvalidateLayout();
  118. }
  119. //-----------------------------------------------------------------------------
  120. CBaseDetailsPanel::CBaseDetailsPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay )
  121. : EditablePanel( pParent, pName ),
  122. m_hReplay( hReplay ),
  123. m_bShouldShow( true )
  124. {
  125. SetScheme( "ClientScheme" );
  126. m_pInsetPanel = new EditablePanel( this, "InsetPanel" );
  127. }
  128. void CBaseDetailsPanel::ApplySchemeSettings( IScheme *pScheme )
  129. {
  130. BaseClass::ApplySchemeSettings( pScheme );
  131. SetBorder( pScheme->GetBorder( "ReplayStatsBorder" ) );
  132. SetBgColor( Color( 0,0,0, 255 ) );
  133. }
  134. void CBaseDetailsPanel::PerformLayout()
  135. {
  136. BaseClass::PerformLayout();
  137. // Setup inset panel bounds
  138. const int n = GetMarginSize();
  139. m_pInsetPanel->SetBounds( n, n, GetWide() - 2*n, GetTall() - 2*n );
  140. }
  141. //-----------------------------------------------------------------------------
  142. CRecordsPanel::CRecordsPanel( Panel *pParent, ReplayHandle_t hReplay )
  143. : CBaseDetailsPanel( pParent, "RecordsPanel", hReplay )
  144. {
  145. m_pClassImage = new ImagePanel( this, "ClassImage" );
  146. }
  147. void CRecordsPanel::ApplySchemeSettings( IScheme *pScheme )
  148. {
  149. BaseClass::ApplySchemeSettings( pScheme );
  150. SetBorder( pScheme->GetBorder( "ReplayDefaultBorder" ) );
  151. SetBgColor( Color( 0,0,0,0 ) );
  152. }
  153. void CRecordsPanel::PerformLayout()
  154. {
  155. BaseClass::PerformLayout();
  156. // Figure out the class image name
  157. char szImage[MAX_OSPATH];
  158. const CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  159. V_snprintf( szImage, sizeof( szImage ), "class_sel_sm_%s_%s", pReplay->GetMaterialFriendlyPlayerClass(), pReplay->GetPlayerTeam() ); // Cause default image to display
  160. int nHeight = 0;
  161. // Get the image
  162. IImage *pImage = scheme()->GetImage( szImage, true );
  163. if ( pImage )
  164. {
  165. // Get image dimensions
  166. int nImageWidth, nImageHeight;
  167. pImage->GetSize( nImageWidth, nImageHeight );
  168. // Compute height of the records panel as a little smaller than the image itself
  169. nHeight = nImageHeight * 11 / 16;
  170. // Setup the image panel - parent to records panel parent so it goes out of the records panel's bounds a bit
  171. const int nMargin = 7;
  172. const float flScale = 1.2f;
  173. m_pClassImage->SetImage( pImage );
  174. m_pClassImage->SetParent( GetParent() );
  175. m_pClassImage->SetShouldScaleImage( true );
  176. m_pClassImage->SetScaleAmount( flScale );
  177. int nX, nY;
  178. GetPos( nX, nY );
  179. m_pClassImage->SetBounds( nX + nMargin, nY - flScale * nImageHeight + GetTall() - nMargin, nImageWidth * flScale, nImageHeight * flScale );
  180. #if !defined( TF_CLIENT_DLL )
  181. m_pClassImage->SetVisible( false );
  182. #endif
  183. }
  184. SetTall( nHeight );
  185. }
  186. //-----------------------------------------------------------------------------
  187. CStatsPanel::CStatsPanel( Panel *pParent, ReplayHandle_t hReplay )
  188. : CBaseDetailsPanel( pParent, "StatsPanel", hReplay )
  189. {
  190. CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
  191. // Don't show the panel unless there are stats to display
  192. m_bShouldShow = false;
  193. // Create all stat labels
  194. RoundStats_t const &stats = pReplay->GetStats();
  195. for ( int i = 0; i < REPLAY_MAX_DISPLAY_GAMESTATS; ++i )
  196. {
  197. const int nCurStat = stats.Get( g_pReplayDisplayGameStats[i].m_nStat );
  198. if ( !nCurStat )
  199. {
  200. m_paStatLabels[ i ] = NULL;
  201. continue;
  202. }
  203. // Setup value
  204. char szValue[256];
  205. V_snprintf( szValue, sizeof( szValue ), "%i", nCurStat );
  206. // Create labels for this stat
  207. m_paStatLabels[ i ] = new CKeyValueLabelPanel( GetInset(), g_pReplayDisplayGameStats[i].m_pStatLocalizationToken, szValue );
  208. // At least one stat to display
  209. m_bShouldShow = true;
  210. }
  211. }
  212. void CStatsPanel::ApplySchemeSettings( IScheme *pScheme )
  213. {
  214. BaseClass::ApplySchemeSettings( pScheme );
  215. }
  216. void CStatsPanel::PerformLayout()
  217. {
  218. BaseClass::PerformLayout();
  219. int nY = 0;
  220. for ( int i = 0; i < REPLAY_MAX_DISPLAY_GAMESTATS; ++i )
  221. {
  222. CKeyValueLabelPanel *pCurStatLabels = m_paStatLabels[ i ];
  223. if ( pCurStatLabels )
  224. {
  225. pCurStatLabels->SetBounds( 0, nY, GetInset()->GetWide(), YRES(13) );
  226. nY += YRES(13);
  227. }
  228. }
  229. SetTall( nY + GetMarginSize() * 2 );
  230. }
  231. //-----------------------------------------------------------------------------
  232. CDominationsPanel::CDominationsPanel( Panel *pParent, ReplayHandle_t hReplay )
  233. : CBaseDetailsPanel( pParent, "DominationsPanel", hReplay )
  234. {
  235. CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
  236. m_pNumDominationsImage = new ImagePanel( GetInset(), "NumDominations" );
  237. char szImage[256];
  238. int nNumDominations = pReplay->GetDominationCount();
  239. // Setup the # of dominations image
  240. V_snprintf( szImage, sizeof( szImage ), "../hud/leaderboard_dom%i", nNumDominations );
  241. m_pNumDominationsImage->SetImage( szImage );
  242. // Add avatars for each person dominated
  243. EUniverse localUniverse = GetUniverse();
  244. if ( localUniverse != k_EUniverseInvalid )
  245. {
  246. for ( int i = 0; i < nNumDominations; ++i )
  247. {
  248. CAvatarImage *pAvatar = new CAvatarImage();
  249. CSteamID id( pReplay->GetDomination( i )->m_nVictimFriendId, 1, localUniverse, k_EAccountTypeIndividual );
  250. pAvatar->SetAvatarSteamID( id );
  251. pAvatar->SetAvatarSize( 32, 32 );
  252. pAvatar->UpdateFriendStatus();
  253. ImagePanel *pImagePanel = new ImagePanel( GetInset(), "DominationImage" );
  254. pImagePanel->SetImage( pAvatar );
  255. pImagePanel->SetShouldScaleImage( false );
  256. m_vecDominationImages.AddToTail( pImagePanel );
  257. }
  258. }
  259. }
  260. void CDominationsPanel::ApplySchemeSettings( IScheme *pScheme )
  261. {
  262. BaseClass::ApplySchemeSettings( pScheme );
  263. }
  264. void CDominationsPanel::PerformLayout()
  265. {
  266. BaseClass::PerformLayout();
  267. const int nBuffer = 7;
  268. int nImageWidth, nImageHeight;
  269. m_pNumDominationsImage->GetImage()->GetSize( nImageWidth, nImageHeight );
  270. m_pNumDominationsImage->SetBounds( 0, 0, nImageWidth, nImageHeight );
  271. int nX = nImageWidth + 2*nBuffer;
  272. int nY = 0;
  273. for ( int i = 0; i < m_vecDominationImages.Count(); ++i )
  274. {
  275. ImagePanel *pImagePanel = m_vecDominationImages[ i ];
  276. pImagePanel->GetImage()->GetSize( nImageWidth, nImageHeight );
  277. m_vecDominationImages[ i ]->SetBounds( nX, nY, nImageWidth, nImageHeight );
  278. nX += nImageWidth + nBuffer;
  279. if ( nX + nImageWidth > GetInset()->GetWide() )
  280. {
  281. nX = 0;
  282. nY += nImageHeight + nBuffer;
  283. }
  284. }
  285. SetTall( nY + nImageHeight + GetMarginSize() * 2 );
  286. }
  287. //-----------------------------------------------------------------------------
  288. CKillsPanel::CKillsPanel( Panel *pParent, ReplayHandle_t hReplay )
  289. : CBaseDetailsPanel( pParent, "KillsPanel", hReplay )
  290. {
  291. // Get the replay from the handle and add all kills
  292. CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
  293. char szKillCount[64] = "0";
  294. if ( pReplay )
  295. {
  296. for ( int i = 0; i < pReplay->GetKillCount(); ++i )
  297. {
  298. // Construct path for image
  299. char szImgPath[MAX_OSPATH] = "";
  300. #if defined( TF_CLIENT_DLL )
  301. // Get the kill info
  302. const CGenericClassBasedReplay::KillData_t *pKill = pReplay->GetKill( i );
  303. char const *pClass = pKill->m_nPlayerClass == TF_CLASS_DEMOMAN
  304. ? "demo"
  305. : g_aPlayerClassNames_NonLocalized[ pKill->m_nPlayerClass ];
  306. V_snprintf( szImgPath, sizeof( szImgPath ), "../hud/leaderboard_class_%s", pClass );
  307. #elif defined( CSTRIKE_DLL )
  308. V_strcpy_safe( szImgPath, "../hud/scoreboard_dead" );
  309. #endif
  310. // Get the image
  311. IImage *pImage = scheme()->GetImage( szImgPath, true );
  312. // Create new image panel
  313. ImagePanel *pImgPanel = new ImagePanel( GetInset(), "img" );
  314. pImgPanel->SetImage( pImage );
  315. // Cache for later
  316. m_vecKillImages.AddToTail( pImgPanel );
  317. }
  318. // Copy kill count
  319. V_snprintf( szKillCount, sizeof( szKillCount ), "%i", pReplay->GetKillCount() );
  320. }
  321. // Create labels
  322. m_pKillLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_Kills", szKillCount );
  323. }
  324. void CKillsPanel::PerformLayout()
  325. {
  326. BaseClass::PerformLayout();
  327. m_pKillLabels->SetBounds( 0, 0, GetWide(), m_pKillLabels->GetHeight() );
  328. // Setup image positions
  329. int nBuffer = 5;
  330. int nY = m_pKillLabels->GetHeight() + nBuffer * 2;
  331. int nImageY = nY;
  332. int nImageX = 0;
  333. for ( int i = 0; i < m_vecKillImages.Count(); ++i )
  334. {
  335. IImage *pCurImage = m_vecKillImages[ i ]->GetImage();
  336. if ( !pCurImage )
  337. continue;
  338. int nImageWidth, nImageHeight;
  339. pCurImage->GetSize( nImageWidth, nImageHeight );
  340. m_vecKillImages[ i ]->SetBounds( nImageX, nImageY, nImageWidth, nImageHeight );
  341. nImageX += nImageWidth + nBuffer;
  342. if ( i == 0 )
  343. {
  344. nY += nImageHeight;
  345. }
  346. if ( nImageX + nImageWidth > GetInset()->GetWide() )
  347. {
  348. nImageX = 0;
  349. nImageY += nImageHeight + nBuffer;
  350. nY += nImageHeight + nBuffer;
  351. }
  352. }
  353. // Set the height
  354. SetTall( nY + GetMarginSize() * 2 );
  355. }
  356. void CKillsPanel::ApplySchemeSettings( IScheme *pScheme )
  357. {
  358. BaseClass::ApplySchemeSettings( pScheme );
  359. }
  360. //-----------------------------------------------------------------------------
  361. extern const char *GetMapDisplayName( const char *mapName );
  362. CBasicLifeInfoPanel::CBasicLifeInfoPanel( Panel *pParent, ReplayHandle_t hReplay )
  363. : CBaseDetailsPanel( pParent, "BasicLifeInfo", hReplay )
  364. {
  365. // Create labels
  366. CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
  367. m_pKilledByLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_StatKilledBy", pReplay->WasKilled() ? pReplay->GetKillerName() : "#Replay_None" );
  368. m_pMapLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_OnMap", GetMapDisplayName( pReplay->m_szMapName ) );
  369. m_pLifeLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_Life", CReplayTime::FormatTimeString( (int)pReplay->m_flLength ) );
  370. }
  371. void CBasicLifeInfoPanel::ApplySchemeSettings( IScheme *pScheme )
  372. {
  373. BaseClass::ApplySchemeSettings( pScheme );
  374. }
  375. void CBasicLifeInfoPanel::PerformLayout()
  376. {
  377. BaseClass::PerformLayout();
  378. int nBuffer = 5;
  379. m_pKilledByLabels->SetBounds( 0, 0, GetWide(), m_pKilledByLabels->GetHeight() );
  380. m_pMapLabels->SetBounds( 0, m_pKilledByLabels->GetTall() + nBuffer, GetWide(), m_pMapLabels->GetHeight() );
  381. int nLifeLabelsY = ( m_pMapLabels->GetTall() + nBuffer ) * 2;
  382. m_pLifeLabels->SetBounds( 0, nLifeLabelsY, GetWide(), m_pLifeLabels->GetHeight() );
  383. SetTall( nLifeLabelsY + m_pLifeLabels->GetTall() + GetMarginSize() * 2 );
  384. }
  385. //-----------------------------------------------------------------------------
  386. CYouTubeInfoPanel::CYouTubeInfoPanel( Panel *pParent )
  387. : CBaseDetailsPanel( pParent, "YouTubeInfo", NULL ),
  388. m_pLabels( NULL )
  389. {
  390. m_pLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_YouTube", g_pVGuiLocalize->Find( "YouTube_NoStats" ) );
  391. }
  392. void CYouTubeInfoPanel::PerformLayout()
  393. {
  394. BaseClass::PerformLayout();
  395. m_pLabels->SetBounds( 0, 0, GetWide(), m_pLabels->GetValueHeight() );
  396. SetTall( m_pLabels->GetTall() + GetMarginSize() * 2 );
  397. }
  398. void CYouTubeInfoPanel::SetInfo( const wchar_t *pInfo )
  399. {
  400. m_pLabels->SetValue( pInfo );
  401. InvalidateLayout();
  402. }
  403. //-----------------------------------------------------------------------------
  404. CTitleEditPanel::CTitleEditPanel( Panel *pParent, QueryableReplayItemHandle_t hReplayItem, IReplayItemManager *pItemManager )
  405. : EditablePanel( pParent, "TitleEditPanel" ),
  406. m_hReplayItem( hReplayItem ),
  407. m_pItemManager( pItemManager ),
  408. m_bMouseOver( false ),
  409. m_pTitleEntry( NULL ),
  410. m_pHeaderLine( NULL ),
  411. m_pClickToEditLabel( NULL ),
  412. m_pCaratLabel( NULL )
  413. {
  414. ivgui()->AddTickSignal( GetVPanel(), 10 );
  415. }
  416. CTitleEditPanel::~CTitleEditPanel()
  417. {
  418. ivgui()->RemoveTickSignal( GetVPanel() );
  419. }
  420. void CTitleEditPanel::ApplySchemeSettings( IScheme *pScheme )
  421. {
  422. BaseClass::ApplySchemeSettings( pScheme );
  423. LoadControlSettings( "resource/ui/replaybrowser/titleeditpanel.res", "GAME" );
  424. // Get ptr to carat label
  425. m_pCaratLabel = dynamic_cast< CExLabel * >( FindChildByName( "CaratLabel" ) );
  426. // Get ptr to "click to edit" label
  427. m_pClickToEditLabel = dynamic_cast< CExLabel * >( FindChildByName( "ClickToEditLabel" ) );
  428. // Setup title entry
  429. m_pTitleEntry = dynamic_cast< TextEntry * >( FindChildByName( "TitleInput" ) );
  430. m_pTitleEntry->SelectAllOnFocusAlways( true );
  431. #if !defined( TF_CLIENT_DLL )
  432. m_pTitleEntry->SetPaintBorderEnabled( false );
  433. #endif
  434. // Setup title entry text
  435. IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
  436. const wchar_t *pTitle = pReplayItem->GetItemTitle();
  437. m_pTitleEntry->SetText( pTitle[0] ? pTitle : L"#Replay_DefaultDetailsTitle" );
  438. // Cache pointer to the image
  439. m_pHeaderLine = dynamic_cast< ImagePanel * >( FindChildByName( "HeaderLine" ) );
  440. }
  441. void CTitleEditPanel::PerformLayout()
  442. {
  443. BaseClass::PerformLayout();
  444. int nCaratW, nCaratH;
  445. m_pCaratLabel->GetContentSize( nCaratW, nCaratH );
  446. m_pCaratLabel->SetWide( nCaratW );
  447. m_pCaratLabel->SetTall( nCaratH );
  448. // Get title entry pos
  449. int nTitleEntryX, nTitleEntryY;
  450. m_pTitleEntry->GetPos( nTitleEntryX, nTitleEntryY );
  451. // Set width of title entry to be width of parent, which has margins
  452. m_pTitleEntry->SetToFullHeight();
  453. m_pTitleEntry->SetWide( GetParent()->GetWide() - nTitleEntryX - 1 );
  454. // Get content size for label
  455. int nClickToEditW, nClickToEditH;
  456. m_pClickToEditLabel->GetContentSize( nClickToEditW, nClickToEditH );
  457. // Set click-to-edit bounds
  458. int nTitleEntryTall = m_pTitleEntry->GetTall();
  459. m_pClickToEditLabel->SetBounds(
  460. nTitleEntryX + GetParent()->GetWide() - nClickToEditW * 1.4f,
  461. nTitleEntryY + ( nTitleEntryTall - nClickToEditH ) / 2,
  462. nClickToEditW,
  463. nClickToEditH
  464. );
  465. // Setup header line position
  466. m_pHeaderLine->SetPos( 0, nTitleEntryY + m_pTitleEntry->GetTall() * 1.2f );
  467. }
  468. void CTitleEditPanel::OnTick()
  469. {
  470. int nMouseX, nMouseY;
  471. input()->GetCursorPos( nMouseX, nMouseY );
  472. m_bMouseOver = m_pTitleEntry->IsWithin( nMouseX, nMouseY );
  473. }
  474. void CTitleEditPanel::PaintBackground()
  475. {
  476. bool bEditing = m_pTitleEntry->HasFocus();
  477. bool bDrawExtraStuff = !vgui::input()->GetAppModalSurface() && ( m_bMouseOver || bEditing ); // Don't draw extra stuff when render dialog is up
  478. // If mouse is over and we're not editing, show the "click to edit" label
  479. m_pClickToEditLabel->SetVisible( m_bMouseOver && !bEditing );
  480. // Draw border if necessary
  481. if ( bDrawExtraStuff )
  482. {
  483. // Use the game UI panel here, since using this panel's vpanel (PushMakeCurrent() is set in
  484. // Panel::PaintTraverse(), the function that calls PaintBackground()) causes dimmed top and
  485. // left lines. Using the game UI panel allows painting outside of the text entry itself.
  486. vgui::VPANEL vGameUI = enginevgui->GetPanel( PANEL_GAMEUIDLL );
  487. // Calculate title entry rect (x0,y0,x1,y1) - move the absolute upper-left corner 1 pixel left & up
  488. int aTitleRect[4];
  489. ipanel()->GetAbsPos( m_pTitleEntry->GetVPanel(), aTitleRect[0], aTitleRect[1] );
  490. --aTitleRect[0];
  491. --aTitleRect[1];
  492. aTitleRect[2] = aTitleRect[0] + m_pTitleEntry->GetWide() + 2;
  493. aTitleRect[3] = aTitleRect[1] + m_pTitleEntry->GetTall() + 2;
  494. surface()->PushMakeCurrent( vGameUI, false );
  495. // Draw background
  496. surface()->DrawSetColor( Color( 29, 28, 26, 255 ) );
  497. surface()->DrawFilledRect( aTitleRect[0], aTitleRect[1], aTitleRect[2], aTitleRect[3] );
  498. // Draw stroke
  499. surface()->DrawSetColor( Color( 202, 190, 164, 255 ) );
  500. surface()->DrawLine( aTitleRect[0], aTitleRect[1], aTitleRect[2], aTitleRect[1] ); // Top
  501. surface()->DrawLine( aTitleRect[0], aTitleRect[1], aTitleRect[0], aTitleRect[3] ); // Left
  502. surface()->DrawLine( aTitleRect[0], aTitleRect[3], aTitleRect[2], aTitleRect[3] ); // Bottom
  503. surface()->DrawLine( aTitleRect[2], aTitleRect[1], aTitleRect[2], aTitleRect[3] ); // Right
  504. surface()->PopMakeCurrent( vGameUI );
  505. }
  506. }
  507. void CTitleEditPanel::OnKeyCodeTyped( KeyCode code )
  508. {
  509. IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
  510. const wchar_t *pTitle = pReplayItem->GetItemTitle();
  511. if ( m_pTitleEntry->HasFocus() && pReplayItem )
  512. {
  513. if ( code == KEY_ESCAPE )
  514. {
  515. // Get replay text and reset it
  516. m_pTitleEntry->SetText( pTitle );
  517. // Remove focus
  518. GetParent()->GetParent()->RequestFocus();
  519. }
  520. else if ( code == KEY_ENTER )
  521. {
  522. // If text is empty, reset to old title
  523. if ( m_pTitleEntry->GetTextLength() == 0 )
  524. {
  525. m_pTitleEntry->SetText( pTitle );
  526. }
  527. else // Save title...
  528. {
  529. // Copy text into the replay
  530. // NOTE: SetItemTitle() will mark replay as dirty
  531. wchar_t wszNewTitle[MAX_REPLAY_TITLE_LENGTH];
  532. m_pTitleEntry->GetText( wszNewTitle, sizeof( wszNewTitle ) );
  533. pReplayItem->SetItemTitle( wszNewTitle );
  534. // Save!
  535. g_pReplayManager->FlagReplayForFlush( pReplayItem->GetItemReplay(), true );
  536. // Notify the thumbnail
  537. void *pUserData = pReplayItem->GetUserData();
  538. if ( pUserData )
  539. {
  540. CReplayBrowserThumbnail *pThumbnail = (CReplayBrowserThumbnail*)pUserData;
  541. pThumbnail->UpdateTitleText();
  542. }
  543. }
  544. GetParent()->GetParent()->RequestFocus();
  545. }
  546. return;
  547. }
  548. BaseClass::OnKeyCodeTyped( code );
  549. }
  550. //-----------------------------------------------------------------------------
  551. CPlaybackPanel::CPlaybackPanel( Panel *pParent )
  552. : EditablePanel( pParent, "PlaybackPanel" )
  553. {
  554. }
  555. CPlaybackPanel::~CPlaybackPanel()
  556. {
  557. ivgui()->RemoveTickSignal( GetVPanel() );
  558. }
  559. void CPlaybackPanel::ApplySchemeSettings( IScheme *pScheme )
  560. {
  561. BaseClass::ApplySchemeSettings( pScheme );
  562. LoadControlSettings( "resource/ui/replaybrowser/playbackpanel.res", "GAME" );
  563. }
  564. void CPlaybackPanel::PerformLayout()
  565. {
  566. BaseClass::PerformLayout();
  567. }
  568. //-----------------------------------------------------------------------------
  569. CPlaybackPanelSlideshow::CPlaybackPanelSlideshow( Panel *pParent, ReplayHandle_t hReplay )
  570. : CPlaybackPanel( pParent ),
  571. m_hReplay( hReplay )
  572. {
  573. m_pScreenshotImage = new CReplayScreenshotSlideshowPanel( this, "Screenshot", hReplay );
  574. }
  575. void CPlaybackPanelSlideshow::ApplySchemeSettings( IScheme *pScheme )
  576. {
  577. BaseClass::ApplySchemeSettings( pScheme );
  578. LoadControlSettings( "resource/ui/replaybrowser/playbackpanelslideshow.res", "GAME" );
  579. m_pNoScreenshotLabel = dynamic_cast< CExLabel * >( FindChildByName( "NoScreenshotLabel" ) );
  580. // Check to see if there's a screenshot
  581. CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  582. if ( !pReplay )
  583. return;
  584. if ( !pReplay->GetScreenshotCount() && m_pNoScreenshotLabel ) // Show no-screenshot label
  585. {
  586. m_pNoScreenshotLabel->SetVisible( true );
  587. }
  588. }
  589. void CPlaybackPanelSlideshow::PerformLayout()
  590. {
  591. BaseClass::PerformLayout();
  592. CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  593. int nMarginWidth = GetMarginSize();
  594. int nScreenshotWidth = GetViewWidth();
  595. if ( m_pScreenshotImage )
  596. {
  597. m_pScreenshotImage->SetBounds( nMarginWidth, nMarginWidth, nScreenshotWidth, GetTall() - 2*nMarginWidth );
  598. // Setup screenshot scale based on width of first screenshot (if there are any screenshots at all) - otherwise don't scale
  599. float flScale = pReplay->GetScreenshotCount() == 0 ? 1.0f : ( (float)nScreenshotWidth / ( .95f * pReplay->GetScreenshot( 0 )->m_nWidth ) );
  600. m_pScreenshotImage->GetImagePanel()->SetScaleAmount( flScale );
  601. m_pScreenshotImage->GetImagePanel()->SetShouldScaleImage( true );
  602. }
  603. // Setup the label
  604. int nLabelW, nLabelH;
  605. m_pNoScreenshotLabel->GetContentSize( nLabelW, nLabelH );
  606. m_pNoScreenshotLabel->SetBounds( 0, ( GetTall() - nLabelH ) / 2, GetWide(), nLabelH );
  607. }
  608. //-----------------------------------------------------------------------------
  609. CPlaybackPanelMovie::CPlaybackPanelMovie( Panel *pParent, ReplayHandle_t hMovie )
  610. : CPlaybackPanel( pParent )
  611. {
  612. IReplayMovie *pMovie = g_pReplayMovieManager->GetMovie( hMovie );
  613. m_pMoviePlayerPanel = new CMoviePlayerPanel( this, "MoviePlayer", pMovie->GetMovieFilename() );
  614. m_pMoviePlayerPanel->SetLooping( true );
  615. // TODO: show controls and don't play right away
  616. m_pMoviePlayerPanel->Play();
  617. }
  618. void CPlaybackPanelMovie::ApplySchemeSettings( IScheme *pScheme )
  619. {
  620. BaseClass::ApplySchemeSettings( pScheme );
  621. }
  622. void CPlaybackPanelMovie::PerformLayout()
  623. {
  624. BaseClass::PerformLayout();
  625. m_pMoviePlayerPanel->SetBounds( 9, 9, GetViewWidth(), GetViewHeight() );
  626. m_pMoviePlayerPanel->SetEnabled( true );
  627. m_pMoviePlayerPanel->SetVisible( true );
  628. m_pMoviePlayerPanel->SetZPos( 101 );
  629. }
  630. void CPlaybackPanelMovie::FreeMovieMaterial()
  631. {
  632. m_pMoviePlayerPanel->FreeMaterial();
  633. }
  634. //-----------------------------------------------------------------------------
  635. CCutImagePanel::CCutImagePanel( Panel *pParent, const char *pName )
  636. : BaseClass( pParent, pName, "" ),
  637. m_pSelectedBorder( NULL )
  638. {
  639. }
  640. void CCutImagePanel::SetSelected( bool bState )
  641. {
  642. BaseClass::SetSelected( bState );
  643. }
  644. IBorder *CCutImagePanel::GetBorder( bool bDepressed, bool bArmed, bool bSelected, bool bKeyFocus )
  645. {
  646. if ( bSelected )
  647. {
  648. return m_pSelectedBorder;
  649. }
  650. return BaseClass::GetBorder( bDepressed, bArmed, bSelected, bKeyFocus );
  651. }
  652. void CCutImagePanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  653. {
  654. BaseClass::ApplySchemeSettings( pScheme );
  655. }
  656. //-----------------------------------------------------------------------------
  657. #define FOR_EACH_BUTTON( _i ) for ( int _i = 0; _i < BUTTONS_PER_PAGE; ++_i )
  658. CCutsPanel::CCutsPanel( Panel *pParent, ReplayHandle_t hReplay, int iSelectedPerformance )
  659. : BaseClass( pParent, "CutsPanel", hReplay ),
  660. m_iPage( 0 ),
  661. m_nVisibleButtons( 0 ),
  662. m_pVerticalLine( NULL ),
  663. m_pNoCutsLabel( NULL ),
  664. m_pOriginalLabel( NULL ),
  665. m_pCutsLabel( NULL )
  666. {
  667. m_hDetailsPanel = dynamic_cast< CReplayDetailsPanel * >( pParent->GetParent() );
  668. FOR_EACH_BUTTON( i )
  669. {
  670. const int iButton = i;
  671. CFmtStr fmtName( "CutButton%i", iButton );
  672. CExImageButton *pNewButton = new CExImageButton( this, fmtName.Access(), "" );
  673. CFmtStr fmtCommand( "select_%i", iButton );
  674. pNewButton->SetCommand( fmtCommand.Access() );
  675. pNewButton->InvalidateLayout( true, true );
  676. pNewButton->AddActionSignalTarget( this );
  677. pNewButton->SetSelected( i == 0 );
  678. #if !defined( TF_CLIENT_DLL )
  679. pNewButton->SetSelectedColor( Color( 0, 0, 0, 0 ), Color( 122, 25, 16, 255 ) );
  680. #endif
  681. const int iPerformance = i - 1;
  682. m_aButtons[ i ].m_pButton = pNewButton;
  683. m_aButtons[ i ].m_iPerformance = iPerformance;
  684. CExButton *pAddToRenderQueueButton = new CExButton( pNewButton, "AddToRenderQueue", "+", this );
  685. m_aButtons[ i ].m_pAddToRenderQueueButton = pAddToRenderQueueButton;
  686. }
  687. // Layout right now
  688. InvalidateLayout( true, true );
  689. // Calculate page
  690. SetPage(
  691. ( 1 + iSelectedPerformance ) / BUTTONS_PER_PAGE,
  692. ( 1 + iSelectedPerformance ) % BUTTONS_PER_PAGE
  693. );
  694. ivgui()->AddTickSignal( GetVPanel(), 10 );
  695. }
  696. CCutsPanel::~CCutsPanel()
  697. {
  698. ivgui()->RemoveTickSignal( GetVPanel() );
  699. }
  700. void CCutsPanel::ApplySchemeSettings( IScheme *pScheme )
  701. {
  702. BaseClass::ApplySchemeSettings( pScheme );
  703. LoadControlSettings( "resource/ui/replaybrowser/cutspanel.res", "GAME" );
  704. m_pVerticalLine = dynamic_cast< EditablePanel * >( FindChildByName( "VerticalLine" ) );
  705. m_pNoCutsLabel = dynamic_cast< CExLabel * >( FindChildByName( "NoCutsLabel" ) );
  706. m_pOriginalLabel = dynamic_cast< CExLabel * >( FindChildByName( "OriginalLabel" ) );
  707. m_pCutsLabel = dynamic_cast< CExLabel * >( FindChildByName( "CutsLabel" ) );
  708. m_pNameLabel = dynamic_cast< CExLabel * >( FindChildByName( "NameLabel" ) );
  709. m_pPrevButton = dynamic_cast< CExButton * >( FindChildByName( "PrevButton" ) );
  710. m_pNextButton = dynamic_cast< CExButton * >( FindChildByName( "NextButton" ) );
  711. FOR_EACH_BUTTON( i )
  712. {
  713. CExImageButton *pCurButton = m_aButtons[ i ].m_pButton;
  714. #if !defined( TF_CLIENT_DLL )
  715. pCurButton->SetPaintBorderEnabled( false );
  716. #endif
  717. pCurButton->InvalidateLayout( true, true );
  718. }
  719. }
  720. void CCutsPanel::ApplySettings( KeyValues *pInResourceData )
  721. {
  722. BaseClass::ApplySettings( pInResourceData );
  723. KeyValues *pButtonSettings = pInResourceData->FindKey( "button_settings" );
  724. if ( pButtonSettings )
  725. {
  726. KeyValues *pAddToRenderQueueButtonSettings = pButtonSettings->FindKey( "addtorenderqueuebutton_settings" );
  727. CReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  728. FOR_EACH_BUTTON( i )
  729. {
  730. CExImageButton *pCurButton = m_aButtons[ i ].m_pButton;
  731. pCurButton->ApplySettings( pButtonSettings );
  732. // Set screenshot as image
  733. if ( pReplay && pReplay->GetScreenshotCount() )
  734. {
  735. const float flScale = (float)m_nCutButtonHeight / pReplay->GetScreenshot( 0 )->m_nHeight;
  736. int nImageWidth = m_nCutButtonWidth - 2 * m_nCutButtonBuffer;
  737. int nImageHeight = m_nCutButtonHeight - 2 * m_nCutButtonBuffer;
  738. CFmtStr fmtFile( "replay\\thumbnails\\%s", pReplay->GetScreenshot( 0 )->m_szBaseFilename );
  739. pCurButton->SetSubImage( fmtFile.Access() );
  740. pCurButton->GetImage()->SetScaleAmount( flScale );
  741. pCurButton->GetImage()->SetBounds( m_nCutButtonBuffer, m_nCutButtonBuffer, nImageWidth, nImageHeight );
  742. }
  743. if ( pAddToRenderQueueButtonSettings )
  744. {
  745. CExButton *pAddToQueueButton = m_aButtons[ i ].m_pAddToRenderQueueButton;
  746. pAddToQueueButton->ApplySettings( pAddToRenderQueueButtonSettings );
  747. pAddToQueueButton->AddActionSignalTarget( this );
  748. }
  749. }
  750. }
  751. }
  752. void CCutsPanel::PerformLayout()
  753. {
  754. BaseClass::PerformLayout();
  755. CReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  756. const int nNumCuts = pReplay->GetPerformanceCount();
  757. int nX = m_iPage > 0 ? m_pPrevButton->GetWide() + m_nCutButtonSpace : 0;
  758. m_nVisibleButtons = 0;
  759. FOR_EACH_BUTTON( i )
  760. {
  761. CExImageButton *pCurButton = m_aButtons[ i ].m_pButton;
  762. const bool bVisible = ButtonToPerformance( i ) < nNumCuts;
  763. pCurButton->SetVisible( bVisible );
  764. if ( bVisible )
  765. {
  766. ++m_nVisibleButtons;
  767. }
  768. pCurButton->SetBounds( nX, m_nButtonStartY, m_nCutButtonWidth, m_nCutButtonHeight );
  769. nX += m_nCutButtonWidth;
  770. if ( i == 0 && m_iPage == 0 )
  771. {
  772. nX += 2 * m_nCutButtonSpaceWide + m_pVerticalLine->GetWide();
  773. }
  774. else
  775. {
  776. nX += m_nCutButtonSpace;
  777. }
  778. }
  779. if ( m_pVerticalLine )
  780. {
  781. m_pVerticalLine->SetVisible( m_nVisibleButtons > 0 && m_iPage == 0 );
  782. m_pVerticalLine->SetPos( m_nCutButtonWidth + m_nCutButtonSpaceWide, 0 );
  783. m_pVerticalLine->SetTall( m_nTopMarginHeight + GetTall() );
  784. }
  785. const int nRightOfVerticalLineX = m_nCutButtonWidth + m_nCutButtonSpaceWide * 2 + m_pVerticalLine->GetWide();
  786. if ( m_pNoCutsLabel )
  787. {
  788. m_pNoCutsLabel->SetVisible( m_nVisibleButtons == 1 && m_iPage == 0 );
  789. int nY = ( GetTall() - m_pNoCutsLabel->GetTall() ) / 2;
  790. m_pNoCutsLabel->SetPos( nRightOfVerticalLineX, nY );
  791. }
  792. if ( m_pOriginalLabel )
  793. {
  794. m_pOriginalLabel->SetVisible( m_iPage == 0 );
  795. }
  796. if ( m_pCutsLabel )
  797. {
  798. m_pCutsLabel->SetVisible( m_nVisibleButtons > 1 && m_iPage == 0 );
  799. m_pCutsLabel->SetPos( m_nCutButtonWidth + 2 * m_nCutButtonSpaceWide + m_pVerticalLine->GetWide(), 0 );
  800. }
  801. bool bPrevCuts = m_iPage > 0;
  802. bool bMoreCuts = ( nNumCuts + 1 ) > ( m_iPage + 1 ) * BUTTONS_PER_PAGE;
  803. int nY = m_nTopMarginHeight + ( GetTall() - m_pNextButton->GetTall() ) / 2;
  804. m_pPrevButton->SetVisible( bPrevCuts );
  805. m_pPrevButton->SetPos( 0, nY );
  806. m_pNextButton->SetVisible( bMoreCuts );
  807. m_pNextButton->SetPos( nX, nY );
  808. }
  809. void CCutsPanel::OnTick()
  810. {
  811. if ( !TFModalStack()->IsEmpty() )
  812. return;
  813. int nMouseX, nMouseY;
  814. input()->GetCursorPos( nMouseX, nMouseY );
  815. // Early-out if not within the cuts panel at all.
  816. if ( !IsWithin( nMouseX, nMouseY ) )
  817. return;
  818. int iHoverPerformance = -2;
  819. bool bFoundHoverButton = false;
  820. FOR_EACH_BUTTON( i )
  821. {
  822. CExImageButton *pCurButton = m_aButtons[ i ].m_pButton;
  823. bool bIsHoverButton = false;
  824. if ( !bFoundHoverButton && pCurButton->IsWithin( nMouseX, nMouseY ) && pCurButton->IsVisible() )
  825. {
  826. iHoverPerformance = ButtonToPerformance( i );
  827. bFoundHoverButton = true;
  828. bIsHoverButton = true;
  829. }
  830. CExButton *pAddToRenderQueueButton = m_aButtons[ i ].m_pAddToRenderQueueButton;
  831. if ( pAddToRenderQueueButton )
  832. {
  833. pAddToRenderQueueButton->SetVisible( bIsHoverButton );
  834. if ( iHoverPerformance >= -1 )
  835. {
  836. // Set the text and command based on whether or not the take's already been queued
  837. const bool bInQueue = g_pClientReplayContext->GetRenderQueue()->IsInQueue( m_hReplay, iHoverPerformance );
  838. CFmtStr fmtCmd( "%srenderqueue_%i", bInQueue ? "removefrom" : "addto", iHoverPerformance );
  839. pAddToRenderQueueButton->SetCommand( fmtCmd.Access() );
  840. pAddToRenderQueueButton->SetText( bInQueue ? "-" : "+" );
  841. }
  842. }
  843. }
  844. // If the mouse is over a performance button, use that, otherwise use the selected
  845. // performance.
  846. if ( m_hDetailsPanel.Get() )
  847. {
  848. int iSelectedPerformance = m_hDetailsPanel->m_iSelectedPerformance;
  849. UpdateNameLabel( iHoverPerformance >= 0 ? iHoverPerformance : iSelectedPerformance >= 0 ? iSelectedPerformance : -1 );
  850. }
  851. }
  852. int CCutsPanel::ButtonToPerformance( int iButton ) const
  853. {
  854. return -1 + m_iPage * BUTTONS_PER_PAGE + iButton;
  855. }
  856. void CCutsPanel::OnCommand( const char *pCommand )
  857. {
  858. if ( !V_strnicmp( pCommand, "select_", 7 ) )
  859. {
  860. const int iButton = atoi( pCommand + 7 );
  861. SelectButtonFromPerformance( ButtonToPerformance( iButton ) );
  862. }
  863. else if ( !V_stricmp( pCommand, "prevpage" ) )
  864. {
  865. SetPage( m_iPage - 1 );
  866. }
  867. else if ( !V_stricmp( pCommand, "nextpage" ) )
  868. {
  869. SetPage( m_iPage + 1 );
  870. }
  871. else if ( !V_strnicmp( pCommand, "addtorenderqueue_", 17 ) )
  872. {
  873. if ( !replay_renderqueue_first_add.GetInt() )
  874. {
  875. ShowMessageBox( "#Replay_FirstRenderQueueAddTitle", "#Replay_FirstRenderQueueAddMsg", "#GameUI_OK" );
  876. replay_renderqueue_first_add.SetValue( 1 );
  877. }
  878. const int iPerformance = atoi( pCommand + 17 );
  879. if ( iPerformance >= -1 )
  880. {
  881. g_pClientReplayContext->GetRenderQueue()->Add( m_hReplay, iPerformance );
  882. }
  883. }
  884. else if ( !V_strnicmp( pCommand, "removefromrenderqueue_", 22 ) )
  885. {
  886. const int iPerformance = atoi( pCommand + 22 );
  887. if ( iPerformance >= -1 )
  888. {
  889. g_pClientReplayContext->GetRenderQueue()->Remove( m_hReplay, iPerformance );
  890. }
  891. }
  892. }
  893. void CCutsPanel::SetPage( int iPage, int iButtonToSelect )
  894. {
  895. m_iPage = iPage;
  896. FOR_EACH_BUTTON( i )
  897. {
  898. ButtonInfo_t *pCurButtonInfo = &m_aButtons[ i ];
  899. const int iPerformance = ButtonToPerformance( i );
  900. pCurButtonInfo->m_iPerformance = iPerformance;
  901. }
  902. InvalidateLayout( true, false );
  903. SelectButtonFromPerformance( ButtonToPerformance( iButtonToSelect ) );
  904. }
  905. const CReplayPerformance *CCutsPanel::GetPerformance( int iPerformance ) const
  906. {
  907. const CReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  908. if ( !pReplay )
  909. return NULL;
  910. return iPerformance >= 0 ? pReplay->GetPerformance( iPerformance ) : NULL;
  911. }
  912. void CCutsPanel::SelectButtonFromPerformance( int iPerformance )
  913. {
  914. FOR_EACH_BUTTON( i )
  915. {
  916. const ButtonInfo_t *pCurButtonInfo = &m_aButtons[ i ];
  917. CExImageButton *pCurButton = pCurButtonInfo->m_pButton;
  918. pCurButton->SetSelected( pCurButtonInfo->m_iPerformance == iPerformance );
  919. pCurButton->InvalidateLayout( true, true );
  920. }
  921. // Cache which performance to use in the details panel
  922. if ( m_hDetailsPanel.Get() )
  923. {
  924. m_hDetailsPanel->m_iSelectedPerformance = iPerformance;
  925. }
  926. UpdateNameLabel( iPerformance );
  927. }
  928. int CCutsPanel::PerformanceToButton( int iPerformance ) const
  929. {
  930. FOR_EACH_BUTTON( i )
  931. {
  932. if ( m_aButtons[ i ].m_iPerformance == iPerformance )
  933. {
  934. return i;
  935. }
  936. }
  937. return -1;
  938. }
  939. void CCutsPanel::UpdateNameLabel( int iPerformance )
  940. {
  941. const CReplayPerformance *pPerformance = GetPerformance( iPerformance );
  942. m_pNameLabel->SetText( pPerformance ? pPerformance->m_wszTitle : L"" );
  943. // Get the button (in the range [0,BUTTONS_PER_PAGE]).
  944. const int iPerformanceButton = PerformanceToButton( iPerformance ); // Not necessarily the selected button - can be hover button
  945. // Get position of the button so we can use it's x position.
  946. int aSelectedButtonPos[2];
  947. m_aButtons[ iPerformanceButton ].m_pButton->GetPos( aSelectedButtonPos[0], aSelectedButtonPos[1] );
  948. if ( m_pNameLabel )
  949. {
  950. const int nNameLabelX = aSelectedButtonPos[0];
  951. const int nNameLabelY = m_nButtonStartY + m_nCutButtonHeight + m_nNameLabelTopMargin;
  952. m_pNameLabel->SetBounds(
  953. nNameLabelX,
  954. nNameLabelY,
  955. GetWide() - nNameLabelX,
  956. GetTall() - nNameLabelY
  957. );
  958. }
  959. }
  960. void CCutsPanel::OnPerformanceDeleted( int iPerformance )
  961. {
  962. int iButton = PerformanceToButton( iPerformance );
  963. if ( iButton < 0 )
  964. return;
  965. // Deleted last performance on page?
  966. CReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  967. const int nNumCuts = pReplay->GetPerformanceCount();
  968. if ( iPerformance == m_aButtons[ 0 ].m_iPerformance && iPerformance == nNumCuts )
  969. {
  970. SetPage( m_iPage - 1, BUTTONS_PER_PAGE - 1 );
  971. }
  972. else
  973. {
  974. SelectButtonFromPerformance( ButtonToPerformance( MIN( m_nVisibleButtons - 1, MAX( 0, iButton ) ) ) );
  975. }
  976. // Select the cut prior to the one we just deleted
  977. Assert( iPerformance >= 0 );
  978. InvalidateLayout( true, false );
  979. }
  980. //-----------------------------------------------------------------------------
  981. static void ConfirmUploadMovie( bool bConfirmed, void *pContext )
  982. {
  983. if ( bConfirmed )
  984. {
  985. CReplayDetailsPanel *pPanel = (CReplayDetailsPanel*)pContext;
  986. IQueryableReplayItem *pReplayItem = pPanel->m_pItemManager->GetItem( pPanel->m_hReplayItem );
  987. if ( pReplayItem && pReplayItem->IsItemAMovie() )
  988. {
  989. IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
  990. if ( YouTube_GetLoginStatus() != kYouTubeLogin_LoggedIn )
  991. {
  992. YouTube_ShowLoginDialog( pMovie, pPanel );
  993. }
  994. else
  995. {
  996. YouTube_ShowUploadDialog( pMovie, pPanel );
  997. }
  998. }
  999. }
  1000. }
  1001. class CYouTubeGetStatsHandler : public CYouTubeResponseHandler
  1002. {
  1003. public:
  1004. CYouTubeGetStatsHandler( CReplayDetailsPanel *pPanel )
  1005. : m_pPanel( pPanel )
  1006. , m_handle( NULL )
  1007. {
  1008. }
  1009. virtual ~CYouTubeGetStatsHandler()
  1010. {
  1011. if ( m_handle != NULL )
  1012. {
  1013. YouTube_CancelGetVideoInfo( m_handle );
  1014. }
  1015. }
  1016. static bool GetEmptyElementTagContents( const char *pXML, const char *pTag, CUtlString &strTagContents )
  1017. {
  1018. CFmtStr1024 kLinkTagStart( "<%s ", pTag );
  1019. const char *kLinkTagEnd = "/>";
  1020. const char *pStart = strstr( pXML, kLinkTagStart.Access() );
  1021. if ( pStart != NULL )
  1022. {
  1023. pStart += kLinkTagStart.Length();
  1024. const char *pEnd = strstr( pStart, kLinkTagEnd );
  1025. if ( pEnd != NULL )
  1026. {
  1027. strTagContents.SetDirect( pStart, pEnd - pStart );
  1028. return true;
  1029. }
  1030. }
  1031. return false;
  1032. }
  1033. static bool GetEmptyTagValue( const char *pTagContents, const char *pKeyName, CUtlString &value )
  1034. {
  1035. CFmtStr1024 kStart( "%s='", pKeyName );
  1036. const char *kEnd = "'";
  1037. const char *pStart = strstr( pTagContents, kStart.Access() );
  1038. if ( pStart != NULL )
  1039. {
  1040. pStart += kStart.Length();
  1041. const char *pEnd = strstr( pStart, kEnd );
  1042. if ( pEnd != NULL )
  1043. {
  1044. value.SetDirect( pStart, pEnd - pStart );
  1045. return true;
  1046. }
  1047. }
  1048. return false;
  1049. }
  1050. virtual void HandleResponse( long responseCode, const char *pResponse )
  1051. {
  1052. // @note tom bui: wish I had an XML parser
  1053. if ( strstr( pResponse, "<internalReason>Private video</internalReason>" ) != NULL )
  1054. {
  1055. m_pPanel->m_pYouTubeInfoPanel->SetInfo( g_pVGuiLocalize->Find( "#YouTube_PrivateVideo" ) );
  1056. m_pPanel->SetYouTubeStatus( CReplayDetailsPanel::kYouTubeStatus_Private );
  1057. return;
  1058. }
  1059. int iNumFavorited = 0;
  1060. int iNumViews = 0;
  1061. int iNumLikes = 0;
  1062. wchar_t wszFavorited[256] = L"0";
  1063. wchar_t wszViews[256] = L"0";
  1064. CUtlString strTagStatistics;
  1065. if ( GetEmptyElementTagContents( pResponse, "yt:statistics", strTagStatistics ) )
  1066. {
  1067. CUtlString favoriteCount;
  1068. CUtlString viewCount;
  1069. GetEmptyTagValue( strTagStatistics, "favoriteCount", favoriteCount );
  1070. GetEmptyTagValue( strTagStatistics, "viewCount", viewCount );
  1071. iNumFavorited = Q_atoi( favoriteCount.Get() );
  1072. iNumViews = Q_atoi( viewCount.Get() );
  1073. g_pVGuiLocalize->ConvertANSIToUnicode( favoriteCount.Get(), wszFavorited, sizeof( wszFavorited ) );
  1074. g_pVGuiLocalize->ConvertANSIToUnicode( viewCount.Get(), wszViews, sizeof( wszViews ) );
  1075. }
  1076. wchar_t wszLikes[256] = L"0";
  1077. CUtlString strTagRating;
  1078. if ( GetEmptyElementTagContents( pResponse, "yt:rating", strTagRating ) )
  1079. {
  1080. CUtlString likes;
  1081. GetEmptyTagValue( strTagRating, "numLikes", likes );
  1082. iNumLikes = Q_atoi( likes.Get() );
  1083. g_pVGuiLocalize->ConvertANSIToUnicode( likes.Get(), wszLikes, sizeof( wszLikes ) );
  1084. }
  1085. //const char *kLinkStartTag = "<link rel='alternate' type='text/html' href='";
  1086. CUtlString strTagLink;
  1087. if ( GetEmptyElementTagContents( pResponse, "link rel='alternate'", strTagLink ) )
  1088. {
  1089. GetEmptyTagValue( strTagLink, "href", m_strVideoURL );
  1090. }
  1091. wchar_t wszStats[256] = L"";
  1092. g_pVGuiLocalize->ConstructString_safe( wszStats, g_pVGuiLocalize->Find( "#YouTube_Stats" ), 3,
  1093. wszFavorited,
  1094. wszViews,
  1095. wszLikes );
  1096. if ( m_strVideoURL.IsEmpty() == false )
  1097. {
  1098. m_pPanel->m_pYouTubeInfoPanel->SetInfo( wszStats );
  1099. m_pPanel->SetYouTubeStatus( CReplayDetailsPanel::kYouTubeStatus_RetrievedInfo );
  1100. m_pPanel->InvalidateLayout();
  1101. IGameEvent *event = gameeventmanager->CreateEvent( "replay_youtube_stats" );
  1102. if ( event )
  1103. {
  1104. event->SetInt( "views", iNumViews );
  1105. event->SetInt( "likes", iNumLikes );
  1106. event->SetInt( "favorited", iNumFavorited );
  1107. gameeventmanager->FireEventClientSide( event );
  1108. }
  1109. }
  1110. else
  1111. {
  1112. m_pPanel->m_pYouTubeInfoPanel->SetInfo( g_pVGuiLocalize->Find( "#YouTube_CouldNotRetrieveStats" ) );
  1113. m_pPanel->SetYouTubeStatus( CReplayDetailsPanel::kYouTubeStatus_CouldNotRetrieveInfo );
  1114. }
  1115. }
  1116. CReplayDetailsPanel *m_pPanel;
  1117. YouTubeInfoHandle_t m_handle;
  1118. CUtlString m_strVideoURL;
  1119. };
  1120. CReplayDetailsPanel::CReplayDetailsPanel( Panel *pParent, QueryableReplayItemHandle_t hReplayItem,
  1121. int iPerformance, IReplayItemManager *pItemManager )
  1122. : EditablePanel( pParent, "DetailsPanel" ),
  1123. m_hReplayItem( hReplayItem ),
  1124. m_pItemManager( pItemManager ),
  1125. m_pCutsPanel( NULL ),
  1126. m_iSelectedPerformance( iPerformance ),
  1127. m_pYouTubeResponseHandler( NULL ),
  1128. m_hExportMovieDialog( NULL )
  1129. {
  1130. m_hReplay = pItemManager->GetItem( hReplayItem )->GetItemReplayHandle();
  1131. CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  1132. m_pInsetPanel = new EditablePanel( this, "InsetPanel" );
  1133. m_pTitleEditPanel = new CTitleEditPanel( GetInset(), m_hReplayItem, m_pItemManager );
  1134. m_pPlaybackPanel = new CPlaybackPanelSlideshow( GetInset(), m_hReplay );
  1135. m_pRecordsPanel = new CRecordsPanel( GetInset(), m_hReplay );
  1136. m_pInfoPanel = new EditablePanel( this, "InfoContainerPanel" );
  1137. m_pScrollPanel = new vgui::ScrollableEditablePanel( GetInset(), m_pInfoPanel, "StatsScroller" );
  1138. m_pScrollPanel->GetScrollbar()->SetAutohideButtons( true );
  1139. #if !defined( TF_CLIENT_DLL )
  1140. for ( int i = 0; i < 2; ++i )
  1141. {
  1142. m_pScrollPanel->GetScrollbar()->GetButton( i )->SetPaintBorderEnabled( false );
  1143. }
  1144. #endif
  1145. m_pBasicInfoPanel = new CBasicLifeInfoPanel( m_pInfoPanel, m_hReplay );
  1146. m_pStatsPanel = new CStatsPanel( m_pInfoPanel, m_hReplay );
  1147. m_pKillsPanel = new CKillsPanel( m_pInfoPanel, m_hReplay );
  1148. const bool bIsMoviePanel = pItemManager->AreItemsMovies();
  1149. if ( bIsMoviePanel )
  1150. {
  1151. m_pYouTubeInfoPanel = new CYouTubeInfoPanel( m_pInfoPanel );
  1152. }
  1153. else
  1154. {
  1155. m_pCutsPanel = new CCutsPanel( GetInset(), m_hReplay, m_iSelectedPerformance );
  1156. }
  1157. // Add info panels to a list
  1158. if ( pReplay->GetDominationCount() )
  1159. {
  1160. m_pDominationsPanel = new CDominationsPanel( m_pInfoPanel, m_hReplay );
  1161. m_vecInfoPanels.AddToTail( m_pDominationsPanel );
  1162. }
  1163. m_vecInfoPanels.AddToTail( m_pBasicInfoPanel );
  1164. m_vecInfoPanels.AddToTail( m_pStatsPanel );
  1165. m_vecInfoPanels.AddToTail( m_pKillsPanel );
  1166. if ( bIsMoviePanel )
  1167. {
  1168. m_vecInfoPanels.AddToTail( m_pYouTubeInfoPanel );
  1169. }
  1170. m_pYouTubeResponseHandler = new CYouTubeGetStatsHandler( this );
  1171. RequestFocus();
  1172. }
  1173. CReplayDetailsPanel::~CReplayDetailsPanel()
  1174. {
  1175. m_pDeleteButton->MarkForDeletion();
  1176. m_pRenderButton->MarkForDeletion();
  1177. m_pPlayButton->MarkForDeletion();
  1178. delete m_pYouTubeResponseHandler;
  1179. }
  1180. void CReplayDetailsPanel::ApplySchemeSettings( IScheme *pScheme )
  1181. {
  1182. BaseClass::ApplySchemeSettings( pScheme );
  1183. LoadControlSettings( "resource/ui/replaybrowser/detailspanel.res", "GAME" );
  1184. m_pExportMovie = dynamic_cast< CExButton * >( FindChildByName( "ExportMovieButton" ) );
  1185. m_pDeleteButton = dynamic_cast< CExButton * >( FindChildByName( "DeleteButton" ) );
  1186. m_pRenderButton = dynamic_cast< CExButton * >( FindChildByName( "RenderButton" ) );
  1187. m_pPlayButton = dynamic_cast< CExButton * >( FindChildByName( "PlayButton" ) );
  1188. m_pYouTubeUpload = dynamic_cast< CExButton * >( FindChildByName( "YouTubeUploadButton" ) );
  1189. m_pYouTubeView = dynamic_cast< CExButton * >( FindChildByName( "ViewYouTubeButton" ) );
  1190. m_pYouTubeShareURL = dynamic_cast< CExButton * >( FindChildByName( "ShareYouTubeURLButton" ) );
  1191. m_pShowRenderInfoButton = dynamic_cast< CExImageButton * >( FindChildByName( "ShowRenderInfoButton") );
  1192. if ( m_pDeleteButton )
  1193. {
  1194. SetXToRed( m_pDeleteButton );
  1195. }
  1196. m_pExportMovie->SetParent( GetInset() );
  1197. m_pYouTubeUpload->SetParent( GetInset() );
  1198. m_pYouTubeView->SetParent( GetInset() );
  1199. m_pYouTubeShareURL->SetParent( GetInset() );
  1200. m_pShowRenderInfoButton->SetParent( GetInset() );
  1201. m_pDeleteButton->SetParent( GetParent()->GetParent()->GetParent() );
  1202. m_pPlayButton->SetParent( GetParent()->GetParent()->GetParent() );
  1203. m_pRenderButton->SetParent( GetParent()->GetParent()->GetParent() );
  1204. m_pDeleteButton->AddActionSignalTarget( this );
  1205. m_pPlayButton->AddActionSignalTarget( this );
  1206. m_pRenderButton->AddActionSignalTarget( this );
  1207. }
  1208. void CReplayDetailsPanel::PerformLayout()
  1209. {
  1210. BaseClass::PerformLayout();
  1211. SetTall( GetParent()->GetTall() );
  1212. int nInsetWidth = GetInset()->GetWide();
  1213. int nScreenshotWidth = nInsetWidth * .55f;
  1214. // Setup info panels along the right-hand side
  1215. const int nBuffer = 7;
  1216. const int nLeftRightBuffer = 19;
  1217. int aPlaybackPos[2];
  1218. m_pPlaybackPanel->GetPos( aPlaybackPos[0], aPlaybackPos[1] );
  1219. int nInfoPanelsStartY = aPlaybackPos[1];
  1220. int nInfoPanelsCurrentY = nInfoPanelsStartY;
  1221. int nRightColumnWidth = nInsetWidth - nScreenshotWidth - nLeftRightBuffer - XRES(20);
  1222. #if defined( TF_CLIENT_DLL )
  1223. if ( m_pRecordsPanel->ShouldShow() )
  1224. {
  1225. m_pRecordsPanel->SetPos( nScreenshotWidth + nLeftRightBuffer, nInfoPanelsStartY );
  1226. m_pRecordsPanel->SetWide( nRightColumnWidth );
  1227. m_pRecordsPanel->InvalidateLayout( true, true );
  1228. m_pRecordsPanel->SetVisible( true );
  1229. nInfoPanelsCurrentY += m_pRecordsPanel->GetTall() + nBuffer;
  1230. }
  1231. else
  1232. #endif
  1233. {
  1234. m_pRecordsPanel->SetVisible( false );
  1235. }
  1236. int insetX, insetY;
  1237. GetInset()->GetPos( insetX, insetY );
  1238. m_pScrollPanel->SetPos( nScreenshotWidth + nLeftRightBuffer, nInfoPanelsCurrentY );
  1239. m_pScrollPanel->SetWide( nRightColumnWidth + XRES(20) );
  1240. m_pScrollPanel->SetTall( GetTall() - insetY - nInfoPanelsCurrentY );
  1241. m_pInfoPanel->SetWide( nRightColumnWidth );
  1242. int nCurrentY = 0;
  1243. for ( int i = 0; i < m_vecInfoPanels.Count(); ++i )
  1244. {
  1245. CBaseDetailsPanel *pPanel = m_vecInfoPanels[ i ];
  1246. if ( pPanel->ShouldShow() )
  1247. {
  1248. // Set the width since these panel's PerformLayout()'s depend on it
  1249. pPanel->SetWide( nRightColumnWidth );
  1250. // Call panel's PerformLayout() now
  1251. pPanel->InvalidateLayout( true, true );
  1252. pPanel->SetPos( 0, nCurrentY );
  1253. // Show it
  1254. pPanel->SetVisible( true );
  1255. // Update the current y position based on the panel's height (set in its PerformLayout())
  1256. nCurrentY += pPanel->GetTall() + nBuffer;
  1257. }
  1258. else
  1259. {
  1260. pPanel->SetVisible( false );
  1261. }
  1262. }
  1263. m_pInfoPanel->SetTall( nCurrentY );
  1264. m_pInfoPanel->InvalidateLayout( true );
  1265. m_pScrollPanel->InvalidateLayout( true );
  1266. m_pScrollPanel->GetScrollbar()->SetAutohideButtons( true );
  1267. m_pScrollPanel->GetScrollbar()->InvalidateLayout( true );
  1268. // @note Tom Bui: set the positions AGAIN now that we've invalidated, cause VGUI hates me
  1269. nCurrentY = 0;
  1270. for ( int i = 0; i < m_vecInfoPanels.Count(); ++i )
  1271. {
  1272. CBaseDetailsPanel *pPanel = m_vecInfoPanels[ i ];
  1273. if ( pPanel->ShouldShow() )
  1274. {
  1275. pPanel->SetPos( 0, nCurrentY );
  1276. nCurrentY += pPanel->GetTall() + nBuffer;
  1277. }
  1278. }
  1279. // Setup playback panel based on dimensions of first screenshot
  1280. CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
  1281. float flAspectRatio;
  1282. if ( pReplay->GetScreenshotCount() )
  1283. {
  1284. const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( 0 );
  1285. flAspectRatio = (float)pScreenshot->m_nWidth / pScreenshot->m_nHeight;
  1286. }
  1287. else
  1288. {
  1289. // Default to 4:3 if there are no screenshots
  1290. flAspectRatio = 4.0f/3;
  1291. }
  1292. if ( m_pItemManager->AreItemsMovies() )
  1293. {
  1294. m_pRenderButton->SetVisible( false );
  1295. m_pPlayButton->SetVisible( false );
  1296. m_pExportMovie->SetVisible( true );
  1297. m_pShowRenderInfoButton->SetVisible( true );
  1298. int nButtonY = nInfoPanelsStartY + m_pPlaybackPanel->GetTall() + YRES( 5 );
  1299. int nButtonX = 0;
  1300. m_pYouTubeUpload->SetPos( nButtonX, nButtonY );
  1301. m_pYouTubeView->SetPos( nButtonX, nButtonY );
  1302. nButtonX += m_pYouTubeUpload->GetWide() + XRES( 5 );
  1303. m_pYouTubeShareURL->SetPos( nButtonX, nButtonY );
  1304. nButtonX += m_pYouTubeShareURL->GetWide() + XRES( 5 );
  1305. m_pExportMovie->SetPos( nButtonX, nButtonY );
  1306. int aDeletePos[2];
  1307. m_pDeleteButton->GetPos( aDeletePos[0], aDeletePos[1] );
  1308. m_pDeleteButton->SetPos( ScreenWidth() / 2 + XRES( 195 ), aDeletePos[1] );
  1309. int aScreenshotPos[2];
  1310. m_pPlaybackPanel->GetPos( aScreenshotPos[0], aScreenshotPos[1] );
  1311. m_pShowRenderInfoButton->SetPos(
  1312. aScreenshotPos[0] + m_pPlaybackPanel->GetWide() - m_pShowRenderInfoButton->GetWide() - XRES( 8 ),
  1313. aScreenshotPos[1] + m_pPlaybackPanel->GetTall() - m_pShowRenderInfoButton->GetTall() - YRES( 8 )
  1314. );
  1315. // retrieve stats
  1316. if ( m_pYouTubeResponseHandler->m_handle == NULL )
  1317. {
  1318. IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
  1319. if ( pReplayItem && pReplayItem->IsItemAMovie() )
  1320. {
  1321. IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
  1322. if ( pMovie->IsUploaded() )
  1323. {
  1324. m_pYouTubeResponseHandler->m_handle = YouTube_GetVideoInfo( pMovie->GetUploadURL(), *m_pYouTubeResponseHandler );
  1325. SetYouTubeStatus( kYouTubeStatus_RetrievingInfo );
  1326. }
  1327. else
  1328. {
  1329. SetYouTubeStatus( kYouTubeStatus_NotUploaded );
  1330. }
  1331. }
  1332. }
  1333. }
  1334. else
  1335. {
  1336. m_pYouTubeUpload->SetVisible( false );
  1337. m_pYouTubeView->SetVisible( false );
  1338. m_pYouTubeShareURL->SetVisible( false );
  1339. m_pShowRenderInfoButton->SetVisible( false );
  1340. // Without this, the name label won't show when we automatically select the recently watched/saved
  1341. // performance, because the cuts panel width/height isn't set when UpdateNameLabel() gets called
  1342. // from within CCutsPanel::CCutsPanel().
  1343. m_pCutsPanel->UpdateNameLabel( m_iSelectedPerformance );
  1344. }
  1345. }
  1346. /*static*/ void CReplayDetailsPanel::OnPlayerWarningDlgConfirm( bool bConfirmed, void *pContext )
  1347. {
  1348. CReplayDetailsPanel *pPanel = (CReplayDetailsPanel*)pContext;
  1349. pPanel->ShowExportDialog();
  1350. }
  1351. void CReplayDetailsPanel::ShowExportDialog()
  1352. {
  1353. IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
  1354. if ( pReplayItem && pReplayItem->IsItemAMovie() )
  1355. {
  1356. IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
  1357. CFmtStr srcMovieFullFilename( "%s%s", g_pReplayMovieManager->GetRenderDir(), pMovie->GetMovieFilename() );
  1358. if ( !g_pFullFileSystem->FileExists( srcMovieFullFilename.Access() ) )
  1359. {
  1360. ShowMessageBox( "#Replay_ExportMovieError_Title", "#Replay_ExportMovieNoFile_Text", "#GameUI_OK" );
  1361. return;
  1362. }
  1363. }
  1364. if ( m_hExportMovieDialog == NULL )
  1365. {
  1366. m_hExportMovieDialog = new FileOpenDialog(NULL, "#Replay_FindExportMovieLocation", FOD_SAVE );
  1367. #ifdef USE_WEBM_FOR_REPLAY
  1368. m_hExportMovieDialog->AddFilter("*.webm", "#Replay_WebMMovieFiles", true );
  1369. #else
  1370. m_hExportMovieDialog->AddFilter("*.mov", "#Replay_MovieFiles", true );
  1371. #endif
  1372. m_hExportMovieDialog->AddActionSignalTarget( this );
  1373. if ( !FStrEq( replay_movie_export_last_dir.GetString(), "" ) )
  1374. {
  1375. m_hExportMovieDialog->SetStartDirectory( replay_movie_export_last_dir.GetString() );
  1376. }
  1377. }
  1378. m_hExportMovieDialog->DoModal(false);
  1379. m_hExportMovieDialog->Activate();
  1380. }
  1381. void CReplayDetailsPanel::OnFileSelected( const char *fullpath )
  1382. {
  1383. // this can take a while, put up a waiting cursor
  1384. surface()->SetCursor(dc_hourglass);
  1385. IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
  1386. if ( pReplayItem && pReplayItem->IsItemAMovie() )
  1387. {
  1388. IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
  1389. CFmtStr srcMovieFullFilename( "%s%s", g_pReplayMovieManager->GetRenderDir(), pMovie->GetMovieFilename() );
  1390. if ( !engine->CopyLocalFile( srcMovieFullFilename.Access(), fullpath ) )
  1391. {
  1392. ShowMessageBox( "#Replay_ExportMovieError_Title", "#Replay_ExportMovieError_Text", "#GameUI_OK" );
  1393. }
  1394. else
  1395. {
  1396. ShowMessageBox( "#Replay_ExportMovieSuccess_Title", "#Replay_ExportMovieSuccess_Text", "#GameUI_OK" );
  1397. }
  1398. char basepath[ MAX_PATH ];
  1399. Q_ExtractFilePath( fullpath, basepath, sizeof( basepath ) );
  1400. replay_movie_export_last_dir.SetValue( basepath );
  1401. }
  1402. // change the cursor back to normal
  1403. surface()->SetCursor(dc_user);
  1404. }
  1405. void CReplayDetailsPanel::OnCommand( const char *pCommand )
  1406. {
  1407. if ( FStrEq( pCommand, "delete_replayitem" ) )
  1408. {
  1409. ReplayUI_GetBrowserPanel()->AttemptToDeleteReplayItem( this, m_hReplayItem, m_pItemManager, m_iSelectedPerformance );
  1410. return;
  1411. }
  1412. else if ( FStrEq( pCommand, "render_replay_dlg" ) )
  1413. {
  1414. ShowRenderDialog();
  1415. return;
  1416. }
  1417. else if ( FStrEq( pCommand, "play" ) )
  1418. {
  1419. if ( engine->IsInGame() )
  1420. {
  1421. ShowPlayConfirmationDialog();
  1422. }
  1423. else
  1424. {
  1425. g_pClientReplayContext->PlayReplay( m_hReplay, m_iSelectedPerformance, true );
  1426. }
  1427. return;
  1428. }
  1429. else if ( FStrEq( pCommand, "exportmovie" ) )
  1430. {
  1431. IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
  1432. if ( !pReplayItem || !pReplayItem->IsItemAMovie() )
  1433. return;
  1434. IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
  1435. if ( !pMovie )
  1436. return;
  1437. if ( replay_movie_reveal_warning.GetBool() )
  1438. {
  1439. #ifdef USE_WEBM_FOR_REPLAY
  1440. CTFMessageBoxDialog *pDialog = ShowMessageBox( "#Replay_Tip", "#Replay_UseVLCPlayer", "#Replay_ThanksIWill", OnPlayerWarningDlgConfirm );
  1441. #else
  1442. CTFMessageBoxDialog *pDialog = ShowMessageBox( "#Replay_Tip", "#Replay_UseQuickTimePlayer", "#Replay_ThanksIWill", OnPlayerWarningDlgConfirm );
  1443. #endif
  1444. pDialog->SetContext( this );
  1445. replay_movie_reveal_warning.SetValue( 0 );
  1446. }
  1447. else if ( pMovie->GetRenderSettings().m_bRaw )
  1448. {
  1449. ShowMessageBox( "#Replay_CantExport", "#YouTube_Upload_MovieIsRaw", "#GameUI_OK" );
  1450. }
  1451. else
  1452. {
  1453. ShowExportDialog();
  1454. }
  1455. }
  1456. else if ( FStrEq( pCommand, "youtubeupload" ) )
  1457. {
  1458. IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
  1459. if ( pReplayItem && pReplayItem->IsItemAMovie() )
  1460. {
  1461. IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
  1462. if ( !pMovie )
  1463. return;
  1464. if ( pMovie->GetRenderSettings().m_bRaw )
  1465. {
  1466. ShowMessageBox( "#Replay_CantUpload", "#YouTube_Upload_MovieIsRaw", "#GameUI_OK" );
  1467. return;
  1468. }
  1469. // Movie already exists?
  1470. CFmtStr srcMovieFullFilename( "%s%s", g_pReplayMovieManager->GetRenderDir(), pMovie->GetMovieFilename() );
  1471. if ( !g_pFullFileSystem->FileExists( srcMovieFullFilename.Access() ) )
  1472. {
  1473. ShowMessageBox( "#YouTube_Upload_Title", "#YouTube_Upload_MissingFile", "#GameUI_OK" );
  1474. return;
  1475. }
  1476. else if ( pMovie->IsUploaded() )
  1477. {
  1478. CTFGenericConfirmDialog *pDialog = ShowConfirmDialog( "#YouTube_Upload_Title", "#YouTube_FileAlreadyUploaded", "#GameUI_OK", "#GameuI_CancelBold", &ConfirmUploadMovie, this );
  1479. pDialog->SetContext( this );
  1480. }
  1481. else
  1482. {
  1483. ConfirmUploadMovie( true, this );
  1484. }
  1485. }
  1486. }
  1487. else if ( FStrEq( pCommand, "viewyoutube" ) )
  1488. {
  1489. if ( steamapicontext && steamapicontext->SteamFriends() && m_pYouTubeResponseHandler->m_strVideoURL.IsEmpty() == false )
  1490. {
  1491. steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( m_pYouTubeResponseHandler->m_strVideoURL.Get() );
  1492. }
  1493. }
  1494. else if ( FStrEq( pCommand, "shareyoutubeurl" ) )
  1495. {
  1496. system()->SetClipboardText( m_pYouTubeResponseHandler->m_strVideoURL.Get(), m_pYouTubeResponseHandler->m_strVideoURL.Length() );
  1497. ShowMessageBox( "#Replay_CopyURL_Title", "#Replay_CopyURL_Text", "#GameUI_OK" );
  1498. }
  1499. else if ( FStrEq( pCommand, "showrenderinfo" ) )
  1500. {
  1501. ShowRenderInfo();
  1502. }
  1503. else
  1504. {
  1505. BaseClass::OnCommand( pCommand );
  1506. }
  1507. }
  1508. void CReplayDetailsPanel::ShowRenderInfo()
  1509. {
  1510. IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
  1511. if ( !pReplayItem || !pReplayItem->IsItemAMovie() )
  1512. return;
  1513. IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
  1514. const ReplayRenderSettings_t &Settings = pMovie->GetRenderSettings();
  1515. const wchar_t *pCodecName = g_pVideo ? g_pVideo->GetCodecName( Settings.m_Codec ) : L"?";
  1516. wchar_t *pAAEnabled = g_pVGuiLocalize->Find( Settings.m_bAAEnabled ? "#Replay_Enabled" : "#Replay_Disabled" );
  1517. wchar_t *pRaw = g_pVGuiLocalize->Find( Settings.m_bRaw ? "#Replay_Yes" : "#Replay_No" );
  1518. CFmtStr fmtRes( "%ix%i", Settings.m_nWidth, Settings.m_nHeight );
  1519. CFmtStr fmtFramerate( "%.3f", Settings.m_FPS.GetFPS() );
  1520. KeyValuesAD kvParams( "params" );
  1521. kvParams->SetString( "res", fmtRes.Access() );
  1522. kvParams->SetString( "framerate", fmtFramerate.Access() );
  1523. kvParams->SetInt( "motionblurquality", Settings.m_nMotionBlurQuality );
  1524. kvParams->SetInt( "encodingquality", Settings.m_nEncodingQuality );
  1525. kvParams->SetWString( "codec", pCodecName );
  1526. kvParams->SetWString( "antialiasing", pAAEnabled );
  1527. kvParams->SetString( "rendertime", CReplayTime::FormatTimeString( pMovie->GetRenderTime() ) );
  1528. kvParams->SetWString( "raw", pRaw );
  1529. wchar_t wszStr[1024];
  1530. g_pVGuiLocalize->ConstructString_safe(
  1531. wszStr,
  1532. "#Replay_MovieRenderInfo",
  1533. kvParams
  1534. );
  1535. ShowMessageBox( "#Replay_RenderInfo", wszStr, "#GameUI_OK" );
  1536. }
  1537. void CReplayDetailsPanel::GoBack()
  1538. {
  1539. // Send to parent
  1540. GetParent()->OnCommand( "back" );
  1541. }
  1542. void CReplayDetailsPanel::ShowPlayConfirmationDialog()
  1543. {
  1544. CConfirmDisconnectFromServerDialog *pConfirm = SETUP_PANEL( new CConfirmDisconnectFromServerDialog( this ) );
  1545. if ( pConfirm )
  1546. {
  1547. pConfirm->Show();
  1548. }
  1549. }
  1550. void CReplayDetailsPanel::OnConfirmDisconnect( KeyValues *pParams )
  1551. {
  1552. if ( pParams->GetBool( "confirmed" ) )
  1553. {
  1554. g_pClientReplayContext->PlayReplay( m_hReplay, m_iSelectedPerformance, true );
  1555. }
  1556. }
  1557. void CReplayDetailsPanel::OnMessage( const KeyValues* pParams, VPANEL hFromPanel )
  1558. {
  1559. if ( FStrEq( pParams->GetName(), "ReplayItemDeleted" ) )
  1560. {
  1561. const int iPerformance = const_cast< KeyValues * >( pParams )->GetInt( "perf", -1 );
  1562. if ( iPerformance >= 0 )
  1563. {
  1564. CReplayPerformance *pPerformance = GetGenericClassBasedReplay( m_hReplay )->GetPerformance( m_iSelectedPerformance );
  1565. g_pReplayPerformanceManager->DeletePerformance( pPerformance );
  1566. m_pCutsPanel->InvalidateLayout( true, false ); // Without this, m_nVisibleButtons will be wrong.
  1567. m_pCutsPanel->OnPerformanceDeleted( m_iSelectedPerformance );
  1568. }
  1569. else
  1570. {
  1571. GoBack();
  1572. }
  1573. return;
  1574. }
  1575. BaseClass::OnMessage( pParams, hFromPanel );
  1576. }
  1577. void CReplayDetailsPanel::ShowRenderDialog()
  1578. {
  1579. ::ReplayUI_ShowRenderDialog( this, m_hReplay, false, m_iSelectedPerformance );
  1580. }
  1581. void CReplayDetailsPanel::FreeMovieFileLock()
  1582. {
  1583. m_pPlaybackPanel->FreeMovieMaterial();
  1584. }
  1585. void CReplayDetailsPanel::SetYouTubeStatus( eYouTubeStatus status )
  1586. {
  1587. m_pYouTubeUpload->SetVisible( status == kYouTubeStatus_CouldNotRetrieveInfo || status == kYouTubeStatus_NotUploaded );
  1588. m_pYouTubeUpload->SetEnabled( status == kYouTubeStatus_CouldNotRetrieveInfo || status == kYouTubeStatus_NotUploaded );
  1589. m_pYouTubeView->SetVisible( !m_pYouTubeUpload->IsVisible() );
  1590. m_pYouTubeView->SetEnabled( status == kYouTubeStatus_RetrievedInfo );
  1591. m_pYouTubeShareURL->SetEnabled( status == kYouTubeStatus_RetrievedInfo );
  1592. }
  1593. void CReplayDetailsPanel::OnMousePressed( MouseCode code )
  1594. {
  1595. if ( code == MOUSE_LEFT )
  1596. {
  1597. RequestFocus();
  1598. }
  1599. }
  1600. void CReplayDetailsPanel::OnKeyCodeTyped( KeyCode code )
  1601. {
  1602. if ( code == KEY_DELETE )
  1603. {
  1604. OnCommand( "delete_replayitem" );
  1605. }
  1606. BaseClass::OnKeyCodeTyped( code );
  1607. }
  1608. #endif