diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp index abe8cc0a0099ae4bb4fdc2c9310dfd109ef14d66..c616cf33b3482426ef5f8dbf1bd708930d253eb1 100644 --- a/src/plugins/cppeditor/cpphighlighter.cpp +++ b/src/plugins/cppeditor/cpphighlighter.cpp @@ -32,7 +32,7 @@ #include <Token.h> #include <cplusplus/SimpleLexer.h> -#include <texteditor/basetexteditor.h> +#include <texteditor/basetextdocumentlayout.h> #include <QtGui/QTextDocument> #include <QtCore/QDebug> diff --git a/src/plugins/cppeditor/cppquickfix.cpp b/src/plugins/cppeditor/cppquickfix.cpp index fb905f5668f7cfb41dfaccf56c828d08d0e53389..179552103ab376d76ecf924676bc28feb8d40012 100644 --- a/src/plugins/cppeditor/cppquickfix.cpp +++ b/src/plugins/cppeditor/cppquickfix.cpp @@ -48,7 +48,8 @@ #include <cpptools/cpptoolsconstants.h> #include <cpptools/cppmodelmanagerinterface.h> -#include <QtDebug> + +#include <QtGui/QTextBlock> using namespace CppEditor::Internal; using namespace CPlusPlus; diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 86a297ef01b08e7e71c68b1ab660fde08d48f0b7..87ad8c3d21a6f41b5ab240d6fb4ecc25d1089c6f 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -85,6 +85,9 @@ #include <QtCore/QTime> #include <QtCore/QTimer> #include <QtConcurrentMap> + +#include <QtGui/QTextBlock> + #include <iostream> #include <sstream> diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index e3a09cba9a45d1c26970a68f67a0375b0f09c959..bde8d7e132e34e4c142dac5d541ab8c37ca50256 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -50,10 +50,10 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/session.h> +#include <texteditor/basetextdocumentlayout.h> #include <texteditor/basetexteditor.h> #include <texteditor/basetextmark.h> #include <texteditor/completionsupport.h> -#include <texteditor/itexteditor.h> #include <texteditor/texteditorconstants.h> #include <texteditor/tabsettings.h> #include <texteditor/texteditorsettings.h> diff --git a/src/plugins/genericprojectmanager/genericprojectfileseditor.h b/src/plugins/genericprojectmanager/genericprojectfileseditor.h index 322614c20ea7b5110f2ced7f235a8942a405925e..6345cf8438624e472456494aa9de6e51dba06b7f 100644 --- a/src/plugins/genericprojectmanager/genericprojectfileseditor.h +++ b/src/plugins/genericprojectmanager/genericprojectfileseditor.h @@ -35,6 +35,10 @@ #include <coreplugin/editormanager/ieditorfactory.h> +namespace TextEditor { +class TextEditorActionHandler; +} + namespace GenericProjectManager { namespace Internal { diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp index 11f3438bee4fb8c89414c0484ad2fae4725469d8..fe7eeb8916f68f5af615a717d8ee0b1e0e25b6de 100644 --- a/src/plugins/qmljseditor/qmljscodecompletion.cpp +++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp @@ -51,20 +51,19 @@ #include <QtCore/QXmlStreamReader> #include <QtCore/QDebug> -#include <QtGui/QPainter> +#include <QtGui/QApplication> +#include <QtGui/QDesktopWidget> +#include <QtGui/QHBoxLayout> #include <QtGui/QLabel> +#include <QtGui/QPainter> #include <QtGui/QStyle> +#include <QtGui/QTextBlock> #include <QtGui/QToolButton> -#include <QtGui/QHBoxLayout> -#include <QtGui/QApplication> -#include <QtGui/QDesktopWidget> using namespace QmlJSEditor; using namespace QmlJSEditor::Internal; using namespace QmlJS; -// #define QML_WITH_SNIPPETS - namespace { // Temporary workaround until we have proper icons for QML completion items diff --git a/src/plugins/qmljseditor/qmljshighlighter.h b/src/plugins/qmljseditor/qmljshighlighter.h index b9ffb1a77475c01e8285f826540b702d6cb3e31c..974bbe9012b6f133bb0fe5b9c663320b3099f011 100644 --- a/src/plugins/qmljseditor/qmljshighlighter.h +++ b/src/plugins/qmljseditor/qmljshighlighter.h @@ -38,7 +38,7 @@ #include <QtCore/QSet> #include <QtGui/QSyntaxHighlighter> -#include <texteditor/basetexteditor.h> +#include <texteditor/basetextdocumentlayout.h> namespace QmlJSEditor { diff --git a/src/plugins/qt4projectmanager/profileeditor.h b/src/plugins/qt4projectmanager/profileeditor.h index b0d3f6015fab6a7508acd3e77d059f71b6503fa2..f6952815af17c5d8f6b2013b96de9a0410206163 100644 --- a/src/plugins/qt4projectmanager/profileeditor.h +++ b/src/plugins/qt4projectmanager/profileeditor.h @@ -35,6 +35,7 @@ namespace TextEditor { class FontSettings; +class TextEditorActionHandler; } namespace Qt4ProjectManager { diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 4886685943a8b0f01bdc22c048baf743b7265a2b..1e4adf0687c768ceb86208865d8f043de4f542fa 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -43,11 +43,12 @@ #include <QtCore/QDebug> #include <QtCore/QDirIterator> #include <QtCore/QSettings> -#include <QtGui/QLabel> -#include <QtGui/QComboBox> +#include <QtGui/QFileDialog> #include <QtGui/QCheckBox> +#include <QtGui/QComboBox> +#include <QtGui/QLabel> #include <QtGui/QPushButton> -#include <QtGui/QFileDialog> +#include <QtGui/QTextBlock> using namespace Utils; using namespace Find; diff --git a/src/plugins/texteditor/basetextdocument.cpp b/src/plugins/texteditor/basetextdocument.cpp index f1bf0e0f1307d5edabe34da7aa715ec5c66222d6..f9999feb78a37d03d7307defac99b465c38f0af3 100644 --- a/src/plugins/texteditor/basetextdocument.cpp +++ b/src/plugins/texteditor/basetextdocument.cpp @@ -28,6 +28,8 @@ **************************************************************************/ #include "basetextdocument.h" + +#include "basetextdocumentlayout.h" #include "basetexteditor.h" #include "storagesettings.h" @@ -52,6 +54,75 @@ DocumentMarker::DocumentMarker(QTextDocument *doc) { } +bool DocumentMarker::addMark(TextEditor::ITextMark *mark, int line) +{ + QTC_ASSERT(line >= 1, return false); + int blockNumber = line - 1; + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document->documentLayout()); + QTC_ASSERT(documentLayout, return false); + QTextBlock block = document->findBlockByNumber(blockNumber); + + if (block.isValid()) { + TextBlockUserData *userData = TextEditDocumentLayout::userData(block); + userData->addMark(mark); + mark->updateLineNumber(blockNumber + 1); + mark->updateBlock(block); + documentLayout->hasMarks = true; + documentLayout->requestUpdate(); + return true; + } + return false; +} + +TextEditor::TextMarks DocumentMarker::marksAt(int line) const +{ + QTC_ASSERT(line >= 1, return TextMarks()); + int blockNumber = line - 1; + QTextBlock block = document->findBlockByNumber(blockNumber); + + if (block.isValid()) { + if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(block)) + return userData->marks(); + } + return TextMarks(); +} + +void DocumentMarker::removeMark(TextEditor::ITextMark *mark) +{ + bool needUpdate = false; + QTextBlock block = document->begin(); + while (block.isValid()) { + if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) { + needUpdate |= data->removeMark(mark); + } + block = block.next(); + } + if (needUpdate) + updateMark(0); +} + +bool DocumentMarker::hasMark(TextEditor::ITextMark *mark) const +{ + QTextBlock block = document->begin(); + while (block.isValid()) { + if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) { + if (data->hasMark(mark)) + return true; + } + block = block.next(); + } + return false; +} + +void DocumentMarker::updateMark(ITextMark *mark) +{ + Q_UNUSED(mark) + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document->documentLayout()); + QTC_ASSERT(documentLayout, return); + documentLayout->requestUpdate(); +} + + BaseTextDocument::BaseTextDocument() : m_document(new QTextDocument(this)), m_highlighter(0) diff --git a/src/plugins/texteditor/basetextdocumentlayout.cpp b/src/plugins/texteditor/basetextdocumentlayout.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d153e62515ad60aa739bceb288613e14e678fca1 --- /dev/null +++ b/src/plugins/texteditor/basetextdocumentlayout.cpp @@ -0,0 +1,593 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "basetextdocumentlayout.h" + +using namespace TextEditor; + +bool Parenthesis::hasClosingCollapse(const Parentheses &parentheses) +{ + return closeCollapseAtPos(parentheses) >= 0; +} + +int Parenthesis::closeCollapseAtPos(const Parentheses &parentheses) +{ + int depth = 0; + for (int i = 0; i < parentheses.size(); ++i) { + const Parenthesis &p = parentheses.at(i); + if (p.chr == QLatin1Char('{') + || p.chr == QLatin1Char('+') + || p.chr == QLatin1Char('[')) { + ++depth; + } else if (p.chr == QLatin1Char('}') + || p.chr == QLatin1Char('-') + || p.chr == QLatin1Char(']')) { + if (--depth < 0) + return p.pos; + } + } + return -1; +} + +int Parenthesis::collapseAtPos(const Parentheses &parentheses, QChar *character) +{ + int result = -1; + QChar c; + + int depth = 0; + for (int i = 0; i < parentheses.size(); ++i) { + const Parenthesis &p = parentheses.at(i); + if (p.chr == QLatin1Char('{') + || p.chr == QLatin1Char('+') + || p.chr == QLatin1Char('[')) { + if (depth == 0) { + result = p.pos; + c = p.chr; + } + ++depth; + } else if (p.chr == QLatin1Char('}') + || p.chr == QLatin1Char('-') + || p.chr == QLatin1Char(']')) { + if (--depth < 0) + depth = 0; + result = -1; + } + } + if (result >= 0 && character) + *character = c; + return result; +} + + +TextBlockUserData::~TextBlockUserData() +{ + TextMarks marks = m_marks; + m_marks.clear(); + foreach (ITextMark *mrk, marks) { + mrk->removedFromEditor(); + } +} + +int TextBlockUserData::collapseAtPos(QChar *character) const +{ + return Parenthesis::collapseAtPos(m_parentheses, character); +} + +int TextBlockUserData::braceDepthDelta() const +{ + int delta = 0; + for (int i = 0; i < m_parentheses.size(); ++i) { + switch (m_parentheses.at(i).chr.unicode()) { + case '{': case '+': case '[': ++delta; break; + case '}': case '-': case ']': --delta; break; + default: break; + } + } + return delta; +} + + +QTextBlock TextBlockUserData::testCollapse(const QTextBlock& block) +{ + QTextBlock info = block; + if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter) + ; + else if (block.next().userData() + && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode() + == TextBlockUserData::CollapseThis) + info = block.next(); + else + return QTextBlock(); + int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos(); + if (pos < 0) + return QTextBlock(); + QTextCursor cursor(info); + cursor.setPosition(cursor.position() + pos); + matchCursorForward(&cursor); + return cursor.block(); +} + +void TextBlockUserData::doCollapse(const QTextBlock& block, bool visible) +{ + QTextBlock info = block; + if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter) + ; + else if (block.next().userData() + && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode() + == TextBlockUserData::CollapseThis) + info = block.next(); + else { + if (visible && !block.next().isVisible()) { + // no match, at least unfold! + QTextBlock b = block.next(); + while (b.isValid() && !b.isVisible()) { + b.setVisible(true); + b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); + b = b.next(); + } + } + return; + } + int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos(); + if (pos < 0) + return; + QTextCursor cursor(info); + cursor.setPosition(cursor.position() + pos); + if (matchCursorForward(&cursor) != Match) { + if (visible) { + // no match, at least unfold! + QTextBlock b = block.next(); + while (b.isValid() && !b.isVisible()) { + b.setVisible(true); + b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); + b = b.next(); + } + } + return; + } + + QTextBlock b = block.next(); + while (b < cursor.block()) { + b.setVisible(visible); + b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); + if (visible) { + TextBlockUserData *data = canCollapse(b); + if (data && data->collapsed()) { + QTextBlock end = testCollapse(b); + if (data->collapseIncludesClosure()) + end = end.next(); + if (end.isValid()) { + b = end; + continue; + } + } + } + b = b.next(); + } + + bool collapseIncludesClosure = hasClosingCollapseAtEnd(b); + if (collapseIncludesClosure) { + b.setVisible(visible); + b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); + } + static_cast<TextBlockUserData*>(info.userData())->setCollapseIncludesClosure(collapseIncludesClosure); + static_cast<TextBlockUserData*>(info.userData())->setCollapsed(!block.next().isVisible()); + +} + + +TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c) +{ + QTextBlock block = cursor->block(); + if (!TextEditDocumentLayout::hasParentheses(block) || TextEditDocumentLayout::ifdefedOut(block)) + return NoMatch; + + Parentheses parenList = TextEditDocumentLayout::parentheses(block); + Parenthesis openParen, closedParen; + QTextBlock closedParenParag = block; + + const int cursorPos = cursor->position() - closedParenParag.position(); + int i = 0; + int ignore = 0; + bool foundOpen = false; + for (;;) { + if (!foundOpen) { + if (i >= parenList.count()) + return NoMatch; + openParen = parenList.at(i); + if (openParen.pos != cursorPos) { + ++i; + continue; + } else { + foundOpen = true; + ++i; + } + } + + if (i >= parenList.count()) { + for (;;) { + closedParenParag = closedParenParag.next(); + if (!closedParenParag.isValid()) + return NoMatch; + if (TextEditDocumentLayout::hasParentheses(closedParenParag) + && !TextEditDocumentLayout::ifdefedOut(closedParenParag)) { + parenList = TextEditDocumentLayout::parentheses(closedParenParag); + break; + } + } + i = 0; + } + + closedParen = parenList.at(i); + if (closedParen.type == Parenthesis::Opened) { + ignore++; + ++i; + continue; + } else { + if (ignore > 0) { + ignore--; + ++i; + continue; + } + + cursor->clearSelection(); + cursor->setPosition(closedParenParag.position() + closedParen.pos + 1, QTextCursor::KeepAnchor); + + if ((c == QLatin1Char('{') && closedParen.chr != QLatin1Char('}')) + || (c == QLatin1Char('(') && closedParen.chr != QLatin1Char(')')) + || (c == QLatin1Char('[') && closedParen.chr != QLatin1Char(']')) + || (c == QLatin1Char('+') && closedParen.chr != QLatin1Char('-')) + ) + return Mismatch; + + return Match; + } + } +} + +TextBlockUserData::MatchType TextBlockUserData::checkClosedParenthesis(QTextCursor *cursor, QChar c) +{ + QTextBlock block = cursor->block(); + if (!TextEditDocumentLayout::hasParentheses(block) || TextEditDocumentLayout::ifdefedOut(block)) + return NoMatch; + + Parentheses parenList = TextEditDocumentLayout::parentheses(block); + Parenthesis openParen, closedParen; + QTextBlock openParenParag = block; + + const int cursorPos = cursor->position() - openParenParag.position(); + int i = parenList.count() - 1; + int ignore = 0; + bool foundClosed = false; + for (;;) { + if (!foundClosed) { + if (i < 0) + return NoMatch; + closedParen = parenList.at(i); + if (closedParen.pos != cursorPos - 1) { + --i; + continue; + } else { + foundClosed = true; + --i; + } + } + + if (i < 0) { + for (;;) { + openParenParag = openParenParag.previous(); + if (!openParenParag.isValid()) + return NoMatch; + + if (TextEditDocumentLayout::hasParentheses(openParenParag) + && !TextEditDocumentLayout::ifdefedOut(openParenParag)) { + parenList = TextEditDocumentLayout::parentheses(openParenParag); + break; + } + } + i = parenList.count() - 1; + } + + openParen = parenList.at(i); + if (openParen.type == Parenthesis::Closed) { + ignore++; + --i; + continue; + } else { + if (ignore > 0) { + ignore--; + --i; + continue; + } + + cursor->clearSelection(); + cursor->setPosition(openParenParag.position() + openParen.pos, QTextCursor::KeepAnchor); + + if ((c == '}' && openParen.chr != '{') || + (c == ')' && openParen.chr != '(') || + (c == ']' && openParen.chr != '[') || + (c == '-' && openParen.chr != '+')) + return Mismatch; + + return Match; + } + } +} + +bool TextBlockUserData::findPreviousOpenParenthesis(QTextCursor *cursor, bool select) +{ + QTextBlock block = cursor->block(); + int position = cursor->position(); + int ignore = 0; + while (block.isValid()) { + Parentheses parenList = TextEditDocumentLayout::parentheses(block); + if (!parenList.isEmpty() && !TextEditDocumentLayout::ifdefedOut(block)) { + for (int i = parenList.count()-1; i >= 0; --i) { + Parenthesis paren = parenList.at(i); + if (block == cursor->block() && + (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0))) + continue; + if (paren.type == Parenthesis::Closed) { + ++ignore; + } else if (ignore > 0) { + --ignore; + } else { + cursor->setPosition(block.position() + paren.pos, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); + return true; + } + } + } + block = block.previous(); + } + return false; +} + +bool TextBlockUserData::findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition) +{ + QTextBlock block = cursor->block(); + int position = cursor->position(); + int ignore = 0; + while (block.isValid()) { + Parentheses parenList = TextEditDocumentLayout::parentheses(block); + if (!parenList.isEmpty() && !TextEditDocumentLayout::ifdefedOut(block)) { + for (int i = parenList.count()-1; i >= 0; --i) { + Parenthesis paren = parenList.at(i); + if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}') + && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-') + && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']')) + continue; + if (block == cursor->block()) { + if (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0)) + continue; + if (checkStartPosition && paren.type == Parenthesis::Opened && paren.pos== cursor->position()) { + return true; + } + } + if (paren.type == Parenthesis::Closed) { + ++ignore; + } else if (ignore > 0) { + --ignore; + } else { + cursor->setPosition(block.position() + paren.pos); + return true; + } + } + } + block = block.previous(); + } + return false; +} + +bool TextBlockUserData::findNextClosingParenthesis(QTextCursor *cursor, bool select) +{ + QTextBlock block = cursor->block(); + int position = cursor->position(); + int ignore = 0; + while (block.isValid()) { + Parentheses parenList = TextEditDocumentLayout::parentheses(block); + if (!parenList.isEmpty() && !TextEditDocumentLayout::ifdefedOut(block)) { + for (int i = 0; i < parenList.count(); ++i) { + Parenthesis paren = parenList.at(i); + if (block == cursor->block() && + (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0))) + continue; + if (paren.type == Parenthesis::Opened) { + ++ignore; + } else if (ignore > 0) { + --ignore; + } else { + cursor->setPosition(block.position() + paren.pos+1, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); + return true; + } + } + } + block = block.next(); + } + return false; +} + +bool TextBlockUserData::findNextBlockClosingParenthesis(QTextCursor *cursor) +{ + QTextBlock block = cursor->block(); + int position = cursor->position(); + int ignore = 0; + while (block.isValid()) { + Parentheses parenList = TextEditDocumentLayout::parentheses(block); + if (!parenList.isEmpty() && !TextEditDocumentLayout::ifdefedOut(block)) { + for (int i = 0; i < parenList.count(); ++i) { + Parenthesis paren = parenList.at(i); + if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}') + && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-') + && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']')) + continue; + if (block == cursor->block() && + (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0))) + continue; + if (paren.type == Parenthesis::Opened) { + ++ignore; + } else if (ignore > 0) { + --ignore; + } else { + cursor->setPosition(block.position() + paren.pos+1); + return true; + } + } + } + block = block.next(); + } + return false; +} + +TextBlockUserData::MatchType TextBlockUserData::matchCursorBackward(QTextCursor *cursor) +{ + cursor->clearSelection(); + const QTextBlock block = cursor->block(); + + if (!TextEditDocumentLayout::hasParentheses(block) || TextEditDocumentLayout::ifdefedOut(block)) + return NoMatch; + + const int relPos = cursor->position() - block.position(); + + Parentheses parentheses = TextEditDocumentLayout::parentheses(block); + const Parentheses::const_iterator cend = parentheses.constEnd(); + for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) { + const Parenthesis &paren = *it; + if (paren.pos == relPos - 1 + && paren.type == Parenthesis::Closed) { + return checkClosedParenthesis(cursor, paren.chr); + } + } + return NoMatch; +} + +TextBlockUserData::MatchType TextBlockUserData::matchCursorForward(QTextCursor *cursor) +{ + cursor->clearSelection(); + const QTextBlock block = cursor->block(); + + if (!TextEditDocumentLayout::hasParentheses(block) || TextEditDocumentLayout::ifdefedOut(block)) + return NoMatch; + + const int relPos = cursor->position() - block.position(); + + Parentheses parentheses = TextEditDocumentLayout::parentheses(block); + const Parentheses::const_iterator cend = parentheses.constEnd(); + for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) { + const Parenthesis &paren = *it; + if (paren.pos == relPos + && paren.type == Parenthesis::Opened) { + return checkOpenParenthesis(cursor, paren.chr); + } + } + return NoMatch; +} + + +TextEditDocumentLayout::TextEditDocumentLayout(QTextDocument *doc) + :QPlainTextDocumentLayout(doc) { + lastSaveRevision = 0; + hasMarks = 0; +} + +TextEditDocumentLayout::~TextEditDocumentLayout() +{ +} + +void TextEditDocumentLayout::setParentheses(const QTextBlock &block, const Parentheses &parentheses) +{ + if (parentheses.isEmpty()) { + if (TextBlockUserData *userData = testUserData(block)) + userData->clearParentheses(); + } else { + userData(block)->setParentheses(parentheses); + } +} + +Parentheses TextEditDocumentLayout::parentheses(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->parentheses(); + return Parentheses(); +} + +bool TextEditDocumentLayout::hasParentheses(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->hasParentheses(); + return false; +} + +bool TextEditDocumentLayout::setIfdefedOut(const QTextBlock &block) +{ + return userData(block)->setIfdefedOut(); +} + +bool TextEditDocumentLayout::clearIfdefedOut(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->clearIfdefedOut(); + return false; +} + +bool TextEditDocumentLayout::ifdefedOut(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->ifdefedOut(); + return false; +} + +int TextEditDocumentLayout::braceDepthDelta(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->braceDepthDelta(); + return 0; +} + +int TextEditDocumentLayout::braceDepth(const QTextBlock &block) +{ + int state = block.userState(); + if (state == -1) + return 0; + return state >> 8; +} + +void TextEditDocumentLayout::setBraceDepth(QTextBlock &block, int depth) +{ + int state = block.userState(); + if (state == -1) + state = 0; + state = state & 0xff; + block.setUserState((depth << 8) | state); +} + +void TextEditDocumentLayout::changeBraceDepth(QTextBlock &block, int delta) +{ + if (delta) + setBraceDepth(block, braceDepth(block) + delta); +} diff --git a/src/plugins/texteditor/basetextdocumentlayout.h b/src/plugins/texteditor/basetextdocumentlayout.h new file mode 100644 index 0000000000000000000000000000000000000000..163368031ebca2ac2324926eb9e0736aff4e2d93 --- /dev/null +++ b/src/plugins/texteditor/basetextdocumentlayout.h @@ -0,0 +1,214 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef BASETEXTDOCUMENTLAYOUT_H +#define BASETEXTDOCUMENTLAYOUT_H + +#include "texteditor_global.h" + +#include "itexteditor.h" + +#include <QtGui/QTextBlockUserData> +#include <QtGui/QPlainTextDocumentLayout> + +namespace TextEditor { + +struct Parenthesis; +typedef QVector<Parenthesis> Parentheses; + +struct TEXTEDITOR_EXPORT Parenthesis +{ + enum Type { Opened, Closed }; + + inline Parenthesis() : type(Opened), pos(-1) {} + inline Parenthesis(Type t, QChar c, int position) + : type(t), chr(c), pos(position) {} + Type type; + QChar chr; + int pos; + static int collapseAtPos(const Parentheses &parentheses, QChar *character = 0); + static int closeCollapseAtPos(const Parentheses &parentheses); + static bool hasClosingCollapse(const Parentheses &parentheses); +}; + + +class TEXTEDITOR_EXPORT TextBlockUserData : public QTextBlockUserData +{ +public: + + enum CollapseMode { NoCollapse , CollapseThis, CollapseAfter }; + enum ClosingCollapseMode { NoClosingCollapse, ClosingCollapse, ClosingCollapseAtEnd }; + + inline TextBlockUserData() + : m_collapseIncludesClosure(false), + m_collapseMode(NoCollapse), + m_closingCollapseMode(NoClosingCollapse), + m_collapsed(false), + m_ifdefedOut(false) {} + ~TextBlockUserData(); + + inline TextMarks marks() const { return m_marks; } + inline void addMark(ITextMark *mark) { m_marks += mark; } + inline bool removeMark(ITextMark *mark) { return m_marks.removeAll(mark); } + inline bool hasMark(ITextMark *mark) const { return m_marks.contains(mark); } + inline void clearMarks() { m_marks.clear(); } + inline void documentClosing() { Q_FOREACH(ITextMark *tm, m_marks) { tm->documentClosing(); } m_marks.clear();} + + inline CollapseMode collapseMode() const { return (CollapseMode)m_collapseMode; } + inline void setCollapseMode(CollapseMode c) { m_collapseMode = c; } + + inline void setClosingCollapseMode(ClosingCollapseMode c) { m_closingCollapseMode = c; } + inline ClosingCollapseMode closingCollapseMode() const { return (ClosingCollapseMode) m_closingCollapseMode; } + + inline bool hasClosingCollapse() const { return closingCollapseMode() != NoClosingCollapse; } + inline bool hasClosingCollapseAtEnd() const { return closingCollapseMode() == ClosingCollapseAtEnd; } + inline bool hasClosingCollapseInside() const { return closingCollapseMode() == ClosingCollapse; } + + inline void setCollapsed(bool b) { m_collapsed = b; } + inline bool collapsed() const { return m_collapsed; } + + inline void setCollapseIncludesClosure(bool b) { m_collapseIncludesClosure = b; } + inline bool collapseIncludesClosure() const { return m_collapseIncludesClosure; } + + inline void setParentheses(const Parentheses &parentheses) { m_parentheses = parentheses; } + inline void clearParentheses() { m_parentheses.clear(); } + inline const Parentheses &parentheses() const { return m_parentheses; } + inline bool hasParentheses() const { return !m_parentheses.isEmpty(); } + int braceDepthDelta() const; + + inline bool setIfdefedOut() { bool result = m_ifdefedOut; m_ifdefedOut = true; return !result; } + inline bool clearIfdefedOut() { bool result = m_ifdefedOut; m_ifdefedOut = false; return result;} + inline bool ifdefedOut() const { return m_ifdefedOut; } + + inline static TextBlockUserData *canCollapse(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + if (!data || data->collapseMode() != CollapseAfter) { + data = static_cast<TextBlockUserData*>(block.next().userData()); + if (!data || data->collapseMode() != TextBlockUserData::CollapseThis) + data = 0; + } + if (data && data->m_ifdefedOut) + data = 0; + return data; + } + + inline static bool hasCollapseAfter(const QTextBlock & block) + { + if (!block.isValid()) { + return false; + } else if (block.next().isValid()) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.next().userData()); + if (data && data->collapseMode() == TextBlockUserData::CollapseThis && !data->m_ifdefedOut) + return true; + } + return false; + } + + inline static bool hasClosingCollapse(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + return (data && data->hasClosingCollapse()); + } + + inline static bool hasClosingCollapseAtEnd(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + return (data && data->hasClosingCollapseAtEnd()); + } + + inline static bool hasClosingCollapseInside(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + return (data && data->hasClosingCollapseInside()); + } + + static QTextBlock testCollapse(const QTextBlock& block); + static void doCollapse(const QTextBlock& block, bool visible); + + int collapseAtPos(QChar *character = 0) const; + + enum MatchType { NoMatch, Match, Mismatch }; + static MatchType checkOpenParenthesis(QTextCursor *cursor, QChar c); + static MatchType checkClosedParenthesis(QTextCursor *cursor, QChar c); + static MatchType matchCursorBackward(QTextCursor *cursor); + static MatchType matchCursorForward(QTextCursor *cursor); + static bool findPreviousOpenParenthesis(QTextCursor *cursor, bool select = false); + static bool findNextClosingParenthesis(QTextCursor *cursor, bool select = false); + + static bool findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition = false); + static bool findNextBlockClosingParenthesis(QTextCursor *cursor); + + +private: + TextMarks m_marks; + uint m_collapseIncludesClosure : 1; + uint m_collapseMode : 4; + uint m_closingCollapseMode : 4; + uint m_collapsed : 1; + uint m_ifdefedOut : 1; + Parentheses m_parentheses; +}; + + +class TEXTEDITOR_EXPORT TextEditDocumentLayout : public QPlainTextDocumentLayout +{ + Q_OBJECT + +public: + TextEditDocumentLayout(QTextDocument *doc); + ~TextEditDocumentLayout(); + + static void setParentheses(const QTextBlock &block, const Parentheses &parentheses); + static void clearParentheses(const QTextBlock &block) { setParentheses(block, Parentheses());} + static Parentheses parentheses(const QTextBlock &block); + static bool hasParentheses(const QTextBlock &block); + static bool setIfdefedOut(const QTextBlock &block); + static bool clearIfdefedOut(const QTextBlock &block); + static bool ifdefedOut(const QTextBlock &block); + static int braceDepthDelta(const QTextBlock &block); + static int braceDepth(const QTextBlock &block); + static void setBraceDepth(QTextBlock &block, int depth); + static void changeBraceDepth(QTextBlock &block, int delta); + + static TextBlockUserData *testUserData(const QTextBlock &block) { + return static_cast<TextBlockUserData*>(block.userData()); + } + static TextBlockUserData *userData(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + if (!data && block.isValid()) + const_cast<QTextBlock &>(block).setUserData((data = new TextBlockUserData)); + return data; + } + + + void emitDocumentSizeChanged() { emit documentSizeChanged(documentSize()); } + int lastSaveRevision; + bool hasMarks; +}; + +} // namespace TextEditor + +#endif // BASETEXTDOCUMENTLAYOUT_H diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index 0e53e88d40c4f4b40e5831a3c310d717b56ce6eb..9c436200b930947031c3997400b884a94205f4ca 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -30,6 +30,7 @@ #include "texteditor_global.h" #include "basetextdocument.h" +#include "basetextdocumentlayout.h" #include "basetexteditor_p.h" #include "behaviorsettings.h" #include "codecselector.h" @@ -325,7 +326,6 @@ static void printPage(int index, QPainter *painter, const QTextDocument *doc, void BaseTextEditorPrivate::print(QPrinter *printer) { - QTextDocument *doc = q->document(); QString title = q->displayName(); @@ -461,26 +461,6 @@ UserCanceled: } -bool DocumentMarker::addMark(TextEditor::ITextMark *mark, int line) -{ - QTC_ASSERT(line >= 1, return false); - int blockNumber = line - 1; - TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document->documentLayout()); - QTC_ASSERT(documentLayout, return false); - QTextBlock block = document->findBlockByNumber(blockNumber); - - if (block.isValid()) { - TextBlockUserData *userData = TextEditDocumentLayout::userData(block); - userData->addMark(mark); - mark->updateLineNumber(blockNumber + 1); - mark->updateBlock(block); - documentLayout->hasMarks = true; - documentLayout->requestUpdate(); - return true; - } - return false; -} - int BaseTextEditorPrivate::visualIndent(const QTextBlock &block) const { if (!block.isValid()) @@ -499,47 +479,6 @@ int BaseTextEditorPrivate::visualIndent(const QTextBlock &block) const return 0; } -TextEditor::TextMarks DocumentMarker::marksAt(int line) const -{ - QTC_ASSERT(line >= 1, return TextMarks()); - int blockNumber = line - 1; - QTextBlock block = document->findBlockByNumber(blockNumber); - - if (block.isValid()) { - if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(block)) - return userData->marks(); - } - return TextMarks(); -} - -void DocumentMarker::removeMark(TextEditor::ITextMark *mark) -{ - bool needUpdate = false; - QTextBlock block = document->begin(); - while (block.isValid()) { - if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) { - needUpdate |= data->removeMark(mark); - } - block = block.next(); - } - if (needUpdate) - updateMark(0); -} - - -bool DocumentMarker::hasMark(TextEditor::ITextMark *mark) const -{ - QTextBlock block = document->begin(); - while (block.isValid()) { - if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) { - if (data->hasMark(mark)) - return true; - } - block = block.next(); - } - return false; -} - ITextMarkable *BaseTextEditor::markableInterface() const { return baseTextDocument()->documentMarker(); @@ -618,15 +557,6 @@ void BaseTextEditor::selectEncoding() } } -void DocumentMarker::updateMark(ITextMark *mark) -{ - Q_UNUSED(mark) - TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document->documentLayout()); - QTC_ASSERT(documentLayout, return); - documentLayout->requestUpdate(); -} - - void BaseTextEditor::triggerCompletions() { emit requestAutoCompletion(editableInterface(), true); @@ -809,7 +739,8 @@ void BaseTextEditor::gotoBlockEndWithSelection() } } -static QTextCursor flippedCursor(const QTextCursor &cursor) { +static QTextCursor flippedCursor(const QTextCursor &cursor) +{ QTextCursor flipped = cursor; flipped.clearSelection(); flipped.setPosition(cursor.anchor(), QTextCursor::KeepAnchor); @@ -1917,169 +1848,6 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward) q->setTextCursor(cursor); } -bool Parenthesis::hasClosingCollapse(const Parentheses &parentheses) -{ - return closeCollapseAtPos(parentheses) >= 0; -} - - -int Parenthesis::closeCollapseAtPos(const Parentheses &parentheses) -{ - int depth = 0; - for (int i = 0; i < parentheses.size(); ++i) { - const Parenthesis &p = parentheses.at(i); - if (p.chr == QLatin1Char('{') - || p.chr == QLatin1Char('+') - || p.chr == QLatin1Char('[')) { - ++depth; - } else if (p.chr == QLatin1Char('}') - || p.chr == QLatin1Char('-') - || p.chr == QLatin1Char(']')) { - if (--depth < 0) - return p.pos; - } - } - return -1; -} - -int Parenthesis::collapseAtPos(const Parentheses &parentheses, QChar *character) -{ - int result = -1; - QChar c; - - int depth = 0; - for (int i = 0; i < parentheses.size(); ++i) { - const Parenthesis &p = parentheses.at(i); - if (p.chr == QLatin1Char('{') - || p.chr == QLatin1Char('+') - || p.chr == QLatin1Char('[')) { - if (depth == 0) { - result = p.pos; - c = p.chr; - } - ++depth; - } else if (p.chr == QLatin1Char('}') - || p.chr == QLatin1Char('-') - || p.chr == QLatin1Char(']')) { - if (--depth < 0) - depth = 0; - result = -1; - } - } - if (result >= 0 && character) - *character = c; - return result; -} - - -int TextBlockUserData::collapseAtPos(QChar *character) const -{ - return Parenthesis::collapseAtPos(m_parentheses, character); -} - -int TextBlockUserData::braceDepthDelta() const -{ - int delta = 0; - for (int i = 0; i < m_parentheses.size(); ++i) { - switch (m_parentheses.at(i).chr.unicode()) { - case '{': case '+': case '[': ++delta; break; - case '}': case '-': case ']': --delta; break; - default: break; - } - } - return delta; -} - -void TextEditDocumentLayout::setParentheses(const QTextBlock &block, const Parentheses &parentheses) -{ - if (parentheses.isEmpty()) { - if (TextBlockUserData *userData = testUserData(block)) - userData->clearParentheses(); - } else { - userData(block)->setParentheses(parentheses); - } -} - -Parentheses TextEditDocumentLayout::parentheses(const QTextBlock &block) -{ - if (TextBlockUserData *userData = testUserData(block)) - return userData->parentheses(); - return Parentheses(); -} - -bool TextEditDocumentLayout::hasParentheses(const QTextBlock &block) -{ - if (TextBlockUserData *userData = testUserData(block)) - return userData->hasParentheses(); - return false; -} - -int TextEditDocumentLayout::braceDepthDelta(const QTextBlock &block) -{ - if (TextBlockUserData *userData = testUserData(block)) - return userData->braceDepthDelta(); - return 0; -} - -int TextEditDocumentLayout::braceDepth(const QTextBlock &block) -{ - int state = block.userState(); - if (state == -1) - return 0; - return state >> 8; -} - -void TextEditDocumentLayout::setBraceDepth(QTextBlock &block, int depth) -{ - int state = block.userState(); - if (state == -1) - state = 0; - state = state & 0xff; - block.setUserState((depth << 8) | state); -} - -void TextEditDocumentLayout::changeBraceDepth(QTextBlock &block, int delta) -{ - if (delta) - setBraceDepth(block, braceDepth(block) + delta); -} - -bool TextEditDocumentLayout::setIfdefedOut(const QTextBlock &block) -{ - return userData(block)->setIfdefedOut(); -} - -bool TextEditDocumentLayout::clearIfdefedOut(const QTextBlock &block) -{ - if (TextBlockUserData *userData = testUserData(block)) - return userData->clearIfdefedOut(); - return false; -} - -bool TextEditDocumentLayout::ifdefedOut(const QTextBlock &block) -{ - if (TextBlockUserData *userData = testUserData(block)) - return userData->ifdefedOut(); - return false; -} - - -TextEditDocumentLayout::TextEditDocumentLayout(QTextDocument *doc) - :QPlainTextDocumentLayout(doc) { - lastSaveRevision = 0; - hasMarks = 0; -} - -TextEditDocumentLayout::~TextEditDocumentLayout() -{ -} - -QRectF TextEditDocumentLayout::blockBoundingRect(const QTextBlock &block) const -{ - QRectF r = QPlainTextDocumentLayout::blockBoundingRect(block); - return r; -} - bool BaseTextEditor::viewportEvent(QEvent *event) { @@ -2137,7 +1905,8 @@ QRect BaseTextEditor::collapseBox() er.bottom() - br.top()); } -QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const { +QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const +{ QPointF offset(contentOffset()); QTextBlock block = firstVisibleBlock(); int top = (int)blockBoundingGeometry(block).translated(offset).top(); @@ -3742,95 +3511,6 @@ void BaseTextEditor::extraAreaMouseEvent(QMouseEvent *e) } } -QTextBlock TextBlockUserData::testCollapse(const QTextBlock& block) -{ - QTextBlock info = block; - if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter) - ; - else if (block.next().userData() - && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode() - == TextBlockUserData::CollapseThis) - info = block.next(); - else - return QTextBlock(); - int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos(); - if (pos < 0) - return QTextBlock(); - QTextCursor cursor(info); - cursor.setPosition(cursor.position() + pos); - matchCursorForward(&cursor); - return cursor.block(); -} - -void TextBlockUserData::doCollapse(const QTextBlock& block, bool visible) -{ - QTextBlock info = block; - if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter) - ; - else if (block.next().userData() - && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode() - == TextBlockUserData::CollapseThis) - info = block.next(); - else { - if (visible && !block.next().isVisible()) { - // no match, at least unfold! - QTextBlock b = block.next(); - while (b.isValid() && !b.isVisible()) { - b.setVisible(true); - b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); - b = b.next(); - } - } - return; - } - int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos(); - if (pos < 0) - return; - QTextCursor cursor(info); - cursor.setPosition(cursor.position() + pos); - if (matchCursorForward(&cursor) != Match) { - if (visible) { - // no match, at least unfold! - QTextBlock b = block.next(); - while (b.isValid() && !b.isVisible()) { - b.setVisible(true); - b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); - b = b.next(); - } - } - return; - } - - QTextBlock b = block.next(); - while (b < cursor.block()) { - b.setVisible(visible); - b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); - if (visible) { - TextBlockUserData *data = canCollapse(b); - if (data && data->collapsed()) { - QTextBlock end = testCollapse(b); - if (data->collapseIncludesClosure()) - end = end.next(); - if (end.isValid()) { - b = end; - continue; - } - } - } - b = b.next(); - } - - bool collapseIncludesClosure = hasClosingCollapseAtEnd(b); - if (collapseIncludesClosure) { - b.setVisible(visible); - b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); - } - static_cast<TextBlockUserData*>(info.userData())->setCollapseIncludesClosure(collapseIncludesClosure); - static_cast<TextBlockUserData*>(info.userData())->setCollapsed(!block.next().isVisible()); - -} - - void BaseTextEditor::ensureCursorVisible() { QTextBlock block = textCursor().block(); @@ -4433,315 +4113,6 @@ void BaseTextEditor::markBlocksAsChanged(QList<int> blockNumbers) } - -TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c) -{ - QTextBlock block = cursor->block(); - if (!TextEditDocumentLayout::hasParentheses(block) || TextEditDocumentLayout::ifdefedOut(block)) - return NoMatch; - - Parentheses parenList = TextEditDocumentLayout::parentheses(block); - Parenthesis openParen, closedParen; - QTextBlock closedParenParag = block; - - const int cursorPos = cursor->position() - closedParenParag.position(); - int i = 0; - int ignore = 0; - bool foundOpen = false; - for (;;) { - if (!foundOpen) { - if (i >= parenList.count()) - return NoMatch; - openParen = parenList.at(i); - if (openParen.pos != cursorPos) { - ++i; - continue; - } else { - foundOpen = true; - ++i; - } - } - - if (i >= parenList.count()) { - for (;;) { - closedParenParag = closedParenParag.next(); - if (!closedParenParag.isValid()) - return NoMatch; - if (TextEditDocumentLayout::hasParentheses(closedParenParag) - && !TextEditDocumentLayout::ifdefedOut(closedParenParag)) { - parenList = TextEditDocumentLayout::parentheses(closedParenParag); - break; - } - } - i = 0; - } - - closedParen = parenList.at(i); - if (closedParen.type == Parenthesis::Opened) { - ignore++; - ++i; - continue; - } else { - if (ignore > 0) { - ignore--; - ++i; - continue; - } - - cursor->clearSelection(); - cursor->setPosition(closedParenParag.position() + closedParen.pos + 1, QTextCursor::KeepAnchor); - - if ((c == QLatin1Char('{') && closedParen.chr != QLatin1Char('}')) - || (c == QLatin1Char('(') && closedParen.chr != QLatin1Char(')')) - || (c == QLatin1Char('[') && closedParen.chr != QLatin1Char(']')) - || (c == QLatin1Char('+') && closedParen.chr != QLatin1Char('-')) - ) - return Mismatch; - - return Match; - } - } -} - -TextBlockUserData::MatchType TextBlockUserData::checkClosedParenthesis(QTextCursor *cursor, QChar c) -{ - QTextBlock block = cursor->block(); - if (!TextEditDocumentLayout::hasParentheses(block) || TextEditDocumentLayout::ifdefedOut(block)) - return NoMatch; - - Parentheses parenList = TextEditDocumentLayout::parentheses(block); - Parenthesis openParen, closedParen; - QTextBlock openParenParag = block; - - const int cursorPos = cursor->position() - openParenParag.position(); - int i = parenList.count() - 1; - int ignore = 0; - bool foundClosed = false; - for (;;) { - if (!foundClosed) { - if (i < 0) - return NoMatch; - closedParen = parenList.at(i); - if (closedParen.pos != cursorPos - 1) { - --i; - continue; - } else { - foundClosed = true; - --i; - } - } - - if (i < 0) { - for (;;) { - openParenParag = openParenParag.previous(); - if (!openParenParag.isValid()) - return NoMatch; - - if (TextEditDocumentLayout::hasParentheses(openParenParag) - && !TextEditDocumentLayout::ifdefedOut(openParenParag)) { - parenList = TextEditDocumentLayout::parentheses(openParenParag); - break; - } - } - i = parenList.count() - 1; - } - - openParen = parenList.at(i); - if (openParen.type == Parenthesis::Closed) { - ignore++; - --i; - continue; - } else { - if (ignore > 0) { - ignore--; - --i; - continue; - } - - cursor->clearSelection(); - cursor->setPosition(openParenParag.position() + openParen.pos, QTextCursor::KeepAnchor); - - if ((c == '}' && openParen.chr != '{') || - (c == ')' && openParen.chr != '(') || - (c == ']' && openParen.chr != '[') || - (c == '-' && openParen.chr != '+')) - return Mismatch; - - return Match; - } - } -} - - -bool TextBlockUserData::findPreviousOpenParenthesis(QTextCursor *cursor, bool select) -{ - QTextBlock block = cursor->block(); - int position = cursor->position(); - int ignore = 0; - while (block.isValid()) { - Parentheses parenList = TextEditDocumentLayout::parentheses(block); - if (!parenList.isEmpty() && !TextEditDocumentLayout::ifdefedOut(block)) { - for (int i = parenList.count()-1; i >= 0; --i) { - Parenthesis paren = parenList.at(i); - if (block == cursor->block() && - (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0))) - continue; - if (paren.type == Parenthesis::Closed) { - ++ignore; - } else if (ignore > 0) { - --ignore; - } else { - cursor->setPosition(block.position() + paren.pos, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); - return true; - } - } - } - block = block.previous(); - } - return false; -} - -bool TextBlockUserData::findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition) -{ - QTextBlock block = cursor->block(); - int position = cursor->position(); - int ignore = 0; - while (block.isValid()) { - Parentheses parenList = TextEditDocumentLayout::parentheses(block); - if (!parenList.isEmpty() && !TextEditDocumentLayout::ifdefedOut(block)) { - for (int i = parenList.count()-1; i >= 0; --i) { - Parenthesis paren = parenList.at(i); - if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}') - && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-') - && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']')) - continue; - if (block == cursor->block()) { - if (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0)) - continue; - if (checkStartPosition && paren.type == Parenthesis::Opened && paren.pos== cursor->position()) { - return true; - } - } - if (paren.type == Parenthesis::Closed) { - ++ignore; - } else if (ignore > 0) { - --ignore; - } else { - cursor->setPosition(block.position() + paren.pos); - return true; - } - } - } - block = block.previous(); - } - return false; -} - -bool TextBlockUserData::findNextClosingParenthesis(QTextCursor *cursor, bool select) -{ - QTextBlock block = cursor->block(); - int position = cursor->position(); - int ignore = 0; - while (block.isValid()) { - Parentheses parenList = TextEditDocumentLayout::parentheses(block); - if (!parenList.isEmpty() && !TextEditDocumentLayout::ifdefedOut(block)) { - for (int i = 0; i < parenList.count(); ++i) { - Parenthesis paren = parenList.at(i); - if (block == cursor->block() && - (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0))) - continue; - if (paren.type == Parenthesis::Opened) { - ++ignore; - } else if (ignore > 0) { - --ignore; - } else { - cursor->setPosition(block.position() + paren.pos+1, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); - return true; - } - } - } - block = block.next(); - } - return false; -} - -bool TextBlockUserData::findNextBlockClosingParenthesis(QTextCursor *cursor) -{ - QTextBlock block = cursor->block(); - int position = cursor->position(); - int ignore = 0; - while (block.isValid()) { - Parentheses parenList = TextEditDocumentLayout::parentheses(block); - if (!parenList.isEmpty() && !TextEditDocumentLayout::ifdefedOut(block)) { - for (int i = 0; i < parenList.count(); ++i) { - Parenthesis paren = parenList.at(i); - if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}') - && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-') - && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']')) - continue; - if (block == cursor->block() && - (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0))) - continue; - if (paren.type == Parenthesis::Opened) { - ++ignore; - } else if (ignore > 0) { - --ignore; - } else { - cursor->setPosition(block.position() + paren.pos+1); - return true; - } - } - } - block = block.next(); - } - return false; -} - -TextBlockUserData::MatchType TextBlockUserData::matchCursorBackward(QTextCursor *cursor) -{ - cursor->clearSelection(); - const QTextBlock block = cursor->block(); - - if (!TextEditDocumentLayout::hasParentheses(block) || TextEditDocumentLayout::ifdefedOut(block)) - return NoMatch; - - const int relPos = cursor->position() - block.position(); - - Parentheses parentheses = TextEditDocumentLayout::parentheses(block); - const Parentheses::const_iterator cend = parentheses.constEnd(); - for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) { - const Parenthesis &paren = *it; - if (paren.pos == relPos - 1 - && paren.type == Parenthesis::Closed) { - return checkClosedParenthesis(cursor, paren.chr); - } - } - return NoMatch; -} - -TextBlockUserData::MatchType TextBlockUserData::matchCursorForward(QTextCursor *cursor) -{ - cursor->clearSelection(); - const QTextBlock block = cursor->block(); - - if (!TextEditDocumentLayout::hasParentheses(block) || TextEditDocumentLayout::ifdefedOut(block)) - return NoMatch; - - const int relPos = cursor->position() - block.position(); - - Parentheses parentheses = TextEditDocumentLayout::parentheses(block); - const Parentheses::const_iterator cend = parentheses.constEnd(); - for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) { - const Parenthesis &paren = *it; - if (paren.pos == relPos - && paren.type == Parenthesis::Opened) { - return checkOpenParenthesis(cursor, paren.chr); - } - } - return NoMatch; -} - - void BaseTextEditor::highlightSearchResults(const QString &txt, Find::IFindSupport::FindFlags findFlags) { QString pattern = txt; @@ -5721,6 +5092,23 @@ void BaseTextEditor::insertFromMimeData(const QMimeData *source) setTextCursor(cursor); } +void BaseTextEditor::appendStandardContextMenuActions(QMenu *menu) +{ + menu->addSeparator(); + Core::ActionManager *am = Core::ICore::instance()->actionManager(); + + QAction *a = am->command(Core::Constants::CUT)->action(); + if (a && a->isEnabled()) + menu->addAction(a); + a = am->command(Core::Constants::COPY)->action(); + if (a && a->isEnabled()) + menu->addAction(a); + a = am->command(Core::Constants::PASTE)->action(); + if (a && a->isEnabled()) + menu->addAction(a); +} + + BaseTextEditorEditable::BaseTextEditorEditable(BaseTextEditor *editor) : e(editor) { @@ -5751,23 +5139,6 @@ BaseTextEditorEditable::BaseTextEditorEditable(BaseTextEditor *editor) connect(editor, SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition())); } -void BaseTextEditor::appendStandardContextMenuActions(QMenu *menu) -{ - menu->addSeparator(); - Core::ActionManager *am = Core::ICore::instance()->actionManager(); - - QAction *a = am->command(Core::Constants::CUT)->action(); - if (a && a->isEnabled()) - menu->addAction(a); - a = am->command(Core::Constants::COPY)->action(); - if (a && a->isEnabled()) - menu->addAction(a); - a = am->command(Core::Constants::PASTE)->action(); - if (a && a->isEnabled()) - menu->addAction(a); -} - - BaseTextEditorEditable::~BaseTextEditorEditable() { delete m_toolBar; @@ -5889,14 +5260,3 @@ QString BaseTextEditorEditable::contextHelpId() const e->textCursor().position()); return m_contextHelpId; } - - -TextBlockUserData::~TextBlockUserData() -{ - TextMarks marks = m_marks; - m_marks.clear(); - foreach (ITextMark *mrk, marks) { - mrk->removedFromEditor(); - } -} - diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index 506090ae22bd7f866205e0c49aa5d666fb1d7563..67ef63858d6c2a1c4959dc46f9b729af56f0e2f8 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -35,10 +35,8 @@ #include <find/ifindsupport.h> #include <QtGui/QPlainTextEdit> -#include <QtGui/QTextBlockUserData> QT_BEGIN_NAMESPACE -class QKeyEvent; class QToolBar; class QTimeLine; QT_END_NAMESPACE @@ -55,191 +53,16 @@ namespace Internal { class TextEditorOverlay; } -class ITextMark; class ITextMarkable; -class TextEditorActionHandler; class BaseTextDocument; +class BaseTextEditorEditable; class FontSettings; struct BehaviorSettings; struct DisplaySettings; struct StorageSettings; struct TabSettings; -struct Parenthesis; -typedef QVector<Parenthesis> Parentheses; - -struct TEXTEDITOR_EXPORT Parenthesis -{ - enum Type { Opened, Closed }; - - inline Parenthesis() : type(Opened), pos(-1) {} - inline Parenthesis(Type t, QChar c, int position) - : type(t), chr(c), pos(position) {} - Type type; - QChar chr; - int pos; - static int collapseAtPos(const Parentheses &parentheses, QChar *character = 0); - static int closeCollapseAtPos(const Parentheses &parentheses); - static bool hasClosingCollapse(const Parentheses &parentheses); -}; - - -class TEXTEDITOR_EXPORT TextBlockUserData : public QTextBlockUserData -{ -public: - - enum CollapseMode { NoCollapse , CollapseThis, CollapseAfter }; - enum ClosingCollapseMode { NoClosingCollapse, ClosingCollapse, ClosingCollapseAtEnd }; - - inline TextBlockUserData() - : m_collapseIncludesClosure(false), - m_collapseMode(NoCollapse), - m_closingCollapseMode(NoClosingCollapse), - m_collapsed(false), - m_ifdefedOut(false) {} - ~TextBlockUserData(); - - inline TextMarks marks() const { return m_marks; } - inline void addMark(ITextMark *mark) { m_marks += mark; } - inline bool removeMark(ITextMark *mark) { return m_marks.removeAll(mark); } - inline bool hasMark(ITextMark *mark) const { return m_marks.contains(mark); } - inline void clearMarks() { m_marks.clear(); } - inline void documentClosing() { Q_FOREACH(ITextMark *tm, m_marks) { tm->documentClosing(); } m_marks.clear();} - - inline CollapseMode collapseMode() const { return (CollapseMode)m_collapseMode; } - inline void setCollapseMode(CollapseMode c) { m_collapseMode = c; } - - inline void setClosingCollapseMode(ClosingCollapseMode c) { m_closingCollapseMode = c; } - inline ClosingCollapseMode closingCollapseMode() const { return (ClosingCollapseMode) m_closingCollapseMode; } - - inline bool hasClosingCollapse() const { return closingCollapseMode() != NoClosingCollapse; } - inline bool hasClosingCollapseAtEnd() const { return closingCollapseMode() == ClosingCollapseAtEnd; } - inline bool hasClosingCollapseInside() const { return closingCollapseMode() == ClosingCollapse; } - - inline void setCollapsed(bool b) { m_collapsed = b; } - inline bool collapsed() const { return m_collapsed; } - - inline void setCollapseIncludesClosure(bool b) { m_collapseIncludesClosure = b; } - inline bool collapseIncludesClosure() const { return m_collapseIncludesClosure; } - - inline void setParentheses(const Parentheses &parentheses) { m_parentheses = parentheses; } - inline void clearParentheses() { m_parentheses.clear(); } - inline const Parentheses &parentheses() const { return m_parentheses; } - inline bool hasParentheses() const { return !m_parentheses.isEmpty(); } - int braceDepthDelta() const; - - inline bool setIfdefedOut() { bool result = m_ifdefedOut; m_ifdefedOut = true; return !result; } - inline bool clearIfdefedOut() { bool result = m_ifdefedOut; m_ifdefedOut = false; return result;} - inline bool ifdefedOut() const { return m_ifdefedOut; } - - inline static TextBlockUserData *canCollapse(const QTextBlock &block) { - TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); - if (!data || data->collapseMode() != CollapseAfter) { - data = static_cast<TextBlockUserData*>(block.next().userData()); - if (!data || data->collapseMode() != TextBlockUserData::CollapseThis) - data = 0; - } - if (data && data->m_ifdefedOut) - data = 0; - return data; - } - - inline static bool hasCollapseAfter(const QTextBlock & block) - { - if (!block.isValid()) { - return false; - } else if (block.next().isValid()) { - TextBlockUserData *data = static_cast<TextBlockUserData*>(block.next().userData()); - if (data && data->collapseMode() == TextBlockUserData::CollapseThis && !data->m_ifdefedOut) - return true; - } - return false; - } - - inline static bool hasClosingCollapse(const QTextBlock &block) { - TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); - return (data && data->hasClosingCollapse()); - } - - inline static bool hasClosingCollapseAtEnd(const QTextBlock &block) { - TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); - return (data && data->hasClosingCollapseAtEnd()); - } - - inline static bool hasClosingCollapseInside(const QTextBlock &block) { - TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); - return (data && data->hasClosingCollapseInside()); - } - - static QTextBlock testCollapse(const QTextBlock& block); - static void doCollapse(const QTextBlock& block, bool visible); - - int collapseAtPos(QChar *character = 0) const; - - enum MatchType { NoMatch, Match, Mismatch }; - static MatchType checkOpenParenthesis(QTextCursor *cursor, QChar c); - static MatchType checkClosedParenthesis(QTextCursor *cursor, QChar c); - static MatchType matchCursorBackward(QTextCursor *cursor); - static MatchType matchCursorForward(QTextCursor *cursor); - static bool findPreviousOpenParenthesis(QTextCursor *cursor, bool select = false); - static bool findNextClosingParenthesis(QTextCursor *cursor, bool select = false); - - static bool findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition = false); - static bool findNextBlockClosingParenthesis(QTextCursor *cursor); - - -private: - TextMarks m_marks; - uint m_collapseIncludesClosure : 1; - uint m_collapseMode : 4; - uint m_closingCollapseMode : 4; - uint m_collapsed : 1; - uint m_ifdefedOut : 1; - Parentheses m_parentheses; -}; - - -class TEXTEDITOR_EXPORT TextEditDocumentLayout : public QPlainTextDocumentLayout -{ - Q_OBJECT - -public: - TextEditDocumentLayout(QTextDocument *doc); - ~TextEditDocumentLayout(); - - QRectF blockBoundingRect(const QTextBlock &block) const; - - static void setParentheses(const QTextBlock &block, const Parentheses &parentheses); - static void clearParentheses(const QTextBlock &block) { setParentheses(block, Parentheses());} - static Parentheses parentheses(const QTextBlock &block); - static bool hasParentheses(const QTextBlock &block); - static bool setIfdefedOut(const QTextBlock &block); - static bool clearIfdefedOut(const QTextBlock &block); - static bool ifdefedOut(const QTextBlock &block); - static int braceDepthDelta(const QTextBlock &block); - static int braceDepth(const QTextBlock &block); - static void setBraceDepth(QTextBlock &block, int depth); - static void changeBraceDepth(QTextBlock &block, int delta); - - static TextBlockUserData *testUserData(const QTextBlock &block) { - return static_cast<TextBlockUserData*>(block.userData()); - } - static TextBlockUserData *userData(const QTextBlock &block) { - TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); - if (!data && block.isValid()) - const_cast<QTextBlock &>(block).setUserData((data = new TextBlockUserData)); - return data; - } - - - void emitDocumentSizeChanged() { emit documentSizeChanged(documentSize()); } - int lastSaveRevision; - bool hasMarks; -}; - - -class BaseTextEditorEditable; class TEXTEDITOR_EXPORT BaseTextEditorAnimator : public QObject { @@ -663,8 +486,7 @@ private slots: }; -class TEXTEDITOR_EXPORT BaseTextEditorEditable - : public ITextEditable +class TEXTEDITOR_EXPORT BaseTextEditorEditable : public ITextEditable { Q_OBJECT friend class BaseTextEditor; diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h index c468f80db470b302f79e89f984862b1f60f57782..cd8eb7c3d1ecd9cd27f01350fe442c158b85fd57 100644 --- a/src/plugins/texteditor/basetexteditor_p.h +++ b/src/plugins/texteditor/basetexteditor_p.h @@ -42,12 +42,13 @@ #include <QtCore/QSharedData> #include <QtCore/QPointer> -#include <QtGui/QTextEdit> #include <QtGui/QPixmap> +#include <QtGui/QTextEdit> namespace TextEditor { class BaseTextDocument; +class TextEditorActionHandler; namespace Internal { diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index 47221c88add7032a6d972235b788d568a28d0626..26429c0858660418673ca84b156108e00e5a9c2a 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -33,7 +33,8 @@ SOURCES += texteditorplugin.cpp \ colorschemeedit.cpp \ itexteditor.cpp \ texteditoroverlay.cpp \ - texteditoroptionspage.cpp + texteditoroptionspage.cpp \ + basetextdocumentlayout.cpp HEADERS += texteditorplugin.h \ textfilewizard.h \ @@ -69,7 +70,8 @@ HEADERS += texteditorplugin.h \ colorscheme.h \ colorschemeedit.h \ texteditoroverlay.h \ - texteditoroptionspage.h + texteditoroptionspage.h \ + basetextdocumentlayout.h FORMS += behaviorsettingspage.ui \ diff --git a/src/plugins/texteditor/texteditoroverlay.cpp b/src/plugins/texteditor/texteditoroverlay.cpp index 044d018c6463b738fd253605eb1243c91b7853a3..331e449955e91133ed49494f79109afe285c0187 100644 --- a/src/plugins/texteditor/texteditoroverlay.cpp +++ b/src/plugins/texteditor/texteditoroverlay.cpp @@ -27,9 +27,10 @@ ** **************************************************************************/ -#include <QtGui/QPainter> #include "texteditoroverlay.h" -#include <QDebug> + +#include <QtGui/QPainter> +#include <QtGui/QTextBlock> using namespace TextEditor; using namespace TextEditor::Internal;