diff --git a/src/cutter.cpp b/src/cutter.cpp index 0894494f..4e4cf0c0 100644 --- a/src/cutter.cpp +++ b/src/cutter.cpp @@ -409,7 +409,7 @@ RVA CutterCore::nextOpAddr(RVA startAddr, int count) { CORE_LOCK(); - QJsonArray array = Core()->cmdj("pdj " + QString::number(count) + "@" + QString::number(startAddr)).array(); + QJsonArray array = Core()->cmdj("pdj " + QString::number(count+1) + "@" + QString::number(startAddr)).array(); if (array.isEmpty()) { return startAddr + 1; diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index ba40530a..e6dcbd7a 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -25,6 +25,17 @@ public: } }; +static DisassemblyTextBlockUserData *getUserData(const QTextBlock &block) +{ + QTextBlockUserData *userData = block.userData(); + if (!userData) + { + return nullptr; + } + + return static_cast(userData); +} + DisassemblyWidget::DisassemblyWidget(QWidget *parent) : QDockWidget(parent) @@ -33,6 +44,8 @@ DisassemblyWidget::DisassemblyWidget(QWidget *parent) , mDisasTextEdit(new DisassemblyTextEdit(this)) { topOffset = bottomOffset = RVA_INVALID; + cursorLineOffset = 0; + seekFromCursor = false; QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(mDisasTextEdit); @@ -126,6 +139,18 @@ DisassemblyWidget::DisassemblyWidget(QWidget *parent) QShortcut *shortcut_escape = new QShortcut(QKeySequence(Qt::Key_Escape), this); shortcut_escape->setContext(Qt::WidgetShortcut); connect(shortcut_escape, SIGNAL(activated()), this, SLOT(seekPrev())); + + +#define ADD_SHORTCUT(ksq, slot) { \ + QShortcut *s = new QShortcut((ksq), this); \ + s->setContext(Qt::WidgetShortcut); \ + connect(s, &QShortcut::activated, this, (slot)); \ +} + ADD_SHORTCUT(QKeySequence::MoveToNextLine, [this]() { moveCursorRelative(false, false); }) + ADD_SHORTCUT(QKeySequence::MoveToPreviousLine, [this]() { moveCursorRelative(true, false); }) + ADD_SHORTCUT(QKeySequence::MoveToNextPage, [this]() { moveCursorRelative(false, true); }) + ADD_SHORTCUT(QKeySequence::MoveToPreviousPage, [this]() { moveCursorRelative(true, true); }) +#undef ADD_SHORTCUT } DisassemblyWidget::DisassemblyWidget(const QString &title, QWidget *parent) : @@ -315,14 +340,13 @@ RVA DisassemblyWidget::readCurrentDisassemblyOffset() RVA DisassemblyWidget::readDisassemblyOffset(QTextCursor tc) { - QTextBlockUserData *userData = tc.block().userData(); + auto userData = getUserData(tc.block()); if (!userData) { return RVA_INVALID; } - auto *dsUserData = static_cast(userData); - return dsUserData->line.offset; + return userData->line.offset; } void DisassemblyWidget::updateCursorPosition() @@ -356,6 +380,11 @@ void DisassemblyWidget::updateCursorPosition() RVA lineOffset = readDisassemblyOffset(cursor); if (lineOffset == offset) { + if (cursorLineOffset > 0) + { + cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, cursorLineOffset); + } + mDisasTextEdit->setTextCursor(cursor); highlightCurrentLine(); break; @@ -401,11 +430,62 @@ void DisassemblyWidget::connectCursorPositionChanged(bool disconnect) void DisassemblyWidget::cursorPositionChanged() { RVA offset = readCurrentDisassemblyOffset(); + + cursorLineOffset = 0; + QTextCursor c = mDisasTextEdit->textCursor(); + while (c.blockNumber() > 0) + { + c.movePosition(QTextCursor::PreviousBlock); + if (readDisassemblyOffset(c) != offset) + { + break; + } + cursorLineOffset++; + } + + seekFromCursor = true; Core()->seek(offset); + seekFromCursor = false; highlightCurrentLine(); mCtxMenu->setCanCopy(mDisasTextEdit->textCursor().hasSelection()); } +void DisassemblyWidget::moveCursorRelative(bool up, bool page) +{ + if (page) + { + // TODO: implement page up/down + return; + } + + int blockCount = mDisasTextEdit->blockCount(); + if (blockCount < 1) + { + return; + } + + int blockNumber = mDisasTextEdit->textCursor().blockNumber(); + + if (blockNumber == blockCount - 1 && !up) + { + scrollInstructions(1); + } + else if (blockNumber == 0 && up) + { + scrollInstructions(-1); + } + + mDisasTextEdit->moveCursor(up ? QTextCursor::Up : QTextCursor::Down); + + // handle cases where top instruction offsets change + RVA offset = readCurrentDisassemblyOffset(); + if (offset != Core()->getOffset()) + { + Core()->seek(offset); + highlightCurrentLine(); + } +} + bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event) { if ((obj == mDisasTextEdit || obj == mDisasTextEdit->viewport()) && event->type() == QEvent::MouseButtonDblClick) @@ -439,6 +519,11 @@ bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event) void DisassemblyWidget::on_seekChanged(RVA offset) { + if (!seekFromCursor) + { + cursorLineOffset = 0; + } + if (topOffset != RVA_INVALID && bottomOffset != RVA_INVALID && offset >= topOffset && offset <= bottomOffset) { @@ -483,6 +568,7 @@ void DisassemblyWidget::setupFonts() mDisasTextEdit->setFont(Config()->getFont()); } + void DisassemblyWidget::setupColors() { mDisasTextEdit->setStyleSheet(QString("QPlainTextEdit { background-color: %1; color: %2; }") @@ -490,7 +576,6 @@ void DisassemblyWidget::setupColors() .arg(ConfigColor("btext").name())); } - DisassemblyScrollArea::DisassemblyScrollArea(QWidget *parent) : QAbstractScrollArea(parent) { } diff --git a/src/widgets/DisassemblyWidget.h b/src/widgets/DisassemblyWidget.h index 84da9b99..767230ea 100644 --- a/src/widgets/DisassemblyWidget.h +++ b/src/widgets/DisassemblyWidget.h @@ -46,6 +46,12 @@ private: RVA bottomOffset; int maxLines; + /*! + * offset of lines below the first line of the current seek + */ + int cursorLineOffset; + bool seekFromCursor; + RVA readCurrentDisassemblyOffset(); RVA readDisassemblyOffset(QTextCursor tc); bool eventFilter(QObject *obj, QEvent *event); @@ -56,6 +62,8 @@ private: void updateCursorPosition(); void connectCursorPositionChanged(bool disconnect); + + void moveCursorRelative(bool up, bool page); }; class DisassemblyScrollArea : public QAbstractScrollArea