1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** 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 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.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "basetextdocumentlayout.h"
36 using namespace TextEditor;
38 CodeFormatterData::~CodeFormatterData()
42 TextBlockUserData::~TextBlockUserData()
44 TextMarks marks = m_marks;
46 foreach (ITextMark *mrk, marks) {
47 mrk->removedFromEditor();
50 if (m_codeFormatterData)
51 delete m_codeFormatterData;
54 int TextBlockUserData::braceDepthDelta() const
57 for (int i = 0; i < m_parentheses.size(); ++i) {
58 switch (m_parentheses.at(i).chr.unicode()) {
59 case '{': case '+': case '[': ++delta; break;
60 case '}': case '-': case ']': --delta; break;
67 TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c)
69 QTextBlock block = cursor->block();
70 if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
73 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
74 Parenthesis openParen, closedParen;
75 QTextBlock closedParenParag = block;
77 const int cursorPos = cursor->position() - closedParenParag.position();
80 bool foundOpen = false;
83 if (i >= parenList.count())
85 openParen = parenList.at(i);
86 if (openParen.pos != cursorPos) {
95 if (i >= parenList.count()) {
97 closedParenParag = closedParenParag.next();
98 if (!closedParenParag.isValid())
100 if (BaseTextDocumentLayout::hasParentheses(closedParenParag)
101 && !BaseTextDocumentLayout::ifdefedOut(closedParenParag)) {
102 parenList = BaseTextDocumentLayout::parentheses(closedParenParag);
109 closedParen = parenList.at(i);
110 if (closedParen.type == Parenthesis::Opened) {
121 cursor->clearSelection();
122 cursor->setPosition(closedParenParag.position() + closedParen.pos + 1, QTextCursor::KeepAnchor);
124 if ((c == QLatin1Char('{') && closedParen.chr != QLatin1Char('}'))
125 || (c == QLatin1Char('(') && closedParen.chr != QLatin1Char(')'))
126 || (c == QLatin1Char('[') && closedParen.chr != QLatin1Char(']'))
127 || (c == QLatin1Char('+') && closedParen.chr != QLatin1Char('-'))
136 TextBlockUserData::MatchType TextBlockUserData::checkClosedParenthesis(QTextCursor *cursor, QChar c)
138 QTextBlock block = cursor->block();
139 if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
142 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
143 Parenthesis openParen, closedParen;
144 QTextBlock openParenParag = block;
146 const int cursorPos = cursor->position() - openParenParag.position();
147 int i = parenList.count() - 1;
149 bool foundClosed = false;
154 closedParen = parenList.at(i);
155 if (closedParen.pos != cursorPos - 1) {
166 openParenParag = openParenParag.previous();
167 if (!openParenParag.isValid())
170 if (BaseTextDocumentLayout::hasParentheses(openParenParag)
171 && !BaseTextDocumentLayout::ifdefedOut(openParenParag)) {
172 parenList = BaseTextDocumentLayout::parentheses(openParenParag);
176 i = parenList.count() - 1;
179 openParen = parenList.at(i);
180 if (openParen.type == Parenthesis::Closed) {
191 cursor->clearSelection();
192 cursor->setPosition(openParenParag.position() + openParen.pos, QTextCursor::KeepAnchor);
194 if ((c == '}' && openParen.chr != '{') ||
195 (c == ')' && openParen.chr != '(') ||
196 (c == ']' && openParen.chr != '[') ||
197 (c == '-' && openParen.chr != '+'))
205 bool TextBlockUserData::findPreviousOpenParenthesis(QTextCursor *cursor, bool select)
207 QTextBlock block = cursor->block();
208 int position = cursor->position();
210 while (block.isValid()) {
211 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
212 if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
213 for (int i = parenList.count()-1; i >= 0; --i) {
214 Parenthesis paren = parenList.at(i);
215 if (block == cursor->block() &&
216 (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0)))
218 if (paren.type == Parenthesis::Closed) {
220 } else if (ignore > 0) {
223 cursor->setPosition(block.position() + paren.pos, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
228 block = block.previous();
233 bool TextBlockUserData::findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition)
235 QTextBlock block = cursor->block();
236 int position = cursor->position();
238 while (block.isValid()) {
239 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
240 if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
241 for (int i = parenList.count()-1; i >= 0; --i) {
242 Parenthesis paren = parenList.at(i);
243 if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}')
244 && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-')
245 && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']'))
247 if (block == cursor->block()) {
248 if (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0))
250 if (checkStartPosition && paren.type == Parenthesis::Opened && paren.pos== cursor->position()) {
254 if (paren.type == Parenthesis::Closed) {
256 } else if (ignore > 0) {
259 cursor->setPosition(block.position() + paren.pos);
264 block = block.previous();
269 bool TextBlockUserData::findNextClosingParenthesis(QTextCursor *cursor, bool select)
271 QTextBlock block = cursor->block();
272 int position = cursor->position();
274 while (block.isValid()) {
275 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
276 if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
277 for (int i = 0; i < parenList.count(); ++i) {
278 Parenthesis paren = parenList.at(i);
279 if (block == cursor->block() &&
280 (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0)))
282 if (paren.type == Parenthesis::Opened) {
284 } else if (ignore > 0) {
287 cursor->setPosition(block.position() + paren.pos+1, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
292 block = block.next();
297 bool TextBlockUserData::findNextBlockClosingParenthesis(QTextCursor *cursor)
299 QTextBlock block = cursor->block();
300 int position = cursor->position();
302 while (block.isValid()) {
303 Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
304 if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
305 for (int i = 0; i < parenList.count(); ++i) {
306 Parenthesis paren = parenList.at(i);
307 if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}')
308 && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-')
309 && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']'))
311 if (block == cursor->block() &&
312 (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0)))
314 if (paren.type == Parenthesis::Opened) {
316 } else if (ignore > 0) {
319 cursor->setPosition(block.position() + paren.pos+1);
324 block = block.next();
329 TextBlockUserData::MatchType TextBlockUserData::matchCursorBackward(QTextCursor *cursor)
331 cursor->clearSelection();
332 const QTextBlock block = cursor->block();
334 if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
337 const int relPos = cursor->position() - block.position();
339 Parentheses parentheses = BaseTextDocumentLayout::parentheses(block);
340 const Parentheses::const_iterator cend = parentheses.constEnd();
341 for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) {
342 const Parenthesis &paren = *it;
343 if (paren.pos == relPos - 1
344 && paren.type == Parenthesis::Closed) {
345 return checkClosedParenthesis(cursor, paren.chr);
351 TextBlockUserData::MatchType TextBlockUserData::matchCursorForward(QTextCursor *cursor)
353 cursor->clearSelection();
354 const QTextBlock block = cursor->block();
356 if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
359 const int relPos = cursor->position() - block.position();
361 Parentheses parentheses = BaseTextDocumentLayout::parentheses(block);
362 const Parentheses::const_iterator cend = parentheses.constEnd();
363 for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) {
364 const Parenthesis &paren = *it;
365 if (paren.pos == relPos
366 && paren.type == Parenthesis::Opened) {
367 return checkOpenParenthesis(cursor, paren.chr);
373 void TextBlockUserData::setCodeFormatterData(CodeFormatterData *data)
375 if (m_codeFormatterData)
376 delete m_codeFormatterData;
378 m_codeFormatterData = data;
381 BaseTextDocumentLayout::BaseTextDocumentLayout(QTextDocument *doc)
382 :QPlainTextDocumentLayout(doc) {
383 lastSaveRevision = 0;
388 BaseTextDocumentLayout::~BaseTextDocumentLayout()
392 void BaseTextDocumentLayout::setParentheses(const QTextBlock &block, const Parentheses &parentheses)
394 if (parentheses.isEmpty()) {
395 if (TextBlockUserData *userData = testUserData(block))
396 userData->clearParentheses();
398 userData(block)->setParentheses(parentheses);
402 Parentheses BaseTextDocumentLayout::parentheses(const QTextBlock &block)
404 if (TextBlockUserData *userData = testUserData(block))
405 return userData->parentheses();
406 return Parentheses();
409 bool BaseTextDocumentLayout::hasParentheses(const QTextBlock &block)
411 if (TextBlockUserData *userData = testUserData(block))
412 return userData->hasParentheses();
416 bool BaseTextDocumentLayout::setIfdefedOut(const QTextBlock &block)
418 return userData(block)->setIfdefedOut();
421 bool BaseTextDocumentLayout::clearIfdefedOut(const QTextBlock &block)
423 if (TextBlockUserData *userData = testUserData(block))
424 return userData->clearIfdefedOut();
428 bool BaseTextDocumentLayout::ifdefedOut(const QTextBlock &block)
430 if (TextBlockUserData *userData = testUserData(block))
431 return userData->ifdefedOut();
435 int BaseTextDocumentLayout::braceDepthDelta(const QTextBlock &block)
437 if (TextBlockUserData *userData = testUserData(block))
438 return userData->braceDepthDelta();
442 int BaseTextDocumentLayout::braceDepth(const QTextBlock &block)
444 int state = block.userState();
450 void BaseTextDocumentLayout::setBraceDepth(QTextBlock &block, int depth)
452 int state = block.userState();
455 state = state & 0xff;
456 block.setUserState((depth << 8) | state);
459 void BaseTextDocumentLayout::changeBraceDepth(QTextBlock &block, int delta)
462 setBraceDepth(block, braceDepth(block) + delta);
465 void BaseTextDocumentLayout::setLexerState(const QTextBlock &block, int state)
468 if (TextBlockUserData *userData = testUserData(block))
469 userData->setLexerState(0);
471 userData(block)->setLexerState(qMax(0,state));
475 int BaseTextDocumentLayout::lexerState(const QTextBlock &block)
477 if (TextBlockUserData *userData = testUserData(block))
478 return userData->lexerState();
482 void BaseTextDocumentLayout::setFoldingIndent(const QTextBlock &block, int indent)
485 if (TextBlockUserData *userData = testUserData(block))
486 userData->setFoldingIndent(0);
488 userData(block)->setFoldingIndent(qMax(0,indent));
492 int BaseTextDocumentLayout::foldingIndent(const QTextBlock &block)
494 if (TextBlockUserData *userData = testUserData(block))
495 return userData->foldingIndent();
499 void BaseTextDocumentLayout::changeFoldingIndent(QTextBlock &block, int delta)
502 setFoldingIndent(block, foldingIndent(block) + delta);
505 bool BaseTextDocumentLayout::canFold(const QTextBlock &block)
507 return (block.next().isValid() && foldingIndent(block.next()) > foldingIndent(block));
510 bool BaseTextDocumentLayout::isFolded(const QTextBlock &block)
512 if (TextBlockUserData *userData = testUserData(block))
513 return userData->folded();
517 void BaseTextDocumentLayout::setFolded(const QTextBlock &block, bool folded)
520 userData(block)->setFolded(true);
522 if (TextBlockUserData *userData = testUserData(block))
523 return userData->setFolded(false);
527 void BaseTextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold)
531 QTextBlock b = block.next();
533 int indent = foldingIndent(block);
534 while (b.isValid() && foldingIndent(b) > indent && (unfold || b.next().isValid())) {
535 b.setVisible(unfold);
536 b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0);
537 if (unfold) { // do not unfold folded sub-blocks
538 if (isFolded(b) && b.next().isValid()) {
539 int jndent = foldingIndent(b);
541 while (b.isValid() && foldingIndent(b) > jndent)
548 setFolded(block, !unfold);
551 void BaseTextDocumentLayout::setRequiredWidth(int width)
553 int oldw = m_requiredWidth;
554 m_requiredWidth = width;
555 int dw = QPlainTextDocumentLayout::documentSize().width();
556 if (oldw > dw || width > dw)
557 emitDocumentSizeChanged();
561 QSizeF BaseTextDocumentLayout::documentSize() const
563 QSizeF size = QPlainTextDocumentLayout::documentSize();
564 size.setWidth(qMax((qreal)m_requiredWidth, size.width()));