OSDN Git Service

Merge branch '2.0'
[qt-creator-jp/qt-creator-jp.git] / src / plugins / find / basetextfind.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
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.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "basetextfind.h"
31
32 #include <utils/qtcassert.h>
33 #include <utils/filesearch.h>
34
35 #include <QtGui/QTextBlock>
36 #include <QtGui/QPlainTextEdit>
37
38 using namespace Find;
39
40 BaseTextFind::BaseTextFind(QTextEdit *editor)
41     : m_editor(editor)
42     , m_findScopeVerticalBlockSelection(0)
43     , m_incrementalStartPos(-1)
44 {
45 }
46
47 BaseTextFind::BaseTextFind(QPlainTextEdit *editor)
48     : m_plaineditor(editor)
49     , m_findScopeVerticalBlockSelection(0)
50     , m_incrementalStartPos(-1)
51 {
52 }
53
54 QTextCursor BaseTextFind::textCursor() const
55 {
56     QTC_ASSERT(m_editor || m_plaineditor, return QTextCursor());
57     return m_editor ? m_editor->textCursor() : m_plaineditor->textCursor();
58
59 }
60
61 void BaseTextFind::setTextCursor(const QTextCursor& cursor)
62 {
63     QTC_ASSERT(m_editor || m_plaineditor, return);
64     m_editor ? m_editor->setTextCursor(cursor) : m_plaineditor->setTextCursor(cursor);
65 }
66
67 QTextDocument *BaseTextFind::document() const
68 {
69     QTC_ASSERT(m_editor || m_plaineditor, return 0);
70     return m_editor ? m_editor->document() : m_plaineditor->document();
71 }
72
73 bool BaseTextFind::isReadOnly() const
74 {
75     QTC_ASSERT(m_editor || m_plaineditor, return true);
76     return m_editor ? m_editor->isReadOnly() : m_plaineditor->isReadOnly();
77 }
78
79 bool BaseTextFind::supportsReplace() const
80 {
81     return !isReadOnly();
82 }
83
84 IFindSupport::FindFlags BaseTextFind::supportedFindFlags() const
85 {
86     return IFindSupport::FindBackward | IFindSupport::FindCaseSensitively
87             | IFindSupport::FindRegularExpression | IFindSupport::FindWholeWords;
88 }
89
90 void BaseTextFind::resetIncrementalSearch()
91 {
92     m_incrementalStartPos = -1;
93 }
94
95 void BaseTextFind::clearResults()
96 {
97     emit highlightAll(QString(), 0);
98 }
99
100 QString BaseTextFind::currentFindString() const
101 {
102     QTextCursor cursor = textCursor();
103     if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor())) {
104         return QString(); // multi block selection
105     }
106
107     if (cursor.hasSelection())
108         return cursor.selectedText();
109
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('_')) {
116                 s.clear();
117                 break;
118             }
119         }
120         return s;
121     }
122
123     return QString();
124 }
125
126 QString BaseTextFind::completedFindString() const
127 {
128     QTextCursor cursor = textCursor();
129     cursor.setPosition(textCursor().selectionStart());
130     cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
131     return cursor.selectedText();
132 }
133
134 IFindSupport::Result BaseTextFind::findIncremental(const QString &txt, IFindSupport::FindFlags findFlags)
135 {
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);
141     if (found)
142         emit highlightAll(txt, findFlags);
143     else
144         emit highlightAll(QString(), 0);
145     return found ? Found : NotFound;
146 }
147
148 IFindSupport::Result BaseTextFind::findStep(const QString &txt, IFindSupport::FindFlags findFlags)
149 {
150     bool found = find(txt, findFlags, textCursor());
151     if (found)
152         m_incrementalStartPos = textCursor().selectionStart();
153     return found ? Found : NotFound;
154 }
155
156 bool BaseTextFind::replaceStep(const QString &before, const QString &after,
157     IFindSupport::FindFlags findFlags)
158 {
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);
164
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);
171     }
172     return find(before, findFlags, cursor);
173 }
174
175 int BaseTextFind::replaceAll(const QString &before, const QString &after,
176     IFindSupport::FindFlags findFlags)
177 {
178     QTextCursor editCursor = textCursor();
179     if (!m_findScopeStart.isNull())
180         editCursor.setPosition(m_findScopeStart.position());
181     else
182         editCursor.movePosition(QTextCursor::Start);
183     editCursor.beginEditBlock();
184     int count = 0;
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())) {
192         ++count;
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));
199     }
200     editCursor.endEditBlock();
201     return count;
202 }
203
204 bool BaseTextFind::find(const QString &txt,
205                                IFindSupport::FindFlags findFlags,
206                                QTextCursor start)
207 {
208     if (txt.isEmpty()) {
209         setTextCursor(start);
210         return true;
211     }
212     QRegExp regexp(txt);
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));
216
217     if (!m_findScopeStart.isNull()) {
218
219         // scoped
220         if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd())) {
221             if ((findFlags&IFindSupport::FindBackward) == 0)
222                 start.setPosition(m_findScopeStart.position());
223             else
224                 start.setPosition(m_findScopeEnd.position());
225             found = findOne(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags));
226             if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd()))
227                 return false;
228         }
229     } else {
230
231         // entire document
232         if (found.isNull()) {
233             if ((findFlags&IFindSupport::FindBackward) == 0)
234                 start.movePosition(QTextCursor::Start);
235             else
236                 start.movePosition(QTextCursor::End);
237             found = findOne(regexp, start, IFindSupport::textDocumentFlagsForFindFlags(findFlags));
238             if (found.isNull()) {
239                 return false;
240             }
241         }
242     }
243     if (!found.isNull()) {
244         setTextCursor(found);
245     }
246     return true;
247 }
248
249
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
252 {
253     QTextCursor candidate = document()->find(expr, from, options);
254     if (candidate.isNull())
255         return candidate;
256
257     if (!m_findScopeVerticalBlockSelection)
258         return candidate;
259     forever {
260         if (!inScope(candidate.selectionStart(), candidate.selectionEnd()))
261             return candidate;
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)
268             return candidate;
269         candidate = document()->find(expr, candidate, options);
270     }
271     return candidate;
272 }
273
274 bool BaseTextFind::inScope(int startPosition, int endPosition) const
275 {
276     if (m_findScopeStart.isNull())
277         return true;
278     return (m_findScopeStart.position() <= startPosition
279             && m_findScopeEnd.position() >= endPosition);
280 }
281
282 void BaseTextFind::defineFindScope()
283 {
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;
289
290         int verticalBlockSelection = 0;
291         if (m_plaineditor && m_plaineditor->metaObject()->indexOfProperty("verticalBlockSelection") >= 0)
292             verticalBlockSelection = m_plaineditor->property("verticalBlockSelection").toInt();
293
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;
303         }
304         emit findScopeChanged(m_findScopeStart, m_findScopeEnd, m_findScopeVerticalBlockSelection);
305         cursor.setPosition(m_findScopeStart.position()+1);
306         setTextCursor(cursor);
307     } else {
308         clearFindScope();
309     }
310 }
311
312 void BaseTextFind::clearFindScope()
313 {
314     m_findScopeStart = QTextCursor();
315     m_findScopeEnd = QTextCursor();
316     m_findScopeVerticalBlockSelection = 0;
317     emit findScopeChanged(m_findScopeStart, m_findScopeEnd, m_findScopeVerticalBlockSelection);
318 }