Added ability to rename resources/directories in the resource table view

This commit is contained in:
Aruki 2017-07-16 03:24:14 -06:00
parent 932e2bff7a
commit db277d7a15
22 changed files with 503 additions and 111 deletions

View File

@ -112,9 +112,8 @@ bool MoveFile(const TString& rkOldPath, const TString& rkNewPath)
return false;
}
// todo: check return value? Docs don't say what the return value actually is
rename(*rkOldPath, *rkNewPath);
return true;
int Result = rename(*rkOldPath, *rkNewPath);
return (Result == 0);
}
bool MoveDirectory(const TString& rkOldPath, const TString& rkNewPath)
@ -131,9 +130,8 @@ bool MoveDirectory(const TString& rkOldPath, const TString& rkNewPath)
return false;
}
// todo: check return value? Docs don't say what the return value actually is
rename(*rkOldPath, *rkNewPath);
return true;
int Result = rename(*rkOldPath, *rkNewPath);
return (Result == 0);
}
bool DeleteFile(const TString& rkFilePath)
@ -305,8 +303,13 @@ TString SimplifyRelativePath(const TString& rkPath)
return Out;
}
u32 MaxFileNameLength()
{
return 255;
}
static const char gskIllegalNameChars[] = {
'<', '>', '\"', '/', '\\', '|', '?', '*'
'<', '>', '\"', '/', '\\', '|', '?', '*', ':'
};
TString SanitizeName(TString Name, bool Directory, bool RootDir /*= false*/)
@ -316,8 +319,6 @@ TString SanitizeName(TString Name, bool Directory, bool RootDir /*= false*/)
return Name;
// Remove illegal characters from path
u32 NumIllegalChars = sizeof(gskIllegalNameChars) / sizeof(char);
for (u32 iChr = 0; iChr < Name.Size(); iChr++)
{
char Chr = Name[iChr];
@ -326,21 +327,11 @@ TString SanitizeName(TString Name, bool Directory, bool RootDir /*= false*/)
if (Chr >= 0 && Chr <= 31)
Remove = true;
// For root, allow colon only as the last character of the name
else if (Chr == ':' && (!RootDir || iChr != Name.Size() - 1))
Remove = true;
// Allow colon only as the last character of root
bool IsLegalColon = (Chr == ':' && RootDir && iChr == Name.Size() - 1);
else
{
for (u32 iBan = 0; iBan < NumIllegalChars; iBan++)
{
if (Chr == gskIllegalNameChars[iBan])
{
Remove = true;
break;
}
}
}
if (!IsLegalColon && !IsValidFileNameCharacter(Chr))
Remove = true;
if (Remove)
{
@ -376,9 +367,9 @@ TString SanitizeName(TString Name, bool Directory, bool RootDir /*= false*/)
Name = Name.ChopFront(NumLeadingSpaces);
// Ensure the name is below the character limit
if (Name.Size() > 255)
if (Name.Size() > MaxFileNameLength())
{
u32 ChopNum = Name.Size() - 255;
u32 ChopNum = Name.Size() - MaxFileNameLength();
Name = Name.ChopBack(ChopNum);
}
@ -406,6 +397,22 @@ TString SanitizePath(TString Path, bool Directory)
return Path;
}
bool IsValidFileNameCharacter(char Chr)
{
static const u32 skNumIllegalChars = sizeof(gskIllegalNameChars) / sizeof(char);
if (Chr >= 0 && Chr <= 31)
return false;
for (u32 BanIdx = 0; BanIdx < skNumIllegalChars; BanIdx++)
{
if (Chr == gskIllegalNameChars[BanIdx])
return false;
}
return true;
}
bool IsValidName(const TString& rkName, bool Directory, bool RootDir /*= false*/)
{
// Only accounting for Windows limitations right now. However, this function should
@ -413,11 +420,9 @@ bool IsValidName(const TString& rkName, bool Directory, bool RootDir /*= false*/
if (rkName.IsEmpty())
return false;
if (rkName.Size() > 255)
if (rkName.Size() > MaxFileNameLength())
return false;
u32 NumIllegalChars = sizeof(gskIllegalNameChars) / sizeof(char);
if (Directory && (rkName == "." || rkName == ".."))
return true;
@ -426,18 +431,11 @@ bool IsValidName(const TString& rkName, bool Directory, bool RootDir /*= false*/
{
char Chr = rkName[iChr];
if (Chr >= 0 && Chr <= 31)
return false;
// Allow colon only as the last character of root
bool IsLegalColon = (Chr == ':' && RootDir && iChr == rkName.Size() - 1);
// Allow colon only on last character of root
if (Chr == ':' && (!RootDir || iChr != rkName.Size() - 1))
if (!IsLegalColon && !IsValidFileNameCharacter(Chr))
return false;
for (u32 iBan = 0; iBan < NumIllegalChars; iBan++)
{
if (Chr == gskIllegalNameChars[iBan])
return false;
}
}
if (Directory && (rkName.Back() == ' ' || rkName.Back() == '.'))

View File

@ -28,8 +28,10 @@ TString WorkingDirectory();
TString MakeAbsolute(TString Path);
TString MakeRelative(const TString& rkPath, const TString& rkRelativeTo = WorkingDirectory());
TString SimplifyRelativePath(const TString& rkPath);
u32 MaxFileNameLength();
TString SanitizeName(TString Name, bool Directory, bool RootDir = false);
TString SanitizePath(TString Path, bool Directory);
bool IsValidFileNameCharacter(char Chr);
bool IsValidName(const TString& rkName, bool Directory, bool RootDir = false);
bool IsValidPath(const TString& rkPath, bool Directory);
void GetDirectoryContents(TString DirPath, TStringList& rOut, bool Recursive = true, bool IncludeFiles = true, bool IncludeDirs = true);

View File

@ -106,7 +106,13 @@ bool CResourceStore::SerializeDatabaseCache(IArchive& rArc)
if (rArc.IsReader())
{
for (auto Iter = EmptyDirectories.begin(); Iter != EmptyDirectories.end(); Iter++)
CreateVirtualDirectory(*Iter);
{
// Don't create empty virtual directories that don't actually exist in the filesystem
TString AbsPath = ResourcesDir() + *Iter;
if (FileUtil::Exists(AbsPath))
CreateVirtualDirectory(*Iter);
}
}
return true;

View File

@ -247,6 +247,30 @@ bool CVirtualDirectory::RemoveChildResource(CResourceEntry *pEntry)
return false;
}
bool CVirtualDirectory::Rename(const TString& rkNewName)
{
Log::Write("MOVING DIRECTORY: " + FullPath() + " -> " + mpParent->FullPath() + rkNewName + '/');
if (!IsRoot())
{
if (!mpParent->FindChildDirectory(rkNewName, false))
{
TString AbsPath = AbsolutePath();
TString NewPath = mpParent->AbsolutePath() + rkNewName + "/";
if (FileUtil::MoveDirectory(AbsPath, NewPath))
{
mName = rkNewName;
mpStore->SetCacheDirty();
return true;
}
}
}
Log::Error("DIRECTORY MOVE FAILED");
return false;
}
bool CVirtualDirectory::Delete()
{
ASSERT(IsEmpty(true) && !IsRoot());
@ -257,6 +281,7 @@ bool CVirtualDirectory::Delete()
{
if (!mpParent || mpParent->RemoveChildDirectory(this))
{
mpStore->SetCacheDirty();
delete this;
return true;
}

View File

@ -36,6 +36,7 @@ public:
bool AddChild(CVirtualDirectory *pDir);
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
bool RemoveChildResource(CResourceEntry *pEntry);
bool Rename(const TString& rkNewName);
bool Delete();
void DeleteEmptySubdirectories();
bool SetParent(CVirtualDirectory *pParent);

View File

@ -240,7 +240,10 @@ void CEditorApplication::TickEditors()
mLastUpdate = CTimer::GlobalTime();
double DeltaTime = mLastUpdate - LastUpdate;
// Make sure the resource store cache is up-to-date
// Make sure the resource store caches are up-to-date
if (gpEditorStore)
gpEditorStore->ConditionalSaveStore();
if (gpResourceStore)
gpResourceStore->ConditionalSaveStore();

View File

@ -0,0 +1,51 @@
#ifndef CFILENAMEVALIDATOR_H
#define CFILENAMEVALIDATOR_H
#include <QValidator>
#include "UICommon.h"
#include <Common/FileUtil.h>
class CFileNameValidator : public QValidator
{
bool mIsDirectory;
public:
CFileNameValidator(bool IsDirectory, QObject *pParent = 0)
: QValidator(pParent)
, mIsDirectory(IsDirectory)
{}
QValidator::State validate(QString& rInput, int&) const
{
QValidator::State Out = QValidator::Acceptable;
if (!FileUtil::IsValidName( TO_TSTRING(rInput), mIsDirectory ))
{
// Uh oh, the input is invalid. Only invalid characters will be considered entirely
// invalid; other errors will be considered intermediate.
Out = QValidator::Intermediate;
for (int ChrIdx = 0; ChrIdx < rInput.size(); ChrIdx++)
{
char Chr = rInput.at(ChrIdx).toLatin1();
if (!FileUtil::IsValidFileNameCharacter(Chr))
{
Out = QValidator::Invalid;
break;
}
}
}
return Out;
}
void fixup(QString& rInput) const
{
TString Sanitized = FileUtil::SanitizeName( TO_TSTRING(rInput), mIsDirectory );
rInput = TO_QSTRING(Sanitized);
}
};
#endif // CFILENAMEVALIDATOR_H

View File

@ -191,7 +191,10 @@ HEADERS += \
ResourceBrowser/CResourceMimeData.h \
ResourceBrowser/CResourceTableView.h \
Undo/CMoveResourceCommand.h \
Undo/CMoveDirectoryCommand.h
Undo/CMoveDirectoryCommand.h \
Undo/CRenameResourceCommand.h \
Undo/CRenameDirectoryCommand.h \
CFileNameValidator.h
# Source Files
SOURCES += \

View File

@ -6,6 +6,8 @@
#include "Editor/CEditorApplication.h"
#include "Editor/Undo/CMoveDirectoryCommand.h"
#include "Editor/Undo/CMoveResourceCommand.h"
#include "Editor/Undo/CRenameDirectoryCommand.h"
#include "Editor/Undo/CRenameResourceCommand.h"
#include <Core/GameProject/AssetNameGeneration.h>
#include <Core/GameProject/CAssetNameMap.h>
@ -220,6 +222,54 @@ void CResourceBrowser::CreateFilterCheckboxes()
mpFilterBoxesLayout->addSpacerItem(pSpacer);
}
bool CResourceBrowser::RenameResource(CResourceEntry *pEntry, const TString& rkNewName)
{
if (pEntry->Name() == rkNewName)
return false;
// Check if the move is valid
if (!pEntry->CanMoveTo(pEntry->DirectoryPath(), rkNewName))
{
if (pEntry->Directory()->FindChildResource(rkNewName, pEntry->ResourceType()) != nullptr)
{
UICommon::ErrorMsg(this, "Failed to rename; the destination directory has conflicting files!");
return false;
}
else
{
UICommon::ErrorMsg(this, "Failed to rename; filename is invalid!");
return false;
}
}
// Everything seems to be valid; proceed with the rename
mUndoStack.push( new CRenameResourceCommand(pEntry, rkNewName) );
return true;
}
bool CResourceBrowser::RenameDirectory(CVirtualDirectory *pDir, const TString& rkNewName)
{
if (pDir->Name() == rkNewName)
return false;
if (!CVirtualDirectory::IsValidDirectoryName(rkNewName))
{
UICommon::ErrorMsg(this, "Failed to rename; directory name is invalid!");
return false;
}
// Check for conflicts
if (pDir->Parent()->FindChildDirectory(rkNewName, false) != nullptr)
{
UICommon::ErrorMsg(this, "Failed to rename; the destination directory has a conflicting directory!");
return false;
}
// No conflicts, proceed with the rename
mUndoStack.push( new CRenameDirectoryCommand(pDir, rkNewName) );
return true;
}
bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir)
{
// Check for any conflicts
@ -242,7 +292,7 @@ bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources,
// 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";
QString ErrorMsg = "Failed to move; the destination directory has conflicting files.\n\n";
foreach (CVirtualDirectory *pDir, ConflictingDirs)
{

View File

@ -56,6 +56,8 @@ public:
void SelectDirectory(CVirtualDirectory *pDir);
void CreateFilterCheckboxes();
bool RenameResource(CResourceEntry *pEntry, const TString& rkNewName);
bool RenameDirectory(CVirtualDirectory *pDir, const TString& rkNewName);
bool MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir);
// Interface

View File

@ -1,11 +1,16 @@
#include "CResourceDelegate.h"
#include "CResourceBrowser.h"
#include "CResourceProxyModel.h"
#include "CResourceTableModel.h"
#include "Editor/CFileNameValidator.h"
#include "Editor/UICommon.h"
#include <Common/Common.h>
#include <QLineEdit>
#include <QPainter>
struct SResDelegateInfo
// Font Info
struct SResDelegateFontInfo
{
QFont NameFont;
QFont InfoFont;
@ -16,12 +21,12 @@ struct SResDelegateInfo
int Margin;
int Spacing;
SResDelegateInfo()
SResDelegateFontInfo()
: NameFontMetrics(NameFont), InfoFontMetrics(InfoFont) {}
};
SResDelegateInfo GetDelegateInfo(const QStyleOptionViewItem& rkOption)
SResDelegateFontInfo GetFontInfo(const QStyleOptionViewItem& rkOption)
{
SResDelegateInfo Info;
SResDelegateFontInfo Info;
Info.NameFont = rkOption.font;
Info.NameFont.setPointSize( rkOption.font.pointSize() + 1 );
@ -42,10 +47,61 @@ SResDelegateInfo GetDelegateInfo(const QStyleOptionViewItem& rkOption)
return Info;
}
// Geometry Info
struct SResDelegateGeometryInfo
{
QRect InnerRect;
QRect IconRect;
QRect NameStringRect;
QRect InfoStringRect;
};
SResDelegateGeometryInfo GetGeometryInfo(const SResDelegateFontInfo& rkFontInfo, const QStyleOptionViewItem& rkOption, bool IsDirectory)
{
SResDelegateGeometryInfo Info;
// Calculate inner rect
int Margin = rkFontInfo.Margin;
Info.InnerRect = rkOption.rect.adjusted(Margin, Margin, -Margin, -Margin);
// Calculate icon
int IdealIconSize = CResourceBrowserDelegate::skIconSize;
int IconSize = Math::Min(IdealIconSize, Info.InnerRect.height());
int IconX = Info.InnerRect.left() + ((IdealIconSize - IconSize) / 2);
int IconY = Info.InnerRect.top() + ((Info.InnerRect.height() - IconSize) / 2);
Info.IconRect = QRect(IconX, IconY, IconSize, IconSize);
// Calculate name string
int NameX = Info.InnerRect.left() + IdealIconSize + (rkFontInfo.Spacing * 2);
int NameY = Info.InnerRect.top();
int NameSizeX = Info.InnerRect.right() - NameX;
int NameSizeY = rkFontInfo.NameFontMetrics.height();
// Adjust Y for directories to center it in the rect
if (IsDirectory)
{
NameY += (Info.InnerRect.height() - NameSizeY) / 2;
}
Info.NameStringRect = QRect(NameX, NameY, NameSizeX, NameSizeY);
// Calculate info string
if (!IsDirectory)
{
int InfoX = NameX;
int InfoY = NameY + NameSizeY + rkFontInfo.Spacing;
int InfoSizeX = NameSizeX;
int InfoSizeY = rkFontInfo.InfoFontMetrics.height();
Info.InfoStringRect = QRect(InfoX, InfoY, InfoSizeX, InfoSizeY);
}
return Info;
}
// Delegate implementation
QSize CResourceBrowserDelegate::sizeHint(const QStyleOptionViewItem& rkOption, const QModelIndex&) const
{
// Get string info
SResDelegateInfo Info = GetDelegateInfo(rkOption);
SResDelegateFontInfo Info = GetFontInfo(rkOption);
// Calculate height
int Height = (Info.Margin * 2) + Info.NameFontMetrics.height() + Info.Spacing + Info.InfoFontMetrics.height();
@ -54,20 +110,12 @@ QSize CResourceBrowserDelegate::sizeHint(const QStyleOptionViewItem& rkOption, c
void CResourceBrowserDelegate::paint(QPainter *pPainter, const QStyleOptionViewItem& rkOption, const QModelIndex& rkIndex) const
{
const CResourceProxyModel *pkProxy = qobject_cast<const CResourceProxyModel*>(rkIndex.model());
ASSERT(pkProxy != nullptr);
const CResourceTableModel *pkModel = qobject_cast<const CResourceTableModel*>(pkProxy->sourceModel());
ASSERT(pkModel != nullptr);
// Get resource entry
QModelIndex SourceIndex = pkProxy->mapToSource(rkIndex);
CResourceEntry *pEntry = pkModel->IndexEntry(SourceIndex);
CResourceEntry *pEntry = GetIndexEntry(rkIndex);
// Initialize
SResDelegateInfo Info = GetDelegateInfo(rkOption);
QRect InnerRect = rkOption.rect.adjusted(Info.Margin, Info.Margin, -Info.Margin, -Info.Margin);
QPoint PainterPos = InnerRect.topLeft();
SResDelegateFontInfo FontInfo = GetFontInfo(rkOption);
SResDelegateGeometryInfo GeomInfo = GetGeometryInfo(FontInfo, rkOption, pEntry == nullptr);
// Draw icon
QVariant IconVariant = rkIndex.model()->data(rkIndex, Qt::DecorationRole);
@ -75,43 +123,16 @@ void CResourceBrowserDelegate::paint(QPainter *pPainter, const QStyleOptionViewI
if (IconVariant != QVariant::Invalid)
{
QIcon Icon = IconVariant.value<QIcon>();
// Determine icon size. Ideally 24x24 if we have space, but downscale if we don't
int IdealIconSize = 24;
int IconSize = Math::Min(InnerRect.height(), IdealIconSize);
// Adjust icon position so it's centered in the ideal rect
QPoint IconPos = PainterPos;
IconPos.rx() += (IdealIconSize - IconSize) / 2;
IconPos.ry() += (InnerRect.height() - IconSize) / 2;
// Paint the icon
QRect IconRect(IconPos, QSize(IconSize, IconSize));
Icon.paint(pPainter, IconRect);
PainterPos.rx() += IdealIconSize + Info.Spacing + Info.Spacing;
Icon.paint(pPainter, GeomInfo.IconRect);
}
// Calculate rects
if (!pEntry)
PainterPos.ry() += (InnerRect.height() - Info.NameFontMetrics.height()) / 2;
int ResNameWidth = InnerRect.width() - (PainterPos.x() - InnerRect.left());
QSize ResNameSize(ResNameWidth, Info.NameFontMetrics.height());
QRect ResNameRect = QRect(PainterPos, ResNameSize);
PainterPos.ry() += ResNameRect.height() + Info.Spacing;
int ResInfoWidth = ResNameWidth;
QSize ResInfoSize(ResInfoWidth, Info.InfoFontMetrics.height());
QRect ResInfoRect = QRect(PainterPos, ResInfoSize);
// Draw resource name
QString ResName = pkModel->data(SourceIndex, Qt::DisplayRole).toString();
QString ElidedName = Info.NameFontMetrics.elidedText(ResName, Qt::ElideRight, ResNameWidth);
QString ResName = rkIndex.model()->data(rkIndex, Qt::DisplayRole).toString();
QString ElidedName = FontInfo.NameFontMetrics.elidedText(ResName, Qt::ElideRight, GeomInfo.NameStringRect.width());
pPainter->setFont(Info.NameFont);
pPainter->setPen(Info.NamePen);
pPainter->drawText(ResNameRect, ElidedName);
pPainter->setFont(FontInfo.NameFont);
pPainter->setPen(FontInfo.NamePen);
pPainter->drawText(GeomInfo.NameStringRect, ElidedName);
// Draw resource info string
if (pEntry)
@ -121,10 +142,92 @@ void CResourceBrowserDelegate::paint(QPainter *pPainter, const QStyleOptionViewI
if (mDisplayAssetIDs)
ResInfo.prepend( TO_QSTRING(pEntry->ID().ToString()) + " | " );
QString ElidedResInfo = Info.InfoFontMetrics.elidedText(ResInfo, Qt::ElideRight, ResInfoWidth);
QString ElidedResInfo = FontInfo.InfoFontMetrics.elidedText(ResInfo, Qt::ElideRight, GeomInfo.InfoStringRect.width());
pPainter->setFont(Info.InfoFont);
pPainter->setPen(Info.InfoPen);
pPainter->drawText(ResInfoRect, ElidedResInfo);
pPainter->setFont(FontInfo.InfoFont);
pPainter->setPen(FontInfo.InfoPen);
pPainter->drawText(GeomInfo.InfoStringRect, ElidedResInfo);
}
}
// Editor
QWidget* CResourceBrowserDelegate::createEditor(QWidget *pParent, const QStyleOptionViewItem&, const QModelIndex& rkIndex) const
{
bool IsDirectory = (GetIndexDirectory(rkIndex) != nullptr);
QLineEdit *pLineEdit = new QLineEdit(pParent);
pLineEdit->setValidator( new CFileNameValidator(IsDirectory, pLineEdit) );
// Set the max length to 150. Limit should be 255 but FileUtil::MoveFile doesn't
// seem to want to work with filenames that long. Not sure why.
u32 MaxLength = 150;
pLineEdit->setMaxLength(MaxLength);
return pLineEdit;
}
void CResourceBrowserDelegate::setEditorData(QWidget *pEditor, const QModelIndex& rkIndex) const
{
QLineEdit *pLineEdit = static_cast<QLineEdit*>(pEditor);
CResourceEntry *pEntry = GetIndexEntry(rkIndex);
if (pEntry)
pLineEdit->setText( TO_QSTRING(pEntry->Name()) );
else
pLineEdit->setText( TO_QSTRING(GetIndexDirectory(rkIndex)->Name()) );
}
void CResourceBrowserDelegate::setModelData(QWidget *pEditor, QAbstractItemModel *, const QModelIndex& rkIndex) const
{
QLineEdit *pLineEdit = static_cast<QLineEdit*>(pEditor);
QString NewName = pLineEdit->text();
pLineEdit->validator()->fixup(NewName);
if (!NewName.isEmpty())
{
CResourceEntry *pEntry = GetIndexEntry(rkIndex);
if (pEntry)
gpEdApp->ResourceBrowser()->RenameResource(pEntry, TO_TSTRING(NewName));
else
gpEdApp->ResourceBrowser()->RenameDirectory( GetIndexDirectory(rkIndex), TO_TSTRING(NewName) );
}
}
void CResourceBrowserDelegate::updateEditorGeometry(QWidget *pEditor, const QStyleOptionViewItem& rkOption, const QModelIndex& rkIndex) const
{
// Check if this is a directory
bool IsDir = GetIndexEntry(rkIndex) == nullptr;
// Get rect
SResDelegateFontInfo FontInfo = GetFontInfo(rkOption);
SResDelegateGeometryInfo GeomInfo = GetGeometryInfo(FontInfo, rkOption, IsDir);
// Set geometry; make it a little bit better than the name string rect to give the user more space
QRect WidgetRect = GeomInfo.NameStringRect.adjusted(-3, -3, 3, 3);
pEditor->setGeometry(WidgetRect);
}
CResourceEntry* CResourceBrowserDelegate::GetIndexEntry(const QModelIndex& rkIndex) const
{
const CResourceProxyModel *pkProxy = qobject_cast<const CResourceProxyModel*>(rkIndex.model());
ASSERT(pkProxy != nullptr);
const CResourceTableModel *pkModel = qobject_cast<const CResourceTableModel*>(pkProxy->sourceModel());
ASSERT(pkModel != nullptr);
QModelIndex SourceIndex = pkProxy->mapToSource(rkIndex);
return pkModel->IndexEntry(SourceIndex);
}
CVirtualDirectory* CResourceBrowserDelegate::GetIndexDirectory(const QModelIndex& rkIndex) const
{
const CResourceProxyModel *pkProxy = qobject_cast<const CResourceProxyModel*>(rkIndex.model());
ASSERT(pkProxy != nullptr);
const CResourceTableModel *pkModel = qobject_cast<const CResourceTableModel*>(pkProxy->sourceModel());
ASSERT(pkModel != nullptr);
QModelIndex SourceIndex = pkProxy->mapToSource(rkIndex);
return pkModel->IndexDirectory(SourceIndex);
}

View File

@ -3,8 +3,13 @@
#include <QStyledItemDelegate>
class CResourceBrowserDelegate : public QStyledItemDelegate
{
public:
static const int skIconSize = 32;
private:
bool mDisplayAssetIDs;
public:
@ -16,7 +21,16 @@ public:
QSize sizeHint(const QStyleOptionViewItem& rkOption, const QModelIndex& rkIndex) const;
void paint(QPainter *pPainter, const QStyleOptionViewItem& rkOption, const QModelIndex& rkIndex) const;
QWidget* createEditor(QWidget *pParent, const QStyleOptionViewItem& rkOption, const QModelIndex& rkIndex) const;
void setEditorData(QWidget *pEditor, const QModelIndex& rkIndex) const;
void setModelData(QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex& rkIndex) const;
void updateEditorGeometry(QWidget *pEditor, const QStyleOptionViewItem& rkOption, const QModelIndex& rkIndex) const;
inline void SetDisplayAssetIDs(bool Display) { mDisplayAssetIDs = Display; }
protected:
class CResourceEntry* GetIndexEntry(const QModelIndex& rkIndex) const;
class CVirtualDirectory* GetIndexDirectory(const QModelIndex& rkIndex) const;
};
#endif // CRESOURCEBROWSERDELEGATE_H

View File

@ -51,7 +51,7 @@ public:
return false;
else if (pLeftDir && pRightDir)
return rkLeft.row() < rkRight.row(); // leave original directory order intact
return pLeftDir->Name().ToUpper() < pRightDir->Name().ToUpper();
else if (mSortMode == eSortByName)
return pLeftRes->UppercaseName() < pRightRes->UppercaseName();

View File

@ -20,6 +20,8 @@ CResourceTableContextMenu::CResourceTableContextMenu(CResourceBrowser *pBrowser,
mpOpenInExternalAppAction = addAction("Open in external application", this, SLOT(OpenInExternalApp()));
mpOpenContainingFolderAction = addAction("Open containing folder", this, SLOT(OpenContainingFolder()));
addSeparator();
mpRenameAction = addAction("Rename", this, SLOT(Rename()));
addSeparator();
mpCopyNameAction = addAction("Copy name", this, SLOT(CopyName()));
mpCopyPathAction = addAction("Copy path", this, SLOT(CopyPath()));
mpCopyIDAction = addAction("Copy asset ID", this, SLOT(CopyID()));
@ -28,11 +30,11 @@ CResourceTableContextMenu::CResourceTableContextMenu(CResourceBrowser *pBrowser,
void CResourceTableContextMenu::ShowMenu(const QPoint& rkPos)
{
// Fetch the entry/directory
QModelIndex ProxyIndex = mpTable->indexAt(rkPos);
mProxyIndex = mpTable->indexAt(rkPos);
if (ProxyIndex.isValid())
if (mProxyIndex.isValid())
{
mIndex = mpProxy->mapToSource(ProxyIndex);
mIndex = mpProxy->mapToSource(mProxyIndex);
mpEntry = mpModel->IndexEntry(mIndex);
mpDirectory = mpModel->IndexDirectory(mIndex);
@ -77,6 +79,11 @@ void CResourceTableContextMenu::OpenContainingFolder()
}
}
void CResourceTableContextMenu::Rename()
{
mpTable->edit(mProxyIndex);
}
void CResourceTableContextMenu::CopyName()
{
if (mpEntry)

View File

@ -16,6 +16,7 @@ class CResourceTableContextMenu : public QMenu
CResourceTableModel *mpModel;
CResourceProxyModel *mpProxy;
QModelIndex mProxyIndex;
QModelIndex mIndex;
CResourceEntry *mpEntry;
CVirtualDirectory *mpDirectory;
@ -25,6 +26,8 @@ class CResourceTableContextMenu : public QMenu
QAction *mpOpenInExternalAppAction;
QAction *mpOpenContainingFolderAction;
QAction *mpRenameAction;
QAction *mpCopyNameAction;
QAction *mpCopyPathAction;
QAction *mpCopyIDAction;
@ -39,6 +42,7 @@ public slots:
void Open();
void OpenInExternalApp();
void OpenContainingFolder();
void Rename();
void CopyName();
void CopyPath();
void CopyID();

View File

@ -59,7 +59,7 @@ QVariant CResourceTableModel::data(const QModelIndex& rkIndex, int Role) const
Qt::ItemFlags CResourceTableModel::flags(const QModelIndex& rkIndex) const
{
Qt::ItemFlags Out = Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled;
Qt::ItemFlags Out = Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable;
if (IsIndexDirectory(rkIndex))
Out |= Qt::ItemIsDropEnabled;
@ -99,16 +99,20 @@ bool CResourceTableModel::canDropMimeData(const QMimeData *pkData, Qt::DropActio
return false;
}
bool CResourceTableModel::dropMimeData(const QMimeData *pkData, Qt::DropAction, int Row, int Column, const QModelIndex& rkParent)
bool CResourceTableModel::dropMimeData(const QMimeData *pkData, Qt::DropAction Action, 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);
if (canDropMimeData(pkData, Action, Row, Column, rkParent))
{
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;
gpEdApp->ResourceBrowser()->MoveResources( pkMimeData->Resources(), pkMimeData->Directories(), pDir );
return true;
}
else return false;
}
QMimeData* CResourceTableModel::mimeData(const QModelIndexList& rkIndexes) const
@ -292,6 +296,13 @@ void CResourceTableModel::OnDirectoryMoved(CVirtualDirectory *pDir, CVirtualDire
bool WasInModel = !mIsAssetListMode && pOldDir == mpCurrentDir;
bool IsInModel = !mIsAssetListMode && pNewDir == mpCurrentDir;
// Handle parent link
if (pDir == mpCurrentDir)
{
ASSERT(mDirectories.front() == pOldDir);
mDirectories[0] = pNewDir;
}
// Handle rename
if (WasInModel && IsInModel && pDir->Name() != OldName)
{
@ -312,7 +323,7 @@ void CResourceTableModel::OnDirectoryMoved(CVirtualDirectory *pDir, CVirtualDire
}
// Add
else if (!WasInModel && !IsInModel)
else if (!WasInModel && IsInModel)
{
// Just append to the end, let the proxy handle sorting
beginInsertRows(QModelIndex(), mDirectories.size(), mDirectories.size());

View File

@ -1,9 +1,17 @@
#include "CResourceTableView.h"
#include <QAction>
#include <QDragEnterEvent>
CResourceTableView::CResourceTableView(QWidget *pParent /*= 0*/)
: QTableView(pParent)
{}
{
// Create "rename" key shortcut
// todo - there's no QKeySequence::Rename. Is there another standard "rename" shortcut on other platforms?
mpRenameAction = new QAction(this);
mpRenameAction->setShortcut( QKeySequence(Qt::Key_F2) );
connect(mpRenameAction, SIGNAL(triggered(bool)), this, SLOT(RenameSelected()));
addAction(mpRenameAction);
}
void CResourceTableView::dragEnterEvent(QDragEnterEvent *pEvent)
{
@ -18,3 +26,24 @@ void CResourceTableView::dragEnterEvent(QDragEnterEvent *pEvent)
setState(QAbstractItemView::DraggingState);
}
}
void CResourceTableView::focusInEvent(QFocusEvent*)
{
mpRenameAction->setEnabled(true);
}
void CResourceTableView::focusOutEvent(QFocusEvent*)
{
mpRenameAction->setEnabled(false);
}
// ************ SLOTS ************
void CResourceTableView::RenameSelected()
{
QModelIndexList List = selectionModel()->selectedIndexes();
if (List.size() == 1)
{
edit(List.front());
}
}

View File

@ -6,10 +6,16 @@
class CResourceTableView : public QTableView
{
Q_OBJECT
QAction *mpRenameAction;
public:
explicit CResourceTableView(QWidget *pParent = 0);
void dragEnterEvent(QDragEnterEvent *pEvent);
void focusInEvent(QFocusEvent*);
void focusOutEvent(QFocusEvent*);
public slots:
void RenameSelected();
};
#endif // CRESOURCETABLEVIEW_H

View File

@ -34,11 +34,10 @@ protected:
void DoMove(const TString& rkPath, bool IsAutoDir)
{
CVirtualDirectory *pOldDir = mpEntry->Directory();
TString OldName = mpEntry->Name();
bool Success = mpEntry->Move(rkPath, IsAutoDir);
ASSERT(Success); // todo better error handling
gpEdApp->ResourceBrowser()->ResourceMoved(mpEntry, pOldDir, OldName);
gpEdApp->ResourceBrowser()->ResourceMoved(mpEntry, pOldDir, mpEntry->Name());
}
};

View File

@ -0,0 +1,38 @@
#ifndef CRENAMEDIRECTORYCOMMAND_H
#define CRENAMEDIRECTORYCOMMAND_H
#include "IUndoCommand.h"
#include "Editor/CEditorApplication.h"
#include "Editor/ResourceBrowser/CResourceBrowser.h"
#include <Core/GameProject/CVirtualDirectory.h>
class CRenameDirectoryCommand : public IUndoCommand
{
CVirtualDirectory *mpDir;
TString mOldName;
TString mNewName;
public:
CRenameDirectoryCommand(CVirtualDirectory *pDir, const TString& rkNewName)
: IUndoCommand("Rename Directory")
, mpDir(pDir)
, mOldName(pDir->Name())
, mNewName(rkNewName)
{}
void undo() { DoMove(mOldName); }
void redo() { DoMove(mNewName); }
bool AffectsCleanState() const { return false; }
protected:
void DoMove(const TString& rkName)
{
TString OldName = mpDir->Name();
bool Success = mpDir->Rename(rkName);
ASSERT(Success);
gpEdApp->ResourceBrowser()->DirectoryMoved(mpDir, mpDir->Parent(), OldName);
}
};
#endif // CRENAMEDIRECTORYCOMMAND_H

View File

@ -0,0 +1,40 @@
#ifndef CRENAMERESOURCECOMMAND_H
#define CRENAMERESOURCECOMMAND_H
#include "IUndoCommand.h"
#include "Editor/CEditorApplication.h"
#include "Editor/ResourceBrowser/CResourceBrowser.h"
#include <Core/GameProject/CResourceEntry.h>
class CRenameResourceCommand : public IUndoCommand
{
CResourceEntry *mpEntry;
TString mOldName;
TString mNewName;
bool mOldNameAutoGenerated;
public:
CRenameResourceCommand(CResourceEntry *pEntry, const TString& rkNewName)
: IUndoCommand("Rename Resource")
, mpEntry(pEntry)
, mOldName(pEntry->Name())
, mNewName(rkNewName)
, mOldNameAutoGenerated(pEntry->HasFlag(eREF_AutoResName))
{}
void undo() { DoMove(mOldName, mOldNameAutoGenerated); }
void redo() { DoMove(mNewName, false); }
bool AffectsCleanState() const { return false; }
protected:
void DoMove(const TString& rkName, bool IsAutoName)
{
TString OldName = mpEntry->Name();
bool Success = mpEntry->Rename(rkName, IsAutoName);
ASSERT(Success);
gpEdApp->ResourceBrowser()->ResourceMoved(mpEntry, mpEntry->Directory(), OldName);
}
};
#endif // CRENAMERESOURCECOMMAND_H

View File

@ -3,7 +3,7 @@
<name>Midi</name>
<properties>
<struct ID="0x255A4580" template="Structs/EditorProperties.xml"/>
<property ID="0x9D1A67A8" type="asset" extensions="STRM"/>
<property ID="0x9D1A67A8" type="asset" extensions="CSNG"/>
<property ID="0x90AA341F" type="float">
<default>0.0</default>
</property>