Added ability to create brand new SCAN/STRG assets. Added ability to update old projects.
This commit is contained in:
parent
1e997dac46
commit
56843e214d
|
@ -1 +1 @@
|
|||
Subproject commit c98f4b3dcef68724627af91ac4103de7f28d2a2b
|
||||
Subproject commit 5c3bfbe57f6ef8a300933afdc053a445cabd5c7c
|
|
@ -626,7 +626,7 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
|
|||
Name = rRes.ResourceID.ToString();
|
||||
#endif
|
||||
|
||||
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResTypeInfo::TypeForCookedExtension(mGame, rRes.ResourceType)->Type(), Directory, Name);
|
||||
CResourceEntry *pEntry = mpStore->CreateNewResource(rRes.ResourceID, CResTypeInfo::TypeForCookedExtension(mGame, rRes.ResourceType)->Type(), Directory, Name);
|
||||
|
||||
// Set flags
|
||||
pEntry->SetFlag(EResEntryFlag::IsBaseGameResource);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "CGameProject.h"
|
||||
#include "CResourceIterator.h"
|
||||
#include "IUIRelay.h"
|
||||
#include "Core/Resource/Script/CGameTemplate.h"
|
||||
#include <Common/Serialization/XML.h>
|
||||
|
@ -233,7 +234,11 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
|
|||
pProj->mpResourceStore = std::make_unique<CResourceStore>(pProj);
|
||||
LoadSuccess = pProj->mpResourceStore->LoadDatabaseCache();
|
||||
|
||||
// Validate resource database
|
||||
// Removed database validation step. We used to do this on project load to make sure all data was correct, but this takes a long
|
||||
// time and significantly extends how long it takes to open a project. In actual practice, this isn't needed most of the time, and
|
||||
// in the odd case that it is needed, there is a button in the resource browser to rebuild the database. So in the interest of
|
||||
// making project startup faster, we no longer validate the database.
|
||||
#if 0
|
||||
if (LoadSuccess)
|
||||
{
|
||||
pProgress->Report("Validating resource database");
|
||||
|
@ -253,6 +258,7 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
|
|||
LoadSuccess = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!LoadSuccess)
|
||||
|
@ -263,6 +269,34 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
|
|||
|
||||
pProj->mProjFileLock.Lock(ProjPath);
|
||||
pProj->mpGameInfo->LoadGameInfo(pProj->mGame);
|
||||
|
||||
// Perform update
|
||||
if (Reader.FileVersion() < (uint16) EProjectVersion::Current)
|
||||
{
|
||||
pProgress->Report("Updating project");
|
||||
|
||||
CResourceStore* pOldStore = gpResourceStore;
|
||||
gpResourceStore = pProj->mpResourceStore.get();
|
||||
|
||||
for (CResourceIterator It; It; ++It)
|
||||
{
|
||||
if (It->TypeInfo()->CanBeSerialized() && !It->HasRawVersion())
|
||||
{
|
||||
It->Save(true, false);
|
||||
|
||||
// Touch the cooked file to update its last modified time.
|
||||
// This prevents PWE from erroneously thinking the cooked file is outdated
|
||||
// (due to the raw file we just made having a more recent last modified time)
|
||||
FileUtil::UpdateLastModifiedTime( It->CookedAssetPath() );
|
||||
}
|
||||
}
|
||||
|
||||
pProj->mpResourceStore->ConditionalSaveStore();
|
||||
pProj->Save();
|
||||
|
||||
gpResourceStore = pOldStore;
|
||||
}
|
||||
|
||||
pProj->mpAudioManager->LoadAssets();
|
||||
pProj->mpTweakManager->LoadTweaks();
|
||||
return pProj;
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace nod { class DiscWii; }
|
|||
enum class EProjectVersion
|
||||
{
|
||||
Initial,
|
||||
RawStrings,
|
||||
// Add new versions before this line
|
||||
|
||||
Max,
|
||||
|
|
|
@ -59,6 +59,16 @@ void CPackage::UpdateDependencyCache() const
|
|||
mCacheDirty = false;
|
||||
}
|
||||
|
||||
void CPackage::MarkDirty()
|
||||
{
|
||||
if (!mNeedsRecook)
|
||||
{
|
||||
mNeedsRecook = true;
|
||||
Save();
|
||||
UpdateDependencyCache();
|
||||
}
|
||||
}
|
||||
|
||||
void CPackage::Cook(IProgressNotifier *pProgress)
|
||||
{
|
||||
SCOPED_TIMER(CookPackage);
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
void Serialize(IArchive& rArc);
|
||||
void AddResource(const TString& rkName, const CAssetID& rkID, const CFourCC& rkType);
|
||||
void UpdateDependencyCache() const;
|
||||
void MarkDirty();
|
||||
|
||||
void Cook(IProgressNotifier *pProgress);
|
||||
void CompareOriginalAssetList(const std::list<CAssetID>& rkNewList);
|
||||
|
@ -77,7 +78,6 @@ public:
|
|||
inline bool NeedsRecook() const { return mNeedsRecook; }
|
||||
|
||||
inline void SetPakName(TString NewName) { mPakName = NewName; }
|
||||
inline void MarkDirty() { mNeedsRecook = true; Save(); UpdateDependencyCache(); }
|
||||
};
|
||||
|
||||
#endif // CPACKAGE
|
||||
|
|
|
@ -40,6 +40,15 @@ CResourceEntry* CResourceEntry::CreateNewResource(CResourceStore *pStore, const
|
|||
pEntry->mpDirectory->AddChild("", pEntry);
|
||||
|
||||
pEntry->mMetadataDirty = true;
|
||||
|
||||
// Check if the data exists or not. If so, then we are creating an entry for an existing resource (game exporter).
|
||||
// If not, we want to initiate the new resource data and save it as soon as possible.
|
||||
if (!pEntry->HasCookedVersion())
|
||||
{
|
||||
pEntry->mpResource = CResourceFactory::SpawnResource(pEntry);
|
||||
pEntry->mpResource->InitializeNewResource();
|
||||
}
|
||||
|
||||
return pEntry;
|
||||
}
|
||||
|
||||
|
@ -261,7 +270,7 @@ bool CResourceEntry::NeedsRecook() const
|
|||
return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath()));
|
||||
}
|
||||
|
||||
bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
|
||||
bool CResourceEntry::Save(bool SkipCacheSave /*= false*/, bool FlagForRecook /*= true*/)
|
||||
{
|
||||
// SkipCacheSave argument tells us not to save the resource cache file. This is generally not advised because we don't
|
||||
// want the actual resource data to desync from the cache data. However, there are occasions where we save multiple
|
||||
|
@ -270,7 +279,6 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
|
|||
//
|
||||
// For now, always save the resource when this function is called even if there's been no changes made to it in memory.
|
||||
// In the future this might not be desired behavior 100% of the time.
|
||||
// We also might want this function to trigger a cook for certain resource types eventually.
|
||||
bool ShouldCollectGarbage = false;
|
||||
|
||||
// Save raw resource
|
||||
|
@ -298,8 +306,11 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (FlagForRecook)
|
||||
{
|
||||
SetFlag(EResEntryFlag::NeedsRecook);
|
||||
}
|
||||
}
|
||||
|
||||
// This resource type doesn't have a raw format; save cooked instead
|
||||
else
|
||||
|
@ -324,13 +335,16 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
|
|||
}
|
||||
|
||||
// Flag dirty any packages that contain this resource.
|
||||
if (FlagForRecook)
|
||||
{
|
||||
for (uint32 iPkg = 0; iPkg < mpStore->Project()->NumPackages(); iPkg++)
|
||||
{
|
||||
CPackage *pPkg = mpStore->Project()->PackageByIndex(iPkg);
|
||||
|
||||
if (pPkg->ContainsAsset(ID()))
|
||||
if (!pPkg->NeedsRecook() && pPkg->ContainsAsset(ID()))
|
||||
pPkg->MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldCollectGarbage)
|
||||
mpStore->DestroyUnreferencedResources();
|
||||
|
|
|
@ -67,7 +67,7 @@ public:
|
|||
bool IsInDirectory(CVirtualDirectory *pDir) const;
|
||||
uint64 Size() const;
|
||||
bool NeedsRecook() const;
|
||||
bool Save(bool SkipCacheSave = false);
|
||||
bool Save(bool SkipCacheSave = false, bool FlagForRecook = true);
|
||||
bool Cook();
|
||||
CResource* Load();
|
||||
CResource* LoadCooked(IInputStream& rInput);
|
||||
|
@ -87,7 +87,7 @@ public:
|
|||
inline void SetFlagEnabled(EResEntryFlag Flag, bool Enabled) { Enabled ? SetFlag(Flag) : ClearFlag(Flag); }
|
||||
|
||||
inline void SetDirty() { SetFlag(EResEntryFlag::NeedsRecook); }
|
||||
inline void SetHidden(bool Hidden) { Hidden ? SetFlag(EResEntryFlag::Hidden) : ClearFlag(EResEntryFlag::Hidden); }
|
||||
inline void SetHidden(bool Hidden) { SetFlagEnabled(EResEntryFlag::Hidden, Hidden); }
|
||||
inline bool HasFlag(EResEntryFlag Flag) const { return mFlags.HasFlag(Flag); }
|
||||
inline bool IsHidden() const { return HasFlag(EResEntryFlag::Hidden); }
|
||||
|
||||
|
|
|
@ -284,7 +284,14 @@ void CResourceStore::ClearDatabase()
|
|||
{
|
||||
// THIS OPERATION REQUIRES THAT ALL RESOURCES ARE UNREFERENCED
|
||||
DestroyUnreferencedResources();
|
||||
ASSERT(mLoadedResources.empty());
|
||||
|
||||
if (!mLoadedResources.empty())
|
||||
{
|
||||
debugf("ERROR: Resources still loaded:");
|
||||
for (auto Iter = mLoadedResources.begin(); Iter != mLoadedResources.end(); Iter++)
|
||||
debugf("\t[%s] %s", *Iter->first.ToString(), *Iter->second->CookedAssetPath(true));
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
// Clear out existing resource entries and directories
|
||||
for (auto Iter = mResourceEntries.begin(); Iter != mResourceEntries.end(); Iter++)
|
||||
|
@ -384,7 +391,7 @@ bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const
|
|||
return FindEntry(rkID) != nullptr;
|
||||
}
|
||||
|
||||
CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName)
|
||||
CResourceEntry* CResourceStore::CreateNewResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName)
|
||||
{
|
||||
CResourceEntry *pEntry = FindEntry(rkID);
|
||||
|
||||
|
@ -398,6 +405,12 @@ CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResource
|
|||
{
|
||||
pEntry = CResourceEntry::CreateNewResource(this, rkID, rkDir, rkName, Type);
|
||||
mResourceEntries[rkID] = pEntry;
|
||||
mDatabaseCacheDirty = true;
|
||||
|
||||
if (pEntry->IsLoaded())
|
||||
{
|
||||
TrackLoadedResource(pEntry);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
TString DefaultResourceDirPath() const;
|
||||
|
||||
bool IsResourceRegistered(const CAssetID& rkID) const;
|
||||
CResourceEntry* RegisterResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName);
|
||||
CResourceEntry* CreateNewResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName);
|
||||
CResourceEntry* FindEntry(const CAssetID& rkID) const;
|
||||
CResourceEntry* FindEntry(const TString& rkPath) const;
|
||||
bool AreAllEntriesValid() const;
|
||||
|
|
|
@ -92,7 +92,7 @@ void CAnimationParameters::Write(IOutputStream& rSCLY)
|
|||
{
|
||||
CAssetID::skInvalidID32.Write(rSCLY);
|
||||
rSCLY.WriteLong(0);
|
||||
rSCLY.WriteLong(0);
|
||||
rSCLY.WriteLong(mGame >= EGame::EchoesDemo ? 0 : 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ CResTypeInfo::CResTypeInfo(EResourceType Type, const TString& rkTypeName, const
|
|||
, mRetroExtension(rkRetroExtension)
|
||||
, mCanBeSerialized(false)
|
||||
, mCanHaveDependencies(true)
|
||||
, mCanBeCreated(false)
|
||||
{
|
||||
#if !PUBLIC_RELEASE
|
||||
ASSERT(smTypeMap.find(Type) == smTypeMap.end());
|
||||
|
@ -332,6 +333,7 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
|
|||
{
|
||||
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Scan, "Scan", "scan");
|
||||
AddExtension(pType, "SCAN", EGame::PrimeDemo, EGame::Corruption);
|
||||
pType->mCanBeCreated = true;
|
||||
}
|
||||
{
|
||||
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Skeleton, "Skeleton", "cin");
|
||||
|
@ -382,6 +384,7 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
|
|||
CResTypeInfo *pType = new CResTypeInfo(EResourceType::StringTable, "String Table", "strg");
|
||||
AddExtension(pType, "STRG", EGame::PrimeDemo, EGame::DKCReturns);
|
||||
pType->mCanBeSerialized = true;
|
||||
pType->mCanBeCreated = true;
|
||||
}
|
||||
{
|
||||
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Texture, "Texture", "txtr");
|
||||
|
|
|
@ -23,6 +23,7 @@ class CResTypeInfo
|
|||
TString mRetroExtension; // File extension in Retro's directory tree. We don't use it directly but it is needed for generating asset ID hashes
|
||||
bool mCanBeSerialized;
|
||||
bool mCanHaveDependencies;
|
||||
bool mCanBeCreated;
|
||||
|
||||
static std::unordered_map<EResourceType, CResTypeInfo*> smTypeMap;
|
||||
|
||||
|
@ -40,6 +41,7 @@ public:
|
|||
inline TString TypeName() const { return mTypeName; }
|
||||
inline bool CanBeSerialized() const { return mCanBeSerialized; }
|
||||
inline bool CanHaveDependencies() const { return mCanHaveDependencies; }
|
||||
inline bool CanBeCreated() const { return mCanBeCreated; }
|
||||
|
||||
// Static
|
||||
static void GetAllTypesInGame(EGame Game, std::list<CResTypeInfo*>& rOut);
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
virtual ~CResource() {}
|
||||
virtual CDependencyTree* BuildDependencyTree() const { return new CDependencyTree(); }
|
||||
virtual void Serialize(IArchive& /*rArc*/) {}
|
||||
virtual void InitializeNewResource() {}
|
||||
|
||||
inline CResourceEntry* Entry() const { return mpEntry; }
|
||||
inline CResTypeInfo* TypeInfo() const { return mpEntry->TypeInfo(); }
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "CStringTable.h"
|
||||
#include "Core/GameProject/CGameProject.h"
|
||||
#include <Common/Math/MathUtil.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
@ -8,27 +9,27 @@
|
|||
* This is also the order that languages appear in game STRG assets.
|
||||
*/
|
||||
// Supported languages in the original NTSC release of Metroid Prime
|
||||
const ELanguage gkSupportedLanguagesMP1[] =
|
||||
const std::vector<ELanguage> gkSupportedLanguagesMP1 =
|
||||
{
|
||||
ELanguage::English
|
||||
};
|
||||
|
||||
// Supported languages in the PAL version of Metroid Prime, and also Metroid Prime 2
|
||||
const ELanguage gkSupportedLanguagesMP1PAL[] =
|
||||
const std::vector<ELanguage> gkSupportedLanguagesMP1PAL =
|
||||
{
|
||||
ELanguage::English, ELanguage::French, ELanguage::German,
|
||||
ELanguage::Spanish, ELanguage::Italian, ELanguage::Japanese
|
||||
};
|
||||
|
||||
// Supported languages in Metroid Prime 3
|
||||
const ELanguage gkSupportedLanguagesMP3[] =
|
||||
const std::vector<ELanguage> gkSupportedLanguagesMP3 =
|
||||
{
|
||||
ELanguage::English, ELanguage::Japanese, ELanguage::German,
|
||||
ELanguage::French, ELanguage::Spanish, ELanguage::Italian
|
||||
};
|
||||
|
||||
// Supported languages in DKCR
|
||||
const ELanguage gkSupportedLanguagesDKCR[] =
|
||||
const std::vector<ELanguage> gkSupportedLanguagesDKCR =
|
||||
{
|
||||
ELanguage::English, ELanguage::Japanese, ELanguage::German,
|
||||
ELanguage::French, ELanguage::Spanish, ELanguage::Italian,
|
||||
|
@ -36,6 +37,32 @@ const ELanguage gkSupportedLanguagesDKCR[] =
|
|||
ELanguage::NAFrench, ELanguage::NASpanish
|
||||
};
|
||||
|
||||
// Utility function - retrieve the language array for a given game/region
|
||||
static const std::vector<ELanguage>& GetSupportedLanguages(EGame Game, ERegion Region)
|
||||
{
|
||||
switch (Game)
|
||||
{
|
||||
default:
|
||||
case EGame::PrimeDemo:
|
||||
case EGame::Prime:
|
||||
if (Region == ERegion::NTSC)
|
||||
return gkSupportedLanguagesMP1;
|
||||
else
|
||||
return gkSupportedLanguagesMP1PAL;
|
||||
|
||||
case EGame::EchoesDemo:
|
||||
case EGame::Echoes:
|
||||
case EGame::CorruptionProto:
|
||||
return gkSupportedLanguagesMP1PAL;
|
||||
|
||||
case EGame::Corruption:
|
||||
return gkSupportedLanguagesMP3;
|
||||
|
||||
case EGame::DKCReturns:
|
||||
return gkSupportedLanguagesDKCR;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function - retrieve the index of a given language
|
||||
static int FindLanguageIndex(const CStringTable* pkInTable, ELanguage InLanguage)
|
||||
{
|
||||
|
@ -192,10 +219,19 @@ void CStringTable::RemoveString(uint StringIndex)
|
|||
}
|
||||
}
|
||||
|
||||
/** Configures the string table with default languages for the game/region pairing of the resource */
|
||||
void CStringTable::ConfigureDefaultLanguages()
|
||||
/** Initialize new resource data */
|
||||
void CStringTable::InitializeNewResource()
|
||||
{
|
||||
//@todo; this should be called on all newly created string tables
|
||||
// Initialize data for whatever languages are supported by our game/region
|
||||
ERegion Region = ( Entry() && Entry()->Project() ? Entry()->Project()->Region() : ERegion::NTSC );
|
||||
const std::vector<ELanguage>& kLanguageArray = GetSupportedLanguages(Game(), Region);
|
||||
mLanguages.resize( kLanguageArray.size() );
|
||||
|
||||
for (uint i=0; i < kLanguageArray.size(); i++)
|
||||
{
|
||||
mLanguages[i].Language = kLanguageArray[i];
|
||||
mLanguages[i].Strings.resize(1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Serialize resource data */
|
||||
|
@ -344,36 +380,12 @@ TString CStringTable::StripFormatting(const TString& kInString)
|
|||
/** Static - Returns whether a given language is supported by the given game/region combination */
|
||||
bool CStringTable::IsLanguageSupported(ELanguage Language, EGame Game, ERegion Region)
|
||||
{
|
||||
// Pick the correct array to iterate based on which game/region was requested.
|
||||
const ELanguage* pkSupportedLanguages = nullptr;
|
||||
uint NumLanguages = 0;
|
||||
|
||||
if (Game <= EGame::Prime && Region == ERegion::NTSC)
|
||||
{
|
||||
return (Language == ELanguage::English);
|
||||
}
|
||||
else if (Game <= EGame::CorruptionProto)
|
||||
{
|
||||
pkSupportedLanguages = &gkSupportedLanguagesMP1PAL[0];
|
||||
NumLanguages = ARRAY_SIZE( gkSupportedLanguagesMP1PAL );
|
||||
}
|
||||
else if (Game <= EGame::Corruption)
|
||||
{
|
||||
pkSupportedLanguages = &gkSupportedLanguagesMP3[0];
|
||||
NumLanguages = ARRAY_SIZE( gkSupportedLanguagesMP3 );
|
||||
}
|
||||
else if (Game <= EGame::DKCReturns)
|
||||
{
|
||||
pkSupportedLanguages = &gkSupportedLanguagesDKCR[0];
|
||||
NumLanguages = ARRAY_SIZE( gkSupportedLanguagesDKCR );
|
||||
}
|
||||
ASSERT(pkSupportedLanguages);
|
||||
ASSERT(NumLanguages > 0);
|
||||
const std::vector<ELanguage>& kLanguageArray = GetSupportedLanguages(Game, Region);
|
||||
|
||||
// Check if the requested language is in the array.
|
||||
for (uint LanguageIdx = 0; LanguageIdx < NumLanguages; LanguageIdx++)
|
||||
for (uint LanguageIdx = 0; LanguageIdx < kLanguageArray.size(); LanguageIdx++)
|
||||
{
|
||||
if (pkSupportedLanguages[LanguageIdx] == Language)
|
||||
if (kLanguageArray[LanguageIdx] == Language)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -84,14 +84,14 @@ public:
|
|||
/** Remove a string from the table */
|
||||
void RemoveString(uint StringIndex);
|
||||
|
||||
/** Configures the string table with default languages for the game/region pairing of the resource */
|
||||
void ConfigureDefaultLanguages();
|
||||
/** Initialize new resource data */
|
||||
virtual void InitializeNewResource() override;
|
||||
|
||||
/** Serialize resource data */
|
||||
virtual void Serialize(IArchive& Arc);
|
||||
virtual void Serialize(IArchive& Arc) override;
|
||||
|
||||
/** Build the dependency tree for this resource */
|
||||
virtual CDependencyTree* BuildDependencyTree() const;
|
||||
virtual CDependencyTree* BuildDependencyTree() const override;
|
||||
|
||||
/** Static - Strip all formatting tags for a given string */
|
||||
static TString StripFormatting(const TString& kInString);
|
||||
|
|
|
@ -9,6 +9,11 @@ CTweakManager::CTweakManager(CGameProject* pInProject)
|
|||
{
|
||||
}
|
||||
|
||||
CTweakManager::~CTweakManager()
|
||||
{
|
||||
ClearTweaks();
|
||||
}
|
||||
|
||||
void CTweakManager::LoadTweaks()
|
||||
{
|
||||
ASSERT( mTweakObjects.empty() );
|
||||
|
@ -33,21 +38,6 @@ void CTweakManager::LoadTweaks()
|
|||
}
|
||||
}
|
||||
|
||||
CTweakManager::~CTweakManager()
|
||||
{
|
||||
for (CTweakData* pTweakData : mTweakObjects)
|
||||
{
|
||||
if (pTweakData->Entry() != nullptr)
|
||||
{
|
||||
pTweakData->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pTweakData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CTweakManager::SaveTweaks()
|
||||
{
|
||||
// MP1 - Save all tweak assets
|
||||
|
@ -82,3 +72,19 @@ bool CTweakManager::SaveTweaks()
|
|||
return CTweakCooker::CookNTWK(mTweakObjects, StandardNTWK);
|
||||
}
|
||||
}
|
||||
|
||||
void CTweakManager::ClearTweaks()
|
||||
{
|
||||
for (CTweakData* pTweakData : mTweakObjects)
|
||||
{
|
||||
if (pTweakData->Entry() != nullptr)
|
||||
{
|
||||
pTweakData->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pTweakData;
|
||||
}
|
||||
}
|
||||
mTweakObjects.clear();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ public:
|
|||
~CTweakManager();
|
||||
void LoadTweaks();
|
||||
bool SaveTweaks();
|
||||
void ClearTweaks();
|
||||
|
||||
// Accessors
|
||||
inline const std::vector<CTweakData*>& TweakObjects() const
|
||||
|
|
|
@ -249,6 +249,7 @@ bool CEditorApplication::RebuildResourceDatabase()
|
|||
{
|
||||
// Fake-close the project, but keep it in memory so we can modify the resource store
|
||||
CGameProject *pProj = mpActiveProject;
|
||||
mpActiveProject->TweakManager()->ClearTweaks();
|
||||
mpActiveProject = nullptr;
|
||||
emit ActiveProjectChanged(nullptr);
|
||||
|
||||
|
@ -263,6 +264,7 @@ bool CEditorApplication::RebuildResourceDatabase()
|
|||
|
||||
// Set project to active again
|
||||
mpActiveProject = pProj;
|
||||
mpActiveProject->TweakManager()->LoadTweaks();
|
||||
emit ActiveProjectChanged(pProj);
|
||||
|
||||
UICommon::InfoMsg(mpWorldEditor, "Success", "Resource database rebuilt successfully!");
|
||||
|
|
|
@ -34,7 +34,12 @@ CPropertyDelegate::CPropertyDelegate(QObject* pParent /*= 0*/)
|
|||
, mEditInProgress(false)
|
||||
, mRelaysBlocked(false)
|
||||
{
|
||||
mpEditor = UICommon::FindAncestor<IEditor>(this);
|
||||
mpEditor = UICommon::FindAncestor<IEditor>(pParent);
|
||||
}
|
||||
|
||||
void CPropertyDelegate::SetEditor(IEditor* pEditor)
|
||||
{
|
||||
mpEditor = pEditor;
|
||||
}
|
||||
|
||||
void CPropertyDelegate::SetPropertyModel(CPropertyModel* pModel)
|
||||
|
|
|
@ -83,6 +83,11 @@ bool CPropertyView::event(QEvent *pEvent)
|
|||
else return QTreeView::event(pEvent);
|
||||
}
|
||||
|
||||
void CPropertyView::SetEditor(IEditor* pEditor)
|
||||
{
|
||||
mpDelegate->SetEditor(pEditor);
|
||||
}
|
||||
|
||||
void CPropertyView::InitColumnWidths(float NameColumnPercentage, float ValueColumnPercentage)
|
||||
{
|
||||
header()->resizeSection(0, width() * NameColumnPercentage);
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
CPropertyView(QWidget* pParent = 0);
|
||||
void setModel(QAbstractItemModel* pModel);
|
||||
bool event(QEvent* pEvent);
|
||||
void SetEditor(IEditor* pEditor);
|
||||
void InitColumnWidths(float NameColumnPercentage, float ValueColumnPercentage);
|
||||
void ClearProperties();
|
||||
void SetIntrinsicProperties(CStructRef InProperties);
|
||||
|
|
|
@ -28,6 +28,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
|||
, mEditorStore(false)
|
||||
, mAssetListMode(false)
|
||||
, mSearching(false)
|
||||
, mpAddMenu(nullptr)
|
||||
, mpInspectedEntry(nullptr)
|
||||
{
|
||||
mpUI->setupUi(this);
|
||||
|
@ -150,7 +151,6 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
|||
connect(mpUI->ResourceTreeButton, SIGNAL(pressed()), this, SLOT(SetResourceTreeView()));
|
||||
connect(mpUI->ResourceListButton, SIGNAL(pressed()), this, SLOT(SetResourceListView()));
|
||||
connect(mpUI->SortComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSortModeChanged(int)));
|
||||
connect(mpUI->NewFolderButton, SIGNAL(pressed()), this, SLOT(CreateDirectory()));
|
||||
connect(mpUI->ClearButton, SIGNAL(pressed()), this, SLOT(OnClearButtonPressed()));
|
||||
|
||||
connect(mpUI->DirectoryTreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(OnDirectorySelectionChanged(QModelIndex)));
|
||||
|
@ -281,6 +281,50 @@ void CResourceBrowser::CreateFilterCheckboxes()
|
|||
mpFilterBoxesLayout->addSpacerItem(pSpacer);
|
||||
}
|
||||
|
||||
void CResourceBrowser::CreateAddMenu()
|
||||
{
|
||||
// Delete any existing menu
|
||||
if (mpAddMenu)
|
||||
{
|
||||
delete mpAddMenu;
|
||||
mpAddMenu = nullptr;
|
||||
}
|
||||
|
||||
// Create new one, only if we have a valid resource store.
|
||||
if (mpStore)
|
||||
{
|
||||
mpAddMenu = new QMenu(this);
|
||||
mpAddMenu->addAction("New Folder", this, SLOT(CreateDirectory()));
|
||||
mpAddMenu->addSeparator();
|
||||
|
||||
QMenu* pCreateMenu = new QMenu("Create...");
|
||||
mpAddMenu->addMenu(pCreateMenu);
|
||||
AddCreateAssetMenuActions(pCreateMenu);
|
||||
|
||||
mpUI->AddButton->setMenu(mpAddMenu);
|
||||
}
|
||||
|
||||
mpUI->AddButton->setEnabled( mpAddMenu != nullptr );
|
||||
}
|
||||
|
||||
void CResourceBrowser::AddCreateAssetMenuActions(QMenu* pMenu)
|
||||
{
|
||||
std::list<CResTypeInfo*> TypeInfos;
|
||||
CResTypeInfo::GetAllTypesInGame(mpStore->Game(), TypeInfos);
|
||||
|
||||
for (auto Iter = TypeInfos.begin(); Iter != TypeInfos.end(); Iter++)
|
||||
{
|
||||
CResTypeInfo* pTypeInfo = *Iter;
|
||||
|
||||
if (pTypeInfo->CanBeCreated())
|
||||
{
|
||||
QString TypeName = TO_QSTRING( pTypeInfo->TypeName() );
|
||||
QAction* pAction = pMenu->addAction(TypeName, this, SLOT(OnCreateAssetAction()));
|
||||
pAction->setProperty("TypeInfo", QVariant((int) pTypeInfo->Type()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CResourceBrowser::RenameResource(CResourceEntry *pEntry, const TString& rkNewName)
|
||||
{
|
||||
if (pEntry->Name() == rkNewName)
|
||||
|
@ -400,6 +444,49 @@ bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources,
|
|||
return true;
|
||||
}
|
||||
|
||||
CResourceEntry* CResourceBrowser::CreateNewResource(EResourceType Type)
|
||||
{
|
||||
// Create new asset ID. Sanity check to make sure the ID is unused.
|
||||
CAssetID NewAssetID;
|
||||
|
||||
while (!NewAssetID.IsValid() || mpStore->FindEntry(NewAssetID) != nullptr)
|
||||
{
|
||||
NewAssetID = CAssetID::RandomID( mpStore->Game() );
|
||||
}
|
||||
|
||||
// Boring generic default name - user will immediately be prompted to change this
|
||||
TString BaseName = TString::Format(
|
||||
"New %s", *CResTypeInfo::FindTypeInfo(Type)->TypeName()
|
||||
);
|
||||
|
||||
TString Name = BaseName;
|
||||
int Num = 0;
|
||||
|
||||
while (mpSelectedDir->FindChildResource(Name, Type) != nullptr)
|
||||
{
|
||||
Num++;
|
||||
Name = TString::Format("%s (%d)", *BaseName, Num);
|
||||
}
|
||||
|
||||
emit ResourceAboutToBeCreated(mpSelectedDir);
|
||||
|
||||
// Create the actual resource
|
||||
CResourceEntry* pEntry = mpStore->CreateNewResource(NewAssetID, Type, mpSelectedDir->FullPath(), Name);
|
||||
pEntry->Save();
|
||||
|
||||
emit ResourceCreated(pEntry);
|
||||
|
||||
// Select new resource so user can enter a name
|
||||
QModelIndex Index = mpModel->GetIndexForEntry(pEntry);
|
||||
ASSERT(Index.isValid());
|
||||
|
||||
QModelIndex ProxyIndex = mpProxyModel->mapFromSource(Index);
|
||||
mpUI->ResourceTableView->selectionModel()->select(ProxyIndex, QItemSelectionModel::ClearAndSelect);
|
||||
mpUI->ResourceTableView->edit(ProxyIndex);
|
||||
|
||||
return pEntry;
|
||||
}
|
||||
|
||||
bool CResourceBrowser::eventFilter(QObject *pWatched, QEvent *pEvent)
|
||||
{
|
||||
if (pWatched == mpUI->ResourceTableView)
|
||||
|
@ -498,6 +585,23 @@ void CResourceBrowser::OnSortModeChanged(int Index)
|
|||
mpProxyModel->SetSortMode(Mode);
|
||||
}
|
||||
|
||||
void CResourceBrowser::OnCreateAssetAction()
|
||||
{
|
||||
// Attempt to retrieve the asset type from the sender. If successful, create the asset.
|
||||
QAction* pSender = qobject_cast<QAction*>(sender());
|
||||
|
||||
if (pSender)
|
||||
{
|
||||
bool Ok;
|
||||
EResourceType Type = (EResourceType) pSender->property("TypeInfo").toInt(&Ok);
|
||||
|
||||
if (Ok)
|
||||
{
|
||||
CreateNewResource(Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CResourceBrowser::CreateDirectory()
|
||||
{
|
||||
if (mpSelectedDir)
|
||||
|
@ -685,7 +789,8 @@ void CResourceBrowser::UpdateStore()
|
|||
mpUI->SearchBar->clear();
|
||||
mSearching = false;
|
||||
|
||||
// Refresh type filter list
|
||||
// Refresh project-specific UI
|
||||
CreateAddMenu();
|
||||
CreateFilterCheckboxes();
|
||||
|
||||
// Refresh directory tree
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#include "CResourceProxyModel.h"
|
||||
#include "CResourceTableModel.h"
|
||||
#include "CVirtualDirectoryModel.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QMenu>
|
||||
#include <QTimer>
|
||||
#include <QUndoStack>
|
||||
#include <QVBoxLayout>
|
||||
|
@ -29,6 +31,9 @@ class CResourceBrowser : public QWidget
|
|||
bool mAssetListMode;
|
||||
bool mSearching;
|
||||
|
||||
// Add Menu
|
||||
QMenu *mpAddMenu;
|
||||
|
||||
// Type Filter
|
||||
QWidget *mpFilterBoxesContainerWidget;
|
||||
QVBoxLayout *mpFilterBoxesLayout;
|
||||
|
@ -59,11 +64,16 @@ public:
|
|||
void SelectResource(CResourceEntry *pEntry, bool ClearFiltersIfNecessary = false);
|
||||
void SelectDirectory(CVirtualDirectory *pDir);
|
||||
void CreateFilterCheckboxes();
|
||||
void CreateAddMenu();
|
||||
|
||||
void AddCreateAssetMenuActions(QMenu* pMenu);
|
||||
|
||||
bool RenameResource(CResourceEntry *pEntry, const TString& rkNewName);
|
||||
bool RenameDirectory(CVirtualDirectory *pDir, const TString& rkNewName);
|
||||
bool MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir);
|
||||
|
||||
CResourceEntry* CreateNewResource(EResourceType Type);
|
||||
|
||||
// Interface
|
||||
bool eventFilter(QObject *pWatched, QEvent *pEvent);
|
||||
|
||||
|
@ -82,6 +92,7 @@ public slots:
|
|||
void SetResourceListView();
|
||||
void OnClearButtonPressed();
|
||||
void OnSortModeChanged(int Index);
|
||||
void OnCreateAssetAction();
|
||||
bool CreateDirectory();
|
||||
bool DeleteDirectories(const QList<CVirtualDirectory*>& rkDirs);
|
||||
void OnSearchStringChanged(QString SearchString);
|
||||
|
@ -116,6 +127,12 @@ signals:
|
|||
void ResourceAboutToBeMoved(CResourceEntry *pRes, QString NewPath);
|
||||
void ResourceMoved(CResourceEntry *pRes, CVirtualDirectory *pOldDir, TString OldName);
|
||||
|
||||
void ResourceAboutToBeCreated(CVirtualDirectory* pInDir);
|
||||
void ResourceCreated(CResourceEntry *pRes);
|
||||
|
||||
void ResourceAboutToBeDeleted(CResourceEntry *pRes);
|
||||
void ResourceDeleted();
|
||||
|
||||
void DirectoryAboutToBeMoved(CVirtualDirectory *pDir, QString NewPath);
|
||||
void DirectoryMoved(CVirtualDirectory *pDir, CVirtualDirectory *pOldDir, TString OldName);
|
||||
|
||||
|
|
|
@ -141,9 +141,18 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="NewFolderButton">
|
||||
<widget class="QPushButton" name="AddButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> New Folder</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Icons.qrc">
|
||||
|
@ -151,8 +160,8 @@
|
|||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
|
|
|
@ -9,180 +9,252 @@ CResourceTableContextMenu::CResourceTableContextMenu(CResourceBrowser *pBrowser,
|
|||
, mpTable(pView)
|
||||
, mpModel(pModel)
|
||||
, mpProxy(pProxy)
|
||||
, mpEntry(nullptr)
|
||||
, mpDirectory(nullptr)
|
||||
, mpClickedEntry(nullptr)
|
||||
, mpClickedDirectory(nullptr)
|
||||
{
|
||||
// Connect to the view
|
||||
connect(pView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(ShowMenu(QPoint)));
|
||||
}
|
||||
|
||||
// Create actions
|
||||
void CResourceTableContextMenu::InitMenu()
|
||||
{
|
||||
// Clear existing menu items
|
||||
clear();
|
||||
|
||||
if (mpClickedEntry)
|
||||
{
|
||||
#if WIN32
|
||||
QString OpenInExplorerString = "Show in Explorer";
|
||||
const QString kOpenInExplorerString = "Show in Explorer";
|
||||
#elif __APPLE__
|
||||
QString OpenInExplorerString = "Show in Finder";
|
||||
const QString kOpenInExplorerString = "Show in Finder";
|
||||
#else
|
||||
QString OpenInExplorerString = "Show in file manager";
|
||||
const QString kOpenInExplorerString = "Show in file manager";
|
||||
#endif
|
||||
|
||||
mpOpenAction = addAction("Open", this, SLOT(Open()));
|
||||
mpOpenInExternalAppAction = addAction("Open in External Application", this, SLOT(OpenInExternalApp()));
|
||||
mpOpenInExplorerAction = addAction(OpenInExplorerString, this, SLOT(OpenInExplorer()));
|
||||
addAction("Open", this, SLOT(Open()));
|
||||
addAction("Open in External Application", this, SLOT(OpenInExternalApp()));
|
||||
addAction(kOpenInExplorerString, this, SLOT(OpenInExplorer()));
|
||||
addSeparator();
|
||||
mpRenameAction = addAction("Rename", this, SLOT(Rename()));
|
||||
mpSelectFolderAction = addAction("Select Folder", this, SLOT(SelectFolder()));
|
||||
mpShowReferencersAction = addAction("Show Referencers", this, SLOT(ShowReferencers()));
|
||||
mpShowDependenciesAction = addAction("Show Dependencies", this, SLOT(ShowDependencies()));
|
||||
mpDeleteAction = addAction("Delete", this, SLOT(Delete()));
|
||||
}
|
||||
|
||||
if (mpClickedEntry || mpClickedDirectory)
|
||||
{
|
||||
addAction("Rename", this, SLOT(Rename()));
|
||||
|
||||
if (mpModel->IsDisplayingAssetList())
|
||||
{
|
||||
addAction("Select Folder", this, SLOT(SelectFolder()));
|
||||
}
|
||||
|
||||
if (mpClickedEntry)
|
||||
{
|
||||
addAction("Show Referencers", this, SLOT(ShowReferencers()));
|
||||
addAction("Show Dependencies", this, SLOT(ShowDependencies()));
|
||||
}
|
||||
}
|
||||
|
||||
if (mpClickedEntry || mpClickedDirectory || !mSelectedIndexes.isEmpty())
|
||||
{
|
||||
addAction("Delete", this, SLOT(Delete()));
|
||||
}
|
||||
|
||||
addSeparator();
|
||||
mpCopyNameAction = addAction("Copy Name", this, SLOT(CopyName()));
|
||||
mpCopyPathAction = addAction("Copy Path", this, SLOT(CopyPath()));
|
||||
mpCopyIDAction = addAction("Copy Asset ID", this, SLOT(CopyID()));
|
||||
|
||||
if (mpClickedEntry)
|
||||
{
|
||||
addAction("Copy Name", this, SLOT(CopyName()));
|
||||
addAction("Copy Path", this, SLOT(CopyPath()));
|
||||
addAction("Copy ID", this, SLOT(CopyID()));
|
||||
addSeparator();
|
||||
}
|
||||
|
||||
QMenu* pCreate = addMenu("Create...");
|
||||
mpBrowser->AddCreateAssetMenuActions(pCreate);
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::ShowMenu(const QPoint& rkPos)
|
||||
{
|
||||
if (mpBrowser->CurrentStore() == nullptr)
|
||||
return;
|
||||
|
||||
// Fetch the entry/directory
|
||||
mProxyIndex = mpTable->indexAt(rkPos);
|
||||
mClickedProxyIndex = mpTable->indexAt(rkPos);
|
||||
|
||||
if (mProxyIndex.isValid())
|
||||
if (mClickedProxyIndex.isValid())
|
||||
{
|
||||
mIndex = mpProxy->mapToSource(mProxyIndex);
|
||||
mpEntry = mpModel->IndexEntry(mIndex);
|
||||
mpDirectory = mpModel->IndexDirectory(mIndex);
|
||||
mClickedIndex = mpProxy->mapToSource(mClickedProxyIndex);
|
||||
mpClickedEntry = mpModel->IndexEntry(mClickedIndex);
|
||||
mpClickedDirectory = mpModel->IndexDirectory(mClickedIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
mClickedIndex = QModelIndex();
|
||||
mpClickedEntry = nullptr;
|
||||
mpClickedDirectory = nullptr;
|
||||
}
|
||||
|
||||
// Show/hide menu options
|
||||
bool IsRes = (mpEntry != nullptr);
|
||||
mpOpenInExternalAppAction->setVisible(IsRes);
|
||||
mpSelectFolderAction->setVisible(mpModel->IsDisplayingAssetList());
|
||||
mpShowDependenciesAction->setVisible(IsRes);
|
||||
mpShowReferencersAction->setVisible(IsRes);
|
||||
mpDeleteAction->setVisible(mpDirectory && mpDirectory->IsEmpty(true));
|
||||
mpCopyIDAction->setVisible(IsRes);
|
||||
// Fetch the list of selected indexes
|
||||
QItemSelection Selection = mpProxy->mapSelectionToSource( mpTable->selectionModel()->selection() );
|
||||
mSelectedIndexes = Selection.indexes();
|
||||
|
||||
InitMenu();
|
||||
|
||||
// Exec menu
|
||||
QPoint GlobalPos = mpTable->viewport()->mapToGlobal(rkPos);
|
||||
exec(GlobalPos);
|
||||
}
|
||||
}
|
||||
|
||||
// Menu Options
|
||||
void CResourceTableContextMenu::Open()
|
||||
{
|
||||
if (mpEntry)
|
||||
gpEdApp->EditResource(mpEntry);
|
||||
if (mpClickedEntry)
|
||||
gpEdApp->EditResource(mpClickedEntry);
|
||||
else
|
||||
mpBrowser->SetActiveDirectory(mpDirectory);
|
||||
mpBrowser->SetActiveDirectory(mpClickedDirectory);
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::OpenInExternalApp()
|
||||
{
|
||||
ASSERT(mpEntry);
|
||||
UICommon::OpenInExternalApplication( TO_QSTRING(mpEntry->CookedAssetPath()) );
|
||||
ASSERT(mpClickedEntry);
|
||||
UICommon::OpenInExternalApplication( TO_QSTRING(mpClickedEntry->CookedAssetPath()) );
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::OpenInExplorer()
|
||||
{
|
||||
if (mpEntry)
|
||||
if (mpClickedEntry)
|
||||
{
|
||||
QString Path = TO_QSTRING( mpEntry->CookedAssetPath() );
|
||||
QString Path = TO_QSTRING( mpClickedEntry->CookedAssetPath() );
|
||||
UICommon::OpenContainingFolder(Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
TString BasePath = mpBrowser->CurrentStore()->ResourcesDir();
|
||||
QString Path = TO_QSTRING( BasePath + mpDirectory->FullPath() );
|
||||
QString Path = TO_QSTRING( BasePath + mpClickedDirectory->FullPath() );
|
||||
UICommon::OpenContainingFolder(Path);
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::Rename()
|
||||
{
|
||||
mpTable->edit(mProxyIndex);
|
||||
mpTable->edit(mClickedProxyIndex);
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::SelectFolder()
|
||||
{
|
||||
CVirtualDirectory *pDir = (mpEntry ? mpEntry->Directory() : mpDirectory->Parent());
|
||||
CVirtualDirectory *pDir = (mpClickedEntry ? mpClickedEntry->Directory() : mpClickedDirectory->Parent());
|
||||
mpBrowser->SetActiveDirectory(pDir);
|
||||
|
||||
if (mpEntry)
|
||||
mpBrowser->SelectResource(mpEntry);
|
||||
if (mpClickedEntry)
|
||||
mpBrowser->SelectResource(mpClickedEntry);
|
||||
else
|
||||
mpBrowser->SelectDirectory(mpDirectory);
|
||||
mpBrowser->SelectDirectory(mpClickedDirectory);
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::ShowReferencers()
|
||||
{
|
||||
ASSERT(mpEntry);
|
||||
ASSERT(mpClickedEntry);
|
||||
|
||||
QList<CResourceEntry*> EntryList;
|
||||
|
||||
for (CResourceIterator Iter(mpEntry->ResourceStore()); Iter; ++Iter)
|
||||
for (CResourceIterator Iter(mpClickedEntry->ResourceStore()); Iter; ++Iter)
|
||||
{
|
||||
if (Iter->Dependencies()->HasDependency(mpEntry->ID()))
|
||||
if (Iter->Dependencies()->HasDependency(mpClickedEntry->ID()))
|
||||
EntryList << *Iter;
|
||||
}
|
||||
|
||||
if (!mpModel->IsDisplayingUserEntryList())
|
||||
mpBrowser->SetInspectedEntry(mpEntry);
|
||||
mpBrowser->SetInspectedEntry(mpClickedEntry);
|
||||
|
||||
QString ListDesc = QString("Referencers of \"%1\"").arg( TO_QSTRING(mpEntry->CookedAssetPath().GetFileName()) );
|
||||
QString ListDesc = QString("Referencers of \"%1\"").arg( TO_QSTRING(mpClickedEntry->CookedAssetPath().GetFileName()) );
|
||||
mpModel->DisplayEntryList(EntryList, ListDesc);
|
||||
mpBrowser->ClearFilters();
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::ShowDependencies()
|
||||
{
|
||||
ASSERT(mpEntry);
|
||||
ASSERT(mpClickedEntry);
|
||||
|
||||
std::set<CAssetID> Dependencies;
|
||||
mpEntry->Dependencies()->GetAllResourceReferences(Dependencies);
|
||||
mpClickedEntry->Dependencies()->GetAllResourceReferences(Dependencies);
|
||||
|
||||
QList<CResourceEntry*> EntryList;
|
||||
|
||||
for (auto Iter = Dependencies.begin(); Iter != Dependencies.end(); Iter++)
|
||||
{
|
||||
CResourceEntry *pEntry = mpEntry->ResourceStore()->FindEntry(*Iter);
|
||||
CResourceEntry *pEntry = mpClickedEntry->ResourceStore()->FindEntry(*Iter);
|
||||
|
||||
if (pEntry)
|
||||
EntryList << pEntry;
|
||||
}
|
||||
|
||||
if (!mpModel->IsDisplayingUserEntryList())
|
||||
mpBrowser->SetInspectedEntry(mpEntry);
|
||||
mpBrowser->SetInspectedEntry(mpClickedEntry);
|
||||
|
||||
QString ListDesc = QString("Dependencies of \"%1\"").arg( TO_QSTRING(mpEntry->CookedAssetPath().GetFileName()) );
|
||||
QString ListDesc = QString("Dependencies of \"%1\"").arg( TO_QSTRING(mpClickedEntry->CookedAssetPath().GetFileName()) );
|
||||
mpModel->DisplayEntryList(EntryList, ListDesc);
|
||||
mpBrowser->ClearFilters();
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::Delete()
|
||||
{
|
||||
ASSERT(mpDirectory && mpDirectory->IsEmpty(true));
|
||||
// Create confirmation message
|
||||
uint NumResources = 0, NumDirectories = 0;
|
||||
|
||||
foreach (const QModelIndex& kIndex, mSelectedIndexes)
|
||||
{
|
||||
if (mpModel->IsIndexDirectory(kIndex))
|
||||
NumDirectories++;
|
||||
else
|
||||
NumResources++;
|
||||
}
|
||||
|
||||
if (NumResources == 0 && NumDirectories == 0)
|
||||
return;
|
||||
|
||||
QString ConfirmMsg = QString("Are you sure you want to permanently delete ");
|
||||
|
||||
if (NumResources > 0)
|
||||
{
|
||||
ConfirmMsg += QString("%d resource%s").arg(NumResources).arg(NumResources == 1 ? "" : "s");
|
||||
|
||||
if (NumDirectories > 0)
|
||||
{
|
||||
ConfirmMsg += " and ";
|
||||
}
|
||||
}
|
||||
if (NumDirectories > 0)
|
||||
{
|
||||
ConfirmMsg += QString("%d %s").arg(NumDirectories).arg(NumDirectories == 1 ? "directory" : "directories");
|
||||
}
|
||||
|
||||
// Allow the user to confirm the action before performing it
|
||||
if (UICommon::YesNoQuestion(mpBrowser, "Warning", ConfirmMsg))
|
||||
{
|
||||
//@todo this is wrong lol
|
||||
QList<CVirtualDirectory*> List;
|
||||
List << mpDirectory;
|
||||
List << mpClickedDirectory;
|
||||
mpBrowser->DeleteDirectories(List);
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::CopyName()
|
||||
{
|
||||
if (mpEntry)
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpEntry->Name()) );
|
||||
if (mpClickedEntry)
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedEntry->Name()) );
|
||||
else
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpDirectory->Name()) );
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedDirectory->Name()) );
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::CopyPath()
|
||||
{
|
||||
if (mpEntry)
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpEntry->CookedAssetPath(true)) );
|
||||
if (mpClickedEntry)
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedEntry->CookedAssetPath(true)) );
|
||||
else
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpDirectory->FullPath()) );
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedDirectory->FullPath()) );
|
||||
}
|
||||
|
||||
void CResourceTableContextMenu::CopyID()
|
||||
{
|
||||
ASSERT(mpEntry);
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpEntry->ID().ToString()) );
|
||||
ASSERT(mpClickedEntry);
|
||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedEntry->ID().ToString()) );
|
||||
}
|
||||
|
|
|
@ -16,30 +16,17 @@ class CResourceTableContextMenu : public QMenu
|
|||
CResourceTableModel *mpModel;
|
||||
CResourceProxyModel *mpProxy;
|
||||
|
||||
QModelIndex mProxyIndex;
|
||||
QModelIndex mIndex;
|
||||
CResourceEntry *mpEntry;
|
||||
CVirtualDirectory *mpDirectory;
|
||||
|
||||
// Actions
|
||||
QAction *mpOpenAction;
|
||||
QAction *mpOpenInExternalAppAction;
|
||||
QAction *mpOpenInExplorerAction;
|
||||
QAction *mpSelectFolderAction;
|
||||
|
||||
QAction *mpRenameAction;
|
||||
QAction *mpShowReferencersAction;
|
||||
QAction *mpShowDependenciesAction;
|
||||
QAction *mpDeleteAction;
|
||||
|
||||
QAction *mpCopyNameAction;
|
||||
QAction *mpCopyPathAction;
|
||||
QAction *mpCopyIDAction;
|
||||
QModelIndexList mSelectedIndexes;
|
||||
QModelIndex mClickedIndex;
|
||||
QModelIndex mClickedProxyIndex;
|
||||
CResourceEntry *mpClickedEntry;
|
||||
CVirtualDirectory *mpClickedDirectory;
|
||||
|
||||
public:
|
||||
CResourceTableContextMenu(CResourceBrowser *pBrowser, QTableView *pView, CResourceTableModel *pModel, CResourceProxyModel *pProxy);
|
||||
|
||||
public slots:
|
||||
void InitMenu();
|
||||
void ShowMenu(const QPoint& rkPos);
|
||||
|
||||
// Menu Options
|
||||
|
|
|
@ -7,6 +7,7 @@ CResourceTableModel::CResourceTableModel(CResourceBrowser *pBrowser, QObject *pP
|
|||
, mpCurrentDir(nullptr)
|
||||
, mIsDisplayingUserEntryList(false)
|
||||
{
|
||||
connect(pBrowser, SIGNAL(ResourceCreated(CResourceEntry*)), this, SLOT(CheckAddResource(CResourceEntry*)));
|
||||
connect(pBrowser, SIGNAL(DirectoryCreated(CVirtualDirectory*)), this, SLOT(CheckAddDirectory(CVirtualDirectory*)));
|
||||
connect(pBrowser, SIGNAL(DirectoryAboutToBeDeleted(CVirtualDirectory*)), this, SLOT(CheckRemoveDirectory(CVirtualDirectory*)));
|
||||
connect(pBrowser, SIGNAL(ResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)), this, SLOT(OnResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)));
|
||||
|
@ -155,7 +156,7 @@ Qt::DropActions CResourceTableModel::supportedDropActions() const
|
|||
// ************ FUNCTIONALITY ************
|
||||
QModelIndex CResourceTableModel::GetIndexForEntry(CResourceEntry *pEntry) const
|
||||
{
|
||||
auto Iter = qBinaryFind(mEntries, pEntry);
|
||||
auto Iter = std::find(mEntries.begin(), mEntries.end(), pEntry);
|
||||
|
||||
if (Iter == mEntries.end())
|
||||
return QModelIndex();
|
||||
|
@ -289,6 +290,32 @@ void CResourceTableModel::RefreshAllIndices()
|
|||
}
|
||||
}
|
||||
|
||||
void CResourceTableModel::CheckAddResource(CResourceEntry *pEntry)
|
||||
{
|
||||
if ( (mIsAssetListMode && pEntry->IsInDirectory(mpCurrentDir)) ||
|
||||
(!mIsAssetListMode && pEntry->Directory() == mpCurrentDir) )
|
||||
{
|
||||
// Append to the end, let the proxy handle sorting
|
||||
int NumRows = mDirectories.size() + mEntries.size();
|
||||
beginInsertRows(QModelIndex(), NumRows, NumRows);
|
||||
mEntries << pEntry;
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceTableModel::CheckRemoveResource(CResourceEntry *pEntry)
|
||||
{
|
||||
int Index = mEntries.indexOf(pEntry);
|
||||
|
||||
if (Index != -1)
|
||||
{
|
||||
Index += mDirectories.size();
|
||||
beginRemoveRows(QModelIndex(), Index, Index);
|
||||
mEntries.removeAt(Index);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceTableModel::CheckAddDirectory(CVirtualDirectory *pDir)
|
||||
{
|
||||
if (pDir->Parent() == mpCurrentDir)
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
|
||||
public slots:
|
||||
void RefreshAllIndices();
|
||||
void CheckAddResource(CResourceEntry *pEntry);
|
||||
void CheckRemoveResource(CResourceEntry *pEntry);
|
||||
void CheckAddDirectory(CVirtualDirectory *pDir);
|
||||
void CheckRemoveDirectory(CVirtualDirectory *pDir);
|
||||
void OnResourceMoved(CResourceEntry *pEntry, CVirtualDirectory *pOldDir, TString OldName);
|
||||
|
|
|
@ -14,9 +14,24 @@ CScanEditor::CScanEditor(CScan* pScan, QWidget* pParent /*= 0*/)
|
|||
QString WindowTitle = "%APP_FULL_NAME% - Scan Editor - %1[*]";
|
||||
WindowTitle = WindowTitle.arg( TO_QSTRING(mpScan->Entry()->CookedAssetPath(true).GetFileName()) );
|
||||
SET_WINDOWTITLE_APPVARS(WindowTitle);
|
||||
|
||||
connect( mpUI->ActionSave, SIGNAL(toggled(bool)), this, SLOT(Save()) );
|
||||
connect( mpUI->ActionSaveAndCook, SIGNAL(toggled(bool)), this, SLOT(SaveAndRepack()) );
|
||||
}
|
||||
|
||||
CScanEditor::~CScanEditor()
|
||||
{
|
||||
delete mpUI;
|
||||
}
|
||||
|
||||
bool CScanEditor::Save()
|
||||
{
|
||||
if (mpScan->Entry()->Save())
|
||||
{
|
||||
UndoStack().setClean();
|
||||
setWindowModified(false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ class CScanEditor : public IEditor
|
|||
public:
|
||||
explicit CScanEditor(CScan* pScan, QWidget* pParent = 0);
|
||||
~CScanEditor();
|
||||
|
||||
public slots:
|
||||
virtual bool Save() override;
|
||||
};
|
||||
|
||||
#endif // CSCANEDITOR_H
|
||||
|
|
|
@ -46,7 +46,7 @@ CStringEditor::CStringEditor(CStringTable* pStringTable, QWidget* pParent)
|
|||
, mpUI(new Ui::CStringEditor)
|
||||
, mpStringTable(pStringTable)
|
||||
, mCurrentLanguage(ELanguage::English)
|
||||
, mCurrentStringIndex(0)
|
||||
, mCurrentStringIndex(-1)
|
||||
, mCurrentStringCount(0)
|
||||
, mIsEditingStringName(false)
|
||||
, mIsEditingStringData(false)
|
||||
|
|
|
@ -17,6 +17,7 @@ WModifyTab::WModifyTab(CWorldEditor *pEditor, QWidget *pParent)
|
|||
{
|
||||
ui->setupUi(this);
|
||||
ui->PropertyView->InitColumnWidths(0.3f, 0.3f);
|
||||
ui->PropertyView->SetEditor(pEditor);
|
||||
|
||||
mpWorldEditor = pEditor;
|
||||
|
||||
|
|
Loading…
Reference in New Issue