Added support for dragging/dropping resources; you can use drag/drop to rearrange resources/folders in the resource browser now, and you can drag/drop resources onto resource selector widgets
This commit is contained in:
parent
fe9a074029
commit
dbe8b7922c
|
@ -106,10 +106,15 @@ bool MoveFile(const TString& rkOldPath, const TString& rkNewPath)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (CopyFile(rkOldPath, rkNewPath))
|
||||
return DeleteFile(rkOldPath);
|
||||
else
|
||||
if (Exists(rkNewPath))
|
||||
{
|
||||
Log::Error("Unable to move file because there is an existing file at the destination path: " + rkNewPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// todo: check return value? Docs don't say what the return value actually is
|
||||
rename(*rkOldPath, *rkNewPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoveDirectory(const TString& rkOldPath, const TString& rkNewPath)
|
||||
|
@ -120,10 +125,15 @@ bool MoveDirectory(const TString& rkOldPath, const TString& rkNewPath)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (CopyDirectory(rkOldPath, rkNewPath))
|
||||
return DeleteDirectory(rkOldPath, false);
|
||||
else
|
||||
if (Exists(rkNewPath))
|
||||
{
|
||||
Log::Error("Unable to move directory because there is an existing directory at the destination path: " + rkNewPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// todo: check return value? Docs don't say what the return value actually is
|
||||
rename(*rkOldPath, *rkNewPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeleteFile(const TString& rkFilePath)
|
||||
|
|
|
@ -79,7 +79,7 @@ void ApplyGeneratedName(CResourceEntry *pEntry, const TString& rkDir, const TStr
|
|||
if (pEntry->Directory() == pNewDir && pEntry->Name() == NewName) return;
|
||||
|
||||
// Perform the move
|
||||
bool Success = pEntry->Move(pNewDir->FullPath(), NewName, true, true);
|
||||
bool Success = pEntry->MoveAndRename(pNewDir->FullPath(), NewName, true, true);
|
||||
ASSERT(Success);
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ void GenerateAssetNames(CGameProject *pProj)
|
|||
|
||||
TString NewDir = (HasCustomDir ? It->DirectoryPath() : pStore->DefaultResourceDirPath());
|
||||
TString NewName = (HasCustomName ? It->Name() : It->ID().ToString());
|
||||
It->Move(NewDir, NewName, true, true);
|
||||
It->MoveAndRename(NewDir, NewName, true, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -656,7 +656,7 @@ void GenerateAssetNames(CGameProject *pProj)
|
|||
}
|
||||
#endif
|
||||
|
||||
pStore->RootDirectory()->RemoveEmptySubdirectories();
|
||||
pStore->RootDirectory()->DeleteEmptySubdirectories();
|
||||
pStore->ConditionalSaveStore();
|
||||
Log::Write("*** Asset Name Generation FINISHED ***");
|
||||
}
|
||||
|
|
|
@ -476,7 +476,7 @@ bool CResourceEntry::CanMoveTo(const TString& rkDir, const TString& rkName)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CResourceEntry::Move(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*/)
|
||||
{
|
||||
if (!CanMoveTo(rkDir, rkName)) return false;
|
||||
|
||||
|
@ -516,7 +516,7 @@ bool CResourceEntry::Move(const TString& rkDir, const TString& rkName, bool IsAu
|
|||
// Move raw file to new location
|
||||
if (FileUtil::Exists(OldRawPath))
|
||||
{
|
||||
FSMoveSuccess = FileUtil::CopyFile(OldRawPath, NewRawPath);
|
||||
FSMoveSuccess = FileUtil::MoveFile(OldRawPath, NewRawPath);
|
||||
|
||||
if (!FSMoveSuccess)
|
||||
MoveFailReason = TString::Format("Failed to move raw file to new destination (%s --> %s)", *OldRawPath, *NewRawPath);
|
||||
|
@ -525,11 +525,10 @@ bool CResourceEntry::Move(const TString& rkDir, const TString& rkName, bool IsAu
|
|||
// Move cooked file to new location
|
||||
if (FSMoveSuccess && FileUtil::Exists(OldCookedPath))
|
||||
{
|
||||
FSMoveSuccess = FileUtil::CopyFile(OldCookedPath, NewCookedPath);
|
||||
FSMoveSuccess = FileUtil::MoveFile(OldCookedPath, NewCookedPath);
|
||||
|
||||
if (!FSMoveSuccess)
|
||||
{
|
||||
FileUtil::DeleteFile(NewRawPath);
|
||||
MoveFailReason = TString::Format("Failed to move cooked file to new destination (%s --> %s)", *OldCookedPath, *NewCookedPath);
|
||||
}
|
||||
}
|
||||
|
@ -539,12 +538,10 @@ bool CResourceEntry::Move(const TString& rkDir, const TString& rkName, bool IsAu
|
|||
{
|
||||
if (FileUtil::Exists(OldMetaPath))
|
||||
{
|
||||
FSMoveSuccess = FileUtil::CopyFile(OldMetaPath, NewMetaPath);
|
||||
FSMoveSuccess = FileUtil::MoveFile(OldMetaPath, NewMetaPath);
|
||||
|
||||
if (!FSMoveSuccess)
|
||||
{
|
||||
FileUtil::DeleteFile(NewRawPath);
|
||||
FileUtil::DeleteFile(NewCookedPath);
|
||||
MoveFailReason = TString::Format("Failed to move metadata file to new destination (%s --> %s)", *OldMetaPath, *NewMetaPath);
|
||||
}
|
||||
}
|
||||
|
@ -582,10 +579,6 @@ bool CResourceEntry::Move(const TString& rkDir, const TString& rkName, bool IsAu
|
|||
|
||||
mpStore->SetCacheDirty();
|
||||
mCachedUppercaseName = rkName.ToUpper();
|
||||
FileUtil::DeleteFile(OldRawPath);
|
||||
FileUtil::DeleteFile(OldCookedPath);
|
||||
FileUtil::DeleteFile(OldMetaPath);
|
||||
|
||||
SaveMetadata();
|
||||
return true;
|
||||
}
|
||||
|
@ -597,10 +590,27 @@ bool CResourceEntry::Move(const TString& rkDir, const TString& rkName, bool IsAu
|
|||
mpDirectory = pOldDir;
|
||||
mName = OldName;
|
||||
mpStore->ConditionalDeleteDirectory(pNewDir, false);
|
||||
|
||||
if (FileUtil::Exists(NewRawPath))
|
||||
FileUtil::MoveFile(NewRawPath, OldRawPath);
|
||||
|
||||
if (FileUtil::Exists(NewCookedPath))
|
||||
FileUtil::MoveFile(NewCookedPath, OldCookedPath);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CResourceEntry::Move(const TString& rkDir, bool IsAutoGenDir /*= false*/)
|
||||
{
|
||||
return MoveAndRename(rkDir, mName, IsAutoGenDir, false);
|
||||
}
|
||||
|
||||
bool CResourceEntry::Rename(const TString& rkName, bool IsAutoGenName /*= false*/)
|
||||
{
|
||||
return MoveAndRename(mpDirectory->FullPath(), rkName, false, IsAutoGenName);
|
||||
}
|
||||
|
||||
CGameProject* CResourceEntry::Project() const
|
||||
{
|
||||
return mpStore ? mpStore->Project() : nullptr;
|
||||
|
|
|
@ -74,7 +74,10 @@ public:
|
|||
CResource* LoadCooked(IInputStream& rInput);
|
||||
bool Unload();
|
||||
bool CanMoveTo(const TString& rkDir, const TString& rkName);
|
||||
bool Move(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 Rename(const TString& rkName, bool IsAutoGenName = false);
|
||||
|
||||
CGameProject* Project() const;
|
||||
EGame Game() const;
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ CResourceStore::~CResourceStore()
|
|||
void RecursiveGetListOfEmptyDirectories(CVirtualDirectory *pDir, TStringList& rOutList)
|
||||
{
|
||||
// Helper function for SerializeResourceDatabase
|
||||
if (pDir->IsEmpty())
|
||||
if (pDir->IsEmpty(false))
|
||||
{
|
||||
rOutList.push_back(pDir->FullPath());
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ void CResourceStore::CreateVirtualDirectory(const TString& rkPath)
|
|||
|
||||
void CResourceStore::ConditionalDeleteDirectory(CVirtualDirectory *pDir, bool Recurse)
|
||||
{
|
||||
if (pDir->IsEmpty() && !pDir->IsRoot())
|
||||
if (pDir->IsEmpty(true) && !pDir->IsRoot())
|
||||
{
|
||||
CVirtualDirectory *pParent = pDir->Parent();
|
||||
pParent->RemoveChildDirectory(pDir);
|
||||
|
@ -366,8 +366,9 @@ bool CResourceStore::BuildFromDirectory(bool ShouldGenerateCacheFile)
|
|||
|
||||
void CResourceStore::RebuildFromDirectory()
|
||||
{
|
||||
ASSERT(mpProj != nullptr);
|
||||
mpProj->AudioManager()->ClearAssets();
|
||||
if (mpProj)
|
||||
mpProj->AudioManager()->ClearAssets();
|
||||
|
||||
ClearDatabase();
|
||||
BuildFromDirectory(true);
|
||||
}
|
||||
|
@ -596,7 +597,7 @@ void CResourceStore::ImportNamesFromPakContentsTxt(const TString& rkTxtPath, boo
|
|||
TString Name = Path.GetFileName(false);
|
||||
if (Dir.IsEmpty()) Dir = pEntry->DirectoryPath();
|
||||
|
||||
pEntry->Move(Dir, Name);
|
||||
pEntry->MoveAndRename(Dir, Name);
|
||||
}
|
||||
|
||||
// Save
|
||||
|
|
|
@ -26,22 +26,37 @@ CVirtualDirectory::~CVirtualDirectory()
|
|||
delete mSubdirectories[iSub];
|
||||
}
|
||||
|
||||
bool CVirtualDirectory::IsEmpty() const
|
||||
bool CVirtualDirectory::IsEmpty(bool CheckFilesystem) const
|
||||
{
|
||||
if (!mResources.empty()) return false;
|
||||
if (!mResources.empty())
|
||||
return false;
|
||||
|
||||
for (u32 iSub = 0; iSub < mSubdirectories.size(); iSub++)
|
||||
if (!mSubdirectories[iSub]->IsEmpty()) return false;
|
||||
if (!mSubdirectories[iSub]->IsEmpty(CheckFilesystem))
|
||||
return false;
|
||||
|
||||
if (CheckFilesystem && !FileUtil::IsEmpty( AbsolutePath() ))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CVirtualDirectory::IsDescendantOf(CVirtualDirectory *pDir) const
|
||||
{
|
||||
return mpParent && (mpParent == pDir || mpParent->IsDescendantOf(pDir));
|
||||
}
|
||||
|
||||
TString CVirtualDirectory::FullPath() const
|
||||
{
|
||||
if (IsRoot())
|
||||
return "";
|
||||
else
|
||||
return (mpParent && !mpParent->IsRoot() ? mpParent->FullPath() + mName + '/' : mName + '/');
|
||||
return (mpParent ? mpParent->FullPath() + mName : mName) + '/';
|
||||
}
|
||||
|
||||
TString CVirtualDirectory::AbsolutePath() const
|
||||
{
|
||||
return mpStore->ResourcesDir() + FullPath();
|
||||
}
|
||||
|
||||
CVirtualDirectory* CVirtualDirectory::GetRoot()
|
||||
|
@ -191,24 +206,26 @@ bool CVirtualDirectory::AddChild(const TString &rkPath, CResourceEntry *pEntry)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CVirtualDirectory::AddChild(CVirtualDirectory *pDir)
|
||||
{
|
||||
if (pDir->Parent() != this) return false;
|
||||
if (FindChildDirectory(pDir->Name(), false) != nullptr) return false;
|
||||
|
||||
mSubdirectories.push_back(pDir);
|
||||
std::sort(mSubdirectories.begin(), mSubdirectories.end(), [](CVirtualDirectory *pLeft, CVirtualDirectory *pRight) -> bool {
|
||||
return (pLeft->Name().ToUpper() < pRight->Name().ToUpper());
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CVirtualDirectory::RemoveChildDirectory(CVirtualDirectory *pSubdir)
|
||||
{
|
||||
ASSERT(pSubdir->IsEmpty());
|
||||
|
||||
for (auto It = mSubdirectories.begin(); It != mSubdirectories.end(); It++)
|
||||
{
|
||||
if (*It == pSubdir)
|
||||
{
|
||||
mSubdirectories.erase(It);
|
||||
|
||||
// If this is part of the resource store, delete the corresponding filesystem directory
|
||||
if (mpStore && pSubdir->GetRoot() == mpStore->RootDirectory())
|
||||
{
|
||||
TString AbsPath = mpStore->ResourcesDir() + pSubdir->FullPath();
|
||||
FileUtil::DeleteDirectory(AbsPath, true);
|
||||
}
|
||||
|
||||
delete pSubdir;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -230,19 +247,73 @@ bool CVirtualDirectory::RemoveChildResource(CResourceEntry *pEntry)
|
|||
return false;
|
||||
}
|
||||
|
||||
void CVirtualDirectory::RemoveEmptySubdirectories()
|
||||
bool CVirtualDirectory::Delete()
|
||||
{
|
||||
ASSERT(IsEmpty(true) && !IsRoot());
|
||||
|
||||
if (IsEmpty(true) && !IsRoot())
|
||||
{
|
||||
if (FileUtil::DeleteDirectory(AbsolutePath(), true))
|
||||
{
|
||||
if (!mpParent || mpParent->RemoveChildDirectory(this))
|
||||
{
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CVirtualDirectory::DeleteEmptySubdirectories()
|
||||
{
|
||||
for (u32 SubdirIdx = 0; SubdirIdx < mSubdirectories.size(); SubdirIdx++)
|
||||
{
|
||||
CVirtualDirectory *pDir = mSubdirectories[SubdirIdx];
|
||||
|
||||
if (pDir->IsEmpty())
|
||||
if (pDir->IsEmpty(true))
|
||||
{
|
||||
RemoveChildDirectory(pDir);
|
||||
pDir->Delete();
|
||||
SubdirIdx--;
|
||||
}
|
||||
else
|
||||
pDir->RemoveEmptySubdirectories();
|
||||
pDir->DeleteEmptySubdirectories();
|
||||
}
|
||||
}
|
||||
|
||||
bool CVirtualDirectory::SetParent(CVirtualDirectory *pParent)
|
||||
{
|
||||
ASSERT(!pParent->IsDescendantOf(this));
|
||||
if (mpParent == pParent) return true;
|
||||
|
||||
Log::Write("MOVING DIRECTORY: " + FullPath() + " -> " + pParent->FullPath() + mName + '/');
|
||||
|
||||
// Check for a conflict
|
||||
CVirtualDirectory *pConflictDir = pParent->FindChildDirectory(mName, false);
|
||||
|
||||
if (pConflictDir)
|
||||
{
|
||||
Log::Error("DIRECTORY MOVE FAILED: Conflicting directory exists at the destination path!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move filesystem contents to new path
|
||||
TString AbsOldPath = mpStore->ResourcesDir() + FullPath();
|
||||
TString AbsNewPath = mpStore->ResourcesDir() + pParent->FullPath() + mName + '/';
|
||||
|
||||
if (mpParent->RemoveChildDirectory(this) && FileUtil::MoveDirectory(AbsOldPath, AbsNewPath))
|
||||
{
|
||||
mpParent = pParent;
|
||||
mpParent->AddChild(this);
|
||||
mpStore->SetCacheDirty();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::Error("DIRECTORY MOVE FAILED: Filesystem move operation failed!");
|
||||
mpParent->AddChild(this);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,16 +24,21 @@ public:
|
|||
CVirtualDirectory(CVirtualDirectory *pParent, const TString& rkName, CResourceStore *pStore);
|
||||
~CVirtualDirectory();
|
||||
|
||||
bool IsEmpty() const;
|
||||
bool IsEmpty(bool CheckFilesystem) const;
|
||||
bool IsDescendantOf(CVirtualDirectory *pDir) const;
|
||||
TString FullPath() const;
|
||||
TString AbsolutePath() const;
|
||||
CVirtualDirectory* GetRoot();
|
||||
CVirtualDirectory* FindChildDirectory(const TString& rkName, bool AllowCreate);
|
||||
CResourceEntry* FindChildResource(const TString& rkPath);
|
||||
CResourceEntry* FindChildResource(const TString& rkName, EResType Type);
|
||||
bool AddChild(const TString& rkPath, CResourceEntry *pEntry);
|
||||
bool AddChild(CVirtualDirectory *pDir);
|
||||
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
|
||||
bool RemoveChildResource(CResourceEntry *pEntry);
|
||||
void RemoveEmptySubdirectories();
|
||||
bool Delete();
|
||||
void DeleteEmptySubdirectories();
|
||||
bool SetParent(CVirtualDirectory *pParent);
|
||||
|
||||
static bool IsValidDirectoryName(const TString& rkName);
|
||||
static bool IsValidDirectoryPath(TString Path);
|
||||
|
|
|
@ -287,7 +287,10 @@ void CEditorApplication::OnEditorClose()
|
|||
mEditorWindows.removeOne(pEditor);
|
||||
delete pEditor;
|
||||
|
||||
mpActiveProject->ResourceStore()->DestroyUnreferencedResources();
|
||||
if (mpActiveProject)
|
||||
{
|
||||
mpActiveProject->ResourceStore()->DestroyUnreferencedResources();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ signals:
|
|||
void ActiveProjectChanged(CGameProject *pNewProj);
|
||||
void AssetsModified();
|
||||
void PackagesCooked();
|
||||
void ResourceRenamed(CResourceEntry *pEntry);
|
||||
void DirectoryRenamed(CVirtualDirectory *pDir);
|
||||
};
|
||||
|
||||
#define gpEdApp static_cast<CEditorApplication*>(qApp)
|
||||
|
|
|
@ -187,7 +187,11 @@ HEADERS += \
|
|||
Widgets/CSelectResourcePanel.h \
|
||||
Widgets/CFilteredResourceModel.h \
|
||||
ResourceBrowser/CResourceDelegate.h \
|
||||
ResourceBrowser/CResourceTableContextMenu.h
|
||||
ResourceBrowser/CResourceTableContextMenu.h \
|
||||
ResourceBrowser/CResourceMimeData.h \
|
||||
ResourceBrowser/CResourceTableView.h \
|
||||
Undo/CMoveResourceCommand.h \
|
||||
Undo/CMoveDirectoryCommand.h
|
||||
|
||||
# Source Files
|
||||
SOURCES += \
|
||||
|
@ -257,7 +261,9 @@ SOURCES += \
|
|||
CProgressDialog.cpp \
|
||||
Widgets/CSelectResourcePanel.cpp \
|
||||
ResourceBrowser/CResourceDelegate.cpp \
|
||||
ResourceBrowser/CResourceTableContextMenu.cpp
|
||||
ResourceBrowser/CResourceTableContextMenu.cpp \
|
||||
ResourceBrowser/CResourceTableModel.cpp \
|
||||
ResourceBrowser/CResourceTableView.cpp
|
||||
|
||||
# UI Files
|
||||
FORMS += \
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "CResourceDelegate.h"
|
||||
#include "CResourceTableContextMenu.h"
|
||||
#include "Editor/CEditorApplication.h"
|
||||
#include "Editor/Undo/CMoveDirectoryCommand.h"
|
||||
#include "Editor/Undo/CMoveResourceCommand.h"
|
||||
#include <Core/GameProject/AssetNameGeneration.h>
|
||||
#include <Core/GameProject/CAssetNameMap.h>
|
||||
|
||||
|
@ -28,6 +30,19 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
|||
// Hide sorting combo box for now. The size isn't displayed on the UI so this isn't really useful for the end user.
|
||||
mpUI->SortComboBox->hide();
|
||||
|
||||
// Create undo/redo actions
|
||||
mpUndoAction = new QAction("Undo", this);
|
||||
mpRedoAction = new QAction("Redo", this);
|
||||
mpUndoAction->setShortcut( QKeySequence::Undo );
|
||||
mpRedoAction->setShortcut( QKeySequence::Redo );
|
||||
addAction(mpUndoAction);
|
||||
addAction(mpRedoAction);
|
||||
|
||||
connect(mpUndoAction, SIGNAL(triggered(bool)), this, SLOT(Undo()));
|
||||
connect(mpRedoAction, SIGNAL(triggered(bool)), this, SLOT(Redo()));
|
||||
connect(&mUndoStack, SIGNAL(canUndoChanged(bool)), this, SLOT(UpdateUndoActionStates()));
|
||||
connect(&mUndoStack, SIGNAL(canRedoChanged(bool)), this, SLOT(UpdateUndoActionStates()));
|
||||
|
||||
// Configure display mode buttons
|
||||
QButtonGroup *pModeGroup = new QButtonGroup(this);
|
||||
pModeGroup->addButton(mpUI->ResourceTreeButton);
|
||||
|
@ -45,6 +60,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
|||
|
||||
mpDelegate = new CResourceBrowserDelegate(this);
|
||||
mpUI->ResourceTableView->setItemDelegate(mpDelegate);
|
||||
mpUI->ResourceTableView->installEventFilter(this);
|
||||
|
||||
// Set up directory tree model
|
||||
mpDirectoryModel = new CVirtualDirectoryModel(this);
|
||||
|
@ -203,6 +219,70 @@ void CResourceBrowser::CreateFilterCheckboxes()
|
|||
mpFilterBoxesLayout->addSpacerItem(pSpacer);
|
||||
}
|
||||
|
||||
bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir)
|
||||
{
|
||||
// Check for any conflicts
|
||||
QList<CResourceEntry*> ConflictingResources;
|
||||
|
||||
foreach (CResourceEntry *pEntry, rkResources)
|
||||
{
|
||||
if (pNewDir->FindChildResource(pEntry->Name(), pEntry->ResourceType()) != nullptr)
|
||||
ConflictingResources << pEntry;
|
||||
}
|
||||
|
||||
QList<CVirtualDirectory*> ConflictingDirs;
|
||||
|
||||
foreach (CVirtualDirectory *pDir, rkDirectories)
|
||||
{
|
||||
if (pNewDir->FindChildDirectory(pDir->Name(), false) != nullptr)
|
||||
ConflictingDirs << pDir;
|
||||
}
|
||||
|
||||
// If there were conflicts, notify the user of them
|
||||
if (!ConflictingResources.isEmpty() || !ConflictingDirs.isEmpty())
|
||||
{
|
||||
QString ErrorMsg = "Unable to move; the destination directory has conflicting files.\n\n";
|
||||
|
||||
foreach (CVirtualDirectory *pDir, ConflictingDirs)
|
||||
{
|
||||
ErrorMsg += QString("* %1").arg( TO_QSTRING(pDir->Name()) );
|
||||
}
|
||||
|
||||
foreach (CResourceEntry *pEntry, ConflictingResources)
|
||||
{
|
||||
ErrorMsg += QString("* %1.%2\n").arg( TO_QSTRING(pEntry->Name()) ).arg( TO_QSTRING(pEntry->CookedExtension().ToString()) );
|
||||
}
|
||||
|
||||
UICommon::ErrorMsg(this, ErrorMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create undo actions to actually perform the moves
|
||||
mUndoStack.beginMacro("Move Resources");
|
||||
|
||||
foreach (CVirtualDirectory *pDir, rkDirectories)
|
||||
mUndoStack.push( new CMoveDirectoryCommand(mpStore, pDir, pNewDir) );
|
||||
|
||||
foreach (CResourceEntry *pEntry, rkResources)
|
||||
mUndoStack.push( new CMoveResourceCommand(pEntry, pNewDir) );
|
||||
|
||||
mUndoStack.endMacro();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CResourceBrowser::eventFilter(QObject *pWatched, QEvent *pEvent)
|
||||
{
|
||||
if (pWatched == mpUI->ResourceTableView)
|
||||
{
|
||||
if (pEvent->type() == QEvent::FocusIn || pEvent->type() == QEvent::FocusOut)
|
||||
{
|
||||
UpdateUndoActionStates();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CResourceBrowser::RefreshResources()
|
||||
{
|
||||
// Fill resource table
|
||||
|
@ -421,7 +501,7 @@ void CResourceBrowser::ImportAssetNameMap()
|
|||
bool AutoDir, AutoName;
|
||||
|
||||
if (Map.GetNameInfo(It->ID(), Dir, Name, AutoDir, AutoName))
|
||||
It->Move(Dir, Name, AutoDir, AutoName);
|
||||
It->MoveAndRename(Dir, Name, AutoDir, AutoName);
|
||||
}
|
||||
|
||||
mpStore->ConditionalSaveStore();
|
||||
|
@ -520,3 +600,24 @@ void CResourceBrowser::OnFilterTypeBoxTicked(bool Checked)
|
|||
mpProxyModel->invalidate();
|
||||
ReentrantGuard = false;
|
||||
}
|
||||
|
||||
void CResourceBrowser::UpdateUndoActionStates()
|
||||
{
|
||||
// Make sure that the undo actions are only enabled when the table view has focus.
|
||||
// This is to prevent them from conflicting with world editor undo/redo actions.
|
||||
bool HasFocus = (mpUI->ResourceTableView->hasFocus());
|
||||
mpUndoAction->setEnabled( HasFocus && mUndoStack.canUndo() );
|
||||
mpRedoAction->setEnabled( HasFocus && mUndoStack.canRedo() );
|
||||
}
|
||||
|
||||
void CResourceBrowser::Undo()
|
||||
{
|
||||
mUndoStack.undo();
|
||||
UpdateUndoActionStates();
|
||||
}
|
||||
|
||||
void CResourceBrowser::Redo()
|
||||
{
|
||||
mUndoStack.redo();
|
||||
UpdateUndoActionStates();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "CVirtualDirectoryModel.h"
|
||||
#include <QCheckBox>
|
||||
#include <QTimer>
|
||||
#include <QUndoStack>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Ui {
|
||||
|
@ -41,6 +42,12 @@ class CResourceBrowser : public QWidget
|
|||
};
|
||||
QList<SResourceType> mTypeList;
|
||||
|
||||
// Undo/Redo
|
||||
QUndoStack mUndoStack;
|
||||
QAction *mpUndoAction;
|
||||
QAction *mpRedoAction;
|
||||
QWidget *mpActionContainerWidget;
|
||||
|
||||
public:
|
||||
explicit CResourceBrowser(QWidget *pParent = 0);
|
||||
~CResourceBrowser();
|
||||
|
@ -49,6 +56,11 @@ public:
|
|||
void SelectDirectory(CVirtualDirectory *pDir);
|
||||
void CreateFilterCheckboxes();
|
||||
|
||||
bool MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir);
|
||||
|
||||
// Interface
|
||||
bool eventFilter(QObject *pWatched, QEvent *pEvent);
|
||||
|
||||
// Accessors
|
||||
inline CResourceStore* CurrentStore() const { return mpStore; }
|
||||
inline CResourceEntry* SelectedEntry() const { return mpSelectedEntry; }
|
||||
|
@ -80,6 +92,10 @@ public slots:
|
|||
void ResetTypeFilter();
|
||||
void OnFilterTypeBoxTicked(bool Checked);
|
||||
|
||||
void UpdateUndoActionStates();
|
||||
void Undo();
|
||||
void Redo();
|
||||
|
||||
signals:
|
||||
void SelectedResourceChanged(CResourceEntry *pNewRes);
|
||||
};
|
||||
|
|
|
@ -257,7 +257,7 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QTableView" name="ResourceTableView">
|
||||
<widget class="CResourceTableView" name="ResourceTableView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
|
@ -275,6 +275,15 @@
|
|||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DragDrop</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
@ -310,6 +319,11 @@
|
|||
<extends>QLineEdit</extends>
|
||||
<header>Editor/Widgets/CTimedLineEdit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>CResourceTableView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>Editor/ResourceBrowser/CResourceTableView.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../Icons.qrc"/>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef CRESOURCEMIMEDATA_H
|
||||
#define CRESOURCEMIMEDATA_H
|
||||
|
||||
#include <Core/GameProject/CResourceEntry.h>
|
||||
#include <Core/GameProject/CVirtualDirectory.h>
|
||||
#include <QMimeData>
|
||||
|
||||
class CResourceMimeData : public QMimeData
|
||||
{
|
||||
Q_OBJECT
|
||||
QList<CResourceEntry*> mEntries;
|
||||
QList<CVirtualDirectory*> mDirectories;
|
||||
|
||||
public:
|
||||
CResourceMimeData(const QList<CResourceEntry*>& rkEntries, const QList<CVirtualDirectory*>& rkDirectories)
|
||||
: QMimeData()
|
||||
, mEntries(rkEntries)
|
||||
, mDirectories(rkDirectories)
|
||||
{}
|
||||
|
||||
CResourceMimeData(CResourceEntry *pEntry)
|
||||
: QMimeData()
|
||||
{
|
||||
mEntries << pEntry;
|
||||
}
|
||||
|
||||
const QList<CResourceEntry*>& Resources() const { return mEntries; }
|
||||
const QList<CVirtualDirectory*>& Directories() const { return mDirectories; }
|
||||
};
|
||||
|
||||
#endif // CRESOURCEMIMEDATA_H
|
|
@ -29,18 +29,22 @@ void CResourceTableContextMenu::ShowMenu(const QPoint& rkPos)
|
|||
{
|
||||
// Fetch the entry/directory
|
||||
QModelIndex ProxyIndex = mpTable->indexAt(rkPos);
|
||||
mIndex = mpProxy->mapToSource(ProxyIndex);
|
||||
mpEntry = mpModel->IndexEntry(mIndex);
|
||||
mpDirectory = mpModel->IndexDirectory(mIndex);
|
||||
|
||||
// Show/hide menu options
|
||||
bool IsRes = (mpEntry != nullptr);
|
||||
mpOpenInExternalAppAction->setVisible(IsRes);
|
||||
mpCopyIDAction->setVisible(IsRes);
|
||||
if (ProxyIndex.isValid())
|
||||
{
|
||||
mIndex = mpProxy->mapToSource(ProxyIndex);
|
||||
mpEntry = mpModel->IndexEntry(mIndex);
|
||||
mpDirectory = mpModel->IndexDirectory(mIndex);
|
||||
|
||||
// Exec menu
|
||||
QPoint GlobalPos = mpTable->viewport()->mapToGlobal(rkPos);
|
||||
exec(GlobalPos);
|
||||
// Show/hide menu options
|
||||
bool IsRes = (mpEntry != nullptr);
|
||||
mpOpenInExternalAppAction->setVisible(IsRes);
|
||||
mpCopyIDAction->setVisible(IsRes);
|
||||
|
||||
// Exec menu
|
||||
QPoint GlobalPos = mpTable->viewport()->mapToGlobal(rkPos);
|
||||
exec(GlobalPos);
|
||||
}
|
||||
}
|
||||
|
||||
// Menu Options
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
#include "CResourceTableModel.h"
|
||||
#include "CResourceBrowser.h"
|
||||
#include "CResourceMimeData.h"
|
||||
|
||||
CResourceTableModel::CResourceTableModel(QObject *pParent /*= 0*/)
|
||||
: QAbstractTableModel(pParent)
|
||||
, mpCurrentDir(nullptr)
|
||||
{
|
||||
connect(gpEdApp, SIGNAL(ResourceRenamed(CResourceEntry*)), this, SLOT(OnResourceRenamed(CResourceEntry*)));
|
||||
connect(gpEdApp, SIGNAL(DirectoryRenamed(CVirtualDirectory*)), this, SLOT(OnDirectoryRenamed(CVirtualDirectory*)));
|
||||
}
|
||||
|
||||
// ************ INTERFACE ************
|
||||
int CResourceTableModel::rowCount(const QModelIndex&) const
|
||||
{
|
||||
return mDirectories.size() + mEntries.size();
|
||||
}
|
||||
|
||||
int CResourceTableModel::columnCount(const QModelIndex&) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariant CResourceTableModel::data(const QModelIndex& rkIndex, int Role) const
|
||||
{
|
||||
if (rkIndex.column() != 0)
|
||||
return QVariant::Invalid;
|
||||
|
||||
// Directory
|
||||
if (IsIndexDirectory(rkIndex))
|
||||
{
|
||||
CVirtualDirectory *pDir = IndexDirectory(rkIndex);
|
||||
|
||||
if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole)
|
||||
return (mHasParent && rkIndex.row() == 0 ? ".." : TO_QSTRING(pDir->Name()));
|
||||
|
||||
else if (Role == Qt::DecorationRole)
|
||||
return QIcon(":/icons/Open_24px.png");
|
||||
|
||||
else
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
// Resource
|
||||
CResourceEntry *pEntry = IndexEntry(rkIndex);
|
||||
|
||||
if (Role == Qt::DisplayRole)
|
||||
return TO_QSTRING(pEntry->Name());
|
||||
|
||||
else if (Role == Qt::ToolTipRole)
|
||||
return TO_QSTRING(pEntry->CookedAssetPath(true));
|
||||
|
||||
else if (Role == Qt::DecorationRole)
|
||||
return QIcon(":/icons/Sphere Preview.png");
|
||||
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
Qt::ItemFlags CResourceTableModel::flags(const QModelIndex& rkIndex) const
|
||||
{
|
||||
Qt::ItemFlags Out = Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled;
|
||||
|
||||
if (IsIndexDirectory(rkIndex))
|
||||
Out |= Qt::ItemIsDropEnabled;
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
||||
bool CResourceTableModel::canDropMimeData(const QMimeData *pkData, Qt::DropAction, int Row, int Column, const QModelIndex& rkParent) const
|
||||
{
|
||||
const CResourceMimeData *pkMimeData = qobject_cast<const CResourceMimeData*>(pkData);
|
||||
|
||||
if (pkMimeData)
|
||||
{
|
||||
// Make sure we're dropping onto a directory
|
||||
QModelIndex Index = (rkParent.isValid() ? rkParent : index(Row, Column, rkParent));
|
||||
|
||||
if (Index.isValid())
|
||||
{
|
||||
CVirtualDirectory *pDir = IndexDirectory(Index);
|
||||
|
||||
if (pDir)
|
||||
{
|
||||
// Make sure this directory isn't part of the mime data, or a subdirectory of a directory in the mime data
|
||||
foreach (CVirtualDirectory *pMimeDir, pkMimeData->Directories())
|
||||
{
|
||||
if (pDir == pMimeDir || pDir->IsDescendantOf(pMimeDir))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid directory
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CResourceTableModel::dropMimeData(const QMimeData *pkData, Qt::DropAction, int Row, int Column, const QModelIndex& rkParent)
|
||||
{
|
||||
const CResourceMimeData *pkMimeData = qobject_cast<const CResourceMimeData*>(pkData);
|
||||
|
||||
QModelIndex Index = (rkParent.isValid() ? rkParent : index(Row, Column, rkParent));
|
||||
CVirtualDirectory *pDir = IndexDirectory(Index);
|
||||
ASSERT(pDir);
|
||||
|
||||
gpEdApp->ResourceBrowser()->MoveResources( pkMimeData->Resources(), pkMimeData->Directories(), pDir );
|
||||
return true;
|
||||
}
|
||||
|
||||
QMimeData* CResourceTableModel::mimeData(const QModelIndexList& rkIndexes) const
|
||||
{
|
||||
if (rkIndexes.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
QList<CResourceEntry*> Resources;
|
||||
QList<CVirtualDirectory*> Dirs;
|
||||
|
||||
foreach(QModelIndex Index, rkIndexes)
|
||||
{
|
||||
CResourceEntry *pEntry = IndexEntry(Index);
|
||||
CVirtualDirectory *pDir = IndexDirectory(Index);
|
||||
|
||||
if (pEntry) Resources << pEntry;
|
||||
else Dirs << pDir;
|
||||
}
|
||||
|
||||
return new CResourceMimeData(Resources, Dirs);
|
||||
}
|
||||
|
||||
Qt::DropActions CResourceTableModel::supportedDragActions() const
|
||||
{
|
||||
return Qt::MoveAction | Qt::CopyAction;
|
||||
}
|
||||
|
||||
Qt::DropActions CResourceTableModel::supportedDropActions() const
|
||||
{
|
||||
return Qt::MoveAction;
|
||||
}
|
||||
|
||||
// ************ FUNCTIONALITY ************
|
||||
QModelIndex CResourceTableModel::GetIndexForEntry(CResourceEntry *pEntry) const
|
||||
{
|
||||
if (mEntryIndexMap.contains(pEntry))
|
||||
return index(mEntryIndexMap[pEntry] + mDirectories.size(), 0, QModelIndex());
|
||||
else
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
CResourceEntry* CResourceTableModel::IndexEntry(const QModelIndex& rkIndex) const
|
||||
{
|
||||
int Index = rkIndex.row() - mDirectories.size();
|
||||
return (Index >= 0 ? mEntries[Index] : nullptr);
|
||||
}
|
||||
|
||||
CVirtualDirectory* CResourceTableModel::IndexDirectory(const QModelIndex& rkIndex) const
|
||||
{
|
||||
return (IsIndexDirectory(rkIndex) ? mDirectories[rkIndex.row()] : nullptr);
|
||||
}
|
||||
|
||||
bool CResourceTableModel::IsIndexDirectory(const QModelIndex& rkIndex) const
|
||||
{
|
||||
return rkIndex.row() >= 0 && rkIndex.row() < mDirectories.size();
|
||||
}
|
||||
|
||||
void CResourceTableModel::FillEntryList(CVirtualDirectory *pDir, bool AssetListMode)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
mEntries.clear();
|
||||
mDirectories.clear();
|
||||
mEntryIndexMap.clear();
|
||||
mHasParent = false;
|
||||
|
||||
if (pDir)
|
||||
{
|
||||
// In filesystem mode, show only subdirectories and assets in the current directory.
|
||||
if (!AssetListMode)
|
||||
{
|
||||
if (!pDir->IsRoot())
|
||||
{
|
||||
mDirectories << pDir->Parent();
|
||||
mHasParent = true;
|
||||
}
|
||||
|
||||
for (u32 iDir = 0; iDir < pDir->NumSubdirectories(); iDir++)
|
||||
mDirectories << pDir->SubdirectoryByIndex(iDir);
|
||||
|
||||
for (u32 iRes = 0; iRes < pDir->NumResources(); iRes++)
|
||||
{
|
||||
CResourceEntry *pEntry = pDir->ResourceByIndex(iRes);
|
||||
|
||||
if (pEntry->TypeInfo()->IsVisibleInBrowser() && !pEntry->IsHidden())
|
||||
{
|
||||
mEntryIndexMap[pEntry] = mEntries.size();
|
||||
mEntries << pEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In asset list mode, do not show subdirectories and show all assets in current directory + all subdirectories.
|
||||
else
|
||||
RecursiveAddDirectoryContents(pDir);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void CResourceTableModel::RecursiveAddDirectoryContents(CVirtualDirectory *pDir)
|
||||
{
|
||||
for (u32 iRes = 0; iRes < pDir->NumResources(); iRes++)
|
||||
{
|
||||
CResourceEntry *pEntry = pDir->ResourceByIndex(iRes);
|
||||
|
||||
if (pEntry->TypeInfo()->IsVisibleInBrowser() && !pEntry->IsHidden())
|
||||
{
|
||||
mEntryIndexMap[pEntry] = mEntries.size();
|
||||
mEntries << pEntry;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 iDir = 0; iDir < pDir->NumSubdirectories(); iDir++)
|
||||
RecursiveAddDirectoryContents(pDir->SubdirectoryByIndex(iDir));
|
||||
}
|
||||
|
||||
void CResourceTableModel::OnResourceRenamed(CResourceEntry *pEntry)
|
||||
{
|
||||
if (mEntryIndexMap.contains(pEntry))
|
||||
{
|
||||
int Index = mEntries.indexOf(pEntry);
|
||||
int Row = Index + mDirectories.size();
|
||||
|
||||
beginRemoveRows(QModelIndex(), Row, Row);
|
||||
mEntries.removeAt(Index);
|
||||
mEntryIndexMap.remove(pEntry);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceTableModel::OnDirectoryRenamed(CVirtualDirectory *pDir)
|
||||
{
|
||||
for (int DirIdx = 0; DirIdx < mDirectories.size(); DirIdx++)
|
||||
{
|
||||
if (mDirectories[DirIdx] == pDir)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), DirIdx, DirIdx);
|
||||
mDirectories.removeAt(DirIdx);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,150 +13,45 @@ class CResourceTableModel : public QAbstractTableModel
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
CVirtualDirectory *mpCurrentDir;
|
||||
QList<CVirtualDirectory*> mDirectories;
|
||||
QList<CResourceEntry*> mEntries;
|
||||
QMap<CResourceEntry*, int> mEntryIndexMap;
|
||||
bool mHasParent;
|
||||
|
||||
public:
|
||||
CResourceTableModel(QObject *pParent = 0)
|
||||
: QAbstractTableModel(pParent)
|
||||
{}
|
||||
CResourceTableModel(QObject *pParent = 0);
|
||||
|
||||
int rowCount(const QModelIndex& /*rkParent*/) const
|
||||
{
|
||||
return mDirectories.size() + mEntries.size();
|
||||
}
|
||||
// Interface
|
||||
int rowCount(const QModelIndex& /*rkParent*/) const;
|
||||
int columnCount(const QModelIndex& /*rkParent*/) const;
|
||||
QVariant data(const QModelIndex& rkIndex, int Role) const;
|
||||
Qt::ItemFlags flags(const QModelIndex& rkIndex) const;
|
||||
|
||||
int columnCount(const QModelIndex& /*rkParent*/) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex& rkIndex, int Role) const
|
||||
{
|
||||
if (rkIndex.column() != 0)
|
||||
return QVariant::Invalid;
|
||||
|
||||
// Directory
|
||||
if (IsIndexDirectory(rkIndex))
|
||||
{
|
||||
CVirtualDirectory *pDir = IndexDirectory(rkIndex);
|
||||
|
||||
if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole)
|
||||
return (mHasParent && rkIndex.row() == 0 ? ".." : TO_QSTRING(pDir->Name()));
|
||||
|
||||
else if (Role == Qt::DecorationRole)
|
||||
return QIcon(":/icons/Open_24px.png");
|
||||
|
||||
else
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
// Resource
|
||||
CResourceEntry *pEntry = IndexEntry(rkIndex);
|
||||
|
||||
if (Role == Qt::DisplayRole)
|
||||
return TO_QSTRING(pEntry->Name());
|
||||
|
||||
else if (Role == Qt::ToolTipRole)
|
||||
return TO_QSTRING(pEntry->CookedAssetPath(true));
|
||||
|
||||
else if (Role == Qt::DecorationRole)
|
||||
return QIcon(":/icons/Sphere Preview.png");
|
||||
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
QModelIndex GetIndexForEntry(CResourceEntry *pEntry) const
|
||||
{
|
||||
if (mEntryIndexMap.contains(pEntry))
|
||||
return index(mEntryIndexMap[pEntry] + mDirectories.size(), 0, QModelIndex());
|
||||
else
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
CResourceEntry* IndexEntry(const QModelIndex& rkIndex) const
|
||||
{
|
||||
int Index = rkIndex.row() - mDirectories.size();
|
||||
return (Index >= 0 ? mEntries[Index] : nullptr);
|
||||
}
|
||||
|
||||
CVirtualDirectory* IndexDirectory(const QModelIndex& rkIndex) const
|
||||
{
|
||||
return (rkIndex.row() < mDirectories.size() ? mDirectories[rkIndex.row()] : nullptr);
|
||||
}
|
||||
|
||||
bool IsIndexDirectory(const QModelIndex& rkIndex) const
|
||||
{
|
||||
return rkIndex.row() < mDirectories.size();
|
||||
}
|
||||
|
||||
void FillEntryList(CVirtualDirectory *pDir, bool AssetListMode)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
mEntries.clear();
|
||||
mDirectories.clear();
|
||||
mEntryIndexMap.clear();
|
||||
mHasParent = false;
|
||||
|
||||
if (pDir)
|
||||
{
|
||||
// In filesystem mode, show only subdirectories and assets in the current directory.
|
||||
if (!AssetListMode)
|
||||
{
|
||||
if (!pDir->IsRoot())
|
||||
{
|
||||
mDirectories << pDir->Parent();
|
||||
mHasParent = true;
|
||||
}
|
||||
|
||||
for (u32 iDir = 0; iDir < pDir->NumSubdirectories(); iDir++)
|
||||
mDirectories << pDir->SubdirectoryByIndex(iDir);
|
||||
|
||||
for (u32 iRes = 0; iRes < pDir->NumResources(); iRes++)
|
||||
{
|
||||
CResourceEntry *pEntry = pDir->ResourceByIndex(iRes);
|
||||
|
||||
if (pEntry->TypeInfo()->IsVisibleInBrowser() && !pEntry->IsHidden())
|
||||
{
|
||||
mEntryIndexMap[pEntry] = mEntries.size();
|
||||
mEntries << pEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In asset list mode, do not show subdirectories and show all assets in current directory + all subdirectories.
|
||||
else
|
||||
RecursiveAddDirectoryContents(pDir);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
bool canDropMimeData(const QMimeData *pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& rkParent) const;
|
||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
||||
QMimeData* mimeData(const QModelIndexList& rkIndexes) const;
|
||||
Qt::DropActions supportedDragActions() const;
|
||||
Qt::DropActions supportedDropActions() const;
|
||||
|
||||
// Functionality
|
||||
QModelIndex GetIndexForEntry(CResourceEntry *pEntry) const;
|
||||
QModelIndex GetIndexForDirectory(CVirtualDirectory *pDir) const;
|
||||
CResourceEntry* IndexEntry(const QModelIndex& rkIndex) const;
|
||||
CVirtualDirectory* IndexDirectory(const QModelIndex& rkIndex) const;
|
||||
bool IsIndexDirectory(const QModelIndex& rkIndex) const;
|
||||
void FillEntryList(CVirtualDirectory *pDir, bool AssetListMode);
|
||||
protected:
|
||||
void RecursiveAddDirectoryContents(CVirtualDirectory *pDir)
|
||||
{
|
||||
for (u32 iRes = 0; iRes < pDir->NumResources(); iRes++)
|
||||
{
|
||||
CResourceEntry *pEntry = pDir->ResourceByIndex(iRes);
|
||||
|
||||
if (pEntry->TypeInfo()->IsVisibleInBrowser() && !pEntry->IsHidden())
|
||||
{
|
||||
mEntryIndexMap[pEntry] = mEntries.size();
|
||||
mEntries << pEntry;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 iDir = 0; iDir < pDir->NumSubdirectories(); iDir++)
|
||||
RecursiveAddDirectoryContents(pDir->SubdirectoryByIndex(iDir));
|
||||
}
|
||||
void RecursiveAddDirectoryContents(CVirtualDirectory *pDir);
|
||||
|
||||
public:
|
||||
// Accessors
|
||||
inline u32 NumDirectories() const { return mDirectories.size(); }
|
||||
inline u32 NumResources() const { return mEntries.size(); }
|
||||
|
||||
public slots:
|
||||
void OnResourceRenamed(CResourceEntry *pEntry);
|
||||
void OnDirectoryRenamed(CVirtualDirectory *pDir);
|
||||
};
|
||||
|
||||
#endif // CRESOURCELISTMODEL
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#include "CResourceTableView.h"
|
||||
#include <QDragEnterEvent>
|
||||
|
||||
CResourceTableView::CResourceTableView(QWidget *pParent /*= 0*/)
|
||||
: QTableView(pParent)
|
||||
{}
|
||||
|
||||
void CResourceTableView::dragEnterEvent(QDragEnterEvent *pEvent)
|
||||
{
|
||||
// need to reimplement this to fix a bug in QAbstractItemView
|
||||
if (dragDropMode() == QAbstractItemView::InternalMove &&
|
||||
(pEvent->source() != this || ((pEvent->possibleActions() & Qt::MoveAction) == 0)) )
|
||||
return;
|
||||
|
||||
if (pEvent->possibleActions() & model()->supportedDropActions())
|
||||
{
|
||||
pEvent->accept();
|
||||
setState(QAbstractItemView::DraggingState);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef CRESOURCETABLEVIEW_H
|
||||
#define CRESOURCETABLEVIEW_H
|
||||
|
||||
#include <QTableView>
|
||||
|
||||
class CResourceTableView : public QTableView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CResourceTableView(QWidget *pParent = 0);
|
||||
void dragEnterEvent(QDragEnterEvent *pEvent);
|
||||
};
|
||||
|
||||
#endif // CRESOURCETABLEVIEW_H
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef CMOVEDIRECTORYCOMMAND_H
|
||||
#define CMOVEDIRECTORYCOMMAND_H
|
||||
|
||||
#include "IUndoCommand.h"
|
||||
#include "Editor/CEditorApplication.h"
|
||||
#include <Core/GameProject/CResourceStore.h>
|
||||
#include <Core/GameProject/CVirtualDirectory.h>
|
||||
|
||||
class CMoveDirectoryCommand : public IUndoCommand
|
||||
{
|
||||
CResourceStore *mpStore;
|
||||
TString mTargetDir;
|
||||
TString mOldParent;
|
||||
TString mNewParent;
|
||||
|
||||
public:
|
||||
CMoveDirectoryCommand(CResourceStore *pStore, CVirtualDirectory *pDir, CVirtualDirectory *pNewParent)
|
||||
: IUndoCommand("Move Directory")
|
||||
, mpStore(pStore)
|
||||
, mTargetDir(pDir->FullPath())
|
||||
, mOldParent(pDir->Parent()->FullPath())
|
||||
, mNewParent(pNewParent->FullPath())
|
||||
{}
|
||||
|
||||
void undo()
|
||||
{
|
||||
CVirtualDirectory *pDir = mpStore->GetVirtualDirectory(mTargetDir, false);
|
||||
CVirtualDirectory *pParent = mpStore->GetVirtualDirectory(mOldParent, false);
|
||||
ASSERT(pDir && pParent);
|
||||
|
||||
pDir->SetParent(pParent);
|
||||
mTargetDir = pDir->FullPath();
|
||||
|
||||
gpEdApp->DirectoryRenamed(pDir);
|
||||
}
|
||||
|
||||
void redo()
|
||||
{
|
||||
CVirtualDirectory *pDir = mpStore->GetVirtualDirectory(mTargetDir, false);
|
||||
CVirtualDirectory *pParent = mpStore->GetVirtualDirectory(mNewParent, false);
|
||||
ASSERT(pDir && pParent);
|
||||
|
||||
pDir->SetParent(pParent);
|
||||
mTargetDir = pDir->FullPath();
|
||||
|
||||
gpEdApp->DirectoryRenamed(pDir);
|
||||
}
|
||||
|
||||
bool AffectsCleanState() const { return false; }
|
||||
};
|
||||
|
||||
#endif // CMOVEDIRECTORYCOMMAND_H
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef CMOVERESOURCECOMMAND_H
|
||||
#define CMOVERESOURCECOMMAND_H
|
||||
|
||||
#include "IUndoCommand.h"
|
||||
#include "Editor/CEditorApplication.h"
|
||||
#include <Core/GameProject/CResourceEntry.h>
|
||||
|
||||
class CMoveResourceCommand : public IUndoCommand
|
||||
{
|
||||
CResourceEntry *mpEntry;
|
||||
TString mOldDirPath;
|
||||
TString mNewDirPath;
|
||||
bool mOldDirAutoGenerated;
|
||||
|
||||
public:
|
||||
CMoveResourceCommand(CResourceEntry *pEntry, CVirtualDirectory *pNewDir)
|
||||
: IUndoCommand("Move Resource")
|
||||
, mpEntry(pEntry)
|
||||
, mOldDirPath(pEntry->DirectoryPath())
|
||||
, mNewDirPath(pNewDir->FullPath())
|
||||
, mOldDirAutoGenerated(pEntry->HasFlag(eREF_AutoResDir))
|
||||
{}
|
||||
|
||||
void undo()
|
||||
{
|
||||
bool Success = mpEntry->Move(mOldDirPath, mOldDirAutoGenerated);
|
||||
ASSERT(Success); // todo better error handling
|
||||
gpEdApp->ResourceRenamed(mpEntry);
|
||||
}
|
||||
|
||||
void redo()
|
||||
{
|
||||
// note: it doesn't matter if the new directory was auto-generated, since the
|
||||
// purpose of tracking that flag is to detect which resources have been auto-categorized.
|
||||
// if this is being called, even if the new directory is auto-generated, it means the
|
||||
// user is intentionally placing it here, so it should be treated as a custom directory
|
||||
bool Success = mpEntry->Move(mNewDirPath);
|
||||
ASSERT(Success); // todo better error handling
|
||||
gpEdApp->ResourceRenamed(mpEntry);
|
||||
}
|
||||
|
||||
bool AffectsCleanState() const { return false; }
|
||||
};
|
||||
|
||||
#endif // CMOVERESOURCECOMMAND_H
|
|
@ -3,17 +3,26 @@
|
|||
#include "Editor/CEditorApplication.h"
|
||||
#include "Editor/UICommon.h"
|
||||
#include "Editor/ResourceBrowser/CResourceBrowser.h"
|
||||
#include "Editor/ResourceBrowser/CResourceMimeData.h"
|
||||
|
||||
#include <Core/GameProject/CResourceStore.h>
|
||||
#include <Core/Resource/CResource.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QClipboard>
|
||||
#include <QDrag>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDragMoveEvent>
|
||||
#include <QDropEvent>
|
||||
#include <QMenu>
|
||||
|
||||
CResourceSelector::CResourceSelector(QWidget *pParent /*= 0*/)
|
||||
: QWidget(pParent)
|
||||
, mpResEntry(nullptr)
|
||||
, mIsEditable(true)
|
||||
, mIsDragging(false)
|
||||
{
|
||||
setAcceptDrops(true);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
// Set up UI
|
||||
|
@ -49,6 +58,9 @@ CResourceSelector::CResourceSelector(QWidget *pParent /*= 0*/)
|
|||
mpLayout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(mpLayout);
|
||||
|
||||
// Set up event filter
|
||||
mpResNameButton->installEventFilter(this);
|
||||
|
||||
// UI Connections
|
||||
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CreateContextMenu(QPoint)));
|
||||
connect(mpResNameButton, SIGNAL(clicked()), this, SLOT(Find()));
|
||||
|
@ -110,22 +122,123 @@ void CResourceSelector::SetTypeFilter(EGame Game, const TString& rkTypeList)
|
|||
|
||||
void CResourceSelector::SetResource(const CAssetID& rkID)
|
||||
{
|
||||
mpResEntry = gpResourceStore->FindEntry(rkID);
|
||||
OnResourceChanged();
|
||||
CResourceEntry *pNewEntry = gpResourceStore->FindEntry(rkID);
|
||||
|
||||
if (mpResEntry != pNewEntry)
|
||||
{
|
||||
mpResEntry = pNewEntry;
|
||||
OnResourceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceSelector::SetResource(CResourceEntry *pEntry)
|
||||
{
|
||||
mpResEntry = pEntry;
|
||||
OnResourceChanged();
|
||||
if (mpResEntry != pEntry)
|
||||
{
|
||||
mpResEntry = pEntry;
|
||||
OnResourceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceSelector::SetResource(CResource *pRes)
|
||||
{
|
||||
mpResEntry = (pRes ? pRes->Entry() : nullptr);
|
||||
OnResourceChanged();
|
||||
CResourceEntry *pNewEntry = (pRes ? pRes->Entry() : nullptr);
|
||||
|
||||
if (mpResEntry != pNewEntry)
|
||||
{
|
||||
mpResEntry = pNewEntry;
|
||||
OnResourceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// ************ INTERFACE ************
|
||||
bool CResourceSelector::eventFilter(QObject *pWatched, QEvent *pEvent)
|
||||
{
|
||||
if (pWatched == mpResNameButton)
|
||||
{
|
||||
if (pEvent->type() == QEvent::MouseButtonPress)
|
||||
{
|
||||
QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
|
||||
mousePressEvent(pMouseEvent);
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (pEvent->type() == QEvent::MouseButtonDblClick)
|
||||
{
|
||||
QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
|
||||
|
||||
if (pMouseEvent->button() == Qt::LeftButton)
|
||||
{
|
||||
if (mpResEntry)
|
||||
gpEdApp->EditResource(mpResEntry);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ************ DRAG ************
|
||||
void CResourceSelector::mousePressEvent(QMouseEvent *pEvent)
|
||||
{
|
||||
if (mpResNameButton->rect().contains(pEvent->pos()) && pEvent->button() == Qt::LeftButton)
|
||||
{
|
||||
mDragStartPosition = pEvent->pos();
|
||||
mIsDragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceSelector::mouseMoveEvent(QMouseEvent *pEvent)
|
||||
{
|
||||
if (mIsDragging)
|
||||
{
|
||||
if ( (pEvent->pos() - mDragStartPosition).manhattanLength() >= gpEdApp->startDragDistance() )
|
||||
{
|
||||
QDrag *pDrag = new QDrag(this);
|
||||
CResourceMimeData *pMimeData = new CResourceMimeData(mpResEntry);
|
||||
pDrag->setMimeData(pMimeData);
|
||||
pDrag->exec(Qt::CopyAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceSelector::mouseReleaseEvent(QMouseEvent *pEvent)
|
||||
{
|
||||
if (pEvent->button() == Qt::LeftButton)
|
||||
{
|
||||
mIsDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ DROP *************
|
||||
void CResourceSelector::dragEnterEvent(QDragEnterEvent *pEvent)
|
||||
{
|
||||
// Check whether the mime data is a valid format
|
||||
if (mIsEditable && (pEvent->possibleActions() & Qt::CopyAction))
|
||||
{
|
||||
const CResourceMimeData *pkData = qobject_cast<const CResourceMimeData*>(pEvent->mimeData());
|
||||
|
||||
if (pkData && pkData->Directories().isEmpty() && pkData->Resources().size() == 1)
|
||||
{
|
||||
CResourceEntry *pEntry = pkData->Resources().front();
|
||||
|
||||
if (!pEntry || mTypeFilter.Accepts(pEntry))
|
||||
pEvent->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceSelector::dropEvent(QDropEvent *pEvent)
|
||||
{
|
||||
// Set the new resource
|
||||
const CResourceMimeData *pkMimeData = qobject_cast<const CResourceMimeData*>(pEvent->mimeData());
|
||||
CResourceEntry *pEntry = pkMimeData->Resources().front();
|
||||
SetResource(pEntry);
|
||||
}
|
||||
|
||||
// ************ SLOTS ************
|
||||
void CResourceSelector::CreateContextMenu(const QPoint& rkPoint)
|
||||
{
|
||||
QMenu Menu;
|
||||
|
|
|
@ -29,6 +29,10 @@ class CResourceSelector : public QWidget
|
|||
QAction *mpCopyNameAction;
|
||||
QAction *mpCopyPathAction;
|
||||
|
||||
// Drag and Drop
|
||||
bool mIsDragging;
|
||||
QPoint mDragStartPosition;
|
||||
|
||||
public:
|
||||
explicit CResourceSelector(QWidget *pParent = 0);
|
||||
void SetFrameVisible(bool Visible);
|
||||
|
@ -39,6 +43,18 @@ public:
|
|||
void SetResource(CResourceEntry *pEntry);
|
||||
void SetResource(CResource *pRes);
|
||||
|
||||
// Interface
|
||||
bool eventFilter(QObject *pWatched, QEvent *pEvent);
|
||||
|
||||
// Drag
|
||||
void mousePressEvent(QMouseEvent *pEvent);
|
||||
void mouseMoveEvent(QMouseEvent *pEvent);
|
||||
void mouseReleaseEvent(QMouseEvent *pEvent);
|
||||
|
||||
// Drop
|
||||
void dragEnterEvent(QDragEnterEvent *pEvent);
|
||||
void dropEvent(QDropEvent *pEvent);
|
||||
|
||||
// Accessors
|
||||
inline CResourceEntry* Entry() const { return mpResEntry; }
|
||||
inline const CResTypeFilter& TypeFilter() const { return mTypeFilter; }
|
||||
|
|
|
@ -62,6 +62,13 @@ int main(int argc, char *argv[])
|
|||
// Create editor resource store
|
||||
gpEditorStore = new CResourceStore("../resources/");
|
||||
|
||||
if (!gpEditorStore->AreAllEntriesValid())
|
||||
{
|
||||
Log::Write("Editor store has invalid entries. Rebuilding database...");
|
||||
gpEditorStore->RebuildFromDirectory();
|
||||
gpEditorStore->ConditionalSaveStore();
|
||||
}
|
||||
|
||||
// Load templates
|
||||
CTemplateLoader::LoadGameList();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<struct ID="0x04" name="PatternedInfo" template="Structs/PatternedInfo.xml"/>
|
||||
<struct ID="0x05" name="ActorParameters" template="Structs/ActorParameters.xml"/>
|
||||
<property ID="0x06" name="WPSC" type="asset" extensions="WPSC"/>
|
||||
<property ID="0x07" name="Model" type="asset" extensions="CMDL"/>
|
||||
<property ID="0x07" name="Bomb Model" type="asset" extensions="CMDL"/>
|
||||
<struct ID="0x08" name="DamageInfo" template="Structs/DamageInfo.xml"/>
|
||||
<property ID="0x09" name="Unknown 1" type="float"/>
|
||||
<property ID="0x0A" name="Unknown 2" type="float"/>
|
||||
|
@ -34,6 +34,12 @@
|
|||
<model source="property">0x05:0x04</model>
|
||||
<model source="property">0x07</model>
|
||||
</assets>
|
||||
<attachments>
|
||||
<attachment propertyID="0x07" locator="bomb1_LCTR"/>
|
||||
<attachment propertyID="0x07" locator="bomb2_LCTR"/>
|
||||
<attachment propertyID="0x07" locator="bomb3_LCTR"/>
|
||||
<attachment propertyID="0x07" locator="bomb4_LCTR"/>
|
||||
</attachments>
|
||||
<rotation_type>enabled</rotation_type>
|
||||
<scale_type>enabled</scale_type>
|
||||
</editor>
|
||||
|
|
Loading…
Reference in New Issue