mirror of
https://github.com/rizinorg/cutter.git
synced 2025-02-21 14:16:08 +00:00
Sync PseudocodeWidget with Seek (#1648)
This commit is contained in:
parent
db3c34a9cd
commit
7017c8a323
@ -348,7 +348,8 @@ SOURCES += \
|
|||||||
common/BugReporting.cpp \
|
common/BugReporting.cpp \
|
||||||
common/HighDpiPixmap.cpp \
|
common/HighDpiPixmap.cpp \
|
||||||
widgets/GraphGridLayout.cpp \
|
widgets/GraphGridLayout.cpp \
|
||||||
widgets/HexWidget.cpp
|
widgets/HexWidget.cpp \
|
||||||
|
common/SelectionHighlight.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
core/Cutter.h \
|
core/Cutter.h \
|
||||||
@ -474,7 +475,8 @@ HEADERS += \
|
|||||||
common/HighDpiPixmap.h \
|
common/HighDpiPixmap.h \
|
||||||
widgets/GraphLayout.h \
|
widgets/GraphLayout.h \
|
||||||
widgets/GraphGridLayout.h \
|
widgets/GraphGridLayout.h \
|
||||||
widgets/HexWidget.h
|
widgets/HexWidget.h \
|
||||||
|
common/SelectionHighlight.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
dialogs/AboutDialog.ui \
|
dialogs/AboutDialog.ui \
|
||||||
|
47
src/common/SelectionHighlight.cpp
Normal file
47
src/common/SelectionHighlight.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
#include "SelectionHighlight.h"
|
||||||
|
#include "Configuration.h"
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QTextCursor>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
QList<QTextEdit::ExtraSelection> createSameWordsSelections(QPlainTextEdit *textEdit, const QString &word)
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> selections;
|
||||||
|
QTextEdit::ExtraSelection highlightSelection;
|
||||||
|
QTextDocument *document = textEdit->document();
|
||||||
|
QColor highlightWordColor = ConfigColor("wordHighlight");
|
||||||
|
|
||||||
|
if (word.isEmpty()) {
|
||||||
|
return QList<QTextEdit::ExtraSelection>();
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightSelection.cursor = textEdit->textCursor();
|
||||||
|
highlightSelection.cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
||||||
|
|
||||||
|
while (!highlightSelection.cursor.isNull() && !highlightSelection.cursor.atEnd()) {
|
||||||
|
highlightSelection.cursor = document->find(word, highlightSelection.cursor,
|
||||||
|
QTextDocument::FindWholeWords);
|
||||||
|
|
||||||
|
if (!highlightSelection.cursor.isNull()) {
|
||||||
|
highlightSelection.format.setBackground(highlightWordColor);
|
||||||
|
|
||||||
|
selections.append(highlightSelection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selections;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextEdit::ExtraSelection createLineHighlightSelection(const QTextCursor &cursor)
|
||||||
|
{
|
||||||
|
QColor highlightColor = ConfigColor("lineHighlight");
|
||||||
|
QTextEdit::ExtraSelection highlightSelection;
|
||||||
|
highlightSelection.cursor = cursor;
|
||||||
|
highlightSelection.format.setBackground(highlightColor);
|
||||||
|
highlightSelection.format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||||
|
highlightSelection.cursor.clearSelection();
|
||||||
|
return highlightSelection;
|
||||||
|
}
|
12
src/common/SelectionHighlight.h
Normal file
12
src/common/SelectionHighlight.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef CUTTER_SELECTIONHIGHLIGHT_H
|
||||||
|
#define CUTTER_SELECTIONHIGHLIGHT_H
|
||||||
|
|
||||||
|
#include <QTextEdit>
|
||||||
|
|
||||||
|
class QPlainTextEdit;
|
||||||
|
class QString;
|
||||||
|
|
||||||
|
QList<QTextEdit::ExtraSelection> createSameWordsSelections(QPlainTextEdit *textEdit, const QString &word);
|
||||||
|
QTextEdit::ExtraSelection createLineHighlightSelection(const QTextCursor &cursor);
|
||||||
|
|
||||||
|
#endif //CUTTER_SELECTIONHIGHLIGHT_H
|
@ -931,9 +931,15 @@ RVA CutterCore::getOffsetJump(RVA addr)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CutterCore::getDecompiledCodePDC(RVA addr)
|
DecompiledCode CutterCore::getDecompiledCodePDC(RVA addr)
|
||||||
{
|
{
|
||||||
return cmd("pdc @ " + QString::number(addr));
|
DecompiledCode code;
|
||||||
|
auto lines = cmd("pdc @ " + QString::number(addr)).split('\n');
|
||||||
|
code.lines.reserve(lines.size());
|
||||||
|
for (const auto &line : lines) {
|
||||||
|
code.lines.append(DecompiledCode::Line(line));
|
||||||
|
}
|
||||||
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CutterCore::getR2DecAvailable()
|
bool CutterCore::getR2DecAvailable()
|
||||||
@ -941,9 +947,48 @@ bool CutterCore::getR2DecAvailable()
|
|||||||
return cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd"));
|
return cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CutterCore::getDecompiledCodeR2Dec(RVA addr)
|
DecompiledCode CutterCore::getDecompiledCodeR2Dec(RVA addr)
|
||||||
{
|
{
|
||||||
return cmd("pdd @ " + QString::number(addr));
|
DecompiledCode code;
|
||||||
|
QString s;
|
||||||
|
|
||||||
|
QJsonObject json = cmdj("pddj @ " + QString::number(addr)).object();
|
||||||
|
if (json.isEmpty()) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &line : json["log"].toArray()) {
|
||||||
|
if (!line.isString()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
code.lines.append(DecompiledCode::Line(line.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto linesArray = json["lines"].toArray();
|
||||||
|
code.lines.reserve(code.lines.size() + linesArray.size());
|
||||||
|
for (const auto &line : linesArray) {
|
||||||
|
QJsonObject lineObject = line.toObject();
|
||||||
|
if (lineObject.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DecompiledCode::Line codeLine;
|
||||||
|
codeLine.str = lineObject["str"].toString();
|
||||||
|
bool ok;
|
||||||
|
codeLine.addr = lineObject["offset"].toVariant().toULongLong(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
codeLine.addr = RVA_INVALID;
|
||||||
|
}
|
||||||
|
code.lines.append(codeLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &line : json["errors"].toArray()) {
|
||||||
|
if (!line.isString()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
code.lines.append(DecompiledCode::Line(line.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -248,9 +248,9 @@ public:
|
|||||||
QString currentlyOpenFile;
|
QString currentlyOpenFile;
|
||||||
|
|
||||||
/* Pseudocode */
|
/* Pseudocode */
|
||||||
QString getDecompiledCodePDC(RVA addr);
|
DecompiledCode getDecompiledCodePDC(RVA addr);
|
||||||
bool getR2DecAvailable();
|
bool getR2DecAvailable();
|
||||||
QString getDecompiledCodeR2Dec(RVA addr);
|
DecompiledCode getDecompiledCodeR2Dec(RVA addr);
|
||||||
|
|
||||||
RVA getOffsetJump(RVA addr);
|
RVA getOffsetJump(RVA addr);
|
||||||
QJsonDocument getFileInfo();
|
QJsonDocument getFileInfo();
|
||||||
|
@ -303,6 +303,35 @@ struct VariableDescription {
|
|||||||
QString type;
|
QString type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the result of a Decompilation Process with optional metadata
|
||||||
|
*/
|
||||||
|
struct DecompiledCode {
|
||||||
|
/**
|
||||||
|
* A single line of decompiled code
|
||||||
|
*/
|
||||||
|
struct Line {
|
||||||
|
QString str;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset of the original instruction
|
||||||
|
*/
|
||||||
|
RVA addr;
|
||||||
|
|
||||||
|
Line()
|
||||||
|
{
|
||||||
|
this->addr = RVA_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Line(const QString &str, RVA addr = RVA_INVALID)
|
||||||
|
{
|
||||||
|
this->str = str;
|
||||||
|
this->addr = addr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
QList<Line> lines = {};
|
||||||
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(FunctionDescription)
|
Q_DECLARE_METATYPE(FunctionDescription)
|
||||||
Q_DECLARE_METATYPE(ImportDescription)
|
Q_DECLARE_METATYPE(ImportDescription)
|
||||||
Q_DECLARE_METATYPE(ExportDescription)
|
Q_DECLARE_METATYPE(ExportDescription)
|
||||||
@ -340,5 +369,6 @@ Q_DECLARE_METATYPE(BreakpointDescription)
|
|||||||
Q_DECLARE_METATYPE(ProcessDescription)
|
Q_DECLARE_METATYPE(ProcessDescription)
|
||||||
Q_DECLARE_METATYPE(RegisterRefDescription)
|
Q_DECLARE_METATYPE(RegisterRefDescription)
|
||||||
Q_DECLARE_METATYPE(VariableDescription)
|
Q_DECLARE_METATYPE(VariableDescription)
|
||||||
|
Q_DECLARE_METATYPE(DecompiledCode::Line)
|
||||||
|
|
||||||
#endif // DESCRIPTIONS_H
|
#endif // DESCRIPTIONS_H
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "common/Configuration.h"
|
#include "common/Configuration.h"
|
||||||
#include "common/Helpers.h"
|
#include "common/Helpers.h"
|
||||||
#include "common/TempConfig.h"
|
#include "common/TempConfig.h"
|
||||||
|
#include "common/SelectionHighlight.h"
|
||||||
#include "core/MainWindow.h"
|
#include "core/MainWindow.h"
|
||||||
|
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
@ -376,7 +377,7 @@ void DisassemblyWidget::highlightCurrentLine()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Highlight all the words in the document same as the current one
|
// Highlight all the words in the document same as the current one
|
||||||
extraSelections.append(getSameWordsSelections());
|
extraSelections.append(createSameWordsSelections(mDisasTextEdit, curHighlightedWord));
|
||||||
|
|
||||||
// highlight PC line
|
// highlight PC line
|
||||||
RVA PCAddr = Core()->getProgramCounterValue();
|
RVA PCAddr = Core()->getProgramCounterValue();
|
||||||
@ -440,7 +441,7 @@ void DisassemblyWidget::updateCursorPosition()
|
|||||||
|
|
||||||
if (offset < topOffset || (offset > bottomOffset && bottomOffset != RVA_INVALID)) {
|
if (offset < topOffset || (offset > bottomOffset && bottomOffset != RVA_INVALID)) {
|
||||||
mDisasTextEdit->moveCursor(QTextCursor::Start);
|
mDisasTextEdit->moveCursor(QTextCursor::Start);
|
||||||
mDisasTextEdit->setExtraSelections(getSameWordsSelections());
|
mDisasTextEdit->setExtraSelections(createSameWordsSelections(mDisasTextEdit, curHighlightedWord));
|
||||||
} else {
|
} else {
|
||||||
RVA currentCursorOffset = readCurrentDisassemblyOffset();
|
RVA currentCursorOffset = readCurrentDisassemblyOffset();
|
||||||
QTextCursor originalCursor = mDisasTextEdit->textCursor();
|
QTextCursor originalCursor = mDisasTextEdit->textCursor();
|
||||||
@ -585,33 +586,6 @@ void DisassemblyWidget::moveCursorRelative(bool up, bool page)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QTextEdit::ExtraSelection> DisassemblyWidget::getSameWordsSelections()
|
|
||||||
{
|
|
||||||
QList<QTextEdit::ExtraSelection> selections;
|
|
||||||
QTextEdit::ExtraSelection highlightSelection;
|
|
||||||
QTextDocument *document = mDisasTextEdit->document();
|
|
||||||
QColor highlightWordColor = ConfigColor("wordHighlight");
|
|
||||||
|
|
||||||
if (curHighlightedWord.isNull()) {
|
|
||||||
return QList<QTextEdit::ExtraSelection>();
|
|
||||||
}
|
|
||||||
|
|
||||||
highlightSelection.cursor = mDisasTextEdit->textCursor();
|
|
||||||
highlightSelection.cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
||||||
|
|
||||||
while (!highlightSelection.cursor.isNull() && !highlightSelection.cursor.atEnd()) {
|
|
||||||
highlightSelection.cursor = document->find(curHighlightedWord, highlightSelection.cursor,
|
|
||||||
QTextDocument::FindWholeWords);
|
|
||||||
|
|
||||||
if (!highlightSelection.cursor.isNull()) {
|
|
||||||
highlightSelection.format.setBackground(highlightWordColor);
|
|
||||||
|
|
||||||
selections.append(highlightSelection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selections;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)
|
bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)
|
||||||
{
|
{
|
||||||
if (event->type() == QEvent::MouseButtonDblClick
|
if (event->type() == QEvent::MouseButtonDblClick
|
||||||
|
@ -81,7 +81,6 @@ private:
|
|||||||
void connectCursorPositionChanged(bool disconnect);
|
void connectCursorPositionChanged(bool disconnect);
|
||||||
|
|
||||||
void moveCursorRelative(bool up, bool page);
|
void moveCursorRelative(bool up, bool page);
|
||||||
QList<QTextEdit::ExtraSelection> getSameWordsSelections();
|
|
||||||
|
|
||||||
QAction syncIt;
|
QAction syncIt;
|
||||||
};
|
};
|
||||||
|
@ -4,8 +4,34 @@
|
|||||||
#include "common/Configuration.h"
|
#include "common/Configuration.h"
|
||||||
#include "common/Helpers.h"
|
#include "common/Helpers.h"
|
||||||
#include "common/TempConfig.h"
|
#include "common/TempConfig.h"
|
||||||
|
#include "common/SelectionHighlight.h"
|
||||||
|
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTextBlockUserData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single line of decompiled code as part of the displayed text,
|
||||||
|
* including the position inside the QTextDocument
|
||||||
|
*/
|
||||||
|
struct DecompiledCodeTextLine
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* position inside the QTextDocument
|
||||||
|
*/
|
||||||
|
int position;
|
||||||
|
|
||||||
|
DecompiledCode::Line line;
|
||||||
|
|
||||||
|
DecompiledCodeTextLine(int position, const DecompiledCode::Line &line)
|
||||||
|
{
|
||||||
|
this->position = position;
|
||||||
|
this->line = line;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
|
PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
|
||||||
MemoryDockWidget(CutterCore::MemoryWidgetType::Pseudocode, main, action),
|
MemoryDockWidget(CutterCore::MemoryWidgetType::Pseudocode, main, action),
|
||||||
@ -40,6 +66,9 @@ PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
|
|||||||
ui->decompilerComboBox->setCurrentIndex(DecompilerCBPdc);
|
ui->decompilerComboBox->setCurrentIndex(DecompilerCBPdc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connectCursorPositionChanged(false);
|
||||||
|
connect(Core(), &CutterCore::seekChanged, this, &PseudocodeWidget::seekChanged);
|
||||||
|
|
||||||
doRefresh(RVA_INVALID);
|
doRefresh(RVA_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,11 +78,11 @@ PseudocodeWidget::~PseudocodeWidget() = default;
|
|||||||
void PseudocodeWidget::doRefresh(RVA addr)
|
void PseudocodeWidget::doRefresh(RVA addr)
|
||||||
{
|
{
|
||||||
if (addr == RVA_INVALID) {
|
if (addr == RVA_INVALID) {
|
||||||
ui->textEdit->setText(tr("Click Refresh to generate Pseudocode from current offset."));
|
ui->textEdit->setPlainText(tr("Click Refresh to generate Pseudocode from current offset."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString decompiledCode;
|
DecompiledCode decompiledCode;
|
||||||
switch (ui->decompilerComboBox->currentIndex()) {
|
switch (ui->decompilerComboBox->currentIndex()) {
|
||||||
case DecompilerCBR2Dec:
|
case DecompilerCBR2Dec:
|
||||||
if (Core()->getR2DecAvailable()) {
|
if (Core()->getR2DecAvailable()) {
|
||||||
@ -66,12 +95,25 @@ void PseudocodeWidget::doRefresh(RVA addr)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decompiledCode.length() == 0) {
|
textLines = {};
|
||||||
ui->textEdit->setText(tr("Cannot decompile at") + " " + RAddressString(
|
textLines.reserve(decompiledCode.lines.size());
|
||||||
|
|
||||||
|
if (decompiledCode.lines.isEmpty()) {
|
||||||
|
ui->textEdit->setPlainText(tr("Cannot decompile at") + " " + RAddressString(
|
||||||
addr) + " " + tr("(Not a function?)"));
|
addr) + " " + tr("(Not a function?)"));
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
connectCursorPositionChanged(true);
|
||||||
|
ui->textEdit->document()->clear();
|
||||||
|
QTextCursor cursor(ui->textEdit->document());
|
||||||
|
for (const DecompiledCode::Line &line : decompiledCode.lines) {
|
||||||
|
textLines.append(DecompiledCodeTextLine(cursor.position(), line));
|
||||||
|
// Can't use cursor.block()->setUserData() here, because the Syntax Highlighter will mess it up.
|
||||||
|
cursor.insertText(line.str + "\n");
|
||||||
|
}
|
||||||
|
connectCursorPositionChanged(false);
|
||||||
|
seekChanged();
|
||||||
}
|
}
|
||||||
ui->textEdit->setText(decompiledCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PseudocodeWidget::refreshPseudocode()
|
void PseudocodeWidget::refreshPseudocode()
|
||||||
@ -79,12 +121,127 @@ void PseudocodeWidget::refreshPseudocode()
|
|||||||
doRefresh(Core()->getOffset());
|
doRefresh(Core()->getOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PseudocodeWidget::connectCursorPositionChanged(bool disconnect)
|
||||||
|
{
|
||||||
|
if (disconnect) {
|
||||||
|
QObject::disconnect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this, &PseudocodeWidget::cursorPositionChanged);
|
||||||
|
} else {
|
||||||
|
connect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this, &PseudocodeWidget::cursorPositionChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PseudocodeWidget::cursorPositionChanged()
|
||||||
|
{
|
||||||
|
RVA offset = getOffsetAtLine(ui->textEdit->textCursor());
|
||||||
|
if (offset != RVA_INVALID && offset != Core()->getOffset()) {
|
||||||
|
seekFromCursor = true;
|
||||||
|
Core()->seek(offset);
|
||||||
|
seekFromCursor = false;
|
||||||
|
}
|
||||||
|
updateSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PseudocodeWidget::seekChanged()
|
||||||
|
{
|
||||||
|
if (seekFromCursor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateCursorPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PseudocodeWidget::updateCursorPosition()
|
||||||
|
{
|
||||||
|
RVA offset = Core()->getOffset();
|
||||||
|
connectCursorPositionChanged(true);
|
||||||
|
|
||||||
|
auto it = findLineByOffset(offset);
|
||||||
|
if (it != textLines.end()) {
|
||||||
|
// move back if the offset is identical (so we don't land on closing braces for example)
|
||||||
|
while (it != textLines.begin()) {
|
||||||
|
auto prev = it - 1;
|
||||||
|
if (prev->line.addr != it->line.addr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
it = prev;
|
||||||
|
}
|
||||||
|
QTextCursor cursor = ui->textEdit->textCursor();
|
||||||
|
cursor.setPosition((*it).position);
|
||||||
|
ui->textEdit->setTextCursor(cursor);
|
||||||
|
updateSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectCursorPositionChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<DecompiledCodeTextLine>::iterator PseudocodeWidget::findLine(int position)
|
||||||
|
{
|
||||||
|
return std::upper_bound(textLines.begin(), textLines.end(), position,
|
||||||
|
[](int pos, const DecompiledCodeTextLine &line) {
|
||||||
|
return pos < line.position;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<DecompiledCodeTextLine>::iterator PseudocodeWidget::findLineByOffset(RVA offset)
|
||||||
|
{
|
||||||
|
auto it = textLines.begin();
|
||||||
|
auto candidate = it;
|
||||||
|
for (; it != textLines.end(); it++) {
|
||||||
|
RVA lineOffset = it->line.addr;
|
||||||
|
if (lineOffset != RVA_INVALID && lineOffset > offset) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (candidate->line.addr == RVA_INVALID || (lineOffset != RVA_INVALID && lineOffset > candidate->line.addr)) {
|
||||||
|
candidate = it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
RVA PseudocodeWidget::getOffsetAtLine(const QTextCursor &tc)
|
||||||
|
{
|
||||||
|
auto it = findLine(tc.position());
|
||||||
|
if (it == textLines.begin()) {
|
||||||
|
return RVA_INVALID;
|
||||||
|
}
|
||||||
|
it--;
|
||||||
|
return (*it).line.addr;
|
||||||
|
}
|
||||||
|
|
||||||
void PseudocodeWidget::setupFonts()
|
void PseudocodeWidget::setupFonts()
|
||||||
{
|
{
|
||||||
QFont font = Config()->getFont();
|
QFont font = Config()->getFont();
|
||||||
ui->textEdit->setFont(font);
|
ui->textEdit->setFont(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PseudocodeWidget::updateSelection()
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||||
|
|
||||||
|
// Highlight the current line
|
||||||
|
auto cursor = ui->textEdit->textCursor();
|
||||||
|
|
||||||
|
RVA cursorOffset = getOffsetAtLine(cursor);
|
||||||
|
if (cursorOffset != RVA_INVALID) {
|
||||||
|
for (auto it = findLineByOffset(cursorOffset);
|
||||||
|
it != textLines.end() && it->line.addr != RVA_INVALID && it->line.addr <= cursorOffset;
|
||||||
|
it++) {
|
||||||
|
auto lineCursor = cursor;
|
||||||
|
lineCursor.setPosition(it->position);
|
||||||
|
extraSelections.append(createLineHighlightSelection(lineCursor));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the cursor position has no valid offset, just highlight the line
|
||||||
|
extraSelections.append(createLineHighlightSelection(cursor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight all the words in the document same as the current one
|
||||||
|
cursor.select(QTextCursor::WordUnderCursor);
|
||||||
|
QString searchString = cursor.selectedText();
|
||||||
|
extraSelections.append(createSameWordsSelections(ui->textEdit, searchString));
|
||||||
|
|
||||||
|
ui->textEdit->setExtraSelections(extraSelections);
|
||||||
|
}
|
||||||
|
|
||||||
QString PseudocodeWidget::getWindowTitle() const
|
QString PseudocodeWidget::getWindowTitle() const
|
||||||
{
|
{
|
||||||
return tr("Pseudocode");
|
return tr("Pseudocode");
|
||||||
|
@ -12,6 +12,8 @@ class PseudocodeWidget;
|
|||||||
|
|
||||||
class QTextEdit;
|
class QTextEdit;
|
||||||
class QSyntaxHighlighter;
|
class QSyntaxHighlighter;
|
||||||
|
class QTextCursor;
|
||||||
|
struct DecompiledCodeTextLine;
|
||||||
|
|
||||||
class PseudocodeWidget : public MemoryDockWidget
|
class PseudocodeWidget : public MemoryDockWidget
|
||||||
{
|
{
|
||||||
@ -25,6 +27,8 @@ private slots:
|
|||||||
void fontsUpdated();
|
void fontsUpdated();
|
||||||
void colorsUpdatedSlot();
|
void colorsUpdatedSlot();
|
||||||
void refreshPseudocode();
|
void refreshPseudocode();
|
||||||
|
void cursorPositionChanged();
|
||||||
|
void seekChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum DecompilerComboBoxValues { DecompilerCBR2Dec, DecompilerCBPdc };
|
enum DecompilerComboBoxValues { DecompilerCBR2Dec, DecompilerCBPdc };
|
||||||
@ -32,8 +36,29 @@ private:
|
|||||||
|
|
||||||
QSyntaxHighlighter *syntaxHighlighter;
|
QSyntaxHighlighter *syntaxHighlighter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of all lines that are currently displayed, ordered by the position in the text
|
||||||
|
*/
|
||||||
|
QList<DecompiledCodeTextLine> textLines;
|
||||||
|
|
||||||
|
bool seekFromCursor = false;
|
||||||
|
|
||||||
void doRefresh(RVA addr);
|
void doRefresh(RVA addr);
|
||||||
void setupFonts();
|
void setupFonts();
|
||||||
|
void updateSelection();
|
||||||
|
void connectCursorPositionChanged(bool disconnect);
|
||||||
|
void updateCursorPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Iterator to the first line that is after position or last if not found.
|
||||||
|
*/
|
||||||
|
QList<DecompiledCodeTextLine>::iterator findLine(int position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Iterator to the first line that is considered to contain offset
|
||||||
|
*/
|
||||||
|
QList<DecompiledCodeTextLine>::iterator findLineByOffset(RVA offset);
|
||||||
|
RVA getOffsetAtLine(const QTextCursor &tc);
|
||||||
|
|
||||||
QString getWindowTitle() const override;
|
QString getWindowTitle() const override;
|
||||||
};
|
};
|
||||||
|
@ -31,9 +31,9 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTextEdit" name="textEdit">
|
<widget class="QPlainTextEdit" name="textEdit">
|
||||||
<property name="lineWrapMode">
|
<property name="lineWrapMode">
|
||||||
<enum>QTextEdit::NoWrap</enum>
|
<enum>QPlainTextEdit::NoWrap</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="readOnly">
|
<property name="readOnly">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
Loading…
Reference in New Issue
Block a user