diff options
author | Francois Ferrand <thetypz@gmail.com> | 2012-11-30 16:15:07 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@digia.com> | 2012-12-14 10:22:41 +0100 |
commit | 058d2e8cb54d1f5e5c409182ae72fbbd1c96c3cc (patch) | |
tree | c5119a151f0c0ee0eb21b17f0ff07cc9dd1f5e01 | |
parent | a8a33b9a3b2bab7660a53919bfbfa011e31755d3 (diff) | |
download | qt-creator-058d2e8cb54d1f5e5c409182ae72fbbd1c96c3cc.tar.gz |
Support preserving case when replacing.
When making a case insensitive search, try to keep the string capitalization when doing
the replace:
- All upper-case matches are replaced with the upper-case new text.
- All lower-case matches are replaced with the lower-case new text.
- Capitalized matches are replace with the capitalized new text.
- Other matches are replaced with the new text as provided.
Note: this does not work with regexp replace, only plain text.
Change-Id: I87cbc28eb64688bdf3c8c6ec173fcb22f91abcd0
Reviewed-by: Cristian Tibirna <tibirna@kde.org>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@digia.com>
Reviewed-by: Eike Ziller <eike.ziller@digia.com>
23 files changed, 256 insertions, 32 deletions
diff --git a/doc/src/editors/creator-editors.qdoc b/doc/src/editors/creator-editors.qdoc index cf217ae9b6..34a340e5b6 100644 --- a/doc/src/editors/creator-editors.qdoc +++ b/doc/src/editors/creator-editors.qdoc @@ -1172,6 +1172,28 @@ \endlist + The \gui{Preserve Case when Replacing} option can be selected to preserve + the case of the original text when replacing. This option is not compatible + with the \gui {Regular Expressions} search option, and will thus be + disabled when regular expressions are used. When the option is used, the + case of the occurrence will be conserved, according to the following rules: + + \list + + \o All upper-case occurrences are replaced with the upper-case new text. + + \o All lower-case occurrences are replaced with the lower-case new text. + + \o Capitalized occurrences are replaced with the capitalized new text. + + \o Other occurrences are replaced with the new text as entered. + + \o If an occurrence and the new text have the same prefix or suffix, + then the case of the prefix and/or suffix are preserved, and the + other rules are applied on the rest of the occurrence only. + + \endlist + \section1 Advanced Search To search through projects, files on a file system or the currently open diff --git a/src/libs/utils/filesearch.cpp b/src/libs/utils/filesearch.cpp index 8af9484cf7..dc5692666f 100644 --- a/src/libs/utils/filesearch.cpp +++ b/src/libs/utils/filesearch.cpp @@ -348,6 +348,72 @@ QString Utils::expandRegExpReplacement(const QString &replaceText, const QString return result; } +namespace Utils { +namespace Internal { +QString matchCaseReplacement(const QString &originalText, const QString &replaceText) +{ + //Now proceed with actual case matching + bool firstIsUpperCase = originalText.at(0).isUpper(); + bool firstIsLowerCase = originalText.at(0).isLower(); + bool restIsLowerCase = true; // to be verified + bool restIsUpperCase = true; // to be verified + + for (int i = 1; i < originalText.length(); ++i) { + if (originalText.at(i).isUpper()) + restIsLowerCase = false; + else if (originalText.at(i).isLower()) + restIsUpperCase = false; + + if (!restIsLowerCase && !restIsUpperCase) + return replaceText; // mixed + } + + if (restIsLowerCase) { + QString res = replaceText.toLower(); + if (firstIsUpperCase) + res.replace(0, 1, res.at(0).toUpper()); + return res; + } + + if (restIsUpperCase) { + QString res = replaceText.toUpper(); + if (firstIsLowerCase) + res.replace(0, 1, res.at(0).toLower()); + return res; + } + + return replaceText; // mixed +} +} +} + +QString Utils::matchCaseReplacement(const QString &originalText, const QString &replaceText) +{ + if (originalText.isEmpty()) + return replaceText; + + //Find common prefix & suffix: these will be unaffected + const int replaceTextLen = replaceText.length(); + const int originalTextLen = originalText.length(); + + int prefixLen = 0; + for (; prefixLen <= replaceTextLen && prefixLen <= originalTextLen; prefixLen++) + if (replaceText.at(prefixLen).toLower() != originalText.at(prefixLen).toLower()) + break; + + int suffixLen = 0; + for (; suffixLen < replaceTextLen - prefixLen && suffixLen < originalTextLen - prefixLen; suffixLen++) + if (replaceText.at(replaceTextLen - 1 - suffixLen).toLower() != originalText.at(originalTextLen- 1 - suffixLen).toLower()) + break; + + //keep prefix and suffix, and do actual replacement on the 'middle' of the string + return originalText.left(prefixLen) + + Internal::matchCaseReplacement(originalText.mid(prefixLen, originalTextLen - prefixLen - suffixLen), + replaceText.mid(prefixLen, replaceTextLen - prefixLen - suffixLen)) + + originalText.right(suffixLen); + +} + // #pragma mark -- FileIterator FileIterator::FileIterator() diff --git a/src/libs/utils/filesearch.h b/src/libs/utils/filesearch.h index d390c4d7c7..4a54e1860a 100644 --- a/src/libs/utils/filesearch.h +++ b/src/libs/utils/filesearch.h @@ -118,6 +118,7 @@ QTCREATOR_UTILS_EXPORT QFuture<FileSearchResultList> findInFilesRegExp(const QSt QTextDocument::FindFlags flags, QMap<QString, QString> fileToContentsMap = QMap<QString, QString>()); QTCREATOR_UTILS_EXPORT QString expandRegExpReplacement(const QString &replaceText, const QStringList &capturedTexts); +QTCREATOR_UTILS_EXPORT QString matchCaseReplacement(const QString &originalText, const QString &replaceText); } // namespace Utils diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp index 173ed20222..c782f1a5ed 100644 --- a/src/plugins/cpptools/cppfindreferences.cpp +++ b/src/plugins/cpptools/cppfindreferences.cpp @@ -255,8 +255,8 @@ void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol, : Find::SearchResultWindow::SearchOnly, QLatin1String("CppEditor")); search->setTextToReplace(replacement); - connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), - SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>))); + connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)), + SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>,bool))); connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool))); search->setSearchAgainSupported(true); connect(search, SIGNAL(searchAgainRequested()), this, SLOT(searchAgain())); @@ -303,9 +303,10 @@ void CppFindReferences::findAll_helper(Find::SearchResult *search) } void CppFindReferences::onReplaceButtonClicked(const QString &text, - const QList<Find::SearchResultItem> &items) + const QList<Find::SearchResultItem> &items, + bool preserveCase) { - const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items); + const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items, preserveCase); if (!fileNames.isEmpty()) { _modelManager->updateSourceFiles(fileNames); Find::SearchResultWindow::instance()->hide(); diff --git a/src/plugins/cpptools/cppfindreferences.h b/src/plugins/cpptools/cppfindreferences.h index 4f5f9e19ab..d3c979106e 100644 --- a/src/plugins/cpptools/cppfindreferences.h +++ b/src/plugins/cpptools/cppfindreferences.h @@ -89,7 +89,7 @@ private Q_SLOTS: void cancel(); void setPaused(bool paused); void openEditor(const Find::SearchResultItem &item); - void onReplaceButtonClicked(const QString &text, const QList<Find::SearchResultItem> &items); + void onReplaceButtonClicked(const QString &text, const QList<Find::SearchResultItem> &items, bool preserveCase); void searchAgain(); private: diff --git a/src/plugins/find/basetextfind.cpp b/src/plugins/find/basetextfind.cpp index 6f25776c4e..4f7ff89f35 100644 --- a/src/plugins/find/basetextfind.cpp +++ b/src/plugins/find/basetextfind.cpp @@ -124,7 +124,8 @@ bool BaseTextFind::supportsReplace() const Find::FindFlags BaseTextFind::supportedFindFlags() const { return Find::FindBackward | Find::FindCaseSensitively - | Find::FindRegularExpression | Find::FindWholeWords; + | Find::FindRegularExpression | Find::FindWholeWords + | Find::FindPreserveCase; } void BaseTextFind::resetIncrementalSearch() @@ -216,12 +217,19 @@ QTextCursor BaseTextFind::replaceInternal(const QString &before, const QString & { QTextCursor cursor = textCursor(); bool usesRegExp = (findFlags & Find::FindRegularExpression); + bool preserveCase = (findFlags & Find::FindPreserveCase); QRegExp regexp(before, (findFlags & Find::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive, usesRegExp ? QRegExp::RegExp : QRegExp::FixedString); if (regexp.exactMatch(cursor.selectedText())) { - QString realAfter = usesRegExp ? Utils::expandRegExpReplacement(after, regexp.capturedTexts()) : after; + QString realAfter; + if (usesRegExp) + realAfter = Utils::expandRegExpReplacement(after, regexp.capturedTexts()); + else if (preserveCase) + realAfter = Utils::matchCaseReplacement(cursor.selectedText(), after); + else + realAfter = after; int start = cursor.selectionStart(); cursor.insertText(realAfter); if ((findFlags&Find::FindBackward) != 0) @@ -252,6 +260,7 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after, editCursor.beginEditBlock(); int count = 0; bool usesRegExp = (findFlags & Find::FindRegularExpression); + bool preserveCase = (findFlags & Find::FindPreserveCase); QRegExp regexp(before); regexp.setPatternSyntax(usesRegExp ? QRegExp::RegExp : QRegExp::FixedString); regexp.setCaseSensitivity((findFlags & Find::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); @@ -263,7 +272,14 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after, editCursor.setPosition(found.selectionStart()); editCursor.setPosition(found.selectionEnd(), QTextCursor::KeepAnchor); regexp.exactMatch(found.selectedText()); - QString realAfter = usesRegExp ? Utils::expandRegExpReplacement(after, regexp.capturedTexts()) : after; + + QString realAfter; + if (usesRegExp) + realAfter = Utils::expandRegExpReplacement(after, regexp.capturedTexts()); + else if (preserveCase) + realAfter = Utils::matchCaseReplacement(found.selectedText(), after); + else + realAfter = after; editCursor.insertText(realAfter); found = findOne(regexp, editCursor, Find::textDocumentFlagsForFindFlags(findFlags)); } diff --git a/src/plugins/find/find.qrc b/src/plugins/find/find.qrc index 52d7a810bf..0c4e128101 100644 --- a/src/plugins/find/find.qrc +++ b/src/plugins/find/find.qrc @@ -5,5 +5,6 @@ <file>images/regexp.png</file> <file>images/expand.png</file> <file>images/wrapindicator.png</file> + <file>images/preservecase.png</file> </qresource> </RCC> diff --git a/src/plugins/find/findplugin.cpp b/src/plugins/find/findplugin.cpp index f24e7d36f3..63e1c0a6bf 100644 --- a/src/plugins/find/findplugin.cpp +++ b/src/plugins/find/findplugin.cpp @@ -270,6 +270,11 @@ void FindPlugin::setRegularExpression(bool regExp) setFindFlag(Find::FindRegularExpression, regExp); } +void FindPlugin::setPreserveCase(bool preserveCase) +{ + setFindFlag(Find::FindPreserveCase, preserveCase); +} + void FindPlugin::setFindFlag(Find::FindFlag flag, bool enabled) { bool hasFlag = hasFindFlag(flag); @@ -296,6 +301,7 @@ void FindPlugin::writeSettings() settings->setValue(QLatin1String("CaseSensitively"), hasFindFlag(Find::FindCaseSensitively)); settings->setValue(QLatin1String("WholeWords"), hasFindFlag(Find::FindWholeWords)); settings->setValue(QLatin1String("RegularExpression"), hasFindFlag(Find::FindRegularExpression)); + settings->setValue(QLatin1String("PreserveCase"), hasFindFlag(Find::FindPreserveCase)); settings->setValue(QLatin1String("FindStrings"), d->m_findCompletions); settings->setValue(QLatin1String("ReplaceStrings"), d->m_replaceCompletions); settings->endGroup(); @@ -312,6 +318,7 @@ void FindPlugin::readSettings() setCaseSensitive(settings->value(QLatin1String("CaseSensitively"), false).toBool()); setWholeWord(settings->value(QLatin1String("WholeWords"), false).toBool()); setRegularExpression(settings->value(QLatin1String("RegularExpression"), false).toBool()); + setPreserveCase(settings->value(QLatin1String("PreserveCase"), false).toBool()); blockSignals(block); d->m_findCompletions = settings->value(QLatin1String("FindStrings")).toStringList(); d->m_replaceCompletions = settings->value(QLatin1String("ReplaceStrings")).toStringList(); diff --git a/src/plugins/find/findplugin.h b/src/plugins/find/findplugin.h index dc80fd83f1..b4b84a3e9f 100644 --- a/src/plugins/find/findplugin.h +++ b/src/plugins/find/findplugin.h @@ -83,6 +83,7 @@ public slots: void setWholeWord(bool wholeOnly); void setBackward(bool backward); void setRegularExpression(bool regExp); + void setPreserveCase(bool preserveCase); signals: void findFlagsChanged(); diff --git a/src/plugins/find/findtoolbar.cpp b/src/plugins/find/findtoolbar.cpp index 47a72db22f..543e92a732 100644 --- a/src/plugins/find/findtoolbar.cpp +++ b/src/plugins/find/findtoolbar.cpp @@ -250,6 +250,15 @@ FindToolBar::FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumen connect(m_regularExpressionAction, SIGNAL(triggered(bool)), this, SLOT(setRegularExpressions(bool))); lineEditMenu->addAction(m_regularExpressionAction); + m_preserveCaseAction = new QAction(tr("Preserve Case when Replacing"), this); + m_preserveCaseAction->setIcon(QPixmap(QLatin1String(":/find/images/preservecase.png"))); + m_preserveCaseAction->setCheckable(true); + m_preserveCaseAction->setChecked(false); + cmd = Core::ActionManager::registerAction(m_preserveCaseAction, Constants::PRESERVE_CASE, globalcontext); + mfind->addAction(cmd, Constants::G_FIND_FLAGS); + connect(m_preserveCaseAction, SIGNAL(triggered(bool)), this, SLOT(setPreserveCase(bool))); + lineEditMenu->addAction(m_preserveCaseAction); + connect(m_currentDocumentFind, SIGNAL(candidateChanged()), this, SLOT(adaptToCandidate())); connect(m_currentDocumentFind, SIGNAL(changed()), this, SLOT(updateToolBar())); updateToolBar(); @@ -357,6 +366,7 @@ void FindToolBar::updateToolBar() m_caseSensitiveAction->setEnabled(enabled); m_wholeWordAction->setEnabled(enabled); m_regularExpressionAction->setEnabled(enabled); + m_preserveCaseAction->setEnabled(replaceEnabled && !hasFindFlag(Find::FindRegularExpression)); if (QApplication::clipboard()->supportsFindBuffer()) m_enterFindStringAction->setEnabled(enabled); bool replaceFocus = m_ui.replaceEdit->hasFocus(); @@ -549,7 +559,8 @@ void FindToolBar::updateIcons() bool casesensitive = effectiveFlags & Find::FindCaseSensitively; bool wholewords = effectiveFlags & Find::FindWholeWords; bool regexp = effectiveFlags & Find::FindRegularExpression; - if (!casesensitive && !wholewords && !regexp) { + bool preserveCase = effectiveFlags & Find::FindPreserveCase; + if (!casesensitive && !wholewords && !regexp && !preserveCase) { QPixmap pixmap(17, 17); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); @@ -565,10 +576,15 @@ void FindToolBar::updateIcons() Find::FindFlags FindToolBar::effectiveFindFlags() { Find::FindFlags supportedFlags; - if (m_currentDocumentFind->isEnabled()) + bool supportsReplace = true; + if (m_currentDocumentFind->isEnabled()) { supportedFlags = m_currentDocumentFind->supportedFindFlags(); - else + supportsReplace = m_currentDocumentFind->supportsReplace(); + } else { supportedFlags = (Find::FindFlags)0xFFFFFF; + } + if (!supportsReplace || m_findFlags & Find::FindRegularExpression) + supportedFlags &= ~Find::FindPreserveCase; return supportedFlags & m_findFlags; } @@ -577,18 +593,23 @@ void FindToolBar::updateFlagMenus() bool wholeOnly = ((m_findFlags & Find::FindWholeWords)); bool sensitive = ((m_findFlags & Find::FindCaseSensitively)); bool regexp = ((m_findFlags & Find::FindRegularExpression)); + bool preserveCase = ((m_findFlags & Find::FindPreserveCase)); if (m_wholeWordAction->isChecked() != wholeOnly) m_wholeWordAction->setChecked(wholeOnly); if (m_caseSensitiveAction->isChecked() != sensitive) m_caseSensitiveAction->setChecked(sensitive); if (m_regularExpressionAction->isChecked() != regexp) m_regularExpressionAction->setChecked(regexp); + if (m_preserveCaseAction->isChecked() != preserveCase) + m_preserveCaseAction->setChecked(preserveCase); Find::FindFlags supportedFlags; if (m_currentDocumentFind->isEnabled()) supportedFlags = m_currentDocumentFind->supportedFindFlags(); m_wholeWordAction->setEnabled(supportedFlags & Find::FindWholeWords); m_caseSensitiveAction->setEnabled(supportedFlags & Find::FindCaseSensitively); m_regularExpressionAction->setEnabled(supportedFlags & Find::FindRegularExpression); + bool replaceEnabled = m_currentDocumentFind->isEnabled() && m_currentDocumentFind->supportsReplace(); + m_preserveCaseAction->setEnabled((supportedFlags & Find::FindPreserveCase) && !regexp && replaceEnabled); } bool FindToolBar::setFocusToCurrentFindSupport() @@ -682,6 +703,7 @@ void FindToolBar::writeSettings() settings->setValue(QLatin1String("CaseSensitively"), QVariant((m_findFlags & Find::FindCaseSensitively) != 0)); settings->setValue(QLatin1String("WholeWords"), QVariant((m_findFlags & Find::FindWholeWords) != 0)); settings->setValue(QLatin1String("RegularExpression"), QVariant((m_findFlags & Find::FindRegularExpression) != 0)); + settings->setValue(QLatin1String("PreserveCase"), QVariant((m_findFlags & Find::FindPreserveCase) != 0)); settings->endGroup(); settings->endGroup(); } @@ -700,6 +722,8 @@ void FindToolBar::readSettings() flags |= Find::FindWholeWords; if (settings->value(QLatin1String("RegularExpression"), false).toBool()) flags |= Find::FindRegularExpression; + if (settings->value(QLatin1String("PreserveCase"), false).toBool()) + flags |= Find::FindPreserveCase; settings->endGroup(); settings->endGroup(); m_findFlags = flags; @@ -744,6 +768,11 @@ void FindToolBar::setRegularExpressions(bool regexp) setFindFlag(Find::FindRegularExpression, regexp); } +void FindToolBar::setPreserveCase(bool preserveCase) +{ + setFindFlag(Find::FindPreserveCase, preserveCase); +} + void FindToolBar::setBackward(bool backward) { setFindFlag(Find::FindBackward, backward); diff --git a/src/plugins/find/findtoolbar.h b/src/plugins/find/findtoolbar.h index a53e721b39..015e15fd36 100644 --- a/src/plugins/find/findtoolbar.h +++ b/src/plugins/find/findtoolbar.h @@ -91,6 +91,7 @@ private slots: void setCaseSensitive(bool sensitive); void setWholeWord(bool wholeOnly); void setRegularExpressions(bool regexp); + void setPreserveCase(bool preserveCase); void adaptToCandidate(); @@ -132,6 +133,7 @@ private: QAction *m_caseSensitiveAction; QAction *m_wholeWordAction; QAction *m_regularExpressionAction; + QAction *m_preserveCaseAction; Find::FindFlags m_findFlags; QTimer m_findIncrementalTimer; diff --git a/src/plugins/find/ifindfilter.cpp b/src/plugins/find/ifindfilter.cpp index 9f5a778800..feda930078 100644 --- a/src/plugins/find/ifindfilter.cpp +++ b/src/plugins/find/ifindfilter.cpp @@ -225,13 +225,16 @@ QPixmap Find::IFindFilter::pixmapForFindFlags(Find::FindFlags flags) static const QPixmap casesensitiveIcon = QPixmap(QLatin1String(":/find/images/casesensitively.png")); static const QPixmap regexpIcon = QPixmap(QLatin1String(":/find/images/regexp.png")); static const QPixmap wholewordsIcon = QPixmap(QLatin1String(":/find/images/wholewords.png")); + static const QPixmap preservecaseIcon = QPixmap(QLatin1String(":/find/images/preservecase.png")); bool casesensitive = flags & Find::FindCaseSensitively; bool wholewords = flags & Find::FindWholeWords; bool regexp = flags & Find::FindRegularExpression; + bool preservecase = flags & Find::FindPreserveCase; int width = 0; if (casesensitive) width += 6; if (wholewords) width += 6; if (regexp) width += 6; + if (preservecase) width += 6; if (width > 0) --width; QPixmap pixmap(width, 17); pixmap.fill(Qt::transparent); @@ -248,6 +251,10 @@ QPixmap Find::IFindFilter::pixmapForFindFlags(Find::FindFlags flags) } if (regexp) { painter.drawPixmap(x - 6, 0, regexpIcon); + x += 6; + } + if (preservecase) { + painter.drawPixmap(x - 6, 0, preservecaseIcon); } return pixmap; } @@ -261,6 +268,8 @@ QString Find::IFindFilter::descriptionForFindFlags(Find::FindFlags flags) flagStrings.append(tr("Whole words")); if (flags & Find::FindRegularExpression) flagStrings.append(tr("Regular expressions")); + if (flags & Find::FindPreserveCase) + flagStrings.append(tr("Preserve case")); QString description = tr("Flags: %1"); if (flagStrings.isEmpty()) description = description.arg(tr("None")); diff --git a/src/plugins/find/images/preservecase.png b/src/plugins/find/images/preservecase.png Binary files differnew file mode 100644 index 0000000000..4869aabd71 --- /dev/null +++ b/src/plugins/find/images/preservecase.png diff --git a/src/plugins/find/searchresultwidget.cpp b/src/plugins/find/searchresultwidget.cpp index e61d36c614..71b1900f83 100644 --- a/src/plugins/find/searchresultwidget.cpp +++ b/src/plugins/find/searchresultwidget.cpp @@ -35,6 +35,7 @@ #include "searchresultcolor.h" #include "ifindsupport.h" +#include "findplugin.h" #include "treeviewfind.h" #include <aggregation/aggregate.h> @@ -163,6 +164,14 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) : m_replaceButton->setText(tr("Replace")); m_replaceButton->setToolButtonStyle(Qt::ToolButtonTextOnly); m_replaceButton->setEnabled(false); + m_preserveCaseCheck = new QCheckBox(topWidget); + m_preserveCaseCheck->setText(tr("Preserve case")); + m_preserveCaseCheck->setEnabled(false); + + if (FindPlugin * plugin = FindPlugin::instance()) { + m_preserveCaseCheck->setChecked(plugin->hasFindFlag(Find::FindPreserveCase)); + connect(m_preserveCaseCheck, SIGNAL(clicked(bool)), plugin, SLOT(setPreserveCase(bool))); + } m_matchesFoundLabel = new QLabel(topWidget); updateMatchesFoundLabel(); @@ -173,6 +182,7 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) : topLayout->addWidget(m_replaceLabel); topLayout->addWidget(m_replaceTextEdit); topLayout->addWidget(m_replaceButton); + topLayout->addWidget(m_preserveCaseCheck); topLayout->addStretch(2); topLayout->addWidget(m_matchesFoundLabel); topWidget->setMinimumHeight(m_cancelButton->sizeHint().height() @@ -285,6 +295,7 @@ void SearchResultWidget::setShowReplaceUI(bool visible) m_replaceLabel->setVisible(visible); m_replaceTextEdit->setVisible(visible); m_replaceButton->setVisible(visible); + m_preserveCaseCheck->setVisible(visible); m_isShowingReplaceUI = visible; } @@ -397,6 +408,7 @@ void SearchResultWidget::finishSearch(bool canceled) m_sizeWarningOverridden = false; m_replaceTextEdit->setEnabled(m_count > 0); m_replaceButton->setEnabled(m_count > 0); + m_preserveCaseCheck->setEnabled(m_count > 0); m_cancelButton->setVisible(false); m_messageWidget->setVisible(canceled); m_searchAgainButton->setVisible(m_searchAgainSupported); @@ -441,7 +453,7 @@ void SearchResultWidget::handleReplaceButton() // by pressing return in replace line edit if (m_replaceButton->isEnabled()) { m_infoBar.clear(); - emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems()); + emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(), m_preserveCaseCheck->isChecked()); } } diff --git a/src/plugins/find/searchresultwidget.h b/src/plugins/find/searchresultwidget.h index 62c4321c93..7bdce4629c 100644 --- a/src/plugins/find/searchresultwidget.h +++ b/src/plugins/find/searchresultwidget.h @@ -39,6 +39,7 @@ #include <QLineEdit> #include <QToolButton> #include <QWidget> +#include <QCheckBox> namespace Find { namespace Internal { @@ -94,7 +95,7 @@ public slots: signals: void activated(const Find::SearchResultItem &item); - void replaceButtonClicked(const QString &replaceText, const QList<Find::SearchResultItem> &checkedItems); + void replaceButtonClicked(const QString &replaceText, const QList<Find::SearchResultItem> &checkedItems, bool preserveCase); void searchAgainRequested(); void cancelled(); void paused(bool paused); @@ -132,6 +133,7 @@ private: QLineEdit *m_replaceTextEdit; QToolButton *m_replaceButton; QToolButton *m_searchAgainButton; + QCheckBox *m_preserveCaseCheck; bool m_searchAgainSupported; QWidget *m_descriptionContainer; QLabel *m_label; diff --git a/src/plugins/find/searchresultwindow.cpp b/src/plugins/find/searchresultwindow.cpp index f8e89d1929..f587698af4 100644 --- a/src/plugins/find/searchresultwindow.cpp +++ b/src/plugins/find/searchresultwindow.cpp @@ -218,7 +218,7 @@ using namespace Find::Internal; */ /*! - \fn void SearchResult::replaceButtonClicked(const QString &replaceText, const QList<Find::SearchResultItem> &checkedItems) + \fn void SearchResult::replaceButtonClicked(const QString &replaceText, const QList<Find::SearchResultItem> &checkedItems, bool preserveCase) \brief Sent when the user initiated a replace, e.g. by pressing the replace all button. @@ -614,8 +614,8 @@ SearchResult::SearchResult(SearchResultWidget *widget) { connect(widget, SIGNAL(activated(Find::SearchResultItem)), this, SIGNAL(activated(Find::SearchResultItem))); - connect(widget, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), - this, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>))); + connect(widget, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)), + this, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool))); connect(widget, SIGNAL(cancelled()), this, SIGNAL(cancelled())); connect(widget, SIGNAL(paused(bool)), diff --git a/src/plugins/find/searchresultwindow.h b/src/plugins/find/searchresultwindow.h index dc737d1391..6966c340df 100644 --- a/src/plugins/find/searchresultwindow.h +++ b/src/plugins/find/searchresultwindow.h @@ -110,7 +110,7 @@ public slots: signals: void activated(const Find::SearchResultItem &item); - void replaceButtonClicked(const QString &replaceText, const QList<Find::SearchResultItem> &checkedItems); + void replaceButtonClicked(const QString &replaceText, const QList<Find::SearchResultItem> &checkedItems, bool preserveCase); void cancelled(); void paused(bool paused); void visibilityChanged(bool visible); diff --git a/src/plugins/find/textfindconstants.h b/src/plugins/find/textfindconstants.h index 1c884a1da8..533e790383 100644 --- a/src/plugins/find/textfindconstants.h +++ b/src/plugins/find/textfindconstants.h @@ -59,6 +59,7 @@ const char REPLACE_ALL[] = "Find.ReplaceAll"; const char CASE_SENSITIVE[] = "Find.CaseSensitive"; const char WHOLE_WORDS[] = "Find.WholeWords"; const char REGULAR_EXPRESSIONS[] = "Find.RegularExpressions"; +const char PRESERVE_CASE[] = "Find.PreserveCase"; const char TASK_SEARCH[] = "Find.Task.Search"; } // namespace Constants @@ -67,7 +68,8 @@ enum FindFlag { FindBackward = 0x01, FindCaseSensitively = 0x02, FindWholeWords = 0x04, - FindRegularExpression = 0x08 + FindRegularExpression = 0x08, + FindPreserveCase = 0x10 }; Q_DECLARE_FLAGS(FindFlags, FindFlag) diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index 48bd7b6c18..d31b1af533 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -933,8 +933,8 @@ void FindReferences::displayResults(int first, int last) m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch( label, QString(), symbolName, Find::SearchResultWindow::SearchAndReplace); m_currentSearch->setTextToReplace(replacement); - connect(m_currentSearch, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), - SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>))); + connect(m_currentSearch, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)), + SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>,bool))); } connect(m_currentSearch, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem))); @@ -996,9 +996,9 @@ void FindReferences::openEditor(const Find::SearchResultItem &item) } } -void FindReferences::onReplaceButtonClicked(const QString &text, const QList<Find::SearchResultItem> &items) +void FindReferences::onReplaceButtonClicked(const QString &text, const QList<Find::SearchResultItem> &items, bool preserveCase) { - const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items); + const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items, preserveCase); // files that are opened in an editor are changed, but not saved QStringList changedOnDisk; diff --git a/src/plugins/qmljseditor/qmljsfindreferences.h b/src/plugins/qmljseditor/qmljsfindreferences.h index 625a75bbd5..d6f6f50289 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.h +++ b/src/plugins/qmljseditor/qmljsfindreferences.h @@ -86,7 +86,7 @@ private Q_SLOTS: void cancel(); void setPaused(bool paused); void openEditor(const Find::SearchResultItem &item); - void onReplaceButtonClicked(const QString &text, const QList<Find::SearchResultItem> &items); + void onReplaceButtonClicked(const QString &text, const QList<Find::SearchResultItem> &items, bool preserveCase); private: QPointer<Find::SearchResult> m_currentSearch; diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index ee87ea1757..bc64ea484d 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -131,8 +131,8 @@ void BaseFileFind::runNewSearch(const QString &txt, Find::FindFlags findFlags, search->setUserData(qVariantFromValue(parameters)); connect(search, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem))); if (searchMode == SearchResultWindow::SearchAndReplace) { - connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), - this, SLOT(doReplace(QString,QList<Find::SearchResultItem>))); + connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)), + this, SLOT(doReplace(QString,QList<Find::SearchResultItem>,bool))); } connect(search, SIGNAL(visibilityChanged(bool)), this, SLOT(hideHighlightAll(bool))); connect(search, SIGNAL(cancelled()), this, SLOT(cancel())); @@ -183,9 +183,10 @@ void BaseFileFind::replaceAll(const QString &txt, Find::FindFlags findFlags) } void BaseFileFind::doReplace(const QString &text, - const QList<Find::SearchResultItem> &items) + const QList<Find::SearchResultItem> &items, + bool preserveCase) { - QStringList files = replaceAll(text, items); + QStringList files = replaceAll(text, items, preserveCase); if (!files.isEmpty()) { Core::DocumentManager::notifyFilesChangedInternally(files); Find::SearchResultWindow::instance()->hide(); @@ -331,7 +332,8 @@ void BaseFileFind::searchAgain() } QStringList BaseFileFind::replaceAll(const QString &text, - const QList<Find::SearchResultItem> &items) + const QList<Find::SearchResultItem> &items, + bool preserveCase) { if (items.isEmpty()) return QStringList(); @@ -358,10 +360,15 @@ QStringList BaseFileFind::replaceAll(const QString &text, processed.insert(p); QString replacement; - if (item.userData.canConvert<QStringList>() && !item.userData.toStringList().isEmpty()) + if (item.userData.canConvert<QStringList>() && !item.userData.toStringList().isEmpty()) { replacement = Utils::expandRegExpReplacement(text, item.userData.toStringList()); - else + } else if (preserveCase) { + const QString originalText = (item.textMarkLength == 0) ? item.text + : item.text.mid(item.textMarkPos, item.textMarkLength); + replacement = Utils::matchCaseReplacement(originalText, text); + } else { replacement = text; + } const int start = file->position(item.lineNumber, item.textMarkPos + 1); const int end = file->position(item.lineNumber, diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index e7451f5bd8..c4d78d72f9 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -71,7 +71,8 @@ public: /* returns the list of unique files that were passed in items */ static QStringList replaceAll(const QString &txt, - const QList<Find::SearchResultItem> &items); + const QList<Find::SearchResultItem> &items, + bool preserveCase = false); protected: virtual Utils::FileIterator *files(const QStringList &nameFilters, @@ -95,7 +96,8 @@ private slots: void setPaused(bool paused); void openEditor(const Find::SearchResultItem &item); void doReplace(const QString &txt, - const QList<Find::SearchResultItem> &items); + const QList<Find::SearchResultItem> &items, + bool preserveCase); void hideHighlightAll(bool visible); void searchAgain(); diff --git a/tests/auto/filesearch/tst_filesearch.cpp b/tests/auto/filesearch/tst_filesearch.cpp index c610d6c5a9..a2d1d47882 100644 --- a/tests/auto/filesearch/tst_filesearch.cpp +++ b/tests/auto/filesearch/tst_filesearch.cpp @@ -51,6 +51,7 @@ private slots: void multipleResults(); void caseSensitive(); void caseInSensitive(); + void matchCaseReplacement(); }; namespace { @@ -99,6 +100,49 @@ void tst_FileSearch::caseInSensitive() test_helper(expectedResults, QLatin1String("CaseSensitive"), QTextDocument::FindFlags(0)); } +void tst_FileSearch::matchCaseReplacement() +{ + QCOMPARE(Utils::matchCaseReplacement("", "foobar"), QString("foobar")); //empty string + + QCOMPARE(Utils::matchCaseReplacement("testpad", "foobar"), QString("foobar")); //lower case + QCOMPARE(Utils::matchCaseReplacement("TESTPAD", "foobar"), QString("FOOBAR")); //upper case + QCOMPARE(Utils::matchCaseReplacement("Testpad", "foobar"), QString("Foobar")); //capitalized + QCOMPARE(Utils::matchCaseReplacement("tESTPAD", "foobar"), QString("fOOBAR")); //un-capitalized + QCOMPARE(Utils::matchCaseReplacement("tEsTpAd", "foobar"), QString("foobar")); //mixed case, use replacement as specified + QCOMPARE(Utils::matchCaseReplacement("TeStPaD", "foobar"), QString("foobar")); //mixed case, use replacement as specified + + QCOMPARE(Utils::matchCaseReplacement("testpad", "fooBar"), QString("foobar")); //lower case + QCOMPARE(Utils::matchCaseReplacement("TESTPAD", "fooBar"), QString("FOOBAR")); //upper case + QCOMPARE(Utils::matchCaseReplacement("Testpad", "fooBar"), QString("Foobar")); //capitalized + QCOMPARE(Utils::matchCaseReplacement("tESTPAD", "fooBar"), QString("fOOBAR")); //un-capitalized + QCOMPARE(Utils::matchCaseReplacement("tEsTpAd", "fooBar"), QString("fooBar")); //mixed case, use replacement as specified + QCOMPARE(Utils::matchCaseReplacement("TeStPaD", "fooBar"), QString("fooBar")); //mixed case, use replacement as specified + + //with common prefix + QCOMPARE(Utils::matchCaseReplacement("pReFiXtestpad", "prefixfoobar"), QString("pReFiXfoobar")); //lower case + QCOMPARE(Utils::matchCaseReplacement("pReFiXTESTPAD", "prefixfoobar"), QString("pReFiXFOOBAR")); //upper case + QCOMPARE(Utils::matchCaseReplacement("pReFiXTestpad", "prefixfoobar"), QString("pReFiXFoobar")); //capitalized + QCOMPARE(Utils::matchCaseReplacement("pReFiXtESTPAD", "prefixfoobar"), QString("pReFiXfOOBAR")); //un-capitalized + QCOMPARE(Utils::matchCaseReplacement("pReFiXtEsTpAd", "prefixfoobar"), QString("pReFiXfoobar")); //mixed case, use replacement as specified + QCOMPARE(Utils::matchCaseReplacement("pReFiXTeStPaD", "prefixfoobar"), QString("pReFiXfoobar")); //mixed case, use replacement as specified + + //with common suffix + QCOMPARE(Utils::matchCaseReplacement("testpadSuFfIx", "foobarsuffix"), QString("foobarSuFfIx")); //lower case + QCOMPARE(Utils::matchCaseReplacement("TESTPADSuFfIx", "foobarsuffix"), QString("FOOBARSuFfIx")); //upper case + QCOMPARE(Utils::matchCaseReplacement("TestpadSuFfIx", "foobarsuffix"), QString("FoobarSuFfIx")); //capitalized + QCOMPARE(Utils::matchCaseReplacement("tESTPADSuFfIx", "foobarsuffix"), QString("fOOBARSuFfIx")); //un-capitalized + QCOMPARE(Utils::matchCaseReplacement("tEsTpAdSuFfIx", "foobarsuffix"), QString("foobarSuFfIx")); //mixed case, use replacement as specified + QCOMPARE(Utils::matchCaseReplacement("TeStPaDSuFfIx", "foobarsuffix"), QString("foobarSuFfIx")); //mixed case, use replacement as specified + + //with common prefix and suffix + QCOMPARE(Utils::matchCaseReplacement("pReFiXtestpadSuFfIx", "prefixfoobarsuffix"), QString("pReFiXfoobarSuFfIx")); //lower case + QCOMPARE(Utils::matchCaseReplacement("pReFiXTESTPADSuFfIx", "prefixfoobarsuffix"), QString("pReFiXFOOBARSuFfIx")); //upper case + QCOMPARE(Utils::matchCaseReplacement("pReFiXTestpadSuFfIx", "prefixfoobarsuffix"), QString("pReFiXFoobarSuFfIx")); //capitalized + QCOMPARE(Utils::matchCaseReplacement("pReFiXtESTPADSuFfIx", "prefixfoobarsuffix"), QString("pReFiXfOOBARSuFfIx")); //un-capitalized + QCOMPARE(Utils::matchCaseReplacement("pReFiXtEsTpAdSuFfIx", "prefixfoobarsuffix"), QString("pReFiXfoobarSuFfIx")); //mixed case, use replacement as specified + QCOMPARE(Utils::matchCaseReplacement("pReFiXTeStPaDSuFfIx", "prefixfoobarsuffix"), QString("pReFiXfoobarSuFfIx")); //mixed case, use replacement as specified +} + QTEST_MAIN(tst_FileSearch) #include "tst_filesearch.moc" |