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 info@qt.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;
395 maxMarkWidthFactor = 1.0;
399 BaseTextDocumentLayout::~BaseTextDocumentLayout()
403 void BaseTextDocumentLayout::setParentheses(const QTextBlock &block, const Parentheses &parentheses)
405 if (parentheses.isEmpty()) {
406 if (TextBlockUserData *userData = testUserData(block))
407 userData->clearParentheses();
409 userData(block)->setParentheses(parentheses);
413 Parentheses BaseTextDocumentLayout::parentheses(const QTextBlock &block)
415 if (TextBlockUserData *userData = testUserData(block))
416 return userData->parentheses();
417 return Parentheses();
420 bool BaseTextDocumentLayout::hasParentheses(const QTextBlock &block)
422 if (TextBlockUserData *userData = testUserData(block))
423 return userData->hasParentheses();
427 bool BaseTextDocumentLayout::setIfdefedOut(const QTextBlock &block)
429 return userData(block)->setIfdefedOut();
432 bool BaseTextDocumentLayout::clearIfdefedOut(const QTextBlock &block)
434 if (TextBlockUserData *userData = testUserData(block))
435 return userData->clearIfdefedOut();
439 bool BaseTextDocumentLayout::ifdefedOut(const QTextBlock &block)
441 if (TextBlockUserData *userData = testUserData(block))
442 return userData->ifdefedOut();
446 int BaseTextDocumentLayout::braceDepthDelta(const QTextBlock &block)
448 if (TextBlockUserData *userData = testUserData(block))
449 return userData->braceDepthDelta();
453 int BaseTextDocumentLayout::braceDepth(const QTextBlock &block)
455 int state = block.userState();
461 void BaseTextDocumentLayout::setBraceDepth(QTextBlock &block, int depth)
463 int state = block.userState();
466 state = state & 0xff;
467 block.setUserState((depth << 8) | state);
470 void BaseTextDocumentLayout::changeBraceDepth(QTextBlock &block, int delta)
473 setBraceDepth(block, braceDepth(block) + delta);
476 void BaseTextDocumentLayout::setLexerState(const QTextBlock &block, int state)
479 if (TextBlockUserData *userData = testUserData(block))
480 userData->setLexerState(0);
482 userData(block)->setLexerState(qMax(0,state));
486 int BaseTextDocumentLayout::lexerState(const QTextBlock &block)
488 if (TextBlockUserData *userData = testUserData(block))
489 return userData->lexerState();
493 void BaseTextDocumentLayout::setFoldingIndent(const QTextBlock &block, int indent)
496 if (TextBlockUserData *userData = testUserData(block))
497 userData->setFoldingIndent(0);
499 userData(block)->setFoldingIndent(qMax(0,indent));
503 int BaseTextDocumentLayout::foldingIndent(const QTextBlock &block)
505 if (TextBlockUserData *userData = testUserData(block))
506 return userData->foldingIndent();
510 void BaseTextDocumentLayout::changeFoldingIndent(QTextBlock &block, int delta)
513 setFoldingIndent(block, foldingIndent(block) + delta);
516 bool BaseTextDocumentLayout::canFold(const QTextBlock &block)
518 return (block.next().isValid() && foldingIndent(block.next()) > foldingIndent(block));
521 bool BaseTextDocumentLayout::isFolded(const QTextBlock &block)
523 if (TextBlockUserData *userData = testUserData(block))
524 return userData->folded();
528 void BaseTextDocumentLayout::setFolded(const QTextBlock &block, bool folded)
531 userData(block)->setFolded(true);
533 if (TextBlockUserData *userData = testUserData(block))
534 return userData->setFolded(false);
538 void BaseTextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold)
542 QTextBlock b = block.next();
544 int indent = foldingIndent(block);
545 while (b.isValid() && foldingIndent(b) > indent && (unfold || b.next().isValid())) {
546 b.setVisible(unfold);
547 b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0);
548 if (unfold) { // do not unfold folded sub-blocks
549 if (isFolded(b) && b.next().isValid()) {
550 int jndent = foldingIndent(b);
552 while (b.isValid() && foldingIndent(b) > jndent)
559 setFolded(block, !unfold);
562 void BaseTextDocumentLayout::setRequiredWidth(int width)
564 int oldw = m_requiredWidth;
565 m_requiredWidth = width;
566 int dw = QPlainTextDocumentLayout::documentSize().width();
567 if (oldw > dw || width > dw)
568 emitDocumentSizeChanged();
572 QSizeF BaseTextDocumentLayout::documentSize() const
574 QSizeF size = QPlainTextDocumentLayout::documentSize();
575 size.setWidth(qMax((qreal)m_requiredWidth, size.width()));