diff --git a/src/plugins/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp index 183ff884ac2..8cce9a9c02d 100644 --- a/src/plugins/fakevim/fakevimactions.cpp +++ b/src/plugins/fakevim/fakevimactions.cpp @@ -198,6 +198,7 @@ FakeVimSettings *theFakeVimSettings() createAction(s, ConfigWrapScan, true, _("WrapScan"), _("ws")); createAction(s, ConfigTildeOp, false, _("TildeOp"), _("top")); createAction(s, ConfigShowCmd, true, _("ShowCmd"), _("sc")); + createAction(s, ConfigRelativeNumber, false, _("RelativeNumber"),_("rnu")); createAction(s, ConfigScrollOff, 0, _("ScrollOff"), _("so")); createAction(s, ConfigBackspace, _("indent,eol,start"), _("ConfigBackspace"), _("bs")); createAction(s, ConfigIsKeyword, _("@,48-57,_,192-255,a-z,A-Z"), _("IsKeyword"), _("isk")); diff --git a/src/plugins/fakevim/fakevimactions.h b/src/plugins/fakevim/fakevimactions.h index dc56976b509..86516047906 100644 --- a/src/plugins/fakevim/fakevimactions.h +++ b/src/plugins/fakevim/fakevimactions.h @@ -98,7 +98,8 @@ enum FakeVimSettingsCode ConfigPassKeys, ConfigClipboard, ConfigShowCmd, - ConfigScrollOff + ConfigScrollOff, + ConfigRelativeNumber }; class FakeVimSettings : public QObject diff --git a/src/plugins/fakevim/fakevimoptions.ui b/src/plugins/fakevim/fakevimoptions.ui index 6f5d21d6b19..aa14acedb47 100644 --- a/src/plugins/fakevim/fakevimoptions.ui +++ b/src/plugins/fakevim/fakevimoptions.ui @@ -137,6 +137,16 @@ + + + + Display line numbers relative to the line containing text cursor. + + + Show line numbers relative to cursor + + + @@ -372,6 +382,7 @@ checkBoxWrapScan checkBoxShowMarks checkBoxPassControlKey + checkBoxRelativeNumber spinBoxShiftWidth spinBoxTabStop spinBoxScrollOff diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 7a7cc902713..a7ed73ab840 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -86,8 +86,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -231,6 +233,127 @@ private: int m_lastMessageLevel; }; +class RelativeNumbersColumn : public QWidget +{ + Q_OBJECT + +public: + RelativeNumbersColumn(BaseTextEditorWidget *baseTextEditor) + : QWidget(baseTextEditor) + , m_currentPos(0) + , m_lineSpacing(0) + , m_editor(baseTextEditor) + { + setAttribute(Qt::WA_TransparentForMouseEvents, true); + + m_timerUpdate.setSingleShot(true); + m_timerUpdate.setInterval(0); + connect(&m_timerUpdate, SIGNAL(timeout()), SLOT(followEditorLayout())); + updateOnSignal(m_editor, SIGNAL(cursorPositionChanged())); + updateOnSignal(m_editor->verticalScrollBar(), SIGNAL(valueChanged(int))); + updateOnSignal(m_editor->document(), SIGNAL(contentsChanged())); + updateOnSignal(TextEditorSettings::instance(), + SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings))); + + m_editor->installEventFilter(this); + + followEditorLayout(); + } + +protected: + void paintEvent(QPaintEvent *event) + { + QTextCursor firstVisibleCursor = m_editor->cursorForPosition(QPoint(0, 0)); + QTextBlock firstVisibleBlock = firstVisibleCursor.block(); + if (firstVisibleCursor.positionInBlock() > 0) { + firstVisibleBlock = firstVisibleBlock.next(); + firstVisibleCursor.setPosition(firstVisibleBlock.position()); + } + + // Find relative number for the first visible line. + QTextBlock block = m_editor->textCursor().block(); + bool forward = firstVisibleBlock.blockNumber() > block.blockNumber(); + int n = 0; + while (block.isValid() && block != firstVisibleBlock) { + block = forward ? block.next() : block.previous(); + if (block.isVisible()) + n += forward ? 1 : -1; + } + + // Copy colors from extra area palette. + QPainter p(this); + QPalette pal = m_editor->extraArea()->palette(); + const QColor fg = pal.color(QPalette::Dark); + const QColor bg = pal.color(QPalette::Background); + p.setPen(fg); + + // Draw relative line numbers. + QRect rect(0, m_editor->cursorRect(firstVisibleCursor).y(), width(), m_lineSpacing); + bool hideLineNumbers = m_editor->lineNumbersVisible(); + while (block.isValid()) { + if (block.isVisible()) { + if (n != 0 && rect.intersects(event->rect())) { + const int line = qAbs(n); + const QString number = QString::number(line); + if (hideLineNumbers) + p.fillRect(rect, bg); + if (hideLineNumbers || line < 100) + p.drawText(rect, Qt::AlignRight | Qt::AlignVCenter, number); + } + + rect.translate(0, m_lineSpacing * block.lineCount()); + if (rect.y() > height()) + break; + + ++n; + } + + block = block.next(); + } + } + + bool eventFilter(QObject *, QEvent *event) + { + if (event->type() == QEvent::Resize || event->type() == QEvent::Move) + m_timerUpdate.start(); + return false; + } + +private slots: + void followEditorLayout() + { + QTextCursor tc = m_editor->textCursor(); + m_currentPos = tc.position(); + m_lineSpacing = m_editor->cursorRect(tc).height(); + setFont(m_editor->extraArea()->font()); + + // Follow geometry of normal line numbers if visible, + // otherwise follow geometry of marks (breakpoints etc.). + QRect rect = m_editor->extraArea()->geometry().adjusted(0, 0, -3, 0); + bool marksVisible = m_editor->marksVisible(); + bool lineNumbersVisible = m_editor->lineNumbersVisible(); + bool foldMarksVisible = m_editor->codeFoldingVisible(); + if (marksVisible && lineNumbersVisible) + rect.setLeft(m_lineSpacing); + if (foldMarksVisible && (marksVisible || lineNumbersVisible)) + rect.setRight(rect.right() - (m_lineSpacing + m_lineSpacing % 2)); + setGeometry(rect); + + update(); + } + + void updateOnSignal(QObject *object, const char *signal) + { + connect(object, signal, &m_timerUpdate, SLOT(start())); + } + +private: + int m_currentPos; + int m_lineSpacing; + BaseTextEditorWidget *m_editor; + QTimer m_timerUpdate; +}; + /////////////////////////////////////////////////////////////////////// // // FakeVimOptionPage @@ -334,6 +457,9 @@ QWidget *FakeVimOptionPage::widget() m_group.insert(theFakeVimSetting(ConfigShowCmd), m_ui.checkBoxShowCmd); + m_group.insert(theFakeVimSetting(ConfigRelativeNumber), + m_ui.checkBoxRelativeNumber); + connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()), SLOT(copyTextEditorSettings())); connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()), @@ -901,6 +1027,7 @@ private slots: void maybeReadVimRc(); void setBlockSelection(bool); void hasBlockSelection(bool*); + void setShowRelativeLineNumbers(const QVariant &value); void resetCommandBuffer(); void showCommandBuffer(const QString &contents, int cursorPos, int anchorPos, @@ -923,6 +1050,8 @@ private slots: void switchToFile(int n); int currentFile() const; + void createRelativeNumberWidget(IEditor *editor); + signals: void delayedQuitRequested(bool forced, Core::IEditor *editor); void delayedQuitAllRequested(bool forced); @@ -1104,6 +1233,8 @@ bool FakeVimPluginPrivate::initialize() this, SLOT(maybeReadVimRc())); connect(theFakeVimSetting(ConfigVimRcPath), SIGNAL(valueChanged(QVariant)), this, SLOT(maybeReadVimRc())); + connect(theFakeVimSetting(ConfigRelativeNumber), SIGNAL(valueChanged(QVariant)), + this, SLOT(setShowRelativeLineNumbers(QVariant))); // Delayed operations. connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)), @@ -1143,6 +1274,15 @@ void FakeVimPluginPrivate::userActionTriggered() } } +void FakeVimPluginPrivate::createRelativeNumberWidget(IEditor *editor) +{ + if (BaseTextEditorWidget *textEditor = qobject_cast(editor->widget())) { + RelativeNumbersColumn *relativeNumbers = new RelativeNumbersColumn(textEditor); + connect(theFakeVimSetting(ConfigRelativeNumber), SIGNAL(valueChanged(QVariant)), + relativeNumbers, SLOT(deleteLater())); + relativeNumbers->show(); + } +} const char exCommandMapGroup[] = "FakeVimExCommand"; const char userCommandMapGroup[] = "FakeVimUserCommand"; @@ -1673,6 +1813,9 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor) resetCommandBuffer(); handler->setupWidget(); } + + if (theFakeVimSetting(ConfigRelativeNumber)->value().toBool()) + createRelativeNumberWidget(editor); } void FakeVimPluginPrivate::editorAboutToClose(IEditor *editor) @@ -1746,6 +1889,14 @@ void FakeVimPluginPrivate::hasBlockSelection(bool *on) *on = bt->hasBlockSelection(); } +void FakeVimPluginPrivate::setShowRelativeLineNumbers(const QVariant &value) +{ + if (value.toBool()) { + foreach (IEditor *editor, m_editorToHandler.keys()) + createRelativeNumberWidget(editor); + } +} + void FakeVimPluginPrivate::checkForElectricCharacter(bool *result, QChar c) { FakeVimHandler *handler = qobject_cast(sender());