aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/client/replay/vgui
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /sp/src/game/client/replay/vgui
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'sp/src/game/client/replay/vgui')
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserbasepage.cpp216
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserbasepage.h67
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserbasepanel.cpp39
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserbasepanel.h25
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserdetailspanel.cpp1950
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserdetailspanel.h462
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowseritemmanager.cpp133
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowseritemmanager.h40
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserlistitempanel.cpp1090
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserlistitempanel.h239
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserlistpanel.cpp484
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserlistpanel.h80
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowsermainpanel.cpp449
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowsermainpanel.h89
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowsermovieplayerpanel.cpp220
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowsermovieplayerpanel.h57
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserpreviewpanel.cpp301
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserpreviewpanel.h120
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserrenderdialog.cpp639
-rw-r--r--sp/src/game/client/replay/vgui/replaybrowserrenderdialog.h109
-rw-r--r--sp/src/game/client/replay/vgui/replayconfirmquitdlg.cpp167
-rw-r--r--sp/src/game/client/replay/vgui/replayconfirmquitdlg.h45
-rw-r--r--sp/src/game/client/replay/vgui/replayinputpanel.cpp245
-rw-r--r--sp/src/game/client/replay/vgui/replayinputpanel.h25
-rw-r--r--sp/src/game/client/replay/vgui/replaymessagepanel.cpp375
-rw-r--r--sp/src/game/client/replay/vgui/replaymessagepanel.h85
-rw-r--r--sp/src/game/client/replay/vgui/replayperformanceeditor.cpp2658
-rw-r--r--sp/src/game/client/replay/vgui/replayperformanceeditor.h239
-rw-r--r--sp/src/game/client/replay/vgui/replayperformancesavedlg.cpp261
-rw-r--r--sp/src/game/client/replay/vgui/replayperformancesavedlg.h27
-rw-r--r--sp/src/game/client/replay/vgui/replayreminderpanel.cpp163
-rw-r--r--sp/src/game/client/replay/vgui/replayreminderpanel.h50
-rw-r--r--sp/src/game/client/replay/vgui/replayrenderoverlay.cpp380
-rw-r--r--sp/src/game/client/replay/vgui/replayrenderoverlay.h73
34 files changed, 11602 insertions, 0 deletions
diff --git a/sp/src/game/client/replay/vgui/replaybrowserbasepage.cpp b/sp/src/game/client/replay/vgui/replaybrowserbasepage.cpp
new file mode 100644
index 00000000..15065509
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserbasepage.cpp
@@ -0,0 +1,216 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "vgui/IInput.h"
+#include "vgui/ISurface.h"
+#include "vgui_controls/TextEntry.h"
+
+#include "replaybrowserbasepage.h"
+#include "replaybrowserdetailspanel.h"
+#include "replaybrowsermainpanel.h"
+#include "replaybrowserlistpanel.h"
+#include "replay/ireplaymoviemanager.h"
+#include "replay/ireplaymanager.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+//-----------------------------------------------------------------------------
+
+extern IReplayMovieManager *g_pReplayMovieManager;
+
+//-----------------------------------------------------------------------------
+
+CReplayBrowserBasePage::CReplayBrowserBasePage( Panel *pParent )
+: BaseClass( pParent, "BasePage" )
+{
+ m_pReplayList = new CReplayListPanel( this, "ReplayList" );
+ m_pReplayList->SetFirstColumnWidth( 0 );
+
+ m_pSearchTextEntry = new vgui::TextEntry( this, "SearchTextEntry" );
+ m_pSearchTextEntry->SelectAllOnFocusAlways( true );
+ m_pSearchTextEntry->AddActionSignalTarget( this );
+ m_pSearchTextEntry->SetCatchEnterKey( true );
+
+ InvalidateLayout( true, true );
+
+ m_pReplayList->AddReplaysToList();
+
+ ivgui()->AddTickSignal( GetVPanel(), 100 );
+}
+
+CReplayBrowserBasePage::~CReplayBrowserBasePage()
+{
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CReplayBrowserBasePage::OnTick()
+{
+ if ( !IsVisible() )
+ return;
+
+ int nCursorX, nCursorY;
+ input()->GetCursorPos( nCursorX, nCursorY );
+
+ if ( input()->IsMouseDown( MOUSE_LEFT ) &&
+ !m_pSearchTextEntry->IsWithin( nCursorX, nCursorY ) &&
+ m_pSearchTextEntry->HasFocus() )
+ {
+ RequestFocus();
+ }
+}
+
+void CReplayBrowserBasePage::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/basepage.res", "GAME" );
+
+ m_pSearchTextEntry->SetText( "#Replay_SearchText" );
+}
+
+void CReplayBrowserBasePage::OnPageShow()
+{
+ BaseClass::OnPageShow();
+ m_pSearchTextEntry->SetText( "#Replay_SearchText" );
+}
+
+void CReplayBrowserBasePage::OnSelectionStarted()
+{
+ PostActionSignal( new KeyValues("SelectionUpdate", "open", 1 ) );
+}
+
+void CReplayBrowserBasePage::OnSelectionEnded()
+{
+ PostActionSignal( new KeyValues("SelectionUpdate", "open", 0 ) );
+}
+
+void CReplayBrowserBasePage::CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem )
+{
+ m_pReplayList->CleanupUIForReplayItem( hReplayItem );
+}
+
+void CReplayBrowserBasePage::AddReplay( ReplayHandle_t hReplay )
+{
+ m_pReplayList->AddReplayItem( hReplay );
+}
+
+void CReplayBrowserBasePage::DeleteReplay( ReplayHandle_t hReplayItem )
+{
+ IReplayItemManager *pItemManager;
+ if ( FindReplayItem( hReplayItem, &pItemManager ) )
+ {
+ ReplayUI_GetBrowserPanel()->AttemptToDeleteReplayItem( this, hReplayItem, pItemManager, -1 );
+ }
+}
+
+void CReplayBrowserBasePage::OnCancelSelection()
+{
+}
+
+void CReplayBrowserBasePage::GoBack()
+{
+ DeleteDetailsPanelAndShowReplayList();
+}
+
+void CReplayBrowserBasePage::OnReplayItemDeleted( KeyValues *pParams )
+{
+ GoBack();
+}
+
+void CReplayBrowserBasePage::OnTextChanged( KeyValues *data )
+{
+ wchar_t wszText[256];
+ m_pSearchTextEntry->GetText( wszText, ARRAYSIZE( wszText ) );
+ m_pReplayList->ApplyFilter( wszText );
+ InvalidateLayout();
+}
+
+void CReplayBrowserBasePage::OnCommand( const char *pCommand )
+{
+ // User wants details on a replay?
+ if ( !V_strnicmp( pCommand, "details", 7 ) )
+ {
+ // Get rid of preview panel
+ m_pReplayList->ClearPreviewPanel();
+
+ QueryableReplayItemHandle_t hReplayItem = (QueryableReplayItemHandle_t)atoi( pCommand + 7 );
+ IReplayItemManager *pItemManager;
+ IQueryableReplayItem *pReplayItem = FindReplayItem( hReplayItem, &pItemManager ); Assert( pReplayItem );
+ if ( pReplayItem )
+ {
+ // Get performance
+ int iPerformance = -1;
+ const char *pPerformanceStr = V_strstr( pCommand + 8, "_" );
+ if ( pPerformanceStr )
+ {
+ iPerformance = atoi( pPerformanceStr + 1 );
+ }
+
+ m_hReplayDetailsPanel = vgui::SETUP_PANEL( new CReplayDetailsPanel( this, hReplayItem, iPerformance, pItemManager ) );
+ m_hReplayDetailsPanel->SetVisible( true );
+ m_hReplayDetailsPanel->MoveToFront();
+
+ m_pReplayList->SetVisible( false );
+
+ surface()->PlaySound( "replay\\showdetails.wav" );
+ }
+ }
+
+ // "back" button was hit in details panel?
+ else if ( FStrEq( pCommand, "back" ) )
+ {
+ GoBack();
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+void CReplayBrowserBasePage::DeleteDetailsPanelAndShowReplayList()
+{
+ // Delete the panel
+ if ( m_hReplayDetailsPanel )
+ {
+ m_hReplayDetailsPanel->MarkForDeletion();
+ m_hReplayDetailsPanel = NULL;
+ }
+
+ m_pReplayList->SetVisible( true );
+}
+
+void CReplayBrowserBasePage::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ if ( m_pSearchTextEntry )
+ {
+ const bool bHasReplays = g_pReplayManager && g_pReplayManager->GetReplayCount();
+ const bool bHasMovies = g_pReplayMovieManager && g_pReplayMovieManager->GetMovieCount();
+ int aListPos[2];
+ m_pReplayList->GetPos( aListPos[0], aListPos[1] );
+ m_pSearchTextEntry->SetPos( aListPos[0] + m_pReplayList->GetWide() - m_pSearchTextEntry->GetWide(), YRES( 5 ) );
+ m_pSearchTextEntry->SetVisible( bHasReplays || bHasMovies );
+ }
+
+ // Invalidate the list too, because we might be laying out due to a replay being removed from the list.
+ m_pReplayList->InvalidateLayout();
+}
+
+void CReplayBrowserBasePage::FreeDetailsPanelMovieLock()
+{
+ m_hReplayDetailsPanel->FreeMovieFileLock();
+}
+
+bool CReplayBrowserBasePage::IsDetailsViewOpen()
+{
+ return m_hReplayDetailsPanel.Get() != NULL && m_hReplayDetailsPanel->IsVisible();
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaybrowserbasepage.h b/sp/src/game/client/replay/vgui/replaybrowserbasepage.h
new file mode 100644
index 00000000..a9733d38
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserbasepage.h
@@ -0,0 +1,67 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef REPLAYBROWSER_BASEPAGE_H
+#define REPLAYBROWSER_BASEPAGE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "vgui_controls/PropertyPage.h"
+#include "replaybrowseritemmanager.h"
+#include "replay/genericclassbased_replay.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class CReplayListPanel;
+class CExLabel;
+class CReplayDetailsPanel;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CReplayBrowserBasePage : public PropertyPage
+{
+ DECLARE_CLASS_SIMPLE( CReplayBrowserBasePage, PropertyPage );
+public:
+ CReplayBrowserBasePage( Panel *pParent );
+ virtual ~CReplayBrowserBasePage();
+
+ void DeleteDetailsPanelAndShowReplayList();
+ bool IsDetailsViewOpen();
+ void GoBack();
+
+ // Movie-only stuff
+ void FreeDetailsPanelMovieLock();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void OnCommand( const char *pCommand );
+ virtual void PerformLayout();
+
+ MESSAGE_FUNC( OnPageShow, "PageShow" );
+ MESSAGE_FUNC( OnSelectionStarted, "SelectionStarted" );
+ MESSAGE_FUNC( OnSelectionEnded, "SelectionEnded" );
+ MESSAGE_FUNC( OnCancelSelection, "CancelSelection" );
+ MESSAGE_FUNC_PARAMS( OnReplayItemDeleted, "ReplayItemDeleted", pParams );
+ MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
+
+ void AddReplay( ReplayHandle_t hReplay );
+ void DeleteReplay( ReplayHandle_t hReplay );
+
+ void OnTick();
+
+ virtual void CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem );
+
+ vgui::TextEntry *m_pSearchTextEntry;
+ CReplayListPanel *m_pReplayList;
+ DHANDLE< CReplayDetailsPanel > m_hReplayDetailsPanel;
+};
+
+#endif // REPLAYBROWSER_BASEPAGE_H
diff --git a/sp/src/game/client/replay/vgui/replaybrowserbasepanel.cpp b/sp/src/game/client/replay/vgui/replaybrowserbasepanel.cpp
new file mode 100644
index 00000000..b8bd0a9f
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserbasepanel.cpp
@@ -0,0 +1,39 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowserbasepanel.h"
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+CReplayBasePanel::CReplayBasePanel( Panel *pParent, const char *pName )
+: BaseClass( pParent, pName )
+{
+}
+
+void CReplayBasePanel::GetPosRelativeToAncestor( Panel *pAncestor, int &nXOut, int &nYOut )
+{
+ nXOut = nYOut = 0;
+
+ Panel *pCurrent = this;
+ while ( pCurrent && pCurrent != pAncestor )
+ {
+ int x,y;
+ pCurrent->GetPos( x, y );
+ nXOut += x;
+ nYOut += y;
+ pCurrent = pCurrent->GetParent();
+ }
+
+ Assert( pAncestor == pCurrent );
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaybrowserbasepanel.h b/sp/src/game/client/replay/vgui/replaybrowserbasepanel.h
new file mode 100644
index 00000000..62d17bf5
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserbasepanel.h
@@ -0,0 +1,25 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#ifndef REPLAYBASEPANEL_H
+#define REPLAYBASEPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "vgui_controls/EditablePanel.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Base panel for replay panels
+//-----------------------------------------------------------------------------
+class CReplayBasePanel : public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayBasePanel, vgui::EditablePanel );
+public:
+ CReplayBasePanel( Panel *pParent, const char *pName );
+
+ void GetPosRelativeToAncestor( Panel *pAncestor, int &nXOut, int &nYOut );
+};
+
+#endif // REPLAYBASEPANEL_H
diff --git a/sp/src/game/client/replay/vgui/replaybrowserdetailspanel.cpp b/sp/src/game/client/replay/vgui/replaybrowserdetailspanel.cpp
new file mode 100644
index 00000000..ef921a43
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserdetailspanel.cpp
@@ -0,0 +1,1950 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowserdetailspanel.h"
+#include "replaybrowsermainpanel.h"
+#include "replaybrowseritemmanager.h"
+#include "replaybrowsermovieplayerpanel.h"
+#include "replaybrowserrenderdialog.h"
+#include "vgui/IVGui.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "vgui/ILocalize.h"
+#include "vgui_controls/FileOpenDialog.h"
+#include "vgui_controls/PanelListPanel.h"
+#include "vgui_controls/ScrollBar.h"
+#include "vgui_controls/ScrollBarSlider.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/TextImage.h"
+#include "vgui_avatarimage.h"
+#include "gamestringpool.h"
+#include "replay/genericclassbased_replay.h"
+#include "replaybrowserlistitempanel.h"
+#include "confirm_dialog.h"
+#include "replay/ireplaymoviemanager.h"
+#include "replay/ireplaymanager.h"
+#include "replay/ireplayrenderqueue.h"
+#include "replay/screenshot.h"
+#include "replay/ireplayperformancemanager.h"
+#include "replay/performance.h"
+#include "vgui/ISystem.h"
+#include "youtubeapi.h"
+#include "replay/replayyoutubeapi.h"
+#include "ienginevgui.h"
+#include <filesystem.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+//-----------------------------------------------------------------------------
+
+extern IClientReplayContext *g_pClientReplayContext;
+extern IReplayMovieManager *g_pReplayMovieManager;
+extern IReplayPerformanceManager *g_pReplayPerformanceManager;
+
+//-----------------------------------------------------------------------------
+
+ConVar replay_movie_reveal_warning( "replay_movie_reveal_warning", "1", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_HIDDEN | FCVAR_DONTRECORD );
+ConVar replay_movie_export_last_dir( "replay_movie_export_last_dir", "", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_HIDDEN | FCVAR_DONTRECORD );
+ConVar replay_renderqueue_first_add( "replay_renderqueue_first_add", "0", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_HIDDEN | FCVAR_DONTRECORD );
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+class CConfirmDisconnectFromServerDialog : public CConfirmDialog
+{
+ DECLARE_CLASS_SIMPLE( CConfirmDisconnectFromServerDialog, CConfirmDialog );
+public:
+ CConfirmDisconnectFromServerDialog( Panel *pParent )
+ : BaseClass( pParent )
+ {
+ surface()->PlaySound( "replay\\replaydialog_warn.wav" );
+ }
+
+ const wchar_t *GetText() { return g_pVGuiLocalize->Find( "#Replay_ConfirmDisconnectFromServer" ); }
+
+ void ApplySchemeSettings( vgui::IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetTall( YRES( 170 ) );
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+CKeyValueLabelPanel::CKeyValueLabelPanel( Panel *pParent, const char *pKey, const char *pValue )
+: EditablePanel( pParent, "KeyValueLabelPanel" )
+{
+ SetScheme( "ClientScheme" );
+
+ m_pLabels[ 0 ] = new CExLabel( this, "KeyLabel", pKey );
+ m_pLabels[ 1 ] = new CExLabel( this, "ValueLabel", pValue );
+}
+
+CKeyValueLabelPanel::CKeyValueLabelPanel( Panel *pParent, const char *pKey, const wchar_t *pValue )
+: EditablePanel( pParent, "KeyValueLabelPanel" )
+{
+ SetScheme( "ClientScheme" );
+
+ m_pLabels[ 0 ] = new CExLabel( this, "KeyLabel", pKey );
+ m_pLabels[ 1 ] = new CExLabel( this, "ValueLabel", pValue );
+}
+
+void CKeyValueLabelPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ HFont hFont = scheme()->GetIScheme( GetScheme() )->GetFont( "ReplayBrowserSmallest", true );
+
+ m_pLabels[ 0 ]->SetFont( hFont );
+ m_pLabels[ 1 ]->SetFont( hFont );
+
+ m_pLabels[ 0 ]->SetFgColor( Color( 119, 107, 95, 255 ) );
+ m_pLabels[ 1 ]->SetFgColor( Color( 255, 255, 255, 255 ) );
+}
+
+void CKeyValueLabelPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int nContentWidth, nContentHeight;
+ m_pLabels[0]->GetContentSize( nContentWidth, nContentHeight );
+
+ int iMidX = GetParent()->GetWide() * 0.55f;
+ m_pLabels[0]->SetBounds( 0, 0, iMidX, nContentHeight );
+
+ m_pLabels[1]->GetContentSize( nContentWidth, nContentHeight );
+ m_pLabels[1]->SetBounds( iMidX, 0, iMidX, nContentHeight );
+}
+
+int CKeyValueLabelPanel::GetHeight() const
+{
+ int nWidth, nHeight;
+ m_pLabels[ 0 ]->GetContentSize( nWidth, nHeight );
+ return nHeight;
+}
+
+int CKeyValueLabelPanel::GetValueHeight() const
+{
+ int nWidth, nHeight;
+ m_pLabels[ 1 ]->GetContentSize( nWidth, nHeight );
+ return nHeight;
+}
+
+void CKeyValueLabelPanel::SetValue( const wchar_t *pValue )
+{
+ m_pLabels[ 1 ]->SetText( pValue );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+
+CBaseDetailsPanel::CBaseDetailsPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay )
+: EditablePanel( pParent, pName ),
+ m_hReplay( hReplay ),
+ m_bShouldShow( true )
+{
+ SetScheme( "ClientScheme" );
+
+ m_pInsetPanel = new EditablePanel( this, "InsetPanel" );
+}
+
+void CBaseDetailsPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetBorder( pScheme->GetBorder( "ReplayStatsBorder" ) );
+ SetBgColor( Color( 0,0,0, 255 ) );
+}
+
+void CBaseDetailsPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // Setup inset panel bounds
+ const int n = GetMarginSize();
+ m_pInsetPanel->SetBounds( n, n, GetWide() - 2*n, GetTall() - 2*n );
+}
+
+//-----------------------------------------------------------------------------
+
+CRecordsPanel::CRecordsPanel( Panel *pParent, ReplayHandle_t hReplay )
+: CBaseDetailsPanel( pParent, "RecordsPanel", hReplay )
+{
+ m_pClassImage = new ImagePanel( this, "ClassImage" );
+}
+
+void CRecordsPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetBorder( pScheme->GetBorder( "ReplayDefaultBorder" ) );
+ SetBgColor( Color( 0,0,0,0 ) );
+}
+
+void CRecordsPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // Figure out the class image name
+ char szImage[MAX_OSPATH];
+ const CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+ V_snprintf( szImage, sizeof( szImage ), "class_sel_sm_%s_%s", pReplay->GetMaterialFriendlyPlayerClass(), pReplay->GetPlayerTeam() ); // Cause default image to display
+
+ int nHeight = 0;
+
+ // Get the image
+ IImage *pImage = scheme()->GetImage( szImage, true );
+ if ( pImage )
+ {
+ // Get image dimensions
+ int nImageWidth, nImageHeight;
+ pImage->GetSize( nImageWidth, nImageHeight );
+
+ // Compute height of the records panel as a little smaller than the image itself
+ nHeight = nImageHeight * 11 / 16;
+
+ // Setup the image panel - parent to records panel parent so it goes out of the records panel's bounds a bit
+ const int nMargin = 7;
+ const float flScale = 1.2f;
+ m_pClassImage->SetImage( pImage );
+ m_pClassImage->SetParent( GetParent() );
+ m_pClassImage->SetShouldScaleImage( true );
+ m_pClassImage->SetScaleAmount( flScale );
+ int nX, nY;
+ GetPos( nX, nY );
+ m_pClassImage->SetBounds( nX + nMargin, nY - flScale * nImageHeight + GetTall() - nMargin, nImageWidth * flScale, nImageHeight * flScale );
+
+#if !defined( TF_CLIENT_DLL )
+ m_pClassImage->SetVisible( false );
+#endif
+ }
+
+ SetTall( nHeight );
+}
+
+//-----------------------------------------------------------------------------
+
+CStatsPanel::CStatsPanel( Panel *pParent, ReplayHandle_t hReplay )
+: CBaseDetailsPanel( pParent, "StatsPanel", hReplay )
+{
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
+
+ // Don't show the panel unless there are stats to display
+ m_bShouldShow = false;
+
+ // Create all stat labels
+ RoundStats_t const &stats = pReplay->GetStats();
+ for ( int i = 0; i < REPLAY_MAX_DISPLAY_GAMESTATS; ++i )
+ {
+ const int nCurStat = stats.Get( g_pReplayDisplayGameStats[i].m_nStat );
+ if ( !nCurStat )
+ {
+ m_paStatLabels[ i ] = NULL;
+ continue;
+ }
+
+ // Setup value
+ char szValue[256];
+ V_snprintf( szValue, sizeof( szValue ), "%i", nCurStat );
+
+ // Create labels for this stat
+ m_paStatLabels[ i ] = new CKeyValueLabelPanel( GetInset(), g_pReplayDisplayGameStats[i].m_pStatLocalizationToken, szValue );
+
+ // At least one stat to display
+ m_bShouldShow = true;
+ }
+}
+
+void CStatsPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+}
+
+void CStatsPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int nY = 0;
+ for ( int i = 0; i < REPLAY_MAX_DISPLAY_GAMESTATS; ++i )
+ {
+ CKeyValueLabelPanel *pCurStatLabels = m_paStatLabels[ i ];
+ if ( pCurStatLabels )
+ {
+ pCurStatLabels->SetBounds( 0, nY, GetInset()->GetWide(), YRES(13) );
+ nY += YRES(13);
+ }
+ }
+
+ SetTall( nY + GetMarginSize() * 2 );
+}
+
+//-----------------------------------------------------------------------------
+
+CDominationsPanel::CDominationsPanel( Panel *pParent, ReplayHandle_t hReplay )
+: CBaseDetailsPanel( pParent, "DominationsPanel", hReplay )
+{
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
+
+ m_pNumDominationsImage = new ImagePanel( GetInset(), "NumDominations" );
+
+ char szImage[256];
+ int nNumDominations = pReplay->GetDominationCount();
+
+ // Setup the # of dominations image
+ V_snprintf( szImage, sizeof( szImage ), "../hud/leaderboard_dom%i", nNumDominations );
+ m_pNumDominationsImage->SetImage( szImage );
+
+ // Add avatars for each person dominated
+ if ( steamapicontext && steamapicontext->SteamUtils() && steamapicontext->SteamUtils()->GetConnectedUniverse() )
+ {
+ for ( int i = 0; i < nNumDominations; ++i )
+ {
+ CAvatarImage *pAvatar = new CAvatarImage();
+ CSteamID id( pReplay->GetDomination( i )->m_nVictimFriendId, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual );
+ pAvatar->SetAvatarSteamID( id );
+ pAvatar->SetAvatarSize( 32, 32 );
+ pAvatar->UpdateFriendStatus();
+
+ ImagePanel *pImagePanel = new ImagePanel( GetInset(), "DominationImage" );
+ pImagePanel->SetImage( pAvatar );
+ pImagePanel->SetShouldScaleImage( false );
+
+ m_vecDominationImages.AddToTail( pImagePanel );
+ }
+ }
+}
+
+void CDominationsPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+}
+
+void CDominationsPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ const int nBuffer = 7;
+
+ int nImageWidth, nImageHeight;
+ m_pNumDominationsImage->GetImage()->GetSize( nImageWidth, nImageHeight );
+ m_pNumDominationsImage->SetBounds( 0, 0, nImageWidth, nImageHeight );
+ int nX = nImageWidth + 2*nBuffer;
+ int nY = 0;
+
+ for ( int i = 0; i < m_vecDominationImages.Count(); ++i )
+ {
+ ImagePanel *pImagePanel = m_vecDominationImages[ i ];
+ pImagePanel->GetImage()->GetSize( nImageWidth, nImageHeight );
+ m_vecDominationImages[ i ]->SetBounds( nX, nY, nImageWidth, nImageHeight );
+
+ nX += nImageWidth + nBuffer;
+ if ( nX + nImageWidth > GetInset()->GetWide() )
+ {
+ nX = 0;
+ nY += nImageHeight + nBuffer;
+ }
+ }
+
+ SetTall( nY + nImageHeight + GetMarginSize() * 2 );
+}
+
+//-----------------------------------------------------------------------------
+
+CKillsPanel::CKillsPanel( Panel *pParent, ReplayHandle_t hReplay )
+: CBaseDetailsPanel( pParent, "KillsPanel", hReplay )
+{
+ // Get the replay from the handle and add all kills
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
+ char szKillCount[64] = "0";
+ if ( pReplay )
+ {
+ for ( int i = 0; i < pReplay->GetKillCount(); ++i )
+ {
+ // Construct path for image
+ char szImgPath[MAX_OSPATH] = "";
+
+#if defined( TF_CLIENT_DLL )
+ // Get the kill info
+ const CGenericClassBasedReplay::KillData_t *pKill = pReplay->GetKill( i );
+
+ char const *pClass = pKill->m_nPlayerClass == TF_CLASS_DEMOMAN
+ ? "demo"
+ : g_aPlayerClassNames_NonLocalized[ pKill->m_nPlayerClass ];
+
+ V_snprintf( szImgPath, sizeof( szImgPath ), "../hud/leaderboard_class_%s", pClass );
+#elif defined( CSTRIKE_DLL )
+ V_strcpy( szImgPath, "../hud/scoreboard_dead" );
+#endif
+
+ // Get the image
+ IImage *pImage = scheme()->GetImage( szImgPath, true );
+
+ // Create new image panel
+ ImagePanel *pImgPanel = new ImagePanel( GetInset(), "img" );
+ pImgPanel->SetImage( pImage );
+
+ // Cache for later
+ m_vecKillImages.AddToTail( pImgPanel );
+ }
+
+ // Copy kill count
+ V_snprintf( szKillCount, sizeof( szKillCount ), "%i", pReplay->GetKillCount() );
+ }
+
+ // Create labels
+ m_pKillLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_Kills", szKillCount );
+}
+
+void CKillsPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ m_pKillLabels->SetBounds( 0, 0, GetWide(), m_pKillLabels->GetHeight() );
+
+ // Setup image positions
+ int nBuffer = 5;
+ int nY = m_pKillLabels->GetHeight() + nBuffer * 2;
+ int nImageY = nY;
+ int nImageX = 0;
+ for ( int i = 0; i < m_vecKillImages.Count(); ++i )
+ {
+ IImage *pCurImage = m_vecKillImages[ i ]->GetImage();
+ if ( !pCurImage )
+ continue;
+
+ int nImageWidth, nImageHeight;
+ pCurImage->GetSize( nImageWidth, nImageHeight );
+ m_vecKillImages[ i ]->SetBounds( nImageX, nImageY, nImageWidth, nImageHeight );
+
+ nImageX += nImageWidth + nBuffer;
+
+ if ( i == 0 )
+ {
+ nY += nImageHeight;
+ }
+
+ if ( nImageX + nImageWidth > GetInset()->GetWide() )
+ {
+ nImageX = 0;
+ nImageY += nImageHeight + nBuffer;
+ nY += nImageHeight + nBuffer;
+ }
+ }
+
+ // Set the height
+ SetTall( nY + GetMarginSize() * 2 );
+}
+
+void CKillsPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+}
+
+//-----------------------------------------------------------------------------
+
+extern const char *GetMapDisplayName( const char *mapName );
+
+CBasicLifeInfoPanel::CBasicLifeInfoPanel( Panel *pParent, ReplayHandle_t hReplay )
+: CBaseDetailsPanel( pParent, "BasicLifeInfo", hReplay )
+{
+ // Create labels
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
+ m_pKilledByLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_StatKilledBy", pReplay->WasKilled() ? pReplay->GetKillerName() : "#Replay_None" );
+ m_pMapLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_OnMap", GetMapDisplayName( pReplay->m_szMapName ) );
+ m_pLifeLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_Life", CReplayTime::FormatTimeString( (int)pReplay->m_flLength ) );
+}
+
+void CBasicLifeInfoPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+}
+
+void CBasicLifeInfoPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int nBuffer = 5;
+ m_pKilledByLabels->SetBounds( 0, 0, GetWide(), m_pKilledByLabels->GetHeight() );
+ m_pMapLabels->SetBounds( 0, m_pKilledByLabels->GetTall() + nBuffer, GetWide(), m_pMapLabels->GetHeight() );
+
+ int nLifeLabelsY = ( m_pMapLabels->GetTall() + nBuffer ) * 2;
+ m_pLifeLabels->SetBounds( 0, nLifeLabelsY, GetWide(), m_pLifeLabels->GetHeight() );
+
+ SetTall( nLifeLabelsY + m_pLifeLabels->GetTall() + GetMarginSize() * 2 );
+}
+
+//-----------------------------------------------------------------------------
+
+CYouTubeInfoPanel::CYouTubeInfoPanel( Panel *pParent )
+ : CBaseDetailsPanel( pParent, "YouTubeInfo", NULL ),
+ m_pLabels( NULL )
+{
+ m_pLabels = new CKeyValueLabelPanel( GetInset(), "#Replay_YouTube", g_pVGuiLocalize->Find( "YouTube_NoStats" ) );
+}
+
+void CYouTubeInfoPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ m_pLabels->SetBounds( 0, 0, GetWide(), m_pLabels->GetValueHeight() );
+
+ SetTall( m_pLabels->GetTall() + GetMarginSize() * 2 );
+}
+
+void CYouTubeInfoPanel::SetInfo( const wchar_t *pInfo )
+{
+ m_pLabels->SetValue( pInfo );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+
+CTitleEditPanel::CTitleEditPanel( Panel *pParent, QueryableReplayItemHandle_t hReplayItem, IReplayItemManager *pItemManager )
+: EditablePanel( pParent, "TitleEditPanel" ),
+ m_hReplayItem( hReplayItem ),
+ m_pItemManager( pItemManager ),
+ m_bMouseOver( false ),
+ m_pTitleEntry( NULL ),
+ m_pHeaderLine( NULL ),
+ m_pClickToEditLabel( NULL ),
+ m_pCaratLabel( NULL )
+{
+ ivgui()->AddTickSignal( GetVPanel(), 10 );
+}
+
+CTitleEditPanel::~CTitleEditPanel()
+{
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CTitleEditPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/titleeditpanel.res", "GAME" );
+
+ // Get ptr to carat label
+ m_pCaratLabel = dynamic_cast< CExLabel * >( FindChildByName( "CaratLabel" ) );
+
+ // Get ptr to "click to edit" label
+ m_pClickToEditLabel = dynamic_cast< CExLabel * >( FindChildByName( "ClickToEditLabel" ) );
+
+ // Setup title entry
+ m_pTitleEntry = dynamic_cast< TextEntry * >( FindChildByName( "TitleInput" ) );
+ m_pTitleEntry->SelectAllOnFocusAlways( true );
+
+#if !defined( TF_CLIENT_DLL )
+ m_pTitleEntry->SetPaintBorderEnabled( false );
+#endif
+
+ // Setup title entry text
+ IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
+ const wchar_t *pTitle = pReplayItem->GetItemTitle();
+ m_pTitleEntry->SetText( pTitle[0] ? pTitle : L"#Replay_DefaultDetailsTitle" );
+
+ // Cache pointer to the image
+ m_pHeaderLine = dynamic_cast< ImagePanel * >( FindChildByName( "HeaderLine" ) );
+}
+
+void CTitleEditPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int nCaratW, nCaratH;
+ m_pCaratLabel->GetContentSize( nCaratW, nCaratH );
+ m_pCaratLabel->SetWide( nCaratW );
+ m_pCaratLabel->SetTall( nCaratH );
+
+ // Get title entry pos
+ int nTitleEntryX, nTitleEntryY;
+ m_pTitleEntry->GetPos( nTitleEntryX, nTitleEntryY );
+
+ // Set width of title entry to be width of parent, which has margins
+ m_pTitleEntry->SetToFullHeight();
+ m_pTitleEntry->SetWide( GetParent()->GetWide() - nTitleEntryX - 1 );
+
+ // Get content size for label
+ int nClickToEditW, nClickToEditH;
+ m_pClickToEditLabel->GetContentSize( nClickToEditW, nClickToEditH );
+
+ // Set click-to-edit bounds
+ int nTitleEntryTall = m_pTitleEntry->GetTall();
+ m_pClickToEditLabel->SetBounds(
+ nTitleEntryX + GetParent()->GetWide() - nClickToEditW * 1.4f,
+ nTitleEntryY + ( nTitleEntryTall - nClickToEditH ) / 2,
+ nClickToEditW,
+ nClickToEditH
+ );
+
+ // Setup header line position
+ m_pHeaderLine->SetPos( 0, nTitleEntryY + m_pTitleEntry->GetTall() * 1.2f );
+}
+
+void CTitleEditPanel::OnTick()
+{
+ int nMouseX, nMouseY;
+ input()->GetCursorPos( nMouseX, nMouseY );
+ m_bMouseOver = m_pTitleEntry->IsWithin( nMouseX, nMouseY );
+}
+
+void CTitleEditPanel::PaintBackground()
+{
+ bool bEditing = m_pTitleEntry->HasFocus();
+ bool bDrawExtraStuff = !vgui::input()->GetAppModalSurface() && ( m_bMouseOver || bEditing ); // Don't draw extra stuff when render dialog is up
+
+ // If mouse is over and we're not editing, show the "click to edit" label
+ m_pClickToEditLabel->SetVisible( m_bMouseOver && !bEditing );
+
+ // Draw border if necessary
+ if ( bDrawExtraStuff )
+ {
+ // Use the game UI panel here, since using this panel's vpanel (PushMakeCurrent() is set in
+ // Panel::PaintTraverse(), the function that calls PaintBackground()) causes dimmed top and
+ // left lines. Using the game UI panel allows painting outside of the text entry itself.
+ vgui::VPANEL vGameUI = enginevgui->GetPanel( PANEL_GAMEUIDLL );
+
+ // Calculate title entry rect (x0,y0,x1,y1) - move the absolute upper-left corner 1 pixel left & up
+ int aTitleRect[4];
+ ipanel()->GetAbsPos( m_pTitleEntry->GetVPanel(), aTitleRect[0], aTitleRect[1] );
+
+ --aTitleRect[0];
+ --aTitleRect[1];
+ aTitleRect[2] = aTitleRect[0] + m_pTitleEntry->GetWide() + 2;
+ aTitleRect[3] = aTitleRect[1] + m_pTitleEntry->GetTall() + 2;
+
+ surface()->PushMakeCurrent( vGameUI, false );
+
+ // Draw background
+ surface()->DrawSetColor( Color( 29, 28, 26, 255 ) );
+ surface()->DrawFilledRect( aTitleRect[0], aTitleRect[1], aTitleRect[2], aTitleRect[3] );
+
+ // Draw stroke
+ surface()->DrawSetColor( Color( 202, 190, 164, 255 ) );
+ surface()->DrawLine( aTitleRect[0], aTitleRect[1], aTitleRect[2], aTitleRect[1] ); // Top
+ surface()->DrawLine( aTitleRect[0], aTitleRect[1], aTitleRect[0], aTitleRect[3] ); // Left
+ surface()->DrawLine( aTitleRect[0], aTitleRect[3], aTitleRect[2], aTitleRect[3] ); // Bottom
+ surface()->DrawLine( aTitleRect[2], aTitleRect[1], aTitleRect[2], aTitleRect[3] ); // Right
+
+ surface()->PopMakeCurrent( vGameUI );
+ }
+}
+
+void CTitleEditPanel::OnKeyCodeTyped( KeyCode code )
+{
+ IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
+
+ const wchar_t *pTitle = pReplayItem->GetItemTitle();
+
+ if ( m_pTitleEntry->HasFocus() && pReplayItem )
+ {
+ if ( code == KEY_ESCAPE )
+ {
+ // Get replay text and reset it
+ m_pTitleEntry->SetText( pTitle );
+
+ // Remove focus
+ GetParent()->GetParent()->RequestFocus();
+ }
+ else if ( code == KEY_ENTER )
+ {
+ // If text is empty, reset to old title
+ if ( m_pTitleEntry->GetTextLength() == 0 )
+ {
+ m_pTitleEntry->SetText( pTitle );
+ }
+ else // Save title...
+ {
+ // Copy text into the replay
+ // NOTE: SetItemTitle() will mark replay as dirty
+ wchar_t wszNewTitle[MAX_REPLAY_TITLE_LENGTH];
+ m_pTitleEntry->GetText( wszNewTitle, sizeof( wszNewTitle ) );
+ pReplayItem->SetItemTitle( wszNewTitle );
+
+ // Save!
+ g_pReplayManager->FlagReplayForFlush( pReplayItem->GetItemReplay(), true );
+
+ // Notify the thumbnail
+ void *pUserData = pReplayItem->GetUserData();
+ if ( pUserData )
+ {
+ CReplayBrowserThumbnail *pThumbnail = (CReplayBrowserThumbnail*)pUserData;
+ pThumbnail->UpdateTitleText();
+ }
+ }
+
+ GetParent()->GetParent()->RequestFocus();
+ }
+
+ return;
+ }
+
+ BaseClass::OnKeyCodeTyped( code );
+}
+
+//-----------------------------------------------------------------------------
+
+CPlaybackPanel::CPlaybackPanel( Panel *pParent )
+: EditablePanel( pParent, "PlaybackPanel" )
+{
+}
+
+CPlaybackPanel::~CPlaybackPanel()
+{
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CPlaybackPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/playbackpanel.res", "GAME" );
+}
+
+void CPlaybackPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+
+CPlaybackPanelSlideshow::CPlaybackPanelSlideshow( Panel *pParent, ReplayHandle_t hReplay )
+: CPlaybackPanel( pParent ),
+ m_hReplay( hReplay )
+{
+ m_pScreenshotImage = new CReplayScreenshotSlideshowPanel( this, "Screenshot", hReplay );
+}
+
+void CPlaybackPanelSlideshow::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/playbackpanelslideshow.res", "GAME" );
+
+ m_pNoScreenshotLabel = dynamic_cast< CExLabel * >( FindChildByName( "NoScreenshotLabel" ) );
+
+ // Check to see if there's a screenshot
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+ if ( !pReplay )
+ return;
+
+ if ( !pReplay->GetScreenshotCount() && m_pNoScreenshotLabel ) // Show no-screenshot label
+ {
+ m_pNoScreenshotLabel->SetVisible( true );
+ }
+}
+
+void CPlaybackPanelSlideshow::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+
+ int nMarginWidth = GetMarginSize();
+ int nScreenshotWidth = GetViewWidth();
+ if ( m_pScreenshotImage )
+ {
+ m_pScreenshotImage->SetBounds( nMarginWidth, nMarginWidth, nScreenshotWidth, GetTall() - 2*nMarginWidth );
+
+ // Setup screenshot scale based on width of first screenshot (if there are any screenshots at all) - otherwise don't scale
+ float flScale = pReplay->GetScreenshotCount() == 0 ? 1.0f : ( (float)nScreenshotWidth / ( .95f * pReplay->GetScreenshot( 0 )->m_nWidth ) );
+ m_pScreenshotImage->GetImagePanel()->SetScaleAmount( flScale );
+ m_pScreenshotImage->GetImagePanel()->SetShouldScaleImage( true );
+ }
+
+ // Setup the label
+ int nLabelW, nLabelH;
+ m_pNoScreenshotLabel->GetContentSize( nLabelW, nLabelH );
+ m_pNoScreenshotLabel->SetBounds( 0, ( GetTall() - nLabelH ) / 2, GetWide(), nLabelH );
+}
+
+//-----------------------------------------------------------------------------
+
+CPlaybackPanelMovie::CPlaybackPanelMovie( Panel *pParent, ReplayHandle_t hMovie )
+: CPlaybackPanel( pParent )
+{
+ IReplayMovie *pMovie = g_pReplayMovieManager->GetMovie( hMovie );
+ m_pMoviePlayerPanel = new CMoviePlayerPanel( this, "MoviePlayer", pMovie->GetMovieFilename() );
+
+ m_pMoviePlayerPanel->SetLooping( true );
+
+ // TODO: show controls and don't play right away
+ m_pMoviePlayerPanel->Play();
+}
+
+void CPlaybackPanelMovie::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+}
+
+void CPlaybackPanelMovie::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ m_pMoviePlayerPanel->SetBounds( 9, 9, GetViewWidth(), GetViewHeight() );
+ m_pMoviePlayerPanel->SetEnabled( true );
+ m_pMoviePlayerPanel->SetVisible( true );
+ m_pMoviePlayerPanel->SetZPos( 101 );
+}
+
+void CPlaybackPanelMovie::FreeMovieMaterial()
+{
+ m_pMoviePlayerPanel->FreeMaterial();
+}
+
+//-----------------------------------------------------------------------------
+
+CCutImagePanel::CCutImagePanel( Panel *pParent, const char *pName )
+: BaseClass( pParent, pName, "" ),
+ m_pSelectedBorder( NULL )
+{
+}
+
+void CCutImagePanel::SetSelected( bool bState )
+{
+ BaseClass::SetSelected( bState );
+}
+
+IBorder *CCutImagePanel::GetBorder( bool bDepressed, bool bArmed, bool bSelected, bool bKeyFocus )
+{
+ if ( bSelected )
+ {
+ return m_pSelectedBorder;
+ }
+
+ return BaseClass::GetBorder( bDepressed, bArmed, bSelected, bKeyFocus );
+}
+
+void CCutImagePanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+}
+
+//-----------------------------------------------------------------------------
+
+#define FOR_EACH_BUTTON( _i ) for ( int _i = 0; _i < BUTTONS_PER_PAGE; ++_i )
+
+CCutsPanel::CCutsPanel( Panel *pParent, ReplayHandle_t hReplay, int iSelectedPerformance )
+: BaseClass( pParent, "CutsPanel", hReplay ),
+ m_iPage( 0 ),
+ m_nVisibleButtons( 0 ),
+ m_pVerticalLine( NULL ),
+ m_pNoCutsLabel( NULL ),
+ m_pOriginalLabel( NULL ),
+ m_pCutsLabel( NULL )
+{
+ m_hDetailsPanel = dynamic_cast< CReplayDetailsPanel * >( pParent->GetParent() );
+
+ FOR_EACH_BUTTON( i )
+ {
+ const int iButton = i;
+ CFmtStr fmtName( "CutButton%i", iButton );
+
+ CExImageButton *pNewButton = new CExImageButton( this, fmtName.Access(), "" );
+ CFmtStr fmtCommand( "select_%i", iButton );
+ pNewButton->SetCommand( fmtCommand.Access() );
+ pNewButton->InvalidateLayout( true, true );
+ pNewButton->AddActionSignalTarget( this );
+ pNewButton->SetSelected( i == 0 );
+#if !defined( TF_CLIENT_DLL )
+ pNewButton->SetSelectedColor( Color( 0, 0, 0, 0 ), Color( 122, 25, 16, 255 ) );
+#endif
+
+ const int iPerformance = i - 1;
+ m_aButtons[ i ].m_pButton = pNewButton;
+ m_aButtons[ i ].m_iPerformance = iPerformance;
+
+ CExButton *pAddToRenderQueueButton = new CExButton( pNewButton, "AddToRenderQueue", "+", this );
+ m_aButtons[ i ].m_pAddToRenderQueueButton = pAddToRenderQueueButton;
+ }
+
+ // Layout right now
+ InvalidateLayout( true, true );
+
+ // Calculate page
+ SetPage(
+ ( 1 + iSelectedPerformance ) / BUTTONS_PER_PAGE,
+ ( 1 + iSelectedPerformance ) % BUTTONS_PER_PAGE
+ );
+
+ ivgui()->AddTickSignal( GetVPanel(), 10 );
+}
+
+CCutsPanel::~CCutsPanel()
+{
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CCutsPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/cutspanel.res", "GAME" );
+
+ m_pVerticalLine = dynamic_cast< EditablePanel * >( FindChildByName( "VerticalLine" ) );
+ m_pNoCutsLabel = dynamic_cast< CExLabel * >( FindChildByName( "NoCutsLabel" ) );
+ m_pOriginalLabel = dynamic_cast< CExLabel * >( FindChildByName( "OriginalLabel" ) );
+ m_pCutsLabel = dynamic_cast< CExLabel * >( FindChildByName( "CutsLabel" ) );
+ m_pNameLabel = dynamic_cast< CExLabel * >( FindChildByName( "NameLabel" ) );
+ m_pPrevButton = dynamic_cast< CExButton * >( FindChildByName( "PrevButton" ) );
+ m_pNextButton = dynamic_cast< CExButton * >( FindChildByName( "NextButton" ) );
+
+ FOR_EACH_BUTTON( i )
+ {
+ CExImageButton *pCurButton = m_aButtons[ i ].m_pButton;
+#if !defined( TF_CLIENT_DLL )
+ pCurButton->SetPaintBorderEnabled( false );
+#endif
+ pCurButton->InvalidateLayout( true, true );
+ }
+}
+
+void CCutsPanel::ApplySettings( KeyValues *pInResourceData )
+{
+ BaseClass::ApplySettings( pInResourceData );
+
+ KeyValues *pButtonSettings = pInResourceData->FindKey( "button_settings" );
+ if ( pButtonSettings )
+ {
+ KeyValues *pAddToRenderQueueButtonSettings = pButtonSettings->FindKey( "addtorenderqueuebutton_settings" );
+
+ CReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+ FOR_EACH_BUTTON( i )
+ {
+ CExImageButton *pCurButton = m_aButtons[ i ].m_pButton;
+
+ pCurButton->ApplySettings( pButtonSettings );
+
+ // Set screenshot as image
+ if ( pReplay && pReplay->GetScreenshotCount() )
+ {
+ const float flScale = (float)m_nCutButtonHeight / pReplay->GetScreenshot( 0 )->m_nHeight;
+ int nImageWidth = m_nCutButtonWidth - 2 * m_nCutButtonBuffer;
+ int nImageHeight = m_nCutButtonHeight - 2 * m_nCutButtonBuffer;
+
+ CFmtStr fmtFile( "replay\\thumbnails\\%s", pReplay->GetScreenshot( 0 )->m_szBaseFilename );
+ pCurButton->SetSubImage( fmtFile.Access() );
+ pCurButton->GetImage()->SetScaleAmount( flScale );
+ pCurButton->GetImage()->SetBounds( m_nCutButtonBuffer, m_nCutButtonBuffer, nImageWidth, nImageHeight );
+ }
+
+ if ( pAddToRenderQueueButtonSettings )
+ {
+ CExButton *pAddToQueueButton = m_aButtons[ i ].m_pAddToRenderQueueButton;
+ pAddToQueueButton->ApplySettings( pAddToRenderQueueButtonSettings );
+ pAddToQueueButton->AddActionSignalTarget( this );
+ }
+ }
+ }
+}
+
+void CCutsPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ CReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+ const int nNumCuts = pReplay->GetPerformanceCount();
+
+ int nX = m_iPage > 0 ? m_pPrevButton->GetWide() + m_nCutButtonSpace : 0;
+
+ m_nVisibleButtons = 0;
+
+ FOR_EACH_BUTTON( i )
+ {
+ CExImageButton *pCurButton = m_aButtons[ i ].m_pButton;
+ const bool bVisible = ButtonToPerformance( i ) < nNumCuts;
+
+ pCurButton->SetVisible( bVisible );
+
+ if ( bVisible )
+ {
+ ++m_nVisibleButtons;
+ }
+
+ pCurButton->SetBounds( nX, m_nButtonStartY, m_nCutButtonWidth, m_nCutButtonHeight );
+ nX += m_nCutButtonWidth;
+
+ if ( i == 0 && m_iPage == 0 )
+ {
+ nX += 2 * m_nCutButtonSpaceWide + m_pVerticalLine->GetWide();
+ }
+ else
+ {
+ nX += m_nCutButtonSpace;
+ }
+ }
+
+ if ( m_pVerticalLine )
+ {
+ m_pVerticalLine->SetVisible( m_nVisibleButtons > 0 && m_iPage == 0 );
+ m_pVerticalLine->SetPos( m_nCutButtonWidth + m_nCutButtonSpaceWide, 0 );
+ m_pVerticalLine->SetTall( m_nTopMarginHeight + GetTall() );
+ }
+
+ const int nRightOfVerticalLineX = m_nCutButtonWidth + m_nCutButtonSpaceWide * 2 + m_pVerticalLine->GetWide();
+
+ if ( m_pNoCutsLabel )
+ {
+ m_pNoCutsLabel->SetVisible( m_nVisibleButtons == 1 && m_iPage == 0 );
+
+ int nY = ( GetTall() - m_pNoCutsLabel->GetTall() ) / 2;
+ m_pNoCutsLabel->SetPos( nRightOfVerticalLineX, nY );
+ }
+
+ if ( m_pOriginalLabel )
+ {
+ m_pOriginalLabel->SetVisible( m_iPage == 0 );
+ }
+
+ if ( m_pCutsLabel )
+ {
+ m_pCutsLabel->SetVisible( m_nVisibleButtons > 1 && m_iPage == 0 );
+ m_pCutsLabel->SetPos( m_nCutButtonWidth + 2 * m_nCutButtonSpaceWide + m_pVerticalLine->GetWide(), 0 );
+ }
+
+ bool bPrevCuts = m_iPage > 0;
+ bool bMoreCuts = ( nNumCuts + 1 ) > ( m_iPage + 1 ) * BUTTONS_PER_PAGE;
+ int nY = m_nTopMarginHeight + ( GetTall() - m_pNextButton->GetTall() ) / 2;
+ m_pPrevButton->SetVisible( bPrevCuts );
+ m_pPrevButton->SetPos( 0, nY );
+ m_pNextButton->SetVisible( bMoreCuts );
+ m_pNextButton->SetPos( nX, nY );
+}
+
+void CCutsPanel::OnTick()
+{
+ if ( !TFModalStack()->IsEmpty() )
+ return;
+
+ int nMouseX, nMouseY;
+ input()->GetCursorPos( nMouseX, nMouseY );
+
+ // Early-out if not within the cuts panel at all.
+ if ( !IsWithin( nMouseX, nMouseY ) )
+ return;
+
+ int iHoverPerformance = -2;
+ bool bFoundHoverButton = false;
+ FOR_EACH_BUTTON( i )
+ {
+ CExImageButton *pCurButton = m_aButtons[ i ].m_pButton;
+ bool bIsHoverButton = false;
+ if ( !bFoundHoverButton && pCurButton->IsWithin( nMouseX, nMouseY ) && pCurButton->IsVisible() )
+ {
+ iHoverPerformance = ButtonToPerformance( i );
+ bFoundHoverButton = true;
+ bIsHoverButton = true;
+ }
+
+ CExButton *pAddToRenderQueueButton = m_aButtons[ i ].m_pAddToRenderQueueButton;
+ if ( pAddToRenderQueueButton )
+ {
+ pAddToRenderQueueButton->SetVisible( bIsHoverButton );
+
+ if ( iHoverPerformance >= -1 )
+ {
+ // Set the text and command based on whether or not the take's already been queued
+ const bool bInQueue = g_pClientReplayContext->GetRenderQueue()->IsInQueue( m_hReplay, iHoverPerformance );
+ CFmtStr fmtCmd( "%srenderqueue_%i", bInQueue ? "removefrom" : "addto", iHoverPerformance );
+ pAddToRenderQueueButton->SetCommand( fmtCmd.Access() );
+ pAddToRenderQueueButton->SetText( bInQueue ? "-" : "+" );
+ }
+ }
+ }
+
+ // If the mouse is over a performance button, use that, otherwise use the selected
+ // performance.
+ if ( m_hDetailsPanel.Get() )
+ {
+ int iSelectedPerformance = m_hDetailsPanel->m_iSelectedPerformance;
+ UpdateNameLabel( iHoverPerformance >= 0 ? iHoverPerformance : iSelectedPerformance >= 0 ? iSelectedPerformance : -1 );
+ }
+}
+
+int CCutsPanel::ButtonToPerformance( int iButton ) const
+{
+ return -1 + m_iPage * BUTTONS_PER_PAGE + iButton;
+}
+
+void CCutsPanel::OnCommand( const char *pCommand )
+{
+ if ( !V_strnicmp( pCommand, "select_", 7 ) )
+ {
+ const int iButton = atoi( pCommand + 7 );
+ SelectButtonFromPerformance( ButtonToPerformance( iButton ) );
+ }
+ else if ( !V_stricmp( pCommand, "prevpage" ) )
+ {
+ SetPage( m_iPage - 1 );
+ }
+ else if ( !V_stricmp( pCommand, "nextpage" ) )
+ {
+ SetPage( m_iPage + 1 );
+ }
+ else if ( !V_strnicmp( pCommand, "addtorenderqueue_", 17 ) )
+ {
+ if ( !replay_renderqueue_first_add.GetInt() )
+ {
+ ShowMessageBox( "#Replay_FirstRenderQueueAddTitle", "#Replay_FirstRenderQueueAddMsg", "#GameUI_OK" );
+ replay_renderqueue_first_add.SetValue( 1 );
+ }
+
+ const int iPerformance = atoi( pCommand + 17 );
+ if ( iPerformance >= -1 )
+ {
+ g_pClientReplayContext->GetRenderQueue()->Add( m_hReplay, iPerformance );
+ }
+ }
+ else if ( !V_strnicmp( pCommand, "removefromrenderqueue_", 22 ) )
+ {
+ const int iPerformance = atoi( pCommand + 22 );
+ if ( iPerformance >= -1 )
+ {
+ g_pClientReplayContext->GetRenderQueue()->Remove( m_hReplay, iPerformance );
+ }
+ }
+}
+
+void CCutsPanel::SetPage( int iPage, int iButtonToSelect )
+{
+ m_iPage = iPage;
+
+ FOR_EACH_BUTTON( i )
+ {
+ ButtonInfo_t *pCurButtonInfo = &m_aButtons[ i ];
+ const int iPerformance = ButtonToPerformance( i );
+ pCurButtonInfo->m_iPerformance = iPerformance;
+ }
+
+ InvalidateLayout( true, false );
+ SelectButtonFromPerformance( ButtonToPerformance( iButtonToSelect ) );
+}
+
+const CReplayPerformance *CCutsPanel::GetPerformance( int iPerformance ) const
+{
+ const CReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+ if ( !pReplay )
+ return NULL;
+
+ return iPerformance >= 0 ? pReplay->GetPerformance( iPerformance ) : NULL;
+}
+
+void CCutsPanel::SelectButtonFromPerformance( int iPerformance )
+{
+ FOR_EACH_BUTTON( i )
+ {
+ const ButtonInfo_t *pCurButtonInfo = &m_aButtons[ i ];
+ CExImageButton *pCurButton = pCurButtonInfo->m_pButton;
+ pCurButton->SetSelected( pCurButtonInfo->m_iPerformance == iPerformance );
+ pCurButton->InvalidateLayout( true, true );
+ }
+
+ // Cache which performance to use in the details panel
+ if ( m_hDetailsPanel.Get() )
+ {
+ m_hDetailsPanel->m_iSelectedPerformance = iPerformance;
+ }
+
+ UpdateNameLabel( iPerformance );
+}
+
+int CCutsPanel::PerformanceToButton( int iPerformance ) const
+{
+ FOR_EACH_BUTTON( i )
+ {
+ if ( m_aButtons[ i ].m_iPerformance == iPerformance )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void CCutsPanel::UpdateNameLabel( int iPerformance )
+{
+ const CReplayPerformance *pPerformance = GetPerformance( iPerformance );
+ m_pNameLabel->SetText( pPerformance ? pPerformance->m_wszTitle : L"" );
+
+ // Get the button (in the range [0,BUTTONS_PER_PAGE]).
+ const int iPerformanceButton = PerformanceToButton( iPerformance ); // Not necessarily the selected button - can be hover button
+
+ // Get position of the button so we can use it's x position.
+ int aSelectedButtonPos[2];
+ m_aButtons[ iPerformanceButton ].m_pButton->GetPos( aSelectedButtonPos[0], aSelectedButtonPos[1] );
+
+ if ( m_pNameLabel )
+ {
+ const int nNameLabelX = aSelectedButtonPos[0];
+ const int nNameLabelY = m_nButtonStartY + m_nCutButtonHeight + m_nNameLabelTopMargin;
+ m_pNameLabel->SetBounds(
+ nNameLabelX,
+ nNameLabelY,
+ GetWide() - nNameLabelX,
+ GetTall() - nNameLabelY
+ );
+ }
+}
+
+void CCutsPanel::OnPerformanceDeleted( int iPerformance )
+{
+ int iButton = PerformanceToButton( iPerformance );
+ if ( iButton < 0 )
+ return;
+
+ // Deleted last performance on page?
+ CReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+ const int nNumCuts = pReplay->GetPerformanceCount();
+ if ( iPerformance == m_aButtons[ 0 ].m_iPerformance && iPerformance == nNumCuts )
+ {
+ SetPage( m_iPage - 1, BUTTONS_PER_PAGE - 1 );
+ }
+ else
+ {
+ SelectButtonFromPerformance( ButtonToPerformance( MIN( m_nVisibleButtons - 1, MAX( 0, iButton ) ) ) );
+ }
+
+ // Select the cut prior to the one we just deleted
+ Assert( iPerformance >= 0 );
+
+ InvalidateLayout( true, false );
+}
+
+//-----------------------------------------------------------------------------
+
+static void ConfirmUploadMovie( bool bConfirmed, void *pContext )
+{
+ if ( bConfirmed )
+ {
+ CReplayDetailsPanel *pPanel = (CReplayDetailsPanel*)pContext;
+ IQueryableReplayItem *pReplayItem = pPanel->m_pItemManager->GetItem( pPanel->m_hReplayItem );
+ if ( pReplayItem && pReplayItem->IsItemAMovie() )
+ {
+ IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
+ if ( YouTube_GetLoginStatus() != kYouTubeLogin_LoggedIn )
+ {
+ YouTube_ShowLoginDialog( pMovie, pPanel );
+ }
+ else
+ {
+ YouTube_ShowUploadDialog( pMovie, pPanel );
+ }
+ }
+ }
+}
+
+class CYouTubeGetStatsHandler : public CYouTubeResponseHandler
+{
+public:
+ CYouTubeGetStatsHandler( CReplayDetailsPanel *pPanel )
+ : m_pPanel( pPanel )
+ , m_handle( NULL )
+ {
+ }
+
+ virtual ~CYouTubeGetStatsHandler()
+ {
+ if ( m_handle != NULL )
+ {
+ YouTube_CancelGetVideoInfo( m_handle );
+ }
+ }
+
+ static bool GetEmptyElementTagContents( const char *pXML, const char *pTag, CUtlString &strTagContents )
+ {
+ CFmtStr1024 kLinkTagStart( "<%s ", pTag );
+ const char *kLinkTagEnd = "/>";
+ const char *pStart = strstr( pXML, kLinkTagStart.Access() );
+ if ( pStart != NULL )
+ {
+ pStart += kLinkTagStart.Length();
+ const char *pEnd = strstr( pStart, kLinkTagEnd );
+ if ( pEnd != NULL )
+ {
+ strTagContents.SetDirect( pStart, pEnd - pStart );
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static bool GetEmptyTagValue( const char *pTagContents, const char *pKeyName, CUtlString &value )
+ {
+ CFmtStr1024 kStart( "%s='", pKeyName );
+ const char *kEnd = "'";
+ const char *pStart = strstr( pTagContents, kStart.Access() );
+ if ( pStart != NULL )
+ {
+ pStart += kStart.Length();
+ const char *pEnd = strstr( pStart, kEnd );
+ if ( pEnd != NULL )
+ {
+ value.SetDirect( pStart, pEnd - pStart );
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual void HandleResponse( long responseCode, const char *pResponse )
+ {
+ // @note tom bui: wish I had an XML parser
+
+ if ( strstr( pResponse, "<internalReason>Private video</internalReason>" ) != NULL )
+ {
+ m_pPanel->m_pYouTubeInfoPanel->SetInfo( g_pVGuiLocalize->Find( "#YouTube_PrivateVideo" ) );
+ m_pPanel->SetYouTubeStatus( CReplayDetailsPanel::kYouTubeStatus_Private );
+ return;
+ }
+
+ int iNumFavorited = 0;
+ int iNumViews = 0;
+ int iNumLikes = 0;
+
+ wchar_t wszFavorited[256] = L"0";
+ wchar_t wszViews[256] = L"0";
+
+ CUtlString strTagStatistics;
+ if ( GetEmptyElementTagContents( pResponse, "yt:statistics", strTagStatistics ) )
+ {
+ CUtlString favoriteCount;
+ CUtlString viewCount;
+ GetEmptyTagValue( strTagStatistics, "favoriteCount", favoriteCount );
+ GetEmptyTagValue( strTagStatistics, "viewCount", viewCount );
+
+ iNumFavorited = Q_atoi( favoriteCount.Get() );
+ iNumViews = Q_atoi( viewCount.Get() );
+
+ g_pVGuiLocalize->ConvertANSIToUnicode( favoriteCount.Get(), wszFavorited, sizeof( wszFavorited ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( viewCount.Get(), wszViews, sizeof( wszViews ) );
+ }
+
+ wchar_t wszLikes[256] = L"0";
+ CUtlString strTagRating;
+ if ( GetEmptyElementTagContents( pResponse, "yt:rating", strTagRating ) )
+ {
+ CUtlString likes;
+ GetEmptyTagValue( strTagRating, "numLikes", likes );
+ iNumLikes = Q_atoi( likes.Get() );
+ g_pVGuiLocalize->ConvertANSIToUnicode( likes.Get(), wszLikes, sizeof( wszLikes ) );
+ }
+
+ //const char *kLinkStartTag = "<link rel='alternate' type='text/html' href='";
+ CUtlString strTagLink;
+ if ( GetEmptyElementTagContents( pResponse, "link rel='alternate'", strTagLink ) )
+ {
+ GetEmptyTagValue( strTagLink, "href", m_strVideoURL );
+ }
+
+ wchar_t wszStats[256] = L"";
+ g_pVGuiLocalize->ConstructString( wszStats,sizeof( wszStats ), g_pVGuiLocalize->Find( "#YouTube_Stats" ), 3,
+ wszFavorited,
+ wszViews,
+ wszLikes );
+
+ if ( m_strVideoURL.IsEmpty() == false )
+ {
+ m_pPanel->m_pYouTubeInfoPanel->SetInfo( wszStats );
+ m_pPanel->SetYouTubeStatus( CReplayDetailsPanel::kYouTubeStatus_RetrievedInfo );
+ m_pPanel->InvalidateLayout();
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "replay_youtube_stats" );
+ if ( event )
+ {
+ event->SetInt( "views", iNumViews );
+ event->SetInt( "likes", iNumLikes );
+ event->SetInt( "favorited", iNumFavorited );
+ gameeventmanager->FireEventClientSide( event );
+ }
+ }
+ else
+ {
+ m_pPanel->m_pYouTubeInfoPanel->SetInfo( g_pVGuiLocalize->Find( "#YouTube_CouldNotRetrieveStats" ) );
+ m_pPanel->SetYouTubeStatus( CReplayDetailsPanel::kYouTubeStatus_CouldNotRetrieveInfo );
+ }
+ }
+
+ CReplayDetailsPanel *m_pPanel;
+ YouTubeInfoHandle_t m_handle;
+ CUtlString m_strVideoURL;
+};
+
+CReplayDetailsPanel::CReplayDetailsPanel( Panel *pParent, QueryableReplayItemHandle_t hReplayItem,
+ int iPerformance, IReplayItemManager *pItemManager )
+: EditablePanel( pParent, "DetailsPanel" ),
+ m_hReplayItem( hReplayItem ),
+ m_pItemManager( pItemManager ),
+ m_pCutsPanel( NULL ),
+ m_iSelectedPerformance( iPerformance ),
+ m_pYouTubeResponseHandler( NULL ),
+ m_hExportMovieDialog( NULL )
+{
+ m_hReplay = pItemManager->GetItem( hReplayItem )->GetItemReplayHandle();
+
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+
+ m_pInsetPanel = new EditablePanel( this, "InsetPanel" );
+ m_pTitleEditPanel = new CTitleEditPanel( GetInset(), m_hReplayItem, m_pItemManager );
+ m_pPlaybackPanel = new CPlaybackPanelSlideshow( GetInset(), m_hReplay );
+ m_pRecordsPanel = new CRecordsPanel( GetInset(), m_hReplay );
+
+ m_pInfoPanel = new EditablePanel( this, "InfoContainerPanel" );
+ m_pScrollPanel = new vgui::ScrollableEditablePanel( GetInset(), m_pInfoPanel, "StatsScroller" );
+ m_pScrollPanel->GetScrollbar()->SetAutohideButtons( true );
+#if !defined( TF_CLIENT_DLL )
+ for ( int i = 0; i < 2; ++i )
+ {
+ m_pScrollPanel->GetScrollbar()->GetButton( i )->SetPaintBorderEnabled( false );
+ }
+#endif
+
+ m_pBasicInfoPanel = new CBasicLifeInfoPanel( m_pInfoPanel, m_hReplay );
+ m_pStatsPanel = new CStatsPanel( m_pInfoPanel, m_hReplay );
+ m_pKillsPanel = new CKillsPanel( m_pInfoPanel, m_hReplay );
+
+ const bool bIsMoviePanel = pItemManager->AreItemsMovies();
+ if ( bIsMoviePanel )
+ {
+ m_pYouTubeInfoPanel = new CYouTubeInfoPanel( m_pInfoPanel );
+ }
+ else
+ {
+ m_pCutsPanel = new CCutsPanel( GetInset(), m_hReplay, m_iSelectedPerformance );
+ }
+
+ // Add info panels to a list
+
+ if ( pReplay->GetDominationCount() )
+ {
+ m_pDominationsPanel = new CDominationsPanel( m_pInfoPanel, m_hReplay );
+ m_vecInfoPanels.AddToTail( m_pDominationsPanel );
+ }
+
+ m_vecInfoPanels.AddToTail( m_pBasicInfoPanel );
+ m_vecInfoPanels.AddToTail( m_pStatsPanel );
+ m_vecInfoPanels.AddToTail( m_pKillsPanel );
+
+ if ( bIsMoviePanel )
+ {
+ m_vecInfoPanels.AddToTail( m_pYouTubeInfoPanel );
+ }
+
+ m_pYouTubeResponseHandler = new CYouTubeGetStatsHandler( this );
+
+ RequestFocus();
+}
+
+CReplayDetailsPanel::~CReplayDetailsPanel()
+{
+ m_pDeleteButton->MarkForDeletion();
+ m_pRenderButton->MarkForDeletion();
+ m_pPlayButton->MarkForDeletion();
+ delete m_pYouTubeResponseHandler;
+}
+
+void CReplayDetailsPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/detailspanel.res", "GAME" );
+
+ m_pExportMovie = dynamic_cast< CExButton * >( FindChildByName( "ExportMovieButton" ) );
+ m_pDeleteButton = dynamic_cast< CExButton * >( FindChildByName( "DeleteButton" ) );
+ m_pRenderButton = dynamic_cast< CExButton * >( FindChildByName( "RenderButton" ) );
+ m_pPlayButton = dynamic_cast< CExButton * >( FindChildByName( "PlayButton" ) );
+ m_pYouTubeUpload = dynamic_cast< CExButton * >( FindChildByName( "YouTubeUploadButton" ) );
+ m_pYouTubeView = dynamic_cast< CExButton * >( FindChildByName( "ViewYouTubeButton" ) );
+ m_pYouTubeShareURL = dynamic_cast< CExButton * >( FindChildByName( "ShareYouTubeURLButton" ) );
+ m_pShowRenderInfoButton = dynamic_cast< CExImageButton * >( FindChildByName( "ShowRenderInfoButton") );
+
+ if ( m_pDeleteButton )
+ {
+ SetXToRed( m_pDeleteButton );
+ }
+
+ m_pExportMovie->SetParent( GetInset() );
+ m_pYouTubeUpload->SetParent( GetInset() );
+ m_pYouTubeView->SetParent( GetInset() );
+ m_pYouTubeShareURL->SetParent( GetInset() );
+ m_pShowRenderInfoButton->SetParent( GetInset() );
+
+ m_pDeleteButton->SetParent( GetParent()->GetParent()->GetParent() );
+ m_pPlayButton->SetParent( GetParent()->GetParent()->GetParent() );
+ m_pRenderButton->SetParent( GetParent()->GetParent()->GetParent() );
+
+ m_pDeleteButton->AddActionSignalTarget( this );
+ m_pPlayButton->AddActionSignalTarget( this );
+ m_pRenderButton->AddActionSignalTarget( this );
+}
+
+void CReplayDetailsPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ SetTall( GetParent()->GetTall() );
+
+ int nInsetWidth = GetInset()->GetWide();
+ int nScreenshotWidth = nInsetWidth * .55f;
+
+ // Setup info panels along the right-hand side
+ const int nBuffer = 7;
+ const int nLeftRightBuffer = 19;
+ int aPlaybackPos[2];
+ m_pPlaybackPanel->GetPos( aPlaybackPos[0], aPlaybackPos[1] );
+ int nInfoPanelsStartY = aPlaybackPos[1];
+ int nInfoPanelsCurrentY = nInfoPanelsStartY;
+ int nRightColumnWidth = nInsetWidth - nScreenshotWidth - nLeftRightBuffer - XRES(20);
+
+#if defined( TF_CLIENT_DLL )
+ if ( m_pRecordsPanel->ShouldShow() )
+ {
+ m_pRecordsPanel->SetPos( nScreenshotWidth + nLeftRightBuffer, nInfoPanelsStartY );
+ m_pRecordsPanel->SetWide( nRightColumnWidth );
+ m_pRecordsPanel->InvalidateLayout( true, true );
+ m_pRecordsPanel->SetVisible( true );
+ nInfoPanelsCurrentY += m_pRecordsPanel->GetTall() + nBuffer;
+ }
+ else
+#endif
+ {
+ m_pRecordsPanel->SetVisible( false );
+ }
+
+ int insetX, insetY;
+ GetInset()->GetPos( insetX, insetY );
+ m_pScrollPanel->SetPos( nScreenshotWidth + nLeftRightBuffer, nInfoPanelsCurrentY );
+ m_pScrollPanel->SetWide( nRightColumnWidth + XRES(20) );
+ m_pScrollPanel->SetTall( GetTall() - insetY - nInfoPanelsCurrentY );
+ m_pInfoPanel->SetWide( nRightColumnWidth );
+
+ int nCurrentY = 0;
+ for ( int i = 0; i < m_vecInfoPanels.Count(); ++i )
+ {
+ CBaseDetailsPanel *pPanel = m_vecInfoPanels[ i ];
+
+ if ( pPanel->ShouldShow() )
+ {
+ // Set the width since these panel's PerformLayout()'s depend on it
+ pPanel->SetWide( nRightColumnWidth );
+
+ // Call panel's PerformLayout() now
+ pPanel->InvalidateLayout( true, true );
+
+ pPanel->SetPos( 0, nCurrentY );
+
+ // Show it
+ pPanel->SetVisible( true );
+
+ // Update the current y position based on the panel's height (set in its PerformLayout())
+ nCurrentY += pPanel->GetTall() + nBuffer;
+ }
+ else
+ {
+ pPanel->SetVisible( false );
+ }
+ }
+ m_pInfoPanel->SetTall( nCurrentY );
+ m_pInfoPanel->InvalidateLayout( true );
+ m_pScrollPanel->InvalidateLayout( true );
+ m_pScrollPanel->GetScrollbar()->SetAutohideButtons( true );
+ m_pScrollPanel->GetScrollbar()->InvalidateLayout( true );
+
+ // @note Tom Bui: set the positions AGAIN now that we've invalidated, cause VGUI hates me
+ nCurrentY = 0;
+ for ( int i = 0; i < m_vecInfoPanels.Count(); ++i )
+ {
+ CBaseDetailsPanel *pPanel = m_vecInfoPanels[ i ];
+ if ( pPanel->ShouldShow() )
+ {
+ pPanel->SetPos( 0, nCurrentY );
+ nCurrentY += pPanel->GetTall() + nBuffer;
+ }
+ }
+
+ // Setup playback panel based on dimensions of first screenshot
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+ float flAspectRatio;
+ if ( pReplay->GetScreenshotCount() )
+ {
+ const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( 0 );
+ flAspectRatio = (float)pScreenshot->m_nWidth / pScreenshot->m_nHeight;
+ }
+ else
+ {
+ // Default to 4:3 if there are no screenshots
+ flAspectRatio = 4.0f/3;
+ }
+
+ if ( m_pItemManager->AreItemsMovies() )
+ {
+ m_pRenderButton->SetVisible( false );
+ m_pPlayButton->SetVisible( false );
+ m_pExportMovie->SetVisible( true );
+ m_pShowRenderInfoButton->SetVisible( true );
+
+ int nButtonY = nInfoPanelsStartY + m_pPlaybackPanel->GetTall() + YRES( 5 );
+ int nButtonX = 0;
+ m_pYouTubeUpload->SetPos( nButtonX, nButtonY );
+ m_pYouTubeView->SetPos( nButtonX, nButtonY );
+ nButtonX += m_pYouTubeUpload->GetWide() + XRES( 5 );
+
+ m_pYouTubeShareURL->SetPos( nButtonX, nButtonY );
+ nButtonX += m_pYouTubeShareURL->GetWide() + XRES( 5 );
+
+ m_pExportMovie->SetPos( nButtonX, nButtonY );
+
+ int aDeletePos[2];
+ m_pDeleteButton->GetPos( aDeletePos[0], aDeletePos[1] );
+ m_pDeleteButton->SetPos( ScreenWidth() / 2 + XRES( 195 ), aDeletePos[1] );
+
+ int aScreenshotPos[2];
+ m_pPlaybackPanel->GetPos( aScreenshotPos[0], aScreenshotPos[1] );
+ m_pShowRenderInfoButton->SetPos(
+ aScreenshotPos[0] + m_pPlaybackPanel->GetWide() - m_pShowRenderInfoButton->GetWide() - XRES( 8 ),
+ aScreenshotPos[1] + m_pPlaybackPanel->GetTall() - m_pShowRenderInfoButton->GetTall() - YRES( 8 )
+ );
+
+ // retrieve stats
+ if ( m_pYouTubeResponseHandler->m_handle == NULL )
+ {
+ IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
+ if ( pReplayItem && pReplayItem->IsItemAMovie() )
+ {
+ IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
+ if ( pMovie->IsUploaded() )
+ {
+ m_pYouTubeResponseHandler->m_handle = YouTube_GetVideoInfo( pMovie->GetUploadURL(), *m_pYouTubeResponseHandler );
+ SetYouTubeStatus( kYouTubeStatus_RetrievingInfo );
+ }
+ else
+ {
+ SetYouTubeStatus( kYouTubeStatus_NotUploaded );
+ }
+ }
+ }
+ }
+ else
+ {
+ m_pYouTubeUpload->SetVisible( false );
+ m_pYouTubeView->SetVisible( false );
+ m_pYouTubeShareURL->SetVisible( false );
+ m_pShowRenderInfoButton->SetVisible( false );
+
+ // Without this, the name label won't show when we automatically select the recently watched/saved
+ // performance, because the cuts panel width/height isn't set when UpdateNameLabel() gets called
+ // from within CCutsPanel::CCutsPanel().
+ m_pCutsPanel->UpdateNameLabel( m_iSelectedPerformance );
+ }
+}
+
+/*static*/ void CReplayDetailsPanel::OnPlayerWarningDlgConfirm( bool bConfirmed, void *pContext )
+{
+ CReplayDetailsPanel *pPanel = (CReplayDetailsPanel*)pContext;
+ pPanel->ShowExportDialog();
+}
+
+void CReplayDetailsPanel::ShowExportDialog()
+{
+ IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
+ if ( pReplayItem && pReplayItem->IsItemAMovie() )
+ {
+ IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
+ CFmtStr srcMovieFullFilename( "%s%s", g_pReplayMovieManager->GetRenderDir(), pMovie->GetMovieFilename() );
+ if ( !g_pFullFileSystem->FileExists( srcMovieFullFilename.Access() ) )
+ {
+ ShowMessageBox( "#Replay_ExportMovieError_Title", "#Replay_ExportMovieNoFile_Text", "#GameUI_OK" );
+ return;
+ }
+ }
+
+ if ( m_hExportMovieDialog == NULL )
+ {
+ m_hExportMovieDialog = new FileOpenDialog(NULL, "#Replay_FindExportMovieLocation", FOD_SAVE );
+#ifdef USE_WEBM_FOR_REPLAY
+ m_hExportMovieDialog->AddFilter("*.webm", "#Replay_WebMMovieFiles", true );
+#else
+ m_hExportMovieDialog->AddFilter("*.mov", "#Replay_MovieFiles", true );
+#endif
+ m_hExportMovieDialog->AddActionSignalTarget( this );
+ if ( !FStrEq( replay_movie_export_last_dir.GetString(), "" ) )
+ {
+ m_hExportMovieDialog->SetStartDirectory( replay_movie_export_last_dir.GetString() );
+ }
+ }
+ m_hExportMovieDialog->DoModal(false);
+ m_hExportMovieDialog->Activate();
+}
+
+void CReplayDetailsPanel::OnFileSelected( const char *fullpath )
+{
+ // this can take a while, put up a waiting cursor
+ surface()->SetCursor(dc_hourglass);
+
+ IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
+ if ( pReplayItem && pReplayItem->IsItemAMovie() )
+ {
+ IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
+ CFmtStr srcMovieFullFilename( "%s%s", g_pReplayMovieManager->GetRenderDir(), pMovie->GetMovieFilename() );
+ if ( !engine->CopyLocalFile( srcMovieFullFilename.Access(), fullpath ) )
+ {
+ ShowMessageBox( "#Replay_ExportMovieError_Title", "#Replay_ExportMovieError_Text", "#GameUI_OK" );
+ }
+ else
+ {
+ ShowMessageBox( "#Replay_ExportMovieSuccess_Title", "#Replay_ExportMovieSuccess_Text", "#GameUI_OK" );
+ }
+ char basepath[ MAX_PATH ];
+ Q_ExtractFilePath( fullpath, basepath, sizeof( basepath ) );
+ replay_movie_export_last_dir.SetValue( basepath );
+ }
+
+ // change the cursor back to normal
+ surface()->SetCursor(dc_user);
+}
+
+void CReplayDetailsPanel::OnCommand( const char *pCommand )
+{
+ if ( FStrEq( pCommand, "delete_replayitem" ) )
+ {
+ ReplayUI_GetBrowserPanel()->AttemptToDeleteReplayItem( this, m_hReplayItem, m_pItemManager, m_iSelectedPerformance );
+ return;
+ }
+
+ else if ( FStrEq( pCommand, "render_replay_dlg" ) )
+ {
+ ShowRenderDialog();
+ return;
+ }
+
+ else if ( FStrEq( pCommand, "play" ) )
+ {
+ if ( engine->IsInGame() )
+ {
+ ShowPlayConfirmationDialog();
+ }
+ else
+ {
+ g_pClientReplayContext->PlayReplay( m_hReplay, m_iSelectedPerformance, true );
+ }
+ return;
+ }
+
+ else if ( FStrEq( pCommand, "exportmovie" ) )
+ {
+ IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
+ if ( !pReplayItem || !pReplayItem->IsItemAMovie() )
+ return;
+
+ IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
+ if ( !pMovie )
+ return;
+
+ if ( replay_movie_reveal_warning.GetBool() )
+ {
+#ifdef USE_WEBM_FOR_REPLAY
+ CTFMessageBoxDialog *pDialog = ShowMessageBox( "#Replay_Tip", "#Replay_UseVLCPlayer", "#Replay_ThanksIWill", OnPlayerWarningDlgConfirm );
+#else
+ CTFMessageBoxDialog *pDialog = ShowMessageBox( "#Replay_Tip", "#Replay_UseQuickTimePlayer", "#Replay_ThanksIWill", OnPlayerWarningDlgConfirm );
+#endif
+ pDialog->SetContext( this );
+ replay_movie_reveal_warning.SetValue( 0 );
+ }
+ else if ( pMovie->GetRenderSettings().m_bRaw )
+ {
+ ShowMessageBox( "#Replay_CantExport", "#YouTube_Upload_MovieIsRaw", "#GameUI_OK" );
+ }
+ else
+ {
+ ShowExportDialog();
+ }
+ }
+
+ else if ( FStrEq( pCommand, "youtubeupload" ) )
+ {
+ IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
+ if ( pReplayItem && pReplayItem->IsItemAMovie() )
+ {
+ IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
+ if ( !pMovie )
+ return;
+
+ if ( pMovie->GetRenderSettings().m_bRaw )
+ {
+ ShowMessageBox( "#Replay_CantUpload", "#YouTube_Upload_MovieIsRaw", "#GameUI_OK" );
+ return;
+ }
+
+ // Movie already exists?
+ CFmtStr srcMovieFullFilename( "%s%s", g_pReplayMovieManager->GetRenderDir(), pMovie->GetMovieFilename() );
+ if ( !g_pFullFileSystem->FileExists( srcMovieFullFilename.Access() ) )
+ {
+ ShowMessageBox( "#YouTube_Upload_Title", "#YouTube_Upload_MissingFile", "#GameUI_OK" );
+ return;
+ }
+ else if ( pMovie->IsUploaded() )
+ {
+ CTFGenericConfirmDialog *pDialog = ShowConfirmDialog( "#YouTube_Upload_Title", "#YouTube_FileAlreadyUploaded", "#GameUI_OK", "#GameuI_CancelBold", &ConfirmUploadMovie, this );
+ pDialog->SetContext( this );
+ }
+ else
+ {
+ ConfirmUploadMovie( true, this );
+ }
+ }
+ }
+
+ else if ( FStrEq( pCommand, "viewyoutube" ) )
+ {
+ if ( steamapicontext && steamapicontext->SteamFriends() && m_pYouTubeResponseHandler->m_strVideoURL.IsEmpty() == false )
+ {
+ steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( m_pYouTubeResponseHandler->m_strVideoURL.Get() );
+ }
+ }
+
+ else if ( FStrEq( pCommand, "shareyoutubeurl" ) )
+ {
+ system()->SetClipboardText( m_pYouTubeResponseHandler->m_strVideoURL.Get(), m_pYouTubeResponseHandler->m_strVideoURL.Length() );
+ ShowMessageBox( "#Replay_CopyURL_Title", "#Replay_CopyURL_Text", "#GameUI_OK" );
+ }
+
+ else if ( FStrEq( pCommand, "showrenderinfo" ) )
+ {
+ ShowRenderInfo();
+ }
+
+ else
+ {
+ BaseClass::OnCommand( pCommand );
+ }
+}
+
+void CReplayDetailsPanel::ShowRenderInfo()
+{
+ IQueryableReplayItem *pReplayItem = m_pItemManager->GetItem( m_hReplayItem );
+ if ( !pReplayItem || !pReplayItem->IsItemAMovie() )
+ return;
+
+ IReplayMovie *pMovie = static_cast< IReplayMovie * >( pReplayItem );
+ const ReplayRenderSettings_t &Settings = pMovie->GetRenderSettings();
+ const wchar_t *pCodecName = g_pVideo ? g_pVideo->GetCodecName( Settings.m_Codec ) : L"?";
+ wchar_t *pAAEnabled = g_pVGuiLocalize->Find( Settings.m_bAAEnabled ? "#Replay_Enabled" : "#Replay_Disabled" );
+ wchar_t *pRaw = g_pVGuiLocalize->Find( Settings.m_bRaw ? "#Replay_Yes" : "#Replay_No" );
+ CFmtStr fmtRes( "%ix%i", Settings.m_nWidth, Settings.m_nHeight );
+ CFmtStr fmtFramerate( "%.3f", Settings.m_FPS.GetFPS() );
+
+ KeyValuesAD kvParams( "params" );
+ kvParams->SetString( "res", fmtRes.Access() );
+ kvParams->SetString( "framerate", fmtFramerate.Access() );
+ kvParams->SetInt( "motionblurquality", Settings.m_nMotionBlurQuality );
+ kvParams->SetInt( "encodingquality", Settings.m_nEncodingQuality );
+ kvParams->SetWString( "codec", pCodecName );
+ kvParams->SetWString( "antialiasing", pAAEnabled );
+ kvParams->SetString( "rendertime", CReplayTime::FormatTimeString( pMovie->GetRenderTime() ) );
+ kvParams->SetWString( "raw", pRaw );
+
+ wchar_t wszStr[1024];
+ g_pVGuiLocalize->ConstructString(
+ wszStr,
+ sizeof( wszStr ),
+ "#Replay_MovieRenderInfo",
+ kvParams
+ );
+
+ ShowMessageBox( "#Replay_RenderInfo", wszStr, "#GameUI_OK" );
+}
+
+void CReplayDetailsPanel::GoBack()
+{
+ // Send to parent
+ GetParent()->OnCommand( "back" );
+}
+
+void CReplayDetailsPanel::ShowPlayConfirmationDialog()
+{
+ CConfirmDisconnectFromServerDialog *pConfirm = SETUP_PANEL( new CConfirmDisconnectFromServerDialog( this ) );
+ if ( pConfirm )
+ {
+ pConfirm->Show();
+ }
+}
+
+void CReplayDetailsPanel::OnConfirmDisconnect( KeyValues *pParams )
+{
+ if ( pParams->GetBool( "confirmed" ) )
+ {
+ g_pClientReplayContext->PlayReplay( m_hReplay, m_iSelectedPerformance, true );
+ }
+}
+
+void CReplayDetailsPanel::OnMessage( const KeyValues* pParams, VPANEL hFromPanel )
+{
+ if ( FStrEq( pParams->GetName(), "ReplayItemDeleted" ) )
+ {
+ const int iPerformance = const_cast< KeyValues * >( pParams )->GetInt( "perf", -1 );
+ if ( iPerformance >= 0 )
+ {
+ CReplayPerformance *pPerformance = GetGenericClassBasedReplay( m_hReplay )->GetPerformance( m_iSelectedPerformance );
+ g_pReplayPerformanceManager->DeletePerformance( pPerformance );
+ m_pCutsPanel->InvalidateLayout( true, false ); // Without this, m_nVisibleButtons will be wrong.
+ m_pCutsPanel->OnPerformanceDeleted( m_iSelectedPerformance );
+ }
+ else
+ {
+ GoBack();
+ }
+ return;
+ }
+
+ BaseClass::OnMessage( pParams, hFromPanel );
+}
+
+void CReplayDetailsPanel::ShowRenderDialog()
+{
+ ::ReplayUI_ShowRenderDialog( this, m_hReplay, false, m_iSelectedPerformance );
+}
+
+void CReplayDetailsPanel::FreeMovieFileLock()
+{
+ m_pPlaybackPanel->FreeMovieMaterial();
+}
+
+void CReplayDetailsPanel::SetYouTubeStatus( eYouTubeStatus status )
+{
+ m_pYouTubeUpload->SetVisible( status == kYouTubeStatus_CouldNotRetrieveInfo || status == kYouTubeStatus_NotUploaded );
+ m_pYouTubeUpload->SetEnabled( status == kYouTubeStatus_CouldNotRetrieveInfo || status == kYouTubeStatus_NotUploaded );
+ m_pYouTubeView->SetVisible( !m_pYouTubeUpload->IsVisible() );
+ m_pYouTubeView->SetEnabled( status == kYouTubeStatus_RetrievedInfo );
+ m_pYouTubeShareURL->SetEnabled( status == kYouTubeStatus_RetrievedInfo );
+}
+
+void CReplayDetailsPanel::OnMousePressed( MouseCode code )
+{
+ if ( code == MOUSE_LEFT )
+ {
+ RequestFocus();
+ }
+}
+
+void CReplayDetailsPanel::OnKeyCodeTyped( KeyCode code )
+{
+ if ( code == KEY_DELETE )
+ {
+ OnCommand( "delete_replayitem" );
+ }
+
+ BaseClass::OnKeyCodeTyped( code );
+}
+
+#endif
diff --git a/sp/src/game/client/replay/vgui/replaybrowserdetailspanel.h b/sp/src/game/client/replay/vgui/replaybrowserdetailspanel.h
new file mode 100644
index 00000000..5a75ae88
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserdetailspanel.h
@@ -0,0 +1,462 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef REPLAYBROWSER_DETAILSPANEL_H
+#define REPLAYBROWSER_DETAILSPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <game/client/iviewport.h>
+#include "vgui_controls/EditablePanel.h"
+#include "vgui_controls/ScrollableEditablePanel.h"
+#include "replay/iqueryablereplayitem.h"
+#include "replay/ireplaymovie.h"
+#include "replay/replayhandle.h"
+#include "replay/gamedefs.h"
+#include "econ/econ_controls.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+#define NUM_CLASSES_IN_LOADOUT_PANEL (TF_LAST_NORMAL_CLASS-1) // We don't allow unlockables for the civilian
+
+//-----------------------------------------------------------------------------
+// Purpose: Forward declarations
+//-----------------------------------------------------------------------------
+class CExLabel;
+class CExButton;
+class CTFReplay;
+class CReplayPerformance;
+class IReplayItemManager;
+
+//-----------------------------------------------------------------------------
+// Purpose: A panel containing 2 labels: one key, one value
+//-----------------------------------------------------------------------------
+class CKeyValueLabelPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CKeyValueLabelPanel, EditablePanel );
+public:
+ CKeyValueLabelPanel( Panel *pParent, const char *pKey, const char *pValue );
+ CKeyValueLabelPanel( Panel *pParent, const char *pKey, const wchar_t *pValue );
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ int GetHeight() const;
+ int GetValueHeight() const;
+
+ void SetValue( const wchar_t *pValue );
+
+private:
+ CExLabel *m_pLabels[2];
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Base details panel with left/top padding and black border
+//-----------------------------------------------------------------------------
+class CBaseDetailsPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CBaseDetailsPanel, EditablePanel );
+public:
+ CBaseDetailsPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ int GetMarginSize() const { return XRES(6); }
+
+ bool ShouldShow() const { return m_bShouldShow; }
+
+protected:
+ EditablePanel *GetInset() { return m_pInsetPanel; }
+
+ ReplayHandle_t m_hReplay;
+ bool m_bShouldShow;
+
+private:
+ EditablePanel *m_pInsetPanel; // padding on left/top
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Score panel - contains score & any records from the round
+//-----------------------------------------------------------------------------
+class CRecordsPanel : public CBaseDetailsPanel
+{
+ DECLARE_CLASS_SIMPLE( CRecordsPanel, CBaseDetailsPanel );
+public:
+ CRecordsPanel( Panel *pParent, ReplayHandle_t hReplay );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+private:
+ ImagePanel *m_pClassImage;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Stats panel
+//-----------------------------------------------------------------------------
+class CStatsPanel : public CBaseDetailsPanel
+{
+ DECLARE_CLASS_SIMPLE( CStatsPanel, CBaseDetailsPanel );
+public:
+ CStatsPanel( Panel *pParent, ReplayHandle_t hReplay );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+private:
+ CKeyValueLabelPanel *m_paStatLabels[ REPLAY_MAX_DISPLAY_GAMESTATS ];
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Dominations panel
+//-----------------------------------------------------------------------------
+class CDominationsPanel : public CBaseDetailsPanel
+{
+ DECLARE_CLASS_SIMPLE( CDominationsPanel, CBaseDetailsPanel );
+public:
+ CDominationsPanel( Panel *pParent, ReplayHandle_t hReplay );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ ImagePanel *m_pNumDominationsImage;
+ CUtlVector< ImagePanel * > m_vecDominationImages;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Kills panel
+//-----------------------------------------------------------------------------
+class CKillsPanel : public CBaseDetailsPanel
+{
+ DECLARE_CLASS_SIMPLE( CKillsPanel, CBaseDetailsPanel );
+public:
+ CKillsPanel( Panel *pParent, ReplayHandle_t hReplay );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ CKeyValueLabelPanel *m_pKillLabels;
+ CUtlVector< ImagePanel * > m_vecKillImages;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CBasicLifeInfoPanel : public CBaseDetailsPanel
+{
+ DECLARE_CLASS_SIMPLE( CBasicLifeInfoPanel, CBaseDetailsPanel );
+public:
+ CBasicLifeInfoPanel( Panel *pParent, ReplayHandle_t hReplay );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+private:
+ CKeyValueLabelPanel *m_pKilledByLabels;
+ CKeyValueLabelPanel *m_pMapLabels;
+ CKeyValueLabelPanel *m_pLifeLabels;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CMovieInfoPanel : public CBaseDetailsPanel
+{
+ DECLARE_CLASS_SIMPLE( CMovieInfoPanel, CBaseDetailsPanel );
+public:
+ CMovieInfoPanel( Panel *pParent, ReplayHandle_t hReplay, QueryableReplayItemHandle_t hMovie,
+ IReplayItemManager *pItemManager );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+private:
+ CKeyValueLabelPanel *m_pRenderTimeLabels;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CYouTubeInfoPanel : public CBaseDetailsPanel
+{
+ DECLARE_CLASS_SIMPLE( CYouTubeInfoPanel, CBaseDetailsPanel );
+public:
+ CYouTubeInfoPanel( Panel *pParent );
+
+ virtual void PerformLayout();
+
+ void SetInfo( const wchar_t *pInfo );
+
+private:
+ CKeyValueLabelPanel *m_pLabels;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CTitleEditPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CTitleEditPanel, EditablePanel );
+public:
+ CTitleEditPanel( Panel *pParent, QueryableReplayItemHandle_t hReplayItem, IReplayItemManager *pItemManager );
+ ~CTitleEditPanel();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+ virtual void PaintBackground();
+
+ virtual void OnKeyCodeTyped(vgui::KeyCode code);
+
+ virtual void OnTick();
+
+ bool m_bMouseOver;
+ TextEntry *m_pTitleEntry;
+ ImagePanel *m_pHeaderLine;
+ CExLabel *m_pClickToEditLabel;
+ CExLabel *m_pCaratLabel;
+ QueryableReplayItemHandle_t m_hReplayItem;
+ IReplayItemManager *m_pItemManager;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CReplayScreenshotSlideshowPanel;
+
+class CPlaybackPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CPlaybackPanel, EditablePanel );
+public:
+ CPlaybackPanel( Panel *pParent );
+ ~CPlaybackPanel();
+
+ virtual void FreeMovieMaterial() {}
+
+protected:
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ inline int GetMarginSize() { return 9; }
+ inline int GetViewWidth() { return GetWide() - 2 * GetMarginSize(); }
+ inline int GetViewHeight() { return GetTall() - 2 * GetMarginSize(); }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CPlaybackPanelSlideshow : public CPlaybackPanel
+{
+ DECLARE_CLASS_SIMPLE( CPlaybackPanelSlideshow, CPlaybackPanel );
+public:
+ CPlaybackPanelSlideshow( Panel *pParent, ReplayHandle_t hReplay );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+private:
+ ReplayHandle_t m_hReplay;
+ CExLabel *m_pNoScreenshotLabel;
+ CReplayScreenshotSlideshowPanel *m_pScreenshotImage;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CMoviePlayerPanel;
+
+class CPlaybackPanelMovie : public CPlaybackPanel
+{
+ DECLARE_CLASS_SIMPLE( CPlaybackPanelMovie, CPlaybackPanel );
+public:
+ CPlaybackPanelMovie( Panel *pParent, ReplayHandle_t hReplay );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ virtual void FreeMovieMaterial();
+
+private:
+ CExLabel *m_pLoadingLabel;
+ CMoviePlayerPanel *m_pMoviePlayerPanel;
+ ReplayHandle_t m_hMovie;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CCutImagePanel : public CExImageButton
+{
+ DECLARE_CLASS_SIMPLE( CCutImagePanel, CExImageButton );
+public:
+ CCutImagePanel( Panel *pParent, const char *pName );
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ virtual void SetSelected( bool bState );
+
+private:
+ virtual IBorder *GetBorder( bool bDepressed, bool bArmed, bool bSelected, bool bKeyFocus );
+
+ IBorder *m_pSelectedBorder;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CReplayDetailsPanel;
+
+class CCutsPanel : public CBaseDetailsPanel
+{
+ DECLARE_CLASS_SIMPLE( CCutsPanel, CBaseDetailsPanel );
+public:
+ CCutsPanel( Panel *pParent, ReplayHandle_t hReplay, int iPerformance );
+ ~CCutsPanel();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+ virtual void OnCommand( const char *pCommand );
+ virtual void ApplySettings( KeyValues *pInResourceData );
+
+ void OnPerformanceDeleted( int iPerformance );
+
+ CPanelAnimationVarAliasType( int, m_nCutButtonWidth, "cut_button_width", "0", "proportional_xpos" );
+ CPanelAnimationVarAliasType( int, m_nCutButtonHeight, "cut_button_height", "0", "proportional_ypos" );
+ CPanelAnimationVarAliasType( int, m_nCutButtonBuffer, "cut_button_buffer", "0", "proportional_xpos" );
+ CPanelAnimationVarAliasType( int, m_nCutButtonSpace, "cut_button_space", "0", "proportional_xpos" );
+ CPanelAnimationVarAliasType( int, m_nCutButtonSpaceWide, "cut_button_space_wide", "0", "proportional_xpos" );
+ CPanelAnimationVarAliasType( int, m_nTopMarginHeight, "top_margin_height", "0", "proportional_ypos" );
+ CPanelAnimationVarAliasType( int, m_nNameLabelTopMargin, "name_label_top_margin", "0", "proportional_ypos" );
+ CPanelAnimationVarAliasType( int, m_nButtonStartY, "button_start_y", "0", "proportional_ypos" );
+
+ void UpdateNameLabel( int iPerformance );
+
+private:
+ void SelectButtonFromPerformance( int iPerformance );
+ void SetPage( int iPage, int iButtonToSelect = 0 );
+ int ButtonToPerformance( int iButton ) const;
+ int PerformanceToButton( int iPerformance ) const;
+ const CReplayPerformance *GetPerformance( int iPerformance ) const;
+
+ virtual void OnTick();
+
+ struct ButtonInfo_t
+ {
+ CExImageButton *m_pButton;
+ CExButton *m_pAddToRenderQueueButton;
+ int m_iPerformance;
+ };
+
+ enum Consts_t
+ {
+ BUTTONS_PER_PAGE = 4
+ };
+
+ ButtonInfo_t m_aButtons[ BUTTONS_PER_PAGE ];
+ EditablePanel *m_pVerticalLine;
+ CExLabel *m_pNoCutsLabel;
+ CExLabel *m_pOriginalLabel;
+ CExLabel *m_pCutsLabel;
+ CExLabel *m_pNameLabel;
+ CExButton *m_pPrevButton;
+ CExButton *m_pNextButton;
+ int m_iPage;
+ int m_nVisibleButtons;
+ vgui::DHANDLE< CReplayDetailsPanel > m_hDetailsPanel;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class IReplayItemManager;
+class CConfirmDialog;
+class CYouTubeGetStatsHandler;
+
+class CReplayDetailsPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayDetailsPanel, EditablePanel );
+public:
+ CReplayDetailsPanel( Panel *pParent, QueryableReplayItemHandle_t hReplayItem, int iPerformance, IReplayItemManager *pItemManager );
+ ~CReplayDetailsPanel();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ virtual void OnMousePressed( MouseCode code );
+ virtual void OnKeyCodeTyped( KeyCode code );
+
+ virtual void OnCommand( const char *pCommand );
+
+ virtual void OnMessage( const KeyValues* pParams, VPANEL hFromPanel );
+
+ EditablePanel *GetInset() { return m_pInsetPanel; }
+
+ void ShowRenderDialog();
+ void FreeMovieFileLock();
+ void ShowExportDialog();
+
+ static void OnPlayerWarningDlgConfirm( bool bConfirmed, void *pContext );
+
+ enum eYouTubeStatus
+ {
+ kYouTubeStatus_Private,
+ kYouTubeStatus_RetrievingInfo,
+ kYouTubeStatus_RetrievedInfo,
+ kYouTubeStatus_CouldNotRetrieveInfo,
+ kYouTubeStatus_NotUploaded
+ };
+
+ void SetYouTubeStatus( eYouTubeStatus status );
+
+ EditablePanel *m_pInsetPanel; // Parent to most child panels listed here - narrower than screen width
+ EditablePanel *m_pInfoPanel; // Container for info panels
+ ScrollableEditablePanel *m_pScrollPanel;
+
+ CPlaybackPanel *m_pPlaybackPanel; // Contains screenshot, playback button
+ CRecordsPanel *m_pRecordsPanel; // Contains score, records
+ CStatsPanel *m_pStatsPanel; // Contains stats
+ CDominationsPanel *m_pDominationsPanel; // Dominations
+ CBasicLifeInfoPanel *m_pBasicInfoPanel; // Killed by, map, life
+ CKillsPanel *m_pKillsPanel; // # kills, kill class icons
+ CYouTubeInfoPanel *m_pYouTubeInfoPanel; // YouTube Info
+ CCutsPanel *m_pCutsPanel; // Buttons for performances
+ CUtlVector< CBaseDetailsPanel* > m_vecInfoPanels; // List of panels on the right
+ CTitleEditPanel *m_pTitleEditPanel;
+ CExButton *m_pBackButton;
+ CExButton *m_pDeleteButton;
+ CExButton *m_pRenderButton;
+ CExButton *m_pPlayButton;
+ CExButton *m_pExportMovie;
+ CExButton *m_pYouTubeUpload;
+ CExButton *m_pYouTubeView;
+ CExButton *m_pYouTubeShareURL;
+ CExImageButton *m_pShowRenderInfoButton;
+ QueryableReplayItemHandle_t m_hReplayItem;
+ ReplayHandle_t m_hReplay;
+ IReplayItemManager *m_pItemManager;
+ int m_iSelectedPerformance; // Which performance to play/render/delete
+ CYouTubeGetStatsHandler *m_pYouTubeResponseHandler;
+ vgui::FileOpenDialog *m_hExportMovieDialog;
+
+private:
+ void ShowRenderInfo();
+
+ MESSAGE_FUNC_PARAMS( OnConfirmDisconnect, "ConfirmDlgResult", data );
+ MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
+
+ CPanelAnimationVarAliasType( int, m_nMarginWidth, "margin_width", "0", "proportional_xpos" );
+
+ void GoBack();
+ void ShowPlayConfirmationDialog();
+};
+
+#endif // REPLAYBROWSER_DETAILSPANEL_H
diff --git a/sp/src/game/client/replay/vgui/replaybrowseritemmanager.cpp b/sp/src/game/client/replay/vgui/replaybrowseritemmanager.cpp
new file mode 100644
index 00000000..1494c4eb
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowseritemmanager.cpp
@@ -0,0 +1,133 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowseritemmanager.h"
+#include "replaybrowserbasepage.h"
+#include "replay/ireplaymoviemanager.h"
+#include "replay/ireplaymanager.h"
+#include "replay/ireplaymovie.h"
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+extern IClientReplayContext *g_pClientReplayContext;
+extern IReplayMovieManager *g_pReplayMovieManager;
+
+//-----------------------------------------------------------------------------
+
+class CReplayItemManager : public IReplayItemManager
+{
+public:
+ virtual int GetItemCount()
+ {
+ return g_pReplayManager->GetReplayCount();
+ }
+
+ virtual void GetItems( CUtlLinkedList< IQueryableReplayItem *, int > &items )
+ {
+ g_pReplayManager->GetReplaysAsQueryableItems( items );
+ }
+
+ virtual IQueryableReplayItem *GetItem( ReplayItemHandle_t hItem )
+ {
+ return static_cast< CReplay * >( g_pReplayManager->GetReplay( (ReplayHandle_t)hItem ) );
+ }
+
+ virtual bool AreItemsMovies()
+ {
+ return false;
+ }
+
+ virtual void DeleteItem( Panel *pPage, ReplayItemHandle_t hItem, bool bNotifyUI )
+ {
+ g_pReplayManager->DeleteReplay( (ReplayHandle_t)hItem, bNotifyUI );
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+class CMovieItemManager : public IReplayItemManager
+{
+public:
+ virtual int GetItemCount()
+ {
+ return g_pReplayMovieManager->GetMovieCount();
+ }
+
+ virtual void GetItems( CUtlLinkedList< IQueryableReplayItem *, int > &items )
+ {
+ g_pReplayMovieManager->GetMoviesAsQueryableItems( items );
+ }
+
+ virtual IQueryableReplayItem *GetItem( ReplayItemHandle_t hItem )
+ {
+ return g_pReplayMovieManager->GetMovie( (ReplayHandle_t)hItem );
+ }
+
+ virtual bool AreItemsMovies()
+ {
+ return true;
+ }
+
+ virtual void DeleteItem( Panel *pPage, ReplayItemHandle_t hItem, bool bNotifyUI )
+ {
+ CReplayBrowserBasePage *pBasePage = static_cast< CReplayBrowserBasePage * >( pPage );
+
+ // Free the lock so the file is deletable
+ pBasePage->FreeDetailsPanelMovieLock();
+
+ // Delete the entry & the file
+ g_pReplayMovieManager->DeleteMovie( hItem );
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+static CReplayItemManager s_ReplayItemManager;
+static CMovieItemManager s_MovieItemManager;
+
+//-----------------------------------------------------------------------------
+
+IReplayItemManager *GetReplayItemManager()
+{
+ return &s_ReplayItemManager;
+}
+
+IReplayItemManager *GetReplayMovieItemManager()
+{
+ return &s_MovieItemManager;
+}
+
+IQueryableReplayItem *FindReplayItem( ReplayItemHandle_t hItem, IReplayItemManager **ppItemManager )
+{
+ static IReplayItemManager *s_pItemManagers[] = { &s_ReplayItemManager, &s_MovieItemManager };
+
+ if ( ppItemManager )
+ {
+ *ppItemManager = NULL;
+ }
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ IQueryableReplayItem *pItem = s_pItemManagers[ i ]->GetItem( hItem );
+ if ( pItem )
+ {
+ if ( ppItemManager )
+ {
+ *ppItemManager = s_pItemManagers[ i ];
+ }
+ return pItem;
+ }
+ }
+ return NULL;
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaybrowseritemmanager.h b/sp/src/game/client/replay/vgui/replaybrowseritemmanager.h
new file mode 100644
index 00000000..311b0cff
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowseritemmanager.h
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#ifndef REPLAYBROWSER_ITEMMANAGER_H
+#define REPLAYBROWSER_ITEMMANAGER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utllinkedlist.h"
+#include <vgui_controls/Panel.h>
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+typedef int ReplayItemHandle_t;
+
+//-----------------------------------------------------------------------------
+// Purpose: Layer of abstraction between UI and replay demos or rendered movies
+//-----------------------------------------------------------------------------
+class IQueryableReplayItem;
+
+abstract_class IReplayItemManager : public IBaseInterface
+{
+public:
+ virtual int GetItemCount() = 0;
+ virtual void GetItems( CUtlLinkedList< IQueryableReplayItem *, int > &items ) = 0;
+ virtual IQueryableReplayItem *GetItem( ReplayItemHandle_t hItem ) = 0;
+ virtual bool AreItemsMovies() = 0;
+ virtual void DeleteItem( vgui::Panel *pPage, ReplayItemHandle_t hItem, bool bNotifyUI ) = 0;
+};
+
+IReplayItemManager *GetReplayItemManager();
+IReplayItemManager *GetReplayMovieItemManager();
+
+// Find an item and put the item manager in ppItemManager
+IQueryableReplayItem *FindReplayItem( ReplayItemHandle_t hItem, IReplayItemManager **ppItemManager );
+
+#endif // REPLAYBROWSER_ITEMMANAGER_H
diff --git a/sp/src/game/client/replay/vgui/replaybrowserlistitempanel.cpp b/sp/src/game/client/replay/vgui/replaybrowserlistitempanel.cpp
new file mode 100644
index 00000000..77491fc1
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserlistitempanel.cpp
@@ -0,0 +1,1090 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowserlistitempanel.h"
+#include "replaybrowsermainpanel.h"
+#include "replaybrowserlistpanel.h"
+#include "replaybrowserrenderdialog.h"
+#include "replaybrowsermovieplayerpanel.h"
+#include "vgui/IInput.h"
+#include "vgui/IVGui.h"
+#include "filesystem.h"
+#include "replay/screenshot.h"
+#include "replay/ireplaymanager.h"
+#include "confirm_dialog.h"
+#include "replay/ireplaymoviemanager.h"
+#include "econ/econ_controls.h"
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+extern IReplayMovieManager *g_pReplayMovieManager;
+
+//-----------------------------------------------------------------------------
+
+#define REPLAY_BORDER_WIDTH XRES(2)
+#define REPLAY_BORDER_HEIGHT YRES(2)
+#define REPLAY_BUFFER_HEIGHT XRES(5)
+
+//-----------------------------------------------------------------------------
+
+CReplayScreenshotSlideshowPanel::CReplayScreenshotSlideshowPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay )
+: CSlideshowPanel( pParent, pName ),
+ m_hReplay( hReplay )
+{
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( hReplay );
+
+ // Add all screenshots for the given replay
+ char szImage[ MAX_OSPATH ];
+ for ( int i = 0; i < pReplay->GetScreenshotCount(); ++i )
+ {
+ const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( i );
+ V_snprintf( szImage, sizeof( szImage ), "replay/thumbnails/%s.vtf", pScreenshot->m_szBaseFilename );
+
+ // Add image to list of slideshow images
+ AddImage( szImage );
+
+ if ( i == 0 )
+ {
+ // Set first image
+ GetImagePanel()->SetImage( szImage );
+ }
+ }
+}
+
+void CReplayScreenshotSlideshowPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+
+CReplayBrowserThumbnail::CReplayBrowserThumbnail( Panel *pParent, const char *pName, QueryableReplayItemHandle_t hReplayItem,
+ IReplayItemManager *pReplayItemManager )
+: CReplayBasePanel( pParent, pName ),
+ m_hReplayItem( hReplayItem ),
+ m_pReplayItemManager( pReplayItemManager ),
+ m_bMouseOver( false ),
+ m_pMoviePlayer( NULL ),
+ m_flLastMovieScrubTime( 0.0f ),
+ m_flHoverStartTime( 0.0f ),
+ m_flLastProgressChangeTime( 0.0f )
+{
+ SetScheme( "ClientScheme" );
+
+ ivgui()->AddTickSignal( GetVPanel(), 10 );
+
+ // Add the list panel as an action signal target
+ // NOTE: Vile hack.
+ Panel *pTarget = GetParent();
+ while ( pTarget )
+ {
+ if ( !V_strcmp( "BasePage", pTarget->GetName() ) )
+ break;
+ pTarget = pTarget->GetParent();
+ }
+ Assert( pTarget );
+ AddActionSignalTarget( pTarget );
+
+ m_pScreenshotThumb = new CCrossfadableImagePanel( this, "ScreenshotThumbnail" );
+ m_pTitle = new Label( this, "TitleLabel", "" );
+ m_pDownloadLabel = new Label( this, "DownloadLabel", "" );
+ m_pRecordingInProgressLabel = new Label( this, "RecordingInProgressLabel", "" );
+ m_pDownloadProgress = new ProgressBar( this, "DownloadProgress" );
+ m_pDownloadButton = new CExButton( this, "DownloadButton", "#Replay_Download" );
+ m_pDeleteButton = new CExButton( this, "DeleteButton", "#X_Delete" );
+ m_pErrorLabel = new Label( this, "ErrorLabel", "" );
+ m_pDownloadOverlay = new Panel( this, "DownloadOverlay" );
+ m_pBorderPanel = new EditablePanel( this, "BorderPanel" );
+
+ m_pScreenshotThumb->InstallMouseHandler( this );
+
+ m_pDownloadButton->AddActionSignalTarget( this );
+ m_pDownloadButton->SetCommand( new KeyValues( "download" ) );
+
+ m_pDeleteButton->AddActionSignalTarget( this );
+ m_pDeleteButton->SetCommand( new KeyValues( "delete_replayitem" ) );
+
+ SetProportional( true );
+
+ SetReplayItem( hReplayItem );
+}
+
+CReplayBrowserThumbnail::~CReplayBrowserThumbnail()
+{
+ // Clear the download event handler ptr
+ CGenericClassBasedReplay *pReplay = GetReplay();
+ if ( pReplay )
+ {
+ pReplay->m_pDownloadEventHandler = NULL;
+ }
+
+ SetupReplayItemUserData( NULL );
+
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CReplayBrowserThumbnail::SetReplayItem( QueryableReplayItemHandle_t hReplayItem )
+{
+ if ( m_hReplayItem != REPLAY_HANDLE_INVALID && m_hReplayItem != hReplayItem )
+ {
+ IQueryableReplayItem *pReplayItem = m_pReplayItemManager->GetItem( m_hReplayItem );
+ if ( pReplayItem )
+ {
+ pReplayItem->SetUserData( NULL );
+
+ CGenericClassBasedReplay *pTFReplay = ToGenericClassBasedReplay( pReplayItem->GetItemReplay() );
+ pTFReplay->m_pDownloadEventHandler = NULL;
+ }
+ }
+
+ m_hReplayItem = hReplayItem;
+
+ if ( hReplayItem != REPLAY_HANDLE_INVALID )
+ {
+ // Set this as user data
+ SetupReplayItemUserData( (void *)this );
+ }
+
+ InvalidateLayout();
+}
+
+void CReplayBrowserThumbnail::SetupReplayItemUserData( void *pUserData )
+{
+ IQueryableReplayItem *pReplayItem = m_pReplayItemManager->GetItem( m_hReplayItem );
+ if ( pReplayItem )
+ {
+ pReplayItem->SetUserData( pUserData );
+ }
+}
+
+void CReplayBrowserThumbnail::OnTick()
+{
+ const CGenericClassBasedReplay *pReplay = GetReplay();
+ if ( !pReplay )
+ return;
+
+ // Get out if the delete confirmation dialog is up
+ if ( vgui::input()->GetAppModalSurface() )
+ return;
+
+ // Need to update layout if status has changed, since some buttons may need to be shifted around
+ // TODO: Only layout when necessary
+ InvalidateLayout( true, false );
+
+ // Get mouse position and store state
+ int x,y;
+ vgui::input()->GetCursorPos(x, y);
+ bool bOldMouseOver = m_bMouseOver;
+ m_bMouseOver = m_pBorderPanel->IsWithin(x,y);
+
+ // If we are just starting to hover over the thumbnail, mark the time
+ if ( bOldMouseOver != m_bMouseOver && m_bMouseOver )
+ {
+ m_flHoverStartTime = gpGlobals->realtime;
+ }
+
+ const char *pBorderName = m_bMouseOver ? "ReplayHighlightBorder" : "ReplayDefaultBorder";
+ IBorder *pBorder = scheme()->GetIScheme( GetScheme() )->GetBorder( pBorderName );
+ m_pBorderPanel->SetBorder( pBorder );
+
+ // Set visibility of buttons and such - a player may have saved their replay but not died yet,
+ // in which case pReplay->m_bComplete will be false.
+ const IQueryableReplayItem *pItem = m_pReplayItemManager->GetItem( m_hReplayItem );
+ bool bIncomplete = !pReplay->m_bComplete;
+ bool bDownloadPhase = !pItem->IsItemAMovie() && pReplay->m_nStatus == CReplay::REPLAYSTATUS_DOWNLOADPHASE;
+ bool bErrorState = pReplay->m_nStatus == CReplay::REPLAYSTATUS_ERROR;
+
+ m_pDownloadButton->SetVisible( false );
+ m_pDeleteButton->SetVisible( bErrorState || bDownloadPhase );
+ m_pDownloadOverlay->SetVisible( bErrorState || bDownloadPhase || bIncomplete );
+ m_pDownloadProgress->SetVisible( bDownloadPhase );
+ m_pErrorLabel->SetVisible( bErrorState );
+ m_pRecordingInProgressLabel->SetVisible( bIncomplete );
+
+ UpdateProgress( bDownloadPhase, pReplay );
+}
+
+void CReplayBrowserThumbnail::UpdateProgress( bool bDownloadPhase, const CReplay *pReplay )
+{
+ if ( !bDownloadPhase )
+ return;
+
+ // Get current download progress
+ const float flProgress = g_pClientReplayContext->GetReplayManager()->GetDownloadProgress( pReplay );
+
+ // Has progress changed?
+ const int nNewProgress = 100 * flProgress;
+ const int nOldProgress = 100 * m_pDownloadProgress->GetProgress();
+ const float flCurTime = gpGlobals->realtime;
+ if ( nNewProgress != nOldProgress )
+ {
+ m_flLastProgressChangeTime = flCurTime;
+ }
+
+ // Set download progress
+ m_pDownloadProgress->SetProgress( flProgress );
+
+ const char *pToken = "#Replay_Waiting";
+ if ( ReplayUI_GetBrowserPanel() && ( flCurTime - ReplayUI_GetBrowserPanel()->GetTimeOpened() > 2.0f ) )
+ {
+ // Use "downloading" string if progress has changed in the last two seconds
+ if ( ( flCurTime - m_flLastProgressChangeTime ) < 2.0f )
+ {
+ pToken = "#Replay_Downloading";
+ }
+ }
+
+ const wchar_t *pLocalizedText = g_pVGuiLocalize->Find( pToken );
+ if ( pLocalizedText )
+ {
+ // Add animating '...' to end of string
+ wchar_t wszText[128];
+ wcscpy( wszText, pLocalizedText );
+ int nNumPeriods = fmod( gpGlobals->realtime * 2.0f, 4.0f ); // Max of 3 dots
+ const int nLen = wcslen( wszText );
+ wcscat( wszText, L"..." );
+ wszText[ nLen + nNumPeriods ] = L'\0';
+ m_pDownloadLabel->SetText( wszText );
+ m_pDownloadLabel->SizeToContents();
+ m_pDownloadLabel->SetWide( m_pDownloadProgress->GetWide() );
+ m_pDownloadLabel->SetPos( 3, (m_pDownloadProgress->GetTall() - m_pDownloadLabel->GetTall()) / 2 );
+ }
+}
+
+void CReplayBrowserThumbnail::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/listthumbnail.res", "GAME" );
+
+ // Get default from the .res file
+ m_clrDefaultBg = GetBgColor();
+ m_clrHighlight = GetSchemeColor( "TanDark", Color( 255, 255, 255, 255 ), pScheme );
+}
+
+void CReplayBrowserThumbnail::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ const CGenericClassBasedReplay *pReplay = GetReplay();
+ AssertValidReadPtr( pReplay );
+
+ if ( !pReplay )
+ return;
+
+ // Get thumbnail for first screenshot
+ char szImage[MAX_OSPATH] = { '\0' };
+ bool bHasScreenshots = false;
+ if ( pReplay->GetScreenshotCount() )
+ {
+ // Use first screenshot for thumbnail
+ bHasScreenshots = true;
+ const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( 0 );
+ V_snprintf( szImage, sizeof( szImage ), "replay/thumbnails/%s.vtf", pScreenshot->m_szBaseFilename );
+ }
+
+ // See if it exists
+ char szSearch[MAX_OSPATH];
+ V_snprintf( szSearch, sizeof( szSearch ), "materials/vgui/%s", szImage );
+ bool bShowScreenshotThumb = true;
+ if ( !bHasScreenshots || !g_pFullFileSystem || !g_pFullFileSystem->FileExists( szSearch, "GAME" ) )
+ {
+ // Hide it
+ bShowScreenshotThumb = false;
+ }
+
+ // Scale the screenshot so that it clips off the dead area of the power of 2 texture
+ float flScale = pReplay->GetScreenshotCount() == 0 ? 1.0f : ( (float)m_pScreenshotThumb->GetWide() / ( .95f * pReplay->GetScreenshot( 0 )->m_nWidth ) );
+ m_pScreenshotThumb->SetScaleAmount( flScale );
+ m_pScreenshotThumb->SetShouldScaleImage( true );
+ if ( bShowScreenshotThumb )
+ {
+ // We don't want to actually hide it (via SetVisible()), since we need to get mouse click events from the image panel
+ m_pScreenshotThumb->SetImage( szImage );
+ }
+
+ // Setup progress bar & label
+ m_pDownloadProgress->SetWide( GetWide() * .95f );
+ m_pDownloadProgress->SetSegmentInfo( 0, 1 );
+ m_pDownloadProgress->SetBarInset( 2 );
+ m_pDownloadProgress->SetMargin( 2 );
+ m_pDownloadProgress->SetPos(
+ (GetWide() - m_pDownloadProgress->GetWide()) / 2,
+ (m_pScreenshotThumb->GetTall() - m_pDownloadProgress->GetTall()) / 2
+ );
+
+ m_pDownloadLabel->SetParent( m_pDownloadProgress );
+ m_pDownloadLabel->SetWide( m_pDownloadProgress->GetWide() );
+ m_pDownloadLabel->SetPos( 3, (m_pDownloadProgress->GetTall() - m_pDownloadLabel->GetTall()) / 2 );
+
+ m_pErrorLabel->SizeToContents();
+ m_pErrorLabel->SetPos(
+ (GetWide() - m_pErrorLabel->GetWide()) / 2,
+ (m_pScreenshotThumb->GetTall() - m_pErrorLabel->GetTall()) / 2
+ );
+
+ // Center the title control horizontally
+ int nThumbX, nThumbY, nThumbW, nThumbH;
+ UpdateTitleText();
+ m_pScreenshotThumb->GetBounds( nThumbX, nThumbY, nThumbW, nThumbH );
+
+ m_pDownloadOverlay->SetBounds( 0, 0, GetWide(), GetTall() );
+
+ if ( m_pMoviePlayer )
+ {
+ m_pMoviePlayer->SetBounds( nThumbX, nThumbY, nThumbW, nThumbH );
+ }
+
+ // Setup recording-in-progress
+ m_pRecordingInProgressLabel->SetBounds( nThumbX, nThumbY, nThumbW, nThumbH );
+ m_pRecordingInProgressLabel->InvalidateLayout( true, false ); // Need this for centerwrap to work properly
+
+ bool bDownloadPhase = pReplay->m_nStatus == CReplay::REPLAYSTATUS_DOWNLOADPHASE;
+ int nButtonWidth = 9 * GetWide() / 10;
+ int nButtonX = nThumbX + ( m_pScreenshotThumb->GetWide() - nButtonWidth ) / 2;
+ int nDownloadButtonY, nDeleteButtonY;
+ if ( bDownloadPhase )
+ {
+ nDownloadButtonY = nThumbY + 12;
+ nDeleteButtonY = nThumbY + m_pScreenshotThumb->GetTall() - m_pDeleteButton->GetTall() - 12;
+ }
+ else
+ {
+ nDownloadButtonY = 0; // We don't care about this now, since it will not be visible
+ nDeleteButtonY = ( m_pScreenshotThumb->GetTall() - m_pDeleteButton->GetTall() ) / 2;
+ }
+
+ // Adjust download button position
+ m_pDownloadButton->SetPos( nButtonX, nDownloadButtonY );
+ m_pDownloadButton->SetWide( nButtonWidth );
+}
+
+void CReplayBrowserThumbnail::OnDownloadClicked( KeyValues *pParams )
+{
+ // Begin the download
+ OnCommand( "download" );
+}
+
+void CReplayBrowserThumbnail::OnDeleteReplay( KeyValues *pParams )
+{
+ OnCommand( "delete_replayitem" );
+}
+
+void CReplayBrowserThumbnail::OnCommand( const char *pCommand )
+{
+ const CGenericClassBasedReplay *pReplay = GetReplay();
+ AssertValidReadPtr( pReplay );
+ if ( !pReplay )
+ return;
+
+ if ( FStrEq( pCommand, "details" ) ) // Display replay details?
+ {
+ char szCmd[256];
+ V_snprintf( szCmd, sizeof( szCmd ), "details%i", (int)m_hReplayItem );
+ PostActionSignal( new KeyValues( "Command", "command", szCmd ) );
+ }
+ else if ( FStrEq( pCommand, "delete_replayitem" ) ) // Delete the replay?
+ {
+ ReplayUI_GetBrowserPanel()->AttemptToDeleteReplayItem( this, m_hReplayItem, m_pReplayItemManager, -1 );
+ }
+ else
+ {
+ BaseClass::OnCommand( pCommand );
+ }
+}
+
+void CReplayBrowserThumbnail::OnMousePressed( MouseCode code )
+{
+ if ( code == MOUSE_LEFT )
+ {
+ OnCommand( "details" );
+ }
+}
+
+void CReplayBrowserThumbnail::UpdateTitleText()
+{
+ IQueryableReplayItem *pReplayItem = m_pReplayItemManager->GetItem( m_hReplayItem );
+ m_pTitle->SetText( pReplayItem->GetItemTitle() );
+}
+
+CGenericClassBasedReplay *CReplayBrowserThumbnail::GetReplay()
+{
+ IQueryableReplayItem *pItem = m_pReplayItemManager->GetItem( m_hReplayItem );
+ if ( !pItem )
+ return NULL;
+
+ return ToGenericClassBasedReplay( pItem->GetItemReplay() );
+}
+
+IQueryableReplayItem *CReplayBrowserThumbnail::GetReplayItem()
+{
+ return m_pReplayItemManager->GetItem( m_hReplayItem );
+}
+
+//-----------------------------------------------------------------------------
+
+CReplayBrowserThumbnailRow::CReplayBrowserThumbnailRow( Panel *pParent, const char *pName, IReplayItemManager *pReplayItemManager )
+: BaseClass( pParent, pName ),
+ m_pReplayItemManager( pReplayItemManager )
+{
+ SetProportional( true );
+}
+
+void CReplayBrowserThumbnailRow::AddReplayThumbnail( const IQueryableReplayItem *pItem )
+{
+ CReplayBrowserThumbnail *pThumbnail = new CReplayBrowserThumbnail( this, "ListThumbnail", pItem->GetItemHandle(), m_pReplayItemManager );
+ m_vecThumbnails.AddToTail( pThumbnail );
+}
+void CReplayBrowserThumbnailRow::AddReplayThumbnail( QueryableReplayItemHandle_t hReplayItem )
+{
+ CReplayBrowserThumbnail *pThumbnail = new CReplayBrowserThumbnail( this, "ListThumbnail", hReplayItem, m_pReplayItemManager );
+ m_vecThumbnails.AddToTail( pThumbnail );
+}
+
+void CReplayBrowserThumbnailRow::DeleteReplayItemThumbnail( const IQueryableReplayItem *pReplayItem )
+{
+ CReplayBrowserThumbnail *pThumbnail = FindThumbnail( pReplayItem );
+ int nThumbnailElement = m_vecThumbnails.Find( pThumbnail );
+ if ( nThumbnailElement == m_vecThumbnails.InvalidIndex() )
+ {
+ AssertMsg( 0, "REPLAY: Should have found replay thumbnail while attempting to delete it from the browser." );
+ return;
+ }
+
+ // Delete the actual panel
+ ivgui()->RemoveTickSignal( pThumbnail->GetVPanel() );
+ pThumbnail->MarkForDeletion();
+
+ // Remove from list of thumbs
+ m_vecThumbnails.Remove( nThumbnailElement );
+}
+
+int CReplayBrowserThumbnailRow::GetNumVisibleReplayItems() const
+{
+ int iCount = 0;
+ FOR_EACH_VEC( m_vecThumbnails, i )
+ {
+ CReplayBrowserThumbnail *pThumbnail = m_vecThumbnails[ i ];
+ if ( pThumbnail->IsVisible() )
+ {
+ iCount++;
+ }
+ }
+ return iCount;
+}
+
+CReplayBrowserThumbnail *CReplayBrowserThumbnailRow::FindThumbnail( const IQueryableReplayItem *pReplayItem )
+{
+ AssertValidReadPtr( pReplayItem );
+ FOR_EACH_VEC( m_vecThumbnails, i )
+ {
+ CReplayBrowserThumbnail *pThumbnail = m_vecThumbnails[ i ];
+ if ( pThumbnail->GetReplayItem() == pReplayItem )
+ return pThumbnail;
+ }
+ return NULL;
+}
+
+void CReplayBrowserThumbnailRow::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/thumbnailrow.res", "GAME" );
+}
+
+void CReplayBrowserThumbnailRow::PerformLayout()
+{
+ for ( int i = 0; i < m_vecThumbnails.Count(); ++i )
+ {
+ CReplayBrowserThumbnail *pThumbnail = m_vecThumbnails[ i ];
+ pThumbnail->SetPos( i * ( pThumbnail->GetWide() + 2 * REPLAY_BORDER_WIDTH ), 0 );
+ bool bShouldBeVisible = pThumbnail->m_hReplayItem != REPLAY_HANDLE_INVALID;
+ if ( pThumbnail->IsVisible() != bShouldBeVisible )
+ {
+ pThumbnail->SetVisible( bShouldBeVisible );
+ }
+ }
+
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+
+CBaseThumbnailCollection::CBaseThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager )
+: EditablePanel( pParent, pName ),
+ m_pReplayItemManager( pReplayItemManager ),
+ m_nStartX( XRES(15) ),
+ m_pCaratLabel( NULL ),
+ m_pTitleLabel( NULL ),
+ m_pNoReplayItemsLabel( NULL ),
+ m_pRenderAllButton( NULL )
+{
+ m_pParentListPanel = static_cast< CReplayListPanel * >( pParent );
+ m_pShowNextButton = NULL;
+ m_pShowPrevButton = NULL;
+ m_iViewingPage = 0;
+
+ m_nReplayThumbnailsPerRow = 6;
+ m_nMaxRows = 2;
+}
+
+void CBaseThumbnailCollection::AddReplay( const IQueryableReplayItem *pItem )
+{
+ m_vecReplays.AddToTail( pItem->GetItemHandle() );
+}
+
+void CBaseThumbnailCollection::CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem )
+{
+ IQueryableReplayItem *pReplayItem = m_pReplayItemManager->GetItem( hReplayItem ); Assert( pReplayItem );
+ if ( !pReplayItem )
+ return;
+
+ // Find the replay thumbnail
+ CReplayBrowserThumbnailRow *pThumbnailRow = FindReplayItemThumbnailRow( pReplayItem );
+ if ( !pThumbnailRow )
+ {
+ AssertMsg( 0, "REPLAY: Should have found replay thumbnail row while attempting to delete it from the browser." );
+ return;
+ }
+
+ // Remove it from the replay list and refresh the page
+ m_vecReplays.FindAndRemove( hReplayItem );
+ UpdateViewingPage();
+ InvalidateLayout();
+}
+
+int CBaseThumbnailCollection::GetRowStartY()
+{
+ int nVerticalBuffer = YRES( 15 );
+ Panel *pLowestPanel = m_pReplayItemManager->GetItemCount() == 0 ? m_pNoReplayItemsLabel : GetLowestPanel( nVerticalBuffer );
+
+ int x,y;
+ pLowestPanel->GetPos( x,y );
+
+ bool bMakeRoomForNextPrev = (m_pShowPrevButton && m_pShowNextButton && (m_pShowPrevButton->IsVisible() || m_pShowNextButton->IsVisible()));
+ if ( bMakeRoomForNextPrev )
+ {
+ nVerticalBuffer += m_pShowPrevButton->GetTall();
+ }
+ return y + pLowestPanel->GetTall() + nVerticalBuffer;
+}
+
+CReplayBrowserThumbnailRow *CBaseThumbnailCollection::FindReplayItemThumbnailRow( const IQueryableReplayItem *pReplayItem )
+{
+ AssertValidReadPtr( pReplayItem );
+
+ FOR_EACH_VEC( m_vecRows, i )
+ {
+ CReplayBrowserThumbnailRow *pRow = m_vecRows[ i ];
+
+ // If the replay thumbnail exists in the given row, return it
+ if ( pRow->FindThumbnail( pReplayItem ) )
+ return pRow;
+ }
+
+ return NULL;
+}
+
+void CBaseThumbnailCollection::RemoveEmptyRows()
+{
+ // Get a pointer to the row
+ CUtlVector< CReplayBrowserThumbnailRow * > vecRowsToDelete;
+ FOR_EACH_VEC( m_vecRows, i )
+ {
+ CReplayBrowserThumbnailRow *pRow = m_vecRows[ i ];
+ if ( pRow->GetNumVisibleReplayItems() == 0 )
+ {
+ vecRowsToDelete.AddToTail( pRow );
+ }
+ }
+
+ // Delete any rows
+ FOR_EACH_VEC( vecRowsToDelete, i )
+ {
+ // Remove it
+ int nElement = m_vecRows.Find( vecRowsToDelete[ i ] );
+ m_vecRows[ nElement ]->MarkForDeletion();
+ m_vecRows.Remove( nElement );
+ }
+
+ // If we deleted any rows...
+ if ( vecRowsToDelete.Count() )
+ {
+ // Reposition and repaint
+ ReplayUI_GetBrowserPanel()->InvalidateLayout();
+ ReplayUI_GetBrowserPanel()->Repaint();
+
+ // If we don't have any rows left...
+ if ( !m_vecRows.Count() )
+ {
+ m_pParentListPanel->RemoveCollection( this );
+ }
+ }
+}
+
+void CBaseThumbnailCollection::RemoveAll()
+{
+ m_vecReplays.RemoveAll();
+ RemoveEmptyRows();
+ UpdateViewingPage();
+ InvalidateLayout();
+}
+
+
+void CBaseThumbnailCollection::OnUpdated()
+{
+ m_iViewingPage = 0;
+ UpdateViewingPage();
+ InvalidateLayout( true, false );
+}
+
+void CBaseThumbnailCollection::OnCommand( const char *pCommand )
+{
+ if ( FStrEq( pCommand, "render_queued_replays" ) )
+ {
+ ::ReplayUI_ShowRenderDialog( this, REPLAY_HANDLE_INVALID, false, -1 );
+ return;
+ }
+ else if ( FStrEq( pCommand, "show_next" ) )
+ {
+ int iThumbnailsPerPage = (m_nReplayThumbnailsPerRow * m_nMaxRows);
+ m_iViewingPage = clamp( m_iViewingPage + 1, 0, Ceil2Int((float)m_vecReplays.Count() / (float)iThumbnailsPerPage) );
+ UpdateViewingPage();
+ return;
+ }
+ else if ( FStrEq( pCommand, "show_prev" ) )
+ {
+ int iThumbnailsPerPage = (m_nReplayThumbnailsPerRow * m_nMaxRows);
+ m_iViewingPage = clamp( m_iViewingPage - 1, 0, Ceil2Int((float)m_vecReplays.Count() / (float)iThumbnailsPerPage) );
+ UpdateViewingPage();
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+void CBaseThumbnailCollection::UpdateViewingPage( void )
+{
+ int iThumbnailsPerPage = (m_nReplayThumbnailsPerRow * m_nMaxRows);
+ int iFirstReplayOnPage = (m_iViewingPage * iThumbnailsPerPage);
+
+ // If the page we're on is not the first page, and we have no replays on it, move back a page.
+ while (iFirstReplayOnPage >= m_vecReplays.Count() && m_iViewingPage > 0 )
+ {
+ m_iViewingPage--;
+ iFirstReplayOnPage = (m_iViewingPage * iThumbnailsPerPage);
+ }
+
+ for ( int i = 0; i < iThumbnailsPerPage; i++ )
+ {
+ int iReplayIndex = (iFirstReplayOnPage + i);
+
+ int iRow = floor( i / (float)m_nReplayThumbnailsPerRow );
+ int iColumn = i % m_nReplayThumbnailsPerRow;
+
+ // Hit the max number of rows we show?
+ if ( iRow >= m_nMaxRows )
+ break;
+
+ // Need a new row?
+ if ( iRow >= m_vecRows.Count() )
+ {
+ // If we don't actually have any more replays, we don't need to make the new row.
+ if ( iReplayIndex >= m_vecReplays.Count() )
+ break;
+
+ // Create a new row and add there
+ CReplayBrowserThumbnailRow *pNewRow = new CReplayBrowserThumbnailRow( this, "ThumbnailRow", m_pReplayItemManager );
+ m_vecRows.AddToTail( pNewRow );
+ InvalidateLayout();
+ }
+
+ // Need another thumbnail in this row?
+ if ( iColumn >= m_vecRows[iRow]->GetNumReplayItems() )
+ {
+ // Hit the max number of thumbnails in this row?
+ if ( iColumn >= m_nReplayThumbnailsPerRow )
+ break;
+
+ m_vecRows[iRow]->AddReplayThumbnail( REPLAY_HANDLE_INVALID );
+ }
+
+ if ( iReplayIndex >= m_vecReplays.Count() )
+ {
+ m_vecRows[iRow]->m_vecThumbnails[iColumn]->SetReplayItem( REPLAY_HANDLE_INVALID );
+ m_vecRows[iRow]->m_vecThumbnails[iColumn]->SetVisible( false );
+ }
+ else
+ {
+ m_vecRows[iRow]->m_vecThumbnails[iColumn]->SetReplayItem( m_vecReplays[iReplayIndex] );
+ m_vecRows[iRow]->m_vecThumbnails[iColumn]->SetVisible( true );
+ }
+ }
+
+ // Update the button counts
+ wchar_t wszTemp[256];
+ wchar_t wszCount[10];
+ int iNextReplays = clamp( m_vecReplays.Count() - iFirstReplayOnPage - iThumbnailsPerPage, 0, iThumbnailsPerPage );
+ if ( iNextReplays > 0 )
+ {
+ _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", iNextReplays );
+ g_pVGuiLocalize->ConstructString( wszTemp, sizeof( wszTemp ), g_pVGuiLocalize->Find("#Replay_NextX"), 1, wszCount );
+ SetDialogVariable( "nextbuttontext", wszTemp );
+ m_pShowNextButton->SetVisible( true );
+ }
+ else
+ {
+ m_pShowNextButton->SetVisible( false );
+ }
+
+ int iPrevReplays = clamp( iFirstReplayOnPage, 0, iThumbnailsPerPage );
+ if ( iPrevReplays > 0 )
+ {
+ _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", iPrevReplays );
+ g_pVGuiLocalize->ConstructString( wszTemp, sizeof( wszTemp ), g_pVGuiLocalize->Find("#Replay_PrevX"), 1, wszCount );
+ SetDialogVariable( "prevbuttontext", wszTemp );
+ m_pShowPrevButton->SetVisible( true );
+ }
+ else
+ {
+ m_pShowPrevButton->SetVisible( false );
+ }
+
+ RemoveEmptyRows();
+
+ // We may have changed our size, so we need to tell our parent that it should re-layout
+ m_pParentListPanel->InvalidateLayout();
+}
+
+void CBaseThumbnailCollection::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/thumbnailcollection.res", "GAME" );
+
+ m_pCaratLabel = dynamic_cast< CExLabel * >( FindChildByName( "CaratLabel" ) );
+ m_pTitleLabel = dynamic_cast< CExLabel * >( FindChildByName( "TitleLabel" ) );
+ m_pNoReplayItemsLabel = dynamic_cast< CExLabel * >( FindChildByName( "NoReplayItemsLabel" ) );
+ m_pShowNextButton = dynamic_cast< CExButton * >( FindChildByName( "ShowNextButton" ) );
+ if ( m_pShowNextButton )
+ {
+ m_pShowNextButton->AddActionSignalTarget( this );
+ }
+ m_pShowPrevButton = dynamic_cast< CExButton * >( FindChildByName( "ShowPrevButton" ) );
+ if ( m_pShowPrevButton )
+ {
+ m_pShowPrevButton->AddActionSignalTarget( this );
+ }
+
+ UpdateViewingPage();
+}
+
+void CBaseThumbnailCollection::PerformLayout()
+{
+ int nUnconvertedBgWidth = GetWide();
+
+ // Layout no replay items label
+ m_pNoReplayItemsLabel->SetPos( ( GetWide() - m_pNoReplayItemsLabel->GetWide() ) / 2, YRES( 40 ) );
+ m_pNoReplayItemsLabel->SetVisible( !m_pReplayItemManager->GetItemCount() );
+ m_pNoReplayItemsLabel->SetContentAlignment( Label::a_center );
+
+ int nStartY = YRES(5);
+
+ // Update the title count
+ wchar_t wszCount[10];
+ _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", m_vecReplays.Count() );
+ wchar_t wszTemp[256];
+ const char *pszLocString = IsMovieCollection() ? "#Replay_SavedMovies" : "#Replay_UnrenderedReplays";
+ g_pVGuiLocalize->ConstructString( wszTemp, sizeof( wszTemp ), g_pVGuiLocalize->Find(pszLocString), 1, wszCount );
+ SetDialogVariable( "titleandcount", wszTemp );
+
+ // Setup labels for unconverted replay display
+ LayoutUpperPanels( nStartY, nUnconvertedBgWidth );
+
+ int nCurrentY = GetRowStartY();
+
+ // Position our prev button
+ int nButtonX = (GetWide() - m_pShowPrevButton->GetWide()) * 0.5;
+ if ( m_pShowPrevButton )
+ {
+ m_pShowPrevButton->SetPos( nButtonX, nCurrentY - m_pShowPrevButton->GetTall() - YRES(2) );
+ nCurrentY += YRES(2);
+ }
+
+ // Setup converted row positions
+ for ( int i = 0; i < m_vecRows.Count(); ++i )
+ {
+ CReplayBrowserThumbnailRow *pRow = m_vecRows[ i ];
+ pRow->SetPos( m_nStartX, nCurrentY );
+ pRow->InvalidateLayout( true, true );
+ int nRowTall = pRow->GetTall();
+ nCurrentY += nRowTall;
+ }
+
+ int nTotalHeight = nCurrentY;
+
+ // Position our next button
+ if ( m_pShowNextButton )
+ {
+ m_pShowNextButton->SetPos( nButtonX, nCurrentY );
+
+ bool bMakeRoomForNextPrev = (m_pShowPrevButton && m_pShowNextButton && (m_pShowPrevButton->IsVisible() || m_pShowNextButton->IsVisible()));
+ if ( bMakeRoomForNextPrev )
+ {
+ nTotalHeight += m_pShowNextButton->GetTall() + YRES(5);
+ }
+ }
+
+ LayoutBackgroundPanel( nUnconvertedBgWidth, nTotalHeight );
+
+ // TODO: Resizing can cause an InvalidateLayout() call if the new & old dimensions differ,
+ // perhaps calling this here is not the best idea.
+ // Adjust total height of panel
+ SetTall( nTotalHeight );
+
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+
+CReplayThumbnailCollection::CReplayThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager )
+: BaseClass( pParent, pName, pReplayItemManager ),
+ m_pWarningLabel( NULL ),
+ m_pUnconvertedBg( NULL )
+{
+ // Create additional panels for unconverted rows
+ m_pLinePanel = new Panel( this, "Line" );
+ m_pWarningLabel = new CExLabel( this, "WarningLabel", "#Replay_ConversionWarning" );
+ m_pUnconvertedBg = new Panel( this, "UnconvertedBg" );
+ m_pRenderAllButton = new CExButton( this, "RenderAllButton", "#Replay_RenderAll" );
+}
+
+bool CReplayThumbnailCollection::IsMovieCollection() const
+{
+ return false;
+}
+
+void CReplayThumbnailCollection::PerformLayout()
+{
+ BaseClass::PerformLayout();
+}
+
+void CReplayThumbnailCollection::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+#if defined( TF_CLIENT_DLL )
+ if ( m_pUnconvertedBg )
+ {
+ vgui::IBorder *pBorder = pScheme->GetBorder( "ButtonBorder" );
+ m_pUnconvertedBg->SetBorder( pBorder );
+ SetPaintBorderEnabled( true );
+ }
+#else
+ SetPaintBorderEnabled( false );
+#endif
+
+ // Get current key binding for "save_replay", if any.
+ const char *pBoundKey = engine->Key_LookupBinding( "save_replay" );
+ if ( !pBoundKey || FStrEq( pBoundKey, "(null)" ) )
+ {
+ m_pNoReplayItemsLabel->SetText( "#Replay_NoKeyBoundNoReplays" );
+ }
+ else
+ {
+ char szKey[16];
+ Q_snprintf( szKey, sizeof(szKey), "%s", pBoundKey );
+
+ wchar_t wKey[16];
+ wchar_t wLabel[256];
+
+ g_pVGuiLocalize->ConvertANSIToUnicode( szKey, wKey, sizeof( wKey ) );
+ g_pVGuiLocalize->ConstructString( wLabel, sizeof( wLabel ), g_pVGuiLocalize->Find("#Replay_NoReplays" ), 1, wKey );
+
+ m_pNoReplayItemsLabel->SetText( wLabel );
+ }
+}
+
+void CReplayThumbnailCollection::LayoutUpperPanels( int nStartY, int nBgWidth )
+{
+ int nUnconvertedY = nStartY + 2 * REPLAY_BUFFER_HEIGHT;
+ if ( !m_pTitleLabel )
+ return;
+
+ m_pTitleLabel->SizeToContents();
+ m_pTitleLabel->SetBounds( m_nStartX, nUnconvertedY, GetWide(), m_pTitleLabel->GetTall() );
+ m_pTitleLabel->SetVisible( true );
+
+ int cx, cy;
+ int nWarningStartY = nUnconvertedY + m_pTitleLabel->GetTall() + REPLAY_BUFFER_HEIGHT;
+ m_pWarningLabel->GetContentSize( cx, cy );
+ m_pWarningLabel->SetBounds( m_nStartX, nWarningStartY, 2 * GetWide() / 3, cy ); // For when "convert all" button is shown
+ m_pWarningLabel->SetVisible( m_pReplayItemManager->GetItemCount() > 0 );
+
+ // Setup carat label
+ if ( m_pCaratLabel )
+ {
+ m_pCaratLabel->SizeToContents();
+ m_pCaratLabel->SetPos( m_nStartX - m_pCaratLabel->GetWide() - XRES(3), nUnconvertedY );
+ m_pCaratLabel->SetVisible( true );
+ }
+
+ // Setup line
+ int nLineBuffer = m_pReplayItemManager->GetItemCount() > 0 ? YRES( 15 ) : nStartY;
+ int nLineY = nWarningStartY + nLineBuffer;
+ m_pLinePanel->SetBounds( 0, nLineY, nBgWidth, 1 );
+ m_pLinePanel->SetVisible( true );
+
+ int nButtonX = (GetWide() - m_pShowPrevButton->GetWide()) * 0.5;
+ if ( m_pShowPrevButton )
+ {
+ m_pShowPrevButton->SetPos( nButtonX, nLineY );
+ }
+}
+
+void CReplayThumbnailCollection::LayoutBackgroundPanel( int nWide, int nTall )
+{
+ // Setup bounds for dark background, if there are unconverted replays in this collection
+ if ( m_pUnconvertedBg )
+ {
+ m_pUnconvertedBg->SetBounds(
+ 0,
+ 0,
+ nWide,
+ nTall
+ );
+ m_pUnconvertedBg->SetVisible( true );
+
+ // Setup convert all button
+ int nWarningLabelX, nWarningLabelY;
+ m_pWarningLabel->GetPos( nWarningLabelX, nWarningLabelY );
+ int nRenderAllX = m_pUnconvertedBg->GetWide() - m_pRenderAllButton->GetWide() - XRES( 5 );
+ m_pRenderAllButton->SetPos( nRenderAllX, nWarningLabelY - m_pRenderAllButton->GetTall()/2 );
+ m_pRenderAllButton->SetVisible( m_pReplayItemManager->GetItemCount() > 0 );
+ }
+}
+
+Panel *CReplayThumbnailCollection::GetLowestPanel( int &nVerticalBuffer )
+{
+ nVerticalBuffer = YRES( 8 );
+ return m_pLinePanel;
+}
+
+//-----------------------------------------------------------------------------
+
+CMovieThumbnailCollection::CMovieThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager,
+ int nDay, int nMonth, int nYear, bool bShowSavedMoviesLabel )
+: BaseClass( pParent, pName, pReplayItemManager )
+{
+ Init( nDay, nMonth, nYear, bShowSavedMoviesLabel );
+}
+
+CMovieThumbnailCollection::CMovieThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager, bool bShowSavedMoviesLabel )
+: BaseClass( pParent, pName, pReplayItemManager )
+{
+ Init( -1, -1, -1, bShowSavedMoviesLabel );
+}
+
+void CMovieThumbnailCollection::Init( int nDay, int nMonth, int nYear, bool bShowSavedMoviesLabel )
+{
+ m_nDay = nDay;
+ m_nMonth = nMonth;
+ m_nYear = nYear;
+
+ if ( m_nDay == OLDER_MOVIES_COLLECTION )
+ {
+ m_pDateLabel = new CExLabel( this, "DateLabel", "#Replay_OlderMovies" );
+ }
+ else
+ {
+ m_pDateLabel = m_nDay >= 0 ? new CExLabel( this, "DateLabel", CReplayTime::GetLocalizedDate( g_pVGuiLocalize, nDay, nMonth, nYear ) ) : NULL;
+ }
+ m_bShowSavedMoviesLabel = bShowSavedMoviesLabel;
+}
+
+bool CMovieThumbnailCollection::IsMovieCollection() const
+{
+ return true;
+}
+
+void CMovieThumbnailCollection::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // Movies have multiple collections under a single header. So we use the total movies, not the amount in this collection.
+ if ( g_pReplayMovieManager )
+ {
+ wchar_t wszCount[10];
+ _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", g_pReplayMovieManager->GetMovieCount() );
+ wchar_t wszTemp[256];
+ g_pVGuiLocalize->ConstructString( wszTemp, sizeof( wszTemp ), g_pVGuiLocalize->Find("#Replay_SavedMovies"), 1, wszCount );
+ SetDialogVariable( "titleandcount", wszTemp );
+ }
+
+ if ( m_pDateLabel )
+ {
+ m_pDateLabel->SetVisible( m_pReplayItemManager->GetItemCount() );
+ }
+}
+
+void CMovieThumbnailCollection::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ if ( m_pDateLabel )
+ {
+ m_pDateLabel->SetVisible( true );
+ }
+
+ if ( m_pCaratLabel )
+ {
+ m_pCaratLabel->SetVisible( m_bShowSavedMoviesLabel );
+ }
+ if ( m_pTitleLabel )
+ {
+ m_pTitleLabel->SetVisible( m_bShowSavedMoviesLabel );
+ }
+
+ m_pNoReplayItemsLabel->SetText( "#Replay_NoMovies" );
+}
+
+void CMovieThumbnailCollection::LayoutUpperPanels( int nStartY, int nBgWidth )
+{
+ if ( m_pTitleLabel && m_pTitleLabel->IsVisible() )
+ {
+ m_pTitleLabel->SizeToContents();
+ m_pTitleLabel->SetPos( m_nStartX, nStartY );
+
+ m_pCaratLabel->SizeToContents();
+ m_pCaratLabel->SetPos( m_nStartX - m_pCaratLabel->GetWide() - XRES(3), nStartY + ( m_pCaratLabel->GetTall() - m_pCaratLabel->GetTall() ) / 2 );
+
+ nStartY += m_pTitleLabel->GetTall() + YRES( 5 );
+ }
+
+ // Date label
+ if ( m_pDateLabel && m_pDateLabel->IsVisible() )
+ {
+ m_pDateLabel->SizeToContents();
+ m_pDateLabel->SetPos( m_nStartX, nStartY );
+ }
+}
+
+Panel *CMovieThumbnailCollection::GetLowestPanel( int &nVerticalBuffer )
+{
+ nVerticalBuffer = YRES( 8 );
+ return m_pDateLabel ? m_pDateLabel : m_pTitleLabel;
+}
+
+bool CMovieThumbnailCollection::DoesDateMatch( int nDay, int nMonth, int nYear )
+{
+ return ( nDay == m_nDay ) && ( nMonth == m_nMonth ) && ( nYear == m_nYear );
+}
+
+#endif
diff --git a/sp/src/game/client/replay/vgui/replaybrowserlistitempanel.h b/sp/src/game/client/replay/vgui/replaybrowserlistitempanel.h
new file mode 100644
index 00000000..ad80985f
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserlistitempanel.h
@@ -0,0 +1,239 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#ifndef REPLAYLISTITEMPANEL_H
+#define REPLAYLISTITEMPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "replaybrowserbasepanel.h"
+#include "replaybrowseritemmanager.h"
+#include "replay/genericclassbased_replay.h"
+#include "game_controls/slideshowpanel.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Slideshow panel that adds all screenshots associated
+// with a given replay.
+//-----------------------------------------------------------------------------
+class CReplayScreenshotSlideshowPanel : public CSlideshowPanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayScreenshotSlideshowPanel, CSlideshowPanel );
+public:
+ CReplayScreenshotSlideshowPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay );
+
+ virtual void PerformLayout();
+
+private:
+ ReplayHandle_t m_hReplay;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: An individual Replay thumbnail, with download button, title, etc.
+//-----------------------------------------------------------------------------
+class CExButton;
+class CExLabel;
+class IReplayItemManager;
+class CMoviePlayerPanel;
+
+class CReplayBrowserThumbnail : public CReplayBasePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayBrowserThumbnail, CReplayBasePanel );
+public:
+ CReplayBrowserThumbnail( Panel *pParent, const char *pName, QueryableReplayItemHandle_t hReplayItem, IReplayItemManager *pReplayItemManager );
+ ~CReplayBrowserThumbnail();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+ virtual void OnMousePressed( MouseCode code );
+
+ virtual void OnTick();
+
+ virtual void OnCommand( const char *pCommand );
+
+ void UpdateTitleText();
+
+ void SetReplayItem( QueryableReplayItemHandle_t hReplayItem );
+
+ CGenericClassBasedReplay *GetReplay();
+ IQueryableReplayItem *GetReplayItem();
+
+ MESSAGE_FUNC_PARAMS( OnDownloadClicked, "Download", pParams );
+ MESSAGE_FUNC_PARAMS( OnDeleteReplay, "delete_replayitem", pParams );
+
+ CCrossfadableImagePanel *m_pScreenshotThumb;
+ QueryableReplayItemHandle_t m_hReplayItem;
+
+private:
+ void SetupReplayItemUserData( void *pUserData );
+ void UpdateProgress( bool bDownloadPhase, const CReplay *pReplay );
+
+ Label *m_pTitle;
+ Label *m_pDownloadLabel;
+ Label *m_pRecordingInProgressLabel;
+ ProgressBar *m_pDownloadProgress;
+ CExButton *m_pDownloadButton;
+ CExButton *m_pDeleteButton;
+ Label *m_pErrorLabel;
+ CMoviePlayerPanel *m_pMoviePlayer;
+ Panel *m_pDownloadOverlay;
+ EditablePanel *m_pBorderPanel;
+ Color m_clrHighlight;
+ Color m_clrDefaultBg;
+ bool m_bMouseOver;
+ IReplayItemManager *m_pReplayItemManager;
+ float m_flLastMovieScrubTime;
+ float m_flHoverStartTime;
+ float m_flLastProgressChangeTime;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: A row of Replay thumbnails (CReplayBrowserThumbnail's)
+//-----------------------------------------------------------------------------
+class CReplayBrowserThumbnailRow : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayBrowserThumbnailRow, EditablePanel );
+public:
+ CReplayBrowserThumbnailRow( Panel *pParent, const char *pName, IReplayItemManager *pReplayItemManager );
+
+ void AddReplayThumbnail( const IQueryableReplayItem *pReplay );
+ void AddReplayThumbnail( QueryableReplayItemHandle_t hReplayItem );
+ void DeleteReplayItemThumbnail( const IQueryableReplayItem *pReplayItem );
+ int GetNumReplayItems() const { return m_vecThumbnails.Count(); }
+ int GetNumVisibleReplayItems() const;
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ CReplayBrowserThumbnail *FindThumbnail( const IQueryableReplayItem *pReplay );
+
+ CUtlVector< CReplayBrowserThumbnail * > m_vecThumbnails;
+ IReplayItemManager *m_pReplayItemManager;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: A collection of CReplayBrowserThumbnailRows containing replays
+// recorded on a given day.
+//-----------------------------------------------------------------------------
+class CExLabel;
+class CExButton;
+class CReplayListPanel;
+
+class CBaseThumbnailCollection : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CBaseThumbnailCollection, EditablePanel );
+public:
+ CBaseThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager );
+
+ void AddReplay( const IQueryableReplayItem *pItem );
+
+ virtual bool IsMovieCollection() const = 0;
+
+ void CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem );
+
+ virtual void PerformLayout();
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+
+ void RemoveEmptyRows();
+ void RemoveAll();
+
+ void OnUpdated();
+
+ void OnCommand( const char *pCommand );
+
+ CReplayBrowserThumbnailRow *FindReplayItemThumbnailRow( const IQueryableReplayItem *pReplayItem );
+
+ inline int GetNumRows() const { return m_vecRows.Count(); }
+
+ typedef CUtlVector< CReplayBrowserThumbnailRow * > RowContainer_t;
+ RowContainer_t m_vecRows;
+
+protected:
+ // Called from PerformLayout() - layout any panels that should appear at the top (vertically)-most position
+ virtual void LayoutUpperPanels( int nStartY, int nBgWidth ) = 0;
+ virtual void LayoutBackgroundPanel( int nWide, int nTall ) {}
+ virtual Panel *GetLowestPanel( int &nVerticalBuffer ) = 0;
+
+ void UpdateViewingPage( void );
+
+ int m_nStartX;
+
+protected:
+ CExLabel *m_pNoReplayItemsLabel;
+ IReplayItemManager *m_pReplayItemManager;
+
+ CExButton *m_pShowNextButton;
+ CExButton *m_pShowPrevButton;
+ CUtlVector<ReplayItemHandle_t> m_vecReplays;
+ int m_iViewingPage;
+
+ int m_nReplayThumbnailsPerRow;
+ int m_nMaxRows;
+
+ CExLabel *m_pCaratLabel;
+ CExLabel *m_pTitleLabel;
+ CExButton *m_pRenderAllButton;
+
+private:
+ int GetRowStartY();
+
+ CReplayListPanel *m_pParentListPanel; // Parent gets altered so we keep this cached ptr around
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CReplayThumbnailCollection : public CBaseThumbnailCollection
+{
+ DECLARE_CLASS_SIMPLE( CReplayThumbnailCollection, CBaseThumbnailCollection );
+public:
+ CReplayThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager );
+
+ virtual bool IsMovieCollection() const;
+
+ virtual void PerformLayout();
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+
+ virtual void LayoutUpperPanels( int nStartY, int nBgWidth );
+ virtual void LayoutBackgroundPanel( int nWide, int nTall );
+ virtual Panel *GetLowestPanel( int &nVerticalBuffer );
+
+ Panel *m_pLinePanel;
+ CExLabel *m_pWarningLabel;
+ Panel *m_pUnconvertedBg;
+};
+
+#define OLDER_MOVIES_COLLECTION -2
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CMovieThumbnailCollection : public CBaseThumbnailCollection
+{
+ DECLARE_CLASS_SIMPLE( CMovieThumbnailCollection, CBaseThumbnailCollection );
+public:
+ CMovieThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager,
+ int nDay, int nMonth, int nYear, bool bShowSavedMoviesLabel );
+ CMovieThumbnailCollection( CReplayListPanel *pParent, const char *pName, IReplayItemManager *pReplayItemManager,
+ bool bShowSavedMoviesLabel );
+
+ bool DoesDateMatch( int nDay, int nMonth, int nYear );
+ virtual bool IsMovieCollection() const;
+
+private:
+ void Init( int nDay, int nMonth, int nYear, bool bShowSavedMoviesLabel );
+ virtual void PerformLayout();
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+
+ Panel *GetLowestPanel( int &nVerticalBuffer );
+ void LayoutUpperPanels( int nStartY, int nBgWidth );
+
+ int m_nDay, m_nMonth, m_nYear;
+ CExLabel *m_pDateLabel;
+ bool m_bShowSavedMoviesLabel;
+};
+
+#endif // REPLAYLISTITEMPANEL_H
diff --git a/sp/src/game/client/replay/vgui/replaybrowserlistpanel.cpp b/sp/src/game/client/replay/vgui/replaybrowserlistpanel.cpp
new file mode 100644
index 00000000..c1d34f6d
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserlistpanel.cpp
@@ -0,0 +1,484 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowserlistpanel.h"
+#include "ienginevgui.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "vgui/IVGui.h"
+#include "vgui_controls/ScrollBar.h"
+#include "vgui_controls/ScrollBarSlider.h"
+#include "replaybrowserlistitempanel.h"
+#include "replaybrowserpreviewpanel.h"
+#include "replaybrowserbasepage.h"
+#include "replay/ireplaymoviemanager.h"
+#include "replay/ireplaymanager.h"
+#include "replaybrowsermainpanel.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+//-----------------------------------------------------------------------------
+
+extern IClientReplayContext *g_pClientReplayContext;
+extern IReplayMovieManager *g_pReplayMovieManager;
+extern const char *GetMapDisplayName( const char *mapName );
+
+//-----------------------------------------------------------------------------
+
+DECLARE_BUILD_FACTORY( CReplayListPanel );
+
+//-----------------------------------------------------------------------------
+
+#define MAX_MOVIE_THUMBNAILS 12 // The remaining movies will be put into a single collection
+
+//-----------------------------------------------------------------------------
+
+CReplayListPanel::CReplayListPanel( Panel *pParent, const char *pName )
+: BaseClass( pParent, pName ),
+ m_pPrevHoverPanel( NULL ),
+ m_pPreviewPanel( NULL )
+{
+ ivgui()->AddTickSignal( GetVPanel(), 10 );
+
+ m_pBorderArrowImg = new ImagePanel( this, "ArrowImage" );
+
+ // Add replays and movies collections, which will contain all replays & movies.
+ m_pReplaysCollection = new CReplayThumbnailCollection( this, "ReplayThumbnailCollection", GetReplayItemManager() );
+ m_pMoviesCollection = new CMovieThumbnailCollection( this, "MovieThumbnailCollection", GetReplayMovieItemManager(), true );
+
+ m_vecCollections.AddToTail( m_pReplaysCollection );
+ m_vecCollections.AddToTail( m_pMoviesCollection );
+
+ m_wszFilter[0] = L' ';
+ m_wszFilter[1] = NULL;
+}
+
+CReplayListPanel::~CReplayListPanel()
+{
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CReplayListPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/replaylistpanel.res", "GAME" );
+
+#if !defined( TF_CLIENT_DLL )
+ SetPaintBorderEnabled( false );
+#endif
+
+ MoveScrollBarToTop();
+
+ vgui::ScrollBar *pScrollBar = dynamic_cast< vgui::ScrollBar * >( FindChildByName( "PanelListPanelVScroll" ) );
+ pScrollBar->SetScrollbarButtonsVisible( false );
+ Color clrButtonColor = GetSchemeColor( "Yellow", Color( 255, 255, 255, 255 ), pScheme );
+ Color clrBgColor = GetSchemeColor( "TanDark", Color( 255, 255, 255, 255 ), pScheme );
+ const int nWidth = XRES( 5 );
+ pScrollBar->SetSize( nWidth, GetTall() );
+ pScrollBar->GetSlider()->SetSize( nWidth, GetTall() );
+}
+
+void CReplayListPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+}
+
+void CReplayListPanel::OnMouseWheeled(int delta)
+{
+ if ( !GetScrollbar()->IsVisible() )
+ return;
+
+ BaseClass::OnMouseWheeled( delta );
+}
+
+void CReplayListPanel::SetupBorderArrow( bool bLeft )
+{
+ m_pBorderArrowImg->SetVisible( true );
+
+ m_pBorderArrowImg->SetImage( bLeft ? "replay/replay_balloon_arrow_left" : "replay/replay_balloon_arrow_right" );
+ m_pBorderArrowImg->SetZPos( 1000 );
+
+ m_pBorderArrowImg->GetImage()->GetContentSize( m_aBorderArrowDims[0], m_aBorderArrowDims[1] );
+ m_pBorderArrowImg->SetSize( m_aBorderArrowDims[0], m_aBorderArrowDims[1] );
+}
+
+void CReplayListPanel::ClearPreviewPanel()
+{
+ if ( m_pPreviewPanel )
+ {
+ m_pPreviewPanel->MarkForDeletion();
+ m_pPreviewPanel = NULL;
+ }
+}
+
+void CReplayListPanel::ApplyFilter( const wchar_t *pFilterText )
+{
+ Q_wcsncpy( m_wszFilter, pFilterText, sizeof( m_wszFilter ) );
+ V_wcslower( m_wszFilter );
+
+ m_pReplaysCollection->RemoveAll();
+ m_pMoviesCollection->RemoveAll();
+ m_pPrevHoverPanel = NULL;
+ ClearPreviewPanel();
+
+ RemoveAll();
+ AddReplaysToList();
+
+ FOR_EACH_VEC( m_vecCollections, i )
+ {
+ m_vecCollections[i]->OnUpdated();
+ }
+
+ InvalidateLayout();
+}
+
+void CReplayListPanel::OnTick()
+{
+ if ( !enginevgui->IsGameUIVisible() )
+ return;
+
+ CReplayBrowserPanel *pReplayBrowser = ReplayUI_GetBrowserPanel();
+ if ( !pReplayBrowser || !pReplayBrowser->IsVisible() )
+ return;
+
+ int x,y;
+ vgui::input()->GetCursorPos(x, y);
+
+ // If the deletion confirmation dialog is up
+ if ( vgui::input()->GetAppModalSurface() )
+ {
+ ClearPreviewPanel();
+
+ // Hide the preview arrow
+ m_pBorderArrowImg->SetVisible( false );
+
+ return;
+ }
+
+ CReplayBrowserThumbnail *pOverPanel = FindThumbnailAtCursor( x, y );
+ if ( m_pPrevHoverPanel != pOverPanel )
+ {
+ if ( m_pPrevHoverPanel )
+ {
+ OnItemPanelExited( m_pPrevHoverPanel );
+ }
+
+ m_pPrevHoverPanel = pOverPanel;
+
+ if ( m_pPrevHoverPanel )
+ {
+ OnItemPanelEntered( m_pPrevHoverPanel );
+ }
+ }
+}
+
+void CReplayListPanel::OnItemPanelEntered( vgui::Panel *pPanel )
+{
+ CReplayBrowserThumbnail *pThumbnail = dynamic_cast< CReplayBrowserThumbnail * >( pPanel );
+
+ if ( IsVisible() && pThumbnail && pThumbnail->IsVisible() )
+ {
+ ClearPreviewPanel();
+
+ // Determine which type of preview panel to display
+ IReplayItemManager *pItemManager;
+ IQueryableReplayItem *pReplayItem = FindReplayItem( pThumbnail->m_hReplayItem, &pItemManager );
+
+ AssertMsg( pReplayItem, "Why is this happening?" );
+ if ( !pReplayItem )
+ return;
+
+ if ( pReplayItem->IsItemAMovie() )
+ {
+ m_pPreviewPanel = new CReplayPreviewPanelBase( this, pReplayItem->GetItemHandle(), pItemManager );
+ }
+ else
+ {
+ m_pPreviewPanel = new CReplayPreviewPanelSlideshow( this, pReplayItem->GetItemHandle(), pItemManager );
+ }
+
+ m_pPreviewPanel->InvalidateLayout( true, true );
+
+ int x,y;
+ pThumbnail->GetPosRelativeToAncestor( this, x, y );
+
+ int nXPos, nYPos;
+ int nOffset = XRES( 1 );
+ nXPos = ( x > GetWide()/2 ) ? ( x - m_pPreviewPanel->GetWide() - nOffset ) : ( x + pThumbnail->GetWide() + nOffset );
+ nYPos = y + ( pThumbnail->GetTall() - m_pPreviewPanel->GetTall() ) / 2;
+
+ // Make sure the popup stays onscreen.
+ if ( nXPos < 0 )
+ {
+ nXPos = 0;
+ }
+ else if ( (nXPos + m_pPreviewPanel->GetWide()) > GetWide() )
+ {
+ nXPos = GetWide() - m_pPreviewPanel->GetWide();
+ }
+
+ if ( nYPos < 0 )
+ {
+ nYPos = 0;
+ }
+ else if ( (nYPos + m_pPreviewPanel->GetTall()) > GetTall() )
+ {
+ // Move it up as much as we can without it going below the bottom
+ nYPos = GetTall() - m_pPreviewPanel->GetTall();
+ }
+
+ // Setup the balloon's arrow
+ bool bLeftArrow = x < (GetWide() / 2);
+ SetupBorderArrow( bLeftArrow ); // Sets proper image and caches image dims in m_aBorderArrowDims
+ int nArrowXPos, nArrowYPos;
+ const int nPreviewBorderWidth = 2; // Should be just big enough to cover the preview's border width
+ if ( bLeftArrow )
+ {
+ // Setup the arrow along the left-hand side
+ nArrowXPos = nXPos - m_aBorderArrowDims[0] + nPreviewBorderWidth;
+ }
+ else
+ {
+ nArrowXPos = nXPos + m_pPreviewPanel->GetWide() - nPreviewBorderWidth;
+ }
+ nArrowYPos = MIN( nYPos + m_pPreviewPanel->GetTall() - m_pBorderArrowImg->GetTall() * 2, y + ( pThumbnail->m_pScreenshotThumb->GetTall() - m_aBorderArrowDims[1] ) / 2 );
+ m_pBorderArrowImg->SetPos( nArrowXPos, nArrowYPos );
+
+ m_pPreviewPanel->SetPos( nXPos, nYPos );
+ m_pPreviewPanel->SetVisible( true );
+
+ surface()->PlaySound( "replay\\replaypreviewpopup.wav" );
+ }
+}
+
+void CReplayListPanel::OnItemPanelExited( vgui::Panel *pPanel )
+{
+ CReplayBrowserThumbnail *pThumbnail = dynamic_cast < CReplayBrowserThumbnail * > ( pPanel );
+
+ if ( pThumbnail && IsVisible() && m_pPreviewPanel )
+ {
+ m_pBorderArrowImg->SetVisible( false );
+ ClearPreviewPanel();
+ }
+}
+
+CBaseThumbnailCollection *CReplayListPanel::FindOrAddReplayThumbnailCollection( const IQueryableReplayItem *pItem, IReplayItemManager *pItemManager )
+{
+ Assert( pItem );
+
+ if ( pItem->IsItemAMovie() )
+ {
+ return m_pMoviesCollection;
+ }
+
+ return m_pReplaysCollection;
+}
+
+void CReplayListPanel::AddReplaysToList()
+{
+ // Cache off list item pointers into a temp list for processing
+ CUtlLinkedList< IQueryableReplayItem *, int > lstMovies;
+ CUtlLinkedList< IQueryableReplayItem *, int > lstReplays;
+
+ // Add all replays to a replays list
+ g_pReplayManager->GetReplaysAsQueryableItems( lstReplays );
+
+ // Add all movies to a movies list
+ g_pReplayMovieManager->GetMoviesAsQueryableItems( lstMovies );
+
+ // Go through all movies, and add them to the proper collection, based on date
+ FOR_EACH_LL( lstMovies, i )
+ {
+ if ( PassesFilter( lstMovies[ i ] ) )
+ {
+ AddReplayItem( lstMovies[ i ]->GetItemHandle() );
+ }
+ }
+
+ // Add any replays to the "temporary replays" collection
+ FOR_EACH_LL( lstReplays, i )
+ {
+ if ( PassesFilter( lstReplays[ i ] ) )
+ {
+ m_pReplaysCollection->AddReplay( lstReplays[ i ] );
+ }
+ }
+
+ // Add all collection panels to the list panel
+ FOR_EACH_VEC( m_vecCollections, i )
+ {
+ AddItem( NULL, m_vecCollections[ i ] );
+ }
+}
+
+void CReplayListPanel::RemoveCollection( CBaseThumbnailCollection *pCollection )
+{
+ // Never remove our two base collections. If they have no entries, they display messages instead.
+ if ( pCollection == m_pMoviesCollection || pCollection == m_pReplaysCollection )
+ return;
+
+ // Find the item and remove it
+ int i = FirstItem();
+ while ( i != InvalidItemID() )
+ {
+ if ( GetItemPanel( i ) == pCollection )
+ {
+ int nNextI = NextItem( i );
+ RemoveItem( i );
+ i = nNextI;
+ }
+ else
+ {
+ i = NextItem( i );
+ }
+ }
+
+ // Remove our own cached ptr
+ i = m_vecCollections.Find( pCollection );
+ if ( i != m_vecCollections.InvalidIndex() )
+ {
+ m_vecCollections.Remove( i );
+ }
+}
+
+CReplayBrowserThumbnail *CReplayListPanel::FindThumbnailAtCursor( int x, int y )
+{
+ // Is the cursor hovering over any of the thumbnails?
+ FOR_EACH_VEC( m_vecCollections, i )
+ {
+ CBaseThumbnailCollection *pCollection = m_vecCollections[ i ];
+ if ( pCollection->IsWithin( x, y ) )
+ {
+ FOR_EACH_VEC( pCollection->m_vecRows, j )
+ {
+ CReplayBrowserThumbnailRow *pRow = pCollection->m_vecRows[ j ];
+ if ( pRow->IsWithin( x, y ) )
+ {
+ FOR_EACH_VEC( pRow->m_vecThumbnails, k )
+ {
+ CReplayBrowserThumbnail *pThumbnail = pRow->m_vecThumbnails[ k ];
+ if ( pThumbnail->IsWithin( x, y ) )
+ {
+ return pThumbnail;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#if defined( WIN32 )
+ #define Q_wcstok( text, delimiters, context ) wcstok( text, delimiters ); context;
+#elif defined( OSX ) || defined( LINUX )
+ #define Q_wcstok( text, delimiters, context ) wcstok( text, delimiters, context )
+#endif
+
+bool CReplayListPanel::PassesFilter( IQueryableReplayItem *pItem )
+{
+ CGenericClassBasedReplay *pReplay = ToGenericClassBasedReplay( pItem->GetItemReplay() );
+ if ( !pReplay )
+ return false;
+
+ wchar_t wszSearchableText[1024] = L"";
+ wchar_t wszTemp[256];
+ // title
+ const wchar_t *pTitle = pItem->GetItemTitle();
+ V_wcscat_safe( wszSearchableText, pTitle );
+ V_wcscat_safe( wszSearchableText, L" " );
+ // map
+ const char *pMapName = GetMapDisplayName( pReplay->m_szMapName );
+ g_pVGuiLocalize->ConvertANSIToUnicode( pMapName, wszTemp, sizeof( wszTemp ) );
+ V_wcscat_safe( wszSearchableText, wszTemp );
+ V_wcscat_safe( wszSearchableText, L" " );
+ // player class
+ g_pVGuiLocalize->ConvertANSIToUnicode( pReplay->GetPlayerClass(), wszTemp, sizeof( wszTemp ) );
+ V_wcscat_safe( wszSearchableText, wszTemp );
+ V_wcscat_safe( wszSearchableText, L" " );
+ // killer name
+ if ( pReplay->WasKilled() )
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( pReplay->GetKillerName(), wszTemp, sizeof( wszTemp ) );
+ V_wcscat_safe( wszSearchableText, wszTemp );
+ V_wcscat_safe( wszSearchableText, L" " );
+ }
+ // lower case
+ V_wcslower( wszSearchableText );
+
+ wchar_t wszFilter[256];
+ Q_wcsncpy( wszFilter, m_wszFilter, sizeof( wszFilter ) );
+
+ bool bPasses = true;
+ wchar_t seps[] = L" ";
+ wchar_t *last = NULL;
+ wchar_t *token = Q_wcstok( wszFilter, seps, &last );
+ while ( token && bPasses )
+ {
+ bPasses &= wcsstr( wszSearchableText, token ) != NULL;
+ token = Q_wcstok( NULL, seps, &last );
+ }
+
+ return bPasses;
+}
+
+void CReplayListPanel::AddReplayItem( ReplayItemHandle_t hItem )
+{
+ IReplayItemManager *pItemManager;
+ const IQueryableReplayItem *pItem = FindReplayItem( hItem, &pItemManager );
+ if ( !pItem )
+ return;
+
+ // Find or add the collection
+ CBaseThumbnailCollection *pCollection = FindOrAddReplayThumbnailCollection( pItem, pItemManager );
+
+ // Add the replay
+ pCollection->AddReplay( pItem );
+}
+
+void CReplayListPanel::CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem )
+{
+ IReplayItemManager *pItemManager;
+ const IQueryableReplayItem *pReplayItem = FindReplayItem( hReplayItem, &pItemManager ); AssertValidReadPtr( pReplayItem );
+
+ CBaseThumbnailCollection *pCollection = NULL;
+
+ FOR_EACH_VEC( m_vecCollections, i )
+ {
+ CBaseThumbnailCollection *pCurCollection = m_vecCollections[ i ];
+ if ( pCurCollection->FindReplayItemThumbnailRow( pReplayItem ) )
+ {
+ pCollection = pCurCollection;
+ break;
+ }
+ }
+
+ // Find the collection associated with the given replay - NOTE: we pass false here for the "bAddIfNotFound" param
+ if ( !pCollection )
+ {
+ AssertMsg( 0, "REPLAY: Should have found collection while attempting to delete a replay from the browser." );
+ return;
+ }
+
+ // Clear the previous hover pointer to avoid a potential crash where we've got a stale ptr
+ m_pPrevHoverPanel = NULL;
+
+ // Clear out the preview panel if it exists and is for the given replay necessary
+ if ( m_pPreviewPanel && m_pPreviewPanel->GetReplayHandle() == pReplayItem->GetItemReplayHandle() )
+ {
+ ClearPreviewPanel();
+ m_pBorderArrowImg->SetVisible( false );
+ }
+
+ pCollection->CleanupUIForReplayItem( hReplayItem );
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaybrowserlistpanel.h b/sp/src/game/client/replay/vgui/replaybrowserlistpanel.h
new file mode 100644
index 00000000..e3134030
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserlistpanel.h
@@ -0,0 +1,80 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#ifndef REPLAYBROWSER_LISTPANEL_H
+#define REPLAYBROWSER_LISTPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <game/client/iviewport.h>
+#include "vgui_controls/PropertyPage.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/PanelListPanel.h"
+#include "vgui_controls/EditablePanel.h"
+#include "replaybrowseritemmanager.h"
+#include "replay/genericclassbased_replay.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class CBaseThumbnailCollection;
+class CReplayPreviewPanelBase;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CReplayBrowserThumbnail;
+class CExLabel;
+
+class CReplayListPanel : public PanelListPanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayListPanel, PanelListPanel );
+public:
+ CReplayListPanel( Panel *pParent, const char *pName );
+ ~CReplayListPanel();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ void AddReplayItem( ReplayItemHandle_t hItem );
+ void CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem );
+ void AddReplaysToList();
+ void RemoveCollection( CBaseThumbnailCollection *pCollection );
+
+ virtual void OnTick();
+
+ void OnItemPanelEntered( Panel *pPanel );
+ void OnItemPanelExited( Panel *pPanel );
+
+ void SetupBorderArrow( bool bLeft );
+
+ void ClearPreviewPanel();
+
+ void ApplyFilter( const wchar_t *pFilterText );
+
+protected:
+ virtual void OnMouseWheeled(int delta);
+
+private:
+ const IQueryableReplayItem *FindItem( ReplayItemHandle_t hItem, int *pItemManagerIndex );
+ CBaseThumbnailCollection *FindOrAddReplayThumbnailCollection( const IQueryableReplayItem *pItem, IReplayItemManager *pItemManager );
+ CReplayBrowserThumbnail *FindThumbnailAtCursor( int x, int y );
+ bool PassesFilter( IQueryableReplayItem *pItem );
+
+ CBaseThumbnailCollection *m_pReplaysCollection;
+ CBaseThumbnailCollection *m_pMoviesCollection;
+
+ CUtlVector< CBaseThumbnailCollection * > m_vecCollections;
+ CReplayPreviewPanelBase *m_pPreviewPanel;
+ Panel *m_pPrevHoverPanel;
+
+ ImagePanel *m_pBorderArrowImg;
+ int m_aBorderArrowDims[2];
+ wchar_t m_wszFilter[256];
+};
+
+#endif // REPLAYBROWSER_LISTPANEL_H
diff --git a/sp/src/game/client/replay/vgui/replaybrowsermainpanel.cpp b/sp/src/game/client/replay/vgui/replaybrowsermainpanel.cpp
new file mode 100644
index 00000000..8d04b945
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowsermainpanel.cpp
@@ -0,0 +1,449 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowsermainpanel.h"
+#include "replaybrowserbasepage.h"
+#include "confirm_delete_dialog.h"
+#include "vgui_controls/PropertySheet.h"
+#include "vgui_controls/TextImage.h"
+#include "vgui/IInput.h"
+#include "vgui/ISurface.h"
+#include "ienginevgui.h"
+#include "replay/ireplaymanager.h"
+#include "replay/ireplaymoviemanager.h"
+#include "econ/econ_controls.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+//-----------------------------------------------------------------------------
+// Purpose: Replay deletion confirmation dialog
+//-----------------------------------------------------------------------------
+class CConfirmDeleteReplayDialog : public CConfirmDeleteDialog
+{
+ DECLARE_CLASS_SIMPLE( CConfirmDeleteReplayDialog, CConfirmDeleteDialog );
+public:
+ CConfirmDeleteReplayDialog( Panel *pParent, IReplayItemManager *pItemManager, int iPerformance )
+ : BaseClass( pParent )
+ {
+ m_pTextId = iPerformance >= 0 ? "#Replay_DeleteEditConfirm" : pItemManager->AreItemsMovies() ? "#Replay_DeleteMovieConfirm" : "#Replay_DeleteReplayConfirm";
+ }
+
+ const wchar_t *GetText()
+ {
+ return g_pVGuiLocalize->Find( m_pTextId );
+ }
+
+ const char *m_pTextId;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CReplayBrowserPanel::CReplayBrowserPanel( Panel *parent )
+: PropertyDialog(parent, "ReplayBrowser"),
+ m_pConfirmDeleteDialog( NULL )
+{
+ // Clear out delete info
+ V_memset( &m_DeleteInfo, 0, sizeof( m_DeleteInfo ) );
+
+ // Replay browser is parented to the game UI panel
+ vgui::VPANEL gameuiPanel = enginevgui->GetPanel( PANEL_GAMEUIDLL );
+ SetParent( gameuiPanel );
+
+ SetMoveable( false );
+ SetSizeable( false );
+
+ vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
+ SetScheme(scheme);
+ SetProportional( true );
+
+ // Setup page
+ m_pReplaysPage = new CReplayBrowserBasePage( this );
+ m_pReplaysPage->AddActionSignalTarget( this );
+
+ AddPage( m_pReplaysPage, "#Replay_MyReplays" );
+
+ m_pReplaysPage->SetVisible( true );
+
+ ListenForGameEvent( "gameui_hidden" );
+
+ // Create this now, so that it can be the default button (if created in .res file, it fights with PropertyDialog's OkButton & generates asserts)
+ CExButton *pCloseButton = new CExButton( this, "BackButton", "" );
+ GetFocusNavGroup().SetDefaultButton(pCloseButton);
+
+ m_flTimeOpened = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CReplayBrowserPanel::~CReplayBrowserPanel()
+{
+ if ( m_pConfirmDeleteDialog )
+ {
+ m_pConfirmDeleteDialog->MarkForDeletion();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/mainpanel.res", "GAME" );
+
+ SetOKButtonVisible(false);
+ SetCancelButtonVisible(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::PerformLayout( void )
+{
+ if ( GetVParent() )
+ {
+ int w,h;
+ vgui::ipanel()->GetSize( GetVParent(), w, h );
+ SetBounds(0,0,w,h);
+ }
+
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::ShowPanel(bool bShow, ReplayHandle_t hReplayDetails/*=REPLAY_HANDLE_INVALID*/,
+ int iPerformance/*=-1*/ )
+{
+ if ( bShow )
+ {
+ GetPropertySheet()->SetActivePage( m_pReplaysPage );
+ InvalidateLayout( false, true );
+ Activate();
+
+ m_flTimeOpened = gpGlobals->realtime;
+ }
+ else
+ {
+ PostMessage( m_pReplaysPage, new KeyValues("CancelSelection") );
+ }
+
+ SetVisible( bShow );
+ m_pReplaysPage->SetVisible( bShow );
+
+ if ( hReplayDetails != REPLAY_HANDLE_INVALID )
+ {
+ char szDetails[32];
+ V_snprintf( szDetails, sizeof( szDetails ), "details%i_%i", (int)hReplayDetails, iPerformance );
+ m_pReplaysPage->OnCommand( szDetails );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::FireGameEvent( IGameEvent *event )
+{
+ const char * type = event->GetName();
+
+ if ( Q_strcmp(type, "gameui_hidden") == 0 )
+ {
+ ShowPanel( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::OnCommand( const char *command )
+{
+ if ( !Q_stricmp( command, "back" ) )
+ {
+ if ( m_pReplaysPage->IsDetailsViewOpen() )
+ {
+ m_pReplaysPage->DeleteDetailsPanelAndShowReplayList();
+ }
+ else
+ {
+ // Close the main panel
+ ShowPanel( false );
+
+ // TODO: Properly manage the browser so that we don't have to recreate it ever time its opened
+ MarkForDeletion();
+
+ // If we're connected to a game server, we also close the game UI.
+ if ( engine->IsInGame() )
+ {
+ engine->ClientCmd_Unrestricted( "gameui_hide" );
+ }
+ }
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+void CReplayBrowserPanel::OnKeyCodeTyped(vgui::KeyCode code)
+{
+ if ( code == KEY_ESCAPE )
+ {
+ ShowPanel( false );
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::OnKeyCodePressed(vgui::KeyCode code)
+{
+ if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B )
+ {
+ ShowPanel( false );
+ }
+ else if ( code == KEY_ENTER )
+ {
+ // do nothing, the default is to close the panel!
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::ShowDeleteReplayDenialDlg()
+{
+ ShowMessageBox( "#Replay_DeleteDenialTitle", "#Replay_DeleteDenialText", "#GameUI_OK" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::AttemptToDeleteReplayItem( Panel *pHandler, ReplayItemHandle_t hReplayItem,
+ IReplayItemManager *pItemManager, int iPerformance )
+{
+ IQueryableReplayItem *pItem = pItemManager->GetItem( hReplayItem );
+ CGenericClassBasedReplay *pReplay = ToGenericClassBasedReplay( pItem->GetItemReplay() );
+
+ // If this is an actual replay the user is trying to delete, only allow it
+ // if the replay says it's OK. Don't execute this code for performances.
+ if ( !pItemManager->AreItemsMovies() && iPerformance < 0 && !pReplay->ShouldAllowDelete() )
+ {
+ ShowDeleteReplayDenialDlg();
+ return;
+ }
+
+ // Otherwise, show the confirm delete dlg
+ vgui::surface()->PlaySound( "replay\\replaydialog_warn.wav" );
+ ConfirmReplayItemDelete( pHandler, hReplayItem, pItemManager, iPerformance );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::ConfirmReplayItemDelete( Panel *pHandler, ReplayItemHandle_t hReplayItem,
+ IReplayItemManager *pItemManager, int iPerformance )
+{
+ CConfirmDeleteReplayDialog *pConfirm = vgui::SETUP_PANEL( new CConfirmDeleteReplayDialog( this, pItemManager, iPerformance ) );
+ if ( pConfirm )
+ {
+ // Cache replay and handler for later
+ m_DeleteInfo.m_hReplayItem = hReplayItem;
+ m_DeleteInfo.m_pItemManager = pItemManager;
+ m_DeleteInfo.m_hHandler = pHandler->GetVPanel();
+ m_DeleteInfo.m_iPerformance = iPerformance;
+
+ // Display the panel!
+ pConfirm->Show();
+
+ // Cache confirm dialog ptr
+ m_pConfirmDeleteDialog = pConfirm;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::OnConfirmDelete( KeyValues *data )
+{
+ // Clear confirm ptr
+ m_pConfirmDeleteDialog = NULL;
+
+ // User confirmed delete?
+ int nConfirmed = data->GetInt( "confirmed", 0 );
+ if ( !nConfirmed )
+ return;
+
+ // Get the replay from the dialog
+ ReplayItemHandle_t hReplayItem = m_DeleteInfo.m_hReplayItem;
+
+ // Post actions signal to the handler
+ KeyValues *pMsg = new KeyValues( "ReplayItemDeleted" );
+ pMsg->SetInt( "replayitem", (int)hReplayItem );
+ pMsg->SetInt( "perf", m_DeleteInfo.m_iPerformance );
+ PostMessage( m_DeleteInfo.m_hHandler, pMsg );
+
+ // Delete actual replay item
+ if ( m_DeleteInfo.m_iPerformance < 0 )
+ {
+ // Cleanup UI related to the replay/movie
+ CleanupUIForReplayItem( hReplayItem );
+
+ // Delete the replay/movie
+ m_DeleteInfo.m_pItemManager->DeleteItem( GetActivePage(), hReplayItem, false );
+ }
+
+ vgui::surface()->PlaySound( "replay\\deleted_take.wav" );
+
+ // Clear delete info
+ V_memset( &m_DeleteInfo, 0, sizeof( m_DeleteInfo ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::OnSaveReplay( ReplayHandle_t hNewReplay )
+{
+ // Verify that the handle is valid
+ Assert( g_pReplayManager->GetReplay( hNewReplay ) );
+
+ m_pReplaysPage->AddReplay( hNewReplay );
+ m_pReplaysPage->Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::OnDeleteReplay( ReplayHandle_t hDeletedReplay )
+{
+ // Verify that the handle is valid
+ Assert( g_pReplayManager->GetReplay( hDeletedReplay ) );
+
+ DeleteReplay( hDeletedReplay );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::DeleteReplay( ReplayHandle_t hReplay )
+{
+ m_pReplaysPage->DeleteReplay( hReplay );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayBrowserPanel::CleanupUIForReplayItem( ReplayItemHandle_t hReplayItem )
+{
+ if ( GetActivePage() == m_pReplaysPage )
+ {
+ m_pReplaysPage->CleanupUIForReplayItem( hReplayItem );
+ }
+}
+
+static vgui::DHANDLE<CReplayBrowserPanel> g_ReplayBrowserPanel;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CReplayBrowserPanel *ReplayUI_OpenReplayBrowserPanel( ReplayHandle_t hReplayDetails,
+ int iPerformance )
+{
+ if ( !g_ReplayBrowserPanel.Get() )
+ {
+ g_ReplayBrowserPanel = vgui::SETUP_PANEL( new CReplayBrowserPanel( NULL ) );
+ g_ReplayBrowserPanel->InvalidateLayout( false, true );
+ }
+
+ engine->ClientCmd_Unrestricted( "gameui_activate" );
+ g_ReplayBrowserPanel->ShowPanel( true, hReplayDetails, iPerformance );
+
+ extern IReplayMovieManager *g_pReplayMovieManager;
+ if ( g_pReplayMovieManager->GetMovieCount() > 0 )
+ {
+ // Fire a message the game DLL can intercept (for achievements, etc).
+ IGameEvent *event = gameeventmanager->CreateEvent( "browse_replays" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+ }
+
+ return g_ReplayBrowserPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CReplayBrowserPanel *ReplayUI_GetBrowserPanel( void )
+{
+ return g_ReplayBrowserPanel.Get();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ReplayUI_CloseReplayBrowser()
+{
+ if ( g_ReplayBrowserPanel )
+ {
+ g_ReplayBrowserPanel->MarkForDeletion();
+ g_ReplayBrowserPanel = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ReplayUI_ReloadBrowser( ReplayHandle_t hReplay/*=REPLAY_HANDLE_INVALID*/,
+ int iPerformance/*=-1*/ )
+{
+ delete g_ReplayBrowserPanel.Get();
+ g_ReplayBrowserPanel = NULL;
+ ReplayUI_OpenReplayBrowserPanel( hReplay, iPerformance );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CON_COMMAND_F( open_replaybrowser, "Open the replay browser.", FCVAR_CLIENTDLL )
+{
+ ReplayUI_OpenReplayBrowserPanel( REPLAY_HANDLE_INVALID, -1 );
+ g_ReplayBrowserPanel->InvalidateLayout( false, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CON_COMMAND_F( replay_reloadbrowser, "Reloads replay data and display replay browser", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE )
+{
+ ReplayUI_ReloadBrowser();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CON_COMMAND_F( replay_hidebrowser, "Hides replay browser", FCVAR_CLIENTDLL )
+{
+ ReplayUI_CloseReplayBrowser();
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaybrowsermainpanel.h b/sp/src/game/client/replay/vgui/replaybrowsermainpanel.h
new file mode 100644
index 00000000..b73d5af0
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowsermainpanel.h
@@ -0,0 +1,89 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#if defined( REPLAY_ENABLED )
+
+#ifndef REPLAYBROWSER_MAIN_PANEL_H
+#define REPLAYBROWSER_MAIN_PANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "vgui_controls/PropertyDialog.h"
+#include "replay/replayhandle.h"
+#include "GameEventListener.h"
+#include "replaybrowseritemmanager.h"
+
+//-----------------------------------------------------------------------------
+
+class CReplayBrowserBasePage;
+class CConfirmDeleteDialog;
+class CExButton;
+
+//-----------------------------------------------------------------------------
+
+class CReplayBrowserPanel : public vgui::PropertyDialog,
+ public CGameEventListener
+{
+ DECLARE_CLASS_SIMPLE( CReplayBrowserPanel, vgui::PropertyDialog );
+public:
+ CReplayBrowserPanel( Panel *parent );
+ virtual ~CReplayBrowserPanel();
+
+ void OnSaveReplay( ReplayHandle_t hNewReplay );
+ void OnDeleteReplay( ReplayHandle_t hDeletedReplay );
+
+ void DeleteReplay( ReplayHandle_t hReplay );
+
+ virtual void CleanupUIForReplayItem( ReplayItemHandle_t hReplay ); // After a replay has been deleted - deletes all UI (thumbnail, but maybe also row and/or collection as well)
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void PerformLayout( void );
+ virtual void OnCommand( const char *command );
+ virtual void ShowPanel( bool bShow, ReplayHandle_t hReplayDetails = REPLAY_HANDLE_INVALID, int iPerformance = -1 );
+ virtual void OnKeyCodeTyped(vgui::KeyCode code);
+ virtual void OnKeyCodePressed(vgui::KeyCode code);
+
+ virtual void FireGameEvent( IGameEvent *event );
+
+ MESSAGE_FUNC_PARAMS( OnConfirmDelete, "ConfirmDlgResult", data );
+
+ void AttemptToDeleteReplayItem( Panel *pHandler, ReplayItemHandle_t hReplayItem, IReplayItemManager *pItemManager, int iPerformance );
+
+ CReplayBrowserBasePage *m_pReplaysPage;
+ CConfirmDeleteDialog *m_pConfirmDeleteDialog;
+
+ struct DeleteInfo_t
+ {
+ ReplayItemHandle_t m_hReplayItem;
+ IReplayItemManager *m_pItemManager;
+ vgui::VPANEL m_hHandler;
+ int m_iPerformance;
+ };
+
+ DeleteInfo_t m_DeleteInfo;
+
+ float GetTimeOpened( void ){ return m_flTimeOpened; }
+
+private:
+ void ShowDeleteReplayDenialDlg();
+ void ConfirmReplayItemDelete( Panel *pHandler, ReplayItemHandle_t hReplayItem, IReplayItemManager *pItemManager, int iPerformance );
+
+ float m_flTimeOpened;
+};
+
+//-----------------------------------------------------------------------------
+
+CReplayBrowserPanel *ReplayUI_GetBrowserPanel();
+void ReplayUI_ReloadBrowser( ReplayHandle_t hReplay = REPLAY_HANDLE_INVALID, int iPerformance = -1 );
+void ReplayUI_CloseReplayBrowser();
+
+//-----------------------------------------------------------------------------
+
+#endif // REPLAYBROWSER_MAIN_PANEL_H
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaybrowsermovieplayerpanel.cpp b/sp/src/game/client/replay/vgui/replaybrowsermovieplayerpanel.cpp
new file mode 100644
index 00000000..23883f2f
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowsermovieplayerpanel.cpp
@@ -0,0 +1,220 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowsermovieplayerpanel.h"
+#include "vgui/IVGui.h"
+#include "vgui/IInput.h"
+#include "engine/IEngineSound.h"
+#include "iclientmode.h"
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+CMoviePlayerPanel::CMoviePlayerPanel( Panel *pParent, const char *pName, const char *pMovieFilename )
+: CReplayBasePanel( pParent, pName ),
+ m_flCurFrame( 0.0f ),
+ m_flLastTime( 0.0f ),
+ m_nLastMouseXPos( 0 ),
+ m_bPlaying( false ),
+ m_bLooping( false ),
+ m_bFullscreen( false ),
+ m_bMouseOverScrub( false ),
+ m_pOldParent( NULL ),
+ m_pVideoMaterial( NULL )
+{
+ if ( g_pVideo )
+ {
+ m_pVideoMaterial = g_pVideo->CreateVideoMaterial( pMovieFilename, pMovieFilename, "GAME" );
+ if ( m_pVideoMaterial )
+ {
+ m_pMaterial = m_pVideoMaterial->GetMaterial();
+ m_pMaterial->AddRef();
+ m_nNumFrames = m_pVideoMaterial->GetFrameCount();
+
+ }
+ }
+
+ ivgui()->AddTickSignal( GetVPanel(), 0 );
+}
+
+CMoviePlayerPanel::~CMoviePlayerPanel()
+{
+ FreeMaterial();
+
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CMoviePlayerPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ GetPosRelativeToAncestor( NULL, m_nGlobalPos[0], m_nGlobalPos[1] );
+
+ if ( m_bFullscreen )
+ {
+ // Cache parent
+ m_pOldParent = GetParent();
+ GetBounds( m_aOldBounds[0], m_aOldBounds[1], m_aOldBounds[2], m_aOldBounds[3] );
+
+ // Adjust parent for fullscreen mode
+ SetParent( g_pClientMode->GetViewport() );
+
+ // Adjust bounds for fullscreen
+ SetBounds( 0, 0, ScreenWidth(), ScreenHeight() );
+ }
+ else if ( m_pOldParent )
+ {
+ // Restore old parent/bounds
+ SetParent( m_pOldParent );
+ SetBounds( m_aOldBounds[0], m_aOldBounds[1], m_aOldBounds[2], m_aOldBounds[3] );
+ }
+}
+
+void CMoviePlayerPanel::OnMousePressed( MouseCode code )
+{
+// ToggleFullscreen();
+}
+
+void CMoviePlayerPanel::SetScrubOnMouseOverMode( bool bOn )
+{
+ if ( bOn )
+ {
+ m_bPlaying = false;
+ }
+
+ m_bMouseOverScrub = bOn;
+}
+
+void CMoviePlayerPanel::Play()
+{
+ m_bPlaying = true;
+ m_flLastTime = gpGlobals->realtime;
+
+ enginesound->NotifyBeginMoviePlayback();
+}
+
+void CMoviePlayerPanel::FreeMaterial()
+{
+ if ( m_pVideoMaterial )
+ {
+ if ( g_pVideo )
+ {
+ g_pVideo->DestroyVideoMaterial( m_pVideoMaterial );
+ }
+
+ m_pVideoMaterial = NULL;
+ }
+
+ if ( m_pMaterial )
+ {
+ m_pMaterial->Release();
+ m_pMaterial = NULL;
+ }
+}
+
+void CMoviePlayerPanel::OnTick()
+{
+ if ( !IsEnabled() )
+ return;
+
+ if ( m_bMouseOverScrub )
+ {
+ int nMouseX, nMouseY;
+ input()->GetCursorPos( nMouseX, nMouseY );
+ if ( IsWithin( nMouseX, nMouseY ) &&
+ nMouseX != m_nLastMouseXPos )
+ {
+ float flPercent = (float)( nMouseX - m_nGlobalPos[0] ) / GetWide();
+ m_flCurFrame = flPercent * ( m_nNumFrames - 1 );
+ m_nLastMouseXPos = nMouseX;
+ }
+ }
+ else if ( m_bPlaying )
+ {
+ float flElapsed = gpGlobals->realtime - m_flLastTime;
+ m_flLastTime = gpGlobals->realtime;
+
+ m_flCurFrame += flElapsed * m_pVideoMaterial->GetVideoFrameRate().GetFPS();
+ // Loop if necessary
+ if ( m_flCurFrame >= m_nNumFrames )
+ {
+ if ( m_bLooping )
+ {
+ m_flCurFrame = m_flCurFrame - m_nNumFrames;
+ }
+ else
+ {
+ // Don't go past last frame
+ m_flCurFrame = m_nNumFrames - 1;
+ }
+ }
+ }
+
+ m_pVideoMaterial->SetFrame( m_flCurFrame );
+
+// Msg( "frame: %f / %i\n", m_flCurFrame, m_nNumFrames );
+}
+
+void CMoviePlayerPanel::Paint()
+{
+ if ( m_pVideoMaterial == NULL )
+ return;
+
+ // Get panel position/dimensions
+ int x,y;
+ int w,h;
+ GetPosRelativeToAncestor( NULL, x, y );
+ GetSize( w,h );
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( m_pMaterial );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+
+ float flMinU = 0.0f, flMinV = 0.0f;
+ float flMaxU, flMaxV;
+ m_pVideoMaterial->GetVideoTexCoordRange( &flMaxU, &flMaxV );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ meshBuilder.Position3f( x, y, 0.0f );
+ meshBuilder.TexCoord2f( 0, flMinU, flMinV );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x + w, y, 0.0f );
+ meshBuilder.TexCoord2f( 0, flMaxU, flMinV );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x + w, y + h, 0.0f );
+ meshBuilder.TexCoord2f( 0, flMaxU, flMaxV );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x, y + h, 0.0f );
+ meshBuilder.TexCoord2f( 0, flMinU, flMaxV );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+void CMoviePlayerPanel::ToggleFullscreen()
+{
+ m_bFullscreen = !m_bFullscreen;
+
+ InvalidateLayout( false, false );
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaybrowsermovieplayerpanel.h b/sp/src/game/client/replay/vgui/replaybrowsermovieplayerpanel.h
new file mode 100644
index 00000000..39ad98a7
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowsermovieplayerpanel.h
@@ -0,0 +1,57 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#ifndef REPLAYBROWSERMOVIEPLAYERPANEL_H
+#define REPLAYBROWSERMOVIEPLAYERPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "replaybrowserbasepanel.h"
+#include "video/ivideoservices.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: A panel that plays AVI's
+//-----------------------------------------------------------------------------
+class CMoviePlayerPanel : public CReplayBasePanel
+{
+ DECLARE_CLASS_SIMPLE( CMoviePlayerPanel, CReplayBasePanel );
+public:
+ CMoviePlayerPanel( Panel *pParent, const char *pName, const char *pMovieFilename );
+ ~CMoviePlayerPanel();
+
+ virtual void Paint();
+
+ void Play();
+ void SetLooping( bool bLooping ) { m_bLooping = bLooping; }
+
+ bool IsPlaying() { return m_bPlaying; }
+ void SetScrubOnMouseOverMode( bool bOn );
+ void FreeMaterial();
+ void ToggleFullscreen();
+
+private:
+ virtual void PerformLayout();
+ virtual void OnMousePressed( MouseCode code );
+ virtual void OnTick();
+
+ IVideoMaterial *m_pVideoMaterial;
+
+ IMaterial *m_pMaterial;
+ float m_flCurFrame;
+ int m_nNumFrames;
+ bool m_bPlaying;
+ bool m_bLooping;
+ float m_flLastTime;
+ int m_nGlobalPos[2];
+ int m_nLastMouseXPos;
+ bool m_bFullscreen;
+ Panel *m_pOldParent;
+ int m_aOldBounds[4];
+ bool m_bMouseOverScrub; // In this mode, we don't playback, only scrub on mouse over
+};
+
+#endif // REPLAYBROWSERMOVIEPLAYERPANEL_H
diff --git a/sp/src/game/client/replay/vgui/replaybrowserpreviewpanel.cpp b/sp/src/game/client/replay/vgui/replaybrowserpreviewpanel.cpp
new file mode 100644
index 00000000..4fa8cdac
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserpreviewpanel.cpp
@@ -0,0 +1,301 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowserpreviewpanel.h"
+#include "replaybrowsermainpanel.h"
+#include "replaybrowsermovieplayerpanel.h"
+#include "replaybrowserlistitempanel.h"
+#include "replay/ireplaymovie.h"
+#include "replay/screenshot.h"
+#include "vgui/ISurface.h"
+#include "econ/econ_controls.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+//-----------------------------------------------------------------------------
+
+CReplayPreviewPanelBase::CReplayPreviewPanelBase( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager )
+: EditablePanel( pParent, "PreviewPanel" ),
+ m_hItem( hItem ),
+ m_pItemManager( pItemManager )
+{
+ CGenericClassBasedReplay *pReplay = GetReplay();
+ IQueryableReplayItem *pItem = pItemManager->GetItem( hItem );
+
+ // Setup class image
+ char szImage[MAX_OSPATH];
+ m_pClassImage = new ImagePanel( this, "ClassImage" );
+ V_snprintf( szImage, sizeof( szImage ), "class_sel_sm_%s_%s", pReplay->GetMaterialFriendlyPlayerClass(), pReplay->GetPlayerTeam() ); // Cause default image to display
+ m_pClassImage->SetImage( szImage );
+
+ m_pInfoPanel = new vgui::EditablePanel( this, "InfoPanel" );
+
+ // Setup map label
+ const char *pMapName = pReplay->m_szMapName;
+ const char *pUnderscore = V_strstr( pMapName, "_" );
+ if ( pUnderscore )
+ {
+ pMapName = pUnderscore + 1;
+ }
+ m_pMapLabel = new CExLabel( m_pInfoPanel, "MapLabel", pMapName );
+
+ // Setup record date/time
+ const CReplayTime &RecordTime = pItem->GetItemDate();
+ int nDay, nMonth, nYear;
+ RecordTime.GetDate( nDay, nMonth, nYear );
+ int nHour, nMin, nSec;
+ RecordTime.GetTime( nHour, nMin, nSec );
+ const wchar_t *pDateAndTime = CReplayTime::GetLocalizedDate( g_pVGuiLocalize, nDay, nMonth, nYear, &nHour, &nMin, &nSec );
+
+ // Setup date / time label
+ m_pDateTimeLabel = new CExLabel( m_pInfoPanel, "DateTimeLabel", pDateAndTime );
+
+ // Setup info labels
+ for ( int i = 0; i < NUM_INFO_LABELS; ++i )
+ {
+ for ( int j = 0; j < 2; ++j )
+ {
+ m_pReplayInfoLabels[i][j] = new CExLabel( m_pInfoPanel, VarArgs("Label%d_%d", i, j), "" );
+ }
+ }
+ m_pReplayInfoLabels[ LABEL_PLAYED_AS ][1]->SetText( pReplay->GetPlayerClass() );
+ m_pReplayInfoLabels[ LABEL_KILLED_BY ][1]->SetText( pReplay->WasKilled() ? pReplay->GetKillerName() : "#Replay_NoKiller" );
+ m_pReplayInfoLabels[ LABEL_LIFE_LENGTH ][1]->SetText( CReplayTime::FormatTimeString( (int)pItem->GetItemLength() ) );
+}
+
+CReplayPreviewPanelBase::~CReplayPreviewPanelBase()
+{
+}
+
+void CReplayPreviewPanelBase::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaybrowser/previewpanel.res", "GAME" );
+
+#if !defined( TF_CLIENT_DLL )
+ m_pClassImage->SetVisible( false );
+#endif
+}
+
+void CReplayPreviewPanelBase::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ CGenericClassBasedReplay *pReplay = GetReplay();
+ if ( !pReplay )
+ return;
+
+ int nWide = XRES(18);
+ int nTall = YRES(18);
+ int nScreenshotH = 0; // Represents the height of the screenshot OR the "no screenshot" label
+ LayoutView( nWide, nTall, nScreenshotH );
+ int iInfoHeight = m_pInfoPanel->GetTall();
+ nTall += iInfoHeight;
+
+ if ( m_pClassImage )
+ {
+ int w, h;
+ m_pClassImage->GetImage()->GetContentSize( w, h );
+ float s = ShoudlUseLargeClassImage() ? 1.25f : 1.0f;
+ int nClassTall = s*h;
+ m_pClassImage->SetSize( s*w, nClassTall );
+ m_pClassImage->SetShouldScaleImage( true );
+ m_pClassImage->SetScaleAmount( s );
+
+ // The panel should be at least as tall as the height of the screenshot
+ // (or "no screenshot" label) and the class image.
+ if ( nTall < nClassTall )
+ {
+ nTall = nClassTall;
+ }
+ m_pClassImage->SetPos( XRES(9), nTall - nClassTall );
+ }
+
+ const int nLabelX = m_pClassImage->GetWide() + XRES( 18 );
+ int iInfoWidth = nWide - nLabelX;
+ m_pMapLabel->SetSize( iInfoWidth, m_pMapLabel->GetTall() );
+ m_pDateTimeLabel->SetSize( iInfoWidth, m_pMapLabel->GetTall() );
+ m_pInfoPanel->SetBounds( nLabelX, nTall - iInfoHeight, iInfoWidth, iInfoHeight );
+
+ nTall += YRES(9);
+
+ SetSize( nWide, nTall );
+}
+
+void CReplayPreviewPanelBase::LayoutView( int &nWide, int &nTall, int &nCurY )
+{
+ nWide = XRES( 188 );
+ nTall = YRES(9);
+ nCurY = nTall;
+}
+
+CGenericClassBasedReplay *CReplayPreviewPanelBase::GetReplay()
+{
+ return ToGenericClassBasedReplay( m_pItemManager->GetItem( m_hItem )->GetItemReplay() );
+}
+
+ReplayHandle_t CReplayPreviewPanelBase::GetReplayHandle()
+{
+ return GetReplay()->GetHandle();
+}
+
+//-----------------------------------------------------------------------------
+
+CReplayPreviewPanelSlideshow::CReplayPreviewPanelSlideshow( Panel *pParent, QueryableReplayItemHandle_t hReplay, IReplayItemManager *pItemManager )
+: BaseClass( pParent, hReplay, pItemManager ),
+ m_pScreenshotPanel( NULL )
+{
+ // Setup screenshot slideshow panel
+ CGenericClassBasedReplay *pReplay = GetReplay();
+ const int nScreenshotCount = pReplay->GetScreenshotCount();
+ if ( nScreenshotCount )
+ {
+ m_pScreenshotPanel = new CReplayScreenshotSlideshowPanel( this, "ScreenshotSlideshowPanel", hReplay );
+
+ // Set pretty quick transition times based on the screenshot count
+ m_pScreenshotPanel->SetInterval( ( nScreenshotCount == 2 ) ? 3.0f : 2.0f );
+ m_pScreenshotPanel->SetTransitionTime( 0.5f );
+ }
+
+ // Setup the no screenshot label
+ m_pNoScreenshotLabel = new CExLabel( this, "NoScreenshotLabel", "#Replay_NoScreenshot" );
+ m_pNoScreenshotLabel->SetVisible( false );
+}
+
+void CReplayPreviewPanelSlideshow::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ m_pNoScreenshotLabel->SizeToContents();
+ m_pNoScreenshotLabel->SetWide( GetWide() );
+}
+
+void CReplayPreviewPanelSlideshow::LayoutView( int &nWide, int &nTall, int &nCurY )
+{
+ if ( m_pScreenshotPanel )
+ {
+ // Use the dimensions from the first screenshot to figure out the scale, even though the dimensions
+ // may vary if the user changed resolutions during gameplay
+ CGenericClassBasedReplay *pReplay = GetReplay();
+ const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( 0 );
+ int nScreenshotW = pScreenshot->m_nWidth;
+ int nScreenshotH = pScreenshot->m_nHeight;
+
+ // Scale the screenshot if it's too big for the current resolution
+ float flScreenshotScale = 1.0f;
+ int nMaxScreenshotWidth = ScreenWidth() / 3;
+ if ( nScreenshotW > nMaxScreenshotWidth )
+ {
+ flScreenshotScale = (float)nMaxScreenshotWidth / pScreenshot->m_nWidth;
+ nScreenshotW = nMaxScreenshotWidth;
+ }
+ nCurY = nScreenshotH * flScreenshotScale;
+
+ m_pScreenshotPanel->GetImagePanel()->SetShouldScaleImage( true );
+ m_pScreenshotPanel->GetImagePanel()->SetScaleAmount( flScreenshotScale );
+
+ nWide += nScreenshotW;
+ nTall += nCurY;
+
+ m_pScreenshotPanel->SetBounds( (nWide - nScreenshotW) * 0.5, YRES(9), nScreenshotW, nCurY );
+ }
+ else
+ {
+ int w, h;
+ m_pNoScreenshotLabel->SetContentAlignment( Label::a_center );
+ m_pNoScreenshotLabel->GetContentSize( w, h );
+ nTall += YRES( 20 );
+ m_pNoScreenshotLabel->SetBounds( 0, nTall, w, h );
+ nTall += YRES( 20 );
+ m_pNoScreenshotLabel->SetVisible( true );
+
+ nWide += XRES( 213 ); // Default width (maps to 640 on 1920x1200)
+ nTall += h;
+ nCurY = nTall;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+CReplayPreviewPanelMovie::CReplayPreviewPanelMovie( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager )
+: BaseClass( pParent, hItem, pItemManager ),
+ m_pMoviePlayerPanel( NULL )
+{
+ m_flCreateTime = gpGlobals->realtime;
+
+ ivgui()->AddTickSignal( GetVPanel(), 10 );
+}
+
+CReplayPreviewPanelMovie::~CReplayPreviewPanelMovie()
+{
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CReplayPreviewPanelMovie::OnTick()
+{
+ if ( gpGlobals->realtime >= m_flCreateTime + 0.5f )
+ {
+ if ( !m_pMoviePlayerPanel )
+ {
+ m_pMoviePlayerPanel = new CMoviePlayerPanel( this, "MoviePlayer", GetReplayMovie()->GetMovieFilename() );
+ InvalidateLayout( true, false );
+ }
+
+ if ( !m_pMoviePlayerPanel->IsPlaying() )
+ {
+ m_pMoviePlayerPanel->SetLooping( true );
+ m_pMoviePlayerPanel->Play();
+ }
+ }
+}
+
+IReplayMovie* CReplayPreviewPanelMovie::GetReplayMovie()
+{
+ return static_cast< IReplayMovie * >( m_pItemManager->GetItem( m_hItem ) );
+}
+
+void CReplayPreviewPanelMovie::LayoutView( int &nWide, int &nTall, int &nCurY )
+{
+ // Get frame dimensions
+ int nFrameWidth, nFrameHeight;
+ IReplayMovie* pReplayMovie = GetReplayMovie();
+ pReplayMovie->GetFrameDimensions( nFrameWidth, nFrameHeight );
+
+ int nScaledWidth = nFrameWidth;
+ int nScaledHeight = nFrameHeight;
+
+ // Scale the screenshot if it's too big for the current resolution
+ float flScale = 1.0f;
+ int nMaxWidth = ScreenWidth() / 3;
+ if ( nFrameWidth > nMaxWidth )
+ {
+ flScale = (float)nMaxWidth / nFrameWidth;
+ nScaledWidth = nMaxWidth;
+ nScaledHeight = nFrameHeight * flScale;
+ }
+
+ nWide += nScaledWidth;
+ nTall += nScaledHeight;
+ nCurY = nTall;
+
+ // Layout movie player panel if it's ready
+ if ( m_pMoviePlayerPanel )
+ {
+ m_pMoviePlayerPanel->SetBounds( 9, 9, nScaledWidth, nScaledHeight );
+ m_pMoviePlayerPanel->SetEnabled( true );
+ m_pMoviePlayerPanel->SetVisible( true );
+ m_pMoviePlayerPanel->SetZPos( 101 );
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaybrowserpreviewpanel.h b/sp/src/game/client/replay/vgui/replaybrowserpreviewpanel.h
new file mode 100644
index 00000000..53b173bc
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserpreviewpanel.h
@@ -0,0 +1,120 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef REPLAYBROWSER_PREVIEWPANEL_H
+#define REPLAYBROWSER_PREVIEWPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <game/client/iviewport.h>
+#include "vgui_controls/PropertyPage.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/PanelListPanel.h"
+#include "vgui_controls/EditablePanel.h"
+#include "replaybrowseritemmanager.h"
+#include "replay/genericclassbased_replay.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class CExLabel;
+class CBaseThumbnailCollection;
+class CReplayDetailsPanel;
+class CReplayScreenshotSlideshowPanel;
+
+//-----------------------------------------------------------------------------
+// Purpose: Preview balloon
+//-----------------------------------------------------------------------------
+class CGenericClassBasedReplay;
+class CCrossfadableImagePanel;
+class CSlideshowPanel;
+
+class CReplayPreviewPanelBase : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayPreviewPanelBase, EditablePanel );
+public:
+ CReplayPreviewPanelBase( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager );
+ ~CReplayPreviewPanelBase();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ ReplayHandle_t GetReplayHandle();
+
+protected:
+ CGenericClassBasedReplay *GetReplay();
+
+ virtual bool ShoudlUseLargeClassImage() { return false; }
+ virtual void LayoutView( int &nWide, int &nTall, int &nCurY );
+
+protected:
+ IReplayItemManager *m_pItemManager;
+ QueryableReplayItemHandle_t m_hItem;
+
+private:
+ ImagePanel *m_pClassImage;
+ vgui::EditablePanel *m_pInfoPanel;
+
+ CExLabel *m_pMapLabel;
+ CExLabel *m_pDateTimeLabel;
+
+ enum ELabels
+ {
+ LABEL_PLAYED_AS,
+ LABEL_KILLED_BY,
+ LABEL_LIFE_LENGTH,
+ NUM_INFO_LABELS
+ };
+ CExLabel *m_pReplayInfoLabels[NUM_INFO_LABELS][2];
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Preview balloon for slideshows (actual replays)
+//-----------------------------------------------------------------------------
+class CReplayPreviewPanelSlideshow : public CReplayPreviewPanelBase
+{
+ DECLARE_CLASS_SIMPLE( CReplayPreviewPanelSlideshow, CReplayPreviewPanelBase );
+public:
+ CReplayPreviewPanelSlideshow( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager );
+
+private:
+ virtual void PerformLayout();
+ virtual void LayoutView( int &nWide, int &nTall, int &nCurY );
+
+ CReplayScreenshotSlideshowPanel *m_pScreenshotPanel;
+ CExLabel *m_pNoScreenshotLabel;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Preview balloon for movies (rendered replays)
+//-----------------------------------------------------------------------------
+class CMoviePlayerPanel;
+class IReplayMovie;
+
+class CReplayPreviewPanelMovie : public CReplayPreviewPanelBase
+{
+ DECLARE_CLASS_SIMPLE( CReplayPreviewPanelMovie, CReplayPreviewPanelBase );
+public:
+ CReplayPreviewPanelMovie( Panel *pParent, QueryableReplayItemHandle_t hItem, IReplayItemManager *pItemManager );
+ ~CReplayPreviewPanelMovie();
+
+private:
+ virtual void OnTick();
+ virtual void LayoutView( int &nWide, int &nTall, int &nCurY );
+
+ virtual IReplayMovie *GetReplayMovie();
+
+ CMoviePlayerPanel *m_pMoviePlayerPanel;
+ float m_flCreateTime;
+};
+
+
+
+#endif // REPLAYBROWSER_PREVIEWPANEL_H
diff --git a/sp/src/game/client/replay/vgui/replaybrowserrenderdialog.cpp b/sp/src/game/client/replay/vgui/replaybrowserrenderdialog.cpp
new file mode 100644
index 00000000..eef4db2f
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserrenderdialog.cpp
@@ -0,0 +1,639 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaybrowserrenderdialog.h"
+#include "vgui_controls/TextImage.h"
+#include "vgui_controls/CheckButton.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui/IInput.h"
+#include "replay/genericclassbased_replay.h"
+#include "ienginevgui.h"
+#include "replayrenderoverlay.h"
+#include "replay/ireplaymanager.h"
+#include "replay/ireplaymoviemanager.h"
+#include "video/ivideoservices.h"
+#include "confirm_dialog.h"
+#include "replay/replayrenderer.h"
+
+#include "replay/performance.h"
+#include "replay/replayvideo.h"
+#include "replay_gamestats_shared.h"
+
+#include "econ/econ_controls.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+//-----------------------------------------------------------------------------
+
+extern IReplayMovieManager *g_pReplayMovieManager;
+
+//-----------------------------------------------------------------------------
+
+ConVar replay_rendersetting_quitwhendone( "replay_rendersetting_quitwhendone", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Quit after rendering is completed.", true, 0.0f, true, 1.0f );
+ConVar replay_rendersetting_exportraw( "replay_rendersetting_exportraw", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Export raw TGA frames and a .wav file, instead of encoding a movie file.", true, 0.0f, true, 1.0f );
+ConVar replay_rendersetting_motionblurquality( "replay_rendersetting_motionblurquality", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Motion blur quality.", true, 0, true, MAX_MOTION_BLUR_QUALITY );
+ConVar replay_rendersetting_motionblurenabled( "replay_rendersetting_motionblurenabled", "1", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Motion blur enabled/disabled.", true, 0.0f, true, 1.0f );
+ConVar replay_rendersetting_encodingquality( "replay_rendersetting_encodingquality", "100", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Render quality: the higher the quality, the larger the resulting movie file size.", true, 0, true, 100 );
+ConVar replay_rendersetting_motionblur_can_toggle( "replay_rendersetting_motionblur_can_toggle", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "" );
+ConVar replay_rendersetting_renderglow( "replay_rendersetting_renderglow", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Glow effect enabled/disabled.", true, 0.0f, true, 1.0f );
+
+//-----------------------------------------------------------------------------
+
+CReplayRenderDialog::CReplayRenderDialog( Panel *pParent, ReplayHandle_t hReplay, bool bSetQuit, int iPerformance )
+: BaseClass( pParent, "RenderDialog" ),
+ m_bShowAdvancedOptions( false ),
+ m_hReplay( hReplay ),
+ m_bSetQuit( bSetQuit ),
+ m_iPerformance( iPerformance ),
+ m_pVideoModesCombo( NULL ),
+ m_pCodecCombo( NULL ),
+ m_pPlayVoiceCheck( NULL ),
+ m_pShowAdvancedOptionsCheck( NULL ),
+ m_pQuitWhenDoneCheck( NULL ),
+ m_pExportRawCheck( NULL ),
+ m_pTitleText( NULL ),
+ m_pResolutionNoteLabel( NULL ),
+ m_pEnterANameLabel( NULL ),
+ m_pVideoModeLabel( NULL ),
+ m_pCodecLabel( NULL ),
+ m_pMotionBlurLabel( NULL ),
+ m_pMotionBlurSlider( NULL ),
+ m_pQualityLabel( NULL ),
+ m_pQualitySlider( NULL ),
+ m_pTitleLabel( NULL ),
+ m_pCancelButton( NULL ),
+ m_pRenderButton( NULL ),
+ m_pBgPanel( NULL ),
+ m_pMotionBlurCheck( NULL ),
+ m_pQualityPresetLabel( NULL ),
+ m_pQualityPresetCombo( NULL ),
+ m_pSeparator( NULL ),
+ m_pGlowEnabledCheck( NULL )
+{
+ m_iQualityPreset = ReplayVideo_GetDefaultQualityPreset();
+}
+
+void CReplayRenderDialog::UpdateControlsValues()
+{
+ ConVarRef replay_voice_during_playback( "replay_voice_during_playback" );
+
+ m_pQuitWhenDoneCheck->SetSelected( replay_rendersetting_quitwhendone.GetBool() );
+ m_pExportRawCheck->SetSelected( replay_rendersetting_exportraw.GetBool() );
+ m_pShowAdvancedOptionsCheck->SetSelected( m_bShowAdvancedOptions );
+ m_pMotionBlurSlider->SetValue( replay_rendersetting_motionblurquality.GetInt() );
+ m_pMotionBlurCheck->SetSelected( replay_rendersetting_motionblurenabled.GetBool() );
+ m_pQualitySlider->SetValue( replay_rendersetting_encodingquality.GetInt() / ReplayVideo_GetQualityInterval() );
+
+ if ( m_pGlowEnabledCheck )
+ {
+ m_pGlowEnabledCheck->SetSelected( replay_rendersetting_renderglow.GetBool() );
+ }
+
+ if ( replay_voice_during_playback.IsValid() )
+ {
+ m_pPlayVoiceCheck->SetSelected( replay_voice_during_playback.GetBool() );
+ }
+ else
+ {
+ m_pPlayVoiceCheck->SetEnabled( false );
+ }
+}
+
+void CReplayRenderDialog::AddControlToAutoLayout( Panel *pPanel, bool bAdvanced )
+{
+ LayoutInfo_t *pNewLayoutInfo = new LayoutInfo_t;
+ pNewLayoutInfo->pPanel = pPanel;
+
+ // Use the positions from the .res file as relative positions for auto-layout
+ pPanel->GetPos( pNewLayoutInfo->nOffsetX, pNewLayoutInfo->nOffsetY );
+
+ pNewLayoutInfo->bAdvanced = bAdvanced;
+
+ // Add to the list
+ m_lstControls.AddToTail( pNewLayoutInfo );
+}
+
+void CReplayRenderDialog::SetValuesFromQualityPreset()
+{
+ const ReplayQualityPreset_t &preset = ReplayVideo_GetQualityPreset( m_iQualityPreset );
+ replay_rendersetting_motionblurquality.SetValue( preset.m_iMotionBlurQuality );
+ replay_rendersetting_motionblurenabled.SetValue( (int)preset.m_bMotionBlurEnabled );
+ replay_rendersetting_encodingquality.SetValue( preset.m_iQuality );
+ for ( int i = 0; i < ReplayVideo_GetCodecCount(); ++i )
+ {
+ const ReplayCodec_t &CurCodec = ReplayVideo_GetCodec( i );
+ if ( CurCodec.m_nCodecId == preset.m_nCodecId )
+ {
+ m_pCodecCombo->ActivateItem( m_pCodecCombo->GetItemIDFromRow( i ) );
+ break;
+ }
+ }
+ UpdateControlsValues();
+ InvalidateLayout();
+}
+
+void CReplayRenderDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ int i;
+
+ // Link in TF scheme
+ extern IEngineVGui *enginevgui;
+ vgui::HScheme pTFScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
+ SetScheme( pTFScheme );
+ SetProportional( true );
+
+ BaseClass::ApplySchemeSettings( vgui::scheme()->GetIScheme( pTFScheme ) );
+
+ LoadControlSettings( "Resource/UI/replaybrowser/renderdialog.res", "GAME" );
+
+ // retrieve controls
+ m_pPlayVoiceCheck = dynamic_cast< CheckButton * >( FindChildByName( "PlayVoice" ) );
+ m_pShowAdvancedOptionsCheck = dynamic_cast< CheckButton * >( FindChildByName( "ShowAdvancedOptions" ) );
+ m_pQuitWhenDoneCheck = dynamic_cast< CheckButton * >( FindChildByName( "QuitWhenDone" ) );
+ m_pExportRawCheck = dynamic_cast< CheckButton * >( FindChildByName( "ExportRaw" ) );
+ m_pTitleText = dynamic_cast< TextEntry * >( FindChildByName( "TitleInput" ) );
+ m_pResolutionNoteLabel = dynamic_cast< CExLabel * >( FindChildByName( "ResolutionNoteLabel" ) );
+ m_pEnterANameLabel = dynamic_cast< CExLabel * >( FindChildByName( "EnterANameLabel" ) );
+ m_pVideoModeLabel = dynamic_cast< CExLabel * >( FindChildByName( "VideoModeLabel" ) );
+ m_pCodecLabel = dynamic_cast< CExLabel * >( FindChildByName( "CodecLabel" ) );
+ m_pMotionBlurLabel = dynamic_cast< CExLabel * >( FindChildByName( "MotionBlurLabel" ) );
+ m_pMotionBlurSlider = dynamic_cast< Slider * >( FindChildByName( "MotionBlurSlider" ) );
+ m_pQualityLabel = dynamic_cast< CExLabel * >( FindChildByName( "QualityLabel" ) );
+ m_pQualitySlider = dynamic_cast< Slider * >( FindChildByName( "QualitySlider" ) );
+ m_pTitleLabel = dynamic_cast< CExLabel * >( FindChildByName( "TitleLabel" ) );
+ m_pRenderButton = dynamic_cast< CExButton * >( FindChildByName( "RenderButton" ) );
+ m_pCancelButton = dynamic_cast< CExButton * >( FindChildByName( "CancelButton" ) );
+ m_pBgPanel = dynamic_cast< EditablePanel * >( FindChildByName( "BGPanel" ) );
+ m_pMotionBlurCheck = dynamic_cast< CheckButton * >( FindChildByName( "MotionBlurEnabled" ) );
+ m_pQualityPresetLabel = dynamic_cast< CExLabel * >( FindChildByName( "QualityPresetLabel" ) );
+ m_pQualityPresetCombo = dynamic_cast< vgui::ComboBox * >( FindChildByName( "QualityPresetCombo" ) );
+ m_pCodecCombo = dynamic_cast< vgui::ComboBox * >( FindChildByName( "CodecCombo" ) );
+ m_pVideoModesCombo = dynamic_cast< vgui::ComboBox * >( FindChildByName( "VideoModeCombo" ) );
+ m_pEstimateTimeLabel = dynamic_cast< CExLabel * >( FindChildByName( "EstimateTimeLabel" ) );
+ m_pEstimateFileLabel = dynamic_cast< CExLabel * >( FindChildByName( "EstimateFileLabel" ) );
+ m_pSeparator = FindChildByName( "SeparatorLine" );
+ m_pGlowEnabledCheck = dynamic_cast< CheckButton * >( FindChildByName( "GlowEnabled" ) );
+ m_pLockWarningLabel = dynamic_cast< CExLabel * >( FindChildByName( "LockWarningLabel" ) );
+
+#if defined( TF_CLIENT_DLL )
+ if ( m_pBgPanel )
+ {
+ m_pBgPanel->SetPaintBackgroundType( 2 ); // Rounded.
+ }
+#endif
+
+ AddControlToAutoLayout( m_pTitleLabel, false );
+
+ // The replay may be REPLAY_HANDLE_INVALID in the case that we are about to render all unrendered replays
+ if ( m_hReplay != REPLAY_HANDLE_INVALID )
+ {
+ CGenericClassBasedReplay *pReplay = GetGenericClassBasedReplay( m_hReplay );
+ m_pTitleText->SetText( pReplay->m_wszTitle );
+ m_pTitleText->SetVisible( true );
+ m_pTitleLabel->SetText( "#Replay_RenderReplay" );
+ m_pEnterANameLabel->SetVisible( true );
+
+ AddControlToAutoLayout( m_pEnterANameLabel, false );
+ }
+ else
+ {
+ m_pTitleLabel->SetText( "#Replay_RenderReplays" );
+ }
+
+ m_pTitleText->SelectAllOnFocusAlways( true );
+
+ AddControlToAutoLayout( m_pTitleText, false );
+
+ // Update controls based on preset
+ SetValuesFromQualityPreset();
+
+ // Set quit button if necessary
+ if ( m_bSetQuit )
+ {
+ m_pQuitWhenDoneCheck->SetSelected( true );
+ }
+
+ m_pPlayVoiceCheck->SetProportional( false );
+ m_pQuitWhenDoneCheck->SetProportional( false );
+ m_pShowAdvancedOptionsCheck->SetProportional( false );
+ m_pMotionBlurCheck->SetProportional( false );
+ m_pMotionBlurSlider->InvalidateLayout( false, true ); // Without this, the range labels show up with "..." because of an invalid font in TextImage::ApplySchemeSettings().
+ m_pExportRawCheck->SetProportional( false );
+ m_pQualitySlider->InvalidateLayout( false, true ); // Without this, the range labels show up with "..." because of an invalid font in TextImage::ApplySchemeSettings().
+
+ if ( m_pGlowEnabledCheck )
+ {
+ m_pGlowEnabledCheck->SetProportional( false );
+ }
+
+ // Fill in combo box with preset quality levels
+ const int nQualityPresetCount = ReplayVideo_GetQualityPresetCount();
+ m_pQualityPresetCombo->SetNumberOfEditLines( nQualityPresetCount );
+ for ( i = 0; i < nQualityPresetCount; ++i )
+ {
+ const ReplayQualityPreset_t &CurQualityPreset = ReplayVideo_GetQualityPreset( i );
+ m_pQualityPresetCombo->AddItem( CurQualityPreset.m_pName, NULL );
+ m_pQualityPresetCombo->SetItemEnabled( i, true );
+ }
+ m_pQualityPresetCombo->ActivateItem( m_pQualityPresetCombo->GetItemIDFromRow( m_iQualityPreset ) );
+
+ // Fill in combo box with video modes
+ int nScreenW = ScreenWidth();
+ int nScreenH = ScreenHeight();
+ const int nVidModeCount = ReplayVideo_GetVideoModeCount();
+ m_pVideoModesCombo->SetNumberOfEditLines( nVidModeCount );
+ bool bAtLeastOneVideoModeAdded = false;
+ bool bEnable = false;
+ bool bSkipped = false;
+
+ for ( i = 0; i < nVidModeCount; ++i )
+ {
+ // Only offer display modes less than the current window size
+ const ReplayVideoMode_t &CurVideoMode = ReplayVideo_GetVideoMode( i );
+
+ int nMw = CurVideoMode.m_nWidth;
+ int nMh = CurVideoMode.m_nHeight;
+
+ // Only display modes that fit in the current window
+ bEnable = ( nMw <= nScreenW && nMh <= nScreenH );
+ if (!bEnable)
+ bSkipped = true;
+
+ m_pVideoModesCombo->AddItem( CurVideoMode.m_pName, NULL );
+ m_pVideoModesCombo->SetItemEnabled( i, bEnable );
+
+ if (bEnable)
+ bAtLeastOneVideoModeAdded = true;
+ }
+ if ( bAtLeastOneVideoModeAdded )
+ {
+ m_pVideoModesCombo->ActivateItem( m_pVideoModesCombo->GetItemIDFromRow( 0 ) );
+ }
+
+ // fill in the combo box with codecs
+ const int nNumCodecs = ReplayVideo_GetCodecCount();
+ m_pCodecCombo->SetNumberOfEditLines( nNumCodecs );
+ for ( i = 0; i < nNumCodecs; ++i )
+ {
+ const ReplayCodec_t &CurCodec = ReplayVideo_GetCodec( i );
+ m_pCodecCombo->AddItem( CurCodec.m_pName, NULL );
+ m_pCodecCombo->SetItemEnabled( i, true );
+ }
+ m_pCodecCombo->ActivateItem( m_pCodecCombo->GetItemIDFromRow( 0 ) );
+
+ // now layout
+
+ // simplified options
+ AddControlToAutoLayout( m_pVideoModeLabel, false );
+ AddControlToAutoLayout( m_pVideoModesCombo, false );
+ // Show the note about "not all resolutions are available?"
+ if ( bSkipped && m_pResolutionNoteLabel )
+ {
+ m_pResolutionNoteLabel->SetVisible( true );
+ AddControlToAutoLayout( m_pResolutionNoteLabel, false );
+ }
+ // other simplified options
+ AddControlToAutoLayout( m_pQualityPresetLabel, false );
+ AddControlToAutoLayout( m_pQualityPresetCombo, false );
+ AddControlToAutoLayout( m_pEstimateTimeLabel, false );
+ AddControlToAutoLayout( m_pEstimateFileLabel, false );
+ AddControlToAutoLayout( m_pPlayVoiceCheck, false );
+ AddControlToAutoLayout( m_pShowAdvancedOptionsCheck, false );
+ AddControlToAutoLayout( m_pQuitWhenDoneCheck, false );
+
+ AddControlToAutoLayout( m_pLockWarningLabel, false );
+
+ // now advanced options
+ AddControlToAutoLayout( m_pSeparator, true );
+
+ AddControlToAutoLayout( m_pCodecLabel, true );
+ AddControlToAutoLayout( m_pCodecCombo, true );
+
+ if ( replay_rendersetting_motionblur_can_toggle.GetBool() )
+ {
+ AddControlToAutoLayout( m_pMotionBlurCheck, true );
+ }
+ else
+ {
+ m_pMotionBlurCheck->SetVisible( false );
+ }
+ AddControlToAutoLayout( m_pMotionBlurLabel, true );
+ AddControlToAutoLayout( m_pMotionBlurSlider, true );
+
+ AddControlToAutoLayout( m_pQualityLabel, true );
+ AddControlToAutoLayout( m_pQualitySlider, true );
+
+ AddControlToAutoLayout( m_pExportRawCheck, true );
+
+ if ( m_pGlowEnabledCheck )
+ {
+ AddControlToAutoLayout( m_pGlowEnabledCheck, true );
+ }
+
+ // these buttons always show up
+ AddControlToAutoLayout( m_pRenderButton, false );
+ AddControlToAutoLayout( m_pCancelButton, false );
+}
+
+void CReplayRenderDialog::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ m_pResolutionNoteLabel->SizeToContents(); // Get the proper height
+
+ int nY = m_nStartY;
+ Panel *pPrevPanel = NULL;
+ int nLastCtrlHeight = 0;
+
+ FOR_EACH_LL( m_lstControls, i )
+ {
+ LayoutInfo_t *pLayoutInfo = m_lstControls[ i ];
+ Panel *pPanel = pLayoutInfo->pPanel;
+
+ // should an advanced option be shown?
+ if ( pLayoutInfo->bAdvanced )
+ {
+ if ( pPanel->IsVisible() != m_bShowAdvancedOptions )
+ {
+ pPanel->SetVisible( m_bShowAdvancedOptions );
+ }
+ }
+
+ if ( !pPanel->IsVisible() )
+ continue;
+
+ if ( pPrevPanel && pLayoutInfo->nOffsetY >= 0 )
+ {
+ nY += pPrevPanel->GetTall() + pLayoutInfo->nOffsetY + m_nVerticalBuffer;
+ }
+
+ pPanel->SetPos( pLayoutInfo->nOffsetX ? pLayoutInfo->nOffsetX : m_nDefaultX, nY );
+
+ pPrevPanel = pPanel;
+ nLastCtrlHeight = pPanel->GetTall();
+ }
+
+ m_pBgPanel->SetTall( nY + nLastCtrlHeight + 2 * m_nVerticalBuffer );
+}
+
+void CReplayRenderDialog::Close()
+{
+ SetVisible( false );
+ MarkForDeletion();
+ TFModalStack()->PopModal( this );
+}
+
+void CReplayRenderDialog::OnCommand( const char *pCommand )
+{
+ if ( FStrEq( pCommand, "cancel" ) )
+ {
+ Close();
+ }
+ else if ( FStrEq( pCommand, "render" ) )
+ {
+ Close();
+ Render();
+ }
+ else
+ {
+ engine->ClientCmd( const_cast<char *>( pCommand ) );
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+void CReplayRenderDialog::Render()
+{
+ // Only complain about QuickTime if we aren't exporting raw TGA's/WAV
+ if ( !m_pExportRawCheck->IsSelected() )
+ {
+#ifndef USE_WEBM_FOR_REPLAY
+ if ( !g_pVideo || !g_pVideo->IsVideoSystemAvailable( VideoSystem::QUICKTIME ) )
+ {
+ ShowMessageBox( "#Replay_QuicktimeTitle", "#Replay_NeedQuicktime", "#GameUI_OK" );
+ return;
+ }
+
+ if ( g_pVideo->GetVideoSystemStatus( VideoSystem::QUICKTIME ) != VideoSystemStatus::OK )
+ {
+ if ( g_pVideo->GetVideoSystemStatus( VideoSystem::QUICKTIME ) == VideoSystemStatus::NOT_CURRENT_VERSION )
+ {
+ ShowMessageBox( "#Replay_QuicktimeTitle", "#Replay_NeedQuicktimeNewer", "#GameUI_OK" );
+ return;
+ }
+
+ ShowMessageBox( "#Replay_QuicktimeTitle", "#Replay_Err_QT_FailedToLoad", "#GameUI_OK" );
+ return;
+ }
+#endif
+ }
+
+ // Update convars from settings
+ const int nMotionBlurQuality = clamp( m_pMotionBlurSlider->GetValue(), 0, MAX_MOTION_BLUR_QUALITY );
+ replay_rendersetting_quitwhendone.SetValue( (int)m_pQuitWhenDoneCheck->IsSelected() );
+ replay_rendersetting_exportraw.SetValue( (int)m_pExportRawCheck->IsSelected() );
+ replay_rendersetting_motionblurquality.SetValue( nMotionBlurQuality );
+ replay_rendersetting_motionblurenabled.SetValue( replay_rendersetting_motionblur_can_toggle.GetBool() ? (int)m_pMotionBlurCheck->IsSelected() : 1 );
+ replay_rendersetting_encodingquality.SetValue( clamp( m_pQualitySlider->GetValue() * ReplayVideo_GetQualityInterval(), 0, 100 ) );
+
+ if ( m_pGlowEnabledCheck )
+ {
+ replay_rendersetting_renderglow.SetValue( m_pGlowEnabledCheck->IsSelected() );
+ }
+
+ ConVarRef replay_voice_during_playback( "replay_voice_during_playback" );
+ if ( replay_voice_during_playback.IsValid() && m_pPlayVoiceCheck->IsEnabled() )
+ {
+ replay_voice_during_playback.SetValue( (int)m_pPlayVoiceCheck->IsSelected() );
+ }
+
+ // Setup parameters for render
+ RenderMovieParams_t params;
+ params.m_hReplay = m_hReplay;
+ params.m_iPerformance = m_iPerformance; // Use performance passed in from details panel
+ params.m_bQuitWhenFinished = m_pQuitWhenDoneCheck->IsSelected();
+ params.m_bExportRaw = m_pExportRawCheck->IsSelected();
+ m_pTitleText->GetText( params.m_wszTitle, sizeof( params.m_wszTitle ) );
+#ifdef USE_WEBM_FOR_REPLAY
+ V_strcpy( params.m_szExtension, ".webm" ); // Use .webm
+#else
+ V_strcpy( params.m_szExtension, ".mov" ); // Use .mov for Quicktime
+#endif
+
+ const int iRes = m_pVideoModesCombo->GetActiveItem();
+ const ReplayVideoMode_t &VideoMode = ReplayVideo_GetVideoMode( iRes );
+
+ params.m_Settings.m_bMotionBlurEnabled = replay_rendersetting_motionblurenabled.GetBool();
+ params.m_Settings.m_bAAEnabled = replay_rendersetting_motionblurenabled.GetBool();
+ params.m_Settings.m_nMotionBlurQuality = nMotionBlurQuality;
+ params.m_Settings.m_nWidth = VideoMode.m_nWidth;
+ params.m_Settings.m_nHeight = VideoMode.m_nHeight;
+ params.m_Settings.m_FPS.SetFPS( VideoMode.m_nBaseFPS, VideoMode.m_bNTSCRate );
+ params.m_Settings.m_Codec = ReplayVideo_GetCodec( m_pCodecCombo->GetActiveItem() ).m_nCodecId;
+ params.m_Settings.m_nEncodingQuality = replay_rendersetting_encodingquality.GetInt();
+ params.m_Settings.m_bRaw = m_pExportRawCheck->IsSelected();
+
+ // Calculate the framerate for the engine - for each engine frame, we need the # of motion blur timesteps,
+ // x 2, since the shutter is open for nNumMotionBlurTimeSteps and closed for nNumMotionBlurTimeSteps,
+ // with the engine frame centered in the shutter open state (ie when we're half way through the motion blur
+ // timesteps). Antialiasing does not factor in here because it doesn't require extra frames - the AA jitter
+ // is interwoven in with the motion sub-frames.
+ const int nNumMotionBlurTimeSteps = ( params.m_Settings.m_bMotionBlurEnabled ) ? CReplayRenderer::GetNumMotionBlurTimeSteps( params.m_Settings.m_nMotionBlurQuality ) : 1;
+
+ if ( params.m_Settings.m_bMotionBlurEnabled )
+ {
+ params.m_flEngineFps = 2 * nNumMotionBlurTimeSteps * params.m_Settings.m_FPS.GetFPS();
+ }
+ else
+ {
+ Assert( nNumMotionBlurTimeSteps == 1 );
+ params.m_flEngineFps = params.m_Settings.m_FPS.GetFPS();
+ }
+
+ // Close the browser
+ extern void ReplayUI_CloseReplayBrowser();
+ ReplayUI_CloseReplayBrowser();
+
+ // Hide the console
+ engine->ExecuteClientCmd( "hideconsole" );
+
+ // Stats tracking.
+ GetReplayGameStatsHelper().SW_ReplayStats_WriteRenderDataStart( params, this );
+
+ // Render the movie
+ g_pReplayMovieManager->RenderMovie( params );
+}
+
+void CReplayRenderDialog::OnKeyCodeTyped( vgui::KeyCode code )
+{
+ if( code == KEY_ENTER )
+ {
+ OnCommand( "render" );
+ }
+ else if ( code == KEY_ESCAPE )
+ {
+ MarkForDeletion();
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ }
+}
+
+void CReplayRenderDialog::OnThink()
+{
+ if ( m_pEstimateTimeLabel == NULL || m_pEstimateFileLabel == NULL )
+ return;
+
+ // The replay may be NULL if this dialog is created by 'save all' from the quit confirmation dialog. In this
+ // case, we don't want to a replay-specific time estimate anyway, so we can just early out here.
+ CGenericClassBasedReplay *pReplay = ToGenericClassBasedReplay( g_pReplayManager->GetReplay( m_hReplay) );
+ if ( !pReplay )
+ return;
+
+ const int nMotionBlurQuality = clamp( m_pMotionBlurSlider->GetValue(), 0, MAX_MOTION_BLUR_QUALITY );
+ const int nCodecQuality = clamp( m_pQualitySlider->GetValue(), 0, ReplayVideo_GetQualityRange() );
+ VideoEncodeCodec::EVideoEncodeCodec_t eCodec = ReplayVideo_GetCodec( m_pCodecCombo->GetActiveItem() ).m_nCodecId;
+
+ // fFrameSize is the scale factor based on the size of the rendered frame.
+ const int iRes = m_pVideoModesCombo->GetActiveItem();
+ const ReplayVideoMode_t &VideoMode = ReplayVideo_GetVideoMode( iRes );
+ float fFrameSize = (float)(VideoMode.m_nWidth * VideoMode.m_nHeight)/(float)(640*480);
+
+
+ float flEstimatedFileSize = 0;
+ float flEstimatedRenderTime_Min = 0;
+
+ static float mjpegToMotionBlurMultiplierTable[] = { 2.0f, 3.0f, 5.5f, 12.0f };
+ static float h264ToMotionBlurMultiplierTable[] = { 2.8f, 4.2f, 6.4f, 13.0f };
+ static float webmToMotionBlurMultiplierTable[] = { 2.8f, 4.2f, 6.4f, 13.0f };
+
+ static float mjpegToQualityMultiplierTable[] = { 620.0f, 736.0f, 1284.0f, 2115.0f, 3028.0f };
+ static float h264ToQualityMultiplierTable[] = { 276.0f, 384.0f, 595.0f, 1026.0f, 1873.0f };
+ static float webmToQualityMultiplierTable[] = { 125.0f, 250.0f, 312.0f, 673.0f, 1048.0f };
+
+ switch ( eCodec )
+ {
+ case VideoEncodeCodec::WEBM_CODEC:
+ flEstimatedFileSize = pReplay->m_flLength * webmToQualityMultiplierTable[nCodecQuality]*fFrameSize;
+ flEstimatedRenderTime_Min = pReplay->m_flLength * webmToMotionBlurMultiplierTable[nMotionBlurQuality];
+ break;
+ case VideoEncodeCodec::H264_CODEC:
+ flEstimatedFileSize = pReplay->m_flLength * h264ToQualityMultiplierTable[nCodecQuality];
+ flEstimatedRenderTime_Min = pReplay->m_flLength * h264ToMotionBlurMultiplierTable[nMotionBlurQuality];
+ break;
+ case VideoEncodeCodec::MJPEG_A_CODEC:
+ flEstimatedFileSize = pReplay->m_flLength * mjpegToQualityMultiplierTable[nCodecQuality];
+ flEstimatedRenderTime_Min = pReplay->m_flLength * mjpegToMotionBlurMultiplierTable[nMotionBlurQuality];
+ break;
+ }
+
+ float flEstimatedRenderTime_Max = flEstimatedRenderTime_Min * 3.0f;
+
+ // @todo Tom Bui: if this goes into hours, we are in trouble...
+ wchar_t wzFileSize[64];
+ _snwprintf( wzFileSize, ARRAYSIZE( wzFileSize ), L"%d", (int)flEstimatedFileSize );
+ wchar_t wzTimeMin[64];
+ wchar_t wzTimeMax[64];
+ g_pVGuiLocalize->ConvertANSIToUnicode( CReplayTime::FormatTimeString( flEstimatedRenderTime_Min ), wzTimeMin, sizeof( wzTimeMin ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( CReplayTime::FormatTimeString( flEstimatedRenderTime_Max ), wzTimeMax, sizeof( wzTimeMax ) );
+
+ wchar_t wzText[256] = L"";
+
+ g_pVGuiLocalize->ConstructString( wzText, sizeof( wzText ), g_pVGuiLocalize->Find( "#Replay_RenderEstimate_File" ), 1,
+ wzFileSize,
+ wzTimeMin,
+ wzTimeMax );
+ m_pEstimateFileLabel->SetText( wzText );
+
+ g_pVGuiLocalize->ConstructString( wzText, sizeof( wzText ), g_pVGuiLocalize->Find( "#Replay_RenderEstimate_Time" ), 2,
+ wzTimeMin,
+ wzTimeMax );
+ m_pEstimateTimeLabel->SetText( wzText );
+}
+
+void CReplayRenderDialog::OnTextChanged( KeyValues *data )
+{
+ Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
+ vgui::ComboBox *pComboBox = dynamic_cast<vgui::ComboBox *>( pPanel );
+
+ if ( pComboBox == m_pQualityPresetCombo )
+ {
+ m_iQualityPreset = m_pQualityPresetCombo->GetActiveItem();
+ SetValuesFromQualityPreset();
+ }
+}
+
+void CReplayRenderDialog::OnCheckButtonChecked( vgui::Panel *panel )
+{
+ if ( panel == m_pShowAdvancedOptionsCheck )
+ {
+ m_bShowAdvancedOptions = m_pShowAdvancedOptionsCheck->IsSelected();
+ InvalidateLayout( true, false );
+ }
+}
+
+void CReplayRenderDialog::OnSetFocus()
+{
+ m_pTitleText->RequestFocus();
+}
+
+void ReplayUI_ShowRenderDialog( Panel* pParent, ReplayHandle_t hReplay, bool bSetQuit, int iPerformance )
+{
+ CReplayRenderDialog *pRenderDialog = vgui::SETUP_PANEL( new CReplayRenderDialog( pParent, hReplay, bSetQuit, iPerformance ) );
+
+ pRenderDialog->SetVisible( true );
+ pRenderDialog->MakePopup();
+ pRenderDialog->MoveToFront();
+ pRenderDialog->SetKeyBoardInputEnabled( true );
+ pRenderDialog->SetMouseInputEnabled( true );
+ TFModalStack()->PushModal( pRenderDialog );
+}
+
+#endif
diff --git a/sp/src/game/client/replay/vgui/replaybrowserrenderdialog.h b/sp/src/game/client/replay/vgui/replaybrowserrenderdialog.h
new file mode 100644
index 00000000..e3cd75b3
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaybrowserrenderdialog.h
@@ -0,0 +1,109 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef REPLAYBROWSER_RENDERDIALOG_H
+#define REPLAYBROWSER_RENDERDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "replaybrowserbasepanel.h"
+#include "vgui/IScheme.h"
+#include "vgui_controls/CheckButton.h"
+#include "vgui_controls/ComboBox.h"
+#include "vgui_controls/Slider.h"
+#include "replay/replayhandle.h"
+
+using namespace vgui;
+
+class CExLabel;
+class CExButton;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CReplayRenderDialog : public CReplayBasePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayRenderDialog, CReplayBasePanel );
+public:
+ CReplayRenderDialog( Panel *pParent, ReplayHandle_t hReplay, bool bSetQuit, int iPerformance );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnKeyCodeTyped( vgui::KeyCode code );
+ virtual void OnThink();
+
+ MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
+ MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel );
+
+private:
+ MESSAGE_FUNC( OnSetFocus, "SetFocus" );
+
+ void Close();
+ void Render();
+ void ValidateRenderData();
+ void UpdateControlsValues();
+ void AddControlToAutoLayout( Panel *pPanel, bool bAdvanced );
+ void SetValuesFromQualityPreset();
+
+ bool m_bShowAdvancedOptions;
+ int m_iQualityPreset;
+ ReplayHandle_t m_hReplay;
+ bool m_bSetQuit;
+ int m_iPerformance;
+ CheckButton *m_pPlayVoiceCheck;
+ CheckButton *m_pShowAdvancedOptionsCheck;
+ CheckButton *m_pQuitWhenDoneCheck;
+ CheckButton *m_pExportRawCheck;
+ CExButton *m_pCancelButton;
+ CExButton *m_pRenderButton;
+ TextEntry *m_pTitleText;
+ ComboBox *m_pVideoModesCombo;
+ ComboBox *m_pCodecCombo;
+ CExLabel *m_pQualityPresetLabel;
+ ComboBox *m_pQualityPresetCombo;
+ CExLabel *m_pResolutionNoteLabel;
+ CExLabel *m_pEnterANameLabel;
+ CExLabel *m_pVideoModeLabel;
+ CExLabel *m_pTitleLabel;
+ CExLabel *m_pLockWarningLabel;
+ CExLabel *m_pCodecLabel;
+ CExLabel *m_pEstimateTimeLabel;
+ CExLabel *m_pEstimateFileLabel;
+ CheckButton *m_pMotionBlurCheck;
+ CExLabel *m_pMotionBlurLabel;
+ Slider *m_pMotionBlurSlider;
+ CExLabel *m_pQualityLabel;
+ Slider *m_pQualitySlider;
+ EditablePanel *m_pBgPanel;
+ Panel *m_pSeparator;
+ CheckButton *m_pGlowEnabledCheck;
+
+ struct LayoutInfo_t
+ {
+ Panel *pPanel;
+ int nOffsetX;
+ int nOffsetY;
+ bool bAdvanced;
+ };
+
+ CUtlLinkedList< LayoutInfo_t * > m_lstControls;
+ CPanelAnimationVarAliasType( int, m_nStartY, "start_y", "0", "proportional_ypos" );
+ CPanelAnimationVarAliasType( int, m_nVerticalBuffer, "vertical_buffer", "0", "proportional_ypos" );
+ CPanelAnimationVarAliasType( int, m_nDefaultX, "default_x", "0", "proportional_xpos" );
+
+ friend class CReplayGameStatsHelper;
+};
+
+//-----------------------------------------------------------------------------
+
+void ReplayUI_ShowRenderDialog( Panel* pParent, ReplayHandle_t hReplay, bool bSetQuit, int iPerformance );
+
+//-----------------------------------------------------------------------------
+
+#endif // REPLAYBROWSER_RENDERDIALOG_H
diff --git a/sp/src/game/client/replay/vgui/replayconfirmquitdlg.cpp b/sp/src/game/client/replay/vgui/replayconfirmquitdlg.cpp
new file mode 100644
index 00000000..9cea0c90
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayconfirmquitdlg.cpp
@@ -0,0 +1,167 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replayconfirmquitdlg.h"
+#include "vgui_controls/TextImage.h"
+#include "vgui_controls/CheckButton.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui/IInput.h"
+#include "vgui/ISurface.h"
+#include "ienginevgui.h"
+#include "replay/genericclassbased_replay.h"
+#include "replaybrowserrenderdialog.h"
+#include "econ/econ_controls.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+ConVar replay_quitmsg_dontaskagain( "replay_quitmsg_dontaskagain", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "The replay system will ask you to render your replays on quit, unless this cvar is 1.", true, 0, true, 1 );
+
+//-----------------------------------------------------------------------------
+
+CReplayConfirmQuitDialog::CReplayConfirmQuitDialog( Panel *pParent )
+: BaseClass( pParent, "confirmquitdlg" ),
+ m_pDontShowAgain( NULL ),
+ m_pQuitButton( NULL )
+{
+ SetScheme( "ClientScheme" );
+ InvalidateLayout( true, true );
+}
+
+void CReplayConfirmQuitDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ // Link in TF scheme
+ extern IEngineVGui *enginevgui;
+ vgui::HScheme pTFScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
+ SetScheme( pTFScheme );
+ SetProportional( true );
+
+ BaseClass::ApplySchemeSettings( vgui::scheme()->GetIScheme( pTFScheme ) );
+
+ LoadControlSettings( "Resource/UI/replaybrowser/confirmquitdlg.res", "GAME" );
+
+ m_pDontShowAgain = dynamic_cast< CheckButton * >( FindChildByName( "DontShowThisAgainCheckbox" ) );
+ m_pQuitButton = dynamic_cast< CExButton * >( FindChildByName( "QuitButton" ) );
+
+ if ( m_pQuitButton )
+ {
+ m_pQuitButton->GetTextImage()->ClearColorChangeStream();
+ m_pQuitButton->GetTextImage()->AddColorChange( Color(200,80,60,255), 0 );
+ }
+}
+
+void CReplayConfirmQuitDialog::OnCommand( const char *pCommand )
+{
+ // Store the setting of our "never show this again" checkbox if the user picked anything
+ // except cancel.
+ if ( !FStrEq( pCommand, "cancel" ) && m_pDontShowAgain && m_pDontShowAgain->IsSelected() )
+ {
+ replay_quitmsg_dontaskagain.SetValue( 1 );
+ }
+
+ if ( FStrEq( pCommand, "rendernow_delay" ) )
+ {
+ // Delete this
+ SetVisible( false );
+ MarkForDeletion();
+
+ // Render all unrendered replays now
+ ReplayUI_ShowRenderDialog( NULL, REPLAY_HANDLE_INVALID, true, -1 );
+ }
+ else if ( FStrEq( pCommand, "rendernow" ) )
+ {
+ // Sometimes this message comes in just before input is processed when using a controller
+ // Refire after a delay
+ PostMessage( this, new KeyValues( "Command", "command", "rendernow_delay" ), 0.001f );
+ }
+ else if ( FStrEq( pCommand, "quit" ) )
+ {
+ MarkForDeletion();
+ engine->ClientCmd_Unrestricted( "quit\n" );
+ }
+ else if ( FStrEq( pCommand, "cancel" ) )
+ {
+ MarkForDeletion();
+ }
+ else if ( FStrEq( pCommand, "gotoreplays"))
+ {
+ // "Go to replays"
+ MarkForDeletion();
+ engine->ClientCmd( "replay_reloadbrowser" );
+ }
+}
+
+void CReplayConfirmQuitDialog::OnKeyCodeTyped( vgui::KeyCode code )
+{
+ if ( code == KEY_ESCAPE )
+ {
+ OnCommand( "cancel" );
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ }
+}
+
+void CReplayConfirmQuitDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B )
+ {
+ OnCommand( "cancel" );
+ }
+ else if ( GetBaseButtonCode( code ) == KEY_XBUTTON_A )
+ {
+ OnCommand( "quit" );
+ }
+ else if ( GetBaseButtonCode( code ) == KEY_XBUTTON_X )
+ {
+ if ( m_pDontShowAgain )
+ {
+ m_pDontShowAgain->SetSelected( !m_pDontShowAgain->IsSelected() );
+ }
+ }
+ else if ( GetBaseButtonCode( code ) == KEY_XBUTTON_Y )
+ {
+ OnCommand( "gotoreplays" );
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed( code );
+ }
+}
+
+bool ReplayUI_ShowConfirmQuitDlg()
+{
+ if ( replay_quitmsg_dontaskagain.GetBool() )
+ return false;
+
+ CReplayConfirmQuitDialog *pConfirmQuitDlg = vgui::SETUP_PANEL( new CReplayConfirmQuitDialog( NULL ) );
+ if ( pConfirmQuitDlg )
+ {
+ vgui::surface()->PlaySound( "replay\\replaydialog_warn.wav" );
+
+ // Display the panel!
+ pConfirmQuitDlg->SetVisible( true );
+ pConfirmQuitDlg->MakePopup();
+ pConfirmQuitDlg->MoveToFront();
+ pConfirmQuitDlg->SetKeyBoardInputEnabled( true );
+ pConfirmQuitDlg->SetMouseInputEnabled( true );
+ TFModalStack()->PushModal( pConfirmQuitDlg );
+ }
+
+ return true;
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replayconfirmquitdlg.h b/sp/src/game/client/replay/vgui/replayconfirmquitdlg.h
new file mode 100644
index 00000000..b63921bf
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayconfirmquitdlg.h
@@ -0,0 +1,45 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef REPLAYBROWSER_CONFIRMQUITDLG_H
+#define REPLAYBROWSER_CONFIRMQUITDLG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "replaybrowserbasepanel.h"
+#include "vgui/IScheme.h"
+#include "vgui_controls/CheckButton.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CExButton;
+
+class CReplayConfirmQuitDialog : public CReplayBasePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayConfirmQuitDialog, CReplayBasePanel );
+public:
+ CReplayConfirmQuitDialog( Panel *pParent );
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void OnCommand( const char *pCommand );
+ virtual void OnKeyCodeTyped( vgui::KeyCode code );
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+
+private:
+ vgui::CheckButton *m_pDontShowAgain;
+ CExButton *m_pQuitButton;
+};
+
+//-----------------------------------------------------------------------------
+
+bool ReplayUI_ShowConfirmQuitDlg();
+
+//-----------------------------------------------------------------------------
+
+#endif // REPLAYBROWSER_CONFIRMQUITDLG_H
diff --git a/sp/src/game/client/replay/vgui/replayinputpanel.cpp b/sp/src/game/client/replay/vgui/replayinputpanel.cpp
new file mode 100644
index 00000000..7ec78ab1
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayinputpanel.cpp
@@ -0,0 +1,245 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replayinputpanel.h"
+#include "replaybrowsermainpanel.h"
+#include "replay/replay.h"
+#include "vgui_controls/EditablePanel.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui/IInput.h"
+#include "vgui/ILocalize.h"
+#include "ienginevgui.h"
+#include "vgui_int.h"
+#include "vgui/ISurface.h"
+#include "iclientmode.h"
+#include "replay/ireplaymanager.h"
+#include "econ/econ_controls.h"
+
+#if defined( TF_CLIENT_DLL )
+#include "tf_item_inventory.h"
+#endif
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+static bool s_bPanelVisible = false;
+
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Purpose: Player input dialog for a replay
+//-----------------------------------------------------------------------------
+class CReplayInputPanel : public EditablePanel
+{
+private:
+ DECLARE_CLASS_SIMPLE( CReplayInputPanel, EditablePanel );
+
+public:
+ CReplayInputPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay );
+ ~CReplayInputPanel();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+ virtual void OnCommand( const char *command );
+ virtual void OnKeyCodePressed( KeyCode code );
+ virtual void OnKeyCodeTyped( KeyCode code );
+
+ MESSAGE_FUNC( OnSetFocus, "SetFocus" );
+
+private:
+ Panel *m_pDlg;
+ TextEntry *m_pTitleEntry;
+ ReplayHandle_t m_hReplay;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: CReplayInputPanel implementation
+//-----------------------------------------------------------------------------
+CReplayInputPanel::CReplayInputPanel( Panel *pParent, const char *pName, ReplayHandle_t hReplay )
+: BaseClass( pParent, pName ),
+ m_hReplay( hReplay ),
+ m_pDlg( NULL ),
+ m_pTitleEntry( NULL )
+{
+ SetScheme( "ClientScheme" );
+ SetProportional( true );
+}
+
+CReplayInputPanel::~CReplayInputPanel()
+{
+}
+
+void CReplayInputPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replayinputpanel.res", "GAME" );
+
+ // Cache off the dlg pointer
+ m_pDlg = FindChildByName( "Dlg" );
+
+ // Setup some action sigsies
+ m_pDlg->FindChildByName( "SaveButton" )->AddActionSignalTarget( this );
+ m_pDlg->FindChildByName( "CancelButton" )->AddActionSignalTarget( this );
+
+ m_pTitleEntry = static_cast< TextEntry * >( m_pDlg->FindChildByName( "TitleInput" ) );
+ m_pTitleEntry->SelectAllOnFocusAlways( true );
+ m_pTitleEntry->SetSelectionBgColor( GetSchemeColor( "Yellow", Color( 255, 255, 255, 255), pScheme ) );
+ m_pTitleEntry->SetSelectionTextColor( Color( 255, 255, 255, 255 ) );
+
+ if ( m_hReplay != REPLAY_HANDLE_INVALID )
+ {
+ CReplay *pReplay = g_pReplayManager->GetReplay( m_hReplay );
+ m_pTitleEntry->SetText( pReplay->m_wszTitle );
+ }
+}
+
+void CReplayInputPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ SetWide( ScreenWidth() );
+ SetTall( ScreenHeight() );
+
+ // Center
+ m_pDlg->SetPos( ( ScreenWidth() - m_pDlg->GetWide() ) / 2, ( ScreenHeight() - m_pDlg->GetTall() ) / 2 );
+}
+
+void CReplayInputPanel::OnKeyCodeTyped( KeyCode code )
+{
+ if ( code == KEY_ESCAPE )
+ {
+ OnCommand( "cancel" );
+ }
+
+ BaseClass::OnKeyCodeTyped( code );
+}
+
+void CReplayInputPanel::OnKeyCodePressed( KeyCode code )
+{
+ if ( code == KEY_ENTER )
+ {
+ OnCommand( "save" );
+ }
+
+ BaseClass::OnKeyCodePressed( code );
+}
+
+void CReplayInputPanel::OnSetFocus()
+{
+ m_pTitleEntry->RequestFocus();
+}
+
+void CReplayInputPanel::OnCommand( const char *command )
+{
+ bool bCloseWindow = false;
+ bool bLocalPlayerDead = false;
+ if ( !Q_strnicmp( command, "save", 4 ) )
+ {
+ if ( m_hReplay != REPLAY_HANDLE_INVALID )
+ {
+ // Store the title
+ CReplay *pReplay = g_pReplayManager->GetReplay( m_hReplay );
+ if ( pReplay )
+ {
+ m_pTitleEntry->GetText( pReplay->m_wszTitle, sizeof( pReplay->m_wszTitle ) );
+ }
+
+ // Cache to disk
+ g_pReplayManager->FlagReplayForFlush( pReplay, false );
+
+ // Add the replay to the browser
+ CReplayBrowserPanel* pReplayBrowser = ReplayUI_GetBrowserPanel();
+ if ( pReplayBrowser )
+ {
+ pReplayBrowser->OnSaveReplay( m_hReplay );
+ }
+
+ // Display a message - if we somehow disconnect, we can crash here if local player isn't checked
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pLocalPlayer )
+ {
+ g_pClientMode->DisplayReplayMessage( pLocalPlayer->IsAlive() ? "#Replay_ReplaySavedAlive" : "#Replay_ReplaySavedDead", -1.0f, false, "replay\\saved.wav", false );
+
+ // Check to see if player's dead - used later to determine if we should show items window
+ bLocalPlayerDead = !pLocalPlayer->IsAlive();
+ }
+ }
+ bCloseWindow = true;
+ }
+ else if ( !Q_strnicmp( command, "cancel", 6 ) )
+ {
+ bCloseWindow = true;
+ }
+
+ // Close the window?
+ if ( bCloseWindow )
+ {
+ s_bPanelVisible = false;
+ SetVisible( false );
+ TFModalStack()->PopModal( this );
+ MarkForDeletion();
+
+ // This logic is perhaps a smidge of a hack. We have to be careful about executing "gameui_hide"
+ // since it will hide the item pickup panel. If there are no items to be picked up, we can safely
+ // hide the gameui panel, but we have to call CheckForRoomAndForceDiscard() (as ShowItemsPickedUp()
+ // does if no items are picked up). Otherwise, skip the "gameui_hide" call and show the item pickup
+ // panel.
+#if defined( TF_CLIENT_DLL )
+ if ( TFInventoryManager()->GetNumItemPickedUpItems() == 0 )
+ {
+ TFInventoryManager()->CheckForRoomAndForceDiscard();
+ engine->ClientCmd_Unrestricted( "gameui_hide" );
+ }
+ else if ( bLocalPlayerDead )
+ {
+ // Now show the items pickup screen if player's dead
+ TFInventoryManager()->ShowItemsPickedUp();
+ }
+#endif
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool IsReplayInputPanelVisible()
+{
+ return s_bPanelVisible;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ShowReplayInputPanel( ReplayHandle_t hReplay )
+{
+ vgui::DHANDLE< CReplayInputPanel > hReplayInputPanel;
+
+ hReplayInputPanel = vgui::SETUP_PANEL( new CReplayInputPanel( NULL, "ReplayInputPanel", hReplay ) );
+ hReplayInputPanel->SetVisible( true );
+ hReplayInputPanel->MakePopup();
+ hReplayInputPanel->MoveToFront();
+ hReplayInputPanel->SetKeyBoardInputEnabled(true);
+ hReplayInputPanel->SetMouseInputEnabled(true);
+ TFModalStack()->PushModal( hReplayInputPanel );
+ engine->ClientCmd_Unrestricted( "gameui_hide" );
+ s_bPanelVisible = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Test the replay input dialog
+//-----------------------------------------------------------------------------
+CON_COMMAND_F( open_replayinputpanel, "Open replay input panel test", FCVAR_NONE )
+{
+ ShowReplayInputPanel( NULL );
+}
+
+#endif
diff --git a/sp/src/game/client/replay/vgui/replayinputpanel.h b/sp/src/game/client/replay/vgui/replayinputpanel.h
new file mode 100644
index 00000000..2143e9a8
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayinputpanel.h
@@ -0,0 +1,25 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#ifndef REPLAY_INPUT_PANEL_H
+#define REPLAY_INPUT_PANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+
+#include "replay/replayhandle.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Show Replay input panel for entering a title, etc.
+//-----------------------------------------------------------------------------
+void ShowReplayInputPanel( ReplayHandle_t hReplay );
+
+//-----------------------------------------------------------------------------
+// Purpose: Is the panel visible?
+//-----------------------------------------------------------------------------
+bool IsReplayInputPanelVisible();
+
+#endif // REPLAY_INPUT_PANEL_H
diff --git a/sp/src/game/client/replay/vgui/replaymessagepanel.cpp b/sp/src/game/client/replay/vgui/replaymessagepanel.cpp
new file mode 100644
index 00000000..a03623c9
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaymessagepanel.cpp
@@ -0,0 +1,375 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//----------------------------------------------------------------------------------------
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replaymessagepanel.h"
+#include "vgui_controls/CheckButton.h"
+#include "ienginevgui.h"
+#include "vgui_controls/PHandle.h"
+#include "econ/econ_controls.h"
+#if defined( CSTRIKE_DLL )
+# include "cstrike/clientmode_csnormal.h"
+#elif defined( TF_CLIENT_DLL )
+# include "tf/clientmode_tf.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+//-----------------------------------------------------------------------------
+
+#if _DEBUG
+CON_COMMAND( testreplaymessagepanel, "" )
+{
+ CReplayMessagePanel *pPanel = new CReplayMessagePanel( "#Replay_StartRecord", replay_msgduration_misc.GetFloat(), rand()%2==0);
+ pPanel->Show();
+}
+
+CON_COMMAND( testreplaymessagedlg, "" )
+{
+ CReplayMessageDlg *pPanel = SETUP_PANEL( new CReplayMessageDlg( "text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text." ) );
+ pPanel->SetVisible( true );
+ pPanel->MakePopup();
+ pPanel->MoveToFront();
+ pPanel->SetKeyBoardInputEnabled( true );
+ pPanel->SetMouseInputEnabled( true );
+ pPanel->RequestFocus();
+ engine->ClientCmd_Unrestricted( "gameui_hide" );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+typedef vgui::DHANDLE< CReplayMessagePanel > ReplayMessagePanelHandle_t;
+static CUtlVector< ReplayMessagePanelHandle_t > g_vecReplayMessagePanels;
+
+//-----------------------------------------------------------------------------
+
+ConVar replay_msgduration_startrecord( "replay_msgduration_startrecord", "6", FCVAR_DONTRECORD, "Duration for start record message.", true, 0.0f, true, 10.0f );
+ConVar replay_msgduration_stoprecord( "replay_msgduration_stoprecord", "6", FCVAR_DONTRECORD, "Duration for stop record message.", true, 0.0f, true, 10.0f );
+ConVar replay_msgduration_replaysavailable( "replay_msgduration_replaysavailable", "6", FCVAR_DONTRECORD, "Duration for replays available message.", true, 0.0f, true, 10.0f );
+ConVar replay_msgduration_error( "replay_msgduration_error", "6", FCVAR_DONTRECORD, "Duration for replays available message.", true, 0.0f, true, 10.0f );
+ConVar replay_msgduration_misc( "replay_msgduration_misc", "5", FCVAR_DONTRECORD, "Duration for misc replays messages (server errors and such).", true, 0.0f, true, 10.0f );
+ConVar replay_msgduration_connectrecording( "replay_msgduration_connectrecording", "8", FCVAR_DONTRECORD, "Duration for the message that pops up when you connect to a server already recording replays.", true, 0.0f, true, 15.0f );
+
+//-----------------------------------------------------------------------------
+
+CReplayMessageDlg::CReplayMessageDlg( const char *pText )
+: BaseClass( NULL, "ReplayMessageDlg" ),
+ m_pOKButton( NULL ),
+ m_pDlg( NULL ),
+ m_pMsgLabel( NULL )
+{
+ InvalidateLayout( true, true );
+
+ m_pMsgLabel->SetText( pText );
+}
+
+CReplayMessageDlg::~CReplayMessageDlg()
+{
+}
+
+void CReplayMessageDlg::ApplySchemeSettings( IScheme *pScheme )
+{
+ // Link in TF scheme
+ extern IEngineVGui *enginevgui;
+ vgui::HScheme pTFScheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
+ SetScheme( pTFScheme );
+ SetProportional( true );
+
+ BaseClass::ApplySchemeSettings( vgui::scheme()->GetIScheme( pTFScheme ) );
+
+ LoadControlSettings( "resource/ui/replaymessagedlg.res", "GAME" );
+
+ m_pDlg = FindChildByName( "Dlg" );
+
+ m_pOKButton = dynamic_cast< CExButton * >( m_pDlg->FindChildByName( "OKButton" ) );
+ m_pMsgLabel = dynamic_cast< CExLabel * >( m_pDlg->FindChildByName( "TextLabel") );
+
+ m_pOKButton->AddActionSignalTarget( this );
+}
+
+void CReplayMessageDlg::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ SetWide( ScreenWidth() );
+ SetTall( ScreenHeight() );
+
+ // Center dlg on screen
+ m_pDlg->SetPos( ( ScreenWidth() - m_pDlg->GetWide() ) / 2, ( ScreenHeight() - m_pDlg->GetTall() ) / 2 );
+
+ // Position OK below text label, centered horizontally
+ int nButtonX = XRES(13);
+ int nButtonY = m_pDlg->GetTall() - m_pOKButton->GetTall() - YRES( 10 );
+ m_pOKButton->SetPos( nButtonX, nButtonY );
+}
+
+void CReplayMessageDlg::Close()
+{
+ // Hide / delete / hide game UI
+ SetVisible( false );
+ MarkForDeletion();
+ engine->ClientCmd_Unrestricted( "gameui_hide" );
+}
+
+void CReplayMessageDlg::OnCommand( const char *pCommand )
+{
+ if ( FStrEq( pCommand, "close" ) )
+ {
+ Close();
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+void CReplayMessageDlg::OnKeyCodeTyped( KeyCode nCode )
+{
+ switch ( nCode )
+ {
+ case KEY_ESCAPE:
+ case KEY_SPACE:
+ case KEY_ENTER:
+ Close();
+ return;
+ }
+
+ BaseClass::OnKeyCodeTyped( nCode );
+}
+
+//-----------------------------------------------------------------------------
+
+int CReplayMessagePanel::InstanceCount()
+{
+ return g_vecReplayMessagePanels.Count();
+}
+
+void CReplayMessagePanel::RemoveAll()
+{
+ FOR_EACH_VEC( g_vecReplayMessagePanels, i )
+ {
+ CReplayMessagePanel *pCurPanel = g_vecReplayMessagePanels[ i ];
+ pCurPanel->MarkForDeletion();
+ }
+
+ g_vecReplayMessagePanels.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+
+ReplayMessagePanelHandle_t GetReplayMessagePanelHandle( CReplayMessagePanel *pPanel )
+{
+ ReplayMessagePanelHandle_t hThis;
+ hThis = pPanel;
+ return hThis;
+}
+
+CReplayMessagePanel::CReplayMessagePanel( const char *pLocalizeName, float flDuration, bool bUrgent )
+: EditablePanel( g_pClientMode->GetViewport(), "ReplayMessagePanel" ),
+ m_bUrgent( bUrgent )
+{
+ m_flShowStartTime = 0;
+ m_flShowDuration = flDuration;
+
+ m_pMessageLabel = new CExLabel( this, "MessageLabel", pLocalizeName );
+ m_pReplayLabel = new CExLabel( this, "ReplayLabel", "" );
+ m_pIcon = new ImagePanel( this, "Icon" );
+
+#if defined( TF_CLIENT_DLL )
+ const char *pBorderName = bUrgent ? "ReplayFatLineBorderRedBGOpaque" : "ReplayFatLineBorderOpaque";
+ V_strncpy( m_szBorderName, pBorderName, sizeof( m_szBorderName ) );
+#endif
+
+ g_vecReplayMessagePanels.AddToTail( GetReplayMessagePanelHandle( const_cast< CReplayMessagePanel * >( this ) ) );
+
+ InvalidateLayout( true, true );
+
+ ivgui()->AddTickSignal( GetVPanel(), 10 );
+}
+
+CReplayMessagePanel::~CReplayMessagePanel()
+{
+ // CUtlVector<>::Find() vomits.
+ int iFind = g_vecReplayMessagePanels.InvalidIndex();
+ FOR_EACH_VEC( g_vecReplayMessagePanels, i )
+ {
+ if ( g_vecReplayMessagePanels[ i ].Get() == this )
+ {
+ iFind = i;
+ }
+ }
+
+ // Remove, if found.
+ if ( iFind != g_vecReplayMessagePanels.InvalidIndex() )
+ {
+ g_vecReplayMessagePanels.FastRemove( iFind );
+ }
+
+ ivgui()->RemoveTickSignal( GetVPanel() );
+}
+
+void CReplayMessagePanel::Show()
+{
+ m_pMessageLabel->SetVisible( true );
+
+ // Setup start time
+ m_flShowStartTime = gpGlobals->curtime;
+
+ m_pMessageLabel->MoveToFront();
+
+ SetAlpha( 0 );
+}
+
+inline float LerpScale( float flIn, float flInMin, float flInMax, float flOutMin, float flOutMax )
+{
+ float flDenom = flInMax - flInMin;
+ if ( flDenom == 0.0f )
+ return 0.0f;
+
+ float t = clamp( ( flIn - flInMin ) / flDenom, 0.0f, 1.0f );
+ return Lerp( t, flOutMin, flOutMax );
+}
+
+inline float SCurve( float t )
+{
+ t = clamp( t, 0.0f, 1.0f );
+ return t * t * (3 - 2*t);
+}
+
+void CReplayMessagePanel::OnTick()
+{
+ // Hide if taking screenshot
+ extern ConVar hud_freezecamhide;
+ extern bool IsTakingAFreezecamScreenshot();
+ if ( hud_freezecamhide.GetBool() && IsTakingAFreezecamScreenshot() )
+ {
+ SetVisible( false );
+ return;
+ }
+
+ // Delete the panel if life exceeded
+ const float flEndTime = m_flShowStartTime + m_flShowDuration;
+ if ( gpGlobals->curtime >= flEndTime )
+ {
+ SetVisible( false );
+ MarkForDeletion();
+ return;
+ }
+
+ SetVisible( true );
+
+ const float flFadeDuration = .4f;
+ float flAlpha;
+
+ // Fade out?
+ if ( gpGlobals->curtime >= flEndTime - flFadeDuration )
+ {
+ flAlpha = LerpScale( gpGlobals->curtime, flEndTime - flFadeDuration, flEndTime, 1.0f, 0.0f );
+ }
+
+ // Fade in?
+ else if ( gpGlobals->curtime <= m_flShowStartTime + flFadeDuration )
+ {
+ flAlpha = LerpScale( gpGlobals->curtime, m_flShowStartTime, m_flShowStartTime + flFadeDuration, 0.0f, 1.0f );
+ }
+
+ // Otherwise, we must be in between fade in/fade out
+ else
+ {
+ flAlpha = 1.0f;
+ }
+
+ SetAlpha( 255 * SCurve( flAlpha ) );
+}
+
+void CReplayMessagePanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replaymessage.res", "GAME" );
+
+#if defined( CSTRIKE_DLL )
+ SetPaintBackgroundEnabled( true );
+ SetPaintBorderEnabled( false );
+ SetPaintBackgroundType( 0 );
+ SetBgColor( pScheme->GetColor( m_bUrgent ? "DarkRed" : "DarkGray", Color( 255, 255, 255, 255 ) ) );
+#endif
+}
+
+void CReplayMessagePanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+#if defined( TF_CLIENT_DLL )
+ // Set the border if one was specified
+ if ( m_szBorderName[0] )
+ {
+ SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( m_szBorderName ) );
+ }
+#endif
+
+ // Adjust overall panel size depending on min-mode
+#if defined( TF_CLIENT_DLL )
+ extern ConVar cl_hud_minmode;
+ bool bMinMode = cl_hud_minmode.GetBool();
+#else
+ bool bMinMode = false;
+#endif
+ int nVerticalBuffer = bMinMode ? YRES(3) : YRES(5);
+ int nMessageLabelY = nVerticalBuffer;
+ int nVerticalOffsetBetweenPanels = YRES(6);
+
+ // Only display replay icon and "replay" label if this is the top-most (vertically) panel
+ // and we're not in min-mode
+ Assert( InstanceCount() > 0 );
+ if ( !InstanceCount() || bMinMode || g_vecReplayMessagePanels[ 0 ].Get() != this )
+ {
+ m_pIcon->SetTall( 0 );
+ m_pReplayLabel->SetTall( 0 );
+ nVerticalOffsetBetweenPanels = YRES(1);
+ }
+ else
+ {
+ m_pReplayLabel->SizeToContents();
+ nMessageLabelY += m_pReplayLabel->GetTall();
+ nVerticalOffsetBetweenPanels = YRES(6);
+ }
+
+ // Resize the message label to fit the text
+ m_pMessageLabel->SizeToContents();
+
+ // Adjust this panel's height to fit the label size
+ SetTall( nMessageLabelY + m_pMessageLabel->GetTall() + nVerticalBuffer );
+
+ // Set the message label's position
+ m_pMessageLabel->SetPos( XRES(8), nMessageLabelY );
+
+ // Get the bottom of the bottom-most message panel
+ int nMaxY = 0;
+ FOR_EACH_VEC( g_vecReplayMessagePanels, it )
+ {
+ CReplayMessagePanel *pPanel = g_vecReplayMessagePanels[ it ];
+ if ( pPanel == this )
+ continue;
+
+ int nX, nY;
+ pPanel->GetPos( nX, nY );
+ nMaxY = MAX( nMaxY, pPanel->GetTall() + nY );
+ }
+
+ // Adjust this panel's position to be below bottom-most panel
+ // NOTE: Intentionally using YRES() for xpos, since we want to match offsets in both x & y margins
+ SetPos( YRES(6), nMaxY + nVerticalOffsetBetweenPanels );
+}
+
+//-----------------------------------------------------------------------------
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replaymessagepanel.h b/sp/src/game/client/replay/vgui/replaymessagepanel.h
new file mode 100644
index 00000000..1d36c19e
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replaymessagepanel.h
@@ -0,0 +1,85 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//----------------------------------------------------------------------------------------
+
+#ifndef REPLAYMESSAGEPANEL_H
+#define REPLAYMESSAGEPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "vgui_controls/EditablePanel.h"
+
+using namespace vgui;
+
+//----------------------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------------------
+extern ConVar replay_msgduration_startrecord;
+extern ConVar replay_msgduration_stoprecord;
+extern ConVar replay_msgduration_replaysavailable;
+extern ConVar replay_msgduration_error;
+extern ConVar replay_msgduration_misc;
+extern ConVar replay_msgduration_connectrecording;
+
+//----------------------------------------------------------------------------------------
+// Purpose: Forward declarations
+//----------------------------------------------------------------------------------------
+class CExLabel;
+class CExButton;
+
+class CReplayMessageDlg : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayMessageDlg, EditablePanel );
+public:
+ CReplayMessageDlg( const char *pText );
+ ~CReplayMessageDlg();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+ virtual void OnKeyCodeTyped( KeyCode nCode );
+ virtual void OnCommand( const char *pCommand );
+
+private:
+ void Close();
+
+ Panel *m_pDlg;
+ CExLabel *m_pMsgLabel;
+ CExButton *m_pOKButton;
+};
+
+//----------------------------------------------------------------------------------------
+// Purpose: A panel for display messages from the replay system during gameplay
+//----------------------------------------------------------------------------------------
+class CReplayMessagePanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CReplayMessagePanel, EditablePanel );
+public:
+ CReplayMessagePanel( const char *pLocalizeName, float flDuration, bool bUrgent );
+ virtual ~CReplayMessagePanel();
+
+ void Show();
+ virtual void OnTick();
+
+ static int InstanceCount();
+ static void RemoveAll();
+
+private:
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+
+
+ CExLabel *m_pMessageLabel;
+ CExLabel *m_pReplayLabel;
+ ImagePanel *m_pIcon;
+ float m_flShowStartTime;
+ float m_flShowDuration;
+ bool m_bUrgent;
+
+#if defined( TF_CLIENT_DLL )
+ char m_szBorderName[ 64 ];
+#endif
+};
+
+#endif // REPLAYMESSAGEPANEL_H \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replayperformanceeditor.cpp b/sp/src/game/client/replay/vgui/replayperformanceeditor.cpp
new file mode 100644
index 00000000..071d1320
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayperformanceeditor.cpp
@@ -0,0 +1,2658 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replayperformanceeditor.h"
+#include "replay/replay.h"
+#include "replay/ireplayperformanceeditor.h"
+#include "replay/ireplayperformancecontroller.h"
+#include "replay/performance.h"
+#include "ienginevgui.h"
+#include "iclientmode.h"
+#include "vgui_controls/ImagePanel.h"
+#include "vgui_controls/TextImage.h"
+#include "vgui_controls/Slider.h"
+#include "vgui_controls/Menu.h"
+#include "vgui/ILocalize.h"
+#include "vgui/IImage.h"
+#include "c_team.h"
+#include "vgui_avatarimage.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "replay/replaycamera.h"
+#include "replay/ireplaymanager.h"
+#include "replay/iclientreplaycontext.h"
+#include "confirm_dialog.h"
+#include "replayperformancesavedlg.h"
+#include "replay/irecordingsessionmanager.h"
+#include "achievementmgr.h"
+#include "c_playerresource.h"
+#include "replay/gamedefs.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+extern CAchievementMgr g_AchievementMgrTF;
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+extern IReplayPerformanceController *g_pReplayPerformanceController;
+
+//-----------------------------------------------------------------------------
+
+// Hack-y global bool to communicate when we are rewinding for map load screens.
+// Order of operations issues preclude the use of engine->IsPlayingDemo().
+bool g_bIsReplayRewinding = false;
+
+//-----------------------------------------------------------------------------
+
+// TODO: Make these archive? Right now, the tips are reset every time the game starts
+ConVar replay_perftip_count_enter( "replay_perftip_count_enter", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
+ConVar replay_perftip_count_exit( "replay_perftip_count_exit", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
+ConVar replay_perftip_count_freecam_enter( "replay_perftip_count_freecam_enter", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
+ConVar replay_perftip_count_freecam_exit( "replay_perftip_count_freecam_exit", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
+ConVar replay_perftip_count_freecam_exit2( "replay_perftip_count_freecam_exit2", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "", true, 0, false, 0 );
+
+ConVar replay_editor_fov_mousewheel_multiplier( "replay_editor_fov_mousewheel_multiplier", "5", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "The multiplier on mousewheel input for adjusting camera FOV in the replay editor." );
+ConVar replay_editor_fov_mousewheel_invert( "replay_editor_fov_mousewheel_invert", "0", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "Invert FOV zoom/unzoom on mousewheel in the replay editor." );
+
+ConVar replay_replayeditor_rewindmsgcounter( "replay_replayeditor_rewindmsgcounter", "0", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_HIDDEN, "" );
+
+//-----------------------------------------------------------------------------
+
+#define MAX_TIP_DISPLAYS 1
+
+//-----------------------------------------------------------------------------
+
+#define TIMESCALE_MIN 0.01f
+#define TIMESCALE_MAX 3.0f
+
+//-----------------------------------------------------------------------------
+
+#define SLIDER_RANGE_MAX 10000.0f
+
+//-----------------------------------------------------------------------------
+
+#define REPLAY_SOUND_DIALOG_POPUP "replay\\replaydialog_warn.wav"
+
+//-----------------------------------------------------------------------------
+
+static const char *gs_pCamNames[ NCAMS ] =
+{
+ "free",
+ "third",
+ "first",
+ "timescale",
+};
+
+static const char *gs_pBaseComponentNames[ NCAMS ] =
+{
+ "replay/replay_camera_%s%s",
+ "replay/replay_camera_%s%s",
+ "replay/replay_camera_%s%s",
+ "replay/replay_%s%s",
+};
+
+//-----------------------------------------------------------------------------
+
+void PlayDemo()
+{
+ engine->ClientCmd_Unrestricted( "demo_resume" );
+}
+
+void PauseDemo()
+{
+ engine->ClientCmd_Unrestricted( "demo_pause" );
+}
+
+//-----------------------------------------------------------------------------
+
+inline float SCurve( float t )
+{
+ t = clamp( t, 0.0f, 1.0f );
+ return t * t * (3 - 2*t);
+}
+
+inline float CubicEaseIn( float t )
+{
+ t = clamp( t, 0.0f, 1.0f );
+ return t * t * t;
+}
+
+inline float LerpScale( float flIn, float flInMin, float flInMax, float flOutMin, float flOutMax )
+{
+ float flDenom = flInMax - flInMin;
+ if ( flDenom == 0.0f )
+ return 0.0f;
+
+ float t = clamp( ( flIn - flInMin ) / flDenom, 0.0f, 1.0f );
+ return Lerp( t, flOutMin, flOutMax );
+}
+
+//-----------------------------------------------------------------------------
+
+void HighlightTipWords( Label *pLabel )
+{
+ // Setup coloring - get # of words that should be highlighted
+ wchar_t *pwNumWords = g_pVGuiLocalize->Find( "#Replay_PerfTip_Highlight_NumWords" );
+ if ( !pwNumWords )
+ return;
+
+ // Get the current label text
+ wchar_t wszLabelText[512];
+ pLabel->GetText( wszLabelText, sizeof( wszLabelText ) );
+
+ pLabel->GetTextImage()->ClearColorChangeStream();
+ pLabel->GetTextImage()->AddColorChange( pLabel->GetFgColor(), 0 );
+
+ int nNumWords = _wtoi( pwNumWords );
+ for ( int i = 0; i < nNumWords; ++i )
+ {
+ char szWordFindStr[64];
+ V_snprintf( szWordFindStr, sizeof( szWordFindStr ), "#Replay_PerfTip_Highlight_Word%i", i );
+ wchar_t *pwWord = g_pVGuiLocalize->Find( szWordFindStr );
+ if ( !pwWord )
+ continue;
+
+ const int nWordLen = wcslen( pwWord );
+
+ // Find any instance of the word in the label text and highlight it in red
+ const wchar_t *p = wszLabelText;
+ do
+ {
+ const wchar_t *pInst = wcsstr( p, pwWord );
+ if ( !pInst )
+ break;
+
+ // Highlight the text
+ int nStartPos = pInst - wszLabelText;
+ int nEndPos = nStartPos + nWordLen;
+
+ // If start pos is non-zero, clear color changes
+ bool bChangeColor = true;
+ if ( nStartPos == 0 )
+ {
+ pLabel->GetTextImage()->ClearColorChangeStream();
+ }
+ else if ( iswalpha( wszLabelText[ nStartPos - 1 ] ) )
+ {
+ // If this is not the beginning of the string, check the previous character. If it's
+ // not whitespace, etc, we found an instance of a keyword within another word. Skip.
+ bChangeColor = false;
+ }
+
+ if ( bChangeColor )
+ {
+ pLabel->GetTextImage()->AddColorChange( Color(200,80,60,255), nStartPos );
+ pLabel->GetTextImage()->AddColorChange( pLabel->GetFgColor(), nEndPos );
+ }
+
+ p = pInst + nWordLen;
+ } while ( 1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+class CSavingDialog : public CGenericWaitingDialog
+{
+ DECLARE_CLASS_SIMPLE( CSavingDialog, CGenericWaitingDialog );
+public:
+ CSavingDialog( CReplayPerformanceEditorPanel *pEditorPanel )
+ : CGenericWaitingDialog( pEditorPanel )
+ {
+ m_pEditorPanel = pEditorPanel;
+ }
+
+ virtual void OnTick()
+ {
+ BaseClass::OnTick();
+
+ if ( !g_pReplayPerformanceController )
+ return;
+
+ // Update async save
+ if ( g_pReplayPerformanceController->IsSaving() )
+ {
+ g_pReplayPerformanceController->SaveThink();
+ }
+ else
+ {
+ if ( m_pEditorPanel.Get() )
+ {
+ m_pEditorPanel->OnSaveComplete();
+ }
+
+ Close();
+ }
+ }
+
+private:
+ CConfirmDialog *m_pLoginDialog;
+ vgui::DHANDLE< CReplayPerformanceEditorPanel > m_pEditorPanel;
+};
+
+//-----------------------------------------------------------------------------
+
+class CReplayTipLabel : public Label
+{
+ DECLARE_CLASS_SIMPLE( CReplayTipLabel, Label );
+public:
+ CReplayTipLabel( Panel *pParent, const char *pName, const char *pText )
+ : BaseClass( pParent, pName, pText )
+ {
+ }
+
+ virtual void ApplySchemeSettings( IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+ HighlightTipWords( this );
+ }
+};
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CReplayTipLabel, Label );
+
+//-----------------------------------------------------------------------------
+
+class CPerformanceTip : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CPerformanceTip, EditablePanel );
+public:
+ static DHANDLE< CPerformanceTip > s_pTip;
+
+ static CPerformanceTip *CreateInstance( const char *pText )
+ {
+ if ( s_pTip )
+ {
+ s_pTip->SetVisible( false );
+ s_pTip->MarkForDeletion();
+ s_pTip = NULL;
+ }
+
+ s_pTip = SETUP_PANEL( new CPerformanceTip( pText ) );
+
+ return s_pTip;
+ }
+
+ CPerformanceTip( const char *pText )
+ : BaseClass( g_pClientMode->GetViewport(), "Tip" ),
+ m_flBornTime( gpGlobals->realtime ),
+ m_flAge( 0.0f ),
+ m_flShowDuration( 15.0f )
+ {
+ m_pTextLabel = new CReplayTipLabel( this, "TextLabel", pText );
+ }
+
+ virtual void OnThink()
+ {
+ // Delete the panel if life exceeded
+ const float flEndTime = m_flBornTime + m_flShowDuration;
+ if ( gpGlobals->realtime >= flEndTime )
+ {
+ SetVisible( false );
+ MarkForDeletion();
+ s_pTip = NULL;
+ return;
+ }
+
+ SetVisible( true );
+
+ const float flFadeDuration = .4f;
+ float flAlpha;
+
+ // Fade out?
+ if ( gpGlobals->realtime >= flEndTime - flFadeDuration )
+ {
+ flAlpha = LerpScale( gpGlobals->realtime, flEndTime - flFadeDuration, flEndTime, 1.0f, 0.0f );
+ }
+
+ // Fade in?
+ else if ( gpGlobals->realtime <= m_flBornTime + flFadeDuration )
+ {
+ flAlpha = LerpScale( gpGlobals->realtime, m_flBornTime, m_flBornTime + flFadeDuration, 0.0f, 1.0f );
+ }
+
+ // Otherwise, we must be in between fade in/fade out
+ else
+ {
+ flAlpha = 1.0f;
+ }
+
+ SetAlpha( 255 * SCurve( flAlpha ) );
+ }
+
+ virtual void ApplySchemeSettings( IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replayperformanceeditor/tip.res", "GAME" );
+
+ // Center relative to parent
+ const int nScreenW = ScreenWidth();
+ const int nScreenH = ScreenHeight();
+ int aContentSize[2];
+ m_pTextLabel->GetContentSize( aContentSize[0], aContentSize[1] );
+ const int nLabelHeight = aContentSize[1];
+ SetBounds(
+ 0,
+ 3 * nScreenH / 4 - nLabelHeight / 2,
+ nScreenW,
+ nLabelHeight + 2 * m_nTopBottomMargin
+ );
+ m_pTextLabel->SetBounds(
+ m_nLeftRightMarginWidth,
+ m_nTopBottomMargin,
+ nScreenW - 2 * m_nLeftRightMarginWidth,
+ nLabelHeight
+ );
+ }
+
+ static void Cleanup()
+ {
+ if ( s_pTip )
+ {
+ s_pTip->MarkForDeletion();
+ s_pTip = NULL;
+ }
+ }
+
+ CPanelAnimationVarAliasType( int, m_nLeftRightMarginWidth, "left_right_margin", "0", "proportional_xpos" );
+ CPanelAnimationVarAliasType( int, m_nTopBottomMargin , "top_bottom_margin", "0", "proportional_ypos" );
+
+ CReplayTipLabel *m_pTextLabel;
+ float m_flBornTime;
+ float m_flAge;
+ float m_flShowDuration;
+};
+
+DHANDLE< CPerformanceTip > CPerformanceTip::s_pTip;
+
+// Display the performance tip if we haven't already displayed it nMaxTimesToDisplay times or more
+inline void DisplayPerformanceTip( const char *pText, ConVar* pCountCv = NULL, int nMaxTimesToDisplay = -1 )
+{
+ // Already displayed too many times? Get out.
+ if ( pCountCv && nMaxTimesToDisplay >= 0 )
+ {
+ int nCount = pCountCv->GetInt();
+ if ( nCount >= nMaxTimesToDisplay )
+ return;
+
+ // Incremement count cvar
+ pCountCv->SetValue( nCount + 1 );
+ }
+
+ // Display the tip
+ CPerformanceTip::CreateInstance( pText );
+}
+
+//-----------------------------------------------------------------------------
+
+inline float GetPlaybackTime()
+{
+ CReplay *pPlayingReplay = g_pReplayManager->GetPlayingReplay();
+ return gpGlobals->curtime - TICKS_TO_TIME( pPlayingReplay->m_nSpawnTick );
+}
+
+//-----------------------------------------------------------------------------
+
+class CPlayerCell : public CExImageButton
+{
+ DECLARE_CLASS_SIMPLE( CPlayerCell, CExImageButton );
+public:
+ CPlayerCell( Panel *pParent, const char *pName, int *pCurTargetPlayerIndex )
+ : CExImageButton( pParent, pName, "" ),
+ m_iPlayerIndex( -1 ),
+ m_pCurTargetPlayerIndex( pCurTargetPlayerIndex )
+ {
+ }
+
+ virtual void ApplySchemeSettings( IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ GetImage()->SetImage( "" );
+ SetFont( pScheme->GetFont( "ReplaySmall" ) );
+ SetContentAlignment( Label::a_center );
+ }
+
+ MESSAGE_FUNC( DoClick, "PressButton" )
+ {
+ ReplayCamera()->SetPrimaryTarget( m_iPlayerIndex );
+ *m_pCurTargetPlayerIndex = m_iPlayerIndex;
+
+ float flCurTime = GetPlaybackTime();
+
+ extern IReplayPerformanceController *g_pReplayPerformanceController;
+ g_pReplayPerformanceController->AddEvent_Camera_ChangePlayer( flCurTime, m_iPlayerIndex );
+ }
+
+ int m_iPlayerIndex;
+ int *m_pCurTargetPlayerIndex; // Allow the button to write current target in outer class when pressed
+};
+
+//-----------------------------------------------------------------------------
+
+/*
+class CReplayEditorSlider : public Slider
+{
+ DECLARE_CLASS_SIMPLE( CReplayEditorSlider, Slider );
+public:
+ CReplayEditorSlider( Panel *pParent, const char *pName )
+ : Slider( pParent, pName )
+ {
+ }
+
+ virtual void SetDefault( float flDefault ) { m_flDefault = flDefault; }
+
+ ON_MESSAGE( Reset, OnReset )
+ {
+ SetValue(
+ }
+
+private:
+ float m_flDefault;
+};
+*/
+
+//-----------------------------------------------------------------------------
+
+class CCameraOptionsPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CCameraOptionsPanel, EditablePanel );
+public:
+ CCameraOptionsPanel( Panel *pParent, const char *pName, const char *pTitle )
+ : EditablePanel( pParent, pName ),
+ m_bControlsAdded( false )
+ {
+ m_pTitleLabel = new CExLabel( this, "TitleLabel", pTitle );
+
+ AddControlToLayout( m_pTitleLabel );
+ }
+
+ ~CCameraOptionsPanel()
+ {
+ m_lstSliderInfos.PurgeAndDeleteElements();
+ }
+
+ void AddControlToLayout( Panel *pControl )
+ {
+ if ( pControl )
+ {
+ m_lstControls.AddToTail( pControl );
+ pControl->SetMouseInputEnabled( true );
+ }
+ }
+
+ // NOTE: Default value is assumed to be stored in flOut
+ void AddSliderToLayout( int nId, Slider *pSlider, const char *pLabelText,
+ float flMinValue, float flMaxValue, float &flOut )
+ {
+ SliderInfo_t *pNewSliderInfo = new SliderInfo_t;
+
+ pNewSliderInfo->m_nId = nId;
+ pNewSliderInfo->m_pSlider = pSlider;
+ pNewSliderInfo->m_flRange[ 0 ] = flMinValue;
+ pNewSliderInfo->m_flRange[ 1 ] = flMaxValue;
+ pNewSliderInfo->m_flDefault = flOut;
+ pNewSliderInfo->m_pValueOut = &flOut;
+
+ m_lstSliderInfos.AddToTail( pNewSliderInfo );
+
+ AddControlToLayout( new EditablePanel( this, "Buffer" ) );
+ AddControlToLayout( NewLabel( pLabelText ) );
+ AddControlToLayout( NewSetDefaultButton( nId ) );
+ AddControlToLayout( pSlider );
+
+ pSlider->AddActionSignalTarget( this );
+ }
+
+ void ResetSlider( int nId )
+ {
+ const SliderInfo_t *pSliderInfo = FindSliderInfoFromId( nId );
+ if ( !pSliderInfo )
+ return;
+
+ SetValue( pSliderInfo, pSliderInfo->m_flDefault );
+ }
+
+ void SetValue( int nId, float flValue )
+ {
+ const SliderInfo_t *pSliderInfo = FindSliderInfoFromId( nId );
+ if ( !pSliderInfo )
+ return;
+
+ SetValue( pSliderInfo, flValue );
+ }
+
+ virtual void ApplySchemeSettings( IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ // Setup border
+ SetBorder( pScheme->GetBorder( "ButtonBorder" ) );
+
+ HFont hFont = pScheme->GetFont( "ReplayBrowserSmallest", true );
+ m_pTitleLabel->SetFont( hFont );
+ m_pTitleLabel->SizeToContents();
+ m_pTitleLabel->SetTall( YRES( 20 ) );
+ m_pTitleLabel->SetColorStr( "235 235 235 255" );
+
+ if ( !m_bControlsAdded )
+ {
+ const char *pResFile = GetResFile();
+ if ( pResFile )
+ {
+ LoadControlSettings( pResFile, "GAME" );
+ }
+
+ AddControls();
+ m_bControlsAdded = true;
+ }
+
+ FOR_EACH_LL( m_lstSliderInfos, it )
+ {
+ SliderInfo_t *pInfo = m_lstSliderInfos[ it ];
+ Slider *pSlider = pInfo->m_pSlider;
+ pSlider->SetRange( 0, SLIDER_RANGE_MAX );
+ pSlider->SetNumTicks( 10 );
+ float flDenom = fabs( pInfo->m_flRange[1] - pInfo->m_flRange[0] );
+ pSlider->SetValue( SLIDER_RANGE_MAX * fabs( pInfo->m_flDefault - pInfo->m_flRange[0] ) / flDenom );
+ }
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+
+ int nWidth = XRES( 140 );
+ int nMargins[2] = { XRES( 5 ), YRES( 5 ) };
+ int nVBuf = YRES( 0 );
+ int nLastY = -1;
+ int nY = nMargins[1];
+ Panel *pPrevPanel = NULL;
+ int nLastCtrlHeight = 0;
+
+ FOR_EACH_LL( m_lstControls, i )
+ {
+ Panel *pPanel = m_lstControls[ i ];
+ if ( !pPanel->IsVisible() )
+ continue;
+
+ int aPos[2];
+ pPanel->GetPos( aPos[0], aPos[1] );
+
+ if ( pPrevPanel && aPos[1] >= 0 )
+ {
+ nY += pPrevPanel->GetTall() + nVBuf;
+ }
+
+ // Gross hack to see if the control is a default button
+ if ( dynamic_cast< CExButton * >( pPanel ) )
+ {
+ pPanel->SetWide( XRES( 36 ) );
+ pPanel->SetPos( pPrevPanel ? ( GetWide() - nMargins[0] - pPanel->GetWide() ) : 0, nLastY );
+ }
+ else
+ {
+ pPanel->SetWide( nWidth - 2 * nMargins[0] );
+ pPanel->SetPos( nMargins[0], nY );
+ }
+
+ nLastY = nY;
+ pPrevPanel = pPanel;
+ nLastCtrlHeight = MAX( nLastCtrlHeight, pPanel->GetTall() );
+ }
+
+ SetSize( nWidth, nY + nLastCtrlHeight + 2 * YRES( 3 ) );
+ }
+
+ virtual void OnCommand( const char *pCommand )
+ {
+ if ( !V_strnicmp( pCommand, "reset_", 6 ) )
+ {
+ const int nSliderInfoId = atoi( pCommand + 6 );
+ ResetSlider( nSliderInfoId );
+ }
+ else
+ {
+ BaseClass::OnCommand( pCommand );
+ }
+ }
+
+ Label *NewLabel( const char *pText )
+ {
+ Label *pLabel = new Label( this, "Label", pText );
+ pLabel->SetTall( YRES( 9 ) );
+ pLabel->SetPos( -1, 0 ); // Use default x and accumulated y
+
+ // Set font
+ IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+ HFont hFont = pScheme->GetFont( "DefaultVerySmall", true );
+ pLabel->SetFont( hFont );
+
+ return pLabel;
+ }
+
+ CExButton *NewSetDefaultButton( int nSliderInfoId )
+ {
+ CExButton *pButton = new CExButton( this, "DefaultButton", "#Replay_SetDefaultSetting" );
+ pButton->SetTall( YRES( 11 ) );
+ pButton->SetPos( XRES( 30 ), -1 ); // Set y to -1 so it will stay on the same line
+ pButton->SetContentAlignment( Label::a_center );
+ CFmtStr fmtResetCommand( "reset_%i", nSliderInfoId );
+ pButton->SetCommand( fmtResetCommand.Access() );
+ pButton->AddActionSignalTarget( this );
+
+ // Set font
+ IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+ HFont hFont = pScheme->GetFont( "DefaultVerySmall", true );
+ pButton->SetFont( hFont );
+
+ return pButton;
+ }
+
+protected:
+ MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", pParams )
+ {
+ Panel *pSlider = (Panel *)pParams->GetPtr( "panel" );
+ float flPercent = pParams->GetInt( "position" ) / SLIDER_RANGE_MAX;
+
+ FOR_EACH_LL( m_lstSliderInfos, it )
+ {
+ SliderInfo_t *pInfo = m_lstSliderInfos[ it ];
+ if ( pSlider == pInfo->m_pSlider )
+ {
+ *pInfo->m_pValueOut = Lerp( flPercent, pInfo->m_flRange[0], pInfo->m_flRange[1] );
+ }
+ }
+ }
+
+ virtual const char *GetResFile() { return NULL; }
+
+ virtual void AddControls()
+ {
+ }
+
+ struct SliderInfo_t
+ {
+ Slider *m_pSlider;
+ float m_flRange[2];
+ float m_flDefault;
+ int m_nId;
+ float *m_pValueOut;
+ };
+
+ const SliderInfo_t *FindSliderInfoFromId( int nId )
+ {
+ FOR_EACH_LL( m_lstSliderInfos, it )
+ {
+ SliderInfo_t *pInfo = m_lstSliderInfos[ it ];
+ if ( pInfo->m_nId == nId )
+ return pInfo;
+ }
+
+ AssertMsg( 0, "Should always find a slider here." );
+
+ return NULL;
+ }
+
+ void SetValue( const SliderInfo_t *pSliderInfo, float flValue )
+ {
+ if ( !pSliderInfo )
+ {
+ AssertMsg( 0, "This should not happen." );
+ return;
+ }
+
+ // Calculate the range
+ const float flRange = fabs( pSliderInfo->m_flRange[1] - pSliderInfo->m_flRange[0] );
+ AssertMsg( flRange > 0, "Bad slider range!" );
+
+ // Calculate the percentile based on the specified value and the range.
+ const float flPercent = fabs( flValue - pSliderInfo->m_flRange[0] ) / flRange;
+ pSliderInfo->m_pSlider->SetValue( flPercent * SLIDER_RANGE_MAX, true );
+ }
+
+ CUtlLinkedList< Panel * > m_lstControls;
+ CUtlLinkedList< SliderInfo_t *, int > m_lstSliderInfos;
+ CExLabel *m_pTitleLabel;
+ bool m_bControlsAdded;
+};
+
+//-----------------------------------------------------------------------------
+
+class CTimeScaleOptionsPanel : public CCameraOptionsPanel
+{
+ DECLARE_CLASS_SIMPLE( CTimeScaleOptionsPanel, CCameraOptionsPanel );
+public:
+ CTimeScaleOptionsPanel( Panel *pParent, float *pTimeScaleProxy )
+ : BaseClass( pParent, "TimeScaleSettings", "#Replay_TimeScale" ),
+ m_pTimeScaleSlider( NULL ),
+ m_pTimeScaleProxy( pTimeScaleProxy )
+ {
+ }
+
+ virtual const char *GetResFile()
+ {
+ return "resource/ui/replayperformanceeditor/settings_timescale.res";
+ }
+
+ virtual void AddControls()
+ {
+ m_pTimeScaleSlider = dynamic_cast< Slider * >( FindChildByName( "TimeScaleSlider" ) );
+
+ AddSliderToLayout( SLIDER_TIMESCALE, m_pTimeScaleSlider, "#Replay_Scale", TIMESCALE_MIN, TIMESCALE_MAX, *m_pTimeScaleProxy );
+ }
+
+ enum FreeCamSliders_t
+ {
+ SLIDER_TIMESCALE,
+ };
+
+ Slider *m_pTimeScaleSlider;
+ float *m_pTimeScaleProxy;
+};
+
+//-----------------------------------------------------------------------------
+class CCameraOptionsPanel_Free : public CCameraOptionsPanel
+{
+ DECLARE_CLASS_SIMPLE( CCameraOptionsPanel_Free, CCameraOptionsPanel );
+public:
+ CCameraOptionsPanel_Free( Panel *pParent )
+ : BaseClass( pParent, "FreeCameraSettings", "#Replay_FreeCam" ),
+ m_pAccelSlider( NULL ),
+ m_pSpeedSlider( NULL ),
+ m_pFovSlider( NULL ),
+ m_pRotFilterSlider( NULL ),
+ m_pShakeSpeedSlider( NULL ),
+ m_pShakeAmountSlider( NULL )
+ {
+ }
+
+ virtual const char *GetResFile()
+ {
+ return "resource/ui/replayperformanceeditor/camsettings_free.res";
+ }
+
+ virtual void AddControls()
+ {
+ m_pAccelSlider = dynamic_cast< Slider * >( FindChildByName( "AccelSlider" ) );
+ m_pSpeedSlider = dynamic_cast< Slider * >( FindChildByName( "SpeedSlider" ) );
+ m_pFovSlider = dynamic_cast< Slider * >( FindChildByName( "FovSlider" ) );
+ m_pRotFilterSlider = dynamic_cast< Slider * >( FindChildByName( "RotFilterSlider" ) );
+ m_pShakeSpeedSlider = dynamic_cast< Slider * >( FindChildByName( "ShakeSpeedSlider" ) );
+ m_pShakeAmountSlider = dynamic_cast< Slider * >( FindChildByName( "ShakeAmountSlider" ) );
+ m_pShakeDirSlider = dynamic_cast< Slider * >( FindChildByName( "ShakeDirSlider" ) );
+
+ AddSliderToLayout( SLIDER_ACCEL, m_pAccelSlider, "#Replay_Accel", FREE_CAM_ACCEL_MIN, FREE_CAM_ACCEL_MAX, ReplayCamera()->m_flRoamingAccel );
+ AddSliderToLayout( SLIDER_SPEED, m_pSpeedSlider, "#Replay_Speed", FREE_CAM_SPEED_MIN, FREE_CAM_SPEED_MAX, ReplayCamera()->m_flRoamingSpeed );
+ AddSliderToLayout( SLIDER_FOV, m_pFovSlider, "#Replay_Fov", FREE_CAM_FOV_MIN, FREE_CAM_FOV_MAX, ReplayCamera()->m_flRoamingFov[1] );
+ AddSliderToLayout( SLIDER_ROTFILTER, m_pRotFilterSlider, "#Replay_RotFilter", FREE_CAM_ROT_FILTER_MIN, FREE_CAM_ROT_FILTER_MAX, ReplayCamera()->m_flRoamingRotFilterFactor );
+ AddSliderToLayout( SLIDER_SHAKE_SPEED, m_pShakeSpeedSlider, "#Replay_ShakeSpeed", FREE_CAM_SHAKE_SPEED_MIN, FREE_CAM_SHAKE_SPEED_MAX, ReplayCamera()->m_flRoamingShakeSpeed );
+ AddSliderToLayout( SLIDER_SHAKE_AMOUNT, m_pShakeAmountSlider, "#Replay_ShakeAmount", FREE_CAM_SHAKE_AMOUNT_MIN, FREE_CAM_SHAKE_AMOUNT_MAX, ReplayCamera()->m_flRoamingShakeAmount );
+ AddSliderToLayout( SLIDER_SHAKE_DIR, m_pShakeDirSlider, "#Replay_ShakeDir", FREE_CAM_SHAKE_DIR_MIN, FREE_CAM_SHAKE_DIR_MAX, ReplayCamera()->m_flRoamingShakeDir );
+ }
+
+ enum FreeCamSliders_t
+ {
+ SLIDER_ACCEL,
+ SLIDER_SPEED,
+ SLIDER_FOV,
+ SLIDER_ROTFILTER,
+ SLIDER_SHAKE_SPEED,
+ SLIDER_SHAKE_AMOUNT,
+ SLIDER_SHAKE_DIR,
+ };
+
+ Slider *m_pAccelSlider;
+ Slider *m_pSpeedSlider;
+ Slider *m_pFovSlider;
+ Slider *m_pRotFilterSlider;
+ Slider *m_pShakeSpeedSlider;
+ Slider *m_pShakeAmountSlider;
+ Slider *m_pShakeDirSlider;
+};
+
+//-----------------------------------------------------------------------------
+
+class CReplayButton : public CExImageButton
+{
+ DECLARE_CLASS_SIMPLE( CReplayButton, CExImageButton );
+public:
+ CReplayButton( Panel *pParent, const char *pName, const char *pText )
+ : BaseClass( pParent, pName, pText ),
+ m_pTipText( NULL )
+ {
+ }
+
+ virtual void ApplySettings( KeyValues *pInResourceData )
+ {
+ BaseClass::ApplySettings( pInResourceData );
+
+ const char *pTipName = pInResourceData->GetString( "tipname" );
+ if ( pTipName && pTipName[0] )
+ {
+ const wchar_t *pTipText = g_pVGuiLocalize->Find( pTipName );
+ if ( pTipText && pTipText[0] )
+ {
+ const int nTipLength = V_wcslen( pTipText );
+ m_pTipText = new wchar_t[ nTipLength + 1 ];
+ V_wcsncpy( m_pTipText, pTipText, sizeof(wchar_t) * ( nTipLength + 1 ) );
+ m_pTipText[ nTipLength ] = L'\0';
+ }
+ }
+ }
+
+ virtual void OnCursorEntered()
+ {
+ BaseClass::OnCursorEntered();
+
+ CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
+ if ( pEditor && m_pTipText )
+ {
+ pEditor->SetButtonTip( m_pTipText, this );
+ pEditor->ShowButtonTip( true );
+ }
+ }
+
+ virtual void OnCursorExited()
+ {
+ BaseClass::OnCursorExited();
+
+ CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
+ if ( pEditor && m_pTipText )
+ {
+ pEditor->ShowButtonTip( false );
+ }
+ }
+
+private:
+ wchar_t *m_pTipText;
+};
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CReplayButton, CExImageButton );
+
+//-----------------------------------------------------------------------------
+
+#define MAX_FF_RAMP_TIME 8.0f // The amount of time until we ramp to max scale value.
+
+class CReplayEditorFastForwardButton : public CReplayButton
+{
+ DECLARE_CLASS_SIMPLE( CReplayEditorFastForwardButton, CReplayButton );
+public:
+ CReplayEditorFastForwardButton( Panel *pParent, const char *pName, const char *pText )
+ : BaseClass( pParent, pName, pText ),
+ m_flPressTime( 0.0f )
+ {
+ m_pHostTimescale = cvar->FindVar( "host_timescale" );
+ AssertMsg( m_pHostTimescale, "host_timescale lookup failed!" );
+
+ ivgui()->AddTickSignal( GetVPanel(), 10 );
+ }
+
+ ~CReplayEditorFastForwardButton()
+ {
+ ivgui()->RemoveTickSignal( GetVPanel() );
+
+ // Avoid a non-1.0 host_timescale after replay edit, which can happen if
+ // the user is still holding downt he FF button at the end of the replay.
+ if ( m_pHostTimescale )
+ {
+ m_pHostTimescale->SetValue( 1.0f );
+ }
+
+ // Resume demo playback so that any demo played later won't start paused.
+ PlayDemo();
+ }
+
+ virtual void OnMousePressed( MouseCode code )
+ {
+ m_flPressTime = gpGlobals->realtime;
+ PlayDemo();
+
+ BaseClass::OnMousePressed( code );
+ }
+
+ virtual void OnMouseReleased( MouseCode code )
+ {
+ m_flPressTime = 0.0f;
+ PauseDemo();
+
+ BaseClass::OnMouseReleased( code );
+ }
+
+ void OnTick()
+ {
+ float flScale;
+
+ if ( m_flPressTime == 0.0f )
+ {
+ flScale = 1.0f;
+ }
+ else
+ {
+ const float flElapsed = clamp( gpGlobals->realtime - m_flPressTime, 0.0f, MAX_FF_RAMP_TIME );
+ const float t = CubicEaseIn( flElapsed / MAX_FF_RAMP_TIME );
+
+ // If a shift key is down...
+ if ( input()->IsKeyDown( KEY_LSHIFT ) || input()->IsKeyDown( KEY_RSHIFT ) )
+ {
+ // ...slow down host_timescale.
+ flScale = .1f + .4f * t;
+ }
+ // If alt key down...
+ else if ( input()->IsKeyDown( KEY_LALT ) || input()->IsKeyDown( KEY_RALT ) )
+ {
+ // ...FF very quickly, ramp from 5 to 10.
+ flScale = 5.0f + 5.0f * t;
+ }
+ else
+ {
+ // Otherwise, start at 1.5 and ramp upwards over time.
+ flScale = 1.5f + 3.5f * t;
+ }
+ }
+
+ // Set host_timescale.
+ if ( m_pHostTimescale )
+ {
+ m_pHostTimescale->SetValue( flScale );
+ }
+ }
+
+private:
+ float m_flPressTime;
+ ConVar *m_pHostTimescale;
+};
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CReplayEditorFastForwardButton, CExImageButton );
+
+//-----------------------------------------------------------------------------
+
+class CRecLightPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CRecLightPanel, vgui::EditablePanel );
+public:
+ CRecLightPanel( Panel *pParent )
+ : EditablePanel( pParent, "RecLightPanel" ),
+ m_flPlayPauseTime( 0.0f ),
+ m_bPaused( false ),
+ m_bPerforming( false )
+ {
+ m_pRecLights[ 0 ] = NULL;
+ m_pRecLights[ 1 ] = NULL;
+ m_pPlayPause[ 0 ] = NULL;
+ m_pPlayPause[ 1 ] = NULL;
+ }
+
+ virtual void ApplySchemeSettings( IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replayperformanceeditor/reclight.res", "GAME" );
+
+ m_pRecLights[ 0 ] = dynamic_cast< ImagePanel * >( FindChildByName( "RecLightOffImg" ) );
+ m_pRecLights[ 1 ] = dynamic_cast< ImagePanel * >( FindChildByName( "RecLightOnImg" ) );
+
+ m_pPlayPause[ 0 ] = dynamic_cast< ImagePanel * >( FindChildByName( "PlayImg" ) );
+ m_pPlayPause[ 1 ] = dynamic_cast< ImagePanel * >( FindChildByName( "PauseImg" ) );
+
+ m_pCameraFringe = dynamic_cast< ImagePanel *>( FindChildByName( "CameraFringe" ) );
+ m_pCameraCrosshair = dynamic_cast< ImagePanel *>( FindChildByName( "CameraCrosshair" ) );
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+
+ SetVisible( m_bPerforming );
+
+ const int nScreenWidth = ScreenWidth();
+ const int nRecLightW = m_pRecLights[ 0 ]->GetWide();
+ int nXPos = nScreenWidth - nRecLightW + XRES( 6 );
+ int nYPos = -YRES( 8 );
+ m_pRecLights[ 0 ]->SetPos( nXPos, nYPos );
+ m_pRecLights[ 1 ]->SetPos( nXPos, nYPos );
+
+ const int nWidth = GetWide();
+ const int nHeight = GetTall();
+
+ // Setup camera fringe height
+ if ( m_pCameraFringe )
+ {
+ m_pCameraFringe->SetSize( nWidth, nHeight );
+ m_pCameraFringe->InstallMouseHandler( this );
+ }
+
+ // Setup camera cross hair height
+ if ( m_pCameraCrosshair )
+ {
+ int aImageSize[2];
+ IImage *pImage = m_pCameraCrosshair->GetImage();
+ pImage->GetSize( aImageSize[0], aImageSize[1] );
+
+ aImageSize[0] = m_pCameraCrosshair->GetWide();
+ aImageSize[1] = m_pCameraCrosshair->GetTall();
+
+ const int nStartY = YRES( 13 );
+
+ m_pCameraCrosshair->SetBounds(
+ nStartY + ( nWidth - aImageSize[0] ) / 2,
+ nStartY + ( nHeight - aImageSize[1] ) / 2,
+ aImageSize[0] - 2 * nStartY,
+ aImageSize[1] - 2 * nStartY
+ );
+
+ m_pCameraCrosshair->InstallMouseHandler( this );
+ }
+ }
+
+ void UpdateBackgroundVisibility()
+ {
+ m_pCameraCrosshair->SetVisible( m_bPaused );
+ m_pCameraFringe->SetVisible( m_bPaused );
+ }
+
+ virtual void OnThink()
+ {
+ const float flTime = gpGlobals->realtime;
+ bool bPauseAnimating = m_flPlayPauseTime > 0.0f &&
+ flTime >= m_flPlayPauseTime &&
+ flTime < ( m_flPlayPauseTime + m_flAnimTime );
+
+ // Setup light visibility
+ int nOnOff = fmod( flTime * 2.0f, 2.0f );
+ bool bOnLightVisible = (bool)nOnOff;
+ bool bRecording = g_pReplayPerformanceController->IsRecording();
+ m_pRecLights[ 0 ]->SetVisible( m_bPaused || ( bRecording && !bOnLightVisible ) );
+ m_pRecLights[ 1 ]->SetVisible( bRecording && ( !m_bPaused && bOnLightVisible ) );
+
+ // Deal with fringe and crosshair vis
+ UpdateBackgroundVisibility();
+
+ int iPlayPauseActive = (int)m_bPaused;
+
+ // Animate the pause icon
+ if ( bPauseAnimating )
+ {
+ const float t = clamp( ( flTime - m_flPlayPauseTime ) / m_flAnimTime, 0.0f, 1.0f );
+ const float s = SCurve( t );
+ const int nSize = (int)Lerp( s, 60.0f, 60.0f * m_nAnimScale );
+ int aCrossHairPos[2];
+ m_pCameraCrosshair->GetPos( aCrossHairPos[0], aCrossHairPos[1] );
+ const int nScreenXCenter = aCrossHairPos[0] + m_pCameraCrosshair->GetWide() / 2;
+ const int nScreenYCenter = aCrossHairPos[1] + m_pCameraCrosshair->GetTall() / 2;
+
+ m_pPlayPause[ iPlayPauseActive ]->SetBounds(
+ nScreenXCenter - nSize / 2,
+ nScreenYCenter - nSize / 2,
+ nSize,
+ nSize
+ );
+
+ m_pPlayPause[ iPlayPauseActive ]->SetAlpha( (int)( MIN( 0.5f, 1.0f - s ) * 255) );
+ }
+
+ m_pPlayPause[ iPlayPauseActive ]->SetVisible( bPauseAnimating );
+ m_pPlayPause[ !iPlayPauseActive ]->SetVisible( false );
+ }
+
+ void UpdatePauseState( bool bPaused )
+ {
+ if ( bPaused == m_bPaused )
+ return;
+
+ m_bPaused = bPaused;
+
+ m_flPlayPauseTime = gpGlobals->realtime;
+ }
+
+ void SetPerforming( bool bPerforming )
+ {
+ if ( bPerforming == m_bPerforming )
+ return;
+
+ m_bPerforming = bPerforming;
+ InvalidateLayout( true, false );
+ }
+
+ float m_flPlayPauseTime;
+ bool m_bPaused;
+ bool m_bPerforming;
+ ImagePanel *m_pPlayPause[2]; // 0=play, 1=pause
+ ImagePanel *m_pRecLights[2]; // 0=off, 1=on
+ ImagePanel *m_pCameraFringe;
+ ImagePanel *m_pCameraCrosshair;
+
+ CPanelAnimationVar( int, m_nAnimScale, "anim_scale", "4" );
+ CPanelAnimationVar( float, m_flAnimTime, "anim_time", "1.5" );
+};
+
+//-----------------------------------------------------------------------------
+
+CReplayPerformanceEditorPanel::CReplayPerformanceEditorPanel( Panel *parent, ReplayHandle_t hReplay )
+: EditablePanel( parent, "ReplayPerformanceEditor" ),
+ m_hReplay( hReplay ),
+ m_flLastTime( -1 ),
+ m_nRedBlueLabelRightX( 0 ),
+ m_nBottomPanelStartY( 0 ),
+ m_nBottomPanelHeight( 0 ),
+ m_nLastRoundedTime( -1 ),
+ m_flSpaceDownStart( 0.0f ),
+ m_flOldFps( -1.0f ),
+ m_flLastTimeSpaceBarPressed( 0.0f ),
+ m_flActiveTimeInEditor( 0.0f ),
+ m_flTimeScaleProxy( 1.0f ),
+ m_iCameraSelection( CAM_FIRST ),
+ m_bMousePressed( false ),
+ m_bMouseDown( false ),
+ m_nMouseClickedOverCameraSettingsPanel( CAM_INVALID ),
+ m_bShownAtLeastOnce( false ),
+ m_bAchievementAwarded( false ),
+ m_pImageList( NULL ),
+ m_pCurTimeLabel( NULL ),
+ m_pTotalTimeLabel( NULL ),
+ m_pPlayerNameLabel( NULL ),
+ m_pMouseTargetPanel( NULL ),
+ m_pSlowMoButton( NULL ),
+ m_pRecLightPanel( NULL ),
+ m_pPlayerCellData( NULL ),
+ m_pBottom( NULL ),
+ m_pMenuButton( NULL ),
+ m_pMenu( NULL ),
+ m_pPlayerCellsPanel( NULL ),
+ m_pButtonTip( NULL ),
+ m_pSavingDlg( NULL )
+{
+ V_memset( m_pCameraButtons, 0, sizeof( m_pCameraButtons ) );
+ V_memset( m_pCtrlButtons, 0, sizeof( m_pCtrlButtons ) );
+ V_memset( m_pCameraOptionsPanels, NULL, sizeof( m_pCameraOptionsPanels ) );
+
+ m_pCameraOptionsPanels[ CAM_FREE ] = new CCameraOptionsPanel_Free( this );
+ m_pCameraOptionsPanels[ COMPONENT_TIMESCALE ] = new CTimeScaleOptionsPanel( this, &m_flTimeScaleProxy );
+
+ m_nRedBlueSigns[0] = -1;
+ m_nRedBlueSigns[1] = 1;
+ m_iCurPlayerTarget = -1;
+
+ m_pImageList = new ImageList( false );
+
+ SetParent( g_pClientMode->GetViewport() );
+
+ HScheme hScheme = scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" );
+ SetScheme( hScheme );
+
+ ivgui()->AddTickSignal( GetVPanel(), 16 ); // Roughly 60hz
+
+ MakePopup( true );
+ SetMouseInputEnabled( true );
+
+ // Create bottom
+ m_pBottom = new EditablePanel( this, "BottomPanel" );
+
+ // Add player cells
+ m_pPlayerCellsPanel = new EditablePanel( m_pBottom, "PlayerCellsPanel" );
+ for ( int i = 0; i < 2; ++i )
+ {
+ for ( int j = 0; j <= MAX_PLAYERS; ++j )
+ {
+ m_pPlayerCells[i][j] = new CPlayerCell( m_pPlayerCellsPanel, "PlayerCell", &m_iCurPlayerTarget );
+ m_pPlayerCells[i][j]->SetVisible( false );
+ AddPanelKeyboardInputDisableList( m_pPlayerCells[i][j] );
+ }
+ }
+
+ // Create rec light panel
+ m_pRecLightPanel = SETUP_PANEL( new CRecLightPanel( g_pClientMode->GetViewport() ) );
+
+ // Display "enter performance mode" tip
+ DisplayPerformanceTip( "#Replay_PerfTip_EnterPerfMode", &replay_perftip_count_enter, MAX_TIP_DISPLAYS );
+
+ // Create menu
+ m_pMenu = new Menu( this, "Menu" );
+ m_aMenuItemIds[ MENU_SAVE ] = m_pMenu->AddMenuItem( "#Replay_Save", "menu_save", this );
+ m_aMenuItemIds[ MENU_SAVEAS ] = m_pMenu->AddMenuItem( "#Replay_SaveAs", "menu_saveas", this );
+ m_pMenu->AddSeparator();
+ m_aMenuItemIds[ MENU_EXIT ] = m_pMenu->AddMenuItem( "#Replay_Exit", "menu_exit", this );
+
+ m_pMenu->EnableUseMenuManager( false ); // The menu manager doesn't play nice with the menu button
+}
+
+CReplayPerformanceEditorPanel::~CReplayPerformanceEditorPanel()
+{
+ m_pRecLightPanel->MarkForDeletion();
+ m_pRecLightPanel = NULL;
+
+ m_pButtonTip->MarkForDeletion();
+ m_pButtonTip = NULL;
+
+ g_bIsReplayRewinding = false;
+
+ surface()->PlaySound( "replay\\performanceeditorclosed.wav" );
+
+ CPerformanceTip::Cleanup();
+
+ ClearPlayerCellData();
+}
+
+void CReplayPerformanceEditorPanel::ClearPlayerCellData()
+{
+ if ( m_pPlayerCellData )
+ {
+ m_pPlayerCellData->deleteThis();
+ m_pPlayerCellData = NULL;
+ }
+}
+
+void CReplayPerformanceEditorPanel::AddPanelKeyboardInputDisableList( Panel *pPanel )
+{
+ m_lstDisableKeyboardInputPanels.AddToTail( pPanel );
+}
+
+void CReplayPerformanceEditorPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replayperformanceeditor/main.res", "GAME" );
+
+ m_lstDisableKeyboardInputPanels.RemoveAll();
+
+ int nParentWidth = GetParent()->GetWide();
+ int nParentHeight = GetParent()->GetTall();
+
+ // Set size of this panel
+ SetSize( nParentWidth, nParentHeight );
+
+ // Layout bottom
+ if ( m_pBottom )
+ {
+ m_nBottomPanelHeight = m_pBottom->GetTall(); // Get from .res
+ m_nBottomPanelStartY = nParentHeight - m_nBottomPanelHeight;
+ m_pBottom->SetBounds( 0, m_nBottomPanelStartY, nParentWidth, m_nBottomPanelHeight );
+ }
+
+ // Layout rec light panel - don't overlap bottom panel
+ m_pRecLightPanel->SetBounds( 0, 0, ScreenWidth(), m_nBottomPanelStartY );
+
+ // Setup camera buttons
+ const int nNumCameraButtons = NCAMS;
+ const char *pCameraButtonNames[nNumCameraButtons] = { "CameraFree", "CameraThird", "CameraFirst", "TimeScaleButton" };
+ int nCurButtonX = nParentWidth - m_nRightMarginWidth;
+ int nLeftmostCameraButtonX = 0;
+ for ( int i = 0; i < nNumCameraButtons; ++i )
+ {
+ m_pCameraButtons[i] = dynamic_cast< CExImageButton * >( FindChildByName( pCameraButtonNames[ i ] ) );
+ if ( m_pCameraButtons[i] )
+ {
+ CExImageButton *pCurButton = m_pCameraButtons[ i ];
+ if ( !pCurButton )
+ continue;
+
+ nCurButtonX -= pCurButton->GetWide();
+
+ int nX, nY;
+ pCurButton->GetPos( nX, nY );
+ pCurButton->SetPos( nCurButtonX, nY );
+
+ pCurButton->SetParent( m_pBottom );
+ pCurButton->AddActionSignalTarget( this );
+
+#if !defined( TF_CLIENT_DLL )
+ pCurButton->SetPaintBorderEnabled( false );
+#endif
+
+ AddPanelKeyboardInputDisableList( pCurButton );
+ }
+ }
+ nLeftmostCameraButtonX = nCurButtonX;
+
+ static const char *s_pControlButtonNames[NUM_CTRLBUTTONS] = {
+ "InButton", "GotoBeginningButton", "RewindButton",
+ "PlayButton",
+ "FastForwardButton", "GotoEndButton", "OutButton"
+ };
+ for ( int i = 0; i < NUM_CTRLBUTTONS; ++i )
+ {
+ CExImageButton *pCurButton = dynamic_cast< CExImageButton * >( FindChildByName( s_pControlButtonNames[ i ] ) ); Assert( pCurButton );
+ if ( !pCurButton )
+ continue;
+
+ pCurButton->SetParent( m_pBottom );
+ pCurButton->AddActionSignalTarget( this );
+
+ AddPanelKeyboardInputDisableList( pCurButton );
+
+#if !defined( TF_CLIENT_DLL )
+ pCurButton->SetPaintBorderEnabled( false );
+#endif
+
+ m_pCtrlButtons[ i ] = pCurButton;
+ }
+
+ // If the performance in tick is set, highlight the in point button
+ {
+ CReplayPerformance *pSavedPerformance = GetSavedPerformance();
+ m_pCtrlButtons[ CTRLBUTTON_IN ]->SetSelected( pSavedPerformance && pSavedPerformance->HasInTick() );
+ m_pCtrlButtons[ CTRLBUTTON_OUT ]->SetSelected( pSavedPerformance && pSavedPerformance->HasOutTick() );
+ }
+
+ // Select first-person camera by default.
+ UpdateCameraSelectionPosition( CAM_FIRST );
+
+ // Position time label
+ m_pCurTimeLabel = dynamic_cast< CExLabel * >( FindChildByName( "CurTimeLabel" ) );
+ m_pTotalTimeLabel = dynamic_cast< CExLabel * >( FindChildByName( "TotalTimeLabel" ) );
+
+ m_pCurTimeLabel->SetParent( m_pBottom );
+ m_pTotalTimeLabel->SetParent( m_pBottom );
+
+ // Get player name label
+ m_pPlayerNameLabel = dynamic_cast< CExLabel * >( FindChildByName( "PlayerNameLabel" ) );
+
+ // Get mouse target panel
+ m_pMouseTargetPanel = dynamic_cast< EditablePanel * >( FindChildByName( "MouseTargetPanel" ) );
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ for ( int j = 0; j <= MAX_PLAYERS; ++j )
+ {
+ m_pPlayerCells[i][j]->SetMouseInputEnabled( true );
+ }
+ }
+
+ // Get menu button
+ m_pMenuButton = dynamic_cast< CExImageButton * >( FindChildByName( "MenuButton" ) );
+ AddPanelKeyboardInputDisableList( m_pMenuButton );
+ m_pMenuButton->SetMouseInputEnabled( true );
+#if !defined( TF_CLIENT_DLL )
+ m_pMenuButton->SetPaintBorderEnabled( false );
+#endif
+
+ // Get button tip
+ m_pButtonTip = dynamic_cast< CReplayTipLabel * >( FindChildByName( "ButtonTip" ) );
+ m_pButtonTip->SetParent( g_pClientMode->GetViewport() );
+}
+
+static void Replay_GotoTick( bool bConfirmed, void *pContext )
+{
+ if ( bConfirmed )
+ {
+ int nGotoTick = (int)pContext;
+ CFmtStr fmtCmd( "demo_gototick %i\ndemo_pause\n", nGotoTick );
+ engine->ClientCmd_Unrestricted( fmtCmd.Access() );
+ }
+}
+
+void CReplayPerformanceEditorPanel::OnSliderMoved( KeyValues *pParams )
+{
+}
+
+void CReplayPerformanceEditorPanel::OnInGameMouseWheelEvent( int nDelta )
+{
+ HandleMouseWheel( nDelta );
+}
+
+void CReplayPerformanceEditorPanel::HandleMouseWheel( int nDelta )
+{
+ if ( ReplayCamera()->GetMode() == OBS_MODE_ROAMING )
+ {
+ // Invert mousewheel input if necessary
+ if ( replay_editor_fov_mousewheel_invert.GetBool() )
+ {
+ nDelta *= -1;
+ }
+
+ float &flFov = ReplayCamera()->m_flRoamingFov[1];
+ flFov = clamp( flFov - nDelta * replay_editor_fov_mousewheel_multiplier.GetFloat(), FREE_CAM_FOV_MIN, FREE_CAM_FOV_MAX );
+
+ // Update FOV slider in free camera settings
+ CCameraOptionsPanel_Free *pFreeCamOptions = static_cast< CCameraOptionsPanel_Free * >( m_pCameraOptionsPanels[ CAM_FREE ] );
+ pFreeCamOptions->m_pFovSlider->SetValue( flFov - FREE_CAM_FOV_MIN, false );
+ }
+}
+
+void CReplayPerformanceEditorPanel::ApplySettings( KeyValues *pInResourceData )
+{
+ BaseClass::ApplySettings( pInResourceData );
+
+ ClearPlayerCellData();
+
+ KeyValues *pPlayerCellData = pInResourceData->FindKey( "PlayerCell" );
+ if ( pPlayerCellData )
+ {
+ m_pPlayerCellData = new KeyValues( "PlayerCell" );
+ pPlayerCellData->CopySubkeys( m_pPlayerCellData );
+ }
+}
+
+CameraMode_t CReplayPerformanceEditorPanel::IsMouseOverActiveCameraOptionsPanel( int nMouseX, int nMouseY )
+{
+ // In one of the camera options panels?
+ for ( int i = 0; i < NCAMS; ++i )
+ {
+ CCameraOptionsPanel *pCurPanel = m_pCameraOptionsPanels[ i ];
+ if ( pCurPanel && pCurPanel->IsVisible() && pCurPanel->IsWithin( nMouseX, nMouseY ) )
+ return (CameraMode_t)i;
+ }
+
+ return CAM_INVALID;
+}
+
+void CReplayPerformanceEditorPanel::OnMouseWheeled( int nDelta )
+{
+ HandleMouseWheel( nDelta );
+}
+
+void CReplayPerformanceEditorPanel::OnTick()
+{
+ BaseClass::OnTick();
+
+// engine->Con_NPrintf( 0, "timescale: %f", g_pReplayPerformanceController->GetPlaybackTimeScale() );
+
+ C_ReplayCamera *pCamera = ReplayCamera();
+ if ( !pCamera )
+ return;
+
+ // Calc elapsed time
+ float flElapsed = gpGlobals->realtime - m_flLastTime;
+ m_flLastTime = gpGlobals->realtime;
+
+ // If this is the first time we're running and camera is valid, get primary target
+ if ( m_iCurPlayerTarget < 0 )
+ {
+ m_iCurPlayerTarget = pCamera->GetPrimaryTargetIndex();
+ }
+
+ // NOTE: Third-person is not "controllable" yet
+ int nCameraMode = pCamera->GetMode();
+ bool bInAControllableCameraMode = nCameraMode == OBS_MODE_ROAMING || nCameraMode == OBS_MODE_CHASE;
+
+ // Get mouse cursor pos
+ int nMouseX, nMouseY;
+ input()->GetCursorPos( nMouseX, nMouseY );
+
+ // Toggle in and out of camera control if appropriate
+ // Mouse pressed?
+ bool bMouseDown = input()->IsMouseDown( MOUSE_LEFT );
+ m_bMousePressed = bMouseDown && !m_bMouseDown;
+ m_bMouseDown = bMouseDown;
+
+ // Reset this flag if mouse is no longer down
+ if ( !m_bMouseDown )
+ {
+ m_nMouseClickedOverCameraSettingsPanel = CAM_INVALID;
+ }
+
+ bool bNoDialogsUp = TFModalStack()->IsEmpty();
+ bool bMouseCursorOverPerfEditor = nMouseY >= m_nBottomPanelStartY;
+ bool bMouseOverMenuButton = m_pMenuButton->IsWithin( nMouseX, nMouseY );
+ bool bMouseOverMenu = m_pMenu->IsWithin( nMouseX, nMouseY );
+ bool bRecording = g_pReplayPerformanceController->IsRecording();
+ if ( IsVisible() && m_bMousePressed )
+ {
+ CameraMode_t nActiveOptionsPanel = IsMouseOverActiveCameraOptionsPanel( nMouseX, nMouseY );
+ if ( nActiveOptionsPanel != CAM_INVALID )
+ {
+ m_nMouseClickedOverCameraSettingsPanel = nActiveOptionsPanel;
+ }
+ else if ( m_pMenu->IsVisible() && !m_pMenu->IsWithin( nMouseX, nMouseY ) )
+ {
+ ToggleMenu();
+ }
+ else if ( bInAControllableCameraMode && !bMouseCursorOverPerfEditor && !bMouseOverMenuButton &&
+ !bMouseOverMenu && bNoDialogsUp )
+ {
+ if ( bRecording )
+ {
+ bool bMouseInputEnabled = IsMouseInputEnabled();
+
+ // Already in a controllable camera mode?
+ if ( bMouseInputEnabled )
+ {
+ DisplayPerformanceTip( "#Replay_PerfTip_ExitFreeCam", &replay_perftip_count_freecam_exit, MAX_TIP_DISPLAYS );
+ surface()->PlaySound( "replay\\cameracontrolmodeentered.wav" );
+ }
+ else
+ {
+ DisplayPerformanceTip( "#Replay_PerfTip_EnterFreeCam", &replay_perftip_count_freecam_enter, MAX_TIP_DISPLAYS );
+ surface()->PlaySound( "replay\\cameracontrolmodeexited.wav" );
+ }
+
+ SetMouseInputEnabled( !bMouseInputEnabled );
+ }
+ else
+ {
+ // Play an error sound
+ surface()->PlaySound( "replay\\cameracontrolerror.wav" );
+ }
+ }
+ }
+
+ // Show panel if space key bar is down
+ bool bSpaceDown = bNoDialogsUp && !enginevgui->IsGameUIVisible() && input()->IsKeyDown( KEY_SPACE );
+ m_bSpacePressed = bSpaceDown && !m_bSpaceDown;
+ m_bSpaceDown = bSpaceDown;
+
+ // Modify visibility?
+ bool bShow = IsVisible();
+ if ( m_bSpacePressed )
+ {
+ bShow = !IsVisible();
+ }
+
+ // Set visibility?
+ if ( IsVisible() != bShow )
+ {
+ ShowPanel( bShow );
+ m_bShownAtLeastOnce = true;
+
+ // For achievements:
+ Achievements_OnSpaceBarPressed();
+ }
+
+ // Factor in host_timescale.
+ float flScaledElapsed = flElapsed;
+ ConVarRef host_timescale( "host_timescale" );
+ if ( host_timescale.GetFloat() > 0 )
+ {
+ flScaledElapsed *= host_timescale.GetFloat();
+ }
+
+ // Do FOV smoothing
+ ReplayCamera()->SmoothFov( flScaledElapsed );
+
+ // Don't do any more processing if not needed
+ if ( !m_bShownAtLeastOnce )
+ return;
+
+ // Update time text if necessary
+ UpdateTimeLabels();
+
+ // Make all player cells invisible
+ int nTeamCounts[2] = {0,0};
+ int nCurTeam = 0;
+ for ( int i = 0; i < 2; ++i )
+ for ( int j = 0; j <= MAX_PLAYERS; ++j )
+ {
+ m_pPlayerCells[i][j]->SetVisible( false );
+ }
+
+ int iMouseOverPlayerIndex = -1;
+ CPlayerCell *pMouseOverCell = NULL;
+
+ // Update player cells
+ bool bLayoutPlayerCells = true; // TODO: only layout when necessary
+ C_ReplayGame_PlayerResource_t *pGamePlayerResource = dynamic_cast< C_ReplayGame_PlayerResource_t * >( g_PR );
+ for ( int iPlayer = 1; iPlayer <= MAX_PLAYERS; ++iPlayer )
+ {
+ IGameResources *pGR = GameResources();
+
+ if ( !pGR || !pGR->IsConnected( iPlayer ) )
+ continue;
+
+ // Which team?
+ int iTeam = pGR->GetTeam( iPlayer );
+ switch ( iTeam )
+ {
+ case REPLAY_TEAM_TEAM0:
+ ++nTeamCounts[0];
+ nCurTeam = 0;
+ break;
+ case REPLAY_TEAM_TEAM1:
+ ++nTeamCounts[1];
+ nCurTeam = 1;
+ break;
+ default:
+ nCurTeam = -1;
+ break;
+ }
+
+ if ( nCurTeam < 0 )
+ continue;
+
+#if !defined( CSTRIKE_DLL )
+ int iPlayerClass = pGamePlayerResource->GetPlayerClass( iPlayer );
+ if ( iPlayerClass == REPLAY_CLASS_UNDEFINED )
+ continue;
+#endif
+
+ int nCurTeamCount = nTeamCounts[ nCurTeam ];
+ CPlayerCell* pCell = m_pPlayerCells[ nCurTeam ][ nCurTeamCount-1 ];
+
+ // Cache the player index
+ pCell->m_iPlayerIndex = iPlayer;
+
+ // Make visible
+ pCell->SetVisible( true );
+
+ // Show leaderboard icon
+#if defined( TF_CLIENT_DLL )
+ char szClassImg[64];
+ extern const char *g_aPlayerClassNames_NonLocalized[ REPLAY_NUM_CLASSES ];
+ char const *pClassName = iPlayerClass == TF_CLASS_DEMOMAN
+ ? "demo"
+ : g_aPlayerClassNames_NonLocalized[ iPlayerClass ];
+ V_snprintf( szClassImg, sizeof( szClassImg ), "../HUD/leaderboard_class_%s", pClassName );
+
+ // Show dead icon instead?
+ if ( !pGamePlayerResource->IsAlive( iPlayer ) )
+ {
+ V_strcat( szClassImg, "_d", sizeof( szClassImg ) );
+ }
+
+ IImage *pImage = scheme()->GetImage( szClassImg, true );
+ if ( pImage )
+ {
+ pImage->SetSize( 32, 32 );
+ pCell->GetImage()->SetImage( pImage );
+ }
+
+#elif defined( CSTRIKE_DLL )
+ // TODO - create and use class icons
+ char szText[16];
+ V_snprintf( szText, sizeof( szText ), "%i", nTeamCounts[ nCurTeam ] );
+ pCell->SetText( szText );
+#endif
+
+ // Display player name if mouse is over the current cell
+ if ( pCell->IsWithin( nMouseX, nMouseY ) )
+ {
+ iMouseOverPlayerIndex = iPlayer;
+ pMouseOverCell = pCell;
+ }
+ }
+
+ // Check to see if we're hovering over a camera-mode, and if so, display its options panel if it has one
+ if ( bRecording )
+ {
+ for ( int i = 0; i < NCAMS; ++i )
+ {
+ CCameraOptionsPanel *pCurOptionsPanel = m_pCameraOptionsPanels[ i ];
+ if ( !pCurOptionsPanel )
+ continue;
+
+ bool bMouseOverButton = m_pCameraButtons[ i ]->IsWithin( nMouseX, nMouseY );
+ bool bMouseOverOptionsPanel = pCurOptionsPanel->IsWithin( nMouseX, nMouseY );
+ bool bInCameraModeThatMouseIsOver = ReplayCamera()->GetMode() == GetCameraModeFromButtonIndex( (CameraMode_t)i );
+ bool bDontCareAboutCameraMode = i == COMPONENT_TIMESCALE;
+ bool bActivate = ( i == m_nMouseClickedOverCameraSettingsPanel ) ||
+ ( ( ( bInCameraModeThatMouseIsOver || bDontCareAboutCameraMode ) && bMouseOverButton ) || ( bMouseOverOptionsPanel && pCurOptionsPanel->IsVisible() ) );
+ pCurOptionsPanel->SetVisible( bActivate );
+ }
+ }
+
+ if ( bLayoutPlayerCells )
+ {
+ LayoutPlayerCells();
+ }
+
+ // Setup player name label and temporary camera view
+ if ( m_pPlayerNameLabel && pGamePlayerResource && pMouseOverCell )
+ {
+ m_pPlayerNameLabel->SetText( pGamePlayerResource->GetPlayerName( iMouseOverPlayerIndex ) );
+ m_pPlayerNameLabel->SizeToContents();
+
+ int nCellPos[2];
+ pMouseOverCell->GetPos( nCellPos[0], nCellPos[1] );
+
+ int nLabelX = MAX(
+ nCellPos[0],
+ m_nRedBlueLabelRightX
+ );
+ int nLabelY = m_nBottomPanelStartY + ( m_nBottomPanelHeight - m_pPlayerNameLabel->GetTall() ) / 2;
+ m_pPlayerNameLabel->SetPos( nLabelX, nLabelY );
+
+ m_pPlayerNameLabel->SetVisible( true );
+
+ // Setup camera
+ pCamera->SetPrimaryTarget( iMouseOverPlayerIndex );
+ }
+ else
+ {
+ m_pPlayerNameLabel->SetVisible( false );
+
+ // Set camera to last valid target
+ Assert( m_iCurPlayerTarget >= 0 );
+ pCamera->SetPrimaryTarget( m_iCurPlayerTarget );
+ }
+
+ // If user clicked, assume it was the selected cell and set primary target in camera
+ if ( iMouseOverPlayerIndex >= 0 )
+ {
+ pCamera->SetPrimaryTarget( iMouseOverPlayerIndex );
+ }
+ else
+ {
+ pCamera->SetPrimaryTarget( m_iCurPlayerTarget );
+ }
+
+ // If in free-cam mode, add set view event if we're not paused
+ if ( bInAControllableCameraMode && m_bShownAtLeastOnce && bRecording )
+ {
+ AddSetViewEvent();
+ AddTimeScaleEvent( m_flTimeScaleProxy );
+ }
+
+ // Set paused state in rec light
+ const bool bPaused = IsPaused();
+ m_pRecLightPanel->UpdatePauseState( bPaused );
+
+ Achievements_Think( flElapsed );
+}
+
+void CReplayPerformanceEditorPanel::Achievements_OnSpaceBarPressed()
+{
+ m_flLastTimeSpaceBarPressed = gpGlobals->realtime;
+}
+
+void CReplayPerformanceEditorPanel::Achievements_Think( float flElapsed )
+{
+// engine->Con_NPrintf( 10, "total time: %f", m_flActiveTimeInEditor );
+// engine->Con_NPrintf( 11, "last time space bar pressed: %f", m_flLastTimeSpaceBarPressed );
+
+ // Already awarded one this editing session?
+ if ( m_bAchievementAwarded )
+ return;
+
+ // Too much idle time since last activity?
+ if ( gpGlobals->realtime - m_flLastTimeSpaceBarPressed > 60.0f )
+ {
+ m_flActiveTimeInEditor = 0.0f;
+ return;
+ }
+
+ // Accumulate active time
+ m_flActiveTimeInEditor += flElapsed;
+
+ // Award now if three-minutes of non-idle time has passed
+ const float flMinutes = 60.0f * 3.0f;
+ if ( m_flActiveTimeInEditor < flMinutes )
+ return;
+
+ Achievements_Grant();
+}
+
+void CReplayPerformanceEditorPanel::Achievements_Grant()
+{
+#if defined( TF_CLIENT_DLL )
+ g_AchievementMgrTF.AwardAchievement( ACHIEVEMENT_TF_REPLAY_EDIT_TIME );
+#endif
+
+ // Awarded
+ m_bAchievementAwarded = true;
+}
+
+bool CReplayPerformanceEditorPanel::IsPaused()
+{
+ return IsVisible();
+}
+
+CReplayPerformance *CReplayPerformanceEditorPanel::GetPerformance() const
+{
+ return g_pReplayPerformanceController->GetPerformance();
+}
+
+CReplayPerformance *CReplayPerformanceEditorPanel::GetSavedPerformance() const
+{
+ return g_pReplayPerformanceController->GetSavedPerformance();
+}
+
+int CReplayPerformanceEditorPanel::GetCameraModeFromButtonIndex( CameraMode_t iCamera )
+{
+ switch ( iCamera )
+ {
+ case CAM_FREE: return OBS_MODE_ROAMING;
+ case CAM_THIRD: return OBS_MODE_CHASE;
+ case CAM_FIRST: return OBS_MODE_IN_EYE;
+ }
+ return CAM_INVALID;
+}
+
+void CReplayPerformanceEditorPanel::UpdateTimeLabels()
+{
+ CReplay *pPlayingReplay = g_pReplayManager->GetPlayingReplay();
+
+ if ( !pPlayingReplay || !m_pCurTimeLabel || !m_pTotalTimeLabel )
+ return;
+
+ float flCurTime, flTotalTime;
+ g_pClientReplayContext->GetPlaybackTimes( flCurTime, flTotalTime, pPlayingReplay, GetPerformance() );
+
+ int nCurRoundedTime = (int)flCurTime; // Essentially floor'd
+ if ( nCurRoundedTime == m_nLastRoundedTime )
+ return;
+
+ m_nLastRoundedTime = nCurRoundedTime;
+
+ // Set current time text
+ char szTimeText[64];
+ V_snprintf( szTimeText, sizeof( szTimeText ), "%s", CReplayTime::FormatTimeString( nCurRoundedTime ) );
+ m_pCurTimeLabel->SetText( szTimeText );
+
+ // Set total time text
+ V_snprintf( szTimeText, sizeof( szTimeText ), "%s", CReplayTime::FormatTimeString( (int)flTotalTime ) );
+ m_pTotalTimeLabel->SetText( szTimeText );
+
+ // Center between left-most camera button and play/pause button
+ m_pCurTimeLabel->SizeToContents();
+ m_pTotalTimeLabel->SizeToContents();
+}
+
+void CReplayPerformanceEditorPanel::UpdateCameraSelectionPosition( CameraMode_t nCameraMode )
+{
+ Assert( nCameraMode >= 0 && nCameraMode < NCAMS );
+ m_iCameraSelection = nCameraMode;
+
+ UpdateCameraButtonImages();
+}
+
+void CReplayPerformanceEditorPanel::UpdateFreeCamSettings( const SetViewParams_t &params )
+{
+ CCameraOptionsPanel_Free *pSettingsPanel = dynamic_cast< CCameraOptionsPanel_Free * >( m_pCameraOptionsPanels[ CAM_FREE ] );
+ if ( !pSettingsPanel )
+ return;
+
+ pSettingsPanel->SetValue( CCameraOptionsPanel_Free::SLIDER_ACCEL, params.m_flAccel );
+ pSettingsPanel->SetValue( CCameraOptionsPanel_Free::SLIDER_SPEED, params.m_flSpeed );
+ pSettingsPanel->SetValue( CCameraOptionsPanel_Free::SLIDER_FOV, params.m_flFov );
+ pSettingsPanel->SetValue( CCameraOptionsPanel_Free::SLIDER_ROTFILTER, params.m_flRotationFilter );
+}
+
+void CReplayPerformanceEditorPanel::UpdateTimeScale( float flScale )
+{
+ CTimeScaleOptionsPanel *pSettingsPanel = dynamic_cast< CTimeScaleOptionsPanel * >( m_pCameraOptionsPanels[ COMPONENT_TIMESCALE ] );
+ if ( !pSettingsPanel )
+ return;
+
+ pSettingsPanel->SetValue( CTimeScaleOptionsPanel::SLIDER_TIMESCALE, flScale );
+}
+
+void CReplayPerformanceEditorPanel::LayoutPlayerCells()
+{
+ int nPanelHeight = m_pPlayerCellsPanel->GetTall();
+ int nCellBuffer = XRES(1);
+ for ( int i = 0; i < 2; ++i )
+ {
+ int nCurX = m_nRedBlueLabelRightX;
+
+ for ( int j = 0; j <= MAX_PLAYERS; ++j )
+ {
+ CPlayerCell *pCurCell = m_pPlayerCells[i][j];
+ if ( !pCurCell->IsVisible() )
+ continue;
+
+ // Apply cached settings from .res file
+ if ( m_pPlayerCellData )
+ {
+ pCurCell->ApplySettings( m_pPlayerCellData );
+ }
+
+ const int nY = nPanelHeight/2 + m_nRedBlueSigns[i] * nPanelHeight/4 - pCurCell->GetTall()/2;
+ pCurCell->SetPos(
+ nCurX,
+ nY
+ );
+
+ nCurX += pCurCell->GetWide() + nCellBuffer;
+ }
+ }
+}
+
+void CReplayPerformanceEditorPanel::PerformLayout()
+{
+ int w = ScreenWidth(), h = ScreenHeight();
+ SetBounds(0,0,w,h);
+
+ // Layout camera options panels
+ for ( int i = 0; i < NCAMS; ++i )
+ {
+ CCameraOptionsPanel *pCurOptionsPanel = m_pCameraOptionsPanels[ i ];
+ if ( !pCurOptionsPanel )
+ continue;
+
+ CExImageButton *pCurCameraButton = m_pCameraButtons[ i ];
+ if ( !pCurCameraButton )
+ continue;
+
+ // Get camera button position
+ int aCameraButtonPos[2];
+ int aBottomPos[2];
+ pCurCameraButton->GetPos( aCameraButtonPos[ 0 ], aCameraButtonPos[ 1 ] );
+ m_pBottom->GetPos( aBottomPos[ 0 ], aBottomPos[ 1 ] );
+
+ // Layout the panel now - it should set its own size, which we need to know to position it properly
+ pCurOptionsPanel->InvalidateLayout( true, true );
+
+ // Position it
+ pCurOptionsPanel->SetPos(
+ aBottomPos[ 0 ] + aCameraButtonPos[ 0 ] + pCurCameraButton->GetWide() - pCurOptionsPanel->GetWide() - XRES( 3 ),
+ aBottomPos[ 1 ] + aCameraButtonPos[ 1 ] - pCurOptionsPanel->GetTall()
+ );
+ }
+
+ // Setup menu position relative to menu button
+ int aMenuButtonPos[2];
+ m_pMenuButton->GetPos( aMenuButtonPos[0], aMenuButtonPos[1] );
+ m_pMenu->SetPos( aMenuButtonPos[0], aMenuButtonPos[1] + m_pMenuButton->GetTall() );
+
+ // Set player cell panel to be the size of half the bottom panel
+ int aBottomSize[2];
+ m_pBottom->GetSize( aBottomSize[0], aBottomSize[1] );
+ m_pPlayerCellsPanel->SetBounds( 0, 0, aBottomSize[0] / 2, m_pPlayerCellsPanel->GetTall() );
+
+ CExLabel *pRedBlueLabels[2] = {
+ dynamic_cast< CExLabel * >( m_pPlayerCellsPanel->FindChildByName( "RedLabel" ) ),
+ dynamic_cast< CExLabel * >( m_pPlayerCellsPanel->FindChildByName( "BlueLabel" ) )
+ };
+ int nMargins[2] = { XRES( 5 ), YRES( 2 ) };
+ for ( int i = 0; i < 2; ++i )
+ {
+ pRedBlueLabels[i]->SizeToContents();
+
+ const int nY = m_pPlayerCellsPanel->GetTall()/2 + m_nRedBlueSigns[i] * m_pPlayerCellsPanel->GetTall()/4 - pRedBlueLabels[i]->GetTall()/2;
+ pRedBlueLabels[i]->SetPos( nMargins[0], nY );
+
+ m_nRedBlueLabelRightX = MAX( m_nRedBlueLabelRightX, nMargins[0] + pRedBlueLabels[i]->GetWide() + nMargins[0] );
+ }
+
+ // Position player cells
+ LayoutPlayerCells();
+
+ BaseClass::PerformLayout();
+}
+
+bool CReplayPerformanceEditorPanel::OnStateChangeRequested( const char *pEventStr )
+{
+ // If we're already recording, allow the change.
+ if ( g_pReplayPerformanceController->IsRecording() )
+ return true;
+
+ // If we aren't recording and there is no forthcoming data in the playback stream, allow the change.
+ if ( !g_pReplayPerformanceController->IsPlaybackDataLeft() )
+ return true;
+
+ // Otherwise, record the event string and show a dialog asking the user if they're sure they want to nuke.
+ V_strncpy( m_szSuspendedEvent, pEventStr, sizeof( m_szSuspendedEvent ) );
+ ShowConfirmDialog( "#Replay_Warning", "#Replay_NukePerformanceChanges", "#GameUI_Confirm", "#GameUI_CancelBold", OnConfirmDestroyChanges, this, this, REPLAY_SOUND_DIALOG_POPUP );
+
+ return false;
+}
+
+void CReplayPerformanceEditorPanel::SetButtonTip( wchar_t *pTipText, Panel *pContextPanel )
+{
+ // Set the text
+ m_pButtonTip->SetText( pTipText );
+ m_pButtonTip->InvalidateLayout( true, true );
+
+ // Center relative to context panel
+ int aPos[2];
+ ipanel()->GetAbsPos( pContextPanel->GetVPanel(), aPos[0], aPos[1] );
+ const int nX = clamp(
+ aPos[0] - m_pButtonTip->GetWide() / 2,
+ 0,
+ ScreenWidth() - m_pButtonTip->GetWide() - (int) XRES( 40 )
+ );
+ const int nY = m_nBottomPanelStartY - m_pButtonTip->GetTall() - (int) YRES( 2 );
+ m_pButtonTip->SetPos( nX, nY );
+}
+
+void CReplayPerformanceEditorPanel::ShowButtonTip( bool bShow )
+{
+ m_pButtonTip->SetVisible( bShow );
+}
+
+void CReplayPerformanceEditorPanel::ShowSavingDialog()
+{
+ Assert( !m_pSavingDlg );
+ m_pSavingDlg = new CSavingDialog( ReplayUI_GetPerformanceEditor() );
+ ShowWaitingDialog( m_pSavingDlg, "#Replay_Saving", true, false, -1 );
+}
+
+void CReplayPerformanceEditorPanel::ShowPanel( bool bShow )
+{
+ if ( bShow == IsVisible() )
+ return;
+
+ if ( bShow )
+ {
+ // We are now performing.
+ m_pRecLightPanel->SetPerforming( true );
+
+ // Disable keyboard input on all panels added to the list
+ FOR_EACH_LL( m_lstDisableKeyboardInputPanels, it )
+ {
+ m_lstDisableKeyboardInputPanels[ it ]->SetKeyBoardInputEnabled( false );
+ }
+
+ DisplayPerformanceTip( "#Replay_PerfTip_ExitPerfMode", &replay_perftip_count_exit, MAX_TIP_DISPLAYS );
+
+ // Fire a message the game DLL can intercept (for achievements, etc).
+ IGameEvent *event = gameeventmanager->CreateEvent( "entered_performance_mode" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+
+ // Play a sound
+ surface()->PlaySound( "replay\\enterperformancemode.wav" );
+ }
+ else
+ {
+ // Display a tip
+ DisplayPerformanceTip( "#Replay_PerfTip_EnterPerfMode", &replay_perftip_count_enter, MAX_TIP_DISPLAYS );
+
+ // Play a sound
+ surface()->PlaySound( "replay\\exitperformancemode.wav" );
+ }
+
+ // Show mouse cursor
+ SetMouseInputEnabled( bShow );
+ SetVisible( bShow );
+ MakePopup( bShow );
+
+ // Avoid waiting for next OnThink() to hide background images
+ m_pRecLightPanel->UpdatePauseState( bShow );
+ m_pRecLightPanel->UpdateBackgroundVisibility();
+
+ // Play or pause
+ if ( bShow )
+ {
+ PauseDemo();
+ }
+ else
+ {
+ PlayDemo();
+ }
+
+ // Keep controller informed about pause state so that it can throw away unimportant events during pause if it's recording.
+ g_pReplayPerformanceController->NotifyPauseState( bShow );
+}
+
+bool CReplayPerformanceEditorPanel::OnEndOfReplayReached()
+{
+ if ( m_bShownAtLeastOnce )
+ {
+ ShowPanel( true );
+ DisplayPerformanceTip( "#Replay_PerfTip_EndOfReplayReached" );
+
+ // Don't end demo playback yet.
+ return true;
+ }
+
+ // Let the demo player end demo playback
+ return false;
+}
+
+void CReplayPerformanceEditorPanel::AddSetViewEvent()
+{
+ if ( !g_pReplayManager->GetPlayingReplay() )
+ return;
+
+ if ( !g_pReplayPerformanceController )
+ return;
+
+ Vector pos;
+ QAngle angles;
+ float fov;
+ ReplayCamera()->GetCachedView( pos, angles, fov );
+
+ SetViewParams_t params;
+ params.m_flTime = GetPlaybackTime();
+ params.m_flFov = fov;
+ params.m_pOrigin = &pos;
+ params.m_pAngles = &angles;
+
+ params.m_flAccel = ReplayCamera()->m_flRoamingAccel;
+ params.m_flSpeed = ReplayCamera()->m_flRoamingSpeed;
+ params.m_flRotationFilter = ReplayCamera()->m_flRoamingRotFilterFactor;
+
+ g_pReplayPerformanceController->AddEvent_Camera_SetView( params );
+}
+
+// Input should be in [0,1]
+void CReplayPerformanceEditorPanel::AddTimeScaleEvent( float flTimeScale )
+{
+ if ( !g_pReplayManager->GetPlayingReplay() )
+ return;
+
+ if ( !g_pReplayPerformanceController )
+ return;
+
+ g_pReplayPerformanceController->AddEvent_TimeScale( GetPlaybackTime(), flTimeScale );
+}
+
+void CReplayPerformanceEditorPanel::UpdateCameraButtonImages( bool bForceUnselected/*=false*/ )
+{
+ CReplayPerformance *pPerformance = GetPerformance();
+ for ( int i = 0; i < NCAMS; ++i )
+ {
+ CFmtStr fmtFile(
+ gs_pBaseComponentNames[i],
+ gs_pCamNames[i],
+ ( !bForceUnselected && ( !pPerformance || g_pReplayPerformanceController->IsRecording() ) && i == m_iCameraSelection ) ? "_selected" : ""
+ );
+
+ if ( m_pCameraButtons[ i ] )
+ {
+ m_pCameraButtons[ i ]->SetSubImage( fmtFile.Access() );
+ }
+ }
+}
+
+void CReplayPerformanceEditorPanel::EnsureRecording( bool bShouldSnip )
+{
+ // Not recording?
+ if ( !g_pReplayPerformanceController->IsRecording() )
+ {
+ // Start recording - snip if needed.
+ g_pReplayPerformanceController->StartRecording( GetReplay(), bShouldSnip );
+ }
+}
+
+void CReplayPerformanceEditorPanel::ToggleMenu()
+{
+ if ( !m_pMenu )
+ return;
+
+ // Show/hide
+ const bool bShow = !m_pMenu->IsVisible();
+ m_pMenu->SetVisible( bShow );
+}
+
+void CReplayPerformanceEditorPanel::SaveAs( const wchar_t *pTitle )
+{
+ if ( !g_pReplayPerformanceController->SaveAsAsync( pTitle ) )
+ {
+ DisplaySavedTip( false );
+ }
+
+ ShowSavingDialog();
+}
+
+/*static*/ void CReplayPerformanceEditorPanel::OnConfirmSaveAs( bool bShouldSave, wchar_t *pTitle, void *pContext )
+{
+ // NOTE: Assumes that overwriting has already been confirmed by the user.
+
+ if ( !bShouldSave )
+ return;
+
+ CReplayPerformanceEditorPanel *pThis = (CReplayPerformanceEditorPanel *)pContext;
+ pThis->SaveAs( pTitle );
+
+ surface()->PlaySound( "replay\\saved_take.wav" );
+}
+
+void CReplayPerformanceEditorPanel::ShowRewindConfirmMessage()
+{
+ ShowMessageBox( "#Replay_RewindWarningTitle", "#Replay_RewindWarningMsg", "#GameUI_OK", OnConfirmRewind, NULL, (void *)this );
+ surface()->PlaySound( "replay\\replaydialog_warn.wav" );
+}
+
+/*static*/ void CReplayPerformanceEditorPanel::OnConfirmRewind( bool bConfirmed, void *pContext )
+{
+ if ( bConfirmed )
+ {
+ if ( pContext )
+ {
+ CReplayPerformanceEditorPanel *pEditor = (CReplayPerformanceEditorPanel *)pContext;
+ pEditor->OnCommand( "goto_back" );
+ }
+ }
+}
+
+void CReplayPerformanceEditorPanel::OnMenuCommand_Save( bool bExitEditorWhenDone/*=false*/ )
+{
+ // If this is the first time we're saving this performance, do a save-as.
+ if ( !g_pReplayPerformanceController->HasSavedPerformance() )
+ {
+ OnMenuCommand_SaveAs( bExitEditorWhenDone );
+ return;
+ }
+
+ // Regular save
+ if ( !g_pReplayPerformanceController->SaveAsync() )
+ {
+ DisplaySavedTip( false );
+ }
+
+ // Show saving dialog
+ ShowSavingDialog();
+
+ // Exit editor?
+ if ( bExitEditorWhenDone )
+ {
+ OnMenuCommand_Exit();
+ }
+}
+
+void CReplayPerformanceEditorPanel::OnMenuCommand_SaveAs( bool bExitEditorWhenDone/*=false*/ )
+{
+ ReplayUI_ShowPerformanceSaveDlg( OnConfirmSaveAs, this, GetReplay(), bExitEditorWhenDone );
+}
+
+void CReplayPerformanceEditorPanel::DisplaySavedTip( bool bSucceess )
+{
+ DisplayPerformanceTip( bSucceess ? "#Replay_PerfTip_Saved" : "#Replay_PerfTip_SaveFailed" );
+}
+
+void CReplayPerformanceEditorPanel::OnSaveComplete()
+{
+ DisplaySavedTip( g_pReplayPerformanceController->GetLastSaveStatus() );
+
+ m_pSavingDlg = NULL;
+}
+
+void CReplayPerformanceEditorPanel::HandleUiToggle()
+{
+ if ( !TFModalStack()->IsEmpty() )
+ return;
+
+ PauseDemo();
+ Exit_ShowDialogs();
+}
+
+void CReplayPerformanceEditorPanel::Exit()
+{
+ engine->ClientCmd_Unrestricted( "disconnect" );
+}
+
+void CReplayPerformanceEditorPanel::Exit_ShowDialogs()
+{
+ if ( g_pReplayPerformanceController->IsDirty() )
+ {
+ ShowConfirmDialog( "#Replay_DiscardTitle", "#Replay_DiscardChanges", "#Replay_Discard", "#Replay_Cancel", OnConfirmDiscard, NULL, this, REPLAY_SOUND_DIALOG_POPUP );
+ }
+ else
+ {
+ ShowConfirmDialog( "#Replay_ExitEditorTitle", "#Replay_BackToReplays", "#GameUI_Confirm", "#Replay_Cancel", OnConfirmExit, NULL, this, REPLAY_SOUND_DIALOG_POPUP );
+ }
+}
+
+void CReplayPerformanceEditorPanel::OnMenuCommand_Exit()
+{
+ Exit_ShowDialogs();
+}
+
+void CReplayPerformanceEditorPanel::OnCommand( const char *command )
+{
+ float flCurTime = GetPlaybackTime();
+
+ g_bIsReplayRewinding = false;
+
+ if ( !V_stricmp( command, "toggle_menu" ) )
+ {
+ ToggleMenu();
+ }
+ else if ( !V_strnicmp( command, "menu_", 5 ) )
+ {
+ const char *pMenuCommand = command + 5;
+
+ if ( !V_stricmp( pMenuCommand, "save" ) )
+ {
+ OnMenuCommand_Save();
+ }
+ else if ( !V_stricmp( pMenuCommand, "saveas" ) )
+ {
+ OnMenuCommand_SaveAs();
+ }
+ else if ( !V_stricmp( pMenuCommand, "exit" ) )
+ {
+ OnMenuCommand_Exit();
+ }
+ }
+ else if ( !V_stricmp( command, "close" ) )
+ {
+ ShowPanel( false );
+ MarkForDeletion();
+ return;
+ }
+ else if ( !V_stricmp( command, "play" ) )
+ {
+ ShowPanel( false );
+ return;
+ }
+ else if ( !V_stricmp( command, "pause" ) )
+ {
+ ShowPanel( true );
+ return;
+ }
+ else if ( !V_strnicmp( command, "timescale_", 10 ) )
+ {
+ const char *pTimeScaleCmd = command + 10;
+ if ( !V_stricmp( pTimeScaleCmd, "showpanel" ) )
+ {
+ // If we're playing back, pop up a dialog asking if the user is sure they want to nuke the
+ // rest of whatever is playing back.
+ if ( !OnStateChangeRequested( command ) )
+ return;
+
+ EnsureRecording();
+ }
+ }
+ else if ( !V_strnicmp( command, "settick_", 8 ) )
+ {
+ const char *pSetType = command + 8;
+ const int nCurTick = engine->GetDemoPlaybackTick();
+
+ if ( !V_stricmp( pSetType, "in" ) )
+ {
+ SetOrRemoveInTick( nCurTick, true );
+ }
+ else if ( !V_stricmp( pSetType, "out" ) )
+ {
+ SetOrRemoveOutTick( nCurTick, true );
+ }
+
+ // Save the replay
+ CReplay *pReplay = GetReplay();
+ if ( pReplay )
+ {
+ g_pReplayManager->FlagReplayForFlush( pReplay, true );
+ }
+
+ return;
+ }
+ else if ( !V_strnicmp( command, "goto_", 5 ) )
+ {
+ const char *pGotoType = command + 5;
+ CReplay *pReplay = GetReplay();
+ if ( pReplay )
+ {
+ const CReplayPerformance *pScratchPerformance = g_pReplayPerformanceController->GetPerformance();
+ const CReplayPerformance *pSavedPerformance = g_pReplayPerformanceController->GetSavedPerformance();
+ const CReplayPerformance *pPerformance = pScratchPerformance ? pScratchPerformance : pSavedPerformance;
+
+ const int nCurTick = engine->GetDemoPlaybackTick();
+
+ // If in or out ticks are set in the performance, use those for the 'full' rewind/fast-forward
+ const int nStartTick = MAX( 0, ( pPerformance && pPerformance->HasInTick() ) ? pPerformance->m_nTickIn : pReplay->m_nSpawnTick );
+ const int nEndTick = MAX( // The MAX() here will keep us from going back in time if we're already past the "end" tick
+ nCurTick,
+ ( ( pPerformance && pPerformance->HasOutTick() ) ?
+ pPerformance->m_nTickOut :
+ ( nStartTick + TIME_TO_TICKS( pReplay->m_flLength ) ) )
+ - TIME_TO_TICKS( 0.1f )
+ );
+
+ int nGotoTick = 0;
+ bool bGoingBack = false;
+
+ if ( !V_stricmp( pGotoType, "start" ) )
+ {
+ bGoingBack = true;
+ nGotoTick = nStartTick;
+ }
+ else if ( !V_stricmp( pGotoType, "back" ) )
+ {
+ // If this is the first time rewinding, display a message
+ if ( !replay_replayeditor_rewindmsgcounter.GetBool() )
+ {
+ replay_replayeditor_rewindmsgcounter.SetValue( 1 );
+ ShowRewindConfirmMessage();
+ return;
+ }
+
+ bGoingBack = true;
+ nGotoTick = nCurTick - TIME_TO_TICKS( 10.0f );
+ }
+ else if ( !V_stricmp( pGotoType, "end" ) )
+ {
+ nGotoTick = nEndTick; // Don't go back in time
+ }
+
+ // Clamp it
+ nGotoTick = clamp( nGotoTick, nStartTick, nEndTick );
+
+ // If going back...
+ if ( bGoingBack )
+ {
+ // ...and notify the recorder that we're skipping, which we only need to do if we're going backwards
+ g_pReplayPerformanceController->NotifyRewinding();
+ g_bIsReplayRewinding = true;
+ }
+
+ // Go to the given tick and pause
+ CFmtStr fmtCmd( "demo_gototick %i\ndemo_pause\n", nGotoTick );
+ engine->ClientCmd_Unrestricted( fmtCmd.Access() );
+ }
+ return;
+ }
+ else if ( !V_strnicmp( command, "setcamera_", 10 ) )
+ {
+ const char *pCamType = command + 10;
+ int nEntIndex = ReplayCamera()->GetPrimaryTargetIndex();
+
+ // If we're playing back, pop up a dialog asking if the user is sure they want to nuke the
+ // rest of whatever is playing back.
+ if ( !OnStateChangeRequested( command ) )
+ return;
+
+ EnsureRecording();
+
+ if ( !V_stricmp( pCamType, "first" ) )
+ {
+ ReplayCamera()->SetMode( OBS_MODE_IN_EYE );
+ UpdateCameraSelectionPosition( CAM_FIRST );
+ g_pReplayPerformanceController->AddEvent_Camera_Change_FirstPerson( flCurTime, nEntIndex );
+ }
+ else if ( !V_stricmp( pCamType, "third" ) )
+ {
+ ReplayCamera()->SetMode( OBS_MODE_CHASE );
+ UpdateCameraSelectionPosition( CAM_THIRD );
+ g_pReplayPerformanceController->AddEvent_Camera_Change_ThirdPerson( flCurTime, nEntIndex );
+ AddSetViewEvent();
+ }
+ else if ( !V_stricmp( pCamType, "free" ) )
+ {
+ ReplayCamera()->SetMode( OBS_MODE_ROAMING );
+ UpdateCameraSelectionPosition( CAM_FREE );
+ g_pReplayPerformanceController->AddEvent_Camera_Change_Free( flCurTime );
+ AddSetViewEvent();
+ DisplayPerformanceTip( "#Replay_PerfTip_EnterFreeCam", &replay_perftip_count_freecam_enter, MAX_TIP_DISPLAYS );
+ }
+
+ return;
+ }
+ else
+ {
+ engine->ClientCmd( const_cast<char *>( command ) );
+ return;
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+void CReplayPerformanceEditorPanel::OnConfirmDestroyChanges( bool bConfirmed, void *pContext )
+{
+ AssertMsg( pContext, "Should have a context! Fix me!" );
+ if ( pContext && bConfirmed )
+ {
+ CReplayPerformanceEditorPanel *pEditorPanel = (CReplayPerformanceEditorPanel *)pContext;
+ if ( bConfirmed )
+ {
+ CReplay *pReplay = pEditorPanel->GetReplay();
+ g_pReplayPerformanceController->StartRecording( pReplay, true );
+
+ // Reissue the command.
+ pEditorPanel->OnCommand( pEditorPanel->m_szSuspendedEvent );
+
+ // Play a sound
+ surface()->PlaySound( "replay\\snip.wav" );
+ }
+
+ // Clear suspended event
+ pEditorPanel->m_szSuspendedEvent[ 0 ] = '\0';
+
+ // Make sure mouse is free
+ pEditorPanel->SetMouseInputEnabled( true );
+
+ DisplayPerformanceTip( "#Replay_PerfTip_Snip" );
+ }
+}
+
+/*static*/ void CReplayPerformanceEditorPanel::OnConfirmDiscard( bool bConfirmed, void *pContext )
+{
+ CReplayPerformanceEditorPanel *pEditor = (CReplayPerformanceEditorPanel *)pContext;
+ if ( bConfirmed )
+ {
+ pEditor->Exit();
+ }
+ else
+ {
+ if ( !pEditor->IsVisible() )
+ {
+ PlayDemo();
+ }
+ }
+}
+
+/*static*/ void CReplayPerformanceEditorPanel::OnConfirmExit( bool bConfirmed, void *pContext )
+{
+ CReplayPerformanceEditorPanel *pEditor = (CReplayPerformanceEditorPanel *)pContext;
+ if ( bConfirmed )
+ {
+ pEditor->Exit();
+ }
+ else
+ {
+ if ( !pEditor->IsVisible() )
+ {
+ PlayDemo();
+ }
+ }
+}
+
+void CReplayPerformanceEditorPanel::SetOrRemoveInTick( int nTick, bool bRemoveIfSet )
+{
+ SetOrRemoveTick( nTick, true, bRemoveIfSet );
+}
+
+void CReplayPerformanceEditorPanel::SetOrRemoveOutTick( int nTick, bool bRemoveIfSet )
+{
+ SetOrRemoveTick( nTick, false, bRemoveIfSet );
+}
+
+void CReplayPerformanceEditorPanel::SetOrRemoveTick( int nTick, bool bUseInTick, bool bRemoveIfSet )
+{
+ CReplayPerformance *pPerformance = GetPerformance();
+ AssertMsg( pPerformance, "Performance should always be valid by this point." );
+
+ ControlButtons_t iButton;
+ int *pResultTick;
+ const char *pSetTickKey;
+ const char *pUnsetTickKey;
+ if ( bUseInTick )
+ {
+ pResultTick = &pPerformance->m_nTickIn;
+ iButton = CTRLBUTTON_IN;
+ pSetTickKey = "#Replay_PerfTip_InPointSet";
+ pUnsetTickKey = "#Replay_PerfTip_InPointRemoved";
+ }
+ else
+ {
+ pResultTick = &pPerformance->m_nTickOut;
+ iButton = CTRLBUTTON_OUT;
+ pSetTickKey = "#Replay_PerfTip_OutPointSet";
+ pUnsetTickKey = "#Replay_PerfTip_OutPointRemoved";
+ }
+
+ // Tick explicitly being removed? Caller passing in -1?
+ const bool bRemoving = nTick < 0;
+
+ // If tick already exists and we want to remove, remove it
+ bool bSetting;
+ if ( ( *pResultTick >= 0 && bRemoveIfSet ) || bRemoving )
+ {
+ *pResultTick = -1;
+ bSetting = false;
+ }
+ else
+ {
+ *pResultTick = nTick;
+ bSetting = true;
+ }
+
+ // Display the appropriate tip
+ DisplayPerformanceTip( bSetting ? pSetTickKey : pUnsetTickKey );
+
+ // Select/unselect button
+ CExImageButton *pButton = m_pCtrlButtons[ iButton ];
+ pButton->SetSelected( bSetting );
+ pButton->InvalidateLayout( true, true ); // Without this, buttons don't update immediately
+
+ // Mark the performance as dirty
+ g_pReplayPerformanceController->NotifyDirty();
+}
+
+CReplay *CReplayPerformanceEditorPanel::GetReplay()
+{
+ return g_pReplayManager->GetReplay( m_hReplay );
+}
+
+void CReplayPerformanceEditorPanel::OnRewindComplete()
+{
+ // Get rid of any "selected" icon - this will happen as soon as we actually start playing back
+ // events, but if we aren't playing back events yet we need to explicitly tell the icons not
+ // to display their "selected" versions.
+ UpdateCameraButtonImages( true );
+}
+
+//-----------------------------------------------------------------------------
+
+static DHANDLE<CReplayPerformanceEditorPanel> g_ReplayPerformanceEditorPanel;
+
+//-----------------------------------------------------------------------------
+
+CReplayPerformanceEditorPanel *ReplayUI_InitPerformanceEditor( ReplayHandle_t hReplay )
+{
+ if ( !g_ReplayPerformanceEditorPanel.Get() )
+ {
+ g_ReplayPerformanceEditorPanel = SETUP_PANEL( new CReplayPerformanceEditorPanel( NULL, hReplay ) );
+ g_ReplayPerformanceEditorPanel->InvalidateLayout( false, true );
+ }
+
+ // Notify recorder of editor
+ g_pReplayPerformanceController->SetEditor( g_ReplayPerformanceEditorPanel.Get() );
+
+ return g_ReplayPerformanceEditorPanel;
+}
+
+void ReplayUI_ClosePerformanceEditor()
+{
+ if ( g_ReplayPerformanceEditorPanel )
+ {
+ g_ReplayPerformanceEditorPanel->MarkForDeletion();
+ g_ReplayPerformanceEditorPanel = NULL;
+ }
+}
+
+CReplayPerformanceEditorPanel *ReplayUI_GetPerformanceEditor()
+{
+ return g_ReplayPerformanceEditorPanel;
+}
+
+#if _DEBUG
+CON_COMMAND_F( replay_showperfeditor, "Show performance editor", FCVAR_CLIENTDLL )
+{
+ ReplayUI_ClosePerformanceEditor();
+ ReplayUI_InitPerformanceEditor( REPLAY_HANDLE_INVALID );
+}
+
+CON_COMMAND_F( replay_tiptest, "", FCVAR_CLIENTDLL )
+{
+ DisplayPerformanceTip( "#Replay_PerfTip_EnterFreeCam" );
+}
+#endif
+
+#endif
diff --git a/sp/src/game/client/replay/vgui/replayperformanceeditor.h b/sp/src/game/client/replay/vgui/replayperformanceeditor.h
new file mode 100644
index 00000000..1fe11f20
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayperformanceeditor.h
@@ -0,0 +1,239 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#if defined( REPLAY_ENABLED )
+
+#ifndef REPLAYPERFORMANCEEDITOR_H
+#define REPLAYPERFORMANCEEDITOR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "vgui_controls/EditablePanel.h"
+#include "vgui_controls/ImagePanel.h"
+#include "vgui_controls/ImageList.h"
+#include "tf/vgui/tf_controls.h"
+#include "replay/replayhandle.h"
+#include "replay/ireplayperformanceeditor.h"
+#include "replay/ireplayperformancecontroller.h"
+
+//-----------------------------------------------------------------------------
+
+class CPlayerCell;
+class CCameraOptionsPanel;
+class CRecLightPanel;
+class CReplay;
+class CReplayPerformance;
+class CReplayTipLabel;
+class CSavingDialog;
+
+//-----------------------------------------------------------------------------
+
+// NOTE: Should not change order here - if you do, you need to modify g_pCamNames.
+enum CameraMode_t
+{
+ CAM_INVALID = -1,
+ CAM_FREE,
+ CAM_THIRD,
+ CAM_FIRST,
+ COMPONENT_TIMESCALE,
+ NCAMS
+};
+
+//-----------------------------------------------------------------------------
+
+class CReplayPerformanceEditorPanel : public vgui::EditablePanel,
+ public IReplayPerformanceEditor
+{
+ DECLARE_CLASS_SIMPLE( CReplayPerformanceEditorPanel, vgui::EditablePanel );
+public:
+ CReplayPerformanceEditorPanel( Panel *parent, ReplayHandle_t hReplay );
+ virtual ~CReplayPerformanceEditorPanel();
+
+ virtual void ShowPanel( bool bShow );
+
+ bool OnEndOfReplayReached();
+ void OnInGameMouseWheelEvent( int nDelta );
+ void UpdateCameraSelectionPosition( CameraMode_t nCameraMode );
+ void UpdateFreeCamSettings( const SetViewParams_t &params );
+ void UpdateTimeScale( float flScale );
+ void HandleUiToggle();
+ void Exit();
+ void Exit_ShowDialogs();
+
+private:
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void ApplySettings( KeyValues *pInResourceData );
+ virtual void PerformLayout();
+ virtual void OnCommand( const char *command );
+ virtual void OnMouseWheeled( int nDelta );
+ virtual void OnTick();
+
+ void Achievements_Think( float flElapsed );
+ void Achievements_OnSpaceBarPressed();
+ void Achievements_Grant();
+
+ friend class CReplayButton;
+ friend class CSavingDialog;
+
+ void SetButtonTip( wchar_t *pTipText, Panel *pContextPanel );
+ void ShowButtonTip( bool bShow );
+
+ void ShowSavingDialog();
+
+ //
+ // IReplayPerformanceEditor:
+ //
+ virtual CReplay *GetReplay();
+ virtual void OnRewindComplete();
+
+ // Called when the user attempts to change to a different camera, etc.
+ // Returns true if request is immediately granted - false means the event
+ // was queued and the user has been asked if they are OK with nuking any
+ // changes after the current time.
+ bool OnStateChangeRequested( const char *pEventStr );
+
+ void EnsureRecording( bool bShouldSnip = true ); // Start recording now if not already doing so
+
+ bool IsPaused();
+
+ void UpdateCameraButtonImages( bool bForceUseUnselected = false );
+ void LayoutPlayerCells();
+ void SetupHighlightPanel( EditablePanel *pPanel, CPlayerCell *pPlayerCell );
+ void UpdateTimeLabels();
+ void ClearPlayerCellData();
+
+ void HandleMouseWheel( int nDelta );
+
+private:
+ enum ControlButtons_t
+ {
+ CTRLBUTTON_IN,
+ CTRLBUTTON_GOTOBEGINNING,
+ CTRLBUTTON_REWIND,
+ CTRLBUTTON_PLAY,
+ CTRLBUTTON_FF,
+ CTRLBUTTON_GOTOEND,
+ CTRLBUTTON_OUT,
+
+ NUM_CTRLBUTTONS
+ };
+
+ CReplayPerformance *GetPerformance() const;
+ CReplayPerformance *GetSavedPerformance() const;
+
+ int GetCameraModeFromButtonIndex( CameraMode_t iCamera );
+ void AddSetViewEvent();
+ void AddTimeScaleEvent( float flTimeScale );
+ void AddPanelKeyboardInputDisableList( vgui::Panel *pPanel );
+ CameraMode_t IsMouseOverActiveCameraOptionsPanel( int nMouseX, int nMouseY );
+ void SetOrRemoveInTick( int nTick, bool bRemoveIfSet );
+ void SetOrRemoveOutTick( int nTick, bool bRemoveIfSet );
+ void SetOrRemoveTick( int nTick, bool bUseInTick, bool bRemoveIfSet );
+ void ToggleMenu();
+ void OnMenuCommand_Save( bool bExitEditorWhenDone = false );
+ void OnMenuCommand_SaveAs( bool bExitEditorWhenDone = false );
+ void OnMenuCommand_Exit();
+ void DisplaySavedTip( bool bSucceess );
+ void OnSaveComplete();
+
+ void SaveAs( const wchar_t *pTitle );
+
+ void ShowRewindConfirmMessage();
+
+ static void OnConfirmSaveAs( bool bShouldSave, wchar_t *pTitle, void *pContext );
+ static void OnConfirmDestroyChanges( bool bConfirmed, void *pContext );
+ static void OnConfirmDiscard( bool bConfirmed, void *pContext );
+ static void OnConfirmExit( bool bConfirmed, void *pContext );
+ static void OnConfirmRewind( bool bConfirmed, void *pContext );
+
+ MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", pParams );
+
+ ReplayHandle_t m_hReplay;
+
+ float m_flLastTime; // Can't use gpGlobals->frametime when playback is paused
+ float m_flOldFps;
+
+ CExLabel *m_pCurTimeLabel;
+ CExLabel *m_pTotalTimeLabel;
+ CExLabel *m_pPlayerNameLabel;
+
+ KeyValues *m_pPlayerCellData;
+ CPlayerCell *m_pPlayerCells[2][MAX_PLAYERS+1];
+ vgui::ImageList *m_pImageList;
+
+ EditablePanel *m_pMouseTargetPanel;
+ EditablePanel *m_pBottom;
+ CPlayerCell *m_pCurTargetCell;
+
+ CExImageButton *m_pCameraButtons[NCAMS];
+ CExImageButton *m_pCtrlButtons[NUM_CTRLBUTTONS];
+
+ float m_flTimeScaleProxy;
+
+ EditablePanel *m_pPlayerCellsPanel;
+
+ vgui::ImagePanel *m_pCameraSelection;
+ CameraMode_t m_iCameraSelection; // NOTE: Indexes into some arrays
+
+ CReplayTipLabel *m_pButtonTip;
+ CSavingDialog *m_pSavingDlg;
+
+ enum MenuItems_t
+ {
+ MENU_SAVE,
+ MENU_SAVEAS,
+ MENU_EXIT,
+
+ NUM_MENUITEMS
+ };
+
+ CExImageButton *m_pMenuButton;
+ vgui::Menu *m_pMenu;
+ int m_aMenuItemIds[ NUM_MENUITEMS ];
+
+ CExButton *m_pSlowMoButton;
+
+ CCameraOptionsPanel *m_pCameraOptionsPanels[NCAMS];
+
+ CUtlLinkedList< vgui::Panel *, int > m_lstDisableKeyboardInputPanels;
+
+ int m_nRedBlueLabelRightX;
+ int m_nBottomPanelStartY;
+ int m_nBottomPanelHeight;
+ int m_nRedBlueSigns[2];
+ int m_iCurPlayerTarget;
+ float m_flSpaceDownStart; // The time at which user started holding down space bar
+ bool m_bSpaceDown;
+ bool m_bSpacePressed;
+ int m_nLastRoundedTime;
+ bool m_bMousePressed;
+ bool m_bMouseDown;
+ float m_flDefaultFramerate; // host_framerate before perf editor started mucking about with it
+ CameraMode_t m_nMouseClickedOverCameraSettingsPanel; // Allows user to drag slider outside of camera settings panel w/o the panel disappearing
+ CRecLightPanel *m_pRecLightPanel;
+ bool m_bShownAtLeastOnce; // Has the replay editor shown at least once? In other words, has the user hit the space bar at all yet?
+ char m_szSuspendedEvent[128];
+
+ bool m_bAchievementAwarded; // Was an achievement awarded during this editing session?
+ float m_flLastTimeSpaceBarPressed;
+ float m_flActiveTimeInEditor; // Will be zero'd out if user is idle (ie if they don't press space bar often enough)
+
+ CPanelAnimationVarAliasType( int, m_nRightMarginWidth, "right_margin_width", "0", "proportional_xpos" );
+};
+
+//-----------------------------------------------------------------------------
+
+CReplayPerformanceEditorPanel *ReplayUI_InitPerformanceEditor( ReplayHandle_t hReplay );
+CReplayPerformanceEditorPanel *ReplayUI_GetPerformanceEditor();
+void ReplayUI_ClosePerformanceEditor();
+
+//-----------------------------------------------------------------------------
+
+#endif // REPLAYPERFORMANCEEDITOR_H
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replayperformancesavedlg.cpp b/sp/src/game/client/replay/vgui/replayperformancesavedlg.cpp
new file mode 100644
index 00000000..f7352a77
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayperformancesavedlg.cpp
@@ -0,0 +1,261 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replayperformancesavedlg.h"
+#include "replay/performance.h"
+#include "replay/ireplaymanager.h"
+#include "replay/ireplayperformancecontroller.h"
+#include "replay/replay.h"
+#include "econ/confirm_dialog.h"
+#include "vgui_controls/EditablePanel.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/TextImage.h"
+#include "vgui/ISurface.h"
+#include "replay/replaycamera.h"
+#include "replayperformanceeditor.h"
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Player input dialog for a replay
+//-----------------------------------------------------------------------------
+class CReplayPerformanceSaveDlg : public EditablePanel
+{
+private:
+ DECLARE_CLASS_SIMPLE( CReplayPerformanceSaveDlg, EditablePanel );
+
+public:
+ CReplayPerformanceSaveDlg( Panel *pParent, const char *pName,
+ OnConfirmSaveCallback pfnCallback, void *pContext, CReplay *pReplay, bool bExitEditorWhenDone );
+ ~CReplayPerformanceSaveDlg();
+
+ static void Show( OnConfirmSaveCallback pfnCallback, void *pContext, CReplay *pReplay,
+ bool bExitEditorWhenDone );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void PerformLayout();
+ virtual void OnCommand( const char *command );
+ virtual void OnKeyCodePressed( KeyCode code );
+ virtual void OnKeyCodeTyped( KeyCode code );
+
+ bool ConfirmOverwriteOrSaveNow();
+ void CloseWindow();
+
+ static void OnConfirmOverwrite( bool bConfirm, void *pContext );
+
+ MESSAGE_FUNC( OnSetFocus, "SetFocus" );
+
+ static vgui::DHANDLE< CReplayPerformanceSaveDlg > ms_hDlg;
+
+private:
+ OnConfirmSaveCallback m_pfnCallback;
+ void *m_pContext;
+ Panel *m_pDlg;
+ CReplay *m_pReplay;
+ TextEntry *m_pTitleEntry;
+ bool m_bExitEditorWhenDone;
+ wchar_t m_wszTitle[ MAX_TAKE_TITLE_LENGTH ];
+};
+
+vgui::DHANDLE< CReplayPerformanceSaveDlg > CReplayPerformanceSaveDlg::ms_hDlg;
+
+//-----------------------------------------------------------------------------
+// Purpose: CReplayPerformanceSaveDlg implementation
+//-----------------------------------------------------------------------------
+CReplayPerformanceSaveDlg::CReplayPerformanceSaveDlg( Panel *pParent, const char *pName,
+ OnConfirmSaveCallback pfnCallback, void *pContext,
+ CReplay *pReplay, bool bExitEditorWhenDone )
+: BaseClass( pParent, pName ),
+ m_pfnCallback( pfnCallback ),
+ m_pContext( pContext ),
+ m_pReplay( pReplay ),
+ m_bExitEditorWhenDone( bExitEditorWhenDone ),
+ m_pDlg( NULL ),
+ m_pTitleEntry( NULL )
+{
+ Assert( m_pContext );
+
+ SetScheme( "ClientScheme" );
+ SetProportional( true );
+}
+
+CReplayPerformanceSaveDlg::~CReplayPerformanceSaveDlg()
+{
+ ms_hDlg = NULL;
+}
+
+/*static*/ void CReplayPerformanceSaveDlg::Show( OnConfirmSaveCallback pfnCallback, void *pContext, CReplay *pReplay,
+ bool bExitEditorWhenDone )
+{
+ Assert( !ms_hDlg.Get() );
+
+ ms_hDlg = vgui::SETUP_PANEL( new CReplayPerformanceSaveDlg( NULL, "ReplayInputPanel", pfnCallback, pContext, pReplay, bExitEditorWhenDone ) );
+ ms_hDlg->SetVisible( true );
+ ms_hDlg->MakePopup();
+ ms_hDlg->MoveToFront();
+ ms_hDlg->SetKeyBoardInputEnabled(true);
+ ms_hDlg->SetMouseInputEnabled(true);
+ TFModalStack()->PushModal( ms_hDlg );
+ engine->ClientCmd_Unrestricted( "gameui_hide" );
+
+ ReplayCamera()->EnableInput( false );
+}
+
+void CReplayPerformanceSaveDlg::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/ui/replayperformanceeditor/savedlg.res", "GAME" );
+
+ // Cache off the dlg pointer
+ m_pDlg = FindChildByName( "Dlg" );
+
+ CExButton *pDiscardButton;
+ pDiscardButton = dynamic_cast< CExButton * >( m_pDlg->FindChildByName( "DiscardButton" ) );
+ SetXToRed( pDiscardButton );
+
+ // Setup some action sigs
+ m_pDlg->FindChildByName( "SaveButton" )->AddActionSignalTarget( this );
+ m_pDlg->FindChildByName( "CancelButton" )->AddActionSignalTarget( this );
+ pDiscardButton->AddActionSignalTarget( this );
+
+ m_pTitleEntry = static_cast< TextEntry * >( m_pDlg->FindChildByName( "TitleInput" ) );
+ m_pTitleEntry->SelectAllOnFocusAlways( true );
+ m_pTitleEntry->SetSelectionBgColor( GetSchemeColor( "Yellow", Color( 255, 255, 255, 255), pScheme ) );
+ m_pTitleEntry->SetSelectionTextColor( Color( 255, 255, 255, 255 ) );
+ m_pTitleEntry->SetText( L"" );
+}
+
+void CReplayPerformanceSaveDlg::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ SetWide( ScreenWidth() );
+ SetTall( ScreenHeight() );
+
+ // Center
+ m_pDlg->SetPos( ( ScreenWidth() - m_pDlg->GetWide() ) / 2, ( ScreenHeight() - m_pDlg->GetTall() ) / 2 );
+}
+
+void CReplayPerformanceSaveDlg::OnKeyCodeTyped( KeyCode code )
+{
+ if ( code == KEY_ESCAPE )
+ {
+ surface()->PlaySound( "replay\\record_fail.wav" );
+ return;
+ }
+
+ BaseClass::OnKeyCodeTyped( code );
+}
+
+void CReplayPerformanceSaveDlg::OnKeyCodePressed( KeyCode code )
+{
+ if ( code == KEY_ENTER )
+ {
+ OnCommand( "save" );
+ }
+
+ BaseClass::OnKeyCodePressed( code );
+}
+
+void CReplayPerformanceSaveDlg::OnSetFocus()
+{
+ m_pTitleEntry->RequestFocus();
+}
+
+/*static*/ void CReplayPerformanceSaveDlg::OnConfirmOverwrite( bool bConfirm, void *pContext )
+{
+ CReplayPerformanceSaveDlg *pThis = (CReplayPerformanceSaveDlg *)pContext;
+ pThis->m_pfnCallback( bConfirm, pThis->m_wszTitle, pThis->m_pContext );
+ pThis->CloseWindow();
+}
+
+bool CReplayPerformanceSaveDlg::ConfirmOverwriteOrSaveNow()
+{
+ // Using the same title as an existing performance?
+ CReplayPerformance *pExistingPerformance = m_pReplay->GetPerformanceWithTitle( m_wszTitle );
+ if ( pExistingPerformance )
+ {
+ ShowConfirmDialog( "#Replay_OverwriteDlgTitle", "#Replay_OverwriteDlgText",
+ "#Replay_ConfirmOverwrite", "#Replay_Cancel", OnConfirmOverwrite, NULL, this );
+ return false;
+ }
+
+ m_pfnCallback( true, m_wszTitle, m_pContext );
+
+ return true;
+}
+
+void CReplayPerformanceSaveDlg::OnCommand( const char *command )
+{
+ bool bCloseWindow = false;
+
+ extern IReplayPerformanceController *g_pReplayPerformanceController;
+
+ if ( !Q_strnicmp( command, "save", 4 ) )
+ {
+ // Get the text and save the replay/performance immediately
+ m_pTitleEntry->GetText( m_wszTitle, MAX_TAKE_TITLE_LENGTH );
+
+ // If we aren't overwriting an existing performance, this func will return true.
+ bCloseWindow = ConfirmOverwriteOrSaveNow();
+ }
+ else if ( !Q_strnicmp( command, "cancel", 6 ) )
+ {
+ bCloseWindow = true;
+ }
+
+ // Close the window?
+ if ( bCloseWindow )
+ {
+ CloseWindow();
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+void CReplayPerformanceSaveDlg::CloseWindow()
+{
+ SetVisible( false );
+ MarkForDeletion();
+ TFModalStack()->PopModal( ms_hDlg.Get() );
+ ReplayCamera()->EnableInput( true );
+
+ CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
+ if ( m_bExitEditorWhenDone && pEditor )
+ {
+ pEditor->Exit();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ReplayUI_ShowPerformanceSaveDlg( OnConfirmSaveCallback pfnCallback,
+ void *pContext, CReplay *pReplay,
+ bool bExitEditorWhenDone )
+{
+ CReplayPerformanceSaveDlg::Show( pfnCallback, pContext, pReplay, bExitEditorWhenDone );
+}
+
+bool ReplayUI_IsPerformanceSaveDlgOpen()
+{
+ return CReplayPerformanceSaveDlg::ms_hDlg.Get() != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Test the replay input dialog
+//-----------------------------------------------------------------------------
+CON_COMMAND_F( replay_test_take_save_dlg, "Open replay save take dlg", FCVAR_NONE )
+{
+ ReplayUI_ShowPerformanceSaveDlg( NULL, NULL, NULL, false );
+}
+
+#endif \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replayperformancesavedlg.h b/sp/src/game/client/replay/vgui/replayperformancesavedlg.h
new file mode 100644
index 00000000..fb4dbd55
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayperformancesavedlg.h
@@ -0,0 +1,27 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#ifndef REPLAYPERFORMANCESAVEDLG_H
+#define REPLAYPERFORMANCESAVEDLG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+
+class CReplay;
+
+//-----------------------------------------------------------------------------
+
+typedef void (*OnConfirmSaveCallback)( bool bConfirmed, wchar_t *pTitle, void *pContext );
+
+//-----------------------------------------------------------------------------
+
+void ReplayUI_ShowPerformanceSaveDlg( OnConfirmSaveCallback pfnCallback, void *pContext, CReplay *pReplay,
+ bool bExitEditorWhenDone );
+bool ReplayUI_IsPerformanceSaveDlgOpen();
+
+//-----------------------------------------------------------------------------
+
+#endif // REPLAYPERFORMANCESAVEDLG_H
diff --git a/sp/src/game/client/replay/vgui/replayreminderpanel.cpp b/sp/src/game/client/replay/vgui/replayreminderpanel.cpp
new file mode 100644
index 00000000..db06c701
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayreminderpanel.cpp
@@ -0,0 +1,163 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replayreminderpanel.h"
+#include "replay/ireplaysystem.h"
+#include "replay/replay.h"
+#include "replay/ireplayscreenshotmanager.h"
+#include "replay/ireplaymanager.h"
+#include "replay/screenshot.h"
+#include "iclientmode.h"
+#include "vgui_controls/AnimationController.h"
+
+//-----------------------------------------------------------------------------
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+
+DECLARE_HUDELEMENT( CReplayReminderPanel );
+
+//-----------------------------------------------------------------------------
+
+CReplayReminderPanel::CReplayReminderPanel( const char *pElementName )
+: EditablePanel( g_pClientMode->GetViewport(), "ReplayReminder" ),
+ CHudElement( pElementName )
+{
+ SetScheme( "ClientScheme" );
+
+ m_flShowTime = 0;
+ m_bShouldDraw = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayReminderPanel::SetupText()
+{
+ // Get current key binding, if any.
+ const char *pBoundKey = engine->Key_LookupBinding( "save_replay" );
+ if ( !pBoundKey || FStrEq( pBoundKey, "(null)" ) )
+ {
+ pBoundKey = " ";
+ }
+
+ char szKey[16];
+ Q_snprintf( szKey, sizeof(szKey), "%s", pBoundKey );
+
+ wchar_t wKey[16];
+ wchar_t wLabel[256];
+
+ g_pVGuiLocalize->ConvertANSIToUnicode( szKey, wKey, sizeof( wKey ) );
+ g_pVGuiLocalize->ConstructString( wLabel, sizeof( wLabel ), g_pVGuiLocalize->Find("#Replay_freezecam_replay" ), 1, wKey );
+
+ // Set the text
+ SetDialogVariable( "text", wLabel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayReminderPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ LoadControlSettings("Resource/UI/ReplayReminder.res", "GAME");
+
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetupText();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayReminderPanel::Show()
+{
+ m_flShowTime = gpGlobals->curtime;
+ SetVisible( true );
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( GetParent(), "HudReplayReminderIn" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayReminderPanel::Hide()
+{
+ SetVisible( false );
+ m_flShowTime = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CReplayReminderPanel::HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
+{
+ if ( ShouldDraw() && pszCurrentBinding )
+ {
+ if ( FStrEq (pszCurrentBinding, "save_replay" ) )
+ {
+ SetVisible( false );
+ }
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayReminderPanel::OnThink()
+{
+ BaseClass::OnThink();
+
+ if ( !IsVisible() )
+ return;
+
+ // If we're displaying the element for some specific duration...
+ if ( m_flShowTime )
+ {
+ // Get maximum duration
+ ConVarRef replay_postwinreminderduration( "replay_postwinreminderduration" );
+ float flShowLength = replay_postwinreminderduration.IsValid() ? replay_postwinreminderduration.GetFloat() : 5.0f;
+
+ if ( gpGlobals->curtime >= m_flShowTime + flShowLength )
+ {
+ m_flShowTime = 0;
+ SetVisible( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReplayReminderPanel::SetVisible( bool bState )
+{
+ if ( bState )
+ {
+ SetupText();
+ }
+
+ // Store this state for ShouldDraw()
+ m_bShouldDraw = bState;
+
+ BaseClass::SetVisible( bState );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CReplayReminderPanel::ShouldDraw()
+{
+ return m_bShouldDraw;
+}
+
+#endif // #if defined( REPLAY_ENABLED ) \ No newline at end of file
diff --git a/sp/src/game/client/replay/vgui/replayreminderpanel.h b/sp/src/game/client/replay/vgui/replayreminderpanel.h
new file mode 100644
index 00000000..2e1b8927
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayreminderpanel.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef REPLAYREMINDERPANEL_H
+#define REPLAYREMINDERPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <vgui_controls/EditablePanel.h>
+#include <game/client/iviewport.h>
+#include <vgui/IScheme.h>
+#include "hud.h"
+#include "hudelement.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Replay reminder panel
+//-----------------------------------------------------------------------------
+class CReplayReminderPanel : public EditablePanel, public CHudElement
+{
+ DECLARE_CLASS_SIMPLE( CReplayReminderPanel, vgui::EditablePanel );
+public:
+ CReplayReminderPanel( const char *pElementName );
+
+ void Hide(); // To be used by HUD only
+ void Show(); // To be used by HUD only
+
+ // CHudElement overrides
+ virtual bool ShouldDraw();
+ virtual void OnThink();
+ virtual int HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding );
+
+ // EditablePanel overrides
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void SetVisible( bool bState );
+
+private:
+ void SetupText();
+
+ float m_flShowTime; // Used by the HUD only, to display the panel only for a certain period of time
+ bool m_bShouldDraw; // Store this state for ShouldDraw(), which allows us to use a single panel for
+ // both the post-win reminder and the freezepanel reminder.
+};
+
+#endif // REPLAYREMINDERPANEL_H
diff --git a/sp/src/game/client/replay/vgui/replayrenderoverlay.cpp b/sp/src/game/client/replay/vgui/replayrenderoverlay.cpp
new file mode 100644
index 00000000..1300acc4
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayrenderoverlay.cpp
@@ -0,0 +1,380 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#include "cbase.h"
+
+#if defined( REPLAY_ENABLED )
+
+#include "replayrenderoverlay.h"
+#include "vgui_controls/TextImage.h"
+#include "replay/genericclassbased_replay.h"
+#include "iclientmode.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "ienginevgui.h"
+#include "vgui/IVGui.h"
+#include "econ/confirm_dialog.h"
+#include "replay/ireplaymanager.h"
+#include "replay/irecordingsessionmanager.h"
+#include "replay/ireplaymoviemanager.h"
+#include "replay/replayrenderer.h"
+#include "econ/econ_controls.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+//-----------------------------------------------------------------------------
+
+extern IReplayMovieManager *g_pReplayMovieManager;
+
+//-----------------------------------------------------------------------------
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+
+#define TMP_ENCODED_AUDIO ".tmp.aac"
+#ifdef USE_WEBM_FOR_REPLAY
+#define TMP_ENCODED_VIDEO ".tmp.webm"
+#else
+#define TMP_ENCODED_VIDEO ".tmp.mov"
+#endif
+
+//-----------------------------------------------------------------------------
+
+ConVar replay_enablerenderpreview( "replay_enablerenderpreview", "1", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Enable preview during replay render." );
+
+//-----------------------------------------------------------------------------
+
+void OnRenderCancelDialogButtonPressed( bool bConfirm, void *pContext )
+{
+ if ( bConfirm )
+ {
+ g_pReplayMovieManager->CancelRender();
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+CReplayRenderOverlay::CReplayRenderOverlay( Panel *pParent )
+: BaseClass( pParent, "ReplayRenderOverlay" ),
+ m_pBottom( NULL ),
+ m_pCancelButton( NULL ),
+ m_pTitleLabel( NULL ),
+ m_pProgressLabel( NULL ),
+ m_pFilenameLabel( NULL ),
+ m_pRenderProgress( NULL ),
+ m_pRenderer( NULL ),
+ m_pPreviewCheckButton( NULL ),
+ m_unNumFrames( 0 ),
+ m_flStartTime( 0.0f ),
+ m_flPreviousTimeLeft( 0.0f )
+{
+ if ( pParent == NULL )
+ {
+ vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
+ SetScheme(scheme);
+ SetProportional( true );
+ }
+
+ ivgui()->AddTickSignal( GetVPanel(), 10 );
+
+ m_pRenderer = new CReplayRenderer( this );
+}
+
+CReplayRenderOverlay::~CReplayRenderOverlay()
+{
+ ivgui()->RemoveTickSignal( GetVPanel() );
+
+ delete m_pRenderer;
+}
+
+void CReplayRenderOverlay::Show()
+{
+ // Setup panel
+ SetVisible( true );
+ SetMouseInputEnabled( true );
+ SetKeyBoardInputEnabled( true );
+ MakePopup( true );
+ MoveToFront();
+ TFModalStack()->PushModal( this );
+
+ // Make sure game UI is hidden
+ engine->ClientCmd_Unrestricted( "gameui_hide" );
+
+ InvalidateLayout( false, true );
+}
+
+void CReplayRenderOverlay::Hide()
+{
+ SetVisible( false );
+ TFModalStack()->PopModal( this );
+ MarkForDeletion();
+}
+
+void CReplayRenderOverlay::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ // Load controls
+ LoadControlSettings( "Resource/UI/replayrenderoverlay.res", "GAME" );
+
+ // Layout bottom
+ m_pBottom = dynamic_cast< EditablePanel * >( FindChildByName( "BottomPanel" ) );
+ if ( !m_pBottom )
+ return;
+
+ // Find some controls
+ m_pTitleLabel = dynamic_cast< CExLabel * >( FindChildByName( "TitleLabel" ) );
+ m_pProgressLabel = dynamic_cast< CExLabel * >( FindChildByName( "ProgressLabel" ) );
+ m_pRenderProgress = dynamic_cast< ProgressBar * >( FindChildByName( "RenderProgress" ) );
+ m_pCancelButton = dynamic_cast< CExButton * >( FindChildByName( "CancelButton" ) );
+ m_pFilenameLabel = dynamic_cast< CExLabel * >( FindChildByName( "FilenameLabel" ) );
+ m_pPreviewCheckButton = dynamic_cast< CheckButton * >( FindChildByName( "PreviewCheckButton" ) );
+
+ m_pPreviewCheckButton->SetProportional( false );
+ m_pPreviewCheckButton->SetSelected( replay_enablerenderpreview.GetBool() );
+ m_pPreviewCheckButton->AddActionSignalTarget( this );
+
+ const char *pMovieFilename = m_pRenderer->GetMovieFilename();
+ if ( m_pFilenameLabel && pMovieFilename )
+ {
+ const char *pFilename = V_UnqualifiedFileName( pMovieFilename );
+ m_pFilenameLabel->SetText( pFilename );
+ }
+}
+
+void CReplayRenderOverlay::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ if ( !m_pBottom )
+ return;
+
+ int sw, sh;
+ vgui::surface()->GetScreenSize( sw, sh );
+ SetBounds( 0, 0, sw, sh );
+
+ int nBottomPanelHeight = sh * .13f;
+ int nBottomPanelStartY = sh - nBottomPanelHeight;
+ m_pBottom->SetBounds( 0, nBottomPanelStartY, sw, nBottomPanelHeight );
+
+ int nBottomW = sw;
+ int nBottomH = nBottomPanelHeight;
+
+ // Setup progress bar
+ if ( !m_pRenderProgress )
+ return;
+
+ int nProgHeight = YRES(20);
+ int nMargin = nBottomW/5;
+ int nProgX = nMargin;
+ int nProgY = nBottomPanelStartY + ( nBottomH - nProgHeight ) / 2;
+ int nProgW = nBottomW - 2*nMargin;
+
+ // Only show progress bar if replay is valid and length of render is non-zero, and the record start tick exists
+ CReplay *pReplay = g_pReplayManager->GetPlayingReplay();
+ if ( pReplay )
+ {
+ const float flTotalTime = pReplay->m_flLength;
+ const int nServerRecordStartTick = g_pClientReplayContext->GetRecordingSessionManager()->GetServerStartTickForSession( pReplay->m_hSession ); // NOTE: Returns -1 on fail
+ if ( flTotalTime > 0.0f && nServerRecordStartTick >= 0 )
+ {
+ m_pRenderProgress->SetVisible( true );
+ m_pRenderProgress->SetBounds( nProgX, nProgY, nProgW, nProgHeight );
+ m_pRenderProgress->SetSegmentInfo( XRES(1), XRES(8) );
+ }
+ }
+
+ // Layout title label
+ const int nTitleLabelY = nBottomPanelStartY + ( m_pBottom->GetTall() - m_pTitleLabel->GetTall() ) / 2;
+ if ( m_pTitleLabel )
+ {
+ m_pTitleLabel->SizeToContents();
+ m_pTitleLabel->SetPos( ( nProgX - m_pTitleLabel->GetWide() ) / 2, nTitleLabelY );
+ }
+
+ // Layout preview check button
+ if ( m_pPreviewCheckButton )
+ {
+ m_pPreviewCheckButton->SizeToContents();
+ m_pPreviewCheckButton->SetPos( ( nProgX - m_pPreviewCheckButton->GetWide() ) / 2, nTitleLabelY + m_pTitleLabel->GetTall() + YRES(3) );
+ }
+
+ // Layout filename label
+ if ( m_pFilenameLabel )
+ {
+ int nProgBottomY = nProgY + nProgHeight;
+ m_pFilenameLabel->SizeToContents();
+ m_pFilenameLabel->SetPos( nProgX, nProgBottomY + ( sh - nProgBottomY - m_pFilenameLabel->GetTall() ) / 2 );
+ }
+
+ // Layout progress label
+ if ( m_pProgressLabel )
+ {
+ int nProgBottomY = nProgY + nProgHeight;
+ m_pProgressLabel->SizeToContents();
+ m_pProgressLabel->SetPos( nProgX, nProgBottomY + ( sh - nProgBottomY - m_pProgressLabel->GetTall() ) / 2 );
+ m_pProgressLabel->SetWide( nProgW );
+ }
+
+ // Layout cancel button
+ if ( !m_pCancelButton )
+ return;
+
+ // Put cancel button half way in between progress bar and screen right
+ int nProgRightX = nProgX + nProgW;
+ m_pCancelButton->SetPos(
+ nProgRightX + ( m_pBottom->GetWide() - nProgRightX - m_pCancelButton->GetWide() ) / 2,
+ nBottomPanelStartY + ( m_pBottom->GetTall() - m_pCancelButton->GetTall() ) / 2
+ );
+
+ SetXToRed( m_pCancelButton );
+
+ m_pCancelButton->RequestFocus();
+}
+
+void CReplayRenderOverlay::OnTick()
+{
+#if _DEBUG
+ if ( m_bReloadScheme )
+ {
+ InvalidateLayout( true, true );
+ m_bReloadScheme = false;
+ }
+#endif
+
+ // Update progress
+ if ( m_pRenderProgress )
+ {
+ CReplay *pReplay = g_pReplayManager->GetPlayingReplay();
+ if ( pReplay && m_pRenderProgress->IsVisible() )
+ {
+ float flCurTime, flTotalTime;
+ g_pClientReplayContext->GetPlaybackTimes( flCurTime, flTotalTime, pReplay, m_pRenderer->GetPerformance() );
+ const float flProgress = ( flTotalTime == 0.0f ) ? 1.0f : ( flCurTime / flTotalTime );
+
+ Assert( flTotalTime > 0.0f ); // NOTE: Progress bar will always be invisible if total time is 0, but check anyway to be safe.
+ m_pRenderProgress->SetProgress( MAX( m_pRenderProgress->GetProgress(), flProgress ) ); // The MAX() here keeps the progress bar from thrashing
+
+ if ( m_pProgressLabel )
+ {
+ // @note Tom Bui: this is a horribly ugly hack, but the first couple of frames take a really freaking long time, so that
+ // really blows out the estimate
+ float flTimePassed = 0.0f;
+ ++m_unNumFrames;
+ const uint32 kNumFramesToWait = 10;
+ if ( m_unNumFrames < kNumFramesToWait )
+ {
+ m_flStartTime = gpGlobals->realtime;
+ }
+ else if ( m_unNumFrames > kNumFramesToWait )
+ {
+ flTimePassed = gpGlobals->realtime - m_flStartTime;
+ float flEstimatedTimeLeft = flProgress > 0.0f ? ( flTimePassed / flProgress ) - flTimePassed : 0.0f;
+ // exponential moving average FIR filter
+ // S(t) = smoothing_factor * Y(t) + (1 - smoothing_factor)* Y(t-1)
+ // previous value is essentially 90% of the current value
+ const float kSmoothingFactor = 0.1f;
+ if ( m_flPreviousTimeLeft == 0.0f )
+ {
+ m_flPreviousTimeLeft = flEstimatedTimeLeft;
+ }
+ else
+ {
+ m_flPreviousTimeLeft = kSmoothingFactor * flEstimatedTimeLeft + ( 1 - kSmoothingFactor ) * m_flPreviousTimeLeft;
+ }
+ }
+
+ wchar_t wszTimeLeft[256];
+ wchar_t wszTime[256];
+ {
+ const char *pRenderTime = CReplayTime::FormatTimeString( RoundFloatToInt( m_flPreviousTimeLeft ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( pRenderTime, wszTimeLeft, sizeof( wszTimeLeft ) );
+ }
+ {
+ const char *pRenderTime = CReplayTime::FormatTimeString( RoundFloatToInt( flTimePassed ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( pRenderTime, wszTime, sizeof( wszTime ) );
+ }
+ wchar_t wszText[256];
+ g_pVGuiLocalize->ConstructString( wszText,sizeof( wszText ), g_pVGuiLocalize->Find( "#Replay_RenderOverlay_TimeLeft" ), 2, wszTime, wszTimeLeft );
+ m_pProgressLabel->SetText( wszText );
+ }
+ }
+ }
+}
+
+void CReplayRenderOverlay::OnMousePressed( MouseCode nCode )
+{
+#if _DEBUG
+ m_bReloadScheme = true;
+#endif
+ BaseClass::OnMousePressed( nCode );
+}
+
+void CReplayRenderOverlay::OnKeyCodeTyped( vgui::KeyCode nCode )
+{
+ if ( nCode == KEY_ESCAPE )
+ {
+ if ( TFModalStack()->Top() == GetVPanel() )
+ {
+ OnCommand( "confirmcancel" );
+ return;
+ }
+ }
+
+ BaseClass::OnKeyCodeTyped( nCode );
+}
+
+void CReplayRenderOverlay::OnCommand( const char *pCommand )
+{
+ if ( !V_stricmp( pCommand, "confirmcancel" ) )
+ {
+ ShowConfirmDialog( "#Replay_CancelRenderTitle", "#Replay_ConfirmCancelRender", "#Replay_YesCancel", "#Replay_No", OnRenderCancelDialogButtonPressed, this, NULL, "replay\\replaydialog_warn.wav" );
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+void CReplayRenderOverlay::OnCheckButtonChecked( Panel *pPanel )
+{
+ replay_enablerenderpreview.SetValue( (int)m_pPreviewCheckButton->IsSelected() );
+}
+
+//-----------------------------------------------------------------------------
+
+static CReplayRenderOverlay *s_pRenderOverlay = NULL;
+
+void ReplayUI_OpenReplayRenderOverlay()
+{
+ if ( !g_pReplayMovieManager->IsRendering() )
+ return;
+
+ // Delete any existing panel
+ if ( s_pRenderOverlay )
+ {
+ s_pRenderOverlay->MarkForDeletion();
+ }
+
+ // Create the panel - get the render resolution from the settings
+ s_pRenderOverlay = SETUP_PANEL( new CReplayRenderOverlay( NULL ) ); // Parenting to NULL allows us to turn off world rendering in engine/view.cpp (V_RenderView())
+
+ // Set the panel as the movie renderer, so it can receive begin/end render calls from the engine
+ g_pClientReplayContext->SetMovieRenderer( s_pRenderOverlay->m_pRenderer );
+}
+
+void ReplayUI_HideRenderOverlay()
+{
+ if ( s_pRenderOverlay )
+ {
+ s_pRenderOverlay->MarkForDeletion();
+ s_pRenderOverlay = NULL;
+ }
+
+ g_pClientReplayContext->SetMovieRenderer( NULL );
+}
+
+//-----------------------------------------------------------------------------
+
+#endif
diff --git a/sp/src/game/client/replay/vgui/replayrenderoverlay.h b/sp/src/game/client/replay/vgui/replayrenderoverlay.h
new file mode 100644
index 00000000..e65170a2
--- /dev/null
+++ b/sp/src/game/client/replay/vgui/replayrenderoverlay.h
@@ -0,0 +1,73 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=======================================================================================//
+
+#ifndef REPLAY_RENDEROVERLAY_H
+#define REPLAY_RENDEROVERLAY_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+
+#include "vgui_controls/Frame.h"
+#include "vgui_controls/ProgressBar.h"
+#include "replay/rendermovieparams.h"
+
+//-----------------------------------------------------------------------------
+
+class CExButton;
+class CExLabel;
+class IQuickTimeMovieMaker;
+class CReplay;
+class CReplayRenderer;
+
+//-----------------------------------------------------------------------------
+
+class CReplayRenderOverlay : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CReplayRenderOverlay, vgui::Frame );
+public:
+ CReplayRenderOverlay( Panel *pParent );
+ ~CReplayRenderOverlay();
+
+ void Show();
+ void Hide();
+
+ CReplayRenderer *m_pRenderer;
+
+private:
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void PerformLayout();
+ virtual void OnTick();
+ virtual void OnMousePressed( vgui::MouseCode nCode );
+ virtual void OnKeyCodeTyped( vgui::KeyCode nCode );
+ virtual void OnCommand( const char *pCommand );
+
+private:
+ MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", pPanel );
+
+#if _DEBUG
+ bool m_bReloadScheme;
+#endif
+
+ int m_unNumFrames;
+ float m_flStartTime;
+ float m_flPreviousTimeLeft;
+ EditablePanel *m_pBottom;
+ vgui::ProgressBar *m_pRenderProgress;
+ vgui::CheckButton *m_pPreviewCheckButton;
+ CExButton *m_pCancelButton;
+ CExLabel *m_pTitleLabel;
+ CExLabel *m_pFilenameLabel;
+ CExLabel *m_pProgressLabel;
+};
+
+//-----------------------------------------------------------------------------
+
+void ReplayUI_OpenReplayRenderOverlay();
+void ReplayUI_HideRenderOverlay();
+
+//-----------------------------------------------------------------------------
+
+#endif // REPLAY_RENDEROVERLAY_H