source: trunk/src/gui/text/qsyntaxhighlighter.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 21.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
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** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsyntaxhighlighter.h"
43
44#ifndef QT_NO_SYNTAXHIGHLIGHTER
45#include <private/qobject_p.h>
46#include <qtextdocument.h>
47#include <private/qtextdocument_p.h>
48#include <qtextlayout.h>
49#include <qpointer.h>
50#include <qtextobject.h>
51#include <qtextcursor.h>
52#include <qdebug.h>
53#include <qtextedit.h>
54#include <qtimer.h>
55
56QT_BEGIN_NAMESPACE
57
58class QSyntaxHighlighterPrivate : public QObjectPrivate
59{
60 Q_DECLARE_PUBLIC(QSyntaxHighlighter)
61public:
62 inline QSyntaxHighlighterPrivate()
63 : rehighlightPending(false), inReformatBlocks(false)
64 {}
65
66 QPointer<QTextDocument> doc;
67
68 void _q_reformatBlocks(int from, int charsRemoved, int charsAdded);
69 void reformatBlocks(int from, int charsRemoved, int charsAdded);
70 void reformatBlock(const QTextBlock &block);
71
72 inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) {
73 inReformatBlocks = true;
74 cursor.beginEditBlock();
75 int from = cursor.position();
76 cursor.movePosition(operation);
77 reformatBlocks(from, 0, cursor.position() - from);
78 cursor.endEditBlock();
79 inReformatBlocks = false;
80 }
81
82 inline void _q_delayedRehighlight() {
83 if (!rehighlightPending)
84 return;
85 rehighlightPending = false;
86 q_func()->rehighlight();
87 }
88
89 void applyFormatChanges();
90 QVector<QTextCharFormat> formatChanges;
91 QTextBlock currentBlock;
92 bool rehighlightPending;
93 bool inReformatBlocks;
94};
95
96void QSyntaxHighlighterPrivate::applyFormatChanges()
97{
98 bool formatsChanged = false;
99
100 QTextLayout *layout = currentBlock.layout();
101
102 QList<QTextLayout::FormatRange> ranges = layout->additionalFormats();
103
104 const int preeditAreaStart = layout->preeditAreaPosition();
105 const int preeditAreaLength = layout->preeditAreaText().length();
106
107 if (preeditAreaLength != 0) {
108 QList<QTextLayout::FormatRange>::Iterator it = ranges.begin();
109 while (it != ranges.end()) {
110 if (it->start >= preeditAreaStart
111 && it->start + it->length <= preeditAreaStart + preeditAreaLength) {
112 ++it;
113 } else {
114 it = ranges.erase(it);
115 formatsChanged = true;
116 }
117 }
118 } else if (!ranges.isEmpty()) {
119 ranges.clear();
120 formatsChanged = true;
121 }
122
123 QTextCharFormat emptyFormat;
124
125 QTextLayout::FormatRange r;
126 r.start = -1;
127
128 int i = 0;
129 while (i < formatChanges.count()) {
130
131 while (i < formatChanges.count() && formatChanges.at(i) == emptyFormat)
132 ++i;
133
134 if (i >= formatChanges.count())
135 break;
136
137 r.start = i;
138 r.format = formatChanges.at(i);
139
140 while (i < formatChanges.count() && formatChanges.at(i) == r.format)
141 ++i;
142
143 if (i >= formatChanges.count())
144 break;
145
146 r.length = i - r.start;
147
148 if (preeditAreaLength != 0) {
149 if (r.start >= preeditAreaStart)
150 r.start += preeditAreaLength;
151 else if (r.start + r.length >= preeditAreaStart)
152 r.length += preeditAreaLength;
153 }
154
155 ranges << r;
156 formatsChanged = true;
157 r.start = -1;
158 }
159
160 if (r.start != -1) {
161 r.length = formatChanges.count() - r.start;
162
163 if (preeditAreaLength != 0) {
164 if (r.start >= preeditAreaStart)
165 r.start += preeditAreaLength;
166 else if (r.start + r.length >= preeditAreaStart)
167 r.length += preeditAreaLength;
168 }
169
170 ranges << r;
171 formatsChanged = true;
172 }
173
174 if (formatsChanged) {
175 layout->setAdditionalFormats(ranges);
176 doc->markContentsDirty(currentBlock.position(), currentBlock.length());
177 }
178}
179
180void QSyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, int charsAdded)
181{
182 if (!inReformatBlocks)
183 reformatBlocks(from, charsRemoved, charsAdded);
184}
185
186void QSyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded)
187{
188 rehighlightPending = false;
189
190 QTextBlock block = doc->findBlock(from);
191 if (!block.isValid())
192 return;
193
194 int endPosition;
195 QTextBlock lastBlock = doc->findBlock(from + charsAdded + (charsRemoved > 0 ? 1 : 0));
196 if (lastBlock.isValid())
197 endPosition = lastBlock.position() + lastBlock.length();
198 else
199 endPosition = doc->docHandle()->length();
200
201 bool forceHighlightOfNextBlock = false;
202
203 while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
204 const int stateBeforeHighlight = block.userState();
205
206 reformatBlock(block);
207
208 forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight);
209
210 block = block.next();
211 }
212
213 formatChanges.clear();
214}
215
216void QSyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block)
217{
218 Q_Q(QSyntaxHighlighter);
219
220 Q_ASSERT_X(!currentBlock.isValid(), "QSyntaxHighlighter::reformatBlock()", "reFormatBlock() called recursively");
221
222 currentBlock = block;
223
224 formatChanges.fill(QTextCharFormat(), block.length() - 1);
225 q->highlightBlock(block.text());
226 applyFormatChanges();
227
228 currentBlock = QTextBlock();
229}
230
231/*!
232 \class QSyntaxHighlighter
233 \reentrant
234
235 \brief The QSyntaxHighlighter class allows you to define syntax
236 highlighting rules, and in addition you can use the class to query
237 a document's current formatting or user data.
238
239 \since 4.1
240
241 \ingroup richtext-processing
242
243 The QSyntaxHighlighter class is a base class for implementing
244 QTextEdit syntax highlighters. A syntax highligher automatically
245 highlights parts of the text in a QTextEdit, or more generally in
246 a QTextDocument. Syntax highlighters are often used when the user
247 is entering text in a specific format (for example source code)
248 and help the user to read the text and identify syntax errors.
249
250 To provide your own syntax highlighting, you must subclass
251 QSyntaxHighlighter and reimplement highlightBlock().
252
253 When you create an instance of your QSyntaxHighlighter subclass,
254 pass it the QTextEdit or QTextDocument that you want the syntax
255 highlighting to be applied to. For example:
256
257 \snippet doc/src/snippets/code/src_gui_text_qsyntaxhighlighter.cpp 0
258
259 After this your highlightBlock() function will be called
260 automatically whenever necessary. Use your highlightBlock()
261 function to apply formatting (e.g. setting the font and color) to
262 the text that is passed to it. QSyntaxHighlighter provides the
263 setFormat() function which applies a given QTextCharFormat on
264 the current text block. For example:
265
266 \snippet doc/src/snippets/code/src_gui_text_qsyntaxhighlighter.cpp 1
267
268 Some syntaxes can have constructs that span several text
269 blocks. For example, a C++ syntax highlighter should be able to
270 cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with
271 these cases it is necessary to know the end state of the previous
272 text block (e.g. "in comment").
273
274 Inside your highlightBlock() implementation you can query the end
275 state of the previous text block using the previousBlockState()
276 function. After parsing the block you can save the last state
277 using setCurrentBlockState().
278
279 The currentBlockState() and previousBlockState() functions return
280 an int value. If no state is set, the returned value is -1. You
281 can designate any other value to identify any given state using
282 the setCurrentBlockState() function. Once the state is set the
283 QTextBlock keeps that value until it is set set again or until the
284 corresponding paragraph of text is deleted.
285
286 For example, if you're writing a simple C++ syntax highlighter,
287 you might designate 1 to signify "in comment":
288
289 \snippet doc/src/snippets/code/src_gui_text_qsyntaxhighlighter.cpp 2
290
291 In the example above, we first set the current block state to
292 0. Then, if the previous block ended within a comment, we higlight
293 from the beginning of the current block (\c {startIndex =
294 0}). Otherwise, we search for the given start expression. If the
295 specified end expression cannot be found in the text block, we
296 change the current block state by calling setCurrentBlockState(),
297 and make sure that the rest of the block is higlighted.