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>
This commit is contained in:
Francois Ferrand
2012-11-30 16:15:07 +01:00
committed by Eike Ziller
parent a8a33b9a3b
commit 058d2e8cb5
23 changed files with 256 additions and 32 deletions

View File

@@ -1172,6 +1172,28 @@
\endlist \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 \section1 Advanced Search
To search through projects, files on a file system or the currently open To search through projects, files on a file system or the currently open

View File

@@ -348,6 +348,72 @@ QString Utils::expandRegExpReplacement(const QString &replaceText, const QString
return result; 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 // #pragma mark -- FileIterator
FileIterator::FileIterator() FileIterator::FileIterator()

View File

@@ -118,6 +118,7 @@ QTCREATOR_UTILS_EXPORT QFuture<FileSearchResultList> findInFilesRegExp(const QSt
QTextDocument::FindFlags flags, QMap<QString, QString> fileToContentsMap = QMap<QString, QString>()); 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 expandRegExpReplacement(const QString &replaceText, const QStringList &capturedTexts);
QTCREATOR_UTILS_EXPORT QString matchCaseReplacement(const QString &originalText, const QString &replaceText);
} // namespace Utils } // namespace Utils

View File

@@ -255,8 +255,8 @@ void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
: Find::SearchResultWindow::SearchOnly, : Find::SearchResultWindow::SearchOnly,
QLatin1String("CppEditor")); QLatin1String("CppEditor"));
search->setTextToReplace(replacement); search->setTextToReplace(replacement);
connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)),
SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>))); SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)));
connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool))); connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
search->setSearchAgainSupported(true); search->setSearchAgainSupported(true);
connect(search, SIGNAL(searchAgainRequested()), this, SLOT(searchAgain())); connect(search, SIGNAL(searchAgainRequested()), this, SLOT(searchAgain()));
@@ -303,9 +303,10 @@ void CppFindReferences::findAll_helper(Find::SearchResult *search)
} }
void CppFindReferences::onReplaceButtonClicked(const QString &text, 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()) { if (!fileNames.isEmpty()) {
_modelManager->updateSourceFiles(fileNames); _modelManager->updateSourceFiles(fileNames);
Find::SearchResultWindow::instance()->hide(); Find::SearchResultWindow::instance()->hide();

View File

@@ -89,7 +89,7 @@ private Q_SLOTS:
void cancel(); void cancel();
void setPaused(bool paused); void setPaused(bool paused);
void openEditor(const Find::SearchResultItem &item); 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(); void searchAgain();
private: private:

View File

@@ -124,7 +124,8 @@ bool BaseTextFind::supportsReplace() const
Find::FindFlags BaseTextFind::supportedFindFlags() const Find::FindFlags BaseTextFind::supportedFindFlags() const
{ {
return Find::FindBackward | Find::FindCaseSensitively return Find::FindBackward | Find::FindCaseSensitively
| Find::FindRegularExpression | Find::FindWholeWords; | Find::FindRegularExpression | Find::FindWholeWords
| Find::FindPreserveCase;
} }
void BaseTextFind::resetIncrementalSearch() void BaseTextFind::resetIncrementalSearch()
@@ -216,12 +217,19 @@ QTextCursor BaseTextFind::replaceInternal(const QString &before, const QString &
{ {
QTextCursor cursor = textCursor(); QTextCursor cursor = textCursor();
bool usesRegExp = (findFlags & Find::FindRegularExpression); bool usesRegExp = (findFlags & Find::FindRegularExpression);
bool preserveCase = (findFlags & Find::FindPreserveCase);
QRegExp regexp(before, QRegExp regexp(before,
(findFlags & Find::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive, (findFlags & Find::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive,
usesRegExp ? QRegExp::RegExp : QRegExp::FixedString); usesRegExp ? QRegExp::RegExp : QRegExp::FixedString);
if (regexp.exactMatch(cursor.selectedText())) { 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(); int start = cursor.selectionStart();
cursor.insertText(realAfter); cursor.insertText(realAfter);
if ((findFlags&Find::FindBackward) != 0) if ((findFlags&Find::FindBackward) != 0)
@@ -252,6 +260,7 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after,
editCursor.beginEditBlock(); editCursor.beginEditBlock();
int count = 0; int count = 0;
bool usesRegExp = (findFlags & Find::FindRegularExpression); bool usesRegExp = (findFlags & Find::FindRegularExpression);
bool preserveCase = (findFlags & Find::FindPreserveCase);
QRegExp regexp(before); QRegExp regexp(before);
regexp.setPatternSyntax(usesRegExp ? QRegExp::RegExp : QRegExp::FixedString); regexp.setPatternSyntax(usesRegExp ? QRegExp::RegExp : QRegExp::FixedString);
regexp.setCaseSensitivity((findFlags & Find::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); 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.selectionStart());
editCursor.setPosition(found.selectionEnd(), QTextCursor::KeepAnchor); editCursor.setPosition(found.selectionEnd(), QTextCursor::KeepAnchor);
regexp.exactMatch(found.selectedText()); 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); editCursor.insertText(realAfter);
found = findOne(regexp, editCursor, Find::textDocumentFlagsForFindFlags(findFlags)); found = findOne(regexp, editCursor, Find::textDocumentFlagsForFindFlags(findFlags));
} }

View File

@@ -5,5 +5,6 @@
<file>images/regexp.png</file> <file>images/regexp.png</file>
<file>images/expand.png</file> <file>images/expand.png</file>
<file>images/wrapindicator.png</file> <file>images/wrapindicator.png</file>
<file>images/preservecase.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -270,6 +270,11 @@ void FindPlugin::setRegularExpression(bool regExp)
setFindFlag(Find::FindRegularExpression, regExp); setFindFlag(Find::FindRegularExpression, regExp);
} }
void FindPlugin::setPreserveCase(bool preserveCase)
{
setFindFlag(Find::FindPreserveCase, preserveCase);
}
void FindPlugin::setFindFlag(Find::FindFlag flag, bool enabled) void FindPlugin::setFindFlag(Find::FindFlag flag, bool enabled)
{ {
bool hasFlag = hasFindFlag(flag); bool hasFlag = hasFindFlag(flag);
@@ -296,6 +301,7 @@ void FindPlugin::writeSettings()
settings->setValue(QLatin1String("CaseSensitively"), hasFindFlag(Find::FindCaseSensitively)); settings->setValue(QLatin1String("CaseSensitively"), hasFindFlag(Find::FindCaseSensitively));
settings->setValue(QLatin1String("WholeWords"), hasFindFlag(Find::FindWholeWords)); settings->setValue(QLatin1String("WholeWords"), hasFindFlag(Find::FindWholeWords));
settings->setValue(QLatin1String("RegularExpression"), hasFindFlag(Find::FindRegularExpression)); settings->setValue(QLatin1String("RegularExpression"), hasFindFlag(Find::FindRegularExpression));
settings->setValue(QLatin1String("PreserveCase"), hasFindFlag(Find::FindPreserveCase));
settings->setValue(QLatin1String("FindStrings"), d->m_findCompletions); settings->setValue(QLatin1String("FindStrings"), d->m_findCompletions);
settings->setValue(QLatin1String("ReplaceStrings"), d->m_replaceCompletions); settings->setValue(QLatin1String("ReplaceStrings"), d->m_replaceCompletions);
settings->endGroup(); settings->endGroup();
@@ -312,6 +318,7 @@ void FindPlugin::readSettings()
setCaseSensitive(settings->value(QLatin1String("CaseSensitively"), false).toBool()); setCaseSensitive(settings->value(QLatin1String("CaseSensitively"), false).toBool());
setWholeWord(settings->value(QLatin1String("WholeWords"), false).toBool()); setWholeWord(settings->value(QLatin1String("WholeWords"), false).toBool());
setRegularExpression(settings->value(QLatin1String("RegularExpression"), false).toBool()); setRegularExpression(settings->value(QLatin1String("RegularExpression"), false).toBool());
setPreserveCase(settings->value(QLatin1String("PreserveCase"), false).toBool());
blockSignals(block); blockSignals(block);
d->m_findCompletions = settings->value(QLatin1String("FindStrings")).toStringList(); d->m_findCompletions = settings->value(QLatin1String("FindStrings")).toStringList();
d->m_replaceCompletions = settings->value(QLatin1String("ReplaceStrings")).toStringList(); d->m_replaceCompletions = settings->value(QLatin1String("ReplaceStrings")).toStringList();

View File

@@ -83,6 +83,7 @@ public slots:
void setWholeWord(bool wholeOnly); void setWholeWord(bool wholeOnly);
void setBackward(bool backward); void setBackward(bool backward);
void setRegularExpression(bool regExp); void setRegularExpression(bool regExp);
void setPreserveCase(bool preserveCase);
signals: signals:
void findFlagsChanged(); void findFlagsChanged();

View File

@@ -250,6 +250,15 @@ FindToolBar::FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumen
connect(m_regularExpressionAction, SIGNAL(triggered(bool)), this, SLOT(setRegularExpressions(bool))); connect(m_regularExpressionAction, SIGNAL(triggered(bool)), this, SLOT(setRegularExpressions(bool)));
lineEditMenu->addAction(m_regularExpressionAction); 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(candidateChanged()), this, SLOT(adaptToCandidate()));
connect(m_currentDocumentFind, SIGNAL(changed()), this, SLOT(updateToolBar())); connect(m_currentDocumentFind, SIGNAL(changed()), this, SLOT(updateToolBar()));
updateToolBar(); updateToolBar();
@@ -357,6 +366,7 @@ void FindToolBar::updateToolBar()
m_caseSensitiveAction->setEnabled(enabled); m_caseSensitiveAction->setEnabled(enabled);
m_wholeWordAction->setEnabled(enabled); m_wholeWordAction->setEnabled(enabled);
m_regularExpressionAction->setEnabled(enabled); m_regularExpressionAction->setEnabled(enabled);
m_preserveCaseAction->setEnabled(replaceEnabled && !hasFindFlag(Find::FindRegularExpression));
if (QApplication::clipboard()->supportsFindBuffer()) if (QApplication::clipboard()->supportsFindBuffer())
m_enterFindStringAction->setEnabled(enabled); m_enterFindStringAction->setEnabled(enabled);
bool replaceFocus = m_ui.replaceEdit->hasFocus(); bool replaceFocus = m_ui.replaceEdit->hasFocus();
@@ -549,7 +559,8 @@ void FindToolBar::updateIcons()
bool casesensitive = effectiveFlags & Find::FindCaseSensitively; bool casesensitive = effectiveFlags & Find::FindCaseSensitively;
bool wholewords = effectiveFlags & Find::FindWholeWords; bool wholewords = effectiveFlags & Find::FindWholeWords;
bool regexp = effectiveFlags & Find::FindRegularExpression; bool regexp = effectiveFlags & Find::FindRegularExpression;
if (!casesensitive && !wholewords && !regexp) { bool preserveCase = effectiveFlags & Find::FindPreserveCase;
if (!casesensitive && !wholewords && !regexp && !preserveCase) {
QPixmap pixmap(17, 17); QPixmap pixmap(17, 17);
pixmap.fill(Qt::transparent); pixmap.fill(Qt::transparent);
QPainter painter(&pixmap); QPainter painter(&pixmap);
@@ -565,10 +576,15 @@ void FindToolBar::updateIcons()
Find::FindFlags FindToolBar::effectiveFindFlags() Find::FindFlags FindToolBar::effectiveFindFlags()
{ {
Find::FindFlags supportedFlags; Find::FindFlags supportedFlags;
if (m_currentDocumentFind->isEnabled()) bool supportsReplace = true;
if (m_currentDocumentFind->isEnabled()) {
supportedFlags = m_currentDocumentFind->supportedFindFlags(); supportedFlags = m_currentDocumentFind->supportedFindFlags();
else supportsReplace = m_currentDocumentFind->supportsReplace();
} else {
supportedFlags = (Find::FindFlags)0xFFFFFF; supportedFlags = (Find::FindFlags)0xFFFFFF;
}
if (!supportsReplace || m_findFlags & Find::FindRegularExpression)
supportedFlags &= ~Find::FindPreserveCase;
return supportedFlags & m_findFlags; return supportedFlags & m_findFlags;
} }
@@ -577,18 +593,23 @@ void FindToolBar::updateFlagMenus()
bool wholeOnly = ((m_findFlags & Find::FindWholeWords)); bool wholeOnly = ((m_findFlags & Find::FindWholeWords));
bool sensitive = ((m_findFlags & Find::FindCaseSensitively)); bool sensitive = ((m_findFlags & Find::FindCaseSensitively));
bool regexp = ((m_findFlags & Find::FindRegularExpression)); bool regexp = ((m_findFlags & Find::FindRegularExpression));
bool preserveCase = ((m_findFlags & Find::FindPreserveCase));
if (m_wholeWordAction->isChecked() != wholeOnly) if (m_wholeWordAction->isChecked() != wholeOnly)
m_wholeWordAction->setChecked(wholeOnly); m_wholeWordAction->setChecked(wholeOnly);
if (m_caseSensitiveAction->isChecked() != sensitive) if (m_caseSensitiveAction->isChecked() != sensitive)
m_caseSensitiveAction->setChecked(sensitive); m_caseSensitiveAction->setChecked(sensitive);
if (m_regularExpressionAction->isChecked() != regexp) if (m_regularExpressionAction->isChecked() != regexp)
m_regularExpressionAction->setChecked(regexp); m_regularExpressionAction->setChecked(regexp);
if (m_preserveCaseAction->isChecked() != preserveCase)
m_preserveCaseAction->setChecked(preserveCase);
Find::FindFlags supportedFlags; Find::FindFlags supportedFlags;
if (m_currentDocumentFind->isEnabled()) if (m_currentDocumentFind->isEnabled())
supportedFlags = m_currentDocumentFind->supportedFindFlags(); supportedFlags = m_currentDocumentFind->supportedFindFlags();
m_wholeWordAction->setEnabled(supportedFlags & Find::FindWholeWords); m_wholeWordAction->setEnabled(supportedFlags & Find::FindWholeWords);
m_caseSensitiveAction->setEnabled(supportedFlags & Find::FindCaseSensitively); m_caseSensitiveAction->setEnabled(supportedFlags & Find::FindCaseSensitively);
m_regularExpressionAction->setEnabled(supportedFlags & Find::FindRegularExpression); 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() bool FindToolBar::setFocusToCurrentFindSupport()
@@ -682,6 +703,7 @@ void FindToolBar::writeSettings()
settings->setValue(QLatin1String("CaseSensitively"), QVariant((m_findFlags & Find::FindCaseSensitively) != 0)); settings->setValue(QLatin1String("CaseSensitively"), QVariant((m_findFlags & Find::FindCaseSensitively) != 0));
settings->setValue(QLatin1String("WholeWords"), QVariant((m_findFlags & Find::FindWholeWords) != 0)); settings->setValue(QLatin1String("WholeWords"), QVariant((m_findFlags & Find::FindWholeWords) != 0));
settings->setValue(QLatin1String("RegularExpression"), QVariant((m_findFlags & Find::FindRegularExpression) != 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();
settings->endGroup(); settings->endGroup();
} }
@@ -700,6 +722,8 @@ void FindToolBar::readSettings()
flags |= Find::FindWholeWords; flags |= Find::FindWholeWords;
if (settings->value(QLatin1String("RegularExpression"), false).toBool()) if (settings->value(QLatin1String("RegularExpression"), false).toBool())
flags |= Find::FindRegularExpression; flags |= Find::FindRegularExpression;
if (settings->value(QLatin1String("PreserveCase"), false).toBool())
flags |= Find::FindPreserveCase;
settings->endGroup(); settings->endGroup();
settings->endGroup(); settings->endGroup();
m_findFlags = flags; m_findFlags = flags;
@@ -744,6 +768,11 @@ void FindToolBar::setRegularExpressions(bool regexp)
setFindFlag(Find::FindRegularExpression, regexp); setFindFlag(Find::FindRegularExpression, regexp);
} }
void FindToolBar::setPreserveCase(bool preserveCase)
{
setFindFlag(Find::FindPreserveCase, preserveCase);
}
void FindToolBar::setBackward(bool backward) void FindToolBar::setBackward(bool backward)
{ {
setFindFlag(Find::FindBackward, backward); setFindFlag(Find::FindBackward, backward);

View File

@@ -91,6 +91,7 @@ private slots:
void setCaseSensitive(bool sensitive); void setCaseSensitive(bool sensitive);
void setWholeWord(bool wholeOnly); void setWholeWord(bool wholeOnly);
void setRegularExpressions(bool regexp); void setRegularExpressions(bool regexp);
void setPreserveCase(bool preserveCase);
void adaptToCandidate(); void adaptToCandidate();
@@ -132,6 +133,7 @@ private:
QAction *m_caseSensitiveAction; QAction *m_caseSensitiveAction;
QAction *m_wholeWordAction; QAction *m_wholeWordAction;
QAction *m_regularExpressionAction; QAction *m_regularExpressionAction;
QAction *m_preserveCaseAction;
Find::FindFlags m_findFlags; Find::FindFlags m_findFlags;
QTimer m_findIncrementalTimer; QTimer m_findIncrementalTimer;

View File

@@ -225,13 +225,16 @@ QPixmap Find::IFindFilter::pixmapForFindFlags(Find::FindFlags flags)
static const QPixmap casesensitiveIcon = QPixmap(QLatin1String(":/find/images/casesensitively.png")); static const QPixmap casesensitiveIcon = QPixmap(QLatin1String(":/find/images/casesensitively.png"));
static const QPixmap regexpIcon = QPixmap(QLatin1String(":/find/images/regexp.png")); static const QPixmap regexpIcon = QPixmap(QLatin1String(":/find/images/regexp.png"));
static const QPixmap wholewordsIcon = QPixmap(QLatin1String(":/find/images/wholewords.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 casesensitive = flags & Find::FindCaseSensitively;
bool wholewords = flags & Find::FindWholeWords; bool wholewords = flags & Find::FindWholeWords;
bool regexp = flags & Find::FindRegularExpression; bool regexp = flags & Find::FindRegularExpression;
bool preservecase = flags & Find::FindPreserveCase;
int width = 0; int width = 0;
if (casesensitive) width += 6; if (casesensitive) width += 6;
if (wholewords) width += 6; if (wholewords) width += 6;
if (regexp) width += 6; if (regexp) width += 6;
if (preservecase) width += 6;
if (width > 0) --width; if (width > 0) --width;
QPixmap pixmap(width, 17); QPixmap pixmap(width, 17);
pixmap.fill(Qt::transparent); pixmap.fill(Qt::transparent);
@@ -248,6 +251,10 @@ QPixmap Find::IFindFilter::pixmapForFindFlags(Find::FindFlags flags)
} }
if (regexp) { if (regexp) {
painter.drawPixmap(x - 6, 0, regexpIcon); painter.drawPixmap(x - 6, 0, regexpIcon);
x += 6;
}
if (preservecase) {
painter.drawPixmap(x - 6, 0, preservecaseIcon);
} }
return pixmap; return pixmap;
} }
@@ -261,6 +268,8 @@ QString Find::IFindFilter::descriptionForFindFlags(Find::FindFlags flags)
flagStrings.append(tr("Whole words")); flagStrings.append(tr("Whole words"));
if (flags & Find::FindRegularExpression) if (flags & Find::FindRegularExpression)
flagStrings.append(tr("Regular expressions")); flagStrings.append(tr("Regular expressions"));
if (flags & Find::FindPreserveCase)
flagStrings.append(tr("Preserve case"));
QString description = tr("Flags: %1"); QString description = tr("Flags: %1");
if (flagStrings.isEmpty()) if (flagStrings.isEmpty())
description = description.arg(tr("None")); description = description.arg(tr("None"));

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

View File

@@ -35,6 +35,7 @@
#include "searchresultcolor.h" #include "searchresultcolor.h"
#include "ifindsupport.h" #include "ifindsupport.h"
#include "findplugin.h"
#include "treeviewfind.h" #include "treeviewfind.h"
#include <aggregation/aggregate.h> #include <aggregation/aggregate.h>
@@ -163,6 +164,14 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
m_replaceButton->setText(tr("Replace")); m_replaceButton->setText(tr("Replace"));
m_replaceButton->setToolButtonStyle(Qt::ToolButtonTextOnly); m_replaceButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
m_replaceButton->setEnabled(false); 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); m_matchesFoundLabel = new QLabel(topWidget);
updateMatchesFoundLabel(); updateMatchesFoundLabel();
@@ -173,6 +182,7 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
topLayout->addWidget(m_replaceLabel); topLayout->addWidget(m_replaceLabel);
topLayout->addWidget(m_replaceTextEdit); topLayout->addWidget(m_replaceTextEdit);
topLayout->addWidget(m_replaceButton); topLayout->addWidget(m_replaceButton);
topLayout->addWidget(m_preserveCaseCheck);
topLayout->addStretch(2); topLayout->addStretch(2);
topLayout->addWidget(m_matchesFoundLabel); topLayout->addWidget(m_matchesFoundLabel);
topWidget->setMinimumHeight(m_cancelButton->sizeHint().height() topWidget->setMinimumHeight(m_cancelButton->sizeHint().height()
@@ -285,6 +295,7 @@ void SearchResultWidget::setShowReplaceUI(bool visible)
m_replaceLabel->setVisible(visible); m_replaceLabel->setVisible(visible);
m_replaceTextEdit->setVisible(visible); m_replaceTextEdit->setVisible(visible);
m_replaceButton->setVisible(visible); m_replaceButton->setVisible(visible);
m_preserveCaseCheck->setVisible(visible);
m_isShowingReplaceUI = visible; m_isShowingReplaceUI = visible;
} }
@@ -397,6 +408,7 @@ void SearchResultWidget::finishSearch(bool canceled)
m_sizeWarningOverridden = false; m_sizeWarningOverridden = false;
m_replaceTextEdit->setEnabled(m_count > 0); m_replaceTextEdit->setEnabled(m_count > 0);
m_replaceButton->setEnabled(m_count > 0); m_replaceButton->setEnabled(m_count > 0);
m_preserveCaseCheck->setEnabled(m_count > 0);
m_cancelButton->setVisible(false); m_cancelButton->setVisible(false);
m_messageWidget->setVisible(canceled); m_messageWidget->setVisible(canceled);
m_searchAgainButton->setVisible(m_searchAgainSupported); m_searchAgainButton->setVisible(m_searchAgainSupported);
@@ -441,7 +453,7 @@ void SearchResultWidget::handleReplaceButton()
// by pressing return in replace line edit // by pressing return in replace line edit
if (m_replaceButton->isEnabled()) { if (m_replaceButton->isEnabled()) {
m_infoBar.clear(); m_infoBar.clear();
emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems()); emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(), m_preserveCaseCheck->isChecked());
} }
} }

View File

@@ -39,6 +39,7 @@
#include <QLineEdit> #include <QLineEdit>
#include <QToolButton> #include <QToolButton>
#include <QWidget> #include <QWidget>
#include <QCheckBox>
namespace Find { namespace Find {
namespace Internal { namespace Internal {
@@ -94,7 +95,7 @@ public slots:
signals: signals:
void activated(const Find::SearchResultItem &item); 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 searchAgainRequested();
void cancelled(); void cancelled();
void paused(bool paused); void paused(bool paused);
@@ -132,6 +133,7 @@ private:
QLineEdit *m_replaceTextEdit; QLineEdit *m_replaceTextEdit;
QToolButton *m_replaceButton; QToolButton *m_replaceButton;
QToolButton *m_searchAgainButton; QToolButton *m_searchAgainButton;
QCheckBox *m_preserveCaseCheck;
bool m_searchAgainSupported; bool m_searchAgainSupported;
QWidget *m_descriptionContainer; QWidget *m_descriptionContainer;
QLabel *m_label; QLabel *m_label;

View File

@@ -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 \brief Sent when the user initiated a replace, e.g. by pressing the replace
all button. all button.
@@ -614,8 +614,8 @@ SearchResult::SearchResult(SearchResultWidget *widget)
{ {
connect(widget, SIGNAL(activated(Find::SearchResultItem)), connect(widget, SIGNAL(activated(Find::SearchResultItem)),
this, SIGNAL(activated(Find::SearchResultItem))); this, SIGNAL(activated(Find::SearchResultItem)));
connect(widget, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), connect(widget, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)),
this, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>))); this, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)));
connect(widget, SIGNAL(cancelled()), connect(widget, SIGNAL(cancelled()),
this, SIGNAL(cancelled())); this, SIGNAL(cancelled()));
connect(widget, SIGNAL(paused(bool)), connect(widget, SIGNAL(paused(bool)),

View File

@@ -110,7 +110,7 @@ public slots:
signals: signals:
void activated(const Find::SearchResultItem &item); 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 cancelled();
void paused(bool paused); void paused(bool paused);
void visibilityChanged(bool visible); void visibilityChanged(bool visible);

View File

@@ -59,6 +59,7 @@ const char REPLACE_ALL[] = "Find.ReplaceAll";
const char CASE_SENSITIVE[] = "Find.CaseSensitive"; const char CASE_SENSITIVE[] = "Find.CaseSensitive";
const char WHOLE_WORDS[] = "Find.WholeWords"; const char WHOLE_WORDS[] = "Find.WholeWords";
const char REGULAR_EXPRESSIONS[] = "Find.RegularExpressions"; const char REGULAR_EXPRESSIONS[] = "Find.RegularExpressions";
const char PRESERVE_CASE[] = "Find.PreserveCase";
const char TASK_SEARCH[] = "Find.Task.Search"; const char TASK_SEARCH[] = "Find.Task.Search";
} // namespace Constants } // namespace Constants
@@ -67,7 +68,8 @@ enum FindFlag {
FindBackward = 0x01, FindBackward = 0x01,
FindCaseSensitively = 0x02, FindCaseSensitively = 0x02,
FindWholeWords = 0x04, FindWholeWords = 0x04,
FindRegularExpression = 0x08 FindRegularExpression = 0x08,
FindPreserveCase = 0x10
}; };
Q_DECLARE_FLAGS(FindFlags, FindFlag) Q_DECLARE_FLAGS(FindFlags, FindFlag)

View File

@@ -933,8 +933,8 @@ void FindReferences::displayResults(int first, int last)
m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch( m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(
label, QString(), symbolName, Find::SearchResultWindow::SearchAndReplace); label, QString(), symbolName, Find::SearchResultWindow::SearchAndReplace);
m_currentSearch->setTextToReplace(replacement); m_currentSearch->setTextToReplace(replacement);
connect(m_currentSearch, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), connect(m_currentSearch, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)),
SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>))); SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)));
} }
connect(m_currentSearch, SIGNAL(activated(Find::SearchResultItem)), connect(m_currentSearch, SIGNAL(activated(Find::SearchResultItem)),
this, SLOT(openEditor(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 // files that are opened in an editor are changed, but not saved
QStringList changedOnDisk; QStringList changedOnDisk;

View File

@@ -86,7 +86,7 @@ private Q_SLOTS:
void cancel(); void cancel();
void setPaused(bool paused); void setPaused(bool paused);
void openEditor(const Find::SearchResultItem &item); 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: private:
QPointer<Find::SearchResult> m_currentSearch; QPointer<Find::SearchResult> m_currentSearch;

View File

@@ -131,8 +131,8 @@ void BaseFileFind::runNewSearch(const QString &txt, Find::FindFlags findFlags,
search->setUserData(qVariantFromValue(parameters)); search->setUserData(qVariantFromValue(parameters));
connect(search, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem))); connect(search, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem)));
if (searchMode == SearchResultWindow::SearchAndReplace) { if (searchMode == SearchResultWindow::SearchAndReplace) {
connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)),
this, SLOT(doReplace(QString,QList<Find::SearchResultItem>))); this, SLOT(doReplace(QString,QList<Find::SearchResultItem>,bool)));
} }
connect(search, SIGNAL(visibilityChanged(bool)), this, SLOT(hideHighlightAll(bool))); connect(search, SIGNAL(visibilityChanged(bool)), this, SLOT(hideHighlightAll(bool)));
connect(search, SIGNAL(cancelled()), this, SLOT(cancel())); 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, 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()) { if (!files.isEmpty()) {
Core::DocumentManager::notifyFilesChangedInternally(files); Core::DocumentManager::notifyFilesChangedInternally(files);
Find::SearchResultWindow::instance()->hide(); Find::SearchResultWindow::instance()->hide();
@@ -331,7 +332,8 @@ void BaseFileFind::searchAgain()
} }
QStringList BaseFileFind::replaceAll(const QString &text, QStringList BaseFileFind::replaceAll(const QString &text,
const QList<Find::SearchResultItem> &items) const QList<Find::SearchResultItem> &items,
bool preserveCase)
{ {
if (items.isEmpty()) if (items.isEmpty())
return QStringList(); return QStringList();
@@ -358,10 +360,15 @@ QStringList BaseFileFind::replaceAll(const QString &text,
processed.insert(p); processed.insert(p);
QString replacement; 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()); 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; replacement = text;
}
const int start = file->position(item.lineNumber, item.textMarkPos + 1); const int start = file->position(item.lineNumber, item.textMarkPos + 1);
const int end = file->position(item.lineNumber, const int end = file->position(item.lineNumber,

View File

@@ -71,7 +71,8 @@ public:
/* returns the list of unique files that were passed in items */ /* returns the list of unique files that were passed in items */
static QStringList replaceAll(const QString &txt, static QStringList replaceAll(const QString &txt,
const QList<Find::SearchResultItem> &items); const QList<Find::SearchResultItem> &items,
bool preserveCase = false);
protected: protected:
virtual Utils::FileIterator *files(const QStringList &nameFilters, virtual Utils::FileIterator *files(const QStringList &nameFilters,
@@ -95,7 +96,8 @@ private slots:
void setPaused(bool paused); void setPaused(bool paused);
void openEditor(const Find::SearchResultItem &item); void openEditor(const Find::SearchResultItem &item);
void doReplace(const QString &txt, void doReplace(const QString &txt,
const QList<Find::SearchResultItem> &items); const QList<Find::SearchResultItem> &items,
bool preserveCase);
void hideHighlightAll(bool visible); void hideHighlightAll(bool visible);
void searchAgain(); void searchAgain();

View File

@@ -51,6 +51,7 @@ private slots:
void multipleResults(); void multipleResults();
void caseSensitive(); void caseSensitive();
void caseInSensitive(); void caseInSensitive();
void matchCaseReplacement();
}; };
namespace { namespace {
@@ -99,6 +100,49 @@ void tst_FileSearch::caseInSensitive()
test_helper(expectedResults, QLatin1String("CaseSensitive"), QTextDocument::FindFlags(0)); 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) QTEST_MAIN(tst_FileSearch)
#include "tst_filesearch.moc" #include "tst_filesearch.moc"