mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-31 08:37:26 +00:00
Multiple graph/disasm/hexdump widgets and independent seeks (#504)
This commit is contained in:
parent
f2dab2d198
commit
0cea9e3287
@ -55,7 +55,7 @@ CutterCore::CutterCore(QObject *parent) :
|
||||
|
||||
// Otherwise r2 may ask the user for input and Cutter would freeze
|
||||
setConfig("scr.interactive", false);
|
||||
|
||||
|
||||
#if defined(APPIMAGE) || defined(MACOS_R2_BUNDLED)
|
||||
auto prefix = QDir(QCoreApplication::applicationDirPath());
|
||||
# ifdef APPIMAGE
|
||||
|
@ -155,6 +155,7 @@ SOURCES += \
|
||||
utils/NestedIPyKernel.cpp \
|
||||
dialogs/R2PluginsDialog.cpp \
|
||||
widgets/CutterDockWidget.cpp \
|
||||
widgets/CutterSeekableWidget.cpp \
|
||||
widgets/GraphWidget.cpp \
|
||||
utils/JsonTreeItem.cpp \
|
||||
utils/JsonModel.cpp \
|
||||
@ -229,6 +230,7 @@ HEADERS += \
|
||||
utils/NestedIPyKernel.h \
|
||||
dialogs/R2PluginsDialog.h \
|
||||
widgets/CutterDockWidget.h \
|
||||
widgets/CutterSeekableWidget.h \
|
||||
widgets/GraphWidget.h \
|
||||
utils/JsonTreeItem.h \
|
||||
utils/JsonModel.h \
|
||||
|
@ -223,6 +223,32 @@ void MainWindow::initUI()
|
||||
connect(core, SIGNAL(projectSaved(const QString &)), this, SLOT(projectSaved(const QString &)));
|
||||
}
|
||||
|
||||
void MainWindow::on_actionExtraGraph_triggered()
|
||||
{
|
||||
QDockWidget *extraDock = new GraphWidget(this, 0);
|
||||
addExtraWidget(extraDock);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionExtraHexdump_triggered()
|
||||
{
|
||||
QDockWidget *extraDock = new HexdumpWidget(this, 0);
|
||||
addExtraWidget(extraDock);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionExtraDisassembly_triggered()
|
||||
{
|
||||
QDockWidget *extraDock = new DisassemblyWidget(this, 0);
|
||||
addExtraWidget(extraDock);
|
||||
}
|
||||
|
||||
void MainWindow::addExtraWidget(QDockWidget *extraDock)
|
||||
{
|
||||
addDockWidget(Qt::TopDockWidgetArea, extraDock);
|
||||
auto restoreExtraDock = qhelpers::forceWidth(extraDock->widget(), 600);
|
||||
qApp->processEvents();
|
||||
restoreExtraDock.restoreWidth(extraDock->widget());
|
||||
}
|
||||
|
||||
void MainWindow::openNewFile(const QString &fn, int analLevel, QList<QString> advancedOptions)
|
||||
{
|
||||
setFilename(fn);
|
||||
@ -525,6 +551,23 @@ void MainWindow::showDefaultDocks()
|
||||
updateDockActionsChecked();
|
||||
}
|
||||
|
||||
void MainWindow::showZenDocks()
|
||||
{
|
||||
const QList<QDockWidget *> zenDocks = { functionsDock,
|
||||
stringsDock,
|
||||
graphDock,
|
||||
disassemblyDock,
|
||||
hexdumpDock,
|
||||
searchDock
|
||||
};
|
||||
for (auto w : dockWidgets) {
|
||||
if (zenDocks.contains(w)) {
|
||||
w->show();
|
||||
}
|
||||
}
|
||||
updateDockActionsChecked();
|
||||
}
|
||||
|
||||
void MainWindow::resetToDefaultLayout()
|
||||
{
|
||||
hideAllDocks();
|
||||
@ -545,6 +588,24 @@ void MainWindow::resetToDefaultLayout()
|
||||
Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Disassembly);
|
||||
}
|
||||
|
||||
void MainWindow::resetToZenLayout()
|
||||
{
|
||||
hideAllDocks();
|
||||
restoreDocks();
|
||||
showZenDocks();
|
||||
disassemblyDock->raise();
|
||||
|
||||
// ugly workaround to set the default widths of functions
|
||||
// if anyone finds a way to do this cleaner that also works, feel free to change it!
|
||||
auto restoreFunctionDock = qhelpers::forceWidth(functionsDock->widget(), 200);
|
||||
|
||||
qApp->processEvents();
|
||||
|
||||
restoreFunctionDock.restoreWidth(functionsDock->widget());
|
||||
|
||||
Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Disassembly);
|
||||
}
|
||||
|
||||
void MainWindow::addOutput(const QString &msg)
|
||||
{
|
||||
consoleDock->addOutput(msg);
|
||||
@ -590,6 +651,11 @@ void MainWindow::on_actionDefault_triggered()
|
||||
resetToDefaultLayout();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionZen_triggered()
|
||||
{
|
||||
resetToZenLayout();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionNew_triggered()
|
||||
{
|
||||
on_actionOpen_triggered();
|
||||
@ -761,7 +827,6 @@ bool MainWindow::eventFilter(QObject *, QEvent *event)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,8 @@ public:
|
||||
|
||||
void addToDockWidgetList(QDockWidget *dockWidget);
|
||||
void addDockWidgetAction(QDockWidget *dockWidget, QAction *action);
|
||||
void addExtraWidget(QDockWidget *extraDock);
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
@ -119,12 +121,16 @@ public slots:
|
||||
|
||||
private slots:
|
||||
void on_actionAbout_triggered();
|
||||
void on_actionExtraGraph_triggered();
|
||||
void on_actionExtraHexdump_triggered();
|
||||
void on_actionExtraDisassembly_triggered();
|
||||
|
||||
void on_actionRefresh_Panels_triggered();
|
||||
|
||||
void on_actionDisasAdd_comment_triggered();
|
||||
|
||||
void on_actionDefault_triggered();
|
||||
void on_actionZen_triggered();
|
||||
|
||||
void on_actionFunctionsRename_triggered();
|
||||
|
||||
@ -211,10 +217,12 @@ private:
|
||||
#endif
|
||||
|
||||
void resetToDefaultLayout();
|
||||
void resetToZenLayout();
|
||||
|
||||
void restoreDocks();
|
||||
void hideAllDocks();
|
||||
void showDefaultDocks();
|
||||
void showZenDocks();
|
||||
void updateDockActionsChecked();
|
||||
|
||||
void toggleDockWidget(QDockWidget *dock_widget, bool show);
|
||||
|
@ -201,6 +201,7 @@ border-top: 0px;
|
||||
<addaction name="actionRefresh_contents"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionDefault"/>
|
||||
<addaction name="actionZen"/>
|
||||
<addaction name="actionReset_settings"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLock"/>
|
||||
@ -256,6 +257,8 @@ border-top: 0px;
|
||||
<addaction name="actionComments"/>
|
||||
<addaction name="actionConsole"/>
|
||||
<addaction name="actionJupyter"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="addExtraWidgets"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuEdit"/>
|
||||
@ -322,6 +325,14 @@ background-color: palette(dark);
|
||||
<string>Reset layout</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZen">
|
||||
<property name="text">
|
||||
<string>Zen Mode</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Zen mode</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>About</string>
|
||||
@ -1093,6 +1104,29 @@ background-color: palette(dark);
|
||||
<string>Jupyter</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExtraGraph">
|
||||
<property name="text">
|
||||
<string>Graph view</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExtraHexdump">
|
||||
<property name="text">
|
||||
<string>Hexdump view</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExtraDisassembly">
|
||||
<property name="text">
|
||||
<string>Disassembly view</string>
|
||||
</property>
|
||||
</action>
|
||||
<widget class="QMenu" name="addExtraWidgets">
|
||||
<property name="title">
|
||||
<string>Add extra...</string>
|
||||
</property>
|
||||
<addaction name="actionExtraGraph"/>
|
||||
<addaction name="actionExtraHexdump"/>
|
||||
<addaction name="actionExtraDisassembly"/>
|
||||
</widget>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
@ -6,8 +6,8 @@ CutterDockWidget::CutterDockWidget(MainWindow *main, QAction *action) :
|
||||
QDockWidget(main),
|
||||
action(action)
|
||||
{
|
||||
main->addToDockWidgetList(this);
|
||||
if (action) {
|
||||
main->addToDockWidgetList(this);
|
||||
main->addDockWidgetAction(this, action);
|
||||
connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget);
|
||||
}
|
||||
|
67
src/widgets/CutterSeekableWidget.cpp
Normal file
67
src/widgets/CutterSeekableWidget.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "MainWindow.h"
|
||||
#include "CutterSeekableWidget.h"
|
||||
|
||||
CutterSeekableWidget::CutterSeekableWidget(QObject *parent)
|
||||
:
|
||||
QObject(parent)
|
||||
{
|
||||
connect(Core(), &CutterCore::seekChanged, this, &CutterSeekableWidget::onSeekChanged);
|
||||
}
|
||||
|
||||
void CutterSeekableWidget::onSeekChanged(RVA addr)
|
||||
{
|
||||
if (isInSyncWithCore) {
|
||||
emit seekChanged(addr);
|
||||
}
|
||||
}
|
||||
|
||||
void CutterSeekableWidget::seek(RVA addr)
|
||||
{
|
||||
if (isInSyncWithCore) {
|
||||
Core()->seek(addr);
|
||||
}
|
||||
else {
|
||||
prevIdenpendentOffset = independentOffset;
|
||||
independentOffset = addr;
|
||||
emit seekChanged(addr);
|
||||
}
|
||||
}
|
||||
|
||||
RVA CutterSeekableWidget::getOffset()
|
||||
{
|
||||
RVA addr;
|
||||
if (isInSyncWithCore) {
|
||||
addr = Core()->getOffset();
|
||||
}
|
||||
else {
|
||||
addr = independentOffset;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
void CutterSeekableWidget::toggleSyncWithCore()
|
||||
{
|
||||
isInSyncWithCore = !isInSyncWithCore;
|
||||
}
|
||||
|
||||
RVA CutterSeekableWidget::getIndependentOffset()
|
||||
{
|
||||
return independentOffset;
|
||||
}
|
||||
|
||||
RVA CutterSeekableWidget::getPrevIndependentOffset()
|
||||
{
|
||||
return prevIdenpendentOffset;
|
||||
}
|
||||
|
||||
bool CutterSeekableWidget::getSyncWithCore()
|
||||
{
|
||||
return isInSyncWithCore;
|
||||
}
|
||||
|
||||
void CutterSeekableWidget::setIndependentOffset(RVA addr)
|
||||
{
|
||||
independentOffset = addr;
|
||||
}
|
||||
|
||||
CutterSeekableWidget::~CutterSeekableWidget() {}
|
32
src/widgets/CutterSeekableWidget.h
Normal file
32
src/widgets/CutterSeekableWidget.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cutter.h"
|
||||
|
||||
class MainWindow;
|
||||
|
||||
class CutterSeekableWidget : public QObject
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CutterSeekableWidget(QObject *parent = nullptr);
|
||||
~CutterSeekableWidget();
|
||||
void seek(RVA addr);
|
||||
void toggleSyncWithCore();
|
||||
RVA getOffset();
|
||||
RVA getIndependentOffset();
|
||||
RVA getPrevIndependentOffset();
|
||||
bool getSyncWithCore();
|
||||
void setIndependentOffset(RVA addr);
|
||||
void onSeekChanged(RVA addr);
|
||||
|
||||
private:
|
||||
RVA independentOffset = RVA_INVALID;
|
||||
RVA prevIdenpendentOffset = RVA_INVALID;
|
||||
bool isInSyncWithCore = true;
|
||||
|
||||
signals:
|
||||
void seekChanged(RVA addr);
|
||||
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
#include "DisassemblerGraphView.h"
|
||||
|
||||
#include "CutterSeekableWidget.h"
|
||||
#include <QPainter>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
@ -20,7 +20,8 @@
|
||||
DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
|
||||
: GraphView(parent),
|
||||
mFontMetrics(nullptr),
|
||||
mMenu(new DisassemblyContextMenu(this))
|
||||
mMenu(new DisassemblyContextMenu(this)),
|
||||
seekable(new CutterSeekableWidget(this))
|
||||
{
|
||||
highlight_token = nullptr;
|
||||
// Signals that require a refresh all
|
||||
@ -99,16 +100,21 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
|
||||
mMenu->addAction(&actionExportGraph);
|
||||
connect(&actionExportGraph, SIGNAL(triggered(bool)), this, SLOT(on_actionExportGraph_triggered()));
|
||||
|
||||
mMenu->addSeparator();
|
||||
actionSyncOffset.setText(tr("Sync/unsync offset"));
|
||||
mMenu->addAction(&actionSyncOffset);
|
||||
|
||||
connect(&actionSyncOffset, SIGNAL(triggered(bool)), this, SLOT(toggleSync()));
|
||||
initFont();
|
||||
colorsUpdatedSlot();
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::connectSeekChanged(bool disconnect)
|
||||
void DisassemblerGraphView::connectSeekChanged(bool disconn)
|
||||
{
|
||||
if (disconnect) {
|
||||
QObject::disconnect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(onSeekChanged(RVA)));
|
||||
if (disconn) {
|
||||
disconnect(seekable, &CutterSeekableWidget::seekChanged, this, &DisassemblerGraphView::onSeekChanged);
|
||||
} else {
|
||||
connect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(onSeekChanged(RVA)));
|
||||
connect(seekable, &CutterSeekableWidget::seekChanged, this, &DisassemblerGraphView::onSeekChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +125,17 @@ DisassemblerGraphView::~DisassemblerGraphView()
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::toggleSync()
|
||||
{
|
||||
seekable->toggleSyncWithCore();
|
||||
if (seekable->getSyncWithCore()) {
|
||||
parentWidget()->setWindowTitle(windowTitle);
|
||||
} else {
|
||||
parentWidget()->setWindowTitle(windowTitle + " (not synced)");
|
||||
seekable->setIndependentOffset(Core()->getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::refreshView()
|
||||
{
|
||||
initFont();
|
||||
@ -134,7 +151,7 @@ void DisassemblerGraphView::loadCurrentGraph()
|
||||
.set("asm.bbline", false)
|
||||
.set("asm.lines", false)
|
||||
.set("asm.lines.fcn", false);
|
||||
QJsonDocument functionsDoc = Core()->cmdj("agJ");
|
||||
QJsonDocument functionsDoc = Core()->cmdj("agJ " + RAddressString(seekable->getOffset()));
|
||||
QJsonArray functions = functionsDoc.array();
|
||||
|
||||
disassembly_blocks.clear();
|
||||
@ -149,12 +166,16 @@ void DisassemblerGraphView::loadCurrentGraph()
|
||||
f.ready = true;
|
||||
f.entry = func["offset"].toVariant().toULongLong();
|
||||
|
||||
QString windowTitle = tr("Graph");
|
||||
windowTitle = tr("Graph");
|
||||
QString funcName = func["name"].toString().trimmed();
|
||||
if (!funcName.isEmpty()) {
|
||||
windowTitle += " (" + funcName + ")";
|
||||
}
|
||||
parentWidget()->setWindowTitle(windowTitle);
|
||||
if (!seekable->getSyncWithCore()) {
|
||||
parentWidget()->setWindowTitle(windowTitle + " (not synced)");
|
||||
} else {
|
||||
parentWidget()->setWindowTitle(windowTitle);
|
||||
}
|
||||
|
||||
RVA entry = func["offset"].toVariant().toULongLong();
|
||||
|
||||
@ -297,7 +318,7 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
|
||||
|
||||
// Figure out if the current block is selected
|
||||
for (const Instr &instr : db.instrs) {
|
||||
RVA addr = Core()->getOffset();
|
||||
RVA addr = seekable->getOffset();
|
||||
if ((instr.addr <= addr) && (addr <= instr.addr + instr.size)) {
|
||||
block_selected = true;
|
||||
selected_instruction = instr.addr;
|
||||
@ -543,27 +564,27 @@ void DisassemblerGraphView::zoomReset()
|
||||
|
||||
void DisassemblerGraphView::takeTrue()
|
||||
{
|
||||
DisassemblyBlock *db = blockForAddress(Core()->getOffset());
|
||||
DisassemblyBlock *db = blockForAddress(seekable->getOffset());
|
||||
if (db->true_path != RVA_INVALID) {
|
||||
Core()->seek(db->true_path);
|
||||
seekable->seek(db->true_path);
|
||||
} else if (blocks[db->entry].exits.size()) {
|
||||
Core()->seek(blocks[db->entry].exits[0]);
|
||||
seekable->seek(blocks[db->entry].exits[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::takeFalse()
|
||||
{
|
||||
DisassemblyBlock *db = blockForAddress(Core()->getOffset());
|
||||
DisassemblyBlock *db = blockForAddress(seekable->getOffset());
|
||||
if (db->false_path != RVA_INVALID) {
|
||||
Core()->seek(db->false_path);
|
||||
seekable->seek(db->false_path);
|
||||
} else if (blocks[db->entry].exits.size()) {
|
||||
Core()->seek(blocks[db->entry].exits[0]);
|
||||
seekable->seek(blocks[db->entry].exits[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::seekInstruction(bool previous_instr)
|
||||
{
|
||||
RVA addr = Core()->getOffset();
|
||||
RVA addr = seekable->getOffset();
|
||||
DisassemblyBlock *db = blockForAddress(addr);
|
||||
if (!db) {
|
||||
return;
|
||||
@ -577,9 +598,9 @@ void DisassemblerGraphView::seekInstruction(bool previous_instr)
|
||||
|
||||
// Found the instructon. Check if a next one exists
|
||||
if (!previous_instr && (i < db->instrs.size() - 1)) {
|
||||
seek(db->instrs[i + 1].addr, true);
|
||||
seekable->seek(db->instrs[i + 1].addr);
|
||||
} else if (previous_instr && (i > 0)) {
|
||||
seek(db->instrs[i - 1].addr);
|
||||
seekable->seek(db->instrs[i - 1].addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -594,10 +615,10 @@ void DisassemblerGraphView::prevInstr()
|
||||
seekInstruction(true);
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::seek(RVA addr, bool update_viewport)
|
||||
void DisassemblerGraphView::seekLocal(RVA addr, bool update_viewport)
|
||||
{
|
||||
connectSeekChanged(true);
|
||||
Core()->seek(addr);
|
||||
seekable->seek(addr);
|
||||
connectSeekChanged(false);
|
||||
if (update_viewport) {
|
||||
viewport()->update();
|
||||
@ -606,7 +627,12 @@ void DisassemblerGraphView::seek(RVA addr, bool update_viewport)
|
||||
|
||||
void DisassemblerGraphView::seekPrev()
|
||||
{
|
||||
Core()->seekPrev();
|
||||
if (seekable->getSyncWithCore()) {
|
||||
Core()->seekPrev();
|
||||
}
|
||||
else {
|
||||
seekable->seek(seekable->getPrevIndependentOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event,
|
||||
@ -617,7 +643,7 @@ void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEve
|
||||
return;
|
||||
}
|
||||
|
||||
seek(instr, true);
|
||||
seekLocal(instr);
|
||||
|
||||
if (event->button() == Qt::RightButton) {
|
||||
mMenu->setOffset(instr);
|
||||
@ -629,13 +655,14 @@ void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMo
|
||||
QPoint pos)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
RVA instr = getAddrForMouseEvent(block, &pos);
|
||||
if (instr == RVA_INVALID) {
|
||||
return;
|
||||
}
|
||||
QList<XrefDescription> refs = Core()->getXRefs(instr, false, false);
|
||||
if (refs.length()) {
|
||||
Core()->seek(refs.at(0).to);
|
||||
seekable->seek(refs.at(0).to);
|
||||
}
|
||||
if (refs.length() > 1) {
|
||||
qWarning() << "Too many references here. Weird behaviour expected.";
|
||||
@ -671,7 +698,7 @@ void DisassemblerGraphView::blockTransitionedTo(GraphView::GraphBlock *to)
|
||||
transition_dont_seek = false;
|
||||
return;
|
||||
}
|
||||
seek(to->entry);
|
||||
seekLocal(to->entry);
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "widgets/GraphView.h"
|
||||
#include "menus/DisassemblyContextMenu.h"
|
||||
#include "utils/RichTextPainter.h"
|
||||
#include "CutterSeekableWidget.h"
|
||||
|
||||
class DisassemblerGraphView : public GraphView
|
||||
{
|
||||
@ -142,6 +143,7 @@ public:
|
||||
virtual void blockTransitionedTo(GraphView::GraphBlock *to) override;
|
||||
|
||||
void loadCurrentGraph();
|
||||
QString windowTitle;
|
||||
// bool navigate(ut64 addr);
|
||||
|
||||
public slots:
|
||||
@ -149,6 +151,7 @@ public slots:
|
||||
void colorsUpdatedSlot();
|
||||
void fontsUpdatedSlot();
|
||||
void onSeekChanged(RVA addr);
|
||||
void toggleSync();
|
||||
|
||||
void zoomIn(QPoint mouse = QPoint(0, 0));
|
||||
void zoomOut(QPoint mouse = QPoint(0, 0));
|
||||
@ -189,9 +192,9 @@ private:
|
||||
RVA getAddrForMouseEvent(GraphBlock &block, QPoint *point);
|
||||
Instr *getInstrForMouseEvent(GraphBlock &block, QPoint *point);
|
||||
DisassemblyBlock *blockForAddress(RVA addr);
|
||||
void seek(RVA addr, bool update_viewport = true);
|
||||
void seekLocal(RVA addr, bool update_viewport = true);
|
||||
void seekInstruction(bool previous_instr);
|
||||
|
||||
CutterSeekableWidget *seekable = nullptr;
|
||||
QList<QShortcut *> shortcuts;
|
||||
|
||||
QColor disassemblyBackgroundColor;
|
||||
@ -218,6 +221,7 @@ private:
|
||||
QColor mDisabledBreakpointColor;
|
||||
|
||||
QAction actionExportGraph;
|
||||
QAction actionSyncOffset;
|
||||
};
|
||||
|
||||
#endif // DISASSEMBLERGRAPHVIEW_H
|
||||
|
@ -41,6 +41,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
|
||||
, mCtxMenu(new DisassemblyContextMenu(this))
|
||||
, mDisasScrollArea(new DisassemblyScrollArea(this))
|
||||
, mDisasTextEdit(new DisassemblyTextEdit(this))
|
||||
, seekable(new CutterSeekableWidget(this))
|
||||
{
|
||||
topOffset = bottomOffset = RVA_INVALID;
|
||||
cursorLineOffset = 0;
|
||||
@ -104,7 +105,6 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
|
||||
}
|
||||
});
|
||||
|
||||
connect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA)));
|
||||
connect(Core(), SIGNAL(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)), this,
|
||||
SLOT(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)));
|
||||
connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshDisasm()));
|
||||
@ -140,6 +140,11 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
|
||||
shortcut_escape->setContext(Qt::WidgetShortcut);
|
||||
connect(shortcut_escape, SIGNAL(activated()), this, SLOT(seekPrev()));
|
||||
|
||||
mCtxMenu->addSeparator();
|
||||
syncIt.setText(tr("Sync/unsync offset"));
|
||||
mCtxMenu->addAction(&syncIt);
|
||||
connect(&syncIt, SIGNAL(triggered(bool)), this, SLOT(toggleSync()));
|
||||
connect(seekable, &CutterSeekableWidget::seekChanged, this, &DisassemblyWidget::on_seekChanged);
|
||||
|
||||
#define ADD_SHORTCUT(ksq, slot) { \
|
||||
QShortcut *s = new QShortcut((ksq), this); \
|
||||
@ -167,6 +172,18 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
|
||||
#undef ADD_SHORTCUT
|
||||
}
|
||||
|
||||
void DisassemblyWidget::toggleSync()
|
||||
{
|
||||
QString windowTitle = tr("Disassembly");
|
||||
seekable->toggleSyncWithCore();
|
||||
if (seekable->getSyncWithCore()) {
|
||||
setWindowTitle(windowTitle);
|
||||
} else {
|
||||
setWindowTitle(windowTitle + " (not synced)");
|
||||
seekable->setIndependentOffset(Core()->getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *DisassemblyWidget::getTextWidget()
|
||||
{
|
||||
return mDisasTextEdit;
|
||||
@ -354,7 +371,7 @@ RVA DisassemblyWidget::readDisassemblyOffset(QTextCursor tc)
|
||||
|
||||
void DisassemblyWidget::updateCursorPosition()
|
||||
{
|
||||
RVA offset = Core()->getOffset();
|
||||
RVA offset = seekable->getOffset();
|
||||
|
||||
// already fine where it is?
|
||||
RVA currentLineOffset = readCurrentDisassemblyOffset();
|
||||
@ -432,7 +449,7 @@ void DisassemblyWidget::cursorPositionChanged()
|
||||
}
|
||||
|
||||
seekFromCursor = true;
|
||||
Core()->seek(offset);
|
||||
seekable->seek(offset);
|
||||
seekFromCursor = false;
|
||||
highlightCurrentLine();
|
||||
mCtxMenu->setCanCopy(mDisasTextEdit->textCursor().hasSelection());
|
||||
@ -492,8 +509,8 @@ void DisassemblyWidget::moveCursorRelative(bool up, bool page)
|
||||
|
||||
// handle cases where top instruction offsets change
|
||||
RVA offset = readCurrentDisassemblyOffset();
|
||||
if (offset != Core()->getOffset()) {
|
||||
Core()->seek(offset);
|
||||
if (offset != seekable->getOffset()) {
|
||||
seekable->seek(offset);
|
||||
highlightCurrentLine();
|
||||
}
|
||||
}
|
||||
@ -520,7 +537,7 @@ bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)
|
||||
}
|
||||
|
||||
if (jump != RVA_INVALID) {
|
||||
Core()->seek(jump);
|
||||
seekable->seek(jump);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -3,9 +3,11 @@
|
||||
|
||||
#include "Cutter.h"
|
||||
#include "CutterDockWidget.h"
|
||||
#include "CutterSeekableWidget.h"
|
||||
#include <QTextEdit>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QShortcut>
|
||||
#include <QAction>
|
||||
|
||||
|
||||
class DisassemblyTextEdit;
|
||||
@ -26,6 +28,7 @@ public slots:
|
||||
void fontsUpdatedSlot();
|
||||
void colorsUpdatedSlot();
|
||||
void seekPrev();
|
||||
void toggleSync();
|
||||
|
||||
private slots:
|
||||
void on_seekChanged(RVA offset);
|
||||
@ -63,6 +66,8 @@ private:
|
||||
void connectCursorPositionChanged(bool disconnect);
|
||||
|
||||
void moveCursorRelative(bool up, bool page);
|
||||
QAction syncIt;
|
||||
CutterSeekableWidget *seekable;
|
||||
};
|
||||
|
||||
class DisassemblyScrollArea : public QAbstractScrollArea
|
||||
|
@ -23,9 +23,6 @@ GraphWidget::GraphWidget(MainWindow *main, QAction *action) :
|
||||
this->graphView->setFocus();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
GraphWidget::~GraphWidget() {}
|
||||
|
@ -15,7 +15,8 @@
|
||||
|
||||
HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
|
||||
CutterDockWidget(main, action),
|
||||
ui(new Ui::HexdumpWidget)
|
||||
ui(new Ui::HexdumpWidget),
|
||||
seekable(new CutterSeekableWidget(this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -44,6 +45,8 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
|
||||
colorsUpdatedSlot();
|
||||
updateHeaders();
|
||||
|
||||
this->setWindowTitle(tr("Hexdump"));
|
||||
connect(&syncAction, SIGNAL(triggered(bool)), this, SLOT(toggleSync()));
|
||||
|
||||
// Set hexdump context menu
|
||||
ui->hexHexText->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
@ -61,7 +64,6 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
|
||||
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated()));
|
||||
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));
|
||||
|
||||
connect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA)));
|
||||
connect(Core(), SIGNAL(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)), this,
|
||||
SLOT(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)));
|
||||
|
||||
@ -80,6 +82,7 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
|
||||
connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexdumpWidget::selectionChanged);
|
||||
connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, this,
|
||||
&HexdumpWidget::selectionChanged);
|
||||
connect(seekable, &CutterSeekableWidget::seekChanged, this, &HexdumpWidget::on_seekChanged);
|
||||
|
||||
format = Format::Hex;
|
||||
initParsing();
|
||||
@ -281,7 +284,7 @@ void HexdumpWidget::refresh(RVA addr)
|
||||
updateHeaders();
|
||||
|
||||
if (addr == RVA_INVALID) {
|
||||
addr = Core()->getOffset();
|
||||
addr = seekable->getOffset();
|
||||
}
|
||||
|
||||
cols = Core()->getConfigi("hex.cols");
|
||||
@ -323,7 +326,7 @@ void HexdumpWidget::refresh(RVA addr)
|
||||
QTextBlockFormat formatTmp = offsetCursor.blockFormat();
|
||||
formatTmp.setBackground(QColor(64, 129, 160));
|
||||
offsetCursor.setBlockFormat(formatTmp);
|
||||
|
||||
|
||||
updateWidths();
|
||||
|
||||
// Update other text areas scroll
|
||||
@ -452,7 +455,7 @@ void HexdumpWidget::selectionChanged()
|
||||
int pos = asciiAddressToPosition(adr);
|
||||
setTextEditPosition(ui->hexASCIIText, pos);
|
||||
sent_seek = true;
|
||||
Core()->seek(adr);
|
||||
seekable->seek(adr);
|
||||
sent_seek = false;
|
||||
connectScroll(false);
|
||||
return;
|
||||
@ -503,7 +506,7 @@ void HexdumpWidget::selectionChanged()
|
||||
targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
|
||||
ui->hexASCIIText->setTextCursor(targetTextCursor);
|
||||
sent_seek = true;
|
||||
Core()->seek(startAddress);
|
||||
seekable->seek(startAddress);
|
||||
sent_seek = false;
|
||||
} else {
|
||||
QTextCursor textCursor = ui->hexASCIIText->textCursor();
|
||||
@ -514,7 +517,7 @@ void HexdumpWidget::selectionChanged()
|
||||
setTextEditPosition(ui->hexHexText, pos);
|
||||
connectScroll(false);
|
||||
sent_seek = true;
|
||||
Core()->seek(adr);
|
||||
seekable->seek(adr);
|
||||
sent_seek = false;
|
||||
return;
|
||||
}
|
||||
@ -534,7 +537,7 @@ void HexdumpWidget::selectionChanged()
|
||||
targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
|
||||
ui->hexHexText->setTextCursor(targetTextCursor);
|
||||
sent_seek = true;
|
||||
Core()->seek(startAddress);
|
||||
seekable->seek(startAddress);
|
||||
sent_seek = false;
|
||||
}
|
||||
|
||||
@ -570,6 +573,11 @@ void HexdumpWidget::showHexdumpContextMenu(const QPoint &pt)
|
||||
QMenu *formatSubmenu = menu->addMenu(tr("Format"));
|
||||
formatSubmenu->addAction(ui->actionFormatHex);
|
||||
formatSubmenu->addAction(ui->actionFormatOctal);
|
||||
|
||||
menu->addSeparator();
|
||||
syncAction.setText(tr("Sync/unsync offset"));
|
||||
menu->addAction(&syncAction);
|
||||
|
||||
// TODO:
|
||||
// formatSubmenu->addAction(ui->actionFormatHalfWord);
|
||||
// formatSubmenu->addAction(ui->actionFormatWord);
|
||||
@ -595,6 +603,18 @@ void HexdumpWidget::showHexdumpContextMenu(const QPoint &pt)
|
||||
delete menu;
|
||||
}
|
||||
|
||||
void HexdumpWidget::toggleSync()
|
||||
{
|
||||
QString windowTitle = tr("Hexdump");
|
||||
seekable->toggleSyncWithCore();
|
||||
if (seekable->getSyncWithCore()) {
|
||||
setWindowTitle(windowTitle);
|
||||
} else {
|
||||
setWindowTitle(windowTitle + " (not synced)");
|
||||
seekable->setIndependentOffset(Core()->getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void HexdumpWidget::showHexASCIIContextMenu(const QPoint &pt)
|
||||
{
|
||||
// Set Hex ASCII popup menu
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "Cutter.h"
|
||||
#include "CutterDockWidget.h"
|
||||
#include "CutterSeekableWidget.h"
|
||||
#include "utils/Highlighter.h"
|
||||
#include "utils/HexAsciiHighlighter.h"
|
||||
#include "utils/HexHighlighter.h"
|
||||
@ -27,9 +28,7 @@ class HexdumpWidget : public CutterDockWidget
|
||||
public:
|
||||
explicit HexdumpWidget(MainWindow *main, QAction *action = nullptr);
|
||||
~HexdumpWidget();
|
||||
|
||||
Highlighter *highlighter;
|
||||
|
||||
enum Format {
|
||||
Hex,
|
||||
Octal,
|
||||
@ -50,6 +49,7 @@ public slots:
|
||||
|
||||
void zoomIn(int range = 1);
|
||||
void zoomOut(int range = 1);
|
||||
void toggleSync();
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
@ -102,6 +102,8 @@ private:
|
||||
|
||||
int bufferLines;
|
||||
int cols;
|
||||
QAction syncAction;
|
||||
CutterSeekableWidget *seekable;
|
||||
|
||||
private slots:
|
||||
void on_seekChanged(RVA addr);
|
||||
|
Loading…
Reference in New Issue
Block a user