PrimeWorldEditor/src/Editor/ResourceBrowser/CVirtualDirectoryModel.cpp

355 lines
9.7 KiB
C++

#include "CVirtualDirectoryModel.h"
#include "CResourceBrowser.h"
#include "CResourceMimeData.h"
CVirtualDirectoryModel::CVirtualDirectoryModel(CResourceBrowser *pBrowser, QObject *pParent /*= 0*/)
: QAbstractItemModel(pParent)
, mpRoot(nullptr)
, mInsertingRows(false)
, mRemovingRows(false)
, mMovingRows(false)
, mChangingLayout(false)
{
connect(pBrowser, SIGNAL(DirectoryAboutToBeMoved(CVirtualDirectory*,QString)), this, SLOT(OnDirectoryAboutToBeMoved(CVirtualDirectory*,QString)));
connect(pBrowser, SIGNAL(DirectoryMoved(CVirtualDirectory*,CVirtualDirectory*,TString)), this, SLOT(FinishModelChanges()));
connect(pBrowser, SIGNAL(DirectoryAboutToBeCreated(QString)), this, SLOT(OnDirectoryAboutToBeCreated(QString)));
connect(pBrowser, SIGNAL(DirectoryCreated(CVirtualDirectory*)), this, SLOT(FinishModelChanges()));
connect(pBrowser, SIGNAL(DirectoryAboutToBeDeleted(CVirtualDirectory*)), this, SLOT(OnDirectoryAboutToBeDeleted(CVirtualDirectory*)));
connect(pBrowser, SIGNAL(DirectoryDeleted()), this, SLOT(FinishModelChanges()));
}
QModelIndex CVirtualDirectoryModel::index(int Row, int Column, const QModelIndex& rkParent) const
{
if (!hasIndex(Row, Column, rkParent))
return QModelIndex();
CVirtualDirectory *pDir = IndexDirectory(rkParent);
if (pDir && pDir->NumSubdirectories() > (uint32) Row)
return createIndex(Row, Column, pDir->SubdirectoryByIndex(Row));
else if (!pDir)
return createIndex(Row, Column, mpRoot);
return QModelIndex();
}
QModelIndex CVirtualDirectoryModel::parent(const QModelIndex& rkChild) const
{
CVirtualDirectory *pDir = IndexDirectory(rkChild);
CVirtualDirectory *pParent = pDir->Parent();
if (pParent)
{
CVirtualDirectory *pGrandparent = pParent->Parent();
if (pGrandparent)
{
for (uint32 iSub = 0; iSub < pGrandparent->NumSubdirectories(); iSub++)
{
if (pGrandparent->SubdirectoryByIndex(iSub) == pParent)
return createIndex(iSub, 0, pParent);
}
}
else return createIndex(0, 0, mpRoot);
}
return QModelIndex();
}
int CVirtualDirectoryModel::rowCount(const QModelIndex& rkParent) const
{
CVirtualDirectory *pDir = IndexDirectory(rkParent);
if (pDir) return pDir->NumSubdirectories();
else return mpRoot ? 1 : 0;
}
int CVirtualDirectoryModel::columnCount(const QModelIndex&) const
{
return 1;
}
QVariant CVirtualDirectoryModel::data(const QModelIndex& rkIndex, int Role) const
{
if (!rkIndex.isValid())
return QVariant::Invalid;
if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole)
{
if (!rkIndex.parent().isValid())
return "Resources";
else
{
CVirtualDirectory *pDir = IndexDirectory(rkIndex);
if (pDir) return TO_QSTRING(pDir->Name());
}
}
if (Role == Qt::DecorationRole)
{
return QIcon(":/icons/Open_24px.svg");
}
return QVariant::Invalid;
}
bool CVirtualDirectoryModel::setData(const QModelIndex& rkIndex, const QVariant& rkValue, int Role)
{
if (Role == Qt::EditRole)
{
QString NewName = rkValue.toString();
CVirtualDirectory *pDir = IndexDirectory(rkIndex);
if (pDir)
{
gpEdApp->ResourceBrowser()->RenameDirectory(pDir, TO_TSTRING(NewName));
return true;
}
}
return false;
}
Qt::ItemFlags CVirtualDirectoryModel::flags(const QModelIndex& rkIndex) const
{
Qt::ItemFlags Out = Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled;
if (rkIndex.isValid() && rkIndex.parent().isValid())
Out |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
return Out;
}
bool CVirtualDirectoryModel::canDropMimeData(const QMimeData *pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& rkParent) const
{
// Don't allow dropping between items
if (Row != -1 || Column != -1)
return false;
if (Action == Qt::MoveAction)
{
CVirtualDirectory *pDir = IndexDirectory(rkParent);
if (pDir)
{
const CResourceMimeData *pkMimeData = qobject_cast<const CResourceMimeData*>(pkData);
if (pkMimeData)
{
// Don't allow moving a directory into one of its children
foreach (CVirtualDirectory *pMoveDir, pkMimeData->Directories())
{
if (pDir->IsDescendantOf(pMoveDir))
return false;
}
return true;
}
}
}
return false;
}
bool CVirtualDirectoryModel::dropMimeData(const QMimeData *pkData, Qt::DropAction Action, int Row, int Column, const QModelIndex& rkParent)
{
// Perform move!
const CResourceMimeData *pkMimeData = qobject_cast<const CResourceMimeData*>(pkData);
if (canDropMimeData(pkData, Action, Row, Column, rkParent))
{
CVirtualDirectory *pDir = IndexDirectory(rkParent);
ASSERT(pDir);
return gpEdApp->ResourceBrowser()->MoveResources(pkMimeData->Resources(), pkMimeData->Directories(), pDir);
}
else return false;
}
QMimeData* CVirtualDirectoryModel::mimeData(const QModelIndexList& rkIndexes) const
{
if (rkIndexes.size() == 1)
{
QModelIndex Index = rkIndexes.front();
CVirtualDirectory *pDir = IndexDirectory(Index);
CResourceMimeData *pMimeData = new CResourceMimeData(pDir);
return pMimeData;
}
else
return nullptr;
}
Qt::DropActions CVirtualDirectoryModel::supportedDragActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
Qt::DropActions CVirtualDirectoryModel::supportedDropActions() const
{
return Qt::MoveAction;
}
QModelIndex CVirtualDirectoryModel::GetIndexForDirectory(CVirtualDirectory *pDir)
{
if (!pDir)
return QModelIndex();
QVector<int> Indices;
CVirtualDirectory *pOriginal = pDir;
CVirtualDirectory *pParent = pDir->Parent();
// Get index list
while (pParent)
{
bool Found = false;
for (uint32 iDir = 0; iDir < pParent->NumSubdirectories(); iDir++)
{
if (pParent->SubdirectoryByIndex(iDir) == pDir)
{
Indices.push_front(iDir);
pDir = pParent;
pParent = pParent->Parent();
Found = true;
break;
}
}
ASSERT(Found); // it should not be possible for this not to work
}
// Traverse hierarchy
QModelIndex Out = index(0, 0, QModelIndex());
foreach (int Idx, Indices)
Out = index(Idx, 0, Out);
ASSERT(IndexDirectory(Out) == pOriginal);
return Out;
}
CVirtualDirectory* CVirtualDirectoryModel::IndexDirectory(const QModelIndex& rkIndex) const
{
if (!rkIndex.isValid()) return nullptr;
return static_cast<CVirtualDirectory*>(rkIndex.internalPointer());
}
void CVirtualDirectoryModel::SetRoot(CVirtualDirectory *pDir)
{
beginResetModel();
mpRoot = pDir;
endResetModel();
}
bool CVirtualDirectoryModel::GetProposedIndex(QString Path, QModelIndex& rOutParent, int& rOutRow)
{
// Get parent path
TString FullPath = TO_TSTRING(Path);
if (FullPath.EndsWith('/') || FullPath.EndsWith('\\'))
FullPath = FullPath.ChopBack(1);
uint32 LastSlash = FullPath.LastIndexOf("\\/");
TString ParentPath = FullPath.ChopBack( FullPath.Size() - LastSlash );
// Find parent index
CVirtualDirectory *pParent = (ParentPath.IsEmpty() ? mpRoot : mpRoot->FindChildDirectory(ParentPath, false));
if (!pParent) return false;
QModelIndex ParentIndex = GetIndexForDirectory(pParent);
if (!ParentIndex.isValid()) return false;
// Determine the row number that the new directory will be inserted at
QString DirName = TO_QSTRING(FullPath.ChopFront( LastSlash + 1 ));
int NumRows = rowCount(ParentIndex);
int RowIdx = 0;
for (; RowIdx < NumRows; RowIdx++)
{
QModelIndex Index = index(RowIdx, 0, ParentIndex);
QString OtherName = data(Index, Qt::DisplayRole).toString();
if (QString::compare(DirName, OtherName, Qt::CaseInsensitive) < 0)
break;
}
rOutParent = ParentIndex;
rOutRow = RowIdx;
return true;
}
void CVirtualDirectoryModel::OnDirectoryAboutToBeMoved(CVirtualDirectory *pDir, QString NewPath)
{
QModelIndex Parent;
int Row;
if (GetProposedIndex(NewPath, Parent, Row))
{
QModelIndex OldIndex = GetIndexForDirectory(pDir);
QModelIndex OldParent = OldIndex.parent();
int OldRow = OldIndex.row();
if (OldParent == Parent && (Row == OldRow || Row == OldRow + 1))
{
emit layoutAboutToBeChanged();
mChangingLayout = true;
}
else
{
beginMoveRows(OldParent, OldRow, OldRow, Parent, Row);
mMovingRows = true;
}
}
}
void CVirtualDirectoryModel::OnDirectoryAboutToBeCreated(QString DirPath)
{
QModelIndex Parent;
int Row;
if (GetProposedIndex(DirPath, Parent, Row))
{
beginInsertRows(Parent, Row, Row);
mInsertingRows = true;
}
}
void CVirtualDirectoryModel::OnDirectoryAboutToBeDeleted(CVirtualDirectory *pDir)
{
QModelIndex Index = GetIndexForDirectory(pDir);
if (Index.isValid())
{
beginRemoveRows(Index.parent(), Index.row(), Index.row());
mRemovingRows = true;
}
}
void CVirtualDirectoryModel::FinishModelChanges()
{
if (mInsertingRows)
{
endInsertRows();
mInsertingRows = false;
}
if (mRemovingRows)
{
endRemoveRows();
mRemovingRows = false;
}
if (mMovingRows)
{
endMoveRows();
mMovingRows = false;
}
if (mChangingLayout)
{
emit layoutChanged();
mChangingLayout = false;
}
}