mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-19 02:48:49 +00:00
DisassemblyWidget (#80)
* Fix scrolling down in DisassemblyWidget * DisassemblyWidget backwards scrolling * DisassemblyWidget with custom scrolling * Temporarily fix HexdumpWidget resizing like crazy * Decouple DisassemblyWidget scrolling from seek * DisassemblyWidget resizing * DisassemblyWidget cursor from seek position * Seek by DisassemblyWidget click * Better up scrolling in DisassemblyWidget * DisassemblyWidget: do not always seek, better bottomOffset * DisassemblyWidget: avoid flicker, retain selection over lines, fix last line selection * Update DisassemblyWidget on comment change * Cleanup DisassemblyWidget scrolling code
This commit is contained in:
parent
638956b41b
commit
9dc51b9801
@ -491,7 +491,7 @@ void MainWindow::updateFrames()
|
|||||||
static bool first_time = true;
|
static bool first_time = true;
|
||||||
|
|
||||||
//TODO Send signal rather than that
|
//TODO Send signal rather than that
|
||||||
disassemblyDock->refreshDisasm();
|
disassemblyDock->refreshDisasm(core->getOffset());
|
||||||
|
|
||||||
if (first_time)
|
if (first_time)
|
||||||
{
|
{
|
||||||
|
@ -346,8 +346,8 @@ void CutterCore::setComment(RVA addr, QString cmt)
|
|||||||
|
|
||||||
void CutterCore::delComment(ut64 addr)
|
void CutterCore::delComment(ut64 addr)
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
cmd("CC- @ " + QString::number(addr));
|
||||||
r_meta_del(core_->anal, 'C', addr, 1, NULL);
|
emit commentsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QList<QList<QString>>> CutterCore::getNestedComments()
|
QMap<QString, QList<QList<QString>>> CutterCore::getNestedComments()
|
||||||
@ -377,7 +377,7 @@ void CutterCore::seek(QString addr)
|
|||||||
// here, or refactor radare2 API.
|
// here, or refactor radare2 API.
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
cmd(QString("s %1").arg(addr));
|
cmd(QString("s %1").arg(addr));
|
||||||
emit seekChanged(core_->offset);
|
// cmd already does emit seekChanged(core_->offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CutterCore::seek(ut64 offset)
|
void CutterCore::seek(ut64 offset)
|
||||||
@ -395,6 +395,43 @@ void CutterCore::seekNext()
|
|||||||
cmd("s+");
|
cmd("s+");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RVA CutterCore::prevOpAddr(RVA startAddr, int count)
|
||||||
|
{
|
||||||
|
CORE_LOCK();
|
||||||
|
RVA prev;
|
||||||
|
if (!r_core_prevop_addr(core_, startAddr, count, &prev))
|
||||||
|
{
|
||||||
|
prev = startAddr - count;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
RVA CutterCore::nextOpAddr(RVA startAddr, int count)
|
||||||
|
{
|
||||||
|
CORE_LOCK();
|
||||||
|
|
||||||
|
QJsonArray array = Core()->cmdj("pdj " + QString::number(count) + "@" + QString::number(startAddr)).array();
|
||||||
|
if (array.isEmpty())
|
||||||
|
{
|
||||||
|
return startAddr + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonValue instValue = array.last();
|
||||||
|
if (!instValue.isObject())
|
||||||
|
{
|
||||||
|
return startAddr + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
RVA offset = instValue.toObject()["offset"].toVariant().toULongLong(&ok);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
return startAddr + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
RVA CutterCore::getOffset()
|
RVA CutterCore::getOffset()
|
||||||
{
|
{
|
||||||
return core_->offset;
|
return core_->offset;
|
||||||
|
@ -215,8 +215,11 @@ public:
|
|||||||
void seekNext();
|
void seekNext();
|
||||||
RVA getOffset();
|
RVA getOffset();
|
||||||
|
|
||||||
|
RVA prevOpAddr(RVA startAddr, int count);
|
||||||
|
RVA nextOpAddr(RVA startAddr, int count);
|
||||||
|
|
||||||
// Graph - Disassembly view priority
|
// Graph - Disassembly view priority
|
||||||
bool graphPriority = true;
|
bool graphPriority = false;
|
||||||
bool graphDisplay = false;
|
bool graphDisplay = false;
|
||||||
|
|
||||||
ut64 math(const QString &expr);
|
ut64 math(const QString &expr);
|
||||||
|
@ -4,27 +4,42 @@
|
|||||||
#include "utils/HexAsciiHighlighter.h"
|
#include "utils/HexAsciiHighlighter.h"
|
||||||
#include "utils/HexHighlighter.h"
|
#include "utils/HexHighlighter.h"
|
||||||
#include "utils/Configuration.h"
|
#include "utils/Configuration.h"
|
||||||
|
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
|
||||||
DisassemblyWidget::DisassemblyWidget(QWidget *parent) :
|
DisassemblyWidget::DisassemblyWidget(QWidget *parent) :
|
||||||
QDockWidget(parent),
|
QDockWidget(parent),
|
||||||
mDisasTextEdit(new QTextEdit(this))
|
mDisasScrollArea(new DisassemblyScrollArea(this)),
|
||||||
|
mDisasTextEdit(new DisassemblyTextEdit(this))
|
||||||
{
|
{
|
||||||
// Configure Dock
|
topOffset = bottomOffset = RVA_INVALID;
|
||||||
setWidget(mDisasTextEdit);
|
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout();
|
||||||
|
layout->addWidget(mDisasTextEdit);
|
||||||
|
layout->setMargin(0);
|
||||||
|
mDisasScrollArea->viewport()->setLayout(layout);
|
||||||
|
mDisasScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
|
setWidget(mDisasScrollArea);
|
||||||
|
|
||||||
setAllowedAreas(Qt::AllDockWidgetAreas);
|
setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||||
setObjectName("DisassemblyWidget");
|
setObjectName("DisassemblyWidget");
|
||||||
|
|
||||||
|
mDisasTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
mDisasTextEdit->setFont(Config()->getFont());
|
mDisasTextEdit->setFont(Config()->getFont());
|
||||||
mDisasTextEdit->setReadOnly(true);
|
mDisasTextEdit->setReadOnly(true);
|
||||||
|
mDisasTextEdit->setLineWrapMode(QPlainTextEdit::WidgetWidth);
|
||||||
|
// wrapping breaks readCurrentDisassemblyOffset() at the moment :-(
|
||||||
|
mDisasTextEdit->setWordWrapMode(QTextOption::NoWrap);
|
||||||
|
|
||||||
// Increase asm text edit margin
|
// Increase asm text edit margin
|
||||||
QTextDocument *asm_docu = mDisasTextEdit->document();
|
QTextDocument *asm_docu = mDisasTextEdit->document();
|
||||||
asm_docu->setDocumentMargin(10);
|
asm_docu->setDocumentMargin(10);
|
||||||
|
|
||||||
// Setup disasm highlight
|
|
||||||
connect(mDisasTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
|
|
||||||
highlightCurrentLine();
|
|
||||||
|
|
||||||
// Event filter to intercept double clicks in the textbox
|
// Event filter to intercept double clicks in the textbox
|
||||||
mDisasTextEdit->viewport()->installEventFilter(this);
|
mDisasTextEdit->viewport()->installEventFilter(this);
|
||||||
@ -39,10 +54,25 @@ DisassemblyWidget::DisassemblyWidget(QWidget *parent) :
|
|||||||
shortcut_x->setContext(Qt::WidgetShortcut);
|
shortcut_x->setContext(Qt::WidgetShortcut);
|
||||||
connect(shortcut_x, SIGNAL(activated()), this, SLOT(showXrefsDialog()));
|
connect(shortcut_x, SIGNAL(activated()), this, SLOT(showXrefsDialog()));
|
||||||
|
|
||||||
// Scrollbar
|
|
||||||
connect(mDisasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
maxLines = 0;
|
||||||
|
updateMaxLines();
|
||||||
|
|
||||||
|
|
||||||
|
connect(mDisasScrollArea, SIGNAL(scrollLines(int)), this, SLOT(scrollInstructions(int)));
|
||||||
|
connect(mDisasScrollArea, SIGNAL(disassemblyResized()), this, SLOT(updateMaxLines()));
|
||||||
|
|
||||||
|
connectCursorPositionChanged(false);
|
||||||
|
connect(mDisasTextEdit->verticalScrollBar(), &QScrollBar::valueChanged, this, [=](int value) {
|
||||||
|
if (value != 0)
|
||||||
|
{
|
||||||
|
mDisasTextEdit->verticalScrollBar()->setValue(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Seek signal
|
// Seek signal
|
||||||
connect(CutterCore::getInstance(), SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA)));
|
connect(CutterCore::getInstance(), SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA)));
|
||||||
|
connect(CutterCore::getInstance(), SIGNAL(commentsChanged()), this, SLOT(refreshDisasm()));
|
||||||
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot()));
|
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdatedSlot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,122 +87,105 @@ QWidget* DisassemblyWidget::getTextWidget()
|
|||||||
return mDisasTextEdit;
|
return mDisasTextEdit;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DisassemblyWidget::readDisasm(RVA offset)
|
QString DisassemblyWidget::readDisasm(const QString &cmd, bool stripLastNewline)
|
||||||
{
|
{
|
||||||
QString cmd = "pd 100";
|
|
||||||
Core()->setConfig("scr.html", true);
|
Core()->setConfig("scr.html", true);
|
||||||
Core()->setConfig("scr.color", true);
|
Core()->setConfig("scr.color", true);
|
||||||
if (offset != RVA_INVALID) {
|
|
||||||
cmd += " @ " + QString::number(offset);
|
|
||||||
}
|
|
||||||
QString disas = Core()->cmd(cmd);
|
QString disas = Core()->cmd(cmd);
|
||||||
Core()->setConfig("scr.html", false);
|
Core()->setConfig("scr.html", false);
|
||||||
Core()->setConfig("scr.color", false);
|
Core()->setConfig("scr.color", false);
|
||||||
|
|
||||||
|
if (stripLastNewline)
|
||||||
|
{
|
||||||
|
// ugly hack to remove trailing newline
|
||||||
|
static const auto trimBrRegExp = QRegularExpression("<br />$");
|
||||||
|
disas = disas.remove(trimBrRegExp);
|
||||||
|
}
|
||||||
|
|
||||||
return disas.trimmed();
|
return disas.trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblyWidget::refreshDisasm()
|
|
||||||
|
void DisassemblyWidget::refreshDisasm(RVA offset)
|
||||||
{
|
{
|
||||||
// Prevent further scroll
|
|
||||||
disconnect(mDisasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
|
||||||
disconnect(mDisasTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
|
|
||||||
|
|
||||||
QString disas = readDisasm();
|
|
||||||
mDisasTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
|
||||||
mDisasTextEdit->setHtml(disas);
|
|
||||||
|
|
||||||
auto cursor = mDisasTextEdit->textCursor();
|
|
||||||
cursor.setPosition(0);
|
|
||||||
mDisasTextEdit->setTextCursor(cursor);
|
|
||||||
mDisasTextEdit->verticalScrollBar()->setValue(0);
|
|
||||||
|
|
||||||
// load more disassembly if necessary
|
|
||||||
/*static const int load_more_limit = 10; // limit passes, so it can't take forever
|
|
||||||
for (int load_more_i = 0; load_more_i < load_more_limit; load_more_i++)
|
|
||||||
{
|
|
||||||
if (!loadMoreDisassembly())
|
|
||||||
break;
|
|
||||||
mDisasTextEdit->verticalScrollBar()->setValue(0);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
connect(mDisasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
|
||||||
connect(mDisasTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
|
|
||||||
//this->on_mDisasTextEdit_cursorPositionChanged();
|
|
||||||
|
|
||||||
//this->highlightDisasms();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool DisassemblyWidget::loadMoreDisassembly()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Add more disasm as the user scrolls
|
|
||||||
* Not working properly when scrolling upwards
|
|
||||||
* r2 doesn't handle properly 'pd-' for archs with variable instruction size
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Disconnect scroll signals to add more content
|
|
||||||
disconnect(mDisasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
|
||||||
|
|
||||||
QScrollBar *sb = mDisasTextEdit->verticalScrollBar();
|
|
||||||
bool loaded = false;
|
|
||||||
|
|
||||||
if (sb->value() > sb->maximum() - 10)
|
|
||||||
{
|
|
||||||
QTextCursor tc = mDisasTextEdit->textCursor();
|
|
||||||
tc.movePosition(QTextCursor::End);
|
|
||||||
tc.movePosition(QTextCursor::StartOfLine);
|
|
||||||
tc.setPosition(tc.position() - 1);
|
|
||||||
mDisasTextEdit->setTextCursor(tc);
|
|
||||||
RVA offset = readCurrentDisassemblyOffset();
|
|
||||||
|
|
||||||
if (offset != RVA_INVALID)
|
if (offset != RVA_INVALID)
|
||||||
{
|
{
|
||||||
mDisasTextEdit->append(readDisasm(offset));
|
topOffset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
loaded = true;
|
if (maxLines <= 0)
|
||||||
|
{
|
||||||
|
mDisasTextEdit->clear();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Code below will be used to append more disasm upwards, one day
|
|
||||||
/* else if (sb->value() < sb->minimum() + 10) {
|
|
||||||
//this->main->add_debug_output("Begining is coming");
|
|
||||||
|
|
||||||
QTextCursor tc = this->disasTextEdit->textCursor();
|
int horizontalScrollValue = mDisasTextEdit->horizontalScrollBar()->value();
|
||||||
tc.movePosition( QTextCursor::Start );
|
mDisasTextEdit->setLockScroll(true); // avoid flicker
|
||||||
tc.select( QTextCursor::LineUnderCursor );
|
|
||||||
QString firstline = tc.selectedText();
|
QString disas = readDisasm("pd " + QString::number(maxLines) + "@" + QString::number(topOffset), true);
|
||||||
//this->main->add_debug_output("First Line: " + firstline);
|
|
||||||
QString ele = firstline.split(" ", QString::SkipEmptyParts)[0];
|
connectCursorPositionChanged(true);
|
||||||
//this->main->add_debug_output("First Offset: " + ele);
|
|
||||||
if (ele.contains("0x")) {
|
mDisasTextEdit->document()->setHtml(disas);
|
||||||
int b = this->disasTextEdit->verticalScrollBar()->maximum();
|
|
||||||
this->core->cmd("ss " + ele);
|
// get bottomOffset from last visible line.
|
||||||
this->core->cmd("so -50");
|
// because pd N may return more than N lines, move maxLines lines down from the top
|
||||||
QString raw = this->core->cmd("pd 50");
|
mDisasTextEdit->moveCursor(QTextCursor::Start);
|
||||||
//this->main->add_debug_output(raw);
|
QTextCursor tc = mDisasTextEdit->textCursor();
|
||||||
//QString txt = raw.section("\n", 1, -1);
|
tc.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, maxLines-1);
|
||||||
//this->main->add_debug_output(txt);
|
mDisasTextEdit->setTextCursor(tc);
|
||||||
tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
||||||
//tc.insertText(raw.trimmed() + "\n ;\n ; New content prepended here\n ;\n");
|
connectCursorPositionChanged(false);
|
||||||
int c = this->disasTextEdit->verticalScrollBar()->maximum();
|
|
||||||
int z = c -b;
|
bottomOffset = readCurrentDisassemblyOffset();
|
||||||
int a = this->disasTextEdit->verticalScrollBar()->sliderPosition();
|
if (bottomOffset == RVA_INVALID)
|
||||||
this->disasTextEdit->verticalScrollBar()->setValue(a + z);
|
{
|
||||||
} else {
|
bottomOffset = topOffset;
|
||||||
tc.movePosition( QTextCursor::Start );
|
|
||||||
tc.select( QTextCursor::LineUnderCursor );
|
|
||||||
QString lastline = tc.selectedText();
|
|
||||||
this->main->add_debug_output("Last line: " + lastline);
|
|
||||||
}
|
}
|
||||||
} */
|
|
||||||
|
|
||||||
// Reconnect scroll signals
|
updateCursorPosition();
|
||||||
connect(mDisasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
|
||||||
|
|
||||||
return loaded;
|
mDisasTextEdit->setLockScroll(false);
|
||||||
|
mDisasTextEdit->horizontalScrollBar()->setValue(horizontalScrollValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DisassemblyWidget::scrollInstructions(int count)
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RVA offset;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
offset = Core()->nextOpAddr(topOffset, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset = Core()->prevOpAddr(topOffset, -count);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshDisasm(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DisassemblyWidget::updateMaxLines()
|
||||||
|
{
|
||||||
|
QFontMetrics fontMetrics(mDisasTextEdit->document()->defaultFont());
|
||||||
|
int currentMaxLines = (mDisasTextEdit->height() -
|
||||||
|
(mDisasTextEdit->contentsMargins().top() + mDisasTextEdit->contentsMargins().bottom()
|
||||||
|
+ (int)(mDisasTextEdit->document()->documentMargin() * 2)))
|
||||||
|
/ fontMetrics.lineSpacing();
|
||||||
|
|
||||||
|
if (currentMaxLines != maxLines)
|
||||||
|
{
|
||||||
|
maxLines = currentMaxLines;
|
||||||
|
refreshDisasm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DisassemblyWidget::highlightCurrentLine()
|
void DisassemblyWidget::highlightCurrentLine()
|
||||||
{
|
{
|
||||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||||
@ -241,91 +254,107 @@ void DisassemblyWidget::showDisasContextMenu(const QPoint &pt)
|
|||||||
RVA DisassemblyWidget::readCurrentDisassemblyOffset()
|
RVA DisassemblyWidget::readCurrentDisassemblyOffset()
|
||||||
{
|
{
|
||||||
// TODO: do this in a different way without parsing the disassembly text
|
// TODO: do this in a different way without parsing the disassembly text
|
||||||
|
|
||||||
|
static const QRegularExpression offsetRegExp("^0x[0-9A-Fa-f]*");
|
||||||
|
|
||||||
QTextCursor tc = mDisasTextEdit->textCursor();
|
QTextCursor tc = mDisasTextEdit->textCursor();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
tc.select(QTextCursor::LineUnderCursor);
|
tc.select(QTextCursor::LineUnderCursor);
|
||||||
QString lastline = tc.selectedText();
|
|
||||||
QStringList parts = lastline.split("\u00a0", QString::SkipEmptyParts);
|
|
||||||
|
|
||||||
if (parts.isEmpty()) {
|
QString line = tc.selectedText();
|
||||||
return RVA_INVALID;
|
|
||||||
|
auto match = offsetRegExp.match(line);
|
||||||
|
if (match.hasMatch())
|
||||||
|
{
|
||||||
|
return match.captured(0).toULongLong(nullptr, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ele = parts[0];
|
tc.movePosition(QTextCursor::StartOfLine);
|
||||||
if (!ele.contains("0x")) {
|
if (tc.atStart())
|
||||||
return RVA_INVALID;
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ele.toULongLong(0, 16);
|
tc.movePosition(QTextCursor::Up);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RVA_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblyWidget::disasmScrolled()
|
void DisassemblyWidget::updateCursorPosition()
|
||||||
{
|
{
|
||||||
loadMoreDisassembly();
|
connectCursorPositionChanged(true);
|
||||||
|
RVA offset = Core()->getOffset();
|
||||||
|
|
||||||
|
if (offset < topOffset || (offset > bottomOffset && bottomOffset != RVA_INVALID))
|
||||||
|
{
|
||||||
|
mDisasTextEdit->moveCursor(QTextCursor::Start);
|
||||||
|
mDisasTextEdit->setExtraSelections({});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RVA currentCursorOffset = readCurrentDisassemblyOffset();
|
||||||
|
QTextCursor originalCursor = mDisasTextEdit->textCursor();
|
||||||
|
|
||||||
|
QTextCursor cursor = originalCursor;
|
||||||
|
cursor.movePosition(QTextCursor::Start);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
mDisasTextEdit->setTextCursor(cursor);
|
||||||
|
RVA lineOffset = readCurrentDisassemblyOffset();
|
||||||
|
if (lineOffset == offset)
|
||||||
|
{
|
||||||
|
highlightCurrentLine();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (lineOffset != RVA_INVALID && lineOffset > offset)
|
||||||
|
{
|
||||||
|
mDisasTextEdit->moveCursor(QTextCursor::Start);
|
||||||
|
mDisasTextEdit->setExtraSelections({});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.movePosition(QTextCursor::EndOfLine);
|
||||||
|
if (cursor.atEnd())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.movePosition(QTextCursor::Down);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is true if a seek came from the user clicking on a line.
|
||||||
|
// then the cursor should be restored 1:1 to retain selection and cursor position.
|
||||||
|
if (currentCursorOffset == offset)
|
||||||
|
{
|
||||||
|
mDisasTextEdit->setTextCursor(originalCursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectCursorPositionChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisassemblyWidget::connectCursorPositionChanged(bool disconnect)
|
||||||
|
{
|
||||||
|
if (disconnect)
|
||||||
|
{
|
||||||
|
QObject::disconnect(mDisasTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connect(mDisasTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblyWidget::cursorPositionChanged()
|
void DisassemblyWidget::cursorPositionChanged()
|
||||||
{
|
{
|
||||||
// Get current offset
|
RVA offset = readCurrentDisassemblyOffset();
|
||||||
QTextCursor tc = mDisasTextEdit->textCursor();
|
Core()->seek(offset);
|
||||||
tc.select(QTextCursor::LineUnderCursor);
|
|
||||||
QString lastline = tc.selectedText().trimmed();
|
|
||||||
QList<QString> words = lastline.split(" ", QString::SkipEmptyParts);
|
|
||||||
if (words.length() == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QString ele = words[0];
|
|
||||||
// TODO
|
|
||||||
/*if (ele.contains("0x"))
|
|
||||||
{
|
|
||||||
this->fillOffsetInfo(ele);
|
|
||||||
QString at = this->core->cmdFunctionAt(ele);
|
|
||||||
QString deco = this->core->getDecompiledCode(at);
|
|
||||||
|
|
||||||
|
|
||||||
RVA addr = ele.midRef(2).toULongLong(0, 16);
|
|
||||||
// FIXME per widget CursorAddress no?
|
|
||||||
// this->main->setCursorAddress(addr);
|
|
||||||
|
|
||||||
if (deco != "")
|
|
||||||
{
|
|
||||||
ui->decoTextEdit->setPlainText(deco);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->decoTextEdit->setPlainText("");
|
|
||||||
}
|
|
||||||
// Get jump information to fill the preview
|
|
||||||
QString jump = this->core->getOffsetJump(ele);
|
|
||||||
if (!jump.isEmpty())
|
|
||||||
{
|
|
||||||
// Fill the preview
|
|
||||||
QString jump_code = this->core->cmd("pdf @ " + jump);
|
|
||||||
ui->previewTextEdit->setPlainText(jump_code.trimmed());
|
|
||||||
ui->previewTextEdit->moveCursor(QTextCursor::End);
|
|
||||||
ui->previewTextEdit->find(jump.trimmed(), QTextDocument::FindBackward);
|
|
||||||
ui->previewTextEdit->moveCursor(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->previewTextEdit->setPlainText("");
|
|
||||||
}
|
|
||||||
//this->main->add_debug_output("Fcn at: '" + at + "'");
|
|
||||||
if (this->last_fcn != at)
|
|
||||||
{
|
|
||||||
this->last_fcn = at;
|
|
||||||
//this->main->add_debug_output("New Fcn: '" + this->last_fcn + "'");
|
|
||||||
// Refresh function information at sidebar
|
|
||||||
ui->fcnNameEdit->setText(at);
|
|
||||||
// FIXME TITLE?
|
|
||||||
// this->main->previewDock->setWindowTitle(at);
|
|
||||||
//this->main->previewDock->create_graph(ele);
|
|
||||||
this->setMiniGraph(at);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)
|
bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)
|
||||||
{
|
{
|
||||||
if ((obj == mDisasTextEdit || obj == mDisasTextEdit->viewport()) && event->type() == QEvent::MouseButtonDblClick)
|
if ((obj == mDisasTextEdit || obj == mDisasTextEdit->viewport()) && event->type() == QEvent::MouseButtonDblClick)
|
||||||
@ -368,18 +397,18 @@ void DisassemblyWidget::on_seekChanged(RVA offset)
|
|||||||
if (!Core()->graphDisplay || !Core()->graphPriority) {
|
if (!Core()->graphDisplay || !Core()->graphPriority) {
|
||||||
this->raise();
|
this->raise();
|
||||||
}
|
}
|
||||||
refreshDisasm();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisassemblyWidget::highlightDisasms()
|
if (topOffset != RVA_INVALID && bottomOffset != RVA_INVALID
|
||||||
{
|
&& offset >= topOffset && offset <= bottomOffset)
|
||||||
// TODO Useless
|
{
|
||||||
//Highlighter *highlighter = new Highlighter(mDisasTextEdit->document());
|
// if the line with the seek offset is currently visible, just move the cursor there
|
||||||
//Highlighter *highlighter_5 = new Highlighter(mDisasTextEdit->document());
|
updateCursorPosition();
|
||||||
//AsciiHighlighter *ascii_highlighter = new AsciiHighlighter(mDisasTextEdit->document());
|
}
|
||||||
//HexHighlighter *hex_highlighter = new HexHighlighter(mDisasTextEdit->document());
|
else
|
||||||
//Highlighter *preview_highlighter = new Highlighter(mDisasTextEdit->document());
|
{
|
||||||
//Highlighter *deco_highlighter = new Highlighter(mDisasTextEdit->document());
|
// otherwise scroll there
|
||||||
|
refreshDisasm(offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblyWidget::fontsUpdatedSlot()
|
void DisassemblyWidget::fontsUpdatedSlot()
|
||||||
@ -403,3 +432,51 @@ void DisassemblyWidget::showXrefsDialog()
|
|||||||
dialog->exec();
|
dialog->exec();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisassemblyScrollArea::DisassemblyScrollArea(QWidget *parent) : QAbstractScrollArea(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DisassemblyScrollArea::viewportEvent(QEvent *event)
|
||||||
|
{
|
||||||
|
int dy = verticalScrollBar()->value() - 5;
|
||||||
|
if (dy != 0)
|
||||||
|
{
|
||||||
|
emit scrollLines(dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->type() == QEvent::Resize)
|
||||||
|
{
|
||||||
|
emit disassemblyResized();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetScrollBars();
|
||||||
|
return QAbstractScrollArea::viewportEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisassemblyScrollArea::resetScrollBars()
|
||||||
|
{
|
||||||
|
verticalScrollBar()->blockSignals(true);
|
||||||
|
verticalScrollBar()->setRange(0, 10);
|
||||||
|
verticalScrollBar()->setValue(5);
|
||||||
|
verticalScrollBar()->blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DisassemblyTextEdit::viewportEvent(QEvent *event)
|
||||||
|
{
|
||||||
|
switch(event->type())
|
||||||
|
{
|
||||||
|
case QEvent::Type::Wheel:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return QAbstractScrollArea::viewportEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisassemblyTextEdit::scrollContentsBy(int dx, int dy)
|
||||||
|
{
|
||||||
|
if (!lockScroll)
|
||||||
|
{
|
||||||
|
QPlainTextEdit::scrollContentsBy(dx, dy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
#ifndef DISASSEMBLYVIEW_H
|
#ifndef DISASSEMBLYWIDGET_H
|
||||||
#define DISASSEMBLYVIEW_H
|
#define DISASSEMBLYWIDGET_H
|
||||||
|
|
||||||
#include "cutter.h"
|
#include "cutter.h"
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <QTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
|
|
||||||
|
|
||||||
|
class DisassemblyTextEdit;
|
||||||
|
class DisassemblyScrollArea;
|
||||||
|
|
||||||
class DisassemblyWidget : public QDockWidget
|
class DisassemblyWidget : public QDockWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -14,26 +18,73 @@ public:
|
|||||||
explicit DisassemblyWidget(const QString &title, QWidget *parent = nullptr);
|
explicit DisassemblyWidget(const QString &title, QWidget *parent = nullptr);
|
||||||
QWidget* getTextWidget();
|
QWidget* getTextWidget();
|
||||||
|
|
||||||
signals:
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void highlightCurrentLine();
|
void highlightCurrentLine();
|
||||||
void disasmScrolled();
|
|
||||||
void showDisasContextMenu(const QPoint &pt);
|
void showDisasContextMenu(const QPoint &pt);
|
||||||
void cursorPositionChanged();
|
|
||||||
void on_seekChanged(RVA offset);
|
void on_seekChanged(RVA offset);
|
||||||
void refreshDisasm();
|
void refreshDisasm(RVA offset = RVA_INVALID);
|
||||||
void fontsUpdatedSlot();
|
void fontsUpdatedSlot();
|
||||||
void showXrefsDialog();
|
void showXrefsDialog();
|
||||||
|
|
||||||
private:
|
private slots:
|
||||||
QTextEdit *mDisasTextEdit;
|
void scrollInstructions(int count);
|
||||||
|
void updateMaxLines();
|
||||||
|
|
||||||
QString readDisasm(RVA offset = RVA_INVALID);
|
void cursorPositionChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
DisassemblyScrollArea *mDisasScrollArea;
|
||||||
|
DisassemblyTextEdit *mDisasTextEdit;
|
||||||
|
|
||||||
|
RVA topOffset;
|
||||||
|
RVA bottomOffset;
|
||||||
|
int maxLines;
|
||||||
|
|
||||||
|
QString readDisasm(const QString &cmd, bool stripLastNewline);
|
||||||
RVA readCurrentDisassemblyOffset();
|
RVA readCurrentDisassemblyOffset();
|
||||||
bool loadMoreDisassembly();
|
|
||||||
void highlightDisasms();
|
|
||||||
bool eventFilter(QObject *obj, QEvent *event);
|
bool eventFilter(QObject *obj, QEvent *event);
|
||||||
|
|
||||||
|
void updateCursorPosition();
|
||||||
|
|
||||||
|
void connectCursorPositionChanged(bool disconnect);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DISASSEMBLYVIEW_H
|
class DisassemblyScrollArea : public QAbstractScrollArea
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DisassemblyScrollArea(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void scrollLines(int lines);
|
||||||
|
void disassemblyResized();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool viewportEvent(QEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resetScrollBars();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class DisassemblyTextEdit: public QPlainTextEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DisassemblyTextEdit(QWidget *parent = nullptr)
|
||||||
|
: QPlainTextEdit(parent),
|
||||||
|
lockScroll(false) {}
|
||||||
|
|
||||||
|
void setLockScroll(bool lock) { this->lockScroll = lock; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool viewportEvent(QEvent *event) override;
|
||||||
|
void scrollContentsBy(int dx, int dy) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool lockScroll;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DISASSEMBLYWIDGET_H
|
||||||
|
@ -309,7 +309,7 @@ void HexdumpWidget::resizeHexdump()
|
|||||||
{
|
{
|
||||||
this->hexOffsetText->setMinimumWidth(this->hexOffsetText->document()->size().width());
|
this->hexOffsetText->setMinimumWidth(this->hexOffsetText->document()->size().width());
|
||||||
this->hexHexText->setMinimumWidth(this->hexHexText->document()->size().width());
|
this->hexHexText->setMinimumWidth(this->hexHexText->document()->size().width());
|
||||||
this->hexASCIIText->setMinimumWidth(this->hexASCIIText->document()->size().width());
|
//this->hexASCIIText->setMinimumWidth(this->hexASCIIText->document()->size().width());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HexdumpWidget::hexScrolled()
|
void HexdumpWidget::hexScrolled()
|
||||||
|
Loading…
Reference in New Issue
Block a user