Skip to content
Snippets Groups Projects
basetexteditor.cpp 179 KiB
Newer Older
con's avatar
con committed

void BaseTextEditor::setReadOnly(bool b)
{
    QPlainTextEdit::setReadOnly(b);
    if (b)
        setTextInteractionFlags(textInteractionFlags() | Qt::TextSelectableByKeyboard);
}

void BaseTextEditor::cut()
{
    if (d->m_inBlockSelectionMode) {
        copy();
        d->removeBlockSelection();
        return;
    }
    QPlainTextEdit::cut();
}

void BaseTextEditor::paste()
{
    if (d->m_inBlockSelectionMode) {
        d->removeBlockSelection();
    }
    QPlainTextEdit::paste();
}

con's avatar
con committed
QMimeData *BaseTextEditor::createMimeDataFromSelection() const
{
    if (d->m_inBlockSelectionMode) {
        QMimeData *mimeData = new QMimeData;
        QString text = d->copyBlockSelection();
        mimeData->setData(QLatin1String("application/vnd.nokia.qtcreator.vblocktext"), text.toUtf8());
con's avatar
con committed
        mimeData->setText(text); // for exchangeability
        return mimeData;
    } else if (textCursor().hasSelection()) {
        QTextCursor cursor = textCursor();
        QMimeData *mimeData = new QMimeData;

        // Copy the selected text as plain text
        QString text = cursor.selectedText();
        convertToPlainText(text);
        mimeData->setText(text);

        // Copy the selected text as HTML
        {
            // Create a new document from the selected text document fragment
            QTextDocument *tempDocument = new QTextDocument;
            QTextCursor tempCursor(tempDocument);
            tempCursor.insertFragment(cursor.selection());

            // Apply the additional formats set by the syntax highlighter
            QTextBlock start = document()->findBlock(cursor.selectionStart());
            QTextBlock end = document()->findBlock(cursor.selectionEnd());
            end = end.next();

            const int selectionStart = cursor.selectionStart();
            const int endOfDocument = tempDocument->characterCount() - 1;
            for (QTextBlock current = start; current.isValid() && current != end; current = current.next()) {
                const QTextLayout *layout = current.layout();
                foreach (const QTextLayout::FormatRange &range, layout->additionalFormats()) {
                    const int start = current.position() + range.start - selectionStart;
                    const int end = start + range.length;
                    if (end <= 0 || start >= endOfDocument)
                        continue;
                    tempCursor.setPosition(qMax(start, 0));
                    tempCursor.setPosition(qMin(end, endOfDocument), QTextCursor::KeepAnchor);
                    tempCursor.setCharFormat(range.format);
                }
            }

            // Reset the user states since they are not interesting
            for (QTextBlock block = tempDocument->begin(); block.isValid(); block = block.next())
                block.setUserState(-1);

            // Make sure the text appears pre-formatted
            tempCursor.setPosition(0);
            tempCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
            QTextBlockFormat blockFormat = tempCursor.blockFormat();
            blockFormat.setNonBreakableLines(true);
            tempCursor.setBlockFormat(blockFormat);

            mimeData->setHtml(tempCursor.selection().toHtml());
            delete tempDocument;
        }

        /*
          Try to figure out whether we are copying an entire block, and store the complete block
          including indentation in the qtcreator.blocktext mimetype.
        */
        QTextCursor selstart = cursor;
        selstart.setPosition(cursor.selectionStart());
        QTextCursor selend = cursor;
        selend.setPosition(cursor.selectionEnd());
        const TabSettings &ts = d->m_document->tabSettings();

        bool startOk = ts.cursorIsAtBeginningOfLine(selstart);
        bool multipleBlocks = (selend.block() != selstart.block());
        if (startOk && multipleBlocks) {
            selstart.movePosition(QTextCursor::StartOfBlock);
            if (ts.cursorIsAtBeginningOfLine(selend))
                selend.movePosition(QTextCursor::StartOfBlock);
            cursor.setPosition(selstart.position());
            cursor.setPosition(selend.position(), QTextCursor::KeepAnchor);
            text = cursor.selectedText();
            mimeData->setData(QLatin1String("application/vnd.nokia.qtcreator.blocktext"), text.toUtf8());
        }
        return mimeData;
con's avatar
con committed
    }
    return 0;
con's avatar
con committed
}

bool BaseTextEditor::canInsertFromMimeData(const QMimeData *source) const
{
    return QPlainTextEdit::canInsertFromMimeData(source);
}

void BaseTextEditor::insertFromMimeData(const QMimeData *source)
{
    if (isReadOnly())
        return;

    if (source->hasFormat(QLatin1String("application/vnd.nokia.qtcreator.vblocktext"))) {
        QString text = QString::fromUtf8(source->data(QLatin1String("application/vnd.nokia.qtcreator.vblocktext")));
con's avatar
con committed
        if (text.isEmpty())
            return;
        QStringList lines = text.split(QLatin1Char('\n'));
        QTextCursor cursor = textCursor();
        cursor.beginEditBlock();
        int initialCursorPosition = cursor.position();
        int column = cursor.position() - cursor.block().position();
        cursor.insertText(lines.first());
        for (int i = 1; i < lines.count(); ++i) {
            QTextBlock next = cursor.block().next();
            if (next.isValid()) {
                cursor.setPosition(next.position() + qMin(column, next.length()-1));
            } else {
                cursor.movePosition(QTextCursor::EndOfBlock);
                cursor.insertBlock();
            }

            int actualColumn = cursor.position() - cursor.block().position();
            if (actualColumn < column)
                cursor.insertText(QString(column - actualColumn, QLatin1Char(' ')));
            cursor.insertText(lines.at(i));
        }
        cursor.setPosition(initialCursorPosition);
        cursor.endEditBlock();
        setTextCursor(cursor);
        ensureCursorVisible();
        return;
    }
    QString text = source->text();
    if (text.isEmpty())
        return;

    const TabSettings &ts = d->m_document->tabSettings();
    QTextCursor cursor = textCursor();
    if (!ts.m_autoIndent) {
        cursor.beginEditBlock();
        cursor.insertText(text);
        cursor.endEditBlock();
        setTextCursor(cursor);
        return;
    }

    cursor.beginEditBlock();
    cursor.removeSelectedText();
    bool insertAtBeginningOfLine = ts.cursorIsAtBeginningOfLine(cursor);

    if (insertAtBeginningOfLine
        && source->hasFormat(QLatin1String("application/vnd.nokia.qtcreator.blocktext"))) {
        text = QString::fromUtf8(source->data(QLatin1String("application/vnd.nokia.qtcreator.blocktext")));
        if (text.isEmpty())
            return;
    int reindentBlockStart = cursor.blockNumber() + (insertAtBeginningOfLine?0:1);
    bool hasFinalNewline = (text.endsWith(QLatin1Char('\n'))
                            || text.endsWith(QChar::ParagraphSeparator)
                            || text.endsWith(QLatin1Char('\r')));
    if (insertAtBeginningOfLine
        && hasFinalNewline) // since we'll add a final newline, preserve current line's indentation
        cursor.setPosition(cursor.block().position());
    int cursorPosition = cursor.position();
    cursor.insertText(text);

    int reindentBlockEnd = cursor.blockNumber() - (hasFinalNewline?1:0);

    if (reindentBlockStart < reindentBlockEnd
        || (reindentBlockStart == reindentBlockEnd
            && (!insertAtBeginningOfLine || hasFinalNewline))) {
        if (insertAtBeginningOfLine && !hasFinalNewline) {
            QTextCursor unnecessaryWhitespace = cursor;
            unnecessaryWhitespace.setPosition(cursorPosition);
            unnecessaryWhitespace.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
            unnecessaryWhitespace.removeSelectedText();
        QTextCursor c = cursor;
        c.setPosition(cursor.document()->findBlockByNumber(reindentBlockStart).position());
        c.setPosition(cursor.document()->findBlockByNumber(reindentBlockEnd).position(),
                      QTextCursor::KeepAnchor);
        reindent(document(), c);
    }

    cursor.endEditBlock();
    setTextCursor(cursor);
con's avatar
con committed
}

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);
}


con's avatar
con committed
BaseTextEditorEditable::BaseTextEditorEditable(BaseTextEditor *editor)
  : e(editor)
{
    using namespace Find;
    Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;
    BaseTextFind *baseTextFind = new BaseTextFind(editor);
    connect(baseTextFind, SIGNAL(highlightAll(QString, Find::IFindSupport::FindFlags)),
            editor, SLOT(highlightSearchResults(QString, Find::IFindSupport::FindFlags)));
mae's avatar
mae committed
    connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor, QTextCursor, int)),
            editor, SLOT(setFindScope(QTextCursor, QTextCursor, int)));
con's avatar
con committed
    aggregate->add(baseTextFind);
    aggregate->add(editor);

    m_cursorPositionLabel = new Utils::LineColumnLabel;
con's avatar
con committed

    QHBoxLayout *l = new QHBoxLayout;
    QWidget *w = new QWidget;
    l->setMargin(0);
    l->setContentsMargins(5, 0, 5, 0);
con's avatar
con committed
    l->addWidget(m_cursorPositionLabel);
    w->setLayout(l);

    m_toolBar = new QToolBar;
    m_toolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
    m_toolBar->addWidget(w);

    connect(editor, SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition()));
}

BaseTextEditorEditable::~BaseTextEditorEditable()
{
    delete m_toolBar;
    delete e;
}

con's avatar
con committed
QWidget *BaseTextEditorEditable::toolBar()
con's avatar
con committed
{
    return m_toolBar;
}

int BaseTextEditorEditable::find(const QString &) const
{
    return 0;
}

int BaseTextEditorEditable::currentLine() const
{
    return e->textCursor().blockNumber() + 1;
}

int BaseTextEditorEditable::currentColumn() const
{
    QTextCursor cursor = e->textCursor();
    return cursor.position() - cursor.block().position() + 1;
}

QRect BaseTextEditorEditable::cursorRect(int pos) const
{
    QTextCursor tc = e->textCursor();
    if (pos >= 0)
        tc.setPosition(pos);
    QRect result = e->cursorRect(tc);
    result.moveTo(e->viewport()->mapToGlobal(result.topLeft()));
    return result;
}

QString BaseTextEditorEditable::contents() const
{
    return e->toPlainText();
}

QString BaseTextEditorEditable::selectedText() const
{
    if (e->textCursor().hasSelection())
        return e->textCursor().selectedText();
    return QString();
}

QString BaseTextEditorEditable::textAt(int pos, int length) const
{
    QTextCursor c = e->textCursor();

    if (pos < 0)
        pos = 0;
    c.movePosition(QTextCursor::End);
    if (pos + length > c.position())
        length = c.position() - pos;

    c.setPosition(pos);
    c.setPosition(pos + length, QTextCursor::KeepAnchor);

    return c.selectedText();
}

void BaseTextEditorEditable::remove(int length)
{
    QTextCursor tc = e->textCursor();
    tc.setPosition(tc.position() + length, QTextCursor::KeepAnchor);
    tc.removeSelectedText();
}

void BaseTextEditorEditable::insert(const QString &string)
{
    QTextCursor tc = e->textCursor();
    tc.insertText(string);
}

void BaseTextEditorEditable::replace(int length, const QString &string)
{
    QTextCursor tc = e->textCursor();
    tc.setPosition(tc.position() + length, QTextCursor::KeepAnchor);
    tc.insertText(string);
}

void BaseTextEditorEditable::setCurPos(int pos)
{
    QTextCursor tc = e->textCursor();
    tc.setPosition(pos);
    e->setTextCursor(tc);
}

void BaseTextEditorEditable::select(int toPos)
{
    QTextCursor tc = e->textCursor();
    tc.setPosition(toPos, QTextCursor::KeepAnchor);
    e->setTextCursor(tc);
}

void BaseTextEditorEditable::updateCursorPosition()
{
    const QTextCursor cursor = e->textCursor();
    const QTextBlock block = cursor.block();
    const int line = block.blockNumber() + 1;
    const int column = cursor.position() - block.position();
    m_cursorPositionLabel->setText(tr("Line: %1, Col: %2").arg(line).arg(e->tabSettings().columnAt(block.text(), column)+1),
                                   tr("Line: %1, Col: 999").arg(e->blockCount()));
con's avatar
con committed
    m_contextHelpId.clear();

    if (!block.isVisible())
        e->ensureCursorVisible();

}

QString BaseTextEditorEditable::contextHelpId() const
{
    if (m_contextHelpId.isEmpty())
        emit const_cast<BaseTextEditorEditable*>(this)->contextHelpIdRequested(e->editableInterface(),
                                                                               e->textCursor().position());
    return m_contextHelpId;
}