diff options
Diffstat (limited to 'game/client/tf/workshop/published_files.cpp')
| -rw-r--r-- | game/client/tf/workshop/published_files.cpp | 1646 |
1 files changed, 1646 insertions, 0 deletions
diff --git a/game/client/tf/workshop/published_files.cpp b/game/client/tf/workshop/published_files.cpp new file mode 100644 index 0000000..c8e0bfe --- /dev/null +++ b/game/client/tf/workshop/published_files.cpp @@ -0,0 +1,1646 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= +#include "cbase.h" + +#include "imageutils.h" +#include "econ_controls.h" +#include "econ/confirm_dialog.h" +#include "game/client/iviewport.h" +#include "ienginevgui.h" +#include "tf_hud_mainmenuoverride.h" +#include "vgui/ILocalize.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui_bitmappanel.h" +#include <vgui_controls/FileOpenDialog.h> + +#ifdef WORKSHOP_IMPORT_ENABLED +#include "itemtest/itemtest.h" +#include "workshop/item_import.h" +#endif + +#include "steampublishedfiles/publish_file_dialog.h" + +#include "tier1/checksum_crc.h" + +// for hud element +#include "iclientmode.h" +#include "tf_gamerules.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#define TF2_PREVIEW_IMAGE_HEIGHT 512 +#define TF2_PREVIEW_IMAGE_WIDTH 512 + +#define COMMUNITY_DEV_HOST "http://localhost/" + +extern ConVar publish_file_last_dir; + +// milliseconds +ConVar tf_steam_workshop_query_timeout( "tf_steam_workshop_query_timeout", "10", FCVAR_CLIENTDLL, "Time in seconds to allow communication with the Steam Workshop server." ); + +//----------------------------------------------------------------------------- +// Purpose: Utility function +//----------------------------------------------------------------------------- +static void MakeModalAndBringToFront( vgui::EditablePanel *dialog ) +{ + dialog->SetVisible( true ); + if ( dialog->GetParent() == NULL ) + { + dialog->MakePopup(); + } + dialog->SetZPos( 10000 ); + dialog->MoveToFront(); + dialog->SetKeyBoardInputEnabled( true ); + dialog->SetMouseInputEnabled( true ); + TFModalStack()->PushModal( dialog ); +} + +//----------------------------------------------------------------------------- +// Purpose: helper class that contains the list of published files for a user +//----------------------------------------------------------------------------- +class CPublishedFiles +{ +public: + enum + { + kState_Initialized, + kState_PopulatingFileList, + kState_ErrorOccurred, + kState_DeletingFile, + kState_ErrorCannotDeleteFile, + kState_Timeout, + kState_Done, + }; + + CPublishedFiles(); + ~CPublishedFiles(); + + bool QueryHasTimedOut( void ); + void PopulateFileList( void ); + bool EnumerateUserPublishedFiles( uint32 unPage ); + void RefreshPublishedFileDetails( uint64 nPublishedFileID ); + void DeletePublishedFile( uint64 nPublishedFileID ); + void ViewPublishedFile( uint64 nPublishedFileID ); + const SteamUGCDetails_t *GetPublishedFileDetails( uint64 nPublishedFileID ) const; + + // Enumerate subscribed files + CCallResult<CPublishedFiles, SteamUGCQueryCompleted_t> m_callbackEnumeratePublishedFiles; + void Steam_OnEnumeratePublishedFiles( SteamUGCQueryCompleted_t *pResult, bool bError ); + + // Callback for deleting files + CCallResult<CPublishedFiles, RemoteStorageDeletePublishedFileResult_t> m_callbackDeletePublishedFile; + void Steam_OnDeletePublishedFile( RemoteStorageDeletePublishedFileResult_t *pResult, bool bError ); + + unsigned int m_nTotalFilesToQuery; + CUtlQueue< PublishedFileId_t > m_FilesToQuery; + CUtlMap< PublishedFileId_t, SteamUGCDetails_t > m_FileDetails; + + long m_nFileQueryTime; + bool m_bQueryErrorOccurred; + uint32 m_state; + uint32 m_unEnumerateStartPage; + PublishedFileId_t m_nCurrentQueryPublishedFileID; +}; + +CPublishedFiles::CPublishedFiles() + : m_nTotalFilesToQuery( 0 ) + , m_nFileQueryTime( 0 ) + , m_bQueryErrorOccurred( false ) + , m_state( kState_Initialized ) + , m_unEnumerateStartPage( 0 ) + , m_nCurrentQueryPublishedFileID( 0 ) +{ + m_FileDetails.SetLessFunc( DefLessFunc( PublishedFileId_t ) ); +} + +CPublishedFiles::~CPublishedFiles() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Determine if our file query has timed out +//----------------------------------------------------------------------------- +bool CPublishedFiles::QueryHasTimedOut( void ) +{ + return ( ( system()->GetTimeMillis() - m_nFileQueryTime ) > tf_steam_workshop_query_timeout.GetInt() * 1000 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPublishedFiles::PopulateFileList( void ) +{ + // Start the process of showing all of our published files + if ( EnumerateUserPublishedFiles( 1 ) ) + { + m_unEnumerateStartPage = 1; + + // Get our starting tick + m_nFileQueryTime = system()->GetTimeMillis(); + + m_state = kState_PopulatingFileList; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Wrapper for creating and sending a CreateQueryUserUGCRequest for this user's published files +//----------------------------------------------------------------------------- +bool CPublishedFiles::EnumerateUserPublishedFiles( uint32 unPage ) +{ + ISteamUGC *pUGC = steamapicontext->SteamUGC(); + ISteamUser *pUser = steamapicontext->SteamUser(); + if ( pUGC && pUser ) + { + AccountID_t nAccountID = pUser->GetSteamID().GetAccountID(); + + UGCQueryHandle_t ugcHandle = pUGC->CreateQueryUserUGCRequest( nAccountID, + k_EUserUGCList_Published, + k_EUGCMatchingUGCType_Items, + k_EUserUGCListSortOrder_CreationOrderDesc, + engine->GetAppID(), engine->GetAppID(), unPage ); + + // make sure we get the entire description and not a truncated version + pUGC->SetReturnLongDescription( ugcHandle, true ); + + SteamAPICall_t hSteamAPICall = pUGC->SendQueryUGCRequest( ugcHandle ); + m_callbackEnumeratePublishedFiles.Set( hSteamAPICall, this, &CPublishedFiles::Steam_OnEnumeratePublishedFiles ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPublishedFiles::DeletePublishedFile( uint64 nPublishedFileID ) +{ + m_state = kState_DeletingFile; + SteamAPICall_t hSteamAPICall = steamapicontext->SteamRemoteStorage()->DeletePublishedFile( nPublishedFileID ); + m_callbackDeletePublishedFile.Set( hSteamAPICall, this, &CPublishedFiles::Steam_OnDeletePublishedFile ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const SteamUGCDetails_t *CPublishedFiles::GetPublishedFileDetails( uint64 nPublishedFileID ) const +{ + int idx = m_FileDetails.Find( nPublishedFileID ); + if ( idx != m_FileDetails.InvalidIndex() ) + { + return &m_FileDetails[idx]; + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPublishedFiles::ViewPublishedFile( uint64 nPublishedFileID ) +{ + EUniverse universe = GetUniverse(); + switch ( universe ) + { + case k_EUniversePublic: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/sharedfiles/filedetails/?id=%llu", nPublishedFileID ) ); + break; + case k_EUniverseBeta: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/sharedfiles/filedetails/?id=%llu", nPublishedFileID ) ); + break; + case k_EUniverseDev: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "sharedfiles/filedetails/?id=%llu", nPublishedFileID ) ); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPublishedFiles::RefreshPublishedFileDetails( uint64 nPublishedFileID ) +{ + if ( m_state != kState_Done ) + { + // Would confuse the refresh in progress + AssertMsg( m_state == kState_Done, "Shouldn't be refreshing file details while other operations are already happening" ); + return; + } + + ISteamUGC *pUGC = steamapicontext->SteamUGC(); + Assert( pUGC ); + if ( pUGC ) + { + UGCQueryHandle_t ugcHandle = pUGC->CreateQueryUGCDetailsRequest( &nPublishedFileID, 1 ); + SteamAPICall_t hSteamAPICall = pUGC->SendQueryUGCRequest( ugcHandle ); + m_callbackEnumeratePublishedFiles.Set( hSteamAPICall, this, &CPublishedFiles::Steam_OnEnumeratePublishedFiles ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPublishedFiles::Steam_OnDeletePublishedFile( RemoteStorageDeletePublishedFileResult_t *pResult, bool bError ) +{ + if ( pResult && !bError ) + { + if ( pResult->m_eResult != k_EResultOK ) + { + m_state = kState_ErrorOccurred; + if ( pResult->m_eResult == k_EResultAccessDenied ) + { + m_state = kState_ErrorCannotDeleteFile; + } + } + else + { + m_state = kState_Done; + m_FileDetails.Remove( pResult->m_nPublishedFileId ); + } + } + else + { + m_state = kState_ErrorOccurred; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPublishedFiles::Steam_OnEnumeratePublishedFiles( SteamUGCQueryCompleted_t *pResult, bool bError ) +{ + // Make sure we succeeded + if ( bError || pResult->m_eResult != k_EResultOK ) + { + m_state = kState_ErrorOccurred; + return; + } + + if ( m_state == kState_PopulatingFileList && m_unEnumerateStartPage == 1 ) + { + // Start from scratch + m_FilesToQuery.Purge(); + m_FileDetails.Purge(); + } + + const int nNumFiles = pResult->m_unNumResultsReturned; + for ( int i = 0; i < nNumFiles; i++ ) + { + SteamUGCDetails_t sDetails = { 0 }; + steamapicontext->SteamUGC()->GetQueryUGCResult( pResult->m_handle, i, &sDetails ); + m_FileDetails.InsertOrReplace( sDetails.m_nPublishedFileId, sDetails ); + } + + if ( m_state == kState_Done ) + { + // This was a one-off request from e.g. RefreshPublishedFileDetails + return; + } + + if ( !nNumFiles || m_FileDetails.Count() >= pResult->m_unTotalMatchingResults ) + { + m_state = kState_Done; + return; + } + + EnumerateUserPublishedFiles( ++m_unEnumerateStartPage ); +} + +// Tags +static const char *kClassTags[TF_LAST_NORMAL_CLASS] = { + "", // TF_CLASS_UNDEFINED + "Scout", // TF_CLASS_SCOUT + "Sniper", // TF_CLASS_SNIPER, + "Soldier", // TF_CLASS_SOLDIER, + "Demoman", // TF_CLASS_DEMOMAN, + "Medic", // TF_CLASS_MEDIC, + "Heavy", //TF_CLASS_HEAVYWEAPONS, + "Pyro", // TF_CLASS_PYRO, + "Spy", // TF_CLASS_SPY, + "Engineer", // TF_CLASS_ENGINEER, +}; + +struct TagPair_t +{ + const char *pCheckboxElementName; + const char *pTag; +}; +static TagPair_t kOtherTags[] = { + { "TagCheckbox_Headgear", "Headgear" }, + { "TagCheckbox_Weapon", "Weapon" }, + { "TagCheckbox_Misc", "Misc" }, + { "TagCheckbox_SoundDevice", "Sound Device" }, + { "TagCheckbox_Halloween", "Halloween" }, + { "TagCheckbox_Taunt", "Taunt" }, + { "TagCheckbox_UnusualEffect", "Unusual Effect" }, + { "TagCheckbox_Jungle", "Jungle" }, +}; +static uint32 kNumOtherTags = ARRAYSIZE( kOtherTags ); + +// Map Tags +static TagPair_t kMapTags[] = { + { "MapsCheckBox_CTF", "Capture the Flag" }, + { "MapsCheckBox_CP", "Control Point" }, + { "MapsCheckBox_Escort", "Payload" }, + { "MapsCheckBox_EscortRace", "Payload Race" }, + { "MapsCheckBox_Arena", "Arena" }, + { "MapsCheckBox_Koth", "King of the Hill" }, + { "MapsCheckBox_AttackDefense", "Attack / Defense" }, + { "MapsCheckBox_SD", "Special Delivery" }, + { "MapsCheckBox_RobotDestruction", "Robot Destruction" }, + { "MapsCheckBox_MVM", "Mann vs. Machine" }, + { "MapsCheckBox_Powerup", "Mannpower" }, + { "MapsCheckBox_Medieval", "Medieval" }, + { "MapsCheckBox_PassTime", "PASS Time" }, + { "MapsCheckBox_Specialty", "Specialty" }, + { "MapsCheckBox_Halloween", "Halloween" }, + { "MapsCheckbox_Smissmas", "Smissmas" }, + { "MapsCheckbox_Night", "Night" }, + { "MapsCheckbox_Jungle", "Jungle" }, +}; +static uint32 kNumMapTags = ARRAYSIZE( kMapTags ); + +static const char *kImportedTag = "Certified Compatible"; + +//----------------------------------------------------------------------------- +// Purpose: Publish file dialog +//----------------------------------------------------------------------------- +class CTFFilePublishDialog : public CFilePublishDialog +{ + DECLARE_CLASS_SIMPLE( CTFFilePublishDialog, CFilePublishDialog ); +public: + CTFFilePublishDialog( Panel *parent, const char *name, PublishedFileDetails_t *pDetails ) : CFilePublishDialog( parent, name, pDetails ), m_bImported(false) {} + + virtual ErrorCode_t ValidateFile( const char *lpszFilename ) + { + if( !g_pFullFileSystem->FileExists( lpszFilename ) ) + return kFailedFileNotFound; + + // TODO This is the nominal max of SteamUGC, but should be found dynamically + const uint32 kMaxFileSize = 400 * 1024 * 1024; + unsigned int unFileSize = g_pFullFileSystem->Size( lpszFilename ); + if ( unFileSize == 0 || unFileSize > kMaxFileSize ) + { + return kFailedFileTooLarge; + } + return kNoError; + } + virtual AppId_t GetTargetAppID( void ) { return engine->GetAppID(); } + virtual unsigned int DesiredPreviewHeight( void ) { return TF2_PREVIEW_IMAGE_HEIGHT; } + virtual unsigned int DesiredPreviewWidth( void ) { return TF2_PREVIEW_IMAGE_WIDTH; } + virtual bool BForceSquarePreviewImage( void ) { return true; } + virtual EWorkshopFileType WorkshipFileTypeForFile( const char *pszFileName ) { + const char *pExt = V_GetFileExtension( pszFileName ); + if ( pExt && V_strcmp( pExt, "bsp" ) == 0 ) + { + return k_EWorkshopFileTypeCommunity; + } + + AssertMsg( pExt && V_strcmp( pExt, "zip" ) == 0, "Unrecognized file type, defaulting to microtransaction\n" ); + return k_EWorkshopFileTypeMicrotransaction; + } + virtual const char *GetPreviewFileTypes( void ) { return "*.tga,*.jpg,*.png"; } + virtual const char *GetPreviewFileTypeDescriptions( void ) { return "#TF_SteamWorkshop_Images"; } + virtual const char *GetFileTypes( eFilterType_t eType = IMPORT_FILTER_NONE ) OVERRIDE + { + if ( eType == IMPORT_FILTER_MAP ) + { + return "*.bsp"; + } + return "*.zip"; + } + virtual const char *GetFileTypeDescriptions( eFilterType_t eType = IMPORT_FILTER_NONE ) OVERRIDE + { + if ( eType == IMPORT_FILTER_MAP ) + { + return "#TF_SteamWorkshop_AcceptableFilesMaps"; + } + return "#TF_SteamWorkshop_AcceptableFiles"; + } + virtual const char *GetResFile() const { return "Resource/UI/PublishFileDialog.res"; } + + virtual void SetFile( const char *lpszFilename, bool bImported ) + { + BaseClass::SetFile( lpszFilename, bImported ); + SetTagsVisible( true, WorkshipFileTypeForFile( lpszFilename ) ); + + m_sFilePath = lpszFilename; + + CUtlBuffer buffer; + g_pFullFileSystem->ReadFile( lpszFilename, NULL, buffer ); + m_fileCRC = CRC32_ProcessSingleBuffer( buffer.Base(), buffer.Size() ); + + m_bImported = bImported; + } + + void SetTagsVisible( bool visible, EWorkshopFileType eFileType = k_EWorkshopFileTypeCommunity ) + { + uint32 i; + vgui::CheckButton *pButton; + + vgui::Label *pLabel = FindControl<vgui::Label>( "TagsTitle", true ); + if ( pLabel ) + { + pLabel->SetVisible( visible ); + } + + if ( !visible ) + { + // no tags visible, so hide everything + // class tags + for ( i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ ) + { + pButton = FindControl<vgui::CheckButton>( VarArgs( "ClassCheckBox%d", i ), true ); + if ( pButton ) + { + pButton->SetVisible( visible ); + } + } + + // other tags + for ( i = 0; i < kNumOtherTags; ++i ) + { + pButton = FindControl<vgui::CheckButton>( kOtherTags[i].pCheckboxElementName, true ); + if ( pButton ) + { + pButton->SetVisible( visible ); + } + } + + // map tags + for ( i = 0; i < kNumMapTags; ++i ) + { + pButton = FindControl<vgui::CheckButton>( kMapTags[i].pCheckboxElementName, true ); + if ( pButton ) + { + pButton->SetVisible( visible ); + } + } + } + else + { + bool bIsMap = ( eFileType == k_EWorkshopFileTypeCommunity ); + + // class tags + for ( i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ ) + { + pButton = FindControl<vgui::CheckButton>( VarArgs( "ClassCheckBox%d", i ), true ); + if ( pButton ) + { + pButton->SetVisible( !bIsMap ); + + if ( bIsMap && pButton->IsSelected() ) + { + pButton->SetSelected( false ); // reset this button if we're using the maps tags + } + } + } + + // other tags + for ( i = 0; i < kNumOtherTags; ++i ) + { + pButton = FindControl<vgui::CheckButton>( kOtherTags[i].pCheckboxElementName, true ); + if ( pButton ) + { + pButton->SetVisible( !bIsMap ); + + if ( bIsMap && pButton->IsSelected() ) + { + pButton->SetSelected( false ); // reset this button if we're using the maps tags + } + } + } + + // map tags + for ( i = 0; i < kNumMapTags; ++i ) + { + pButton = FindControl<vgui::CheckButton>( kMapTags[i].pCheckboxElementName, true ); + if ( pButton ) + { + pButton->SetVisible( bIsMap ); + + if ( !bIsMap && pButton->IsSelected() ) + { + pButton->SetSelected( false ); // reset this button if we're not using the maps tags + } + } + } + } + } + + virtual void PopulateTags( SteamParamStringArray_t &strArray ) + { + m_vecTags.RemoveAll(); + + // class tags + vgui::EditablePanel* pClassUsagePanel = dynamic_cast<vgui::EditablePanel*>( FindChildByName( "ClassUsagePanel" ) ); + if ( pClassUsagePanel ) + { + for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ ) + { + if ( IsChildButtonSelected( pClassUsagePanel, VarArgs("ClassCheckBox%d",i), false ) ) + { + m_vecTags.AddToTail( kClassTags[i] ); + } + } + } + + // other tags + for ( uint32 i = 0; i < kNumOtherTags; ++i ) + { + if ( IsChildButtonSelected( this, kOtherTags[i].pCheckboxElementName, true ) ) + { + m_vecTags.AddToTail( kOtherTags[i].pTag ); + } + } + + // map tags + for ( uint32 i = 0; i < kNumMapTags; ++i ) + { + if ( IsChildButtonSelected( this, kMapTags[i].pCheckboxElementName, true ) ) + { + m_vecTags.AddToTail( kMapTags[i].pTag ); + } + } + + if ( m_bImported ) + { + m_vecTags.AddToTail( kImportedTag ); + } + + strArray.m_ppStrings = m_vecTags.Base(); + strArray.m_nNumStrings = m_vecTags.Count(); + } + + virtual void PerformLayout() + { + BaseClass::PerformLayout(); + + // Center it, keeping requested size + int x, y, ww, wt, wide, tall; + vgui::surface()->GetWorkspaceBounds( x, y, ww, wt ); + GetSize(wide, tall); + SetPos(x + ((ww - wide) / 2), y + ((wt - tall) / 2)); + } + + virtual bool PrepSteamCloudFilePath( const char *lpszFileName, CUtlString &steamCloudFileName ) + { + char szShortName[ MAX_PATH ]; + Q_FileBase( lpszFileName, szShortName, sizeof( szShortName ) ); + const char *szExt = Q_GetFileExtension( lpszFileName ); + Q_SetExtension( szShortName, CFmtStr( ".%s", szExt ).Access(), sizeof(szShortName ) ); + steamCloudFileName.Format( "steamworkshop/tf2/%s", szShortName ); + return true; + } + + virtual void ErrorMessage( ErrorCode_t errorCode, KeyValues *pkvTokens ) + { + switch ( errorCode ) + { + case kFailedToPublishFile: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedToPublishFile", "#GameUI_OK" ); + break; + case kFailedToPrepareFile: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedToPrepareFile", "#GameUI_OK" ); + break; + case kFailedToUpdateFile: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedToUpdateFile", "#GameUI_OK" ); + break; + case kSteamCloudNotAvailable: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kSteamCloudNotAvailable", "#GameUI_OK" ); + break; + case kSteamExceededCloudQuota: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kSteamExceededCloudQuota", "#GameUI_OK" ); + break; + case kFailedToWriteToSteamCloud: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedToWriteToSteamCloud", "#GameUI_OK" ); + break; + case kFileNotFound: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFileNotFound", "#GameUI_OK" ); + break; + case kNeedTitleAndDescription: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kNeedTitleAndDescription", "#GameUI_OK" ); + break; + case kFailedFileValidation: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedFileValidation", "#GameUI_OK" ); + break; + case kFailedFileTooLarge: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedFileTooLarge", "#GameUI_OK" ); + break; + case kFailedFileNotFound: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedFileNotFound", "#GameUI_OK" ); + break; + case kFailedUserModifiedFile: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kFailedUserModifiedFile", "#GameUI_OK" ); + break; + case kInvalidMapName: + ShowMessageBox( "#TF_PublishFile_Error", "#TF_PublishFile_kInvalidMapName", "#GameUI_OK" ); + break; + default: + Assert( false ); // Unhandled enum value + break; + } + if ( pkvTokens ) + { + pkvTokens->deleteThis(); + } + } + + void ApplySchemeSettings( vgui::IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + + if ( !m_bAddingNewFile ) + { + bool bIsMap = ( m_FileDetails.publishedFileDetails.m_eFileType == k_EWorkshopFileTypeCommunity ); + + CExImageButton *pImageButton = FindControl<CExImageButton>( "ButtonSourceCosmetics", true ); + if ( pImageButton ) + { + pImageButton->SetVisible( !bIsMap ); + } + + vgui::Button *pButton = FindControl<Button>( "ButtonSourceOther", true ); + if ( pButton ) + { + pButton->SetVisible( !bIsMap ); + } + + pImageButton = FindControl<CExImageButton>( "ButtonSourceMaps", true ); + if ( pImageButton ) + { + pImageButton->SetVisible( bIsMap ); + } + } + } + +protected: + + virtual void PopulateEditFields( void ) + { + BaseClass::PopulateEditFields(); + + // class tags + vgui::EditablePanel* pClassUsagePanel = dynamic_cast<vgui::EditablePanel*>( FindChildByName( "ClassUsagePanel" ) ); + if ( pClassUsagePanel ) + { + for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ ) + { + bool bHasTag = Q_strstr( m_FileDetails.publishedFileDetails.m_rgchTags, kClassTags[i] ) != 0; + SetChildButtonSelected( pClassUsagePanel, VarArgs("ClassCheckBox%d",i), bHasTag ); + } + } + + // other tags + for ( uint32 i = 0; i < kNumOtherTags; ++i ) + { + bool bHasTag = Q_strstr( m_FileDetails.publishedFileDetails.m_rgchTags, kOtherTags[i].pTag ) != 0; + SetChildButtonSelected( this, kOtherTags[i].pCheckboxElementName, bHasTag, true ); + } + + // map tags + for ( uint32 i = 0; i < kNumMapTags; ++i ) + { + bool bHasTag = Q_strstr( m_FileDetails.publishedFileDetails.m_rgchTags, kMapTags[i].pTag ) != 0; + SetChildButtonSelected( this, kMapTags[i].pCheckboxElementName, bHasTag, true ); + } + + if ( Q_strstr( m_FileDetails.publishedFileDetails.m_rgchTags, kImportedTag ) ) + { + m_bImported = true; + } + else + { + m_bImported = false; + } + + // Default the tags hidden until there is a file set + if ( !m_FileDetails.lpszFilename ) + { + SetTagsVisible( false ); + } + else + { + SetTagsVisible( true, m_FileDetails.publishedFileDetails.m_eFileType ); + } + +#ifndef WORKSHOP_IMPORT_ENABLED + vgui::Button *pImportButton = FindControl<vgui::Button>( "ButtonSourceCosmetics" ); + if ( pImportButton ) + pImportButton->SetVisible( false ); +#endif + } + + const char* GetStatusString( StatusCode_t statusCode ) + { + switch ( statusCode ) + { + case kPublishing: + return "#TF_PublishFile_Publishing"; + break; + case kUpdating: + return "#TF_PublishFile_Updating"; + break; + } + return ""; + } + + void ShowStatusWindow( StatusCode_t statusCode ) + { + ShowWaitingDialog( new CGenericWaitingDialog( this ), GetStatusString( statusCode ), true, false, 0.0f ); + } + + void HideStatusWindow( void ) + { + CloseWaitingDialog(); + } + + virtual void OnCommand( const char *command ) + { + if ( V_stricmp( command, "MainFileCosmetics" ) == 0 ) + { +#ifdef WORKSHOP_IMPORT_ENABLED + if ( CItemUpload::InitManifest() ) + { + CTFFileImportDialog *pImportDialog = new CTFFileImportDialog( this ); + pImportDialog->SetDeleteSelfOnClose( true ); + pImportDialog->SetSizeable( false ); + MakeModalAndBringToFront( pImportDialog ); + } + else + { + ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_ImportFile_InvalidManifest" ); + } +#endif + } + else if ( V_stricmp( command, "Publish" ) == 0 || V_stricmp( command, "Update" ) == 0 ) + { + if ( m_bImported ) + { + CUtlBuffer buffer; + g_pFullFileSystem->ReadFile( m_sFilePath, NULL, buffer ); + CRC32_t crc = CRC32_ProcessSingleBuffer( buffer.Base(), buffer.Size() ); + if ( crc != m_fileCRC ) + { + ErrorMessage( kFailedUserModifiedFile, NULL ); + m_bImported = false; + return; + } + } + + BaseClass::OnCommand( command ); + } + else + { + BaseClass::OnCommand( command ); + } + } + +private: + CUtlVector< const char* > m_vecTags; + + CRC32_t m_fileCRC; + CUtlString m_sFilePath; + bool m_bImported; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CUGCFileRequestCache +{ +public: + CUGCFileRequestCache() + { + m_mapPreviewFileRequests.SetLessFunc( DefLessFunc( UGCHandle_t ) ); + } + + ~CUGCFileRequestCache() + { + m_mapPreviewFileRequests.PurgeAndDeleteElements(); + } + + CUGCFileRequest *GetFileRequest( UGCHandle_t fileHandle ) + { + int idx = m_mapPreviewFileRequests.Find( fileHandle ); + if ( idx == m_mapPreviewFileRequests.InvalidIndex() ) + { + idx = m_mapPreviewFileRequests.Insert( fileHandle, new CUGCFileRequest() ); + } + return m_mapPreviewFileRequests[idx]; + } + +private: + CUtlMap< UGCHandle_t, CUGCFileRequest* > m_mapPreviewFileRequests; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CSteamWorkshopItemPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CSteamWorkshopItemPanel, vgui::EditablePanel ); +public: + CSteamWorkshopItemPanel( vgui::Panel *parent, const char *panelName, CUGCFileRequestCache &previewFileCache ) + : vgui::EditablePanel( parent, panelName ) + , m_bSelected( false ) + , m_bPreviewDownloadPending( false ) + , m_pUGCPreviewFileRequest( NULL ) + , m_previewFileCache( previewFileCache ) + + { + memset( &m_FileDetails, 0, sizeof( m_FileDetails ) ); + m_pCroppedTextureImagePanel = new CBitmapPanel( this, "PreviewImage" ); + m_pHighlightPanel = new vgui::EditablePanel( this, "HighlightPanel" ); + vgui::ivgui()->AddTickSignal( GetVPanel(), 100 ); + } + + void SetPublishedFileDetails( const SteamUGCDetails_t *pDetails ) + { + if ( pDetails ) + { + m_FileDetails = *pDetails; + + SetDialogVariable( "title", m_FileDetails.m_rgchTitle ); + DownloadPreviewImage(); + + SetVisible( true ); + InvalidateLayout( true ); + } + else + { + m_bPreviewDownloadPending = false; + SetVisible( false ); + m_pUGCPreviewFileRequest = NULL; + } + + m_bSelected = false; + UpdateBorder(); + } + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + LoadControlSettings( "Resource/UI/SteamWorkshopItem.res" ); + } + + virtual void PerformLayout() + { + BaseClass::PerformLayout(); + } + + virtual void OnTick() + { + BaseClass::OnTick(); + + if ( m_bPreviewDownloadPending && m_pUGCPreviewFileRequest ) + { + UGCFileRequestStatus_t ugcStatus = m_pUGCPreviewFileRequest->Update(); + switch ( ugcStatus ) + { + case UGCFILEREQUEST_ERROR: + m_bPreviewDownloadPending = false; + break; + + case UGCFILEREQUEST_FINISHED: + SetPreviewImage(); + m_bPreviewDownloadPending = false; + break; + + default: + // Working, continue to wait... + return; + break; + } + } + } + + virtual void OnCursorEntered() + { + m_pHighlightPanel->SetVisible( true ); + } + + virtual void OnCursorExited() + { + UpdateBorder(); + } + + virtual void OnMousePressed(vgui::MouseCode code) + { + if ( code != MOUSE_LEFT ) + return; + + PostActionSignal( new KeyValues( "ItemPanelMousePressed" ) ); + } + + void UpdateBorder() + { + m_pHighlightPanel->SetVisible( m_bSelected ); + } + + void SetSelected( bool bSelected ) { m_bSelected = bSelected; } + bool GetSelected() const { return m_bSelected; } + + uint64 GetPublishedFileID() const { return m_FileDetails.m_nPublishedFileId; } + +protected: + void DownloadPreviewImage() + { + m_pCroppedTextureImagePanel->SetImage( NULL ); + m_pUGCPreviewFileRequest = m_previewFileCache.GetFileRequest( m_FileDetails.m_hPreviewFile ); + + switch ( m_pUGCPreviewFileRequest->GetStatus() ) + { + case UGCFILEREQUEST_READY: + { + // Start off our download + char szTargetFilename[MAX_PATH]; + V_snprintf( szTargetFilename, sizeof(szTargetFilename), "%llu_thumb.jpg", m_FileDetails.m_nPublishedFileId ); + m_pUGCPreviewFileRequest->StartDownload( m_FileDetails.m_hPreviewFile, "downloads", szTargetFilename ); + m_bPreviewDownloadPending = true; + } + break; + + case UGCFILEREQUEST_DOWNLOADING: + case UGCFILEREQUEST_DOWNLOAD_WRITING: + { + // download already going for another preview panel + m_bPreviewDownloadPending = true; + } + break; + + case UGCFILEREQUEST_FINISHED: + { + SetPreviewImage(); + } + break; + + default: + { + m_pCroppedTextureImagePanel->SetVisible( false ); + } + break; + } // switch + } + + void SetPreviewImage() + { + // Update our image preview + char szLocalFilename[MAX_PATH]; + m_pUGCPreviewFileRequest->GetLocalFileName( szLocalFilename, sizeof(szLocalFilename) ); + char szLocalPath[ _MAX_PATH ]; + g_pFullFileSystem->GetLocalPath( szLocalFilename, szLocalPath, sizeof(szLocalPath) ); + SetPreviewImage( szLocalPath ); + } + + void SetPreviewImage( const char *lpszFilename ) + { + if ( lpszFilename == NULL ) + return; + + ConversionErrorType nErrorCode = ImgUtl_LoadBitmap( lpszFilename, m_imgSource ); + if ( nErrorCode == CE_SUCCESS ) + { + m_pCroppedTextureImagePanel->SetBitmap( m_imgSource ); + m_pCroppedTextureImagePanel->SetVisible( true ); + } + } + + // data + bool m_bSelected; + bool m_bPreviewDownloadPending; + Bitmap_t m_imgSource; // original resolution and aspect + CBitmapPanel *m_pCroppedTextureImagePanel; + vgui::EditablePanel *m_pHighlightPanel; + CUGCFileRequest *m_pUGCPreviewFileRequest; + SteamUGCDetails_t m_FileDetails; + CUGCFileRequestCache &m_previewFileCache; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#define MAX_ITEMS_VIEWABLE 4 + +class CSteamWorkshopDialog : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CSteamWorkshopDialog, vgui::EditablePanel ); +public: + CSteamWorkshopDialog( vgui::Panel *parent ) + : vgui::EditablePanel( parent, "SteamWorkshopDialog" ) + , m_lastPublishedFilesState( CPublishedFiles::kState_Initialized ) + { + vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme" ); + SetScheme(scheme); + SetProportional( true ); + + m_pContainer = new vgui::EditablePanel( this, "Container" ); + m_pItemsContainer = new vgui::EditablePanel( m_pContainer, "ItemsContainer" ); + + vgui::ivgui()->AddTickSignal( GetVPanel(), 0 ); + + for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i ) + { + m_items[i] = new CSteamWorkshopItemPanel( m_pItemsContainer, VarArgs("SteamWorkshopItem%d", i ), m_previewFileCache ); + m_items[i]->AddActionSignalTarget( this ); + } + } + + virtual ~CSteamWorkshopDialog() + { + } + + virtual void OnTick( void ) + { + BaseClass::OnTick(); + + if ( m_lastPublishedFilesState != m_publishedFiles.m_state ) + { + CloseWaitingDialog(); + switch ( m_publishedFiles.m_state ) + { + case CPublishedFiles::kState_PopulatingFileList: + ShowWaitingDialog( new CGenericWaitingDialog( this ), "#TF_SteamWorkshop_PopulatingList", true, false, 30.0f ); + break; + case CPublishedFiles::kState_ErrorOccurred: + ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_SteamWorkshop_ErrorText", "#GameUI_OK" ); + break; + case CPublishedFiles::kState_DeletingFile: + ShowWaitingDialog( new CGenericWaitingDialog( this ), "#TF_SteamWorkshop_DeletingFile", true, false, 30.0f ); + break; + case CPublishedFiles::kState_ErrorCannotDeleteFile: + ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_SteamWorkshop_CannotDeleteFile", "#GameUI_OK" ); + break; + case CPublishedFiles::kState_Timeout: + ShowMessageBox( "#TF_SteamWorkshop_Error", "#TF_SteamWorkshop_Timeout", "#GameUI_OK" ); + break; + case CPublishedFiles::kState_Done: + RefreshItemsUI(); + break; + } + m_lastPublishedFilesState = m_publishedFiles.m_state; + } + } + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings( "Resource/ui/SteamWorkshopDialog.res" ); + + SetupButton( "LearnMoreButton" ); + SetupButton( "LearnMore2Button" ); + SetupButton( "BrowseButton" ); + SetupButton( "PublishButton" ); + SetupButton( "ViewPublishedButton" ); + SetupButton( "LoadTestMapButton" ); + SetupButton( "ViewLegalAgreementButton" ); + + SetupButton( "PrevPageButton" ); + SetupButton( "NextPageButton" ); + SetupButton( "ViewButton" ); + SetupButton( "EditButton" ); + SetupButton( "DeleteButton" ); + + SetupButton( "CancelButton" ); + + SetChildPanelEnabled( this, "LoadTestMapButton", !engine->IsInGame() || !FStrEq( engine->GetLevelName(), "maps/itemtest.bsp" ), true ); + } + + virtual void PerformLayout() + { + BaseClass::PerformLayout(); + + // Center it, keeping requested size + int x, y, ww, wt, wide, tall; + vgui::surface()->GetWorkspaceBounds( x, y, ww, wt ); + GetSize(wide, tall); + SetPos(x + ((ww - wide) / 2), y + ((wt - tall) / 2)); + } + + virtual void Show() + { + TFModalStack()->PushModal( this ); + + // Make sure we're signed on + if ( !CheckSteamSignOn() ) + { + Close(); + return; + } + + SetVisible( true ); + MakePopup(); + MoveToFront(); + SetKeyBoardInputEnabled( true ); + SetMouseInputEnabled( true ); + + // start download + m_publishedFiles.PopulateFileList(); + } + + virtual void OnCommand( const char *pCommand ) + { + if ( FStrEq( pCommand, "cancel" ) ) + { + Close(); + } + else if ( FStrEq( pCommand, "publish" ) ) + { + ShowPublishFileDialog(); + } + else if ( FStrEq( pCommand, "learn_more" ) ) + { + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( "http://www.teamfortress.com/contribute/" ); + } + else if ( FStrEq( pCommand, "view_files" ) ) + { + EUniverse universe = GetUniverse(); + uint64 ulSteamID = steamapicontext->SteamUser()->GetSteamID().ConvertToUint64(); + switch ( universe ) + { + case k_EUniversePublic: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/profiles/%llu/mysharedfiles/", ulSteamID ) ); + break; + case k_EUniverseBeta: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/profiles/%llu/mysharedfiles/", ulSteamID ) ); + break; + case k_EUniverseDev: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "profiles/%llu/mysharedfiles/", ulSteamID ) ); + break; + } + } + else if ( FStrEq( pCommand, "view_legal_agreement" ) ) + { + EUniverse universe = GetUniverse(); + switch ( universe ) + { + case k_EUniversePublic: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/workshop/workshoplegalagreement/?appid=%d", engine->GetAppID() ) ); + break; + case k_EUniverseBeta: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/workshop/workshoplegalagreement/?appid=%d", engine->GetAppID() ) ); + break; + case k_EUniverseDev: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "workshop/workshoplegalagreement/?appid=%d", engine->GetAppID() ) ); + break; + } + } + else if ( FStrEq( pCommand, "browse" ) ) + { + EUniverse universe = GetUniverse(); + switch ( universe ) + { + case k_EUniversePublic: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/workshop/browse?appid=%d", engine->GetAppID() ) ); + break; + case k_EUniverseBeta: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/workshop/browse?appid=%d", engine->GetAppID() ) ); + break; + case k_EUniverseDev: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "workshop/browse?appid=%d", engine->GetAppID() ) ); + break; + } + } + else if ( FStrEq( pCommand, "itemtest" ) ) + { + Close(); + engine->ClientCmd_Unrestricted( "disconnect\nwait\nwait\n\nprogress_enable\nmap itemtest\n" ); + } + else if ( FStrEq( pCommand, "prevpage" ) ) + { + if ( m_unCurrentPage > 0 ) + { + --m_unCurrentPage; + } + PopulatePublishedFilesUI(); + } + else if ( FStrEq( pCommand, "nextpage" ) ) + { + uint32 unNumPages = ceil( (float)m_publishedFiles.m_FileDetails.Count() / (float)MAX_ITEMS_VIEWABLE ); + if ( m_unCurrentPage < unNumPages ) + { + ++m_unCurrentPage; + PopulatePublishedFilesUI(); + } + } + else if ( FStrEq( pCommand, "view" ) ) + { + for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i ) + { + if ( m_items[i]->GetSelected() ) + { + ViewPublishedFile( m_items[i]->GetPublishedFileID() ); + break; + } + } + } + else if ( FStrEq( pCommand, "edit" ) ) + { + for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i ) + { + if ( m_items[i]->GetSelected() ) + { + EditPublishedFile( m_items[i]->GetPublishedFileID() ); + break; + } + } + } + else if ( FStrEq( pCommand, "delete_item" ) ) + { + for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i ) + { + if ( m_items[i]->GetSelected() ) + { + DeletePublishedFile( m_items[i]->GetPublishedFileID() ); + break; + } + } + } + else + { + BaseClass::OnCommand( pCommand ); + } + } + + virtual void OnKeyCodeTyped( vgui::KeyCode code ) + { + if( code == KEY_ESCAPE ) + { + OnCommand( "cancel" ); + } + else + { + BaseClass::OnKeyCodeTyped( code ); + } + } + + virtual void OnKeyCodePressed( vgui::KeyCode code ) + { + if( GetBaseButtonCode( code ) == KEY_XBUTTON_B ) + { + OnCommand( "cancel" ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } + } + + MESSAGE_FUNC_PTR( OnItemPanelMousePressed, "ItemPanelMousePressed", panel ) + { + CSteamWorkshopItemPanel *pItemPanel = static_cast< CSteamWorkshopItemPanel * >( panel ); + for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i ) + { + m_items[i]->SetSelected( m_items[i] == pItemPanel ); + m_items[i]->UpdateBorder(); + } + + // enable per item controls + SetChildPanelEnabled( m_pItemsContainer, "ViewButton", true ); + SetChildPanelEnabled( m_pItemsContainer, "EditButton", true ); + SetChildPanelEnabled( m_pItemsContainer, "DeleteButton", true ); + } + + MESSAGE_FUNC_UINT64( OnChangedFile, "ChangedFile", nPublishedFileID ) + { + m_publishedFiles.RefreshPublishedFileDetails( nPublishedFileID ); + } + +protected: + + bool CheckSteamSignOn() + { + // Make sure we are connected to steam, or they are going to be disappointed + if ( steamapicontext == NULL + || steamapicontext->SteamUtils() == NULL + || steamapicontext->SteamMatchmakingServers() == NULL + || steamapicontext->SteamUser() == NULL + || !steamapicontext->SteamUser()->BLoggedOn() + ) { + Warning( "Steam not properly initialized or connected.\n" ); + ShowMessageBox( "#TF_MM_GenericFailure_Title", "#TF_MM_GenericFailure", "#GameUI_OK" ); + return false; + } + return true; + } + + void RefreshItemsUI() + { + SetChildPanelVisible( m_pContainer, "NoItemsContainer", m_publishedFiles.m_FileDetails.Count() == 0 ); + SetChildPanelVisible( m_pContainer, "ItemsContainer", m_publishedFiles.m_FileDetails.Count() != 0 ); + SetChildPanelVisible( m_pContainer, "LearnMore2Button", m_publishedFiles.m_FileDetails.Count() != 0 ); + SetChildPanelVisible( m_pContainer, "BrowseButton", m_publishedFiles.m_FileDetails.Count() == 0 ); + + m_unCurrentPage = 0; + PopulatePublishedFilesUI(); + } + + void PopulatePublishedFilesUI() + { + if ( m_publishedFiles.m_FileDetails.Count() != 0 ) + { + uint32 unIndex = m_unCurrentPage * MAX_ITEMS_VIEWABLE; + int nFileIdx = m_publishedFiles.m_FileDetails.FirstInorder(); + for ( uint32 i = 0; i < unIndex && nFileIdx != m_publishedFiles.m_FileDetails.InvalidIndex(); ++i ) + { + nFileIdx = m_publishedFiles.m_FileDetails.NextInorder( nFileIdx ); + } + bool bSelected = false; + for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i ) + { + CSteamWorkshopItemPanel *pItemPanel = m_items[i]; + if ( nFileIdx != m_publishedFiles.m_FileDetails.InvalidIndex() ) + { + SteamUGCDetails_t &details = m_publishedFiles.m_FileDetails[ nFileIdx ]; + pItemPanel->SetPublishedFileDetails( &details ); + if ( !bSelected ) + { + pItemPanel->SetSelected( true ); + pItemPanel->UpdateBorder(); + bSelected = true; + } + nFileIdx = m_publishedFiles.m_FileDetails.NextInorder( nFileIdx ); + } + else + { + pItemPanel->SetPublishedFileDetails( NULL ); + } + } + + // paging + uint32 unNumPages = ceil( (float)m_publishedFiles.m_FileDetails.Count() / (float)MAX_ITEMS_VIEWABLE ); + bool bMultiplePages = ( unNumPages > 1 ); + if ( bMultiplePages ) + { + m_pItemsContainer->SetDialogVariable( "page", CFmtStr( "%d/%d", m_unCurrentPage + 1, unNumPages ) ); + } + else + { + m_pItemsContainer->SetDialogVariable( "page", "" ); + } + SetChildPanelVisible( m_pItemsContainer, "NextPageButton", bMultiplePages ); + SetChildPanelVisible( m_pItemsContainer, "PrevPageButton", bMultiplePages ); + SetChildPanelEnabled( m_pItemsContainer, "NextPageButton", m_unCurrentPage < unNumPages - 1 ); + SetChildPanelEnabled( m_pItemsContainer, "PrevPageButton", m_unCurrentPage > 0 ); + + // other controls + SetChildPanelEnabled( m_pItemsContainer, "ViewButton", bSelected ); + SetChildPanelEnabled( m_pItemsContainer, "EditButton", bSelected ); + SetChildPanelEnabled( m_pItemsContainer, "DeleteButton", bSelected ); + } + else + { + SetChildPanelEnabled( m_pItemsContainer, "ViewButton", false ); + SetChildPanelEnabled( m_pItemsContainer, "EditButton", false ); + SetChildPanelEnabled( m_pItemsContainer, "DeleteButton", false ); + } + } + + void ShowPublishFileDialog() + { + CTFFilePublishDialog *pPublishDialog = new CTFFilePublishDialog( this, "PublishFileDialog", NULL ); + pPublishDialog->AddActionSignalTarget( this ); + pPublishDialog->SetDeleteSelfOnClose( true ); + pPublishDialog->SetSizeable( false ); + MakeModalAndBringToFront( pPublishDialog ); + } + + void ViewPublishedFile( uint64 nPublishedFileID ) + { + EUniverse universe = GetUniverse(); + switch ( universe ) + { + case k_EUniversePublic: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://steamcommunity.com/sharedfiles/filedetails/?id=%llu", nPublishedFileID ) ); + break; + case k_EUniverseBeta: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( "http://beta.steamcommunity.com/sharedfiles/filedetails/?id=%llu", nPublishedFileID ) ); + break; + case k_EUniverseDev: + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( CFmtStrMax( COMMUNITY_DEV_HOST "sharedfiles/filedetails/?id=%llu", nPublishedFileID ) ); + break; + } + } + + void EditPublishedFile( uint64 nPublishedFileID ) + { + const SteamUGCDetails_t *pDetails = m_publishedFiles.GetPublishedFileDetails( nPublishedFileID ); + if ( pDetails ) + { + + PublishedFileDetails_t fileDetails; + fileDetails.publishedFileDetails = *pDetails; + fileDetails.lpszFilename = pDetails->m_pchFileName; + + CTFFilePublishDialog *pPublishDialog = new CTFFilePublishDialog( this, "PublishFileDialog", &fileDetails ); + pPublishDialog->AddActionSignalTarget( this ); + pPublishDialog->SetDeleteSelfOnClose( true ); + pPublishDialog->SetSizeable( false ); + MakeModalAndBringToFront( pPublishDialog ); + + if ( fileDetails.publishedFileDetails.m_eFileType == k_EWorkshopFileTypeMicrotransaction && + !Q_strstr( fileDetails.publishedFileDetails.m_rgchTags, kImportedTag ) ) + { + ShowMessageBox( "#TF_ImportFile_Warning", "#TF_ImportFile_NotCompatible" ); + } + } + } + + static void ConfirmDeletePublishedFile( bool bConfirmed, void *pContext ) + { + if ( bConfirmed ) + { + CSteamWorkshopDialog *pDialog = static_cast< CSteamWorkshopDialog* >( pContext ); + uint64 nPublishedFileID = 0; + for ( uint32 i = 0; i < MAX_ITEMS_VIEWABLE; ++i ) + { + if ( pDialog->m_items[i]->GetSelected() ) + { + nPublishedFileID = pDialog->m_items[i]->GetPublishedFileID(); + break; + } + } + if ( nPublishedFileID != 0 ) + { + const SteamUGCDetails_t *pDetails = pDialog->m_publishedFiles.GetPublishedFileDetails( nPublishedFileID ); + if ( pDetails ) + { + pDialog->m_publishedFiles.DeletePublishedFile( nPublishedFileID ); + } + } + } + } + + void DeletePublishedFile( uint64 nPublishedFileID ) + { + ShowConfirmDialog( "#TF_SteamWorkshop_DeleteConfirmTitle", "#TF_SteamWorkshop_DeleteConfirmText", "#GameUI_OK", "#GameuI_CancelBold", &ConfirmDeletePublishedFile, this, this ); + } + + void SetupButton( const char *pPanelName ) + { + vgui::Panel *pPanel = m_pContainer->FindChildByName( pPanelName, true ); + if ( pPanel ) + { + pPanel->AddActionSignalTarget( this ); + } + } + + // called when the Cancel button is pressed + void Close() + { + SetVisible( false ); + TFModalStack()->PopModal( this ); + MarkForDeletion(); + } + + vgui::EditablePanel *m_pContainer; + vgui::EditablePanel *m_pItemsContainer; + CPublishedFiles m_publishedFiles; + uint32 m_lastPublishedFilesState; + uint32 m_unCurrentPage; + CSteamWorkshopItemPanel *m_items[MAX_ITEMS_VIEWABLE]; + CUGCFileRequestCache m_previewFileCache; +}; +static vgui::DHANDLE<CSteamWorkshopDialog> g_pSteamWorkshopDialog; + +//----------------------------------------------------------------------------- +// Purpose: Callback to open the game menus +//----------------------------------------------------------------------------- +static void CL_OpenSteamWorkshopDialog( const CCommand &args ) +{ + if ( g_pSteamWorkshopDialog.Get() == NULL ) + { + IViewPortPanel *pMMOverride = ( gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE ) ); + g_pSteamWorkshopDialog = vgui::SETUP_PANEL( new CSteamWorkshopDialog( (CHudMainMenuOverride*)pMMOverride ) ); + } + engine->ExecuteClientCmd( "gameui_activate" ); + g_pSteamWorkshopDialog->Show(); +} + +// the console commands +static ConCommand steamworkshopdialog( "OpenSteamWorkshopDialog", &CL_OpenSteamWorkshopDialog, "" ); + +//----------------------------------------------------------------------------- + +class CItemTestHUDPanel : public CHudElement, public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CItemTestHUDPanel, vgui::EditablePanel ); +public: + CItemTestHUDPanel( const char *pElementName ) + : CHudElement( pElementName ) + , BaseClass( NULL, "ItemTestHUDPanel" ) + { + vgui::Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetHiddenBits( HIDEHUD_MISCSTATUS ); + } + + virtual ~CItemTestHUDPanel() + { + + } + + virtual bool ShouldDraw( void ) + { + if ( !CHudElement::ShouldDraw() ) + return false; + + if ( !engine->IsInGame() ) + return false; + + if ( TFGameRules() && TFGameRules()->IsInItemTestingMode() ) + return true; + + return FStrEq( engine->GetLevelName(), "maps/itemtest.bsp" ); + } + + virtual void PerformLayout() + { + BaseClass::PerformLayout(); + + if ( m_pBGPanel_Blue && m_pBGPanel_Red && C_TFPlayer::GetLocalTFPlayer() ) + { + bool bRed = ( C_TFPlayer::GetLocalTFPlayer()->GetTeamNumber() == TF_TEAM_RED ); + m_pBGPanel_Blue->SetVisible( !bRed ); + m_pBGPanel_Red->SetVisible( bRed ); + } + } + + virtual void ApplySchemeSettings( vgui::IScheme *scheme ) + { + if ( g_pFullFileSystem->FileExists( "resource/UI/ItemTestHUDPanel.res" ) ) + { + LoadControlSettings( "resource/UI/ItemTestHUDPanel.res" ); + } + + BaseClass::ApplySchemeSettings( scheme ); + + m_pBGPanel_Blue = FindChildByName("Background_Blue"); + m_pBGPanel_Red = FindChildByName("Background_Red"); + + if ( m_pBGPanel_Blue ) + { + m_pBGPanel_Blue->SetVisible( true ); + } + } + + int HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) + { + if ( !IsVisible() ) + return 1; // key not handled + + if ( !down ) + return 1; // key not handled + + switch ( keynum ) + { + case KEY_F7: + { + engine->ClientCmd_Unrestricted( "itemtest" ); + return 0; + } + break; + case KEY_F8: + { + engine->ClientCmd_Unrestricted( "itemtest_botcontrols" ); + return 0; + } + break; + case KEY_F9: + { + InvalidateLayout( true, true ); + return 0; + } + break; + } + + return 1; // key not handled + } + +protected: + vgui::Panel *m_pBGPanel_Blue; + vgui::Panel *m_pBGPanel_Red; +}; + +DECLARE_HUDELEMENT( CItemTestHUDPanel ); + +//----------------------------------------------------------------------------- + +// @return true if the item test HUD handled the input, false otherwise +bool ItemTestHandlesKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) +{ + CItemTestHUDPanel *pItemTestHUDPanel = ( CItemTestHUDPanel * )GET_HUDELEMENT( CItemTestHUDPanel ); + if ( pItemTestHUDPanel ) + { + return pItemTestHUDPanel->HudElementKeyInput( down, keynum, pszCurrentBinding ) == 0; + } + return false; +} + |