Skip to content
Snippets Groups Projects
basetexteditor.cpp 179 KiB
Newer Older
            cursor.beginEditBlock();
            cursor.deleteChar();
            cursor.deletePreviousChar();
            cursor.endEditBlock();
            return true;
        }
    }
int BaseTextEditor::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor)
    if (!d->m_autoParenthesesEnabled)
        return 0;

    if (characterAt(cursor.position() - 1) != QLatin1Char('{'))
        return 0;

    if (!contextAllowsAutoParentheses(cursor))
        return 0;

    // verify that we indeed do have an extra opening brace in the document
    int braceDepth = document()->lastBlock().userState();
    if (braceDepth >= 0)
        braceDepth >>= 8;
    else
        braceDepth= 0;

    if (braceDepth <= 0)
        return 0; // braces are all balanced or worse, no need to do anything

    // we have an extra brace , let's see if we should close it


    /* verify that the next block is not further intended compared to the current block.
       This covers the following case:

            if (condition) {|
                statement;
    */
    const TabSettings &ts = tabSettings();
    QTextBlock block = cursor.block();
    int indentation = ts.indentationColumn(block.text());
    if (block.next().isValid()
        && ts.indentationColumn(block.next().text()) > indentation)
        return 0;

    int pos = cursor.position();

    const QString textToInsert = insertParagraphSeparator(cursor);

    cursor.insertText(textToInsert);
    cursor.setPosition(pos);
    if (ts.m_autoIndent) {
        cursor.insertBlock();
        indent(document(), cursor, QChar::Null);
    } else {
        QString previousBlockText = cursor.block().text();
        cursor.insertBlock();
        cursor.insertText(ts.indentationString(previousBlockText));
    }
    cursor.setPosition(pos);
    d->m_allowSkippingOfBlockEnd = true;
    return 1;
con's avatar
con committed
void BaseTextEditor::indentBlock(QTextDocument *, QTextBlock, QChar)
{
}

void BaseTextEditor::indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar)
{
    if (cursor.hasSelection()) {
        QTextBlock block = doc->findBlock(cursor.selectionStart());
        const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next();
con's avatar
con committed
        do {
            indentBlock(doc, block, typedChar);
            block = block.next();
        } while (block.isValid() && block != end);
    } else {
        indentBlock(doc, cursor.block(), typedChar);
    }
}

void BaseTextEditor::reindent(QTextDocument *doc, const QTextCursor &cursor)
{
    if (cursor.hasSelection()) {
        QTextBlock block = doc->findBlock(cursor.selectionStart());
        const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next();

        const TabSettings &ts = d->m_document->tabSettings();

        // skip empty blocks
        while (block.isValid() && block != end) {
            QString bt = block.text();
            if (ts.firstNonSpace(bt) < bt.size())
                break;
            indentBlock(doc, block, QChar::Null);
            block = block.next();
        }

        int previousIndentation = ts.indentationColumn(block.text());
        indentBlock(doc, block, QChar::Null);
        int currentIndentation = ts.indentationColumn(block.text());
        int delta = currentIndentation - previousIndentation;

        block = block.next();
        while (block.isValid() && block != end) {
            ts.reindentLine(block, delta);
            block = block.next();
        }
    } else {
        indentBlock(doc, cursor.block(), QChar::Null);
    }
}


BaseTextEditor::Link BaseTextEditor::findLinkAt(const QTextCursor &, bool)
{
    return Link();
}

bool BaseTextEditor::openLink(const Link &link)
{
    if (link.fileName.isEmpty())
        return false;

    if (baseTextDocument()->fileName() == link.fileName) {
        Core::EditorManager *editorManager = Core::EditorManager::instance();
        editorManager->addCurrentPositionToNavigationHistory();
        gotoLine(link.line, link.column);
        setFocus();
        return true;
    }

    return openEditorAt(link.fileName, link.line, link.column);
}

void BaseTextEditor::updateLink(QMouseEvent *e)
{
    bool linkFound = false;

    if (mouseNavigationEnabled() && e->modifiers() & Qt::ControlModifier) {
        // Link emulation behaviour for 'go to definition'
        const QTextCursor cursor = cursorForPosition(e->pos());

        // Check that the mouse was actually on the text somewhere
        bool onText = cursorRect(cursor).right() >= e->x();
        if (!onText) {
            QTextCursor nextPos = cursor;
            nextPos.movePosition(QTextCursor::Right);
            onText = cursorRect(nextPos).right() >= e->x();
        }

        const Link link = findLinkAt(cursor, false);

        if (onText && link.isValid()) {
            showLink(link);
            linkFound = true;
        }
    }

    if (!linkFound)
        clearLink();
}

void BaseTextEditor::showLink(const Link &link)
{
    QTextEdit::ExtraSelection sel;
    sel.cursor = textCursor();
    sel.cursor.setPosition(link.begin);
    sel.cursor.setPosition(link.end, QTextCursor::KeepAnchor);
    sel.format = d->m_linkFormat;
    sel.format.setFontUnderline(true);
    setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
    viewport()->setCursor(Qt::PointingHandCursor);
    d->m_currentLink = link;
    d->m_linkPressed = false;
        return;

    setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
    viewport()->setCursor(Qt::IBeamCursor);
    d->m_currentLink = Link();
    d->m_linkPressed = false;
con's avatar
con committed
void BaseTextEditorPrivate::updateMarksBlock(const QTextBlock &block)
{
    if (const TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(block))
        foreach (ITextMark *mrk, userData->marks())
con's avatar
con committed
            mrk->updateBlock(block);
}

void BaseTextEditorPrivate::updateMarksLineNumber()
{
    QTextDocument *doc = q->document();
    QTextBlock block = doc->begin();
    int blockNumber = 0;
    while (block.isValid()) {
        if (const TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(block))
con's avatar
con committed
            foreach (ITextMark *mrk, userData->marks()) {
                mrk->updateLineNumber(blockNumber + 1);
            }
        block = block.next();
        ++blockNumber;
    }
}

void BaseTextEditor::markBlocksAsChanged(QList<int> blockNumbers)
{
con's avatar
con committed
    QTextBlock block = document()->begin();
    while (block.isValid()) {
        if (block.revision() < 0)
            block.setRevision(-block.revision() - 1);
        block = block.next();
    }
    foreach (const int blockNumber, blockNumbers) {
        QTextBlock block = document()->findBlockByNumber(blockNumber);
        if (block.isValid())
            block.setRevision(-block.revision() - 1);
    }
}


void BaseTextEditor::highlightSearchResults(const QString &txt, Find::IFindSupport::FindFlags findFlags)
con's avatar
con committed
{
    QString pattern = txt;
    if (pattern.size() < 2)
        pattern.clear(); // highlighting single characters is a bit pointless

    if (d->m_searchExpr.pattern() == pattern)
con's avatar
con committed
        return;
    d->m_searchExpr.setPattern(pattern);
    d->m_searchExpr.setPatternSyntax((findFlags & Find::IFindSupport::FindRegularExpression) ?
                                     QRegExp::RegExp : QRegExp::FixedString);
    d->m_searchExpr.setCaseSensitivity((findFlags & Find::IFindSupport::FindCaseSensitively) ?
con's avatar
con committed
                                       Qt::CaseSensitive : Qt::CaseInsensitive);
    d->m_findFlags = findFlags;

    d->m_delayedUpdateTimer->start(10);
}
con's avatar
con committed

int BaseTextEditor::verticalBlockSelection() const
{
    if (!d->m_inBlockSelectionMode)
        return 0;
    QTextCursor b = textCursor();
    QTextCursor e = b;
    b.setPosition(b.selectionStart());
    e.setPosition(e.selectionEnd());

    return qAbs(b.positionInBlock() - e.positionInBlock()) + d->m_blockSelectionExtraX;
}

mae's avatar
mae committed
void BaseTextEditor::setFindScope(const QTextCursor &start, const QTextCursor &end, int verticalBlockSelection)
con's avatar
con committed
{
mae's avatar
mae committed
    if (start != d->m_findScopeStart || end != d->m_findScopeEnd) {
        d->m_findScopeStart = start;
        d->m_findScopeEnd = end;
        d->m_findScopeVerticalBlockSelection = verticalBlockSelection;
con's avatar
con committed
        viewport()->update();
    }
}

void BaseTextEditor::_q_animateUpdate(int position, QPointF lastPos, QRectF rect)
{
    QTextCursor cursor(textCursor());
    cursor.setPosition(position);
    viewport()->update(QRectF(cursorRect(cursor).topLeft() + rect.topLeft(), rect.size()).toAlignedRect());
    if (!lastPos.isNull())
        viewport()->update(QRectF(lastPos + rect.topLeft(), rect.size()).toAlignedRect());
}


BaseTextEditorAnimator::BaseTextEditorAnimator(QObject *parent)
        :QObject(parent)
{
    m_value = 0;
    m_timeline = new QTimeLine(256, this);
    m_timeline->setCurveShape(QTimeLine::SineCurve);
    connect(m_timeline, SIGNAL(valueChanged(qreal)), this, SLOT(step(qreal)));
    connect(m_timeline, SIGNAL(finished()), this, SLOT(deleteLater()));
    m_timeline->start();
}


void BaseTextEditorAnimator::setData(QFont f, QPalette pal, const QString &text)
{
    m_font = f;
    m_palette = pal;
    m_text = text;
    QFontMetrics fm(m_font);
    m_size = QSizeF(fm.width(m_text), fm.height());
}

void BaseTextEditorAnimator::draw(QPainter *p, const QPointF &pos)
{
    p->setPen(m_palette.text().color());
    QFont f = m_font;
    f.setPointSizeF(f.pointSizeF() * (1.0 + m_value/2));
    QFontMetrics fm(f);
    int width = fm.width(m_text);
    QRectF r((m_size.width()-width)/2, (m_size.height() - fm.height())/2, width, fm.height());
    r.translate(pos);
    p->fillRect(r, m_palette.base());
    p->setFont(f);
    p->drawText(r, m_text);
}

mae's avatar
mae committed
bool BaseTextEditorAnimator::isRunning() const
{
    return m_timeline->state() == QTimeLine::Running;
}

QRectF BaseTextEditorAnimator::rect() const
{
    QFont f = m_font;
    f.setPointSizeF(f.pointSizeF() * (1.0 + m_value/2));
    QFontMetrics fm(f);
    int width = fm.width(m_text);
    return QRectF((m_size.width()-width)/2, (m_size.height() - fm.height())/2, width, fm.height());
}

void BaseTextEditorAnimator::step(qreal v)
{
    QRectF before = rect();
    m_value = v;
    QRectF after = rect();
    emit updateRequest(m_position, m_lastDrawPos, before.united(after));
con's avatar
con committed
void BaseTextEditor::_q_matchParentheses()
{
    if (isReadOnly())
        return;

    QTextCursor backwardMatch = textCursor();
    QTextCursor forwardMatch = textCursor();
    const TextBlockUserData::MatchType backwardMatchType = TextBlockUserData::matchCursorBackward(&backwardMatch);
    const TextBlockUserData::MatchType forwardMatchType = TextBlockUserData::matchCursorForward(&forwardMatch);

    QList<QTextEdit::ExtraSelection> extraSelections;
con's avatar
con committed

    if (backwardMatchType == TextBlockUserData::NoMatch && forwardMatchType == TextBlockUserData::NoMatch) {
        setExtraSelections(ParenthesesMatchingSelection, extraSelections); // clear
        return;
    }

con's avatar
con committed
    if (backwardMatch.hasSelection()) {
        QTextEdit::ExtraSelection sel;
        if (backwardMatchType == TextBlockUserData::Mismatch) {
            sel.cursor = backwardMatch;
            sel.format = d->m_mismatchFormat;
        } else {

            if (d->m_displaySettings.m_animateMatchingParentheses) {
                animatePosition = backwardMatch.selectionStart();
            } else if (d->m_formatRange) {
                sel.cursor = backwardMatch;
                sel.format = d->m_rangeFormat;
                extraSelections.append(sel);
            }
con's avatar
con committed

            sel.cursor = backwardMatch;
            sel.format = d->m_matchFormat;

            sel.cursor.setPosition(backwardMatch.selectionStart());
            sel.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
            extraSelections.append(sel);

            sel.cursor.setPosition(backwardMatch.selectionEnd());
            sel.cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
        }
        extraSelections.append(sel);
    }

    if (forwardMatch.hasSelection()) {
        QTextEdit::ExtraSelection sel;
        if (forwardMatchType == TextBlockUserData::Mismatch) {
            sel.cursor = forwardMatch;
            sel.format = d->m_mismatchFormat;
        } else {

            if (d->m_displaySettings.m_animateMatchingParentheses) {
                animatePosition = forwardMatch.selectionEnd()-1;
            } else if (d->m_formatRange) {
                sel.cursor = forwardMatch;
                sel.format = d->m_rangeFormat;
                extraSelections.append(sel);
            }
con's avatar
con committed

            sel.cursor = forwardMatch;
            sel.format = d->m_matchFormat;

            sel.cursor.setPosition(forwardMatch.selectionStart());
            sel.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
            extraSelections.append(sel);

            sel.cursor.setPosition(forwardMatch.selectionEnd());
            sel.cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
        }
        extraSelections.append(sel);
    }
    if (animatePosition >= 0) {
        foreach (const QTextEdit::ExtraSelection &sel, BaseTextEditor::extraSelections(ParenthesesMatchingSelection)) {
            if (sel.cursor.selectionStart() == animatePosition
                || sel.cursor.selectionEnd() - 1 == animatePosition) {
                animatePosition = -1;
                break;
            }
        }
    }

    if (animatePosition >= 0) {
        if (d->m_animator)
            d->m_animator->finish();  // one animation is enough
        d->m_animator = new BaseTextEditorAnimator(this);
        d->m_animator->setPosition(animatePosition);
        QPalette pal;
        pal.setBrush(QPalette::Text, d->m_matchFormat.foreground());
        pal.setBrush(QPalette::Base, d->m_rangeFormat.background());
        d->m_animator->setData(font(), pal, characterAt(d->m_animator->position()));
        connect(d->m_animator, SIGNAL(updateRequest(int,QPointF,QRectF)),
                this, SLOT(_q_animateUpdate(int,QPointF,QRectF)));
    setExtraSelections(ParenthesesMatchingSelection, extraSelections);
con's avatar
con committed
}

void BaseTextEditor::_q_highlightBlocks()
{
    BaseTextEditorPrivateHighlightBlocks highlightBlocksInfo;

    if (d->extraAreaHighlightCollapseBlockNumber >= 0) {
        QTextBlock block = document()->findBlockByNumber(d->extraAreaHighlightCollapseBlockNumber);
        if (block.isValid()) {
            QTextCursor cursor(block);
            if (d->extraAreaHighlightCollapseColumn >= 0)
mae's avatar
mae committed
                cursor.setPosition(cursor.position() + qMin(d->extraAreaHighlightCollapseColumn,
                                                            block.length()-1));
            QTextCursor closeCursor;
mae's avatar
mae committed
            bool firstRun = true;
mae's avatar
mae committed
            while (TextBlockUserData::findPreviousBlockOpenParenthesis(&cursor, firstRun)) {
mae's avatar
mae committed
                firstRun = false;
                highlightBlocksInfo.open.prepend(cursor.blockNumber());
                int visualIndent = d->visualIndent(cursor.block());
                if (closeCursor.isNull())
                    closeCursor = cursor;
                if (TextBlockUserData::findNextBlockClosingParenthesis(&closeCursor)) {
                    highlightBlocksInfo.close.append(closeCursor.blockNumber());
                    visualIndent = qMin(visualIndent, d->visualIndent(closeCursor.block()));
                }
                highlightBlocksInfo.visualIndent.prepend(visualIndent);
    if (d->m_highlightBlocksInfo != highlightBlocksInfo) {
        d->m_highlightBlocksInfo = highlightBlocksInfo;
        viewport()->update();
        d->m_extraArea->update();
con's avatar
con committed
void BaseTextEditor::setActionHack(QObject *hack)
{
    d->m_actionHack = hack;
}

QObject *BaseTextEditor::actionHack() const
{
    return d->m_actionHack;
}

void BaseTextEditor::changeEvent(QEvent *e)
{
    QPlainTextEdit::changeEvent(e);
    if (e->type() == QEvent::ApplicationFontChange
        || e->type() == QEvent::FontChange) {
        if (d->m_extraArea) {
            QFont f = d->m_extraArea->font();
            f.setPointSize(font().pointSize());
            d->m_extraArea->setFont(f);
            slotUpdateExtraAreaWidth();
            d->m_extraArea->update();
        }
    }
}

void BaseTextEditor::focusInEvent(QFocusEvent *e)
{
    QPlainTextEdit::focusInEvent(e);
con's avatar
con committed
    updateHighlights();
mae's avatar
mae committed
void BaseTextEditor::focusOutEvent(QFocusEvent *e)
{
    QPlainTextEdit::focusOutEvent(e);
    if (viewport()->cursor().shape() == Qt::BlankCursor)
        viewport()->setCursor(Qt::IBeamCursor);
}


void BaseTextEditor::maybeSelectLine()
con's avatar
con committed
{
    QTextCursor cursor = textCursor();
    if (!cursor.hasSelection()) {
        const QTextBlock &block = cursor.block();
        if (block.next().isValid()) {
            cursor.setPosition(block.position());
            cursor.setPosition(block.next().position(), QTextCursor::KeepAnchor);
        } else {
            cursor.movePosition(QTextCursor::EndOfBlock);
            cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
            cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
        }
        setTextCursor(cursor);
    }
}

// shift+del
void BaseTextEditor::cutLine()
{
    maybeSelectLine();
con's avatar
con committed
    cut();
}

void BaseTextEditor::deleteLine()
{
    maybeSelectLine();
    textCursor().removeSelectedText();
}

void BaseTextEditor::setExtraSelections(ExtraSelectionKind kind, const QList<QTextEdit::ExtraSelection> &selections)
con's avatar
con committed
{
    if (selections.isEmpty() && d->m_extraSelections[kind].isEmpty())
        return;
    d->m_extraSelections[kind] = selections;

    if (kind == CodeSemanticsSelection) {
        d->m_overlay->clear();
        foreach (const QTextEdit::ExtraSelection &selection, d->m_extraSelections[kind]) {
mae's avatar
mae committed
            d->m_overlay->addOverlaySelection(selection.cursor,
                                              selection.format.background().color(),
                                              selection.format.background().color(),
                                              TextEditorOverlay::LockSize);
        }
        d->m_overlay->setVisible(!d->m_overlay->isEmpty());
    } else if (kind == SnippetPlaceholderSelection) {
        d->m_snippetOverlay->clear();
        foreach (const QTextEdit::ExtraSelection &selection, d->m_extraSelections[kind]) {
            d->m_snippetOverlay->addOverlaySelection(selection.cursor,
                                              selection.format.background().color(),
                                              selection.format.background().color(),
                                              TextEditorOverlay::ExpandBegin);
        }
        d->m_snippetOverlay->setVisible(!d->m_snippetOverlay->isEmpty());
    } else {
        QList<QTextEdit::ExtraSelection> all;
        for (int i = 0; i < NExtraSelectionKinds; ++i) {
            if (i == CodeSemanticsSelection || i == SnippetPlaceholderSelection)
                continue;
            all += d->m_extraSelections[i];
        }
        QPlainTextEdit::setExtraSelections(all);
    }
con's avatar
con committed
}

QList<QTextEdit::ExtraSelection> BaseTextEditor::extraSelections(ExtraSelectionKind kind) const
con's avatar
con committed
{
    return d->m_extraSelections[kind];
con's avatar
con committed
}

QString BaseTextEditor::extraSelectionTooltip(int pos) const
{
    QList<QTextEdit::ExtraSelection> all;
    for (int i = 0; i < NExtraSelectionKinds; ++i) {
        const QList<QTextEdit::ExtraSelection> &sel = d->m_extraSelections[i];
        for (int j = 0; j < sel.size(); ++j) {
            const QTextEdit::ExtraSelection &s = sel.at(j);
            if (s.cursor.selectionStart() <= pos
                && s.cursor.selectionEnd() >= pos
                && !s.format.toolTip().isEmpty())
                return s.format.toolTip();
        }
    }
    return QString();
}
con's avatar
con committed

// the blocks list must be sorted
void BaseTextEditor::setIfdefedOutBlocks(const QList<BaseTextEditor::BlockRange> &blocks)
{
    QTextDocument *doc = document();
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
hjk's avatar
hjk committed
    QTC_ASSERT(documentLayout, return);
con's avatar
con committed

    bool needUpdate = false;

    QTextBlock block = doc->firstBlock();

    int rangeNumber = 0;
    int braceDepthDelta = 0;
con's avatar
con committed
    while (block.isValid()) {
        bool cleared = false;
        bool set = false;
con's avatar
con committed
        if (rangeNumber < blocks.size()) {
            const BlockRange &range = blocks.at(rangeNumber);
            if (block.position() >= range.first && ((block.position() + block.length() - 1) <= range.last || !range.last)) {
                set = BaseTextDocumentLayout::setIfdefedOut(block);
con's avatar
con committed
            } else {
                cleared = BaseTextDocumentLayout::clearIfdefedOut(block);
con's avatar
con committed
            }
            if (block.contains(range.last))
                ++rangeNumber;
        } else {
            cleared = BaseTextDocumentLayout::clearIfdefedOut(block);
con's avatar
con committed
        }

        if (cleared || set) {
            needUpdate = true;
            int delta = BaseTextDocumentLayout::braceDepthDelta(block);
            if (cleared)
                braceDepthDelta += delta;
            else if (set)
                braceDepthDelta -= delta;
        }

        if (braceDepthDelta)
            BaseTextDocumentLayout::changeBraceDepth(block,braceDepthDelta);
con's avatar
con committed
        block = block.next();
    }

    if (needUpdate)
        documentLayout->requestUpdate();
}

void BaseTextEditor::format()
{
    QTextCursor cursor = textCursor();
    cursor.beginEditBlock();
    indent(document(), cursor, QChar::Null);
    cursor.endEditBlock();
}
con's avatar
con committed

void BaseTextEditor::rewrapParagraph()
{
    const int paragraphWidth = displaySettings().m_wrapColumn;
    const QRegExp anyLettersOrNumbers = QRegExp("\\w");
    const int tabSize = tabSettings().m_tabSize;

    QTextCursor cursor = textCursor();
    cursor.beginEditBlock();

    // Find start of paragraph.

    while (cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) {
        QTextBlock block = cursor.block();
        QString text = block.text();

        // If this block is empty, move marker back to previous and terminate.
        if (!text.contains(anyLettersOrNumbers)) {
            cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
            break;
        }
    }

    cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);

    // Find indent level of current block.

    int indentLevel = 0;
    QString text = cursor.block().text();

    for (int i = 0; i < text.length(); i++) {
        const QChar ch = text.at(i);

        if (ch == QLatin1Char(' '))
            indentLevel++;
        else if (ch == QLatin1Char('\t'))
            indentLevel += tabSize - (indentLevel % tabSize);
        else
            break;
    }

    // If there is a common prefix, it should be kept and expanded to all lines.
    // this allows nice reflowing of doxygen style comments.
    QTextCursor nextBlock = cursor;
    QString commonPrefix;

    if (nextBlock.movePosition(QTextCursor::NextBlock))
    {
         QString nText = nextBlock.block().text();
         int maxLength = qMin(text.length(), nText.length());

         for (int i = 0; i < maxLength; ++i) {
             const QChar ch = text.at(i);

             if (ch != nText[i] || ch.isLetterOrNumber())
                 break;
             commonPrefix.append(ch);
         }
    }


    // Find end of paragraph.
    while (cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor)) {
        QString text = cursor.block().text();

        if (!text.contains(anyLettersOrNumbers))
            break;
    }


    QString selectedText = cursor.selectedText();

    // Preserve initial indent level.or common prefix.
    QString spacing;

    if (commonPrefix.isEmpty()) {
        spacing = tabSettings().indentationString(0, indentLevel, textCursor().block());
    } else {
        spacing = commonPrefix;
        indentLevel = commonPrefix.length();
    }

    int currentLength = indentLevel;
    QString result;
    result.append(spacing);

    // Remove existing instances of any common prefix from paragraph to
    // reflow.
    selectedText.remove(0, commonPrefix.length());
    commonPrefix.prepend(QChar::ParagraphSeparator);
    selectedText.replace(commonPrefix, QLatin1String("\n"));

    // remove any repeated spaces, trim lines to PARAGRAPH_WIDTH width and
    // keep the same indentation level as first line in paragraph.
    QString currentWord;

    for (int i = 0; i < selectedText.length(); ++i) {
        QChar ch = selectedText.at(i);
        if (ch.isSpace()) {
            if (!currentWord.isEmpty()) {
                currentLength += currentWord.length() + 1;

                if (currentLength > paragraphWidth) {
                    currentLength = currentWord.length() + 1 + indentLevel;
                    result.chop(1); // remove trailing space
                    result.append(QChar::ParagraphSeparator);
                    result.append(spacing);
                }

                result.append(currentWord);
                result.append(QLatin1Char(' '));
                currentWord.clear();
            }

            continue;
        }

        currentWord.append(ch);
    }
    result.append(QChar::ParagraphSeparator);

    cursor.insertText(result);
    cursor.endEditBlock();
}

void BaseTextEditor::unCommentSelection()
con's avatar
con committed
{
void BaseTextEditor::showEvent(QShowEvent* e)
{
    if (!d->m_fontSettings.isEmpty()) {
        setFontSettings(d->m_fontSettings);
        d->m_fontSettings.clear();
    }
    QPlainTextEdit::showEvent(e);
}


void BaseTextEditor::setFontSettingsIfVisible(const TextEditor::FontSettings &fs)
{
    if (!isVisible()) {
    }
    setFontSettings(fs);
}
void BaseTextEditor::setFontSettings(const TextEditor::FontSettings &fs)
{
    const QTextCharFormat textFormat = fs.toTextCharFormat(QLatin1String(Constants::C_TEXT));
    const QTextCharFormat selectionFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SELECTION));
    const QTextCharFormat lineNumberFormat = fs.toTextCharFormat(QLatin1String(Constants::C_LINE_NUMBER));
    const QTextCharFormat searchResultFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SEARCH_RESULT));
    d->m_searchScopeFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SEARCH_SCOPE));
    const QTextCharFormat parenthesesFormat = fs.toTextCharFormat(QLatin1String(Constants::C_PARENTHESES));
    d->m_currentLineFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE));
    d->m_currentLineNumberFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE_NUMBER));
    d->m_linkFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LINK));
    d->m_ifdefedOutFormat = fs.toTextCharFormat(QLatin1String(Constants::C_DISABLED_CODE));
    QFont font(textFormat.font());

    const QColor foreground = textFormat.foreground().color();
    const QColor background = textFormat.background().color();
    QPalette p = palette();
    p.setColor(QPalette::Text, foreground);
    p.setColor(QPalette::Foreground, foreground);
    p.setColor(QPalette::Base, background);
    p.setColor(QPalette::Highlight, (selectionFormat.background().style() != Qt::NoBrush) ?
               selectionFormat.background().color() :
               QApplication::palette().color(QPalette::Highlight));
    p.setColor(QPalette::HighlightedText, selectionFormat.foreground().color());
    p.setBrush(QPalette::Inactive, QPalette::Highlight, p.highlight());
    p.setBrush(QPalette::Inactive, QPalette::HighlightedText, p.highlightedText());
    setPalette(p);
    setFont(font);
    setTabSettings(d->m_document->tabSettings()); // update tabs, they depend on the font

    // Line numbers
    QPalette ep = d->m_extraArea->palette();
    ep.setColor(QPalette::Dark, lineNumberFormat.foreground().color());
    ep.setColor(QPalette::Background, lineNumberFormat.background().style() != Qt::NoBrush ?
                lineNumberFormat.background().color() : background);
    d->m_extraArea->setPalette(ep);

    // Search results
    d->m_searchResultFormat.setBackground(searchResultFormat.background());

    // Matching braces
    d->m_matchFormat.setForeground(parenthesesFormat.foreground());
    d->m_rangeFormat.setBackground(parenthesesFormat.background());

mae's avatar
mae committed

    // snippests
    d->m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
    d->m_occurrencesFormat.clearForeground();
    d->m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
    d->m_occurrenceRenameFormat.clearForeground();

    slotUpdateExtraAreaWidth();   // Adjust to new font width
    updateCurrentLineHighlight(); // Make sure it takes the new color
}

void BaseTextEditor::setTabSettings(const TabSettings &ts)
{
    d->m_document->setTabSettings(ts);
    int charWidth = QFontMetrics(font()).width(QChar(' '));
    setTabStopWidth(charWidth * ts.m_tabSize);
}

void BaseTextEditor::setDisplaySettings(const DisplaySettings &ds)
{
    setLineWrapMode(ds.m_textWrapping ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap);
    setLineNumbersVisible(ds.m_displayLineNumbers);
    setVisibleWrapColumn(ds.m_showWrapColumn ? ds.m_wrapColumn : 0);
    setCodeFoldingVisible(ds.m_displayFoldingMarkers);
    setHighlightCurrentLine(ds.m_highlightCurrentLine);
    setRevisionsVisible(ds.m_markTextChanges);
    setCenterOnScroll(ds.m_centerCursorOnScroll);

    if (d->m_displaySettings.m_visualizeWhitespace != ds.m_visualizeWhitespace) {
        if (QSyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter())
            highlighter->rehighlight();
        QTextOption option =  document()->defaultTextOption();
        if (ds.m_visualizeWhitespace)
            option.setFlags(option.flags() | QTextOption::ShowTabsAndSpaces);
        else
            option.setFlags(option.flags() & ~QTextOption::ShowTabsAndSpaces);
        option.setFlags(option.flags() | QTextOption::AddSpaceForLineAndParagraphSeparators);
        document()->setDefaultTextOption(option);
con's avatar
con committed
    }
    if (!ds.m_highlightBlocks) {
        d->extraAreaHighlightCollapseBlockNumber = d->extraAreaHighlightCollapseColumn = -1;
        d->m_highlightBlocksInfo = BaseTextEditorPrivateHighlightBlocks();
    }
con's avatar
con committed
    updateHighlights();
mae's avatar
mae committed
    viewport()->update();
    extraArea()->update();
void BaseTextEditor::setBehaviorSettings(const TextEditor::BehaviorSettings &bs)
{
    setMouseNavigationEnabled(bs.m_mouseNavigation);
    setScrollWheelZoomingEnabled(bs.m_scrollWheelZooming);
void BaseTextEditor::setStorageSettings(const StorageSettings &storageSettings)
{
    d->m_document->setStorageSettings(storageSettings);
con's avatar
con committed
}

void BaseTextEditor::setCompletionSettings(const TextEditor::CompletionSettings &completionSettings)
{
    setAutoParenthesesEnabled(completionSettings.m_autoInsertBrackets);
}

con's avatar
con committed
void BaseTextEditor::collapse()
{
    QTextDocument *doc = document();
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
hjk's avatar
hjk committed
    QTC_ASSERT(documentLayout, return);
con's avatar
con committed
    QTextBlock block = textCursor().block();
    QTextBlock curBlock = block;
con's avatar
con committed
    while (block.isValid()) {
        if (TextBlockUserData::canCollapse(block) && block.next().isVisible()) {
mae's avatar
mae committed
            if (block == curBlock || block.next() == curBlock)
                break;
            if ((block.next().userState()) >> 8 <= (curBlock.previous().userState() >> 8))
con's avatar
con committed
                break;
        }
        block = block.previous();
    }
    if (block.isValid()) {
        TextBlockUserData::doCollapse(block, false);
        d->moveCursorVisible();
        documentLayout->requestUpdate();
        documentLayout->emitDocumentSizeChanged();
    }
}

void BaseTextEditor::expand()
{
    QTextDocument *doc = document();
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
hjk's avatar
hjk committed
    QTC_ASSERT(documentLayout, return);
con's avatar
con committed
    QTextBlock block = textCursor().block();
    while (block.isValid() && !block.isVisible())
        block = block.previous();
    TextBlockUserData::doCollapse(block, true);
    d->moveCursorVisible();
    documentLayout->requestUpdate();
    documentLayout->emitDocumentSizeChanged();
}

void BaseTextEditor::unCollapseAll()
{
    QTextDocument *doc = document();
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
hjk's avatar
hjk committed
    QTC_ASSERT(documentLayout, return);
con's avatar
con committed

    QTextBlock block = doc->firstBlock();
    bool makeVisible = true;
    while (block.isValid()) {
        if (block.isVisible() && TextBlockUserData::canCollapse(block) && block.next().isVisible()) {
            makeVisible = false;
            break;
        }
        block = block.next();
    }

    block = doc->firstBlock();

    while (block.isValid()) {
        if (TextBlockUserData::canCollapse(block))
            TextBlockUserData::doCollapse(block, makeVisible);
        block = block.next();
    }

    d->moveCursorVisible();
    documentLayout->requestUpdate();
    documentLayout->emitDocumentSizeChanged();
con's avatar
con committed
}

void BaseTextEditor::setTextCodec(QTextCodec *codec)
{
    baseTextDocument()->setCodec(codec);
}

QTextCodec *BaseTextEditor::textCodec() const
{
    return baseTextDocument()->codec();
}