Multiple graph/disasm/hexdump widgets and independent seeks (#504)

This commit is contained in:
fcasal 2018-05-25 15:30:59 +01:00 committed by xarkes
parent f2dab2d198
commit 0cea9e3287
15 changed files with 329 additions and 49 deletions

View File

@ -155,6 +155,7 @@ SOURCES += \
utils/NestedIPyKernel.cpp \ utils/NestedIPyKernel.cpp \
dialogs/R2PluginsDialog.cpp \ dialogs/R2PluginsDialog.cpp \
widgets/CutterDockWidget.cpp \ widgets/CutterDockWidget.cpp \
widgets/CutterSeekableWidget.cpp \
widgets/GraphWidget.cpp \ widgets/GraphWidget.cpp \
utils/JsonTreeItem.cpp \ utils/JsonTreeItem.cpp \
utils/JsonModel.cpp \ utils/JsonModel.cpp \
@ -229,6 +230,7 @@ HEADERS += \
utils/NestedIPyKernel.h \ utils/NestedIPyKernel.h \
dialogs/R2PluginsDialog.h \ dialogs/R2PluginsDialog.h \
widgets/CutterDockWidget.h \ widgets/CutterDockWidget.h \
widgets/CutterSeekableWidget.h \
widgets/GraphWidget.h \ widgets/GraphWidget.h \
utils/JsonTreeItem.h \ utils/JsonTreeItem.h \
utils/JsonModel.h \ utils/JsonModel.h \

View File

@ -223,6 +223,32 @@ void MainWindow::initUI()
connect(core, SIGNAL(projectSaved(const QString &)), this, SLOT(projectSaved(const QString &))); 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) void MainWindow::openNewFile(const QString &fn, int analLevel, QList<QString> advancedOptions)
{ {
setFilename(fn); setFilename(fn);
@ -525,6 +551,23 @@ void MainWindow::showDefaultDocks()
updateDockActionsChecked(); 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() void MainWindow::resetToDefaultLayout()
{ {
hideAllDocks(); hideAllDocks();
@ -545,6 +588,24 @@ void MainWindow::resetToDefaultLayout()
Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Disassembly); 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) void MainWindow::addOutput(const QString &msg)
{ {
consoleDock->addOutput(msg); consoleDock->addOutput(msg);
@ -590,6 +651,11 @@ void MainWindow::on_actionDefault_triggered()
resetToDefaultLayout(); resetToDefaultLayout();
} }
void MainWindow::on_actionZen_triggered()
{
resetToZenLayout();
}
void MainWindow::on_actionNew_triggered() void MainWindow::on_actionNew_triggered()
{ {
on_actionOpen_triggered(); on_actionOpen_triggered();
@ -761,7 +827,6 @@ bool MainWindow::eventFilter(QObject *, QEvent *event)
return true; return true;
} }
} }
return false; return false;
} }

View File

@ -95,6 +95,8 @@ public:
void addToDockWidgetList(QDockWidget *dockWidget); void addToDockWidgetList(QDockWidget *dockWidget);
void addDockWidgetAction(QDockWidget *dockWidget, QAction *action); void addDockWidgetAction(QDockWidget *dockWidget, QAction *action);
void addExtraWidget(QDockWidget *extraDock);
public slots: public slots:
@ -119,12 +121,16 @@ public slots:
private slots: private slots:
void on_actionAbout_triggered(); void on_actionAbout_triggered();
void on_actionExtraGraph_triggered();
void on_actionExtraHexdump_triggered();
void on_actionExtraDisassembly_triggered();
void on_actionRefresh_Panels_triggered(); void on_actionRefresh_Panels_triggered();
void on_actionDisasAdd_comment_triggered(); void on_actionDisasAdd_comment_triggered();
void on_actionDefault_triggered(); void on_actionDefault_triggered();
void on_actionZen_triggered();
void on_actionFunctionsRename_triggered(); void on_actionFunctionsRename_triggered();
@ -211,10 +217,12 @@ private:
#endif #endif
void resetToDefaultLayout(); void resetToDefaultLayout();
void resetToZenLayout();
void restoreDocks(); void restoreDocks();
void hideAllDocks(); void hideAllDocks();
void showDefaultDocks(); void showDefaultDocks();
void showZenDocks();
void updateDockActionsChecked(); void updateDockActionsChecked();
void toggleDockWidget(QDockWidget *dock_widget, bool show); void toggleDockWidget(QDockWidget *dock_widget, bool show);

View File

@ -201,6 +201,7 @@ border-top: 0px;
<addaction name="actionRefresh_contents"/> <addaction name="actionRefresh_contents"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionDefault"/> <addaction name="actionDefault"/>
<addaction name="actionZen"/>
<addaction name="actionReset_settings"/> <addaction name="actionReset_settings"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionLock"/> <addaction name="actionLock"/>
@ -256,6 +257,8 @@ border-top: 0px;
<addaction name="actionComments"/> <addaction name="actionComments"/>
<addaction name="actionConsole"/> <addaction name="actionConsole"/>
<addaction name="actionJupyter"/> <addaction name="actionJupyter"/>
<addaction name="separator"/>
<addaction name="addExtraWidgets"/>
</widget> </widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuEdit"/> <addaction name="menuEdit"/>
@ -322,6 +325,14 @@ background-color: palette(dark);
<string>Reset layout</string> <string>Reset layout</string>
</property> </property>
</action> </action>
<action name="actionZen">
<property name="text">
<string>Zen Mode</string>
</property>
<property name="toolTip">
<string>Zen mode</string>
</property>
</action>
<action name="actionAbout"> <action name="actionAbout">
<property name="text"> <property name="text">
<string>About</string> <string>About</string>
@ -1093,6 +1104,29 @@ background-color: palette(dark);
<string>Jupyter</string> <string>Jupyter</string>
</property> </property>
</action> </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> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>

View File

@ -6,8 +6,8 @@ CutterDockWidget::CutterDockWidget(MainWindow *main, QAction *action) :
QDockWidget(main), QDockWidget(main),
action(action) action(action)
{ {
main->addToDockWidgetList(this);
if (action) { if (action) {
main->addToDockWidgetList(this);
main->addDockWidgetAction(this, action); main->addDockWidgetAction(this, action);
connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget); connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget);
} }

View 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() {}

View 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);
};

View File

@ -1,5 +1,5 @@
#include "DisassemblerGraphView.h" #include "DisassemblerGraphView.h"
#include "CutterSeekableWidget.h"
#include <QPainter> #include <QPainter>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
@ -20,7 +20,8 @@
DisassemblerGraphView::DisassemblerGraphView(QWidget *parent) DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
: GraphView(parent), : GraphView(parent),
mFontMetrics(nullptr), mFontMetrics(nullptr),
mMenu(new DisassemblyContextMenu(this)) mMenu(new DisassemblyContextMenu(this)),
seekable(new CutterSeekableWidget(this))
{ {
highlight_token = nullptr; highlight_token = nullptr;
// Signals that require a refresh all // Signals that require a refresh all
@ -99,16 +100,21 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
mMenu->addAction(&actionExportGraph); mMenu->addAction(&actionExportGraph);
connect(&actionExportGraph, SIGNAL(triggered(bool)), this, SLOT(on_actionExportGraph_triggered())); 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(); initFont();
colorsUpdatedSlot(); colorsUpdatedSlot();
} }
void DisassemblerGraphView::connectSeekChanged(bool disconnect) void DisassemblerGraphView::connectSeekChanged(bool disconn)
{ {
if (disconnect) { if (disconn) {
QObject::disconnect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(onSeekChanged(RVA))); disconnect(seekable, &CutterSeekableWidget::seekChanged, this, &DisassemblerGraphView::onSeekChanged);
} else { } 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() void DisassemblerGraphView::refreshView()
{ {
initFont(); initFont();
@ -134,7 +151,7 @@ void DisassemblerGraphView::loadCurrentGraph()
.set("asm.bbline", false) .set("asm.bbline", false)
.set("asm.lines", false) .set("asm.lines", false)
.set("asm.lines.fcn", false); .set("asm.lines.fcn", false);
QJsonDocument functionsDoc = Core()->cmdj("agJ"); QJsonDocument functionsDoc = Core()->cmdj("agJ " + RAddressString(seekable->getOffset()));
QJsonArray functions = functionsDoc.array(); QJsonArray functions = functionsDoc.array();
disassembly_blocks.clear(); disassembly_blocks.clear();
@ -149,12 +166,16 @@ void DisassemblerGraphView::loadCurrentGraph()
f.ready = true; f.ready = true;
f.entry = func["offset"].toVariant().toULongLong(); f.entry = func["offset"].toVariant().toULongLong();
QString windowTitle = tr("Graph"); windowTitle = tr("Graph");
QString funcName = func["name"].toString().trimmed(); QString funcName = func["name"].toString().trimmed();
if (!funcName.isEmpty()) { if (!funcName.isEmpty()) {
windowTitle += " (" + funcName + ")"; windowTitle += " (" + funcName + ")";
} }
if (!seekable->getSyncWithCore()) {
parentWidget()->setWindowTitle(windowTitle + " (not synced)");
} else {
parentWidget()->setWindowTitle(windowTitle); parentWidget()->setWindowTitle(windowTitle);
}
RVA entry = func["offset"].toVariant().toULongLong(); 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 // Figure out if the current block is selected
for (const Instr &instr : db.instrs) { for (const Instr &instr : db.instrs) {
RVA addr = Core()->getOffset(); RVA addr = seekable->getOffset();
if ((instr.addr <= addr) && (addr <= instr.addr + instr.size)) { if ((instr.addr <= addr) && (addr <= instr.addr + instr.size)) {
block_selected = true; block_selected = true;
selected_instruction = instr.addr; selected_instruction = instr.addr;
@ -543,27 +564,27 @@ void DisassemblerGraphView::zoomReset()
void DisassemblerGraphView::takeTrue() void DisassemblerGraphView::takeTrue()
{ {
DisassemblyBlock *db = blockForAddress(Core()->getOffset()); DisassemblyBlock *db = blockForAddress(seekable->getOffset());
if (db->true_path != RVA_INVALID) { if (db->true_path != RVA_INVALID) {
Core()->seek(db->true_path); seekable->seek(db->true_path);
} else if (blocks[db->entry].exits.size()) { } else if (blocks[db->entry].exits.size()) {
Core()->seek(blocks[db->entry].exits[0]); seekable->seek(blocks[db->entry].exits[0]);
} }
} }
void DisassemblerGraphView::takeFalse() void DisassemblerGraphView::takeFalse()
{ {
DisassemblyBlock *db = blockForAddress(Core()->getOffset()); DisassemblyBlock *db = blockForAddress(seekable->getOffset());
if (db->false_path != RVA_INVALID) { if (db->false_path != RVA_INVALID) {
Core()->seek(db->false_path); seekable->seek(db->false_path);
} else if (blocks[db->entry].exits.size()) { } 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) void DisassemblerGraphView::seekInstruction(bool previous_instr)
{ {
RVA addr = Core()->getOffset(); RVA addr = seekable->getOffset();
DisassemblyBlock *db = blockForAddress(addr); DisassemblyBlock *db = blockForAddress(addr);
if (!db) { if (!db) {
return; return;
@ -577,9 +598,9 @@ void DisassemblerGraphView::seekInstruction(bool previous_instr)
// Found the instructon. Check if a next one exists // Found the instructon. Check if a next one exists
if (!previous_instr && (i < db->instrs.size() - 1)) { 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)) { } 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); seekInstruction(true);
} }
void DisassemblerGraphView::seek(RVA addr, bool update_viewport) void DisassemblerGraphView::seekLocal(RVA addr, bool update_viewport)
{ {
connectSeekChanged(true); connectSeekChanged(true);
Core()->seek(addr); seekable->seek(addr);
connectSeekChanged(false); connectSeekChanged(false);
if (update_viewport) { if (update_viewport) {
viewport()->update(); viewport()->update();
@ -606,8 +627,13 @@ void DisassemblerGraphView::seek(RVA addr, bool update_viewport)
void DisassemblerGraphView::seekPrev() void DisassemblerGraphView::seekPrev()
{ {
if (seekable->getSyncWithCore()) {
Core()->seekPrev(); Core()->seekPrev();
} }
else {
seekable->seek(seekable->getPrevIndependentOffset());
}
}
void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event,
QPoint pos) QPoint pos)
@ -617,7 +643,7 @@ void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEve
return; return;
} }
seek(instr, true); seekLocal(instr);
if (event->button() == Qt::RightButton) { if (event->button() == Qt::RightButton) {
mMenu->setOffset(instr); mMenu->setOffset(instr);
@ -629,13 +655,14 @@ void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMo
QPoint pos) QPoint pos)
{ {
Q_UNUSED(event); Q_UNUSED(event);
RVA instr = getAddrForMouseEvent(block, &pos); RVA instr = getAddrForMouseEvent(block, &pos);
if (instr == RVA_INVALID) { if (instr == RVA_INVALID) {
return; return;
} }
QList<XrefDescription> refs = Core()->getXRefs(instr, false, false); QList<XrefDescription> refs = Core()->getXRefs(instr, false, false);
if (refs.length()) { if (refs.length()) {
Core()->seek(refs.at(0).to); seekable->seek(refs.at(0).to);
} }
if (refs.length() > 1) { if (refs.length() > 1) {
qWarning() << "Too many references here. Weird behaviour expected."; qWarning() << "Too many references here. Weird behaviour expected.";
@ -671,7 +698,7 @@ void DisassemblerGraphView::blockTransitionedTo(GraphView::GraphBlock *to)
transition_dont_seek = false; transition_dont_seek = false;
return; return;
} }
seek(to->entry); seekLocal(to->entry);
} }

View File

@ -10,6 +10,7 @@
#include "widgets/GraphView.h" #include "widgets/GraphView.h"
#include "menus/DisassemblyContextMenu.h" #include "menus/DisassemblyContextMenu.h"
#include "utils/RichTextPainter.h" #include "utils/RichTextPainter.h"
#include "CutterSeekableWidget.h"
class DisassemblerGraphView : public GraphView class DisassemblerGraphView : public GraphView
{ {
@ -142,6 +143,7 @@ public:
virtual void blockTransitionedTo(GraphView::GraphBlock *to) override; virtual void blockTransitionedTo(GraphView::GraphBlock *to) override;
void loadCurrentGraph(); void loadCurrentGraph();
QString windowTitle;
// bool navigate(ut64 addr); // bool navigate(ut64 addr);
public slots: public slots:
@ -149,6 +151,7 @@ public slots:
void colorsUpdatedSlot(); void colorsUpdatedSlot();
void fontsUpdatedSlot(); void fontsUpdatedSlot();
void onSeekChanged(RVA addr); void onSeekChanged(RVA addr);
void toggleSync();
void zoomIn(QPoint mouse = QPoint(0, 0)); void zoomIn(QPoint mouse = QPoint(0, 0));
void zoomOut(QPoint mouse = QPoint(0, 0)); void zoomOut(QPoint mouse = QPoint(0, 0));
@ -189,9 +192,9 @@ private:
RVA getAddrForMouseEvent(GraphBlock &block, QPoint *point); RVA getAddrForMouseEvent(GraphBlock &block, QPoint *point);
Instr *getInstrForMouseEvent(GraphBlock &block, QPoint *point); Instr *getInstrForMouseEvent(GraphBlock &block, QPoint *point);
DisassemblyBlock *blockForAddress(RVA addr); 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); void seekInstruction(bool previous_instr);
CutterSeekableWidget *seekable = nullptr;
QList<QShortcut *> shortcuts; QList<QShortcut *> shortcuts;
QColor disassemblyBackgroundColor; QColor disassemblyBackgroundColor;
@ -218,6 +221,7 @@ private:
QColor mDisabledBreakpointColor; QColor mDisabledBreakpointColor;
QAction actionExportGraph; QAction actionExportGraph;
QAction actionSyncOffset;
}; };
#endif // DISASSEMBLERGRAPHVIEW_H #endif // DISASSEMBLERGRAPHVIEW_H

View File

@ -41,6 +41,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
, mCtxMenu(new DisassemblyContextMenu(this)) , mCtxMenu(new DisassemblyContextMenu(this))
, mDisasScrollArea(new DisassemblyScrollArea(this)) , mDisasScrollArea(new DisassemblyScrollArea(this))
, mDisasTextEdit(new DisassemblyTextEdit(this)) , mDisasTextEdit(new DisassemblyTextEdit(this))
, seekable(new CutterSeekableWidget(this))
{ {
topOffset = bottomOffset = RVA_INVALID; topOffset = bottomOffset = RVA_INVALID;
cursorLineOffset = 0; 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, connect(Core(), SIGNAL(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)), this,
SLOT(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType))); SLOT(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)));
connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshDisasm())); connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshDisasm()));
@ -140,6 +140,11 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
shortcut_escape->setContext(Qt::WidgetShortcut); shortcut_escape->setContext(Qt::WidgetShortcut);
connect(shortcut_escape, SIGNAL(activated()), this, SLOT(seekPrev())); 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) { \ #define ADD_SHORTCUT(ksq, slot) { \
QShortcut *s = new QShortcut((ksq), this); \ QShortcut *s = new QShortcut((ksq), this); \
@ -167,6 +172,18 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
#undef ADD_SHORTCUT #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() QWidget *DisassemblyWidget::getTextWidget()
{ {
return mDisasTextEdit; return mDisasTextEdit;
@ -354,7 +371,7 @@ RVA DisassemblyWidget::readDisassemblyOffset(QTextCursor tc)
void DisassemblyWidget::updateCursorPosition() void DisassemblyWidget::updateCursorPosition()
{ {
RVA offset = Core()->getOffset(); RVA offset = seekable->getOffset();
// already fine where it is? // already fine where it is?
RVA currentLineOffset = readCurrentDisassemblyOffset(); RVA currentLineOffset = readCurrentDisassemblyOffset();
@ -432,7 +449,7 @@ void DisassemblyWidget::cursorPositionChanged()
} }
seekFromCursor = true; seekFromCursor = true;
Core()->seek(offset); seekable->seek(offset);
seekFromCursor = false; seekFromCursor = false;
highlightCurrentLine(); highlightCurrentLine();
mCtxMenu->setCanCopy(mDisasTextEdit->textCursor().hasSelection()); mCtxMenu->setCanCopy(mDisasTextEdit->textCursor().hasSelection());
@ -492,8 +509,8 @@ void DisassemblyWidget::moveCursorRelative(bool up, bool page)
// handle cases where top instruction offsets change // handle cases where top instruction offsets change
RVA offset = readCurrentDisassemblyOffset(); RVA offset = readCurrentDisassemblyOffset();
if (offset != Core()->getOffset()) { if (offset != seekable->getOffset()) {
Core()->seek(offset); seekable->seek(offset);
highlightCurrentLine(); highlightCurrentLine();
} }
} }
@ -520,7 +537,7 @@ bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)
} }
if (jump != RVA_INVALID) { if (jump != RVA_INVALID) {
Core()->seek(jump); seekable->seek(jump);
} }
return true; return true;

View File

@ -3,9 +3,11 @@
#include "Cutter.h" #include "Cutter.h"
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "CutterSeekableWidget.h"
#include <QTextEdit> #include <QTextEdit>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QShortcut> #include <QShortcut>
#include <QAction>
class DisassemblyTextEdit; class DisassemblyTextEdit;
@ -26,6 +28,7 @@ public slots:
void fontsUpdatedSlot(); void fontsUpdatedSlot();
void colorsUpdatedSlot(); void colorsUpdatedSlot();
void seekPrev(); void seekPrev();
void toggleSync();
private slots: private slots:
void on_seekChanged(RVA offset); void on_seekChanged(RVA offset);
@ -63,6 +66,8 @@ private:
void connectCursorPositionChanged(bool disconnect); void connectCursorPositionChanged(bool disconnect);
void moveCursorRelative(bool up, bool page); void moveCursorRelative(bool up, bool page);
QAction syncIt;
CutterSeekableWidget *seekable;
}; };
class DisassemblyScrollArea : public QAbstractScrollArea class DisassemblyScrollArea : public QAbstractScrollArea

View File

@ -23,9 +23,6 @@ GraphWidget::GraphWidget(MainWindow *main, QAction *action) :
this->graphView->setFocus(); this->graphView->setFocus();
} }
}); });
} }
GraphWidget::~GraphWidget() {} GraphWidget::~GraphWidget() {}

View File

@ -15,7 +15,8 @@
HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) : HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), CutterDockWidget(main, action),
ui(new Ui::HexdumpWidget) ui(new Ui::HexdumpWidget),
seekable(new CutterSeekableWidget(this))
{ {
ui->setupUi(this); ui->setupUi(this);
@ -44,6 +45,8 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
colorsUpdatedSlot(); colorsUpdatedSlot();
updateHeaders(); updateHeaders();
this->setWindowTitle(tr("Hexdump"));
connect(&syncAction, SIGNAL(triggered(bool)), this, SLOT(toggleSync()));
// Set hexdump context menu // Set hexdump context menu
ui->hexHexText->setContextMenuPolicy(Qt::CustomContextMenu); 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(fontsUpdated()), this, SLOT(fontsUpdated()));
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));
connect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA)));
connect(Core(), SIGNAL(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)), this, connect(Core(), SIGNAL(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)), this,
SLOT(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType))); SLOT(raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType)));
@ -80,6 +82,7 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexdumpWidget::selectionChanged); connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexdumpWidget::selectionChanged);
connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, this, connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, this,
&HexdumpWidget::selectionChanged); &HexdumpWidget::selectionChanged);
connect(seekable, &CutterSeekableWidget::seekChanged, this, &HexdumpWidget::on_seekChanged);
format = Format::Hex; format = Format::Hex;
initParsing(); initParsing();
@ -281,7 +284,7 @@ void HexdumpWidget::refresh(RVA addr)
updateHeaders(); updateHeaders();
if (addr == RVA_INVALID) { if (addr == RVA_INVALID) {
addr = Core()->getOffset(); addr = seekable->getOffset();
} }
cols = Core()->getConfigi("hex.cols"); cols = Core()->getConfigi("hex.cols");
@ -452,7 +455,7 @@ void HexdumpWidget::selectionChanged()
int pos = asciiAddressToPosition(adr); int pos = asciiAddressToPosition(adr);
setTextEditPosition(ui->hexASCIIText, pos); setTextEditPosition(ui->hexASCIIText, pos);
sent_seek = true; sent_seek = true;
Core()->seek(adr); seekable->seek(adr);
sent_seek = false; sent_seek = false;
connectScroll(false); connectScroll(false);
return; return;
@ -503,7 +506,7 @@ void HexdumpWidget::selectionChanged()
targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor); targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
ui->hexASCIIText->setTextCursor(targetTextCursor); ui->hexASCIIText->setTextCursor(targetTextCursor);
sent_seek = true; sent_seek = true;
Core()->seek(startAddress); seekable->seek(startAddress);
sent_seek = false; sent_seek = false;
} else { } else {
QTextCursor textCursor = ui->hexASCIIText->textCursor(); QTextCursor textCursor = ui->hexASCIIText->textCursor();
@ -514,7 +517,7 @@ void HexdumpWidget::selectionChanged()
setTextEditPosition(ui->hexHexText, pos); setTextEditPosition(ui->hexHexText, pos);
connectScroll(false); connectScroll(false);
sent_seek = true; sent_seek = true;
Core()->seek(adr); seekable->seek(adr);
sent_seek = false; sent_seek = false;
return; return;
} }
@ -534,7 +537,7 @@ void HexdumpWidget::selectionChanged()
targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor); targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
ui->hexHexText->setTextCursor(targetTextCursor); ui->hexHexText->setTextCursor(targetTextCursor);
sent_seek = true; sent_seek = true;
Core()->seek(startAddress); seekable->seek(startAddress);
sent_seek = false; sent_seek = false;
} }
@ -570,6 +573,11 @@ void HexdumpWidget::showHexdumpContextMenu(const QPoint &pt)
QMenu *formatSubmenu = menu->addMenu(tr("Format")); QMenu *formatSubmenu = menu->addMenu(tr("Format"));
formatSubmenu->addAction(ui->actionFormatHex); formatSubmenu->addAction(ui->actionFormatHex);
formatSubmenu->addAction(ui->actionFormatOctal); formatSubmenu->addAction(ui->actionFormatOctal);
menu->addSeparator();
syncAction.setText(tr("Sync/unsync offset"));
menu->addAction(&syncAction);
// TODO: // TODO:
// formatSubmenu->addAction(ui->actionFormatHalfWord); // formatSubmenu->addAction(ui->actionFormatHalfWord);
// formatSubmenu->addAction(ui->actionFormatWord); // formatSubmenu->addAction(ui->actionFormatWord);
@ -595,6 +603,18 @@ void HexdumpWidget::showHexdumpContextMenu(const QPoint &pt)
delete menu; 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) void HexdumpWidget::showHexASCIIContextMenu(const QPoint &pt)
{ {
// Set Hex ASCII popup menu // Set Hex ASCII popup menu

View File

@ -10,6 +10,7 @@
#include "Cutter.h" #include "Cutter.h"
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "CutterSeekableWidget.h"
#include "utils/Highlighter.h" #include "utils/Highlighter.h"
#include "utils/HexAsciiHighlighter.h" #include "utils/HexAsciiHighlighter.h"
#include "utils/HexHighlighter.h" #include "utils/HexHighlighter.h"
@ -27,9 +28,7 @@ class HexdumpWidget : public CutterDockWidget
public: public:
explicit HexdumpWidget(MainWindow *main, QAction *action = nullptr); explicit HexdumpWidget(MainWindow *main, QAction *action = nullptr);
~HexdumpWidget(); ~HexdumpWidget();
Highlighter *highlighter; Highlighter *highlighter;
enum Format { enum Format {
Hex, Hex,
Octal, Octal,
@ -50,6 +49,7 @@ public slots:
void zoomIn(int range = 1); void zoomIn(int range = 1);
void zoomOut(int range = 1); void zoomOut(int range = 1);
void toggleSync();
protected: protected:
virtual void resizeEvent(QResizeEvent *event) override; virtual void resizeEvent(QResizeEvent *event) override;
@ -102,6 +102,8 @@ private:
int bufferLines; int bufferLines;
int cols; int cols;
QAction syncAction;
CutterSeekableWidget *seekable;
private slots: private slots:
void on_seekChanged(RVA addr); void on_seekChanged(RVA addr);