1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "basetextfind.h"
32 #include <utils/qtcassert.h>
33 #include <utils/filesearch.h>
35 #include <QtGui/QTextBlock>
36 #include <QtGui/QPlainTextEdit>
40 BaseTextFind::BaseTextFind(QTextEdit *editor)
42 , m_findScopeVerticalBlockSelection(0)
43 , m_incrementalStartPos(-1)
47 BaseTextFind::BaseTextFind(QPlainTextEdit *editor)
48 : m_plaineditor(editor)
49 , m_findScopeVerticalBlockSelection(0)
50 , m_incrementalStartPos(-1)
54 QTextCursor BaseTextFind::textCursor() const
56 QTC_ASSERT(m_editor || m_plaineditor, return QTextCursor());
57 return m_editor ? m_editor->textCursor() : m_plaineditor->textCursor();
61 void BaseTextFind::setTextCursor(const QTextCursor& cursor)
63 QTC_ASSERT(m_editor || m_plaineditor, return);
64 m_editor ? m_editor->setTextCursor(cursor) : m_plaineditor->setTextCursor(cursor);
67 QTextDocument *BaseTextFind::document() const
69 QTC_ASSERT(m_editor || m_plaineditor, return 0);
70 return m_editor ? m_editor->document() : m_plaineditor->document();
73 bool BaseTextFind::isReadOnly() const
75 QTC_ASSERT(m_editor || m_plaineditor, return true);
76 return m_editor ? m_editor->isReadOnly() : m_plaineditor->isReadOnly();
79 bool BaseTextFind::supportsReplace() const
84 IFindSupport::FindFlags BaseTextFind::supportedFindFlags() const
86 return IFindSupport::FindBackward | IFindSupport::FindCaseSensitively
87 | IFindSupport::FindRegularExpression | IFindSupport::FindWholeWords;
90 void BaseTextFind::resetIncrementalSearch()
92 m_incrementalStartPos = -1;
95 void BaseTextFind::clearResults()
97 emit highlightAll(QString(), 0);
100 QString BaseTextFind::currentFindString() const
102 QTextCursor cursor = textCursor();
103 if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor())) {
104 return QString(); // multi block selection
107 if (cursor.hasSelection())
108 return cursor.selectedText();
110 if (!cursor.atBlockEnd() && !cursor.hasSelection()) {
111 cursor.movePosition(QTextCursor::StartOfWord);
112 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
113 QString s = cursor.selectedText();
114 foreach (QChar c, s) {
115 if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
126 QString BaseTextFind::completedFindString() const
128 QTextCursor cursor = textCursor();
129 cursor.setPosition(textCursor().selectionStart());
130 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
131 return cursor.selectedText();
134 IFindSupport::Result BaseTextFind::findIncremental(const QString &txt, IFindSupport::FindFlags findFlags)
136 QTextCursor cursor = textCursor();
137 if (m_incrementalStartPos < 0)
138 m_incrementalStartPos = cursor.selectionStart();
139 cursor.setPosition(m_incrementalStartPos);
140 bool found = find(txt, findFlags, cursor);
142 emit highlightAll(txt, findFlags);
144 emit highlightAll(QString(), 0);
145 return found ? Found : NotFound;
148 IFindSupport::Result BaseTextFind::findStep(const QString &txt, IFindSupport::FindFlags findFlags)
150 bool found = find(txt, findFlags, textCursor());
152 m_incrementalStartPos = textCursor().selectionStart();
153 return found ? Found : NotFound;
156 bool BaseTextFind::replaceStep(const QString &before, const QString &after,
157 IFindSupport::FindFlags findFlags)
159 QTextCursor cursor = textCursor();
160 bool usesRegExp = (findFlags & IFindSupport::FindRegularExpression);
161 QRegExp regexp(before,
162 (findFlags & IFindSupport::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive,
163 usesRegExp ? QRegExp::RegExp : QRegExp::FixedString);
165 if (regexp.exactMatch(cursor.selectedText())) {
166 QString realAfter = usesRegExp ? Utils::expandRegExpReplacement(after, regexp.capturedTexts()) : after;
167 int start = cursor.selectionStart();
168 cursor.insertText(realAfter);
169 if ((findFlags&IFindSupport::FindBackward) != 0)
170 cursor.setPosition(start);
172 return find(before, findFlags, cursor);
175 int BaseTextFind::replaceAll(const QString &before, const QString &after,
176 IFindSupport::FindFlags findFlags)
178 QTextCursor editCursor = textCursor();
179 if (!m_findScopeStart.isNull())
180 editCursor.setPosition(m_findScopeStart.position());
182 editCursor.movePosition(QTextCursor::Start);
183 editCursor.beginEditBlock();
185 bool usesRegExp = (findFlags & IFindSupport::FindRegularExpression);
186 QRegExp regexp(before);
187 regexp.setPatternSyntax(usesRegExp ? QRegExp::RegExp : QRegExp::FixedString);
188 regexp.setCaseSensitivity((findFlags & IFindSupport::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
189 QTextCursor found = findOne(regexp, editCursor, IFindSupport::textDocumentFlagsForFindFlags(findFlags));
190 while (!found.isNull() && found.selectionStart() < found.selectionEnd()
191 && inScope(found.selectionStart(), found.selectionEnd())) {
193 editCursor.setPosition(found.selectionStart());
194 editCursor.setPosition(found.selectionEnd(), QTextCursor::KeepAnchor);
195 regexp.exactMatch(found.selectedText());
196 QString realAfter = usesRegExp ? Utils::expandRegExpReplacement(after, regexp.capturedTexts()) : after;
197 editCursor.insertText(realAfter);
198 found = findOne(regexp, editCursor, IFindSupport::textDocumentFlagsForFindFlags(findFlags));
200 editCursor.endEditBlock();
204 bool BaseTextFind::find(const QString &txt,
205 IFindSupport::FindFlags findFlags,
209 setTextCursor(start);
213 regexp.setPatternSyntax((findFlags&IFindSupport::FindRegularExpression) ? QRegExp::RegExp : QRegExp::FixedString);
214 regexp.setCaseSensitivity((findFlags&IFindSupport::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
215 QTextCursor found = findOne(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags));
217 if (!m_findScopeStart.isNull()) {
220 if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd())) {
221 if ((findFlags&IFindSupport::FindBackward) == 0)
222 start.setPosition(m_findScopeStart.position());
224 start.setPosition(m_findScopeEnd.position());
225 found = findOne(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags));
226 if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd()))
232 if (found.isNull()) {
233 if ((findFlags&IFindSupport::FindBackward) == 0)
234 start.movePosition(QTextCursor::Start);
236 start.movePosition(QTextCursor::End);
237 found = findOne(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags));
238 if (found.isNull()) {
243 if (!found.isNull()) {
244 setTextCursor(found);
250 // helper function. Works just like QTextDocument::find() but supports vertical block selection
251 QTextCursor BaseTextFind::findOne(const QRegExp &expr, const QTextCursor &from, QTextDocument::FindFlags options) const
253 QTextCursor candidate = document()->find(expr, from, options);
254 if (candidate.isNull())
257 if (!m_findScopeVerticalBlockSelection)
260 if (!inScope(candidate.selectionStart(), candidate.selectionEnd()))
262 QTextCursor b = candidate;
263 b.setPosition(candidate.selectionStart());
264 QTextCursor e = candidate;
265 e.setPosition(candidate.selectionEnd());
266 if (b.positionInBlock() >= m_findScopeStart.positionInBlock() + 1
267 && e.positionInBlock() <= m_findScopeStart.positionInBlock() + 1 + m_findScopeVerticalBlockSelection)
269 candidate = document()->find(expr, candidate, options);
274 bool BaseTextFind::inScope(int startPosition, int endPosition) const
276 if (m_findScopeStart.isNull())
278 return (m_findScopeStart.position() <= startPosition
279 && m_findScopeEnd.position() >= endPosition);
282 void BaseTextFind::defineFindScope()
284 QTextCursor cursor = textCursor();
285 if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor())) {
286 m_findScopeStart = QTextCursor(document()->docHandle(), qMax(0, cursor.selectionStart()-1));
287 m_findScopeEnd = QTextCursor(document()->docHandle(), cursor.selectionEnd());
288 m_findScopeVerticalBlockSelection = 0;
290 int verticalBlockSelection = 0;
291 if (m_plaineditor && m_plaineditor->metaObject()->indexOfProperty("verticalBlockSelection") >= 0)
292 verticalBlockSelection = m_plaineditor->property("verticalBlockSelection").toInt();
294 if (verticalBlockSelection) {
295 QTextCursor findScopeVisualStart(document()->docHandle(), cursor.selectionStart());
296 int findScopeFromColumn = qMin(findScopeVisualStart.positionInBlock(),
297 m_findScopeEnd.positionInBlock());
298 int findScopeToColumn = findScopeFromColumn + verticalBlockSelection;
299 m_findScopeStart.setPosition(findScopeVisualStart.block().position() + findScopeFromColumn - 1);
300 m_findScopeEnd.setPosition(m_findScopeEnd.block().position()
301 + qMin(m_findScopeEnd.block().length()-1, findScopeToColumn));
302 m_findScopeVerticalBlockSelection = verticalBlockSelection;
304 emit findScopeChanged(m_findScopeStart, m_findScopeEnd, m_findScopeVerticalBlockSelection);
305 cursor.setPosition(m_findScopeStart.position()+1);
306 setTextCursor(cursor);
312 void BaseTextFind::clearFindScope()
314 m_findScopeStart = QTextCursor();
315 m_findScopeEnd = QTextCursor();
316 m_findScopeVerticalBlockSelection = 0;
317 emit findScopeChanged(m_findScopeStart, m_findScopeEnd, m_findScopeVerticalBlockSelection);