Added support for deleting resources + minor fixes

This commit is contained in:
Aruki 2019-02-03 02:22:36 -07:00
parent 56843e214d
commit 96c1aae27f
26 changed files with 524 additions and 125 deletions

2
externals/LibCommon vendored

@ -1 +1 @@
Subproject commit 5c3bfbe57f6ef8a300933afdc053a445cabd5c7c Subproject commit 3c6a40742551d7afd0737d1293d036df69f34ec6

View File

@ -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;

View File

@ -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/"; }

View File

@ -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;

View File

@ -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() ); }

View File

@ -22,12 +22,16 @@ public:
virtual CResourceEntry* Next() virtual CResourceEntry* Next()
{ {
if (mIter != mpkStore->mResourceEntries.end()) do
{ {
mpCurEntry = mIter->second; if (mIter != mpkStore->mResourceEntries.end())
mIter++; {
mpCurEntry = mIter->second;
mIter++;
}
else mpCurEntry = nullptr;
} }
else mpCurEntry = nullptr; while (mpCurEntry && mpCurEntry->IsMarkedForDeletion());
return mpCurEntry; return mpCurEntry;
} }

View File

@ -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())
@ -85,10 +101,13 @@ bool CResourceStore::SerializeDatabaseCache(IArchive& rArc)
{ {
for (CResourceIterator It(this); It; ++It) for (CResourceIterator It(this); It; ++It)
{ {
if (rArc.ParamBegin("Resource", 0)) if (!It->IsMarkedForDeletion())
{ {
It->SerializeEntryInfo(rArc, false); if (rArc.ParamBegin("Resource", 0))
rArc.ParamEnd(); {
It->SerializeEntryInfo(rArc, false);
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); {
if (Found == mResourceEntries.end()) return nullptr; auto Found = mResourceEntries.find(rkID);
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

View File

@ -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);

View File

@ -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())

View File

@ -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();

View File

@ -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++ )

View File

@ -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);

View File

@ -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 += \

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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

View File

@ -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();
} }

View File

@ -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); else
ResourcesToDelete << pModel->IndexEntry(SourceIndex);
if (pDir)
{
if (pDir->IsEmpty(true))
DirsToDelete << pDir;
else
HasNonEmptyDirSelected = true;
}
}
}
// 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);
} }

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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"/>