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 <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2019-08-15 16:59:12 +02:00
parent 4e45f71339
commit 087396fe00
3 changed files with 56 additions and 10 deletions

View File

@@ -59,6 +59,8 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <functional> #include <functional>
#include <tuple>
#include <vector>
using namespace Utils; using namespace Utils;
@@ -221,11 +223,53 @@ bool FlatModel::setData(const QModelIndex &index, const QVariant &value, int rol
Node *node = nodeForIndex(index); Node *node = nodeForIndex(index);
QTC_ASSERT(node, return false); QTC_ASSERT(node, return false);
std::vector<std::tuple<Node *, FilePath, FilePath>> toRename;
const Utils::FilePath orgFilePath = node->filePath(); const Utils::FilePath orgFilePath = node->filePath();
const Utils::FilePath newFilePath = orgFilePath.parentDir().pathAppended(value.toString()); 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()); // The base name of the file was changed. Go look for other files with the same base name
emit renamed(orgFilePath, newFilePath); // 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<Node *> 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<QStringList>(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; return true;
} }

View File

@@ -335,18 +335,20 @@ int ProjectTreeWidget::expandedCount(Node *node)
void ProjectTreeWidget::rowsInserted(const QModelIndex &parent, int start, int end) void ProjectTreeWidget::rowsInserted(const QModelIndex &parent, int start, int end)
{ {
if (m_delayedRename.isEmpty())
return;
Node *node = m_model->nodeForIndex(parent); Node *node = m_model->nodeForIndex(parent);
QTC_ASSERT(node, return); QTC_ASSERT(node, return);
int i = start; for (int i = start; i <= end && !m_delayedRename.isEmpty(); ++i) {
while (i <= end) {
QModelIndex idx = m_model->index(i, 0, parent); QModelIndex idx = m_model->index(i, 0, parent);
Node *n = m_model->nodeForIndex(idx); 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_view->setCurrentIndex(idx);
m_delayedRename.clear(); m_delayedRename.removeAt(renameIdx);
break;
} }
++i;
} }
} }
@@ -467,7 +469,7 @@ void ProjectTreeWidget::renamed(const FilePath &oldPath, const FilePath &newPath
if (node) if (node)
m_view->setCurrentIndex(m_model->indexForNode(node)); m_view->setCurrentIndex(m_model->indexForNode(node));
else else
m_delayedRename = newPath; m_delayedRename << newPath;
} }
} }

View File

@@ -94,7 +94,7 @@ private:
QString m_modelId; QString m_modelId;
bool m_autoSync = true; bool m_autoSync = true;
Utils::FilePath m_delayedRename; QList<Utils::FilePath> m_delayedRename;
static QList<ProjectTreeWidget *> m_projectTreeWidgets; static QList<ProjectTreeWidget *> m_projectTreeWidgets;
friend class ProjectTreeWidgetFactory; friend class ProjectTreeWidgetFactory;