Source

pynoto / src / PythonEditor / PythonEditorWidget.cpp

Full commit
#include <QDebug>
#include <QPainter>
#include <QPaintEvent>
#include <QIcon>
#include <math.h>
#include <QTextBlock>
#include "PythonEditorWidget.h"
#include "PythonCompliter.h"
#include "Include/IIconProvider.h"
#include "Include/PynotoApplication.h"
#include "ErrorModel.h"
#include "Settings.h"
#include "PythonDocumentLayout.h"
#include "Highlighter.h"

namespace PythonEditor {

PythonEditorWidget::PythonEditorWidget(const QString& fileName, bool sample, QWidget* parent):
    TextEditor::TextEditorWidget(fileName, parent),
    _compliter(NULL),
    _errorListModel(NULL),
    _isSample(sample)
{
    setMouseTracking(true);
    document()->setDocumentLayout(new PythonDocumentLayout(document()));
    _updateErrorsTimer.setSingleShot(true);
    _updateErrorsTimer.setInterval(500);
    _updateErrorsTimer.stop();
    if (!_isSample){
        connect(&_updateErrorsTimer, SIGNAL(timeout()), SLOT(checkIssues()));
    }
    Settings set;
    _showIdent = set.showIndentLine();
    _identColor = set.identColor();
}

void PythonEditorWidget::editorLoaded()
{
    connect(document(), SIGNAL(contentsChanged()), SLOT(onTextChanged()));
    _compliter = new PythonCompliter(this);
    if (!_isSample)
        _compliter->checkErrors();
}

TextEditor::Highlighter* PythonEditorWidget::hightlighter()
{
    return new PythonEditor::Highlighter(document(), "python");
}

void PythonEditorWidget::paintEvent(QPaintEvent *event)
{
    TextEditorWidget::paintEvent(event);

    if (_showIdent){
        QPainter painter(viewport());
        //painter.setRenderHint(QPainter::HighQualityAntialiasing);
        QPen pen(Qt::DotLine);
        pen.setColor(_identColor);
        pen.setCapStyle(Qt::RoundCap);
        painter.setPen(pen);

        QTextBlock block = firstVisibleBlock();

        QPointF offset(contentOffset());

        int whiteWidth = viewport()->fontMetrics().width(' ');
        int x = contentsRect().left()+offset.x()+2;
        PythonDocumentLayout *lay = qobject_cast<PythonDocumentLayout*>(document()->documentLayout());

        while (block.isValid()) {
            if (block.isVisible()) {
                for (int i = 1; i < round(lay->spaces(block)/4)+(lay->spaces(block) % 4 > 0 ? 1 : 0); ++i) {
                    int top = (int)blockBoundingGeometry(block).translated(offset).top();
                    int bottom = top + (int)blockBoundingRect(block).height();
                    painter.drawLine(x+4*whiteWidth*i, top, x+4*whiteWidth*i, bottom);
                }
            }
            block = block.next();
        }
    }
}

void PythonEditorWidget::keyPressEvent(QKeyEvent *e)
{
    if (_compliter->isVisible()){
        switch (e->key()) {
        case Qt::Key_Enter:
        case Qt::Key_Return:
        case Qt::Key_Escape:
        case Qt::Key_Tab:
        case Qt::Key_Backtab:
             e->ignore();
             return;
        default:
            break;
        }
    }
    if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return){
        if (e->modifiers() != Qt::ShiftModifier){
            autoIndentNewLine();
            e->accept();
            return;
        }
    }
    if (e->key() == Qt::Key_Space && e->modifiers() == Qt::CTRL){
        _compliter->showComplit(textCursor().position());
    }
    if (e->key() == Qt::Key_Escape){
        _compliter->hide();
    }
    TextEditorWidget::keyPressEvent(e);
}

void PythonEditorWidget::autoIndentNewLine()
{
    PythonDocumentLayout *lay = qobject_cast<PythonDocumentLayout*>(document()->documentLayout());
    QTextCursor cur = textCursor();
    QTextBlock curBlock = cur.block();
    cur.beginEditBlock();
    cur.insertBlock();
    int indent = lay->foldingIndent(curBlock);
    if  (
        curBlock.text().trimmed().endsWith(":") ||
        curBlock.text().trimmed().endsWith("(") ||
        curBlock.text().trimmed().endsWith("[") ||
        curBlock.text().trimmed().endsWith("{")
        )
        indent++;
    cur.insertText(QString(indent*4, ' '));
    cur.endEditBlock();
}

void PythonEditorWidget::setErrors(const QList<PythonCode::IPythonCode::ErrorItem>& items)
{
    clearMarks();
    foreach(PythonCode::IPythonCode::ErrorItem it, items){
        QString icoName;
        if (it.severity == PythonCode::IPythonCode::Error)
            icoName = "error";
        if (it.severity == PythonCode::IPythonCode::Warning)
            icoName = "warning";
        if (it.severity == PythonCode::IPythonCode::CodeStyle)
            icoName = "codestyle";
        if (it.severity == PythonCode::IPythonCode::Suggestion)
            icoName = "suggestion";
        setMark(it.line, Aux::icons()->icon(icoName), it.message);
    }
    _errorListModel = new ErrorModel(items, this);
    emit errorListChanged(_errorListModel);
}

QAbstractTableModel* PythonEditorWidget::errorListModel()
{
    return _errorListModel;
}

bool PythonEditorWidget::reset()
{
    _compliter->hide();
    return false;
}

void PythonEditorWidget::checkIssues()
{
    if (!_isSample)
        _compliter->checkErrors();
}

void PythonEditorWidget::preferencesChanged(const QString& what)
{
    TextEditorWidget::preferencesChanged(what);
    if (what == "show-ident-line"){
        Settings set;
        _showIdent = set.showIndentLine();
        update();
    }
    if (what == "ident-color"){
        Settings set;
        _identColor = set.identColor();
        update();
    }
}

void PythonEditorWidget::onTextChanged()
{
    _updateErrorsTimer.start();
}

void PythonEditorWidget::mouseMoveEvent(QMouseEvent *e)
{
    if (e->modifiers() & Qt::ControlModifier){
        QTextCursor cur = cursorForPosition(e->pos());
        int pos = cur.position();
        cur.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
        cur.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
        if (cur.selectedText() != _codeDecl.wordHight){
            _codeDecl.wordHight = cur.selectedText();
            _compliter->findDeclaration(pos);
        }
    }
    TextEditor::TextEditorWidget::mouseMoveEvent(e);
}

void PythonEditorWidget::declaration(int wordBegin, int wordEnd, int offset, const QString& url)
{
    viewport()->setCursor(Qt::IBeamCursor);
    setExtraSelections(LinkKind, QList<QTextEdit::ExtraSelection>());
    _codeDecl.file = "";
    _codeDecl.offset = _codeDecl.wbegin = _codeDecl.wend = -1;
    if (url.isEmpty())
        return;

    QTextCursor cur = textCursor();
    cur.setPosition(wordBegin);
    cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, wordEnd-wordBegin);

    if (cur.selectedText() == _codeDecl.wordHight){
        _codeDecl.file = url;
        _codeDecl.offset = offset;
        _codeDecl.wbegin = wordBegin;
        _codeDecl.wend = wordEnd;
        QList<QTextEdit::ExtraSelection> extra;
        QTextEdit::ExtraSelection sel;
        sel.format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
        sel.cursor = cur;
        extra.append(sel);
        setExtraSelections(LinkKind, extra);

        viewport()->setCursor(Qt::PointingHandCursor);
    }
}

void PythonEditorWidget::keyReleaseEvent(QKeyEvent *e)
{
    if (e->key() == Qt::Key_Control){
        setExtraSelections(LinkKind, QList<QTextEdit::ExtraSelection>());
        viewport()->setCursor(Qt::IBeamCursor);
    }
    TextEditor::TextEditorWidget::keyReleaseEvent(e);
}

void PythonEditorWidget::mousePressEvent(QMouseEvent *e)
{
    QTextCursor cur = cursorForPosition(e->pos());
    if (cur.position() >=  _codeDecl.wbegin && cur.position() <= _codeDecl.wend && (e->modifiers() & Qt::ControlModifier)){
        emit jumpToFileOffset(_codeDecl.file, _codeDecl.offset);
        e->isAccepted();
        return;
    }
    TextEditor::TextEditorWidget::mousePressEvent(e);
}

void PythonEditorWidget::focusOutEvent(QFocusEvent *e)
{
    setExtraSelections(LinkKind, QList<QTextEdit::ExtraSelection>());
    viewport()->setCursor(Qt::IBeamCursor);
}

}