forked from qt-creator/qt-creator
Add "Rename" to file system view
Renames the file on disk, notifies editors, and tries to change the file in all open projects that had it (and support renaming). Task-number: QTCREATORBUG-19209 Change-Id: I165e9468c7235f9f503a3820bda3eb00f3c086d0 Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
@@ -25,17 +25,22 @@
|
||||
|
||||
#include "foldernavigationwidget.h"
|
||||
#include "projectexplorer.h"
|
||||
#include "projectexplorerconstants.h"
|
||||
#include "projectexplorericons.h"
|
||||
#include "projectnodes.h"
|
||||
#include "projecttree.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/actionmanager/command.h>
|
||||
#include <coreplugin/diffservice.h>
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/idocument.h>
|
||||
#include <coreplugin/fileiconprovider.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/editormanager/ieditor.h>
|
||||
#include <coreplugin/fileiconprovider.h>
|
||||
#include <coreplugin/fileutils.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/idocument.h>
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
@@ -44,8 +49,8 @@
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/filecrumblabel.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/navigationtreeview.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QAction>
|
||||
@@ -57,6 +62,7 @@
|
||||
#include <QFileSystemModel>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollBar>
|
||||
#include <QSize>
|
||||
#include <QTimer>
|
||||
@@ -68,6 +74,7 @@ const int ID_ROLE = Qt::UserRole + 1;
|
||||
const int SORT_ROLE = Qt::UserRole + 2;
|
||||
|
||||
const char PROJECTSDIRECTORYROOT_ID[] = "A.Projects";
|
||||
const char C_FOLDERNAVIGATIONWIDGET[] = "ProjectExplorer.FolderNavigationWidget";
|
||||
|
||||
namespace ProjectExplorer {
|
||||
namespace Internal {
|
||||
@@ -111,8 +118,10 @@ class FolderNavigationModel : public QFileSystemModel
|
||||
{
|
||||
public:
|
||||
explicit FolderNavigationModel(QObject *parent = nullptr);
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||
Qt::DropActions supportedDragActions() const;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const final;
|
||||
Qt::DropActions supportedDragActions() const final;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const final;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) final;
|
||||
};
|
||||
|
||||
FolderNavigationModel::FolderNavigationModel(QObject *parent) : QFileSystemModel(parent)
|
||||
@@ -131,6 +140,72 @@ Qt::DropActions FolderNavigationModel::supportedDragActions() const
|
||||
return Qt::MoveAction;
|
||||
}
|
||||
|
||||
Qt::ItemFlags FolderNavigationModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (index.isValid() && !fileInfo(index).isRoot())
|
||||
return QFileSystemModel::flags(index) | Qt::ItemIsEditable;
|
||||
return QFileSystemModel::flags(index);
|
||||
}
|
||||
|
||||
static QVector<FolderNode *> renamableFolderNodes(const Utils::FileName &before,
|
||||
const Utils::FileName &after)
|
||||
{
|
||||
QVector<FolderNode *> folderNodes;
|
||||
ProjectTree::forEachNode([&](Node *node) {
|
||||
if (node->nodeType() == NodeType::File && node->filePath() == before
|
||||
&& node->parentFolderNode()
|
||||
&& node->parentFolderNode()->renameFile(before.toString(), after.toString())) {
|
||||
folderNodes.append(node->parentFolderNode());
|
||||
}
|
||||
});
|
||||
return folderNodes;
|
||||
}
|
||||
|
||||
bool FolderNavigationModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
QTC_ASSERT(index.isValid() && parent(index).isValid() && index.column() == 0
|
||||
&& role == Qt::EditRole && value.canConvert<QString>(),
|
||||
return false);
|
||||
const QString afterFileName = value.toString();
|
||||
const QString beforeFilePath = filePath(index);
|
||||
const QString parentPath = filePath(parent(index));
|
||||
const QString afterFilePath = parentPath + '/' + afterFileName;
|
||||
if (beforeFilePath == afterFilePath)
|
||||
return false;
|
||||
// need to rename through file system model, which takes care of not changing our selection
|
||||
const bool success = QFileSystemModel::setData(index, value, role);
|
||||
// for files we can do more than just rename on disk, for directories the user is on his/her own
|
||||
if (success && fileInfo(index).isFile()) {
|
||||
Core::DocumentManager::renamedFile(beforeFilePath, afterFilePath);
|
||||
const QVector<FolderNode *> folderNodes
|
||||
= renamableFolderNodes(Utils::FileName::fromString(beforeFilePath),
|
||||
Utils::FileName::fromString(afterFilePath));
|
||||
QVector<FolderNode *> failedNodes;
|
||||
for (FolderNode *folder : folderNodes) {
|
||||
if (!folder->canRenameFile(beforeFilePath, afterFilePath))
|
||||
failedNodes.append(folder);
|
||||
}
|
||||
if (!failedNodes.isEmpty()) {
|
||||
const QString projects
|
||||
= Utils::transform<QList>(failedNodes,
|
||||
[](FolderNode *n) {
|
||||
return n->managingProject()->filePath().fileName();
|
||||
})
|
||||
.join(", ");
|
||||
const QString errorMessage
|
||||
= tr("The file \"%1\" was renamed to \"%2\", "
|
||||
"but the following projects could not be automatically changed: %3")
|
||||
.arg(beforeFilePath, afterFilePath, projects);
|
||||
QTimer::singleShot(0, Core::ICore::instance(), [errorMessage] {
|
||||
QMessageBox::warning(Core::ICore::dialogParent(),
|
||||
ProjectExplorerPlugin::tr("Project Editing Failed"),
|
||||
errorMessage);
|
||||
});
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static void showOnlyFirstColumn(QTreeView *view)
|
||||
{
|
||||
const int columnCount = view->header()->count();
|
||||
@@ -166,6 +241,11 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
||||
m_rootSelector(new QComboBox),
|
||||
m_crumbLabel(new DelayedFileCrumbLabel(this))
|
||||
{
|
||||
m_context = new Core::IContext(this);
|
||||
m_context->setContext(Core::Context(C_FOLDERNAVIGATIONWIDGET));
|
||||
m_context->setWidget(this);
|
||||
Core::ICore::addContextObject(m_context);
|
||||
|
||||
setBackgroundRole(QPalette::Base);
|
||||
setAutoFillBackground(true);
|
||||
m_fileSystemModel->setResolveSymlinks(false);
|
||||
@@ -179,6 +259,7 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
||||
setHiddenFilesFilter(false);
|
||||
m_listView->setIconSize(QSize(16,16));
|
||||
m_listView->setModel(m_fileSystemModel);
|
||||
m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
m_listView->setDragEnabled(true);
|
||||
m_listView->setDragDropMode(QAbstractItemView::DragOnly);
|
||||
showOnlyFirstColumn(m_listView);
|
||||
@@ -248,6 +329,11 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
||||
setAutoSynchronization(true);
|
||||
}
|
||||
|
||||
FolderNavigationWidget::~FolderNavigationWidget()
|
||||
{
|
||||
Core::ICore::removeContextObject(m_context);
|
||||
}
|
||||
|
||||
void FolderNavigationWidget::toggleAutoSynchronization()
|
||||
{
|
||||
setAutoSynchronization(!m_autoSync);
|
||||
@@ -302,6 +388,13 @@ void FolderNavigationWidget::removeRootDirectory(const QString &id)
|
||||
setCurrentEditor(Core::EditorManager::currentEditor());
|
||||
}
|
||||
|
||||
void FolderNavigationWidget::editCurrentItem()
|
||||
{
|
||||
const QModelIndex current = m_listView->currentIndex();
|
||||
if (m_fileSystemModel->flags(current) & Qt::ItemIsEditable)
|
||||
m_listView->edit(current);
|
||||
}
|
||||
|
||||
bool FolderNavigationWidget::autoSynchronization() const
|
||||
{
|
||||
return m_autoSync;
|
||||
@@ -465,8 +558,10 @@ void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
|
||||
fakeEntry.document = &document;
|
||||
Core::EditorManager::addNativeDirAndOpenWithActions(&menu, &fakeEntry);
|
||||
|
||||
if (hasCurrentItem && !isDir) {
|
||||
if (Core::DiffService::instance()) {
|
||||
if (hasCurrentItem) {
|
||||
if (m_fileSystemModel->flags(current) & Qt::ItemIsEditable)
|
||||
menu.addAction(Core::ActionManager::command(Constants::RENAMEFILE)->action());
|
||||
if (!isDir && Core::DiffService::instance()) {
|
||||
menu.addAction(
|
||||
TextEditor::TextDocument::createDiffAgainstCurrentFileAction(&menu, [filePath]() {
|
||||
return filePath;
|
||||
@@ -535,6 +630,7 @@ FolderNavigationWidgetFactory::FolderNavigationWidgetFactory()
|
||||
&Core::DocumentManager::projectsDirectoryChanged,
|
||||
this,
|
||||
&FolderNavigationWidgetFactory::updateProjectsDirectoryRoot);
|
||||
registerActions();
|
||||
}
|
||||
|
||||
Core::NavigationView FolderNavigationWidgetFactory::createWidget()
|
||||
@@ -616,6 +712,19 @@ void FolderNavigationWidgetFactory::updateProjectsDirectoryRoot()
|
||||
Utils::Icons::PROJECT.icon()});
|
||||
}
|
||||
|
||||
void FolderNavigationWidgetFactory::registerActions()
|
||||
{
|
||||
Core::Context context(C_FOLDERNAVIGATIONWIDGET);
|
||||
auto rename = new QAction(this);
|
||||
Core::ActionManager::registerAction(rename, Constants::RENAMEFILE, context);
|
||||
connect(rename, &QAction::triggered, Core::ICore::instance(), [] {
|
||||
Core::IContext *context = Core::ICore::currentContextObject();
|
||||
QWidget *widget = context ? context->widget() : nullptr;
|
||||
if (auto navWidget = qobject_cast<FolderNavigationWidget *>(widget))
|
||||
navWidget->editCurrentItem();
|
||||
});
|
||||
}
|
||||
|
||||
int DelayedFileCrumbLabel::immediateHeightForWidth(int w) const
|
||||
{
|
||||
return Utils::FileCrumbLabel::heightForWidth(w);
|
||||
|
||||
Reference in New Issue
Block a user