mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-05-28 18:21:20 +00:00
Added ability to rename resources/directories in the resource table view
This commit is contained in:
parent
932e2bff7a
commit
db277d7a15
@ -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() == '.'))
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
51
src/Editor/CFileNameValidator.h
Normal file
51
src/Editor/CFileNameValidator.h
Normal 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
|
@ -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 += \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
|
38
src/Editor/Undo/CRenameDirectoryCommand.h
Normal file
38
src/Editor/Undo/CRenameDirectoryCommand.h
Normal 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
|
40
src/Editor/Undo/CRenameResourceCommand.h
Normal file
40
src/Editor/Undo/CRenameResourceCommand.h
Normal 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
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user