From 087396fe002462c236a6c76a20803b63183fe77d Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 15 Aug 2019 16:59:12 +0200 Subject: [PATCH] ProjectExplorer: Support bulk renamings in project tree If a user changes the name of a file abc.cpp to def.cpp, and there is a file abc.h in the same directory, it's likely that the user wants to rename that file to def.h. Detect such circumstances and offer the user to automatically rename the sibling files. Fixes: QTCREATORBUG-21738 Change-Id: Ib3ece08698a3341ef4087066d2289048f6b0fa61 Reviewed-by: hjk --- src/plugins/projectexplorer/projectmodels.cpp | 48 ++++++++++++++++++- .../projectexplorer/projecttreewidget.cpp | 16 ++++--- .../projectexplorer/projecttreewidget.h | 2 +- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index 3e99eba356e..015c260fdb2 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -59,6 +59,8 @@ #include #include +#include +#include using namespace Utils; @@ -221,11 +223,53 @@ bool FlatModel::setData(const QModelIndex &index, const QVariant &value, int rol Node *node = nodeForIndex(index); QTC_ASSERT(node, return false); + std::vector> toRename; const Utils::FilePath orgFilePath = node->filePath(); const Utils::FilePath newFilePath = orgFilePath.parentDir().pathAppended(value.toString()); + const QFileInfo orgFileInfo = orgFilePath.toFileInfo(); + toRename.emplace_back(std::make_tuple(node, orgFilePath, newFilePath)); - ProjectExplorerPlugin::renameFile(node, newFilePath.toString()); - emit renamed(orgFilePath, newFilePath); + // The base name of the file was changed. Go look for other files with the same base name + // and offer to rename them as well. + if (orgFilePath != newFilePath && orgFileInfo.suffix() == newFilePath.toFileInfo().suffix()) { + ProjectNode *productNode = node->parentProjectNode(); + while (productNode && !productNode->isProduct()) + productNode = productNode->parentProjectNode(); + if (productNode) { + const auto filter = [&orgFilePath, &orgFileInfo](const Node *n) { + return n->asFileNode() + && n->filePath().toFileInfo().dir() == orgFileInfo.dir() + && n->filePath().toFileInfo().completeBaseName() + == orgFileInfo.completeBaseName() + && n->filePath() != orgFilePath; + }; + const QList candidateNodes = productNode->findNodes(filter); + if (!candidateNodes.isEmpty()) { + const QMessageBox::StandardButton reply = QMessageBox::question( + Core::ICore::mainWindow(), tr("Rename More Files?"), + tr("Would you like to rename these files as well?\n %1") + .arg(transform(candidateNodes, [](const Node *n) { + return n->filePath().toFileInfo().fileName(); + }).join("\n "))); + if (reply == QMessageBox::Yes) { + for (Node * const n : candidateNodes) { + QString targetFilePath = orgFileInfo.absolutePath() + '/' + + newFilePath.toFileInfo().completeBaseName(); + const QString suffix = n->filePath().toFileInfo().suffix(); + if (!suffix.isEmpty()) + targetFilePath.append('.').append(suffix); + toRename.emplace_back(std::make_tuple(n, n->filePath(), + FilePath::fromString(targetFilePath))); + } + } + } + } + } + + for (const auto &f : toRename) { + ProjectExplorerPlugin::renameFile(std::get<0>(f), std::get<2>(f).toString()); + emit renamed(std::get<1>(f), std::get<2>(f)); + } return true; } diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp index eb4513b5cd7..f18b21a9be2 100644 --- a/src/plugins/projectexplorer/projecttreewidget.cpp +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -335,18 +335,20 @@ int ProjectTreeWidget::expandedCount(Node *node) void ProjectTreeWidget::rowsInserted(const QModelIndex &parent, int start, int end) { + if (m_delayedRename.isEmpty()) + return; Node *node = m_model->nodeForIndex(parent); QTC_ASSERT(node, return); - int i = start; - while (i <= end) { + for (int i = start; i <= end && !m_delayedRename.isEmpty(); ++i) { QModelIndex idx = m_model->index(i, 0, parent); Node *n = m_model->nodeForIndex(idx); - if (n && n->filePath() == m_delayedRename) { + if (!n) + continue; + const int renameIdx = m_delayedRename.indexOf(n->filePath()); + if (renameIdx != -1) { m_view->setCurrentIndex(idx); - m_delayedRename.clear(); - break; + m_delayedRename.removeAt(renameIdx); } - ++i; } } @@ -467,7 +469,7 @@ void ProjectTreeWidget::renamed(const FilePath &oldPath, const FilePath &newPath if (node) m_view->setCurrentIndex(m_model->indexForNode(node)); else - m_delayedRename = newPath; + m_delayedRename << newPath; } } diff --git a/src/plugins/projectexplorer/projecttreewidget.h b/src/plugins/projectexplorer/projecttreewidget.h index c4b729f097e..74b301e93bd 100644 --- a/src/plugins/projectexplorer/projecttreewidget.h +++ b/src/plugins/projectexplorer/projecttreewidget.h @@ -94,7 +94,7 @@ private: QString m_modelId; bool m_autoSync = true; - Utils::FilePath m_delayedRename; + QList m_delayedRename; static QList m_projectTreeWidgets; friend class ProjectTreeWidgetFactory;