diff --git a/src/plugins/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp index 183ff884ac2d1f7ff1b309862879111600164860..8cce9a9c02d518e000904bcbe2b053dfcc327388 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 dc56976b50931ea6af888ccd2eac70c1fdfa33e4..8651604790640f23225c582b37e0886386a41104 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 6f5d21d6b19bfe9972e4d8a768536951b57eda2f..aa14acedb47985ed602151d4a76096ceee346636 100644 --- a/src/plugins/fakevim/fakevimoptions.ui +++ b/src/plugins/fakevim/fakevimoptions.ui @@ -137,6 +137,16 @@ </property> </widget> </item> + <item row="7" column="1"> + <widget class="QCheckBox" name="checkBoxRelativeNumber"> + <property name="toolTip"> + <string>Display line numbers relative to the line containing text cursor.</string> + </property> + <property name="text"> + <string>Show line numbers relative to cursor</string> + </property> + </widget> + </item> </layout> </item> <item> @@ -372,6 +382,7 @@ <tabstop>checkBoxWrapScan</tabstop> <tabstop>checkBoxShowMarks</tabstop> <tabstop>checkBoxPassControlKey</tabstop> + <tabstop>checkBoxRelativeNumber</tabstop> <tabstop>spinBoxShiftWidth</tabstop> <tabstop>spinBoxTabStop</tabstop> <tabstop>spinBoxScrollOff</tabstop> diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 7a7cc9027138d3b7f648459211389712ba3c8aca..a7ed73ab840f8c5b8a6ad892ed0504beeb19a921 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -86,8 +86,10 @@ #include <QFileDialog> #include <QtPlugin> #include <QObject> +#include <QPainter> #include <QPointer> #include <QSettings> +#include <QScrollBar> #include <QStackedWidget> #include <QTextStream> @@ -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<BaseTextEditorWidget *>(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<FakeVimHandler *>(sender());