1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "basetextdocumentlayout.h"
35 using namespace TextEditor;
37 CodeFormatterData::~CodeFormatterData()
41 TextBlockUserData::~TextBlockUserData()
43 TextMarks marks = m_marks;
45 foreach (ITextMark *mrk, marks) {
46 mrk->removedFromEditor();
49 if (m_codeFormatterData)
50 delete m_codeFormatterData;
53 int TextBlockUserData::braceDepthDelta() const
56 for (int i = 0; i < m_parentheses.size(); ++i) {
57 switch (m_parentheses.at(i).chr.unicode()) {
58 case '{': case '+': case '[': ++delta; break;
59 case '}': case '-': case ']': --delta; break;
66 TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c)
68 QTextBlock block = cursor->block();
69 if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
72 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
73 Parenthesis openParen, closedParen;
74 QTextBlock closedParenParag = block;
76 const int cursorPos = cursor->position() - closedParenParag.position();
79 bool foundOpen = false;
82 if (i >= parenList.count())
84 openParen = parenList.at(i);
85 if (openParen.pos != cursorPos) {
94 if (i >= parenList.count()) {
96 closedParenParag = closedParenParag.next();
97 if (!closedParenParag.isValid())
99 if (BaseTextDocumentLayout::hasParentheses(closedParenParag)
100 && !BaseTextDocumentLayout::ifdefedOut(closedParenParag)) {
101 parenList = BaseTextDocumentLayout::parentheses(closedParenParag);
108 closedParen = parenList.at(i);
109 if (closedParen.type == Parenthesis::Opened) {
120 cursor->clearSelection();
121 cursor->setPosition(closedParenParag.position() + closedParen.pos + 1, QTextCursor::KeepAnchor);
123 if ((c == QLatin1Char('{') && closedParen.chr != QLatin1Char('}'))
124 || (c == QLatin1Char('(') && closedParen.chr != QLatin1Char(')'))
125 || (c == QLatin1Char('[') && closedParen.chr != QLatin1Char(']'))
126 || (c == QLatin1Char('+') && closedParen.chr != QLatin1Char('-'))
135 TextBlockUserData::MatchType TextBlockUserData::checkClosedParenthesis(QTextCursor *cursor, QChar c)
137 QTextBlock block = cursor->block();
138 if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
141 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
142 Parenthesis openParen, closedParen;
143 QTextBlock openParenParag = block;
145 const int cursorPos = cursor->position() - openParenParag.position();
146 int i = parenList.count() - 1;
148 bool foundClosed = false;
153 closedParen = parenList.at(i);
154 if (closedParen.pos != cursorPos - 1) {
165 openParenParag = openParenParag.previous();
166 if (!openParenParag.isValid())
169 if (BaseTextDocumentLayout::hasParentheses(openParenParag)
170 && !BaseTextDocumentLayout::ifdefedOut(openParenParag)) {
171 parenList = BaseTextDocumentLayout::parentheses(openParenParag);
175 i = parenList.count() - 1;
178 openParen = parenList.at(i);
179 if (openParen.type == Parenthesis::Closed) {
190 cursor->clearSelection();
191 cursor->setPosition(openParenParag.position() + openParen.pos, QTextCursor::KeepAnchor);
193 if ((c == '}' && openParen.chr != '{') ||
194 (c == ')' && openParen.chr != '(') ||
195 (c == ']' && openParen.chr != '[') ||
196 (c == '-' && openParen.chr != '+'))
204 bool TextBlockUserData::findPreviousOpenParenthesis(QTextCursor *cursor, bool select)
206 QTextBlock block = cursor->block();
207 int position = cursor->position();
209 while (block.isValid()) {
210 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
211 if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
212 for (int i = parenList.count()-1; i >= 0; --i) {
213 Parenthesis paren = parenList.at(i);
214 if (block == cursor->block() &&
215 (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0)))
217 if (paren.type == Parenthesis::Closed) {
219 } else if (ignore > 0) {
222 cursor->setPosition(block.position() + paren.pos, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
227 block = block.previous();
232 bool TextBlockUserData::findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition)
234 QTextBlock block = cursor->block();
235 int position = cursor->position();
237 while (block.isValid()) {
238 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
239 if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
240 for (int i = parenList.count()-1; i >= 0; --i) {
241 Parenthesis paren = parenList.at(i);
242 if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}')
243 && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-')
244 && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']'))
246 if (block == cursor->block()) {
247 if (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0))
249 if (checkStartPosition && paren.type == Parenthesis::Opened && paren.pos== cursor->position()) {
253 if (paren.type == Parenthesis::Closed) {
255 } else if (ignore > 0) {
258 cursor->setPosition(block.position() + paren.pos);
263 block = block.previous();
268 bool TextBlockUserData::findNextClosingParenthesis(QTextCursor *cursor, bool select)
270 QTextBlock block = cursor->block();
271 int position = cursor->position();
273 while (block.isValid()) {
274 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
275 if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
276 for (int i = 0; i < parenList.count(); ++i) {
277 Parenthesis paren = parenList.at(i);
278 if (block == cursor->block() &&
279 (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0)))
281 if (paren.type == Parenthesis::Opened) {
283 } else if (ignore > 0) {
286 cursor->setPosition(block.position() + paren.pos+1, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
291 block = block.next();
296 bool TextBlockUserData::findNextBlockClosingParenthesis(QTextCursor *cursor)
298 QTextBlock block = cursor->block();
299 int position = cursor->position();
301 while (block.isValid()) {
302 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
303 if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
304 for (int i = 0; i < parenList.count(); ++i) {
305 Parenthesis paren = parenList.at(i);
306 if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}')
307 && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-')
308 && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']'))
310 if (block == cursor->block() &&
311 (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0)))
313 if (paren.type == Parenthesis::Opened) {
315 } else if (ignore > 0) {
318 cursor->setPosition(block.position() + paren.pos+1);
323 block = block.next();
328 TextBlockUserData::MatchType TextBlockUserData::matchCursorBackward(QTextCursor *cursor)
330 cursor->clearSelection();
331 const QTextBlock block = cursor->block();
333 if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
336 const int relPos = cursor->position() - block.position();
338 Parentheses parentheses = BaseTextDocumentLayout::parentheses(block);
339 const Parentheses::const_iterator cend = parentheses.constEnd();
340 for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) {
341 const Parenthesis &paren = *it;
342 if (paren.pos == relPos - 1
343 && paren.type == Parenthesis::Closed) {
344 return checkClosedParenthesis(cursor, paren.chr);
350 TextBlockUserData::MatchType TextBlockUserData::matchCursorForward(QTextCursor *cursor)
352 cursor->clearSelection();
353 const QTextBlock block = cursor->block();
355 if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
358 const int relPos = cursor->position() - block.position();
360 Parentheses parentheses = BaseTextDocumentLayout::parentheses(block);
361 const Parentheses::const_iterator cend = parentheses.constEnd();
362 for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) {
363 const Parenthesis &paren = *it;
364 if (paren.pos == relPos
365 && paren.type == Parenthesis::Opened) {
366 return checkOpenParenthesis(cursor, paren.chr);
372 void TextBlockUserData::setCodeFormatterData(CodeFormatterData *data)
374 if (m_codeFormatterData)
375 delete m_codeFormatterData;
377 m_codeFormatterData = data;
380 void TextBlockUserData::addMark(ITextMark *mark)
383 for ( ; i < m_marks.size(); ++i) {
384 if (mark->priority() < m_marks.at(i)->priority())
387 m_marks.insert(i, mark);
391 BaseTextDocumentLayout::BaseTextDocumentLayout(QTextDocument *doc)
392 :QPlainTextDocumentLayout(doc) {
393 lastSaveRevision = 0;
398 BaseTextDocumentLayout::~BaseTextDocumentLayout()
402 void BaseTextDocumentLayout::setParentheses(const QTextBlock &block, const Parentheses &parentheses)
404 if (parentheses.isEmpty()) {
405 if (TextBlockUserData *userData = testUserData(block))
406 userData->clearParentheses();
408 userData(block)->setParentheses(parentheses);
412 Parentheses BaseTextDocumentLayout::parentheses(const QTextBlock &block)
414 if (TextBlockUserData *userData = testUserData(block))
415 return userData->parentheses();
416 return Parentheses();
419 bool BaseTextDocumentLayout::hasParentheses(const QTextBlock &block)
421 if (TextBlockUserData *userData = testUserData(block))
422 return userData->hasParentheses();
426 bool BaseTextDocumentLayout::setIfdefedOut(const QTextBlock &block)
428 return userData(block)->setIfdefedOut();
431 bool BaseTextDocumentLayout::clearIfdefedOut(const QTextBlock &block)
433 if (TextBlockUserData *userData = testUserData(block))
434 return userData->clearIfdefedOut();
438 bool BaseTextDocumentLayout::ifdefedOut(const QTextBlock &block)
440 if (TextBlockUserData *userData = testUserData(block))
441 return userData->ifdefedOut();
445 int BaseTextDocumentLayout::braceDepthDelta(const QTextBlock &block)
447 if (TextBlockUserData *userData = testUserData(block))
448 return userData->braceDepthDelta();
452 int BaseTextDocumentLayout::braceDepth(const QTextBlock &block)
454 int state = block.userState();
460 void BaseTextDocumentLayout::setBraceDepth(QTextBlock &block, int depth)
462 int state = block.userState();
465 state = state & 0xff;
466 block.setUserState((depth << 8) | state);
469 void BaseTextDocumentLayout::changeBraceDepth(QTextBlock &block, int delta)
472 setBraceDepth(block, braceDepth(block) + delta);
475 void BaseTextDocumentLayout::setLexerState(const QTextBlock &block, int state)
478 if (TextBlockUserData *userData = testUserData(block))
479 userData->setLexerState(0);
481 userData(block)->setLexerState(qMax(0,state));
485 int BaseTextDocumentLayout::lexerState(const QTextBlock &block)
487 if (TextBlockUserData *userData = testUserData(block))
488 return userData->lexerState();
492 void BaseTextDocumentLayout::setFoldingIndent(const QTextBlock &block, int indent)
495 if (TextBlockUserData *userData = testUserData(block))
496 userData->setFoldingIndent(0);
498 userData(block)->setFoldingIndent(qMax(0,indent));
502 int BaseTextDocumentLayout::foldingIndent(const QTextBlock &block)
504 if (TextBlockUserData *userData = testUserData(block))
505 return userData->foldingIndent();
509 void BaseTextDocumentLayout::changeFoldingIndent(QTextBlock &block, int delta)
512 setFoldingIndent(block, foldingIndent(block) + delta);
515 bool BaseTextDocumentLayout::canFold(const QTextBlock &block)
517 return (block.next().isValid() && foldingIndent(block.next()) > foldingIndent(block));
520 bool BaseTextDocumentLayout::isFolded(const QTextBlock &block)
522 if (TextBlockUserData *userData = testUserData(block))
523 return userData->folded();
527 void BaseTextDocumentLayout::setFolded(const QTextBlock &block, bool folded)
530 userData(block)->setFolded(true);
532 if (TextBlockUserData *userData = testUserData(block))
533 return userData->setFolded(false);
537 void BaseTextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold)
541 QTextBlock b = block.next();
543 int indent = foldingIndent(block);
544 while (b.isValid() && foldingIndent(b) > indent && (unfold || b.next().isValid())) {
545 b.setVisible(unfold);
546 b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0);
547 if (unfold) { // do not unfold folded sub-blocks
548 if (isFolded(b) && b.next().isValid()) {
549 int jndent = foldingIndent(b);
551 while (b.isValid() && foldingIndent(b) > jndent)
558 setFolded(block, !unfold);
561 void BaseTextDocumentLayout::setRequiredWidth(int width)
563 int oldw = m_requiredWidth;
564 m_requiredWidth = width;
565 int dw = QPlainTextDocumentLayout::documentSize().width();
566 if (oldw > dw || width > dw)
567 emitDocumentSizeChanged();
571 QSizeF BaseTextDocumentLayout::documentSize() const
573 QSizeF size = QPlainTextDocumentLayout::documentSize();
574 size.setWidth(qMax((qreal)m_requiredWidth, size.width()));