1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtGui module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** 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 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qsyntaxhighlighter.h" |
41 | |
42 | #ifndef QT_NO_SYNTAXHIGHLIGHTER |
43 | #include <private/qobject_p.h> |
44 | #include <qtextdocument.h> |
45 | #include <private/qtextdocument_p.h> |
46 | #include <qtextlayout.h> |
47 | #include <qpointer.h> |
48 | #include <qscopedvaluerollback.h> |
49 | #include <qtextobject.h> |
50 | #include <qtextcursor.h> |
51 | #include <qdebug.h> |
52 | #include <qtimer.h> |
53 | |
54 | #include <algorithm> |
55 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | class QSyntaxHighlighterPrivate : public QObjectPrivate |
59 | { |
60 | Q_DECLARE_PUBLIC(QSyntaxHighlighter) |
61 | public: |
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 | { |
74 | QScopedValueRollback<bool> bg(inReformatBlocks, true); |
75 | cursor.beginEditBlock(); |
76 | int from = cursor.position(); |
77 | cursor.movePosition(operation); |
78 | reformatBlocks(from, 0, cursor.position() - from); |
79 | cursor.endEditBlock(); |
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 | QList<QTextCharFormat> formatChanges; |
91 | QTextBlock currentBlock; |
92 | bool rehighlightPending; |
93 | bool inReformatBlocks; |
94 | }; |
95 | |
96 | void QSyntaxHighlighterPrivate::applyFormatChanges() |
97 | { |
98 | bool formatsChanged = false; |
99 | |
100 | QTextLayout *layout = currentBlock.layout(); |
101 | |
102 | QList<QTextLayout::FormatRange> ranges = layout->formats(); |
103 | |
104 | const int preeditAreaStart = layout->preeditAreaPosition(); |
105 | const int preeditAreaLength = layout->preeditAreaText().length(); |
106 | |
107 | if (preeditAreaLength != 0) { |
108 | auto isOutsidePreeditArea = [=](const QTextLayout::FormatRange &range) { |
109 | return range.start < preeditAreaStart |
110 | || range.start + range.length > preeditAreaStart + preeditAreaLength; |
111 | }; |
112 | const auto it = std::remove_if(ranges.begin(), ranges.end(), |
113 | isOutsidePreeditArea); |
114 | if (it != ranges.end()) { |
115 | ranges.erase(it, ranges.end()); |
116 | formatsChanged = true; |
117 | } |
118 | } else if (!ranges.isEmpty()) { |
119 | ranges.clear(); |
120 | formatsChanged = true; |
121 | } |
122 | |
123 | int i = 0; |
124 | while (i < formatChanges.count()) { |
125 | QTextLayout::FormatRange r; |
126 | |
127 | while (i < formatChanges.count() && formatChanges.at(i) == r.format) |
128 | ++i; |
129 | |
130 | if (i == formatChanges.count()) |
131 | break; |
132 | |
133 | r.start = i; |
134 | r.format = formatChanges.at(i); |
135 | |
136 | while (i < formatChanges.count() && formatChanges.at(i) == r.format) |
137 | ++i; |
138 | |
139 | Q_ASSERT(i <= formatChanges.count()); |
140 | r.length = i - r.start; |
141 | |
142 | if (preeditAreaLength != 0) { |
143 | if (r.start >= preeditAreaStart) |
144 | r.start += preeditAreaLength; |
145 | else if (r.start + r.length >= preeditAreaStart) |
146 | r.length += preeditAreaLength; |
147 | } |
148 | |
149 | ranges << r; |
150 | formatsChanged = true; |
151 | } |
152 | |
153 | if (formatsChanged) { |
154 | layout->setFormats(ranges); |
155 | doc->markContentsDirty(currentBlock.position(), currentBlock.length()); |
156 | } |
157 | } |
158 | |
159 | void QSyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, int charsAdded) |
160 | { |
161 | if (!inReformatBlocks && !rehighlightPending) |
162 | reformatBlocks(from, charsRemoved, charsAdded); |
163 | } |
164 | |
165 | void QSyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded) |
166 | { |
167 | QTextBlock block = doc->findBlock(from); |
168 | if (!block.isValid()) |
169 | return; |
170 | |
171 | int endPosition; |
172 | QTextBlock lastBlock = doc->findBlock(from + charsAdded + (charsRemoved > 0 ? 1 : 0)); |
173 | if (lastBlock.isValid()) |
174 | endPosition = lastBlock.position() + lastBlock.length(); |
175 | else |
176 | endPosition = QTextDocumentPrivate::get(doc)->length(); |
177 | |
178 | bool forceHighlightOfNextBlock = false; |
179 | |
180 | while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) { |
181 | const int stateBeforeHighlight = block.userState(); |
182 | |
183 | reformatBlock(block); |
184 | |
185 | forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight); |
186 | |
187 | block = block.next(); |
188 | } |
189 | |
190 | formatChanges.clear(); |
191 | } |
192 | |
193 | void QSyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block) |
194 | { |
195 | Q_Q(QSyntaxHighlighter); |
196 | |
197 | Q_ASSERT_X(!currentBlock.isValid(), "QSyntaxHighlighter::reformatBlock()" , "reFormatBlock() called recursively" ); |
198 | |
199 | currentBlock = block; |
200 | |
201 | formatChanges.fill(QTextCharFormat(), block.length() - 1); |
202 | q->highlightBlock(block.text()); |
203 | applyFormatChanges(); |
204 | |
205 | currentBlock = QTextBlock(); |
206 | } |
207 | |
208 | /*! |
209 | \class QSyntaxHighlighter |
210 | \reentrant |
211 | \inmodule QtGui |
212 | |
213 | \brief The QSyntaxHighlighter class allows you to define syntax |
214 | highlighting rules, and in addition you can use the class to query |
215 | a document's current formatting or user data. |
216 | |
217 | \since 4.1 |
218 | |
219 | \ingroup richtext-processing |
220 | |
221 | The QSyntaxHighlighter class is a base class for implementing |
222 | QTextDocument syntax highlighters. A syntax highligher automatically |
223 | highlights parts of the text in a QTextDocument. Syntax highlighters are |
224 | often used when the user is entering text in a specific format (for example source code) |
225 | and help the user to read the text and identify syntax errors. |
226 | |
227 | To provide your own syntax highlighting, you must subclass |
228 | QSyntaxHighlighter and reimplement highlightBlock(). |
229 | |
230 | When you create an instance of your QSyntaxHighlighter subclass, |
231 | pass it the QTextDocument that you want the syntax |
232 | highlighting to be applied to. For example: |
233 | |
234 | \snippet code/src_gui_text_qsyntaxhighlighter.cpp 0 |
235 | |
236 | After this your highlightBlock() function will be called |
237 | automatically whenever necessary. Use your highlightBlock() |
238 | function to apply formatting (e.g. setting the font and color) to |
239 | the text that is passed to it. QSyntaxHighlighter provides the |
240 | setFormat() function which applies a given QTextCharFormat on |
241 | the current text block. For example: |
242 | |
243 | \snippet code/src_gui_text_qsyntaxhighlighter.cpp 1 |
244 | |
245 | \target QSyntaxHighlighter multiblock |
246 | |
247 | Some syntaxes can have constructs that span several text |
248 | blocks. For example, a C++ syntax highlighter should be able to |
249 | cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with |
250 | these cases it is necessary to know the end state of the previous |
251 | text block (e.g. "in comment"). |
252 | |
253 | Inside your highlightBlock() implementation you can query the end |
254 | state of the previous text block using the previousBlockState() |
255 | function. After parsing the block you can save the last state |
256 | using setCurrentBlockState(). |
257 | |
258 | The currentBlockState() and previousBlockState() functions return |
259 | an int value. If no state is set, the returned value is -1. You |
260 | can designate any other value to identify any given state using |
261 | the setCurrentBlockState() function. Once the state is set the |
262 | QTextBlock keeps that value until it is set set again or until the |
263 | corresponding paragraph of text is deleted. |
264 | |
265 | For example, if you're writing a simple C++ syntax highlighter, |
266 | you might designate 1 to signify "in comment": |
267 | |
268 | \snippet code/src_gui_text_qsyntaxhighlighter.cpp 2 |
269 | |
270 | In the example above, we first set the current block state to |
271 | 0. Then, if the previous block ended within a comment, we highlight |
272 | from the beginning of the current block (\c {startIndex = |
273 | 0}). Otherwise, we search for the given start expression. If the |
274 | specified end expression cannot be found in the text block, we |
275 | change the current block state by calling setCurrentBlockState(), |
276 | and make sure that the rest of the block is highlighted. |
277 | |
278 | In addition you can query the current formatting and user data |
279 | using the format() and currentBlockUserData() functions |
280 | respectively. You can also attach user data to the current text |
281 | block using the setCurrentBlockUserData() function. |
282 | QTextBlockUserData can be used to store custom settings. In the |
283 | case of syntax highlighting, it is in particular interesting as |
284 | cache storage for information that you may figure out while |
285 | parsing the paragraph's text. For an example, see the |
286 | setCurrentBlockUserData() documentation. |
287 | |
288 | \sa QTextDocument, {Syntax Highlighter Example} |
289 | */ |
290 | |
291 | /*! |
292 | Constructs a QSyntaxHighlighter with the given \a parent. |
293 | |
294 | If the parent is a QTextEdit, it installs the syntax highlighter on the |
295 | parents document. The specified QTextEdit also becomes the owner of |
296 | the QSyntaxHighlighter. |
297 | */ |
298 | QSyntaxHighlighter::QSyntaxHighlighter(QObject *parent) |
299 | : QObject(*new QSyntaxHighlighterPrivate, parent) |
300 | { |
301 | if (parent && parent->inherits("QTextEdit" )) { |
302 | QTextDocument *doc = qvariant_cast<QTextDocument *>(parent->property("document" )); |
303 | if (doc) |
304 | setDocument(doc); |
305 | } |
306 | } |
307 | |
308 | /*! |
309 | Constructs a QSyntaxHighlighter and installs it on \a parent. |
310 | The specified QTextDocument also becomes the owner of the |
311 | QSyntaxHighlighter. |
312 | */ |
313 | QSyntaxHighlighter::QSyntaxHighlighter(QTextDocument *parent) |
314 | : QObject(*new QSyntaxHighlighterPrivate, parent) |
315 | { |
316 | setDocument(parent); |
317 | } |
318 | |
319 | /*! |
320 | Destructor. Uninstalls this syntax highlighter from the text document. |
321 | */ |
322 | QSyntaxHighlighter::~QSyntaxHighlighter() |
323 | { |
324 | setDocument(nullptr); |
325 | } |
326 | |
327 | /*! |
328 | Installs the syntax highlighter on the given QTextDocument \a doc. |
329 | A QSyntaxHighlighter can only be used with one document at a time. |
330 | */ |
331 | void QSyntaxHighlighter::setDocument(QTextDocument *doc) |
332 | { |
333 | Q_D(QSyntaxHighlighter); |
334 | if (d->doc) { |
335 | disconnect(d->doc, SIGNAL(contentsChange(int,int,int)), |
336 | this, SLOT(_q_reformatBlocks(int,int,int))); |
337 | |
338 | QTextCursor cursor(d->doc); |
339 | cursor.beginEditBlock(); |
340 | for (QTextBlock blk = d->doc->begin(); blk.isValid(); blk = blk.next()) |
341 | blk.layout()->clearFormats(); |
342 | cursor.endEditBlock(); |
343 | } |
344 | d->doc = doc; |
345 | if (d->doc) { |
346 | connect(d->doc, SIGNAL(contentsChange(int,int,int)), |
347 | this, SLOT(_q_reformatBlocks(int,int,int))); |
348 | if (!d->doc->isEmpty()) { |
349 | d->rehighlightPending = true; |
350 | QTimer::singleShot(0, this, SLOT(_q_delayedRehighlight())); |
351 | } |
352 | } |
353 | } |
354 | |
355 | /*! |
356 | Returns the QTextDocument on which this syntax highlighter is |
357 | installed. |
358 | */ |
359 | QTextDocument *QSyntaxHighlighter::document() const |
360 | { |
361 | Q_D(const QSyntaxHighlighter); |
362 | return d->doc; |
363 | } |
364 | |
365 | /*! |
366 | \since 4.2 |
367 | |
368 | Reapplies the highlighting to the whole document. |
369 | |
370 | \sa rehighlightBlock() |
371 | */ |
372 | void QSyntaxHighlighter::rehighlight() |
373 | { |
374 | Q_D(QSyntaxHighlighter); |
375 | if (!d->doc) |
376 | return; |
377 | |
378 | QTextCursor cursor(d->doc); |
379 | d->rehighlight(cursor, QTextCursor::End); |
380 | d->rehighlightPending = false; // user manually did a full rehighlight |
381 | } |
382 | |
383 | /*! |
384 | \since 4.6 |
385 | |
386 | Reapplies the highlighting to the given QTextBlock \a block. |
387 | |
388 | \sa rehighlight() |
389 | */ |
390 | void QSyntaxHighlighter::rehighlightBlock(const QTextBlock &block) |
391 | { |
392 | Q_D(QSyntaxHighlighter); |
393 | if (!d->doc || !block.isValid() || block.document() != d->doc) |
394 | return; |
395 | |
396 | const bool rehighlightPending = d->rehighlightPending; |
397 | |
398 | QTextCursor cursor(block); |
399 | d->rehighlight(cursor, QTextCursor::EndOfBlock); |
400 | |
401 | if (rehighlightPending) |
402 | d->rehighlightPending = rehighlightPending; |
403 | } |
404 | |
405 | /*! |
406 | \fn void QSyntaxHighlighter::highlightBlock(const QString &text) |
407 | |
408 | Highlights the given text block. This function is called when |
409 | necessary by the rich text engine, i.e. on text blocks which have |
410 | changed. |
411 | |
412 | To provide your own syntax highlighting, you must subclass |
413 | QSyntaxHighlighter and reimplement highlightBlock(). In your |
414 | reimplementation you should parse the block's \a text and call |
415 | setFormat() as often as necessary to apply any font and color |
416 | changes that you require. For example: |
417 | |
418 | \snippet code/src_gui_text_qsyntaxhighlighter.cpp 1 |
419 | |
420 | See the \l{QSyntaxHighlighter multiblock}{Detailed Description} for |
421 | examples of using setCurrentBlockState(), currentBlockState() |
422 | and previousBlockState() to handle syntaxes with constructs that |
423 | span several text blocks |
424 | |
425 | \sa previousBlockState(), setFormat(), setCurrentBlockState() |
426 | */ |
427 | |
428 | /*! |
429 | This function is applied to the syntax highlighter's current text |
430 | block (i.e. the text that is passed to the highlightBlock() |
431 | function). |
432 | |
433 | The specified \a format is applied to the text from the \a start |
434 | position for a length of \a count characters (if \a count is 0, |
435 | nothing is done). The formatting properties set in \a format are |
436 | merged at display time with the formatting information stored |
437 | directly in the document, for example as previously set with |
438 | QTextCursor's functions. Note that the document itself remains |
439 | unmodified by the format set through this function. |
440 | |
441 | \sa format(), highlightBlock() |
442 | */ |
443 | void QSyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format) |
444 | { |
445 | Q_D(QSyntaxHighlighter); |
446 | if (start < 0 || start >= d->formatChanges.count()) |
447 | return; |
448 | |
449 | const int end = qMin(start + count, d->formatChanges.count()); |
450 | for (int i = start; i < end; ++i) |
451 | d->formatChanges[i] = format; |
452 | } |
453 | |
454 | /*! |
455 | \overload |
456 | |
457 | The specified \a color is applied to the current text block from |
458 | the \a start position for a length of \a count characters. |
459 | |
460 | The other attributes of the current text block, e.g. the font and |
461 | background color, are reset to default values. |
462 | |
463 | \sa format(), highlightBlock() |
464 | */ |
465 | void QSyntaxHighlighter::setFormat(int start, int count, const QColor &color) |
466 | { |
467 | QTextCharFormat format; |
468 | format.setForeground(color); |
469 | setFormat(start, count, format); |
470 | } |
471 | |
472 | /*! |
473 | \overload |
474 | |
475 | The specified \a font is applied to the current text block from |
476 | the \a start position for a length of \a count characters. |
477 | |
478 | The other attributes of the current text block, e.g. the font and |
479 | background color, are reset to default values. |
480 | |
481 | \sa format(), highlightBlock() |
482 | */ |
483 | void QSyntaxHighlighter::setFormat(int start, int count, const QFont &font) |
484 | { |
485 | QTextCharFormat format; |
486 | format.setFont(font); |
487 | setFormat(start, count, format); |
488 | } |
489 | |
490 | /*! |
491 | \fn QTextCharFormat QSyntaxHighlighter::format(int position) const |
492 | |
493 | Returns the format at \a position inside the syntax highlighter's |
494 | current text block. |
495 | */ |
496 | QTextCharFormat QSyntaxHighlighter::format(int pos) const |
497 | { |
498 | Q_D(const QSyntaxHighlighter); |
499 | if (pos < 0 || pos >= d->formatChanges.count()) |
500 | return QTextCharFormat(); |
501 | return d->formatChanges.at(pos); |
502 | } |
503 | |
504 | /*! |
505 | Returns the end state of the text block previous to the |
506 | syntax highlighter's current block. If no value was |
507 | previously set, the returned value is -1. |
508 | |
509 | \sa highlightBlock(), setCurrentBlockState() |
510 | */ |
511 | int QSyntaxHighlighter::previousBlockState() const |
512 | { |
513 | Q_D(const QSyntaxHighlighter); |
514 | if (!d->currentBlock.isValid()) |
515 | return -1; |
516 | |
517 | const QTextBlock previous = d->currentBlock.previous(); |
518 | if (!previous.isValid()) |
519 | return -1; |
520 | |
521 | return previous.userState(); |
522 | } |
523 | |
524 | /*! |
525 | Returns the state of the current text block. If no value is set, |
526 | the returned value is -1. |
527 | */ |
528 | int QSyntaxHighlighter::currentBlockState() const |
529 | { |
530 | Q_D(const QSyntaxHighlighter); |
531 | if (!d->currentBlock.isValid()) |
532 | return -1; |
533 | |
534 | return d->currentBlock.userState(); |
535 | } |
536 | |
537 | /*! |
538 | Sets the state of the current text block to \a newState. |
539 | |
540 | \sa highlightBlock() |
541 | */ |
542 | void QSyntaxHighlighter::setCurrentBlockState(int newState) |
543 | { |
544 | Q_D(QSyntaxHighlighter); |
545 | if (!d->currentBlock.isValid()) |
546 | return; |
547 | |
548 | d->currentBlock.setUserState(newState); |
549 | } |
550 | |
551 | /*! |
552 | Attaches the given \a data to the current text block. The |
553 | ownership is passed to the underlying text document, i.e. the |
554 | provided QTextBlockUserData object will be deleted if the |
555 | corresponding text block gets deleted. |
556 | |
557 | QTextBlockUserData can be used to store custom settings. In the |
558 | case of syntax highlighting, it is in particular interesting as |
559 | cache storage for information that you may figure out while |
560 | parsing the paragraph's text. |
561 | |
562 | For example while parsing the text, you can keep track of |
563 | parenthesis characters that you encounter ('{[(' and the like), |
564 | and store their relative position and the actual QChar in a simple |
565 | class derived from QTextBlockUserData: |
566 | |
567 | \snippet code/src_gui_text_qsyntaxhighlighter.cpp 3 |
568 | |
569 | During cursor navigation in the associated editor, you can ask the |
570 | current QTextBlock (retrieved using the QTextCursor::block() |
571 | function) if it has a user data object set and cast it to your \c |
572 | BlockData object. Then you can check if the current cursor |
573 | position matches with a previously recorded parenthesis position, |
574 | and, depending on the type of parenthesis (opening or closing), |
575 | find the next opening or closing parenthesis on the same level. |
576 | |
577 | In this way you can do a visual parenthesis matching and highlight |
578 | from the current cursor position to the matching parenthesis. That |
579 | makes it easier to spot a missing parenthesis in your code and to |
580 | find where a corresponding opening/closing parenthesis is when |
581 | editing parenthesis intensive code. |
582 | |
583 | \sa QTextBlock::setUserData() |
584 | */ |
585 | void QSyntaxHighlighter::setCurrentBlockUserData(QTextBlockUserData *data) |
586 | { |
587 | Q_D(QSyntaxHighlighter); |
588 | if (!d->currentBlock.isValid()) |
589 | return; |
590 | |
591 | d->currentBlock.setUserData(data); |
592 | } |
593 | |
594 | /*! |
595 | Returns the QTextBlockUserData object previously attached to the |
596 | current text block. |
597 | |
598 | \sa QTextBlock::userData(), setCurrentBlockUserData() |
599 | */ |
600 | QTextBlockUserData *QSyntaxHighlighter::currentBlockUserData() const |
601 | { |
602 | Q_D(const QSyntaxHighlighter); |
603 | if (!d->currentBlock.isValid()) |
604 | return nullptr; |
605 | |
606 | return d->currentBlock.userData(); |
607 | } |
608 | |
609 | /*! |
610 | \since 4.4 |
611 | |
612 | Returns the current text block. |
613 | */ |
614 | QTextBlock QSyntaxHighlighter::currentBlock() const |
615 | { |
616 | Q_D(const QSyntaxHighlighter); |
617 | return d->currentBlock; |
618 | } |
619 | |
620 | QT_END_NAMESPACE |
621 | |
622 | #include "moc_qsyntaxhighlighter.cpp" |
623 | |
624 | #endif // QT_NO_SYNTAXHIGHLIGHTER |
625 | |