Added ability to create/delete directories

This commit is contained in:
Aruki 2017-07-20 20:48:12 -06:00
parent 16e310fb2f
commit 905173a0a0
13 changed files with 270 additions and 46 deletions

View File

@ -7,7 +7,6 @@ std::unordered_map<EResType, CResTypeInfo*> CResTypeInfo::smTypeMap;
CResTypeInfo::CResTypeInfo(EResType Type, const TString& rkTypeName)
: mType(Type)
, mTypeName(rkTypeName)
, mHidden(false)
, mCanBeSerialized(false)
, mCanHaveDependencies(true)
{
@ -160,13 +159,11 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{
CResTypeInfo *pType = new CResTypeInfo(eAnimCollisionPrimData, "Animation Collision Primitive Data");
AddExtension(pType, "CPRM", eReturns, eReturns);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
CResTypeInfo *pType = new CResTypeInfo(eAnimEventData, "Animation Event Data");
AddExtension(pType, "EVNT", ePrimeDemo, ePrime);
pType->mHidden = true;
}
{
CResTypeInfo *pType = new CResTypeInfo(eAnimSet, "Animation Character Set");
@ -197,7 +194,6 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{
CResTypeInfo *pType = new CResTypeInfo(eAudioLookupTable, "Audio Lookup Table");
AddExtension(pType, "ATBL", ePrimeDemo, eCorruption);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
@ -240,17 +236,14 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{
CResTypeInfo *pType = new CResTypeInfo(eMapArea, "Area Map");
AddExtension(pType, "MAPA", ePrimeDemo, eCorruption);
pType->mHidden = true;
}
{
CResTypeInfo *pType = new CResTypeInfo(eMapWorld, "World Map");
AddExtension(pType, "MAPW", ePrimeDemo, eCorruption);
pType->mHidden = true;
}
{
CResTypeInfo *pType = new CResTypeInfo(eMapUniverse, "Universe Map");
AddExtension(pType, "MAPU", ePrimeDemo, eEchoes);
pType->mHidden = true;
}
{
CResTypeInfo *pType = new CResTypeInfo(eMidi, "MIDI");
@ -299,13 +292,11 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{
CResTypeInfo *pType = new CResTypeInfo(ePathfinding, "Pathfinding Mesh");
AddExtension(pType, "PATH", ePrimeDemo, eCorruption);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
CResTypeInfo *pType = new CResTypeInfo(ePortalArea, "Portal Area");
AddExtension(pType, "PTLA", eEchoesDemo, eCorruption);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
@ -315,13 +306,11 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{
CResTypeInfo *pType = new CResTypeInfo(eSaveArea, "Area Save Info");
AddExtension(pType, "SAVA", eCorruptionProto, eCorruption);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
CResTypeInfo *pType = new CResTypeInfo(eSaveWorld, "World Save Info");
AddExtension(pType, "SAVW", ePrime, eReturns);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
@ -331,19 +320,16 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{
CResTypeInfo *pType = new CResTypeInfo(eSkeleton, "Skeleton");
AddExtension(pType, "CINF", ePrimeDemo, eReturns);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
CResTypeInfo *pType = new CResTypeInfo(eSkin, "Skin");
AddExtension(pType, "CSKR", ePrimeDemo, eReturns);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
CResTypeInfo *pType = new CResTypeInfo(eSourceAnimData, "Source Animation Data");
AddExtension(pType, "SAND", eCorruptionProto, eCorruption);
pType->mHidden = true;
pType->mCanHaveDependencies = false; // all dependencies are added to the CHAR dependency tree
}
{
@ -362,9 +348,8 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
AddExtension(pType, "FSM2", eEchoesDemo, eCorruption);
}
{
CResTypeInfo *pType = new CResTypeInfo(eStaticGeometryMap, "Static Geometry Map");
CResTypeInfo *pType = new CResTypeInfo(eStaticGeometryMap, "Static Scan Map");
AddExtension(pType, "EGMC", eEchoesDemo, eCorruption);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{
@ -375,7 +360,6 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{
CResTypeInfo *pType = new CResTypeInfo(eStringList, "String List");
AddExtension(pType, "STLC", eEchoesDemo, eCorruptionProto);
pType->mHidden = true;
pType->mCanHaveDependencies = false;
}
{

View File

@ -20,7 +20,6 @@ class CResTypeInfo
EResType mType;
TString mTypeName;
std::vector<SGameExtension> mCookedExtensions;
bool mHidden;
bool mCanBeSerialized;
bool mCanHaveDependencies;
@ -38,7 +37,6 @@ public:
// Accessors
inline EResType Type() const { return mType; }
inline TString TypeName() const { return mTypeName; }
inline bool IsVisibleInBrowser() const { return !mHidden; }
inline bool CanBeSerialized() const { return mCanBeSerialized; }
inline bool CanHaveDependencies() const { return mCanHaveDependencies; }

View File

@ -194,7 +194,8 @@ HEADERS += \
Undo/CMoveDirectoryCommand.h \
Undo/CRenameResourceCommand.h \
Undo/CRenameDirectoryCommand.h \
CFileNameValidator.h
CFileNameValidator.h \
Undo/ICreateDeleteDirectoryCommand.h
# Source Files
SOURCES += \

View File

@ -8,6 +8,7 @@
#include "Editor/Undo/CMoveResourceCommand.h"
#include "Editor/Undo/CRenameDirectoryCommand.h"
#include "Editor/Undo/CRenameResourceCommand.h"
#include "Editor/Undo/ICreateDeleteDirectoryCommand.h"
#include <Core/GameProject/AssetNameGeneration.h>
#include <Core/GameProject/CAssetNameMap.h>
@ -140,6 +141,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
connect(mpUI->ResourceTreeButton, SIGNAL(pressed()), this, SLOT(SetResourceTreeView()));
connect(mpUI->ResourceListButton, SIGNAL(pressed()), this, SLOT(SetResourceListView()));
connect(mpUI->SortComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSortModeChanged(int)));
connect(mpUI->NewFolderButton, SIGNAL(pressed()), this, SLOT(CreateDirectory()));
connect(mpUI->DirectoryTreeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnDirectorySelectionChanged(QModelIndex,QModelIndex)));
connect(mpUI->ResourceTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnDoubleClickTable(QModelIndex)));
connect(mpUI->ResourceTableView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnResourceSelectionChanged(QModelIndex, QModelIndex)));
@ -198,15 +200,11 @@ void CResourceBrowser::CreateFilterCheckboxes()
for (auto Iter = TypeList.begin(); Iter != TypeList.end(); Iter++)
{
CResTypeInfo *pType = *Iter;
if (pType->IsVisibleInBrowser())
{
QCheckBox *pCheck = new QCheckBox(this);
pCheck->setFont(mFilterBoxFont);
pCheck->setText(TO_QSTRING(pType->TypeName()));
mTypeList << SResourceType { pType, pCheck };
}
}
qSort(mTypeList.begin(), mTypeList.end(), [](const SResourceType& rkLeft, const SResourceType& rkRight) -> bool {
return rkLeft.pTypeInfo->TypeName().ToUpper() < rkRight.pTypeInfo->TypeName().ToUpper();
@ -399,6 +397,68 @@ void CResourceBrowser::OnSortModeChanged(int Index)
mpProxyModel->SetSortMode(Mode);
}
bool CResourceBrowser::CreateDirectory()
{
if (mpSelectedDir)
{
TString DirNameBase = "New Folder";
TString DirName = DirNameBase;
u32 AppendNum = 0;
while (mpSelectedDir->FindChildDirectory(DirName, false) != nullptr)
{
AppendNum++;
DirName = TString::Format("%s (%d)", *DirNameBase, AppendNum);
}
// Push create command to actually create the directory
CCreateDirectoryCommand *pCmd = new CCreateDirectoryCommand(mpStore, mpSelectedDir->FullPath(), DirName);
mUndoStack.push(pCmd);
// Now fetch the new directory and start editing it so the user can enter a name
CVirtualDirectory *pNewDir = mpSelectedDir->FindChildDirectory(DirName, false);
if (!pNewDir) return false;
// todo: edit in the directory tree view instead if it has focus
if (!mpUI->DirectoryTreeView->hasFocus())
{
QModelIndex Index = mpModel->GetIndexForDirectory(pNewDir);
ASSERT(Index.isValid());
QModelIndex ProxyIndex = mpProxyModel->mapFromSource(Index);
mpUI->ResourceTableView->edit(ProxyIndex);
}
return true;
}
return false;
}
bool CResourceBrowser::DeleteDirectories(const QList<CVirtualDirectory*>& rkDirs)
{
QList<CVirtualDirectory*> DeletableDirs;
foreach (CVirtualDirectory *pDir, rkDirs)
{
if (pDir && pDir->IsEmpty(true))
DeletableDirs << pDir;
}
if (DeletableDirs.size() > 0)
{
mUndoStack.beginMacro("Delete Directories");
foreach (CVirtualDirectory *pDir, DeletableDirs)
mUndoStack.push( new CDeleteDirectoryCommand(mpStore, pDir->Parent()->FullPath(), pDir->Name()) );
mUndoStack.endMacro();
return true;
}
else return false;
}
void CResourceBrowser::OnSearchStringChanged(QString SearchString)
{
bool WasSearching = mSearching;

View File

@ -75,6 +75,8 @@ public slots:
void SetResourceTreeView();
void SetResourceListView();
void OnSortModeChanged(int Index);
bool CreateDirectory();
bool DeleteDirectories(const QList<CVirtualDirectory*>& rkDirs);
void OnSearchStringChanged(QString SearchString);
void OnDirectorySelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& rkPrevIndex);
void OnDoubleClickTable(QModelIndex Index);
@ -100,6 +102,9 @@ public slots:
signals:
void SelectedResourceChanged(CResourceEntry *pNewRes);
void DirectoryCreated(CVirtualDirectory *pDir);
void DirectoryAboutToBeDeleted(CVirtualDirectory *pDir);
void DirectoryDeleted();
void ResourceMoved(CResourceEntry *pRes, CVirtualDirectory *pOldDir, TString OldName);
void DirectoryMoved(CVirtualDirectory *pDir, CVirtualDirectory *pOldDir, TString OldName);
};

View File

@ -140,6 +140,23 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="NewFolderButton">
<property name="text">
<string> New Folder</string>
</property>
<property name="icon">
<iconset resource="../Icons.qrc">
<normaloff>:/icons/Plus.png</normaloff>:/icons/Plus.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="SortComboBox">
<property name="sizePolicy">

View File

@ -6,6 +6,8 @@ CResourceTableModel::CResourceTableModel(CResourceBrowser *pBrowser, QObject *pP
: QAbstractTableModel(pParent)
, mpCurrentDir(nullptr)
{
connect(pBrowser, SIGNAL(DirectoryCreated(CVirtualDirectory*)), this, SLOT(CheckAddDirectory(CVirtualDirectory*)));
connect(pBrowser, SIGNAL(DirectoryAboutToBeDeleted(CVirtualDirectory*)), this, SLOT(CheckRemoveDirectory(CVirtualDirectory*)));
connect(pBrowser, SIGNAL(ResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)), this, SLOT(OnResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)));
connect(pBrowser, SIGNAL(DirectoryMoved(CVirtualDirectory*,CVirtualDirectory*,TString)), this, SLOT(OnDirectoryMoved(CVirtualDirectory*,CVirtualDirectory*,TString)));
}
@ -211,7 +213,7 @@ void CResourceTableModel::FillEntryList(CVirtualDirectory *pDir, bool AssetListM
{
CResourceEntry *pEntry = pDir->ResourceByIndex(iRes);
if (pEntry->TypeInfo()->IsVisibleInBrowser() && !pEntry->IsHidden())
if (!pEntry->IsHidden())
{
int Index = EntryListIndex(pEntry);
mEntries.insert(Index, pEntry);
@ -233,7 +235,7 @@ void CResourceTableModel::RecursiveAddDirectoryContents(CVirtualDirectory *pDir)
{
CResourceEntry *pEntry = pDir->ResourceByIndex(iRes);
if (pEntry->TypeInfo()->IsVisibleInBrowser() && !pEntry->IsHidden())
if (!pEntry->IsHidden())
{
int Index = EntryListIndex(pEntry);
mEntries.insert(Index, pEntry);
@ -249,6 +251,29 @@ int CResourceTableModel::EntryListIndex(CResourceEntry *pEntry)
return qLowerBound(mEntries, pEntry) - mEntries.constBegin();
}
void CResourceTableModel::CheckAddDirectory(CVirtualDirectory *pDir)
{
if (pDir->Parent() == mpCurrentDir)
{
// Just append to the end, let the proxy handle sorting
beginInsertRows(QModelIndex(), mDirectories.size(), mDirectories.size());
mDirectories << pDir;
endInsertRows();
}
}
void CResourceTableModel::CheckRemoveDirectory(CVirtualDirectory *pDir)
{
if (pDir->Parent() == mpCurrentDir)
{
QModelIndex Index = GetIndexForDirectory(pDir);
beginRemoveRows(QModelIndex(), Index.row(), Index.row());
mDirectories.removeAt(Index.row());
endRemoveRows();
}
}
void CResourceTableModel::OnResourceMoved(CResourceEntry *pEntry, CVirtualDirectory *pOldDir, TString OldName)
{
CVirtualDirectory *pNewDir = pEntry->Directory();
@ -314,21 +339,10 @@ void CResourceTableModel::OnDirectoryMoved(CVirtualDirectory *pDir, CVirtualDire
{
// Remove
if (WasInModel && !IsInModel)
{
QModelIndex Index = GetIndexForDirectory(pDir);
beginRemoveRows(QModelIndex(), Index.row(), Index.row());
mDirectories.removeOne(pDir);
endRemoveRows();
}
CheckRemoveDirectory(pDir);
// Add
else if (!WasInModel && IsInModel)
{
// Just append to the end, let the proxy handle sorting
beginInsertRows(QModelIndex(), mDirectories.size(), mDirectories.size());
mDirectories << pDir;
endInsertRows();
}
CheckAddDirectory(pDir);
}
}

View File

@ -50,6 +50,8 @@ public:
inline u32 NumResources() const { return mEntries.size(); }
public slots:
void CheckAddDirectory(CVirtualDirectory *pDir);
void CheckRemoveDirectory(CVirtualDirectory *pDir);
void OnResourceMoved(CResourceEntry *pEntry, CVirtualDirectory *pOldDir, TString OldName);
void OnDirectoryMoved(CVirtualDirectory *pDir, CVirtualDirectory *pOldDir, TString OldName);
};

View File

@ -1,4 +1,6 @@
#include "CResourceTableView.h"
#include "CResourceBrowser.h"
#include "CResourceProxyModel.h"
#include <QAction>
#include <QDragEnterEvent>
@ -11,6 +13,17 @@ CResourceTableView::CResourceTableView(QWidget *pParent /*= 0*/)
mpRenameAction->setShortcut( QKeySequence(Qt::Key_F2) );
connect(mpRenameAction, SIGNAL(triggered(bool)), this, SLOT(RenameSelected()));
addAction(mpRenameAction);
mpDeleteAction = new QAction(this);
mpDeleteAction->setShortcut(QKeySequence::Delete);
connect(mpDeleteAction, SIGNAL(triggered(bool)), this, SLOT(DeleteSelected()));
addAction(mpDeleteAction);
}
void CResourceTableView::setModel(QAbstractItemModel *pModel)
{
if (qobject_cast<CResourceProxyModel*>(pModel) != nullptr)
QTableView::setModel(pModel);
}
void CResourceTableView::dragEnterEvent(QDragEnterEvent *pEvent)
@ -47,3 +60,42 @@ void CResourceTableView::RenameSelected()
edit(List.front());
}
}
void CResourceTableView::DeleteSelected()
{
QModelIndexList List = selectionModel()->selectedIndexes();
// Figure out which indices can actually be deleted
CResourceProxyModel *pProxy = static_cast<CResourceProxyModel*>(model());
CResourceTableModel *pModel = static_cast<CResourceTableModel*>(pProxy->sourceModel());
QList<CVirtualDirectory*> DirsToDelete;
bool HasNonEmptyDirSelected = false;
foreach (QModelIndex Index, List)
{
QModelIndex SourceIndex = pProxy->mapToSource(Index);
if (pModel->IsIndexDirectory(SourceIndex))
{
CVirtualDirectory *pDir = pModel->IndexDirectory(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
gpEdApp->ResourceBrowser()->DeleteDirectories(DirsToDelete);
}

View File

@ -7,15 +7,18 @@ class CResourceTableView : public QTableView
{
Q_OBJECT
QAction *mpRenameAction;
QAction *mpDeleteAction;
public:
explicit CResourceTableView(QWidget *pParent = 0);
void setModel(QAbstractItemModel *pModel);
void dragEnterEvent(QDragEnterEvent *pEvent);
void focusInEvent(QFocusEvent*);
void focusOutEvent(QFocusEvent*);
public slots:
void RenameSelected();
void DeleteSelected();
};
#endif // CRESOURCETABLEVIEW_H

View File

@ -0,0 +1,90 @@
#ifndef CCREATEDIRECTORYCOMMAND_H
#define CCREATEDIRECTORYCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/CEditorApplication.h"
#include "Editor/ResourceBrowser/CResourceBrowser.h"
#include <Core/GameProject/CResourceStore.h>
#include <Core/GameProject/CVirtualDirectory.h>
class ICreateDeleteDirectoryCommand : public IUndoCommand
{
protected:
CResourceStore *mpStore;
TString mParentPath;
TString mDirName;
CVirtualDirectory *mpDir;
public:
ICreateDeleteDirectoryCommand(CResourceStore *pStore, TString ParentPath, TString DirName)
: IUndoCommand("Create Directory")
, mpStore(pStore)
, mParentPath(ParentPath)
, mDirName(DirName)
, mpDir(nullptr)
{}
protected:
void DoCreate()
{
CVirtualDirectory *pParent = mpStore->GetVirtualDirectory(mParentPath, false);
if (pParent)
{
mpDir = pParent->FindChildDirectory(mDirName, true);
if (mpDir)
gpEdApp->ResourceBrowser()->DirectoryCreated(mpDir);
}
}
void DoDelete()
{
if (mpDir && !mpDir->IsRoot())
{
if (mpDir->IsEmpty(true))
{
gpEdApp->ResourceBrowser()->DirectoryAboutToBeDeleted(mpDir);
bool DeleteSuccess = mpDir->Delete();
ASSERT(DeleteSuccess);
gpEdApp->ResourceBrowser()->DirectoryDeleted();
mpDir = nullptr;
}
else
{
Log::Write("Directory delete failed, directory is not empty: " + mParentPath + mDirName);
}
}
}
bool AffectsCleanState() const { return false; }
};
class CCreateDirectoryCommand : public ICreateDeleteDirectoryCommand
{
public:
CCreateDirectoryCommand(CResourceStore *pStore, TString ParentPath, TString DirName)
: ICreateDeleteDirectoryCommand(pStore, ParentPath, DirName)
{}
void undo() { DoDelete(); }
void redo() { DoCreate(); }
};
class CDeleteDirectoryCommand : public ICreateDeleteDirectoryCommand
{
public:
CDeleteDirectoryCommand(CResourceStore *pStore, TString ParentPath, TString DirName)
: ICreateDeleteDirectoryCommand(pStore, ParentPath, DirName)
{
mpDir = pStore->GetVirtualDirectory(ParentPath + DirName, false);
ASSERT(mpDir);
ASSERT(!mpDir->IsRoot());
}
void undo() { DoCreate(); }
void redo() { DoDelete(); }
};
#endif // CCREATEDIRECTORYCOMMAND_H

View File

@ -277,8 +277,6 @@ void CResourceSelector::Find()
{
CResourceBrowser *pBrowser = gpEdApp->ResourceBrowser();
pBrowser->SelectResource(mpResEntry);
pBrowser->show();
pBrowser->raise();
}
}

View File

@ -51,7 +51,7 @@ CWorldEditor::CWorldEditor(QWidget *parent)
pLayout->setContentsMargins(0,0,0,0);
CResourceBrowser *pResourceBrowser = gpEdApp->ResourceBrowser();
//pResourceBrowser->setParent(this);
pResourceBrowser->setParent(this);
pLayout->addWidget( pResourceBrowser );
ui->ResourceBrowserContainer->setLayout(pLayout);