Added support for deleting resources + minor fixes
This commit is contained in:
parent
56843e214d
commit
96c1aae27f
|
@ -1 +1 @@
|
||||||
Subproject commit 5c3bfbe57f6ef8a300933afdc053a445cabd5c7c
|
Subproject commit 3c6a40742551d7afd0737d1293d036df69f34ec6
|
|
@ -297,6 +297,15 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
|
||||||
gpResourceStore = pOldStore;
|
gpResourceStore = pOldStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create hidden files directory, if needed
|
||||||
|
TString HiddenDir = pProj->HiddenFilesDir();
|
||||||
|
|
||||||
|
if (!FileUtil::Exists(HiddenDir))
|
||||||
|
{
|
||||||
|
FileUtil::MakeDirectory(HiddenDir);
|
||||||
|
FileUtil::MarkHidden(HiddenDir, true);
|
||||||
|
}
|
||||||
|
|
||||||
pProj->mpAudioManager->LoadAssets();
|
pProj->mpAudioManager->LoadAssets();
|
||||||
pProj->mpTweakManager->LoadTweaks();
|
pProj->mpTweakManager->LoadTweaks();
|
||||||
return pProj;
|
return pProj;
|
||||||
|
|
|
@ -84,6 +84,7 @@ public:
|
||||||
// Directory Handling
|
// Directory Handling
|
||||||
inline TString ProjectRoot() const { return mProjectRoot; }
|
inline TString ProjectRoot() const { return mProjectRoot; }
|
||||||
inline TString ProjectPath() const { return mProjectRoot + FileUtil::SanitizeName(mProjectName, false) + ".prj"; }
|
inline TString ProjectPath() const { return mProjectRoot + FileUtil::SanitizeName(mProjectName, false) + ".prj"; }
|
||||||
|
inline TString HiddenFilesDir() const { return mProjectRoot + ".project/"; }
|
||||||
inline TString DiscDir(bool Relative) const { return Relative ? "Disc/" : mProjectRoot + "Disc/"; }
|
inline TString DiscDir(bool Relative) const { return Relative ? "Disc/" : mProjectRoot + "Disc/"; }
|
||||||
inline TString PackagesDir(bool Relative) const { return Relative ? "Packages/" : mProjectRoot + "Packages/"; }
|
inline TString PackagesDir(bool Relative) const { return Relative ? "Packages/" : mProjectRoot + "Packages/"; }
|
||||||
inline TString ResourcesDir(bool Relative) const { return Relative ? "Resources/" : mProjectRoot + "Resources/"; }
|
inline TString ResourcesDir(bool Relative) const { return Relative ? "Resources/" : mProjectRoot + "Resources/"; }
|
||||||
|
|
|
@ -113,6 +113,9 @@ bool CResourceEntry::LoadMetadata()
|
||||||
|
|
||||||
bool CResourceEntry::SaveMetadata(bool ForceSave /*= false*/)
|
bool CResourceEntry::SaveMetadata(bool ForceSave /*= false*/)
|
||||||
{
|
{
|
||||||
|
// Make sure we aren't saving a deleted resource
|
||||||
|
ASSERT( !HasFlag(EResEntryFlag::MarkedForDeletion) );
|
||||||
|
|
||||||
if (mMetadataDirty || ForceSave)
|
if (mMetadataDirty || ForceSave)
|
||||||
{
|
{
|
||||||
TString Path = MetadataFilePath();
|
TString Path = MetadataFilePath();
|
||||||
|
@ -491,6 +494,9 @@ bool CResourceEntry::CanMoveTo(const TString& rkDir, const TString& rkName)
|
||||||
|
|
||||||
bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName, bool IsAutoGenDir /*= false*/, bool IsAutoGenName /*= false*/)
|
bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName, bool IsAutoGenDir /*= false*/, bool IsAutoGenName /*= false*/)
|
||||||
{
|
{
|
||||||
|
// Make sure we are not moving a deleted resource.
|
||||||
|
ASSERT( !IsMarkedForDeletion() );
|
||||||
|
|
||||||
if (!CanMoveTo(rkDir, rkName)) return false;
|
if (!CanMoveTo(rkDir, rkName)) return false;
|
||||||
|
|
||||||
// Store old paths
|
// Store old paths
|
||||||
|
@ -501,7 +507,15 @@ bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName,
|
||||||
TString OldMetaPath = MetadataFilePath();
|
TString OldMetaPath = MetadataFilePath();
|
||||||
|
|
||||||
// Set new directory and name
|
// Set new directory and name
|
||||||
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, true);
|
bool DirAlreadyExisted = true;
|
||||||
|
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, false);
|
||||||
|
|
||||||
|
if (!pNewDir)
|
||||||
|
{
|
||||||
|
pNewDir = mpStore->GetVirtualDirectory(rkDir, true);
|
||||||
|
DirAlreadyExisted = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (pNewDir == mpDirectory && rkName == mName) return false;
|
if (pNewDir == mpDirectory && rkName == mName) return false;
|
||||||
|
|
||||||
// Check if we can legally move to this spot
|
// Check if we can legally move to this spot
|
||||||
|
@ -580,7 +594,7 @@ bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName,
|
||||||
// If we succeeded, finish the move
|
// If we succeeded, finish the move
|
||||||
if (FSMoveSuccess)
|
if (FSMoveSuccess)
|
||||||
{
|
{
|
||||||
if (mpDirectory != pOldDir)
|
if (mpDirectory != pOldDir && pOldDir != nullptr)
|
||||||
{
|
{
|
||||||
FSMoveSuccess = pOldDir->RemoveChildResource(this);
|
FSMoveSuccess = pOldDir->RemoveChildResource(this);
|
||||||
ASSERT(FSMoveSuccess == true); // this shouldn't be able to fail
|
ASSERT(FSMoveSuccess == true); // this shouldn't be able to fail
|
||||||
|
@ -605,7 +619,11 @@ bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName,
|
||||||
errorf("MOVE FAILED: %s", *MoveFailReason);
|
errorf("MOVE FAILED: %s", *MoveFailReason);
|
||||||
mpDirectory = pOldDir;
|
mpDirectory = pOldDir;
|
||||||
mName = OldName;
|
mName = OldName;
|
||||||
mpStore->ConditionalDeleteDirectory(pNewDir, false);
|
|
||||||
|
if (!DirAlreadyExisted)
|
||||||
|
{
|
||||||
|
mpStore->ConditionalDeleteDirectory(pNewDir, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (FileUtil::Exists(NewRawPath))
|
if (FileUtil::Exists(NewRawPath))
|
||||||
FileUtil::MoveFile(NewRawPath, OldRawPath);
|
FileUtil::MoveFile(NewRawPath, OldRawPath);
|
||||||
|
@ -627,6 +645,96 @@ bool CResourceEntry::Rename(const TString& rkName, bool IsAutoGenName /*= false*
|
||||||
return MoveAndRename(mpDirectory->FullPath(), rkName, false, IsAutoGenName);
|
return MoveAndRename(mpDirectory->FullPath(), rkName, false, IsAutoGenName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CResourceEntry::MarkDeleted(bool InDeleted)
|
||||||
|
{
|
||||||
|
// Flags resource for future deletion. "Deleted" resources remain in memory (which
|
||||||
|
// allows them to easily be un-deleted) but cannot be looked up in the resource
|
||||||
|
// store and will not save back out to the resource database. Their file data is
|
||||||
|
// stored in a temporary directory, which allows them to be moved back if the user
|
||||||
|
// un-does the deletion.
|
||||||
|
if (IsMarkedForDeletion() != InDeleted)
|
||||||
|
{
|
||||||
|
SetFlagEnabled(EResEntryFlag::MarkedForDeletion, InDeleted);
|
||||||
|
|
||||||
|
// Restore old name/directory if un-deleting
|
||||||
|
if (!InDeleted)
|
||||||
|
{
|
||||||
|
// Our directory path is stored in the Name field - see below for explanation
|
||||||
|
int NameEnd = mName.IndexOf('|');
|
||||||
|
ASSERT( NameEnd != -1 );
|
||||||
|
|
||||||
|
TString DirPath = mName.ChopFront(NameEnd + 1);
|
||||||
|
mName = mName.ChopBack( mName.Size() - NameEnd);
|
||||||
|
mpDirectory = mpStore->GetVirtualDirectory( DirPath, true );
|
||||||
|
ASSERT( mpDirectory != nullptr );
|
||||||
|
mpDirectory->AddChild("", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TString CookedPath = CookedAssetPath();
|
||||||
|
TString RawPath = RawAssetPath();
|
||||||
|
TString MetaPath = MetadataFilePath();
|
||||||
|
|
||||||
|
TString PathBase = mpStore->DeletedResourcePath() + mID.ToString() + ".";
|
||||||
|
TString DelCookedPath = PathBase + CookedExtension().ToString();
|
||||||
|
TString DelRawPath = DelCookedPath + ".rsraw";
|
||||||
|
TString DelMetaPath = DelCookedPath + ".rsmeta";
|
||||||
|
|
||||||
|
// If we are deleting...
|
||||||
|
if (InDeleted)
|
||||||
|
{
|
||||||
|
// Temporarily store our directory path in the name string.
|
||||||
|
// This is a hack, but we can't store the directory pointer because it may have been
|
||||||
|
// deleted and remade by the user by the time the resource is un-deleted, which
|
||||||
|
// means it is not safe to access later. Separating the name and the path with
|
||||||
|
// the '|' character is safe because this character is not allowed in filenames
|
||||||
|
// (which is enforced in FileUtil::IsValidName()).
|
||||||
|
mName = mName + "|" + mpDirectory->FullPath();
|
||||||
|
|
||||||
|
// Remove from parent directory.
|
||||||
|
mpDirectory->RemoveChildResource(this);
|
||||||
|
mpDirectory = nullptr;
|
||||||
|
|
||||||
|
// Move any resource files out of the project into a temporary folder.
|
||||||
|
FileUtil::MakeDirectory(DelMetaPath.GetFileDirectory());
|
||||||
|
|
||||||
|
if (FileUtil::Exists(MetaPath))
|
||||||
|
{
|
||||||
|
FileUtil::MoveFile(MetaPath, DelMetaPath);
|
||||||
|
}
|
||||||
|
if (FileUtil::Exists(RawPath))
|
||||||
|
{
|
||||||
|
FileUtil::MoveFile(RawPath, DelRawPath);
|
||||||
|
}
|
||||||
|
if (FileUtil::Exists(CookedPath))
|
||||||
|
{
|
||||||
|
FileUtil::MoveFile(CookedPath, DelCookedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we are un-deleting...
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Move any resource files out of the temporary folder back into the project.
|
||||||
|
FileUtil::MakeDirectory(MetaPath.GetFileDirectory());
|
||||||
|
|
||||||
|
if (FileUtil::Exists(DelMetaPath))
|
||||||
|
{
|
||||||
|
FileUtil::MoveFile(DelMetaPath, MetaPath);
|
||||||
|
}
|
||||||
|
if (FileUtil::Exists(DelRawPath))
|
||||||
|
{
|
||||||
|
FileUtil::MoveFile(DelRawPath, RawPath);
|
||||||
|
}
|
||||||
|
if (FileUtil::Exists(DelCookedPath))
|
||||||
|
{
|
||||||
|
FileUtil::MoveFile(DelCookedPath, CookedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mpStore->SetCacheDirty();
|
||||||
|
debugf("%s FOR DELETION: [%s] %s", InDeleted ? "MARKED" : "UNMARKED", *ID().ToString(), *CookedPath.GetFileName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CGameProject* CResourceEntry::Project() const
|
CGameProject* CResourceEntry::Project() const
|
||||||
{
|
{
|
||||||
return mpStore ? mpStore->Project() : nullptr;
|
return mpStore ? mpStore->Project() : nullptr;
|
||||||
|
|
|
@ -21,6 +21,7 @@ enum class EResEntryFlag
|
||||||
HasBeenModified = 0x00000008, // Resource has been modified and resaved by the user
|
HasBeenModified = 0x00000008, // Resource has been modified and resaved by the user
|
||||||
AutoResName = 0x00000010, // Resource name is auto-generated
|
AutoResName = 0x00000010, // Resource name is auto-generated
|
||||||
AutoResDir = 0x00000020, // Resource directory name is auto-generated
|
AutoResDir = 0x00000020, // Resource directory name is auto-generated
|
||||||
|
MarkedForDeletion = 0x00000040, // Resource has been marked for deletion by the user
|
||||||
};
|
};
|
||||||
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
|
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
|
||||||
|
|
||||||
|
@ -76,6 +77,7 @@ public:
|
||||||
bool MoveAndRename(const TString& rkDir, const TString& rkName, bool IsAutoGenDir = false, bool IsAutoGenName = false);
|
bool MoveAndRename(const TString& rkDir, const TString& rkName, bool IsAutoGenDir = false, bool IsAutoGenName = false);
|
||||||
bool Move(const TString& rkDir, bool IsAutoGenDir = false);
|
bool Move(const TString& rkDir, bool IsAutoGenDir = false);
|
||||||
bool Rename(const TString& rkName, bool IsAutoGenName = false);
|
bool Rename(const TString& rkName, bool IsAutoGenName = false);
|
||||||
|
void MarkDeleted(bool InDeleted);
|
||||||
|
|
||||||
CGameProject* Project() const;
|
CGameProject* Project() const;
|
||||||
EGame Game() const;
|
EGame Game() const;
|
||||||
|
@ -90,6 +92,7 @@ public:
|
||||||
inline void SetHidden(bool Hidden) { SetFlagEnabled(EResEntryFlag::Hidden, Hidden); }
|
inline void SetHidden(bool Hidden) { SetFlagEnabled(EResEntryFlag::Hidden, Hidden); }
|
||||||
inline bool HasFlag(EResEntryFlag Flag) const { return mFlags.HasFlag(Flag); }
|
inline bool HasFlag(EResEntryFlag Flag) const { return mFlags.HasFlag(Flag); }
|
||||||
inline bool IsHidden() const { return HasFlag(EResEntryFlag::Hidden); }
|
inline bool IsHidden() const { return HasFlag(EResEntryFlag::Hidden); }
|
||||||
|
inline bool IsMarkedForDeletion() const { return HasFlag(EResEntryFlag::MarkedForDeletion); }
|
||||||
|
|
||||||
inline bool IsLoaded() const { return mpResource != nullptr; }
|
inline bool IsLoaded() const { return mpResource != nullptr; }
|
||||||
inline bool IsCategorized() const { return mpDirectory && !mpDirectory->FullPath().CaseInsensitiveCompare( mpStore->DefaultResourceDirPath() ); }
|
inline bool IsCategorized() const { return mpDirectory && !mpDirectory->FullPath().CaseInsensitiveCompare( mpStore->DefaultResourceDirPath() ); }
|
||||||
|
|
|
@ -21,6 +21,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual CResourceEntry* Next()
|
virtual CResourceEntry* Next()
|
||||||
|
{
|
||||||
|
do
|
||||||
{
|
{
|
||||||
if (mIter != mpkStore->mResourceEntries.end())
|
if (mIter != mpkStore->mResourceEntries.end())
|
||||||
{
|
{
|
||||||
|
@ -28,6 +30,8 @@ public:
|
||||||
mIter++;
|
mIter++;
|
||||||
}
|
}
|
||||||
else mpCurEntry = nullptr;
|
else mpCurEntry = nullptr;
|
||||||
|
}
|
||||||
|
while (mpCurEntry && mpCurEntry->IsMarkedForDeletion());
|
||||||
|
|
||||||
return mpCurEntry;
|
return mpCurEntry;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,22 @@ bool CResourceStore::SerializeDatabaseCache(IArchive& rArc)
|
||||||
{
|
{
|
||||||
// Serialize resources
|
// Serialize resources
|
||||||
uint32 ResourceCount = mResourceEntries.size();
|
uint32 ResourceCount = mResourceEntries.size();
|
||||||
|
|
||||||
|
if (rArc.IsWriter())
|
||||||
|
{
|
||||||
|
// Make sure deleted resources aren't included in the count.
|
||||||
|
// We can't use CResourceIterator because it skips MarkedForDeletion resources.
|
||||||
|
for (auto Iter = mResourceEntries.begin(); Iter != mResourceEntries.end(); Iter++)
|
||||||
|
{
|
||||||
|
CResourceEntry* pEntry = Iter->second;
|
||||||
|
|
||||||
|
if (pEntry->IsMarkedForDeletion())
|
||||||
|
{
|
||||||
|
ResourceCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rArc << SerialParameter("ResourceCount", ResourceCount);
|
rArc << SerialParameter("ResourceCount", ResourceCount);
|
||||||
|
|
||||||
if (rArc.IsReader())
|
if (rArc.IsReader())
|
||||||
|
@ -84,6 +100,8 @@ bool CResourceStore::SerializeDatabaseCache(IArchive& rArc)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (CResourceIterator It(this); It; ++It)
|
for (CResourceIterator It(this); It; ++It)
|
||||||
|
{
|
||||||
|
if (!It->IsMarkedForDeletion())
|
||||||
{
|
{
|
||||||
if (rArc.ParamBegin("Resource", 0))
|
if (rArc.ParamBegin("Resource", 0))
|
||||||
{
|
{
|
||||||
|
@ -92,6 +110,7 @@ bool CResourceStore::SerializeDatabaseCache(IArchive& rArc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
rArc.ParamEnd();
|
rArc.ParamEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,18 +159,22 @@ bool CResourceStore::LoadDatabaseCache()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Database is succesfully loaded at this point
|
// Database is successfully loaded at this point
|
||||||
if (mpProj)
|
if (mpProj)
|
||||||
|
{
|
||||||
ASSERT(mpProj->Game() == Reader.Game());
|
ASSERT(mpProj->Game() == Reader.Game());
|
||||||
}
|
}
|
||||||
|
|
||||||
mGame = Reader.Game();
|
mGame = Reader.Game();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CResourceStore::SaveDatabaseCache()
|
bool CResourceStore::SaveDatabaseCache()
|
||||||
{
|
{
|
||||||
TString Path = DatabasePath();
|
TString Path = DatabasePath();
|
||||||
|
debugf("Saving database cache...");
|
||||||
|
|
||||||
CBasicBinaryWriter Writer(Path, FOURCC('CACH'), 0, mGame);
|
CBasicBinaryWriter Writer(Path, FOURCC('CACH'), 0, mGame);
|
||||||
|
|
||||||
|
@ -182,6 +205,14 @@ void CResourceStore::SetProject(CGameProject *pProj)
|
||||||
mDatabasePath = mpProj->ProjectRoot();
|
mDatabasePath = mpProj->ProjectRoot();
|
||||||
mpDatabaseRoot = new CVirtualDirectory(this);
|
mpDatabaseRoot = new CVirtualDirectory(this);
|
||||||
mGame = mpProj->Game();
|
mGame = mpProj->Game();
|
||||||
|
|
||||||
|
// Clear deleted files from previous runs
|
||||||
|
TString DeletedPath = DeletedResourcePath();
|
||||||
|
|
||||||
|
if (FileUtil::Exists(DeletedPath))
|
||||||
|
{
|
||||||
|
FileUtil::ClearDirectory(DeletedPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +246,14 @@ void CResourceStore::CloseProject()
|
||||||
It = mResourceEntries.erase(It);
|
It = mResourceEntries.erase(It);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear deleted files from previous runs
|
||||||
|
TString DeletedPath = DeletedResourcePath();
|
||||||
|
|
||||||
|
if (FileUtil::Exists(DeletedPath))
|
||||||
|
{
|
||||||
|
FileUtil::ClearDirectory(DeletedPath);
|
||||||
|
}
|
||||||
|
|
||||||
delete mpDatabaseRoot;
|
delete mpDatabaseRoot;
|
||||||
mpDatabaseRoot = nullptr;
|
mpDatabaseRoot = nullptr;
|
||||||
mpProj = nullptr;
|
mpProj = nullptr;
|
||||||
|
@ -256,12 +295,27 @@ TString CResourceStore::DefaultResourceDirPath() const
|
||||||
return StaticDefaultResourceDirPath( mGame );
|
return StaticDefaultResourceDirPath( mGame );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TString CResourceStore::DeletedResourcePath() const
|
||||||
|
{
|
||||||
|
return mpProj->HiddenFilesDir() / "delete/";
|
||||||
|
}
|
||||||
|
|
||||||
CResourceEntry* CResourceStore::FindEntry(const CAssetID& rkID) const
|
CResourceEntry* CResourceStore::FindEntry(const CAssetID& rkID) const
|
||||||
{
|
{
|
||||||
if (!rkID.IsValid()) return nullptr;
|
if (rkID.IsValid())
|
||||||
|
{
|
||||||
auto Found = mResourceEntries.find(rkID);
|
auto Found = mResourceEntries.find(rkID);
|
||||||
if (Found == mResourceEntries.end()) return nullptr;
|
|
||||||
else return Found->second;
|
if (Found != mResourceEntries.end())
|
||||||
|
{
|
||||||
|
CResourceEntry* pEntry = Found->second;
|
||||||
|
|
||||||
|
if (!pEntry->IsMarkedForDeletion())
|
||||||
|
return pEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CResourceEntry* CResourceStore::FindEntry(const TString& rkPath) const
|
CResourceEntry* CResourceStore::FindEntry(const TString& rkPath) const
|
||||||
|
@ -411,6 +465,8 @@ CResourceEntry* CResourceStore::CreateNewResource(const CAssetID& rkID, EResourc
|
||||||
{
|
{
|
||||||
TrackLoadedResource(pEntry);
|
TrackLoadedResource(pEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugf("CREATED NEW RESOURCE: [%s] %s", *rkID.ToString(), *pEntry->CookedAssetPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
void CreateVirtualDirectory(const TString& rkPath);
|
void CreateVirtualDirectory(const TString& rkPath);
|
||||||
void ConditionalDeleteDirectory(CVirtualDirectory *pDir, bool Recurse);
|
void ConditionalDeleteDirectory(CVirtualDirectory *pDir, bool Recurse);
|
||||||
TString DefaultResourceDirPath() const;
|
TString DefaultResourceDirPath() const;
|
||||||
|
TString DeletedResourcePath() const;
|
||||||
|
|
||||||
bool IsResourceRegistered(const CAssetID& rkID) const;
|
bool IsResourceRegistered(const CAssetID& rkID) const;
|
||||||
CResourceEntry* CreateNewResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName);
|
CResourceEntry* CreateNewResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName);
|
||||||
|
|
|
@ -46,6 +46,28 @@ bool CVirtualDirectory::IsDescendantOf(CVirtualDirectory *pDir) const
|
||||||
return (this == pDir) || (mpParent && pDir && (mpParent == pDir || mpParent->IsDescendantOf(pDir)));
|
return (this == pDir) || (mpParent && pDir && (mpParent == pDir || mpParent->IsDescendantOf(pDir)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CVirtualDirectory::IsSafeToDelete() const
|
||||||
|
{
|
||||||
|
// Return false if we contain any referenced assets.
|
||||||
|
for (CResourceEntry* pEntry : mResources)
|
||||||
|
{
|
||||||
|
if (pEntry->IsLoaded() && pEntry->Resource()->IsReferenced())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CVirtualDirectory* pSubdir : mSubdirectories)
|
||||||
|
{
|
||||||
|
if (!pSubdir->IsSafeToDelete())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
TString CVirtualDirectory::FullPath() const
|
TString CVirtualDirectory::FullPath() const
|
||||||
{
|
{
|
||||||
if (IsRoot())
|
if (IsRoot())
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
|
|
||||||
bool IsEmpty(bool CheckFilesystem) const;
|
bool IsEmpty(bool CheckFilesystem) const;
|
||||||
bool IsDescendantOf(CVirtualDirectory *pDir) const;
|
bool IsDescendantOf(CVirtualDirectory *pDir) const;
|
||||||
|
bool IsSafeToDelete() const;
|
||||||
TString FullPath() const;
|
TString FullPath() const;
|
||||||
TString AbsolutePath() const;
|
TString AbsolutePath() const;
|
||||||
CVirtualDirectory* GetRoot();
|
CVirtualDirectory* GetRoot();
|
||||||
|
|
|
@ -64,19 +64,6 @@ bool CTweakEditor::Save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTweakEditor::showEvent(QShowEvent* pEvent)
|
|
||||||
{
|
|
||||||
// Perform first-time UI initialization
|
|
||||||
// Property view cannot initialize correctly until first show due to window width not being configured
|
|
||||||
if (!mHasBeenShown)
|
|
||||||
{
|
|
||||||
mpUI->PropertyView->InitColumnWidths(0.6f, 0.3f);
|
|
||||||
mHasBeenShown = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
IEditor::showEvent(pEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CTweakEditor::SetActiveTweakData(CTweakData* pTweakData)
|
void CTweakEditor::SetActiveTweakData(CTweakData* pTweakData)
|
||||||
{
|
{
|
||||||
for( int TweakIdx = 0; TweakIdx < mTweakAssets.size(); TweakIdx++ )
|
for( int TweakIdx = 0; TweakIdx < mTweakAssets.size(); TweakIdx++ )
|
||||||
|
|
|
@ -29,7 +29,6 @@ public:
|
||||||
bool HasTweaks();
|
bool HasTweaks();
|
||||||
|
|
||||||
virtual bool Save() override;
|
virtual bool Save() override;
|
||||||
virtual void showEvent(QShowEvent* pEvent) override;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetActiveTweakData(CTweakData* pTweakData);
|
void SetActiveTweakData(CTweakData* pTweakData);
|
||||||
|
|
|
@ -203,7 +203,9 @@ HEADERS += \
|
||||||
Undo/CEditIntrinsicPropertyCommand.h \
|
Undo/CEditIntrinsicPropertyCommand.h \
|
||||||
Undo/TSerializeUndoCommand.h \
|
Undo/TSerializeUndoCommand.h \
|
||||||
StringEditor/CStringMimeData.h \
|
StringEditor/CStringMimeData.h \
|
||||||
ScanEditor/CScanEditor.h
|
ScanEditor/CScanEditor.h \
|
||||||
|
Undo/ICreateDeleteResourceCommand.h \
|
||||||
|
Undo/CSaveStoreCommand.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
|
|
@ -79,8 +79,20 @@ bool CPropertyView::event(QEvent *pEvent)
|
||||||
pEvent->ignore();
|
pEvent->ignore();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (pEvent->type() == QEvent::Resize && !isVisible())
|
||||||
|
{
|
||||||
|
resizeColumnToContents(0);
|
||||||
|
}
|
||||||
|
|
||||||
else return QTreeView::event(pEvent);
|
return QTreeView::event(pEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CPropertyView::sizeHintForColumn(int Column) const
|
||||||
|
{
|
||||||
|
if (Column == 0)
|
||||||
|
return width() * 0.6f;
|
||||||
|
else
|
||||||
|
return width() * 0.4f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPropertyView::SetEditor(IEditor* pEditor)
|
void CPropertyView::SetEditor(IEditor* pEditor)
|
||||||
|
@ -88,13 +100,6 @@ void CPropertyView::SetEditor(IEditor* pEditor)
|
||||||
mpDelegate->SetEditor(pEditor);
|
mpDelegate->SetEditor(pEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPropertyView::InitColumnWidths(float NameColumnPercentage, float ValueColumnPercentage)
|
|
||||||
{
|
|
||||||
header()->resizeSection(0, width() * NameColumnPercentage);
|
|
||||||
header()->resizeSection(1, width() * ValueColumnPercentage);
|
|
||||||
header()->setSectionResizeMode(1, QHeaderView::Fixed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPropertyView::ClearProperties()
|
void CPropertyView::ClearProperties()
|
||||||
{
|
{
|
||||||
mpObject = nullptr;
|
mpObject = nullptr;
|
||||||
|
|
|
@ -25,8 +25,9 @@ public:
|
||||||
CPropertyView(QWidget* pParent = 0);
|
CPropertyView(QWidget* pParent = 0);
|
||||||
void setModel(QAbstractItemModel* pModel);
|
void setModel(QAbstractItemModel* pModel);
|
||||||
bool event(QEvent* pEvent);
|
bool event(QEvent* pEvent);
|
||||||
|
int sizeHintForColumn(int Column) const;
|
||||||
|
|
||||||
void SetEditor(IEditor* pEditor);
|
void SetEditor(IEditor* pEditor);
|
||||||
void InitColumnWidths(float NameColumnPercentage, float ValueColumnPercentage);
|
|
||||||
void ClearProperties();
|
void ClearProperties();
|
||||||
void SetIntrinsicProperties(CStructRef InProperties);
|
void SetIntrinsicProperties(CStructRef InProperties);
|
||||||
void SetInstance(CScriptObject* pObj);
|
void SetInstance(CScriptObject* pObj);
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
#include "Editor/Undo/CMoveResourceCommand.h"
|
#include "Editor/Undo/CMoveResourceCommand.h"
|
||||||
#include "Editor/Undo/CRenameDirectoryCommand.h"
|
#include "Editor/Undo/CRenameDirectoryCommand.h"
|
||||||
#include "Editor/Undo/CRenameResourceCommand.h"
|
#include "Editor/Undo/CRenameResourceCommand.h"
|
||||||
|
#include "Editor/Undo/CSaveStoreCommand.h"
|
||||||
#include "Editor/Undo/ICreateDeleteDirectoryCommand.h"
|
#include "Editor/Undo/ICreateDeleteDirectoryCommand.h"
|
||||||
|
#include "Editor/Undo/ICreateDeleteResourceCommand.h"
|
||||||
#include <Core/GameProject/AssetNameGeneration.h>
|
#include <Core/GameProject/AssetNameGeneration.h>
|
||||||
#include <Core/GameProject/CAssetNameMap.h>
|
#include <Core/GameProject/CAssetNameMap.h>
|
||||||
|
|
||||||
|
@ -346,7 +348,11 @@ bool CResourceBrowser::RenameResource(CResourceEntry *pEntry, const TString& rkN
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything seems to be valid; proceed with the rename
|
// Everything seems to be valid; proceed with the rename
|
||||||
|
mUndoStack.beginMacro("Rename Resource");
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
mUndoStack.push( new CRenameResourceCommand(pEntry, rkNewName) );
|
mUndoStack.push( new CRenameResourceCommand(pEntry, rkNewName) );
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
|
mUndoStack.endMacro();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +375,11 @@ bool CResourceBrowser::RenameDirectory(CVirtualDirectory *pDir, const TString& r
|
||||||
}
|
}
|
||||||
|
|
||||||
// No conflicts, proceed with the rename
|
// No conflicts, proceed with the rename
|
||||||
|
mUndoStack.beginMacro("Rename Directory");
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
mUndoStack.push( new CRenameDirectoryCommand(pDir, rkNewName) );
|
mUndoStack.push( new CRenameDirectoryCommand(pDir, rkNewName) );
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
|
mUndoStack.endMacro();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,6 +441,7 @@ bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources,
|
||||||
if (!ValidResources.isEmpty() || !ValidDirs.isEmpty())
|
if (!ValidResources.isEmpty() || !ValidDirs.isEmpty())
|
||||||
{
|
{
|
||||||
mUndoStack.beginMacro("Move Resources");
|
mUndoStack.beginMacro("Move Resources");
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
|
|
||||||
foreach (CVirtualDirectory *pDir, ValidDirs)
|
foreach (CVirtualDirectory *pDir, ValidDirs)
|
||||||
mUndoStack.push( new CMoveDirectoryCommand(mpStore, pDir, pNewDir) );
|
mUndoStack.push( new CMoveDirectoryCommand(mpStore, pDir, pNewDir) );
|
||||||
|
@ -438,43 +449,59 @@ bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources,
|
||||||
foreach (CResourceEntry *pEntry, ValidResources)
|
foreach (CResourceEntry *pEntry, ValidResources)
|
||||||
mUndoStack.push( new CMoveResourceCommand(pEntry, pNewDir) );
|
mUndoStack.push( new CMoveResourceCommand(pEntry, pNewDir) );
|
||||||
|
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
mUndoStack.endMacro();
|
mUndoStack.endMacro();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CResourceEntry* CResourceBrowser::CreateNewResource(EResourceType Type)
|
CResourceEntry* CResourceBrowser::CreateNewResource(EResourceType Type,
|
||||||
|
TString Name /*= ""*/,
|
||||||
|
CVirtualDirectory* pDir /*= nullptr*/,
|
||||||
|
CAssetID ID /*= CAssetID()*/)
|
||||||
{
|
{
|
||||||
// Create new asset ID. Sanity check to make sure the ID is unused.
|
if (!pDir)
|
||||||
CAssetID NewAssetID;
|
|
||||||
|
|
||||||
while (!NewAssetID.IsValid() || mpStore->FindEntry(NewAssetID) != nullptr)
|
|
||||||
{
|
{
|
||||||
NewAssetID = CAssetID::RandomID( mpStore->Game() );
|
pDir = mpSelectedDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new asset ID. Sanity check to make sure the ID is unused.
|
||||||
|
while (!ID.IsValid() || mpStore->FindEntry(ID) != nullptr)
|
||||||
|
{
|
||||||
|
ID = CAssetID::RandomID( mpStore->Game() );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boring generic default name - user will immediately be prompted to change this
|
// Boring generic default name - user will immediately be prompted to change this
|
||||||
TString BaseName = TString::Format(
|
TString BaseName = Name;
|
||||||
|
|
||||||
|
if (BaseName.IsEmpty())
|
||||||
|
{
|
||||||
|
BaseName = TString::Format(
|
||||||
"New %s", *CResTypeInfo::FindTypeInfo(Type)->TypeName()
|
"New %s", *CResTypeInfo::FindTypeInfo(Type)->TypeName()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TString Name = BaseName;
|
Name = BaseName;
|
||||||
int Num = 0;
|
int Num = 0;
|
||||||
|
|
||||||
while (mpSelectedDir->FindChildResource(Name, Type) != nullptr)
|
while (pDir->FindChildResource(Name, Type) != nullptr)
|
||||||
{
|
{
|
||||||
Num++;
|
Num++;
|
||||||
Name = TString::Format("%s (%d)", *BaseName, Num);
|
Name = TString::Format("%s (%d)", *BaseName, Num);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit ResourceAboutToBeCreated(mpSelectedDir);
|
|
||||||
|
|
||||||
// Create the actual resource
|
// Create the actual resource
|
||||||
CResourceEntry* pEntry = mpStore->CreateNewResource(NewAssetID, Type, mpSelectedDir->FullPath(), Name);
|
CResourceEntry* pEntry = mpStore->CreateNewResource(ID, Type, pDir->FullPath(), Name);
|
||||||
pEntry->Save();
|
|
||||||
|
|
||||||
emit ResourceCreated(pEntry);
|
// Push undo command
|
||||||
|
mUndoStack.beginMacro("Create Resource");
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
|
mUndoStack.push( new CCreateResourceCommand(pEntry) );
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
|
mUndoStack.endMacro();
|
||||||
|
|
||||||
|
pEntry->Save();
|
||||||
|
|
||||||
// Select new resource so user can enter a name
|
// Select new resource so user can enter a name
|
||||||
QModelIndex Index = mpModel->GetIndexForEntry(pEntry);
|
QModelIndex Index = mpModel->GetIndexForEntry(pEntry);
|
||||||
|
@ -617,8 +644,12 @@ bool CResourceBrowser::CreateDirectory()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push create command to actually create the directory
|
// Push create command to actually create the directory
|
||||||
|
mUndoStack.beginMacro("Create Directory");
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
CCreateDirectoryCommand *pCmd = new CCreateDirectoryCommand(mpStore, mpSelectedDir->FullPath(), DirName);
|
CCreateDirectoryCommand *pCmd = new CCreateDirectoryCommand(mpStore, mpSelectedDir->FullPath(), DirName);
|
||||||
mUndoStack.push(pCmd);
|
mUndoStack.push(pCmd);
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
|
mUndoStack.endMacro();
|
||||||
|
|
||||||
// Now fetch the new directory and start editing it so the user can enter a name
|
// Now fetch the new directory and start editing it so the user can enter a name
|
||||||
CVirtualDirectory *pNewDir = mpSelectedDir->FindChildDirectory(DirName, false);
|
CVirtualDirectory *pNewDir = mpSelectedDir->FindChildDirectory(DirName, false);
|
||||||
|
@ -641,28 +672,101 @@ bool CResourceBrowser::CreateDirectory()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CResourceBrowser::DeleteDirectories(const QList<CVirtualDirectory*>& rkDirs)
|
bool CResourceBrowser::Delete(QVector<CResourceEntry*> Resources, QVector<CVirtualDirectory*> Directories)
|
||||||
{
|
{
|
||||||
QList<CVirtualDirectory*> DeletableDirs;
|
// Don't delete any resources/directories that are still referenced.
|
||||||
|
// This is kind of a hack but there's no good way to clear out these references right now.
|
||||||
|
QString ErrorPaths;
|
||||||
|
|
||||||
foreach (CVirtualDirectory *pDir, rkDirs)
|
for (int DirIdx = 0; DirIdx < Directories.size(); DirIdx++)
|
||||||
{
|
{
|
||||||
if (pDir && pDir->IsEmpty(true))
|
if (!Directories[DirIdx]->IsSafeToDelete())
|
||||||
DeletableDirs << pDir;
|
{
|
||||||
|
ErrorPaths += TO_QSTRING( Directories[DirIdx]->FullPath() ) + '\n';
|
||||||
|
Directories.removeAt(DirIdx);
|
||||||
|
DirIdx--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DeletableDirs.size() > 0)
|
for (int ResIdx = 0; ResIdx < Resources.size(); ResIdx++)
|
||||||
{
|
{
|
||||||
mUndoStack.beginMacro("Delete Directories");
|
if (Resources[ResIdx]->IsLoaded() && Resources[ResIdx]->Resource()->IsReferenced())
|
||||||
|
{
|
||||||
|
ErrorPaths += TO_QSTRING( Resources[ResIdx]->CookedAssetPath(true) ) + '\n';
|
||||||
|
Resources.removeAt(ResIdx);
|
||||||
|
ResIdx--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (CVirtualDirectory *pDir, DeletableDirs)
|
if (!ErrorPaths.isEmpty())
|
||||||
|
{
|
||||||
|
// Remove trailing newline
|
||||||
|
ErrorPaths.chop(1);
|
||||||
|
UICommon::ErrorMsg(this, QString("The following resources/directories are still referenced and cannot be deleted:\n\n%1")
|
||||||
|
.arg(ErrorPaths));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather a complete list of resources in subdirectories
|
||||||
|
for (int DirIdx = 0; DirIdx < Directories.size(); DirIdx++)
|
||||||
|
{
|
||||||
|
CVirtualDirectory* pDir = Directories[DirIdx];
|
||||||
|
Resources.reserve( Resources.size() + pDir->NumResources() );
|
||||||
|
Directories.reserve( Directories.size() + pDir->NumSubdirectories() );
|
||||||
|
|
||||||
|
for (uint ResourceIdx = 0; ResourceIdx < pDir->NumResources(); ResourceIdx++)
|
||||||
|
Resources << pDir->ResourceByIndex(ResourceIdx);
|
||||||
|
|
||||||
|
for (uint SubdirIdx = 0; SubdirIdx < pDir->NumSubdirectories(); SubdirIdx++)
|
||||||
|
Directories << pDir->SubdirectoryByIndex(SubdirIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit if we have nothing to do.
|
||||||
|
if (Resources.isEmpty() && Directories.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Allow the user to confirm before proceeding.
|
||||||
|
QString ConfirmMsg = QString("Are you sure you want to permanently delete ");
|
||||||
|
|
||||||
|
if (Resources.size() > 0)
|
||||||
|
{
|
||||||
|
ConfirmMsg += QString("%1 resource%2").arg(Resources.size()).arg(Resources.size() == 1 ? "" : "s");
|
||||||
|
|
||||||
|
if (Directories.size() > 0)
|
||||||
|
{
|
||||||
|
ConfirmMsg += " and ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Directories.size() > 0)
|
||||||
|
{
|
||||||
|
ConfirmMsg += QString("%1 %2").arg(Directories.size()).arg(Directories.size() == 1 ? "directory" : "directories");
|
||||||
|
}
|
||||||
|
ConfirmMsg += "?";
|
||||||
|
|
||||||
|
if (UICommon::YesNoQuestion(this, "Warning", ConfirmMsg))
|
||||||
|
{
|
||||||
|
// Note that the undo stack will undo actions in the reverse order they are pushed
|
||||||
|
// So we need to push commands last that we want to be undone first
|
||||||
|
// We want to delete subdirectories first, then parent directories, then resources
|
||||||
|
mUndoStack.beginMacro("Delete");
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
|
|
||||||
|
// Delete resources first.
|
||||||
|
foreach (CResourceEntry* pEntry, Resources)
|
||||||
|
mUndoStack.push( new CDeleteResourceCommand(pEntry) );
|
||||||
|
|
||||||
|
// Now delete directories in reverse order (so subdirectories delete first)
|
||||||
|
for (int DirIdx = Directories.size()-1; DirIdx >= 0; DirIdx--)
|
||||||
|
{
|
||||||
|
CVirtualDirectory* pDir = Directories[DirIdx];
|
||||||
mUndoStack.push( new CDeleteDirectoryCommand(mpStore, pDir->Parent()->FullPath(), pDir->Name()) );
|
mUndoStack.push( new CDeleteDirectoryCommand(mpStore, pDir->Parent()->FullPath(), pDir->Name()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
mUndoStack.push( new CSaveStoreCommand(mpStore) );
|
||||||
mUndoStack.endMacro();
|
mUndoStack.endMacro();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
else return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceBrowser::OnSearchStringChanged(QString SearchString)
|
void CResourceBrowser::OnSearchStringChanged(QString SearchString)
|
||||||
|
|
|
@ -72,7 +72,10 @@ public:
|
||||||
bool RenameDirectory(CVirtualDirectory *pDir, const TString& rkNewName);
|
bool RenameDirectory(CVirtualDirectory *pDir, const TString& rkNewName);
|
||||||
bool MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir);
|
bool MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir);
|
||||||
|
|
||||||
CResourceEntry* CreateNewResource(EResourceType Type);
|
CResourceEntry* CreateNewResource(EResourceType Type,
|
||||||
|
TString Name = "",
|
||||||
|
CVirtualDirectory* pDir = nullptr,
|
||||||
|
CAssetID ID = CAssetID());
|
||||||
|
|
||||||
// Interface
|
// Interface
|
||||||
bool eventFilter(QObject *pWatched, QEvent *pEvent);
|
bool eventFilter(QObject *pWatched, QEvent *pEvent);
|
||||||
|
@ -94,7 +97,7 @@ public slots:
|
||||||
void OnSortModeChanged(int Index);
|
void OnSortModeChanged(int Index);
|
||||||
void OnCreateAssetAction();
|
void OnCreateAssetAction();
|
||||||
bool CreateDirectory();
|
bool CreateDirectory();
|
||||||
bool DeleteDirectories(const QList<CVirtualDirectory*>& rkDirs);
|
bool Delete(QVector<CResourceEntry*> Resources, QVector<CVirtualDirectory*> Directories);
|
||||||
void OnSearchStringChanged(QString SearchString);
|
void OnSearchStringChanged(QString SearchString);
|
||||||
void OnDirectorySelectionChanged(const QModelIndex& rkNewIndex);
|
void OnDirectorySelectionChanged(const QModelIndex& rkNewIndex);
|
||||||
void OnDoubleClickTable(QModelIndex Index);
|
void OnDoubleClickTable(QModelIndex Index);
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#include "CResourceTableContextMenu.h"
|
#include "CResourceTableContextMenu.h"
|
||||||
#include "CResourceBrowser.h"
|
#include "CResourceBrowser.h"
|
||||||
#include "Editor/CEditorApplication.h"
|
#include "Editor/CEditorApplication.h"
|
||||||
|
|
||||||
|
#include <Core/Resource/Scan/CScan.h>
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
|
||||||
CResourceTableContextMenu::CResourceTableContextMenu(CResourceBrowser *pBrowser, QTableView *pView, CResourceTableModel *pModel, CResourceProxyModel *pProxy)
|
CResourceTableContextMenu::CResourceTableContextMenu(CResourceBrowser *pBrowser, QTableView *pView, CResourceTableModel *pModel, CResourceProxyModel *pProxy)
|
||||||
|
@ -70,6 +73,17 @@ void CResourceTableContextMenu::InitMenu()
|
||||||
|
|
||||||
QMenu* pCreate = addMenu("Create...");
|
QMenu* pCreate = addMenu("Create...");
|
||||||
mpBrowser->AddCreateAssetMenuActions(pCreate);
|
mpBrowser->AddCreateAssetMenuActions(pCreate);
|
||||||
|
|
||||||
|
// Asset-specific
|
||||||
|
if (mpClickedEntry)
|
||||||
|
{
|
||||||
|
switch (mpClickedEntry->ResourceType())
|
||||||
|
{
|
||||||
|
case EResourceType::StringTable:
|
||||||
|
addAction("Create Scan", this, SLOT(CreateSCAN()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceTableContextMenu::ShowMenu(const QPoint& rkPos)
|
void CResourceTableContextMenu::ShowMenu(const QPoint& rkPos)
|
||||||
|
@ -198,43 +212,18 @@ void CResourceTableContextMenu::ShowDependencies()
|
||||||
void CResourceTableContextMenu::Delete()
|
void CResourceTableContextMenu::Delete()
|
||||||
{
|
{
|
||||||
// Create confirmation message
|
// Create confirmation message
|
||||||
uint NumResources = 0, NumDirectories = 0;
|
QVector<CResourceEntry*> Resources;
|
||||||
|
QVector<CVirtualDirectory*> Directories;
|
||||||
|
|
||||||
foreach (const QModelIndex& kIndex, mSelectedIndexes)
|
foreach (const QModelIndex& kIndex, mSelectedIndexes)
|
||||||
{
|
{
|
||||||
if (mpModel->IsIndexDirectory(kIndex))
|
if (mpModel->IsIndexDirectory(kIndex))
|
||||||
NumDirectories++;
|
Directories << mpModel->IndexDirectory(kIndex);
|
||||||
else
|
else
|
||||||
NumResources++;
|
Resources << mpModel->IndexEntry(kIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NumResources == 0 && NumDirectories == 0)
|
mpBrowser->Delete(Resources, Directories);
|
||||||
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 << mpClickedDirectory;
|
|
||||||
mpBrowser->DeleteDirectories(List);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceTableContextMenu::CopyName()
|
void CResourceTableContextMenu::CopyName()
|
||||||
|
@ -258,3 +247,22 @@ void CResourceTableContextMenu::CopyID()
|
||||||
ASSERT(mpClickedEntry);
|
ASSERT(mpClickedEntry);
|
||||||
gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedEntry->ID().ToString()) );
|
gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedEntry->ID().ToString()) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Asset Specific
|
||||||
|
void CResourceTableContextMenu::CreateSCAN()
|
||||||
|
{
|
||||||
|
// Create a SCAN asset to go along with a selected STRG asset
|
||||||
|
ASSERT( mpClickedEntry && mpClickedEntry->ResourceType() == EResourceType::StringTable );
|
||||||
|
|
||||||
|
CResourceEntry* pNewEntry = mpBrowser->CreateNewResource(EResourceType::Scan,
|
||||||
|
mpClickedEntry->Name(),
|
||||||
|
mpClickedEntry->Directory());
|
||||||
|
|
||||||
|
if (pNewEntry)
|
||||||
|
{
|
||||||
|
CScan* pScan = (CScan*) pNewEntry->Load();
|
||||||
|
pScan->ScanStringPropertyRef().Set( mpClickedEntry->ID() );
|
||||||
|
pNewEntry->Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -41,6 +41,9 @@ public slots:
|
||||||
void CopyName();
|
void CopyName();
|
||||||
void CopyPath();
|
void CopyPath();
|
||||||
void CopyID();
|
void CopyID();
|
||||||
|
|
||||||
|
// Asset Specific
|
||||||
|
void CreateSCAN();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CRESOURCETABLECONTEXTMENU_H
|
#endif // CRESOURCETABLECONTEXTMENU_H
|
||||||
|
|
|
@ -8,6 +8,7 @@ CResourceTableModel::CResourceTableModel(CResourceBrowser *pBrowser, QObject *pP
|
||||||
, mIsDisplayingUserEntryList(false)
|
, mIsDisplayingUserEntryList(false)
|
||||||
{
|
{
|
||||||
connect(pBrowser, SIGNAL(ResourceCreated(CResourceEntry*)), this, SLOT(CheckAddResource(CResourceEntry*)));
|
connect(pBrowser, SIGNAL(ResourceCreated(CResourceEntry*)), this, SLOT(CheckAddResource(CResourceEntry*)));
|
||||||
|
connect(pBrowser, SIGNAL(ResourceAboutToBeDeleted(CResourceEntry*)), this, SLOT(CheckRemoveResource(CResourceEntry*)));
|
||||||
connect(pBrowser, SIGNAL(DirectoryCreated(CVirtualDirectory*)), this, SLOT(CheckAddDirectory(CVirtualDirectory*)));
|
connect(pBrowser, SIGNAL(DirectoryCreated(CVirtualDirectory*)), this, SLOT(CheckAddDirectory(CVirtualDirectory*)));
|
||||||
connect(pBrowser, SIGNAL(DirectoryAboutToBeDeleted(CVirtualDirectory*)), this, SLOT(CheckRemoveDirectory(CVirtualDirectory*)));
|
connect(pBrowser, SIGNAL(DirectoryAboutToBeDeleted(CVirtualDirectory*)), this, SLOT(CheckRemoveDirectory(CVirtualDirectory*)));
|
||||||
connect(pBrowser, SIGNAL(ResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)), this, SLOT(OnResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)));
|
connect(pBrowser, SIGNAL(ResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)), this, SLOT(OnResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)));
|
||||||
|
@ -309,8 +310,8 @@ void CResourceTableModel::CheckRemoveResource(CResourceEntry *pEntry)
|
||||||
|
|
||||||
if (Index != -1)
|
if (Index != -1)
|
||||||
{
|
{
|
||||||
Index += mDirectories.size();
|
int RowIndex = Index + mDirectories.size();
|
||||||
beginRemoveRows(QModelIndex(), Index, Index);
|
beginRemoveRows(QModelIndex(), RowIndex, RowIndex);
|
||||||
mEntries.removeAt(Index);
|
mEntries.removeAt(Index);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,34 +44,20 @@ void CResourceTableView::DeleteSelected()
|
||||||
// Figure out which indices can actually be deleted
|
// Figure out which indices can actually be deleted
|
||||||
CResourceProxyModel *pProxy = static_cast<CResourceProxyModel*>(model());
|
CResourceProxyModel *pProxy = static_cast<CResourceProxyModel*>(model());
|
||||||
CResourceTableModel *pModel = static_cast<CResourceTableModel*>(pProxy->sourceModel());
|
CResourceTableModel *pModel = static_cast<CResourceTableModel*>(pProxy->sourceModel());
|
||||||
QList<CVirtualDirectory*> DirsToDelete;
|
QVector<CResourceEntry*> ResourcesToDelete;
|
||||||
bool HasNonEmptyDirSelected = false;
|
QVector<CVirtualDirectory*> DirsToDelete;
|
||||||
|
|
||||||
foreach (QModelIndex Index, List)
|
foreach (QModelIndex Index, List)
|
||||||
{
|
{
|
||||||
QModelIndex SourceIndex = pProxy->mapToSource(Index);
|
QModelIndex SourceIndex = pProxy->mapToSource(Index);
|
||||||
|
|
||||||
if (pModel->IsIndexDirectory(SourceIndex))
|
if (pModel->IsIndexDirectory(SourceIndex))
|
||||||
{
|
DirsToDelete << pModel->IndexDirectory(SourceIndex);
|
||||||
CVirtualDirectory *pDir = pModel->IndexDirectory(SourceIndex);
|
|
||||||
|
|
||||||
if (pDir)
|
|
||||||
{
|
|
||||||
if (pDir->IsEmpty(true))
|
|
||||||
DirsToDelete << pDir;
|
|
||||||
else
|
else
|
||||||
HasNonEmptyDirSelected = true;
|
ResourcesToDelete << pModel->IndexEntry(SourceIndex);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the user know if all selected directories are non empty
|
|
||||||
if (HasNonEmptyDirSelected && DirsToDelete.isEmpty())
|
|
||||||
{
|
|
||||||
UICommon::ErrorMsg(parentWidget(), "Unable to delete; one or more of the selected directories is non-empty.");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
gpEdApp->ResourceBrowser()->DeleteDirectories(DirsToDelete);
|
gpEdApp->ResourceBrowser()->Delete(ResourcesToDelete, DirsToDelete);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef CSAVESTORECOMMAND_H
|
||||||
|
#define CSAVESTORECOMMAND_H
|
||||||
|
|
||||||
|
#include "IUndoCommand.h"
|
||||||
|
#include <Core/GameProject/CResourceStore.h>
|
||||||
|
|
||||||
|
/** Command that calls ConditionalSaveStore on a resource store.
|
||||||
|
* This is meant to be added to undo macros that modify the resource store
|
||||||
|
* in order to trigger the store to resave when the macro is complete.
|
||||||
|
*/
|
||||||
|
class CSaveStoreCommand : public IUndoCommand
|
||||||
|
{
|
||||||
|
CResourceStore* mpStore;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CSaveStoreCommand(CResourceStore* pInStore)
|
||||||
|
: IUndoCommand("Save Store")
|
||||||
|
, mpStore(pInStore)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void undo() override { mpStore->ConditionalSaveStore(); }
|
||||||
|
virtual void redo() override { mpStore->ConditionalSaveStore(); }
|
||||||
|
virtual bool AffectsCleanState() const override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CSAVESTORECOMMAND_H
|
|
@ -16,8 +16,8 @@ protected:
|
||||||
CVirtualDirectory *mpDir;
|
CVirtualDirectory *mpDir;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ICreateDeleteDirectoryCommand(CResourceStore *pStore, TString ParentPath, TString DirName)
|
ICreateDeleteDirectoryCommand(const QString& rkText, CResourceStore *pStore, TString ParentPath, TString DirName)
|
||||||
: IUndoCommand("Create Directory")
|
: IUndoCommand(rkText)
|
||||||
, mpStore(pStore)
|
, mpStore(pStore)
|
||||||
, mParentPath(ParentPath)
|
, mParentPath(ParentPath)
|
||||||
, mDirName(DirName)
|
, mDirName(DirName)
|
||||||
|
@ -64,7 +64,7 @@ class CCreateDirectoryCommand : public ICreateDeleteDirectoryCommand
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CCreateDirectoryCommand(CResourceStore *pStore, TString ParentPath, TString DirName)
|
CCreateDirectoryCommand(CResourceStore *pStore, TString ParentPath, TString DirName)
|
||||||
: ICreateDeleteDirectoryCommand(pStore, ParentPath, DirName)
|
: ICreateDeleteDirectoryCommand("Create Directory", pStore, ParentPath, DirName)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void undo() { DoDelete(); }
|
void undo() { DoDelete(); }
|
||||||
|
@ -75,7 +75,7 @@ class CDeleteDirectoryCommand : public ICreateDeleteDirectoryCommand
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CDeleteDirectoryCommand(CResourceStore *pStore, TString ParentPath, TString DirName)
|
CDeleteDirectoryCommand(CResourceStore *pStore, TString ParentPath, TString DirName)
|
||||||
: ICreateDeleteDirectoryCommand(pStore, ParentPath, DirName)
|
: ICreateDeleteDirectoryCommand("Delete Directory", pStore, ParentPath, DirName)
|
||||||
{
|
{
|
||||||
mpDir = pStore->GetVirtualDirectory(ParentPath + DirName, false);
|
mpDir = pStore->GetVirtualDirectory(ParentPath + DirName, false);
|
||||||
ASSERT(mpDir);
|
ASSERT(mpDir);
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
#ifndef ICREATEDELETERESOURCECOMMAND_H
|
||||||
|
#define ICREATEDELETERESOURCECOMMAND_H
|
||||||
|
|
||||||
|
#include "IUndoCommand.h"
|
||||||
|
#include "Editor/CEditorApplication.h"
|
||||||
|
#include "Editor/ResourceBrowser/CResourceBrowser.h"
|
||||||
|
#include <Core/GameProject/CResourceEntry.h>
|
||||||
|
#include <Core/GameProject/CResourceStore.h>
|
||||||
|
|
||||||
|
class ICreateDeleteResourceCommand : public IUndoCommand
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
CResourceEntry* mpEntry;
|
||||||
|
TString mDirPath;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ICreateDeleteResourceCommand(const QString& kText, CResourceEntry* pEntry)
|
||||||
|
: IUndoCommand(kText)
|
||||||
|
, mpEntry(pEntry)
|
||||||
|
{
|
||||||
|
mDirPath = mpEntry->Directory()->FullPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoCreate()
|
||||||
|
{
|
||||||
|
CVirtualDirectory* pDir = mpEntry->ResourceStore()->GetVirtualDirectory(mDirPath, true);
|
||||||
|
gpEdApp->ResourceBrowser()->ResourceAboutToBeCreated(pDir);
|
||||||
|
|
||||||
|
// restore directory and undelete
|
||||||
|
mpEntry->MarkDeleted(false);
|
||||||
|
|
||||||
|
gpEdApp->ResourceBrowser()->ResourceCreated(mpEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoDelete()
|
||||||
|
{
|
||||||
|
gpEdApp->ResourceBrowser()->ResourceAboutToBeDeleted(mpEntry);
|
||||||
|
|
||||||
|
// save directory and delete
|
||||||
|
mpEntry->MarkDeleted(true);
|
||||||
|
|
||||||
|
gpEdApp->ResourceBrowser()->ResourceDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AffectsCleanState() const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class CCreateResourceCommand : public ICreateDeleteResourceCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CCreateResourceCommand(CResourceEntry* pEntry)
|
||||||
|
: ICreateDeleteResourceCommand("Create Resource", pEntry)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void undo() { DoDelete(); }
|
||||||
|
void redo() { DoCreate(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class CDeleteResourceCommand : public ICreateDeleteResourceCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CDeleteResourceCommand(CResourceEntry* pEntry)
|
||||||
|
: ICreateDeleteResourceCommand("Delete Resource", pEntry)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void undo() { DoCreate(); }
|
||||||
|
void redo() { DoDelete(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ICREATEDELETERESOURCECOMMAND_H
|
|
@ -16,7 +16,6 @@ WModifyTab::WModifyTab(CWorldEditor *pEditor, QWidget *pParent)
|
||||||
, mIsPicking(false)
|
, mIsPicking(false)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->PropertyView->InitColumnWidths(0.3f, 0.3f);
|
|
||||||
ui->PropertyView->SetEditor(pEditor);
|
ui->PropertyView->SetEditor(pEditor);
|
||||||
|
|
||||||
mpWorldEditor = pEditor;
|
mpWorldEditor = pEditor;
|
||||||
|
|
|
@ -36795,7 +36795,7 @@
|
||||||
</Element>
|
</Element>
|
||||||
<Element>
|
<Element>
|
||||||
<Key ID="0xC915686F" Type="float"/>
|
<Key ID="0xC915686F" Type="float"/>
|
||||||
<Value Name="Unknown"/>
|
<Value Name="DeathBallDamageDelay"/>
|
||||||
</Element>
|
</Element>
|
||||||
<Element>
|
<Element>
|
||||||
<Key ID="0xC91B0946" Type="int"/>
|
<Key ID="0xC91B0946" Type="int"/>
|
||||||
|
|
Loading…
Reference in New Issue