diff --git a/src/plugins/coreplugin/filemanager.cpp b/src/plugins/coreplugin/filemanager.cpp index 4036c7396dc2c33674666c38a0a26c7568031ac9..24e2178307af9528750574fa7c9df2f19b80ae55 100644 --- a/src/plugins/coreplugin/filemanager.cpp +++ b/src/plugins/coreplugin/filemanager.cpp @@ -781,6 +781,11 @@ void FileManager::setFileDialogLastVisitedDirectory(const QString &directory) d->m_lastVisitedDirectory = directory; } +void FileManager::notifyFilesChangedInternally(const QStringList &files) +{ + emit filesChangedInternally(files); +} + // -------------- FileChangeBlocker FileChangeBlocker::FileChangeBlocker(const QString &fileName) diff --git a/src/plugins/coreplugin/filemanager.h b/src/plugins/coreplugin/filemanager.h index 1cc606425fb1bb44a152b5c6c3ac6589db8fff64..55dd392e40963de77db9eb881b8999c22c4523fd 100644 --- a/src/plugins/coreplugin/filemanager.h +++ b/src/plugins/coreplugin/filemanager.h @@ -106,8 +106,16 @@ public: QString projectsDirectory() const; void setProjectsDirectory(const QString &); +public slots: + /* Used to notify e.g. the code model to update the given files. Does *not* + lead to any editors to reload or any other editor manager actions. */ + void notifyFilesChangedInternally(const QStringList &files); + signals: void currentFileChanged(const QString &filePath); + /* Used to notify e.g. the code model to update the given files. Does *not* + lead to any editors to reload or any other editor manager actions. */ + void filesChangedInternally(const QStringList &files); private slots: void fileDestroyed(QObject *obj); diff --git a/src/plugins/coreplugin/vcsmanager.cpp b/src/plugins/coreplugin/vcsmanager.cpp index 500454758798bad9ccdf4a0e1b64b8842c371439..2238d94bf44b3bf3cd62fe889a953658e9360002 100644 --- a/src/plugins/coreplugin/vcsmanager.cpp +++ b/src/plugins/coreplugin/vcsmanager.cpp @@ -29,6 +29,8 @@ #include "vcsmanager.h" #include "iversioncontrol.h" +#include "icore.h" +#include "filemanager.h" #include <extensionsystem/pluginmanager.h> @@ -74,9 +76,10 @@ VCSManager::~VCSManager() void VCSManager::extensionsInitialized() { // Change signal connections + FileManager *fileManager = ICore::instance()->fileManager(); foreach (IVersionControl *versionControl, allVersionControls()) { connect(versionControl, SIGNAL(filesChanged(QStringList)), - this, SIGNAL(filesChanged(QStringList))); + fileManager, SIGNAL(filesChangedInternally(QStringList))); connect(versionControl, SIGNAL(repositoryChanged(QString)), this, SIGNAL(repositoryChanged(QString))); } diff --git a/src/plugins/coreplugin/vcsmanager.h b/src/plugins/coreplugin/vcsmanager.h index eb5b67a1b2703c1c4d00483648e92f4eb43d1b56..df8932c2b3884516ec83dd87aaa4f66577ee3cac 100644 --- a/src/plugins/coreplugin/vcsmanager.h +++ b/src/plugins/coreplugin/vcsmanager.h @@ -78,7 +78,6 @@ public: signals: void repositoryChanged(const QString &repository); - void filesChanged(const QStringList &files); private: VCSManagerPrivate *m_d; diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp index 8758a9f4b4a8e150c8768a40bd850ce337ba0767..bf01f13fad8559911aec5314ed173debc7298ed5 100644 --- a/src/plugins/cpptools/cppfindreferences.cpp +++ b/src/plugins/cpptools/cppfindreferences.cpp @@ -32,6 +32,7 @@ #include "cpptoolsconstants.h" #include <texteditor/basetexteditor.h> +#include <texteditor/basefilefind.h> #include <find/searchresultwindow.h> #include <extensionsystem/pluginmanager.h> #include <utils/filesearch.h> @@ -294,63 +295,11 @@ void CppFindReferences::onReplaceButtonClicked(const QString &text, { Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename")); - if (text.isEmpty()) - return; - - QHash<QString, QList<Find::SearchResultItem> > changes; - - foreach (const Find::SearchResultItem &item, items) - changes[item.fileName].append(item); - - Core::EditorManager *editorManager = Core::EditorManager::instance(); - - QHashIterator<QString, QList<Find::SearchResultItem> > it(changes); - while (it.hasNext()) { - it.next(); - - const QString fileName = it.key(); - const QList<Find::SearchResultItem> items = it.value(); - - const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName); - TextEditor::BaseTextEditor *textEditor = 0; - foreach (Core::IEditor *editor, editors) { - textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget()); - if (textEditor != 0) - break; - } - - if (textEditor != 0) { - QTextCursor tc = textEditor->textCursor(); - tc.beginEditBlock(); - applyChanges(textEditor->document(), text, items); - tc.endEditBlock(); - } else { - QFile file(fileName); - - if (file.open(QFile::ReadOnly)) { - QTextStream stream(&file); - // ### set the encoding - const QString plainText = stream.readAll(); - file.close(); - - QTextDocument doc; - doc.setPlainText(plainText); - - applyChanges(&doc, text, items); - - QFile newFile(fileName); - if (newFile.open(QFile::WriteOnly)) { - QTextStream stream(&newFile); - // ### set the encoding - stream << doc.toPlainText(); - } - } - } + const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items); + if (!fileNames.isEmpty()) { + _modelManager->updateSourceFiles(fileNames); + _resultWindow->hide(); } - - const QStringList fileNames = changes.keys(); - _modelManager->updateSourceFiles(fileNames); - _resultWindow->hide(); } void CppFindReferences::displayResults(int first, int last) diff --git a/src/plugins/cpptools/cpptoolsplugin.cpp b/src/plugins/cpptools/cpptoolsplugin.cpp index 5f5e0011a71801ca64a7825f54f5dc793254d060..7b758237c041230fd91551e626fb462a0d10ce67 100644 --- a/src/plugins/cpptools/cpptoolsplugin.cpp +++ b/src/plugins/cpptools/cpptoolsplugin.cpp @@ -48,6 +48,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/vcsmanager.h> +#include <coreplugin/filemanager.h> #include <cppeditor/cppeditorconstants.h> #include <QtCore/QtConcurrentRun> @@ -99,10 +100,11 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error) // Objects m_modelManager = new CppModelManager(this); Core::VCSManager *vcsManager = core->vcsManager(); + Core::FileManager *fileManager = core->fileManager(); connect(vcsManager, SIGNAL(repositoryChanged(QString)), m_modelManager, SLOT(updateModifiedSourceFiles())); - connect(vcsManager, SIGNAL(filesChanged(QStringList)), - m_modelManager, SLOT(updateModifiedSourceFiles())); + connect(fileManager, SIGNAL(filesChangedInternally(QStringList)), + m_modelManager, SLOT(updateSourceFiles(QStringList))); addAutoReleasedObject(m_modelManager); m_completion = new CppCodeCompletion(m_modelManager); diff --git a/src/plugins/find/finddialog.ui b/src/plugins/find/finddialog.ui index 479299316f6d6b68d2ed4259593a5a814f215464..a88929fee8b32665e6b5c3bcb61a0aa0995dcb54 100644 --- a/src/plugins/find/finddialog.ui +++ b/src/plugins/find/finddialog.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>426</width> + <width>454</width> <height>168</height> </rect> </property> @@ -117,6 +117,13 @@ </property> </widget> </item> + <item row="2" column="2"> + <widget class="QPushButton" name="replaceButton"> + <property name="text"> + <string>Search && Replace</string> + </property> + </widget> + </item> </layout> </item> <item> @@ -139,6 +146,7 @@ <tabstop>searchTerm</tabstop> <tabstop>searchButton</tabstop> <tabstop>closeButton</tabstop> + <tabstop>replaceButton</tabstop> <tabstop>matchCase</tabstop> <tabstop>wholeWords</tabstop> </tabstops> diff --git a/src/plugins/find/findtoolwindow.cpp b/src/plugins/find/findtoolwindow.cpp index e687b21227354ee65125a95c8c4b8277bc5e71de..873473b3cd2eb113e8e2ff3a8fce7be3e68523c2 100644 --- a/src/plugins/find/findtoolwindow.cpp +++ b/src/plugins/find/findtoolwindow.cpp @@ -46,11 +46,11 @@ FindToolWindow::FindToolWindow(FindPlugin *plugin) { m_ui.setupUi(this); connect(m_ui.closeButton, SIGNAL(clicked()), this, SLOT(reject())); - connect(m_ui.searchButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_ui.searchButton, SIGNAL(clicked()), this, SLOT(search())); + connect(m_ui.replaceButton, SIGNAL(clicked()), this, SLOT(replace())); connect(m_ui.matchCase, SIGNAL(toggled(bool)), m_plugin, SLOT(setCaseSensitive(bool))); connect(m_ui.wholeWords, SIGNAL(toggled(bool)), m_plugin, SLOT(setWholeWord(bool))); connect(m_ui.filterList, SIGNAL(activated(int)), this, SLOT(setCurrentFilter(int))); - connect(this, SIGNAL(accepted()), this, SLOT(search())); m_findCompleter->setModel(m_plugin->findCompletionModel()); m_ui.searchTerm->setCompleter(m_findCompleter); QVBoxLayout *layout = new QVBoxLayout; @@ -108,12 +108,14 @@ void FindToolWindow::setCurrentFilter(int index) if (!configWidget) continue; if (i == index) { + IFindFilter *filter = m_filters.at(i); m_ui.configWidget->layout()->addWidget(configWidget); - bool enabled = m_filters.at(i)->isEnabled(); + bool enabled = filter->isEnabled(); m_ui.matchCase->setEnabled(enabled); m_ui.wholeWords->setEnabled(enabled); m_ui.searchTerm->setEnabled(enabled); m_ui.searchButton->setEnabled(enabled); + m_ui.replaceButton->setEnabled(filter->isReplaceSupported() && enabled); configWidget->setEnabled(enabled); } else { configWidget->setParent(0); @@ -122,17 +124,38 @@ void FindToolWindow::setCurrentFilter(int index) m_currentFilter = m_filters.at(index); } -void FindToolWindow::search() +void FindToolWindow::acceptAndGetParameters(QString *term, IFindFilter **filter) { + if (filter) + *filter = 0; + accept(); m_plugin->updateFindCompletion(m_ui.searchTerm->text()); int index = m_ui.filterList->currentIndex(); - QString term = m_ui.searchTerm->text(); - if (term.isEmpty() || index < 0) + QString searchTerm = m_ui.searchTerm->text(); + if (term) + *term = searchTerm; + if (searchTerm.isEmpty() || index < 0) return; - IFindFilter *filter = m_filters.at(index); + if (filter) + *filter = m_filters.at(index); +} + +void FindToolWindow::search() +{ + QString term; + IFindFilter *filter; + acceptAndGetParameters(&term, &filter); filter->findAll(term, m_plugin->findFlags()); } +void FindToolWindow::replace() +{ + QString term; + IFindFilter *filter; + acceptAndGetParameters(&term, &filter); + filter->replaceAll(term, m_plugin->findFlags()); +} + void FindToolWindow::writeSettings() { QSettings *settings = Core::ICore::instance()->settings(); diff --git a/src/plugins/find/findtoolwindow.h b/src/plugins/find/findtoolwindow.h index a187efa73004b63d4224c614faaf4daadbe3f838..57dc15b4e345ac0f6876ad27776209e6433c5a58 100644 --- a/src/plugins/find/findtoolwindow.h +++ b/src/plugins/find/findtoolwindow.h @@ -59,9 +59,12 @@ public: private slots: void search(); + void replace(); void setCurrentFilter(int index); private: + void acceptAndGetParameters(QString *term, IFindFilter **filter); + Ui::FindDialog m_ui; FindPlugin *m_plugin; QList<IFindFilter *> m_filters; diff --git a/src/plugins/find/ifindfilter.h b/src/plugins/find/ifindfilter.h index 9878835b8988bf5fd79fc9eb36aca92714c85b64..e9a38511df3fcc138e639067943ec47e1dd28d67 100644 --- a/src/plugins/find/ifindfilter.h +++ b/src/plugins/find/ifindfilter.h @@ -53,7 +53,11 @@ public: virtual QString name() const = 0; virtual bool isEnabled() const = 0; virtual QKeySequence defaultShortcut() const = 0; + virtual bool isReplaceSupported() const { return false; } + virtual void findAll(const QString &txt, QTextDocument::FindFlags findFlags) = 0; + virtual void replaceAll(const QString &txt, QTextDocument::FindFlags findFlags) + { Q_UNUSED(txt) Q_UNUSED(findFlags) } virtual QWidget *createConfigWidget() { return 0; } virtual void writeSettings(QSettings *settings) { Q_UNUSED(settings) } diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index b4379631607f943ac7d371b427362f4b98cbd2e2..385cfe32581bc80f8350907bece0ba042b2b4624 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -32,6 +32,7 @@ #include <coreplugin/icore.h> #include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/filemanager.h> #include <find/textfindconstants.h> #include <find/searchresultwindow.h> #include <texteditor/itexteditor.h> @@ -106,6 +107,41 @@ void BaseFileFind::findAll(const QString &txt, QTextDocument::FindFlags findFlag connect(progress, SIGNAL(clicked()), m_resultWindow, SLOT(popup())); } +void BaseFileFind::replaceAll(const QString &txt, QTextDocument::FindFlags findFlags) +{ + m_isSearching = true; + emit changed(); + if (m_filterCombo) + updateComboEntries(m_filterCombo, false); + m_watcher.setFuture(QFuture<FileSearchResult>()); + SearchResult *result = m_resultWindow->startNewSearch(SearchResultWindow::SearchAndReplace); + connect(result, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem))); + connect(result, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), + this, SLOT(doReplace(QString,QList<Find::SearchResultItem>))); + m_resultWindow->popup(true); + if (m_useRegExp) + m_watcher.setFuture(Utils::findInFilesRegExp(txt, files(), findFlags, ITextEditor::openedTextEditorsContents())); + else + m_watcher.setFuture(Utils::findInFiles(txt, files(), findFlags, ITextEditor::openedTextEditorsContents())); + Core::FutureProgress *progress = + Core::ICore::instance()->progressManager()->addTask(m_watcher.future(), + "Search", + Constants::TASK_SEARCH); + progress->setWidget(createProgressWidget()); + connect(progress, SIGNAL(clicked()), m_resultWindow, SLOT(popup())); +} + +void BaseFileFind::doReplace(const QString &text, + const QList<Find::SearchResultItem> &items) +{ + QStringList files = replaceAll(text, items); + Core::FileManager *fileManager = Core::ICore::instance()->fileManager(); + if (!files.isEmpty()) { + fileManager->notifyFilesChangedInternally(files); + m_resultWindow->hide(); + } +} + void BaseFileFind::displayResult(int index) { Utils::FileSearchResult result = m_watcher.future().resultAt(index); m_resultWindow->addResult(result.fileName, @@ -237,3 +273,96 @@ void BaseFileFind::openEditor(const Find::SearchResultItem &item) { TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.searchTermStart); } + +#pragma mark Static methods + +static void applyChanges(QTextDocument *doc, const QString &text, const QList<Find::SearchResultItem> &items) +{ + QList<QTextCursor> cursors; + + foreach (const Find::SearchResultItem &item, items) { + const int blockNumber = item.lineNumber - 1; + QTextCursor tc(doc->findBlockByNumber(blockNumber)); + + const int cursorPosition = tc.position() + item.searchTermStart; + + int cursorIndex = 0; + for (; cursorIndex < cursors.size(); ++cursorIndex) { + const QTextCursor &tc = cursors.at(cursorIndex); + + if (tc.position() == cursorPosition) + break; + } + + if (cursorIndex != cursors.size()) + continue; // skip this change. + + tc.setPosition(cursorPosition); + tc.setPosition(tc.position() + item.searchTermLength, + QTextCursor::KeepAnchor); + cursors.append(tc); + } + + foreach (QTextCursor tc, cursors) + tc.insertText(text); +} + +QStringList BaseFileFind::replaceAll(const QString &text, + const QList<Find::SearchResultItem> &items) +{ + if (text.isEmpty() || items.isEmpty()) + return QStringList(); + + QHash<QString, QList<Find::SearchResultItem> > changes; + + foreach (const Find::SearchResultItem &item, items) + changes[item.fileName].append(item); + + Core::EditorManager *editorManager = Core::EditorManager::instance(); + + QHashIterator<QString, QList<Find::SearchResultItem> > it(changes); + while (it.hasNext()) { + it.next(); + + const QString fileName = it.key(); + const QList<Find::SearchResultItem> items = it.value(); + + const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName); + TextEditor::BaseTextEditor *textEditor = 0; + foreach (Core::IEditor *editor, editors) { + textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget()); + if (textEditor != 0) + break; + } + + if (textEditor != 0) { + QTextCursor tc = textEditor->textCursor(); + tc.beginEditBlock(); + applyChanges(textEditor->document(), text, items); + tc.endEditBlock(); + } else { + QFile file(fileName); + + if (file.open(QFile::ReadOnly)) { + QTextStream stream(&file); + // ### set the encoding + const QString plainText = stream.readAll(); + file.close(); + + QTextDocument doc; + doc.setPlainText(plainText); + + applyChanges(&doc, text, items); + + QFile newFile(fileName); + if (newFile.open(QFile::WriteOnly)) { + QTextStream stream(&newFile); + // ### set the encoding + stream << doc.toPlainText(); + } + } + } + } + + return changes.keys(); +} diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index c31858d2409932b6bd9ea8fee0c1d7395cb72eef..3da9ca3c2c9911b38547b82841d5bc987bdcaba0 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -62,8 +62,13 @@ public: explicit BaseFileFind(Find::SearchResultWindow *resultWindow); bool isEnabled() const; + bool isReplaceSupported() const { return true; } void findAll(const QString &txt, QTextDocument::FindFlags findFlags); + void replaceAll(const QString &txt, QTextDocument::FindFlags findFlags); + /* returns the list of unique files that were passed in items */ + static QStringList replaceAll(const QString &txt, + const QList<Find::SearchResultItem> &items); protected: virtual QStringList files() = 0; void writeCommonSettings(QSettings *settings); @@ -79,6 +84,8 @@ private slots: void searchFinished(); void openEditor(const Find::SearchResultItem &item); void syncRegExpSetting(bool useRegExp); + void doReplace(const QString &txt, + const QList<Find::SearchResultItem> &items); private: QWidget *createProgressWidget();