diff --git a/develop/managers/assetmanager/src/assetmanager.cpp b/develop/managers/assetmanager/src/assetmanager.cpp index 67d26fbe6..1cd6d3117 100644 --- a/develop/managers/assetmanager/src/assetmanager.cpp +++ b/develop/managers/assetmanager/src/assetmanager.cpp @@ -338,19 +338,61 @@ void AssetManager::renameResource(const QFileInfo &oldName, const QFileInfo &new } } +namespace +{ +// Copied from: https://forum.qt.io/topic/59245/is-there-any-api-to-recursively-copy-a-directory-and-all-it-s-sub-dirs-and-files/3 +bool copyRecursively(QString sourceFolder, QString destFolder) +{ + bool success = false; + QDir sourceDir(sourceFolder); + + if(!sourceDir.exists()) + return false; + + QDir destDir(destFolder); + if(!destDir.exists()) + destDir.mkdir(destFolder); + + QStringList files = sourceDir.entryList(QDir::Files); + for(int i = 0; i< files.count(); i++) { + QString srcName = sourceFolder + QDir::separator() + files[i]; + QString destName = destFolder + QDir::separator() + files[i]; + success = QFile::copy(srcName, destName); + if(!success) + return false; + } + + files.clear(); + files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); + for(int i = 0; i< files.count(); i++) + { + QString srcName = sourceFolder + QDir::separator() + files[i]; + QString destName = destFolder + QDir::separator() + files[i]; + success = copyRecursively(srcName, destName); + if(!success) + return false; + } + + return true; +} +} + void AssetManager::duplicateResource(const QFileInfo &source) { QDir dir(m_pProjectManager->contentPath()); QFileInfo src(m_pProjectManager->contentPath() + "/" + source.filePath()); QString name = src.baseName(); QString path = src.absolutePath() + "/"; - QString suff = "." + src.suffix(); + QString suff = !src.suffix().isEmpty() ? "." + src.suffix() : ""; findFreeName(name, path, suff); - - QFileInfo target(src.absoluteFilePath(), path + name + suff); - // Source and meta - QFile::copy(src.absoluteFilePath(), target.filePath()); - QFile::copy(src.absoluteFilePath() + gMetaExt, target.filePath() + gMetaExt); + QFileInfo target (src.absoluteFilePath(), path + name + suff); + if (src.isDir()) { + copyRecursively(src.absoluteFilePath(), target.absoluteFilePath()); + } else { + // Source and meta + QFile::copy(src.absoluteFilePath(), target.filePath()); + QFile::copy(src.absoluteFilePath() + gMetaExt, target.filePath() + gMetaExt); + } IConverterSettings *settings = createSettings(target); QString guid = settings->destination(); diff --git a/develop/models/include/baseobjectmodel.h b/develop/models/include/baseobjectmodel.h index 218884b47..3767394f0 100644 --- a/develop/models/include/baseobjectmodel.h +++ b/develop/models/include/baseobjectmodel.h @@ -19,6 +19,10 @@ class BaseObjectModel : public QAbstractItemModel { Qt::ItemFlags flags (const QModelIndex &index) const; + virtual bool removeResource (const QModelIndex &index) { return false; } + + virtual QString path (const QModelIndex &index) const { Q_UNUSED(index); return QString(); } + protected: QObject *m_rootItem; diff --git a/worldeditor/src/editors/contentbrowser/contentbrowser.cpp b/worldeditor/src/editors/contentbrowser/contentbrowser.cpp index bc70298dc..0b4105982 100644 --- a/worldeditor/src/editors/contentbrowser/contentbrowser.cpp +++ b/worldeditor/src/editors/contentbrowser/contentbrowser.cpp @@ -129,31 +129,7 @@ ContentBrowser::ContentBrowser(QWidget* parent) : connect(m_pFilterMenu, SIGNAL(triggered(QAction*)), this, SLOT(onFilterMenuTriggered(QAction*))); readSettings(); - - QString showIn(tr("Show in Explorer")); - { - QLabel *label = new QLabel(tr("Create Asset"), this); - QWidgetAction *a = new QWidgetAction(&m_CreationMenu); - a->setDefaultWidget(label); - - m_CreationMenu.addAction(tr("New Folder"))->setData(true); - m_CreationMenu.addAction(showIn, this, SLOT(showInGraphicalShell())); - m_CreationMenu.addSeparator(); - m_CreationMenu.addAction(a); - m_CreationMenu.addAction(tr("NativeBehaviour"))->setData(".cpp"); - m_CreationMenu.addAction(tr("AngelBehaviour"))->setData(".as"); - m_CreationMenu.addAction(tr("ParticleEffect"))->setData(".efx"); - m_CreationMenu.addAction(tr("Material"))->setData(".mtl"); - } - - createAction(showIn, SLOT(showInGraphicalShell())); - createAction(tr("Duplicate"), SLOT(onItemDuplicate())); - createAction(tr("Rename"), SLOT(onItemRename()), QKeySequence(Qt::Key_F2)); - createAction(tr("Delete"), SLOT(onItemDelete()), QKeySequence(Qt::Key_Delete)); - m_ContentMenu.addSeparator(); - createAction(tr("Reimport"), SLOT(onItemReimport())); - - connect(&m_CreationMenu, SIGNAL(triggered(QAction*)), this, SLOT(onCreationMenuTriggered(QAction*))); + createContextMenus(); } ContentBrowser::~ContentBrowser() { @@ -175,6 +151,40 @@ void ContentBrowser::writeSettings() { settings.setValue("content.geometry", ui->splitter->saveState()); } +void ContentBrowser::createContextMenus() { + QString showIn(tr("Show in Explorer")); + QLabel *label = new QLabel(tr("Create Asset"), this); + QWidgetAction *a = new QWidgetAction(&m_CreationMenu); + a->setDefaultWidget(label); + + m_CreationMenu.addAction(tr("New Folder"))->setData(true); + m_CreationMenu.addAction(showIn, this, SLOT(showInGraphicalShell())); + m_CreationMenu.addSeparator(); + m_CreationMenu.addAction(a); + m_CreationMenu.addAction(tr("NativeBehaviour"))->setData(".cpp"); + m_CreationMenu.addAction(tr("AngelBehaviour"))->setData(".as"); + m_CreationMenu.addAction(tr("ParticleEffect"))->setData(".efx"); + m_CreationMenu.addAction(tr("Material"))->setData(".mtl"); + + createAction(showIn, SLOT(showInGraphicalShell())); + createAction(tr("Duplicate"), SLOT(onItemDuplicate()))->setData(QVariant::fromValue(ui->contentList)); + createAction(tr("Rename"), SLOT(onItemRename()), QKeySequence(Qt::Key_F2))->setData(QVariant::fromValue(ui->contentList)); + createAction(tr("Delete"), SLOT(onItemDelete()), QKeySequence(Qt::Key_Delete))->setData(QVariant::fromValue(ui->contentList)); + m_ContentMenu.addSeparator(); + createAction(tr("Reimport"), SLOT(onItemReimport())); + + m_contentTreeMenu.addAction(tr("New Folder"))->setData(true); + m_contentTreeMenu.addAction(showIn, this, SLOT(showInGraphicalShell())); + m_contentTreeMenu.addSeparator(); + + m_contentTreeMenu.addAction(tr("Duplicate"), this, SLOT(onItemDuplicate()))->setData(QVariant::fromValue(ui->contentTree)); + m_contentTreeMenu.addAction(tr("Rename"), this, SLOT(onItemRename()))->setData(QVariant::fromValue(ui->contentTree)); + m_contentTreeMenu.addAction(tr("Delete"), this, SLOT(onItemDelete()))->setData(QVariant::fromValue(ui->contentTree)); + + connect(&m_CreationMenu, SIGNAL(triggered(QAction*)), this, SLOT(onCreationMenuTriggered(QAction*))); + connect(&m_contentTreeMenu, SIGNAL(triggered(QAction*)), this, SLOT(onCreationMenuTriggered(QAction*))); +} + void ContentBrowser::onCreationMenuTriggered(QAction *action) { QDir dir(m_pContentProxy->rootPath()); switch(action->data().type()) { @@ -217,13 +227,27 @@ void ContentBrowser::onFilterMenuTriggered(QAction *) { } void ContentBrowser::onItemRename() { - ui->contentList->edit(ui->contentList->currentIndex()); + QAction* action = qobject_cast(sender()); + if (action) + { + QAbstractItemView* view = qvariant_cast(action->data()); + view->edit(view->currentIndex()); + } } void ContentBrowser::onItemDuplicate() { - QModelIndex index = m_pContentProxy->mapToSource(ui->contentList->currentIndex()); - QString path = ContentList::instance()->path(index); - AssetManager::instance()->duplicateResource(QFileInfo(path)); + QAction* action = qobject_cast(sender()); + if (action) + { + QAbstractItemView* view = qvariant_cast(action->data()); + QSortFilterProxyModel* filter = static_cast(view->model()); + BaseObjectModel* model = static_cast(filter->sourceModel()); + + QModelIndex index = filter->mapToSource(view->currentIndex()); + QString path = model->path(index); + QFileInfo info = dynamic_cast(view) != nullptr ? QFileInfo(path).fileName() : QFileInfo(path); + AssetManager::instance()->duplicateResource(info); + } } void ContentBrowser::onItemReimport() { @@ -232,9 +256,17 @@ void ContentBrowser::onItemReimport() { } void ContentBrowser::onItemDelete() { - QMessageBox msgBox(QMessageBox::Question, tr("Delete Asset"), tr("This action cannot be reverted. Do you want to delete selected asset?"), QMessageBox::Yes | QMessageBox::No); - if(msgBox.exec() == QMessageBox::Yes) { - ContentList::instance()->removeResource(m_pContentProxy->mapToSource(ui->contentList->currentIndex())); + QAction* action = qobject_cast(sender()); + if (action) + { + QAbstractItemView* view = qvariant_cast(action->data()); + QSortFilterProxyModel* filter = static_cast(view->model()); + BaseObjectModel* model = static_cast(filter->sourceModel()); + + QMessageBox msgBox(QMessageBox::Question, tr("Delete Asset"), tr("This action cannot be reverted. Do you want to delete selected asset?"), QMessageBox::Yes | QMessageBox::No); + if (msgBox.exec() == QMessageBox::Yes) { + model->removeResource(filter->mapToSource(view->currentIndex())); + } } } @@ -266,13 +298,14 @@ void ContentBrowser::on_contentList_doubleClicked(const QModelIndex &index) { } } -void ContentBrowser::createAction(const QString &name, const char *member, const QKeySequence &shortcut) { +QAction* ContentBrowser::createAction(const QString &name, const char *member, const QKeySequence &shortcut) { QAction *a = new QAction(name, this); a->setShortcutContext(Qt::WidgetWithChildrenShortcut); a->setShortcut(shortcut); connect(a, SIGNAL(triggered(bool)), this, member); ui->contentList->addAction(a); m_ContentMenu.addAction(a); + return a; } void ContentBrowser::on_contentList_customContextMenuRequested(const QPoint &pos) { @@ -284,6 +317,14 @@ void ContentBrowser::on_contentList_customContextMenuRequested(const QPoint &pos } } +void ContentBrowser::on_contentTree_customContextMenuRequested(const QPoint &pos) { + QWidget* w = static_cast(QObject::sender()); + if (!ui->contentTree->selectionModel()->selectedIndexes().empty()) + { + m_contentTreeMenu.exec(w->mapToGlobal(pos)); + } +} + void ContentBrowser::showInGraphicalShell() { QString path; QModelIndexList list = ui->contentList->selectionModel()->selectedIndexes(); diff --git a/worldeditor/src/editors/contentbrowser/contentbrowser.h b/worldeditor/src/editors/contentbrowser/contentbrowser.h index 90ec7e340..de155cfcd 100644 --- a/worldeditor/src/editors/contentbrowser/contentbrowser.h +++ b/worldeditor/src/editors/contentbrowser/contentbrowser.h @@ -11,10 +11,10 @@ #include "converters/converter.h" -class QSortFilterProxyModel; class ContentItemDeligate; class ContentListFilter; class ContentTreeFilter; +class BaseObjectModel; namespace Ui { class ContentBrowser; @@ -57,6 +57,7 @@ class ContentBrowser : public QWidget { protected: void readSettings (); void writeSettings (); + void createContextMenus (); ContentItemDeligate *m_pContentDeligate; @@ -79,13 +80,14 @@ private slots: void on_contentTree_clicked (const QModelIndex &index); void on_contentList_doubleClicked (const QModelIndex &index); + void on_contentTree_customContextMenuRequested (const QPoint &pos); void on_contentList_customContextMenuRequested (const QPoint &pos); void on_findContent_textChanged (const QString &arg1); void showInGraphicalShell (); private: - void createAction (const QString &name, const char *member, const QKeySequence &shortcut = 0); + QAction* createAction (const QString &name, const char *member, const QKeySequence &shortcut = 0); private: Ui::ContentBrowser *ui; @@ -94,6 +96,8 @@ private slots: QMenu m_CreationMenu; + QMenu m_contentTreeMenu; + }; #endif // CONTENTBROWSER_H diff --git a/worldeditor/src/editors/contentbrowser/contentbrowser.ui b/worldeditor/src/editors/contentbrowser/contentbrowser.ui index 902a14132..54038d19f 100644 --- a/worldeditor/src/editors/contentbrowser/contentbrowser.ui +++ b/worldeditor/src/editors/contentbrowser/contentbrowser.ui @@ -38,6 +38,9 @@ Qt::CustomContextMenu + + QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + true diff --git a/worldeditor/src/editors/contentbrowser/contenttree.cpp b/worldeditor/src/editors/contentbrowser/contenttree.cpp index 8fc0b7a0f..8ce38e9c8 100644 --- a/worldeditor/src/editors/contentbrowser/contenttree.cpp +++ b/worldeditor/src/editors/contentbrowser/contenttree.cpp @@ -174,8 +174,27 @@ QVariant ContentTree::data(const QModelIndex &index, int role) const { return QVariant(); } +bool ContentTree::setData(const QModelIndex& index, const QVariant& value, int role) +{ + Q_UNUSED(role) + switch(index.column()) { + case 0: { + QDir dir(ProjectManager::instance()->contentPath()); + QObject *item = static_cast(index.internalPointer()); + QFileInfo info(item->objectName()); + QString path = (info.path() != ".") ? (info.path() + QDir::separator()) : ""; + QString suff = (!info.suffix().isEmpty()) ? ("." + info.suffix()) : ""; + QFileInfo dest(path + value.toString() + suff); + AssetManager::instance()->renameResource(dir.relativeFilePath(info.filePath()), + dir.relativeFilePath(dest.filePath())); + } break; + default: break; + } + return true; +} + Qt::ItemFlags ContentTree::flags(const QModelIndex &index) const { - return BaseObjectModel::flags(index) | Qt::ItemIsSelectable; + return BaseObjectModel::flags(index) | Qt::ItemIsSelectable | Qt::ItemIsEditable; } QString ContentTree::path(const QModelIndex &index) const { @@ -204,6 +223,22 @@ void ContentTree::onRendered(const QString &uuid) { } } +bool ContentTree::removeResource(const QModelIndex &index) { + if(index.isValid()) { + QObject *item = static_cast(index.internalPointer()); + if(item) { + QFileInfo fullName(item->objectName()); + AssetManager::instance()->removeResource(QFileInfo(fullName.fileName())); + item->setParent(nullptr); + delete item; + } + } + + emit layoutAboutToBeChanged(); + emit layoutChanged(); + return true; +} + void ContentTree::update(const QString &path) { QDir dir(ProjectManager::instance()->contentPath()); m_pContent->setObjectName(dir.absolutePath()); diff --git a/worldeditor/src/editors/contentbrowser/contenttree.h b/worldeditor/src/editors/contentbrowser/contenttree.h index 0088ce44a..6746f7654 100644 --- a/worldeditor/src/editors/contentbrowser/contenttree.h +++ b/worldeditor/src/editors/contentbrowser/contenttree.h @@ -46,10 +46,14 @@ class ContentTree : public BaseObjectModel { QVariant data (const QModelIndex &index, int role) const; + bool setData (const QModelIndex &index, const QVariant &value, int role); + Qt::ItemFlags flags (const QModelIndex &index) const; QString path (const QModelIndex &index) const; + bool removeResource (const QModelIndex &index); + public slots: void onRendered (const QString &uuid);