GraphView fixes (#214)

This commit is contained in:
Thomas (nezza-_-) Roth 2017-12-14 22:07:48 +01:00 committed by Maijin
parent 273c87eaba
commit ce2557ccbe
5 changed files with 335 additions and 167 deletions

View File

@ -102,6 +102,8 @@ Cutter is developed on OS X, Linux and Windows. The first release for users will
| + | Zoom in | | + | Zoom in |
| - | Zoom out | | - | Zoom out |
| = | Reset zoom | | = | Reset zoom |
| J | Next instruction |
| K | Previous instruction |
## Help ## Help

View File

@ -17,7 +17,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
mFontMetrics(nullptr), mFontMetrics(nullptr),
mMenu(new DisassemblyContextMenu(this)) mMenu(new DisassemblyContextMenu(this))
{ {
this->highlight_token = nullptr; highlight_token = nullptr;
// Signals that require a refresh all // Signals that require a refresh all
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshView())); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshView()));
connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshView())); connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshView()));
@ -31,9 +31,9 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
connect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(onSeekChanged(RVA))); connect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(onSeekChanged(RVA)));
// Space to switch to disassembly // Space to switch to disassembly
QShortcut *disassemblyShortcut = new QShortcut(QKeySequence(Qt::Key_Space), this); QShortcut *shortcut_disassembly = new QShortcut(QKeySequence(Qt::Key_Space), this);
disassemblyShortcut->setContext(Qt::WidgetShortcut); shortcut_disassembly->setContext(Qt::WidgetShortcut);
connect(disassemblyShortcut, &QShortcut::activated, this, []{ connect(shortcut_disassembly, &QShortcut::activated, this, []{
Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Disassembly); Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Disassembly);
Core()->triggerRaisePrioritizedMemoryWidget(); Core()->triggerRaisePrioritizedMemoryWidget();
}); });
@ -61,15 +61,39 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
shortcut_take_false->setContext(Qt::WidgetShortcut); shortcut_take_false->setContext(Qt::WidgetShortcut);
connect(shortcut_take_false, SIGNAL(activated()), this, SLOT(takeFalse())); connect(shortcut_take_false, SIGNAL(activated()), this, SLOT(takeFalse()));
// Navigation shortcuts
QShortcut *shortcut_next_instr = new QShortcut(QKeySequence(Qt::Key_J), this);
shortcut_next_instr->setContext(Qt::WidgetShortcut);
connect(shortcut_next_instr, SIGNAL(activated()), this, SLOT(nextInstr()));
QShortcut *shortcut_prev_instr = new QShortcut(QKeySequence(Qt::Key_K), this);
shortcut_prev_instr->setContext(Qt::WidgetShortcut);
connect(shortcut_prev_instr, SIGNAL(activated()), this, SLOT(prevInstr()));
shortcuts.append(shortcut_disassembly);
shortcuts.append(shortcut_escape);
shortcuts.append(shortcut_zoom_in);
shortcuts.append(shortcut_zoom_out);
shortcuts.append(shortcut_zoom_reset);
shortcuts.append(shortcut_next_instr);
shortcuts.append(shortcut_prev_instr);
initFont(); initFont();
colorsUpdatedSlot(); colorsUpdatedSlot();
} }
DisassemblerGraphView::~DisassemblerGraphView()
{
for(QShortcut *shortcut : shortcuts)
{
delete shortcut;
}
}
void DisassemblerGraphView::refreshView() void DisassemblerGraphView::refreshView()
{ {
initFont(); initFont();
loadCurrentGraph(); loadCurrentGraph();
this->viewport()->update(); viewport()->update();
} }
void DisassemblerGraphView::loadCurrentGraph() void DisassemblerGraphView::loadCurrentGraph()
@ -78,7 +102,7 @@ void DisassemblerGraphView::loadCurrentGraph()
QJsonArray functions = functionsDoc.array(); QJsonArray functions = functionsDoc.array();
disassembly_blocks.clear(); disassembly_blocks.clear();
this->blocks.clear(); blocks.clear();
Analysis anal; Analysis anal;
anal.ready = true; anal.ready = true;
@ -95,7 +119,7 @@ void DisassemblerGraphView::loadCurrentGraph()
{ {
windowTitle += " (" + funcName + ")"; windowTitle += " (" + funcName + ")";
} }
this->parentWidget()->setWindowTitle(windowTitle); parentWidget()->setWindowTitle(windowTitle);
RVA entry = func["offset"].toVariant().toULongLong(); RVA entry = func["offset"].toVariant().toULongLong();
@ -157,7 +181,13 @@ void DisassemblerGraphView::loadCurrentGraph()
if(func["blocks"].toArray().size() > 0) if(func["blocks"].toArray().size() > 0)
{ {
computeGraph(entry); computeGraph(entry);
this->viewport()->update(); viewport()->update();
if(first_draw)
{
showBlock(blocks[entry]);
first_draw = false;
}
} }
} }
@ -187,20 +217,20 @@ void DisassemblerGraphView::prepareGraphNode(GraphBlock &block)
height += 1; height += 1;
} }
} }
int extra = 4 * this->charWidth + 4; int extra = 4 * charWidth + 4;
block.width = width + extra + this->charWidth; block.width = width + extra + charWidth;
block.height = (height * this->charHeight) + extra; block.height = (height * charHeight) + extra;
} }
void DisassemblerGraphView::initFont() void DisassemblerGraphView::initFont()
{ {
setFont(Config()->getFont()); setFont(Config()->getFont());
QFontMetricsF metrics(this->font()); QFontMetricsF metrics(font());
this->baseline = int(metrics.ascent()); baseline = int(metrics.ascent());
this->charWidth = metrics.width('X'); charWidth = metrics.width('X');
this->charHeight = metrics.height(); charHeight = metrics.height();
this->charOffset = 0; charOffset = 0;
if(mFontMetrics) if(mFontMetrics)
delete mFontMetrics; delete mFontMetrics;
mFontMetrics = new CachedFontMetrics(this, font()); mFontMetrics = new CachedFontMetrics(this, font());
@ -257,7 +287,7 @@ void DisassemblerGraphView::drawBlock(QPainter & p, GraphView::GraphBlock &block
// Draw different background for selected instruction // Draw different background for selected instruction
if(selected_instruction != RVA_INVALID) if(selected_instruction != RVA_INVALID)
{ {
int y = block.y + (2 * this->charWidth) + (db.header_text.lines.size() * this->charHeight); int y = block.y + (2 * charWidth) + (db.header_text.lines.size() * charHeight);
for(Instr & instr : db.instrs) for(Instr & instr : db.instrs)
{ {
auto selected = instr.addr == selected_instruction; auto selected = instr.addr == selected_instruction;
@ -265,13 +295,13 @@ void DisassemblerGraphView::drawBlock(QPainter & p, GraphView::GraphBlock &block
auto traceCount = 0; auto traceCount = 0;
if(selected && traceCount) if(selected && traceCount)
{ {
p.fillRect(QRect(block.x + this->charWidth, y, block.width - (10 + 2 * this->charWidth), p.fillRect(QRect(block.x + charWidth, y, block.width - (10 + 2 * charWidth),
int(instr.text.lines.size()) * this->charHeight), disassemblyTracedSelectionColor); int(instr.text.lines.size()) * charHeight), disassemblyTracedSelectionColor);
} }
else if(selected) else if(selected)
{ {
p.fillRect(QRect(block.x + this->charWidth, y, block.width - (10 + 2 * this->charWidth), p.fillRect(QRect(block.x + charWidth, y, block.width - (10 + 2 * charWidth),
int(instr.text.lines.size()) * this->charHeight), disassemblySelectionColor); int(instr.text.lines.size()) * charHeight), disassemblySelectionColor);
} }
else if(traceCount) else if(traceCount)
{ {
@ -285,40 +315,40 @@ void DisassemblerGraphView::drawBlock(QPainter & p, GraphView::GraphBlock &block
if(disassemblyTracedColor.blue() > 160) if(disassemblyTracedColor.blue() > 160)
colorDiff *= -1; colorDiff *= -1;
p.fillRect(QRect(block.x + this->charWidth, y, block.width - (10 + 2 * this->charWidth), int(instr.text.lines.size()) * this->charHeight), p.fillRect(QRect(block.x + charWidth, y, block.width - (10 + 2 * charWidth), int(instr.text.lines.size()) * charHeight),
QColor(disassemblyTracedColor.red(), QColor(disassemblyTracedColor.red(),
disassemblyTracedColor.green(), disassemblyTracedColor.green(),
std::max(0, std::min(256, disassemblyTracedColor.blue() + colorDiff)))); std::max(0, std::min(256, disassemblyTracedColor.blue() + colorDiff))));
} }
y += int(instr.text.lines.size()) * this->charHeight; y += int(instr.text.lines.size()) * charHeight;
} }
} }
// Render node text // Render node text
auto x = block.x + (2 * this->charWidth); auto x = block.x + (2 * charWidth);
int y = block.y + (2 * this->charWidth); int y = block.y + (2 * charWidth);
for(auto & line : db.header_text.lines) for(auto & line : db.header_text.lines)
{ {
RichTextPainter::paintRichText(&p, x, y, block.width, this->charHeight, 0, line, mFontMetrics); RichTextPainter::paintRichText(&p, x, y, block.width, charHeight, 0, line, mFontMetrics);
y += this->charHeight; y += charHeight;
} }
for(Instr & instr : db.instrs) for(Instr & instr : db.instrs)
{ {
for(auto & line : instr.text.lines) for(auto & line : instr.text.lines)
{ {
int rectSize = qRound(this->charWidth); int rectSize = qRound(charWidth);
if(rectSize % 2) if(rectSize % 2)
{ {
rectSize++; rectSize++;
} }
// Assume charWidth <= charHeight // Assume charWidth <= charHeight
QRectF bpRect(x - rectSize / 3.0, y + (this->charHeight - rectSize) / 2.0, rectSize, rectSize); QRectF bpRect(x - rectSize / 3.0, y + (charHeight - rectSize) / 2.0, rectSize, rectSize);
// TODO: Breakpoint/Cip stuff // TODO: Breakpoint/Cip stuff
RichTextPainter::paintRichText(&p, x + this->charWidth, y, block.width - this->charWidth, this->charHeight, 0, line, mFontMetrics); RichTextPainter::paintRichText(&p, x + charWidth, y, block.width - charWidth, charHeight, 0, line, mFontMetrics);
y += this->charHeight; y += charHeight;
} }
} }
@ -348,7 +378,13 @@ GraphView::EdgeConfiguration DisassemblerGraphView::edgeConfiguration(GraphView:
RVA DisassemblerGraphView::getInstrForMouseEvent(GraphBlock &block, QPoint* point) RVA DisassemblerGraphView::getInstrForMouseEvent(GraphBlock &block, QPoint* point)
{ {
DisassemblyBlock &db = disassembly_blocks[block.entry]; DisassemblyBlock &db = disassembly_blocks[block.entry];
int mouse_row = ((point->y()-(2*this->charWidth)) / this->charHeight);
// Remove header and margin
int off_y = (2 * charWidth) + (db.header_text.lines.size() * charHeight);
// Get mouse coordinate over the actual text
int text_point_y = point->y() - off_y;
int mouse_row = text_point_y / charHeight;
int cur_row = db.header_text.lines.size(); int cur_row = db.header_text.lines.size();
if (mouse_row < cur_row) if (mouse_row < cur_row)
{ {
@ -389,7 +425,7 @@ void DisassemblerGraphView::colorsUpdatedSlot()
void DisassemblerGraphView::fontsUpdatedSlot() void DisassemblerGraphView::fontsUpdatedSlot()
{ {
this->initFont(); initFont();
refreshView(); refreshView();
} }
@ -440,26 +476,26 @@ void DisassemblerGraphView::onSeekChanged(RVA addr)
void DisassemblerGraphView::zoomIn() void DisassemblerGraphView::zoomIn()
{ {
current_scale += 0.1; current_scale += 0.1;
auto areaSize = this->viewport()->size(); auto areaSize = viewport()->size();
this->adjustSize(areaSize.width(), areaSize.height()); adjustSize(areaSize.width(), areaSize.height());
this->viewport()->update(); viewport()->update();
} }
void DisassemblerGraphView::zoomOut() void DisassemblerGraphView::zoomOut()
{ {
current_scale -= 0.1; current_scale -= 0.1;
current_scale = std::max(current_scale, 0.3); current_scale = std::max(current_scale, 0.3);
auto areaSize = this->viewport()->size(); auto areaSize = viewport()->size();
this->adjustSize(areaSize.width(), areaSize.height()); adjustSize(areaSize.width(), areaSize.height());
this->viewport()->update(); viewport()->update();
} }
void DisassemblerGraphView::zoomReset() void DisassemblerGraphView::zoomReset()
{ {
current_scale = 1.0; current_scale = 1.0;
auto areaSize = this->viewport()->size(); auto areaSize = viewport()->size();
this->adjustSize(areaSize.width(), areaSize.height()); adjustSize(areaSize.width(), areaSize.height());
this->viewport()->update(); viewport()->update();
} }
void DisassemblerGraphView::takeTrue() void DisassemblerGraphView::takeTrue()
@ -488,13 +524,52 @@ void DisassemblerGraphView::takeFalse()
} }
} }
void DisassemblerGraphView::seekInstruction(bool previous_instr)
{
RVA addr = Core()->getOffset();
DisassemblyBlock *db = blockForAddress(addr);
if(!db)
{
return;
}
for(size_t i=0; i < db->instrs.size(); i++)
{
Instr &instr = db->instrs[i];
if(!((instr.addr <= addr) && (addr <= instr.addr + instr.size)))
{
continue;
}
// Found the instructon. Check if a next one exists
if(!previous_instr && (i < db->instrs.size()-1))
{
seek(db->instrs[i+1].addr, true);
}
else if(previous_instr && (i > 0))
{
seek(db->instrs[i-1].addr);
}
}
}
void DisassemblerGraphView::nextInstr()
{
seekInstruction(false);
}
void DisassemblerGraphView::prevInstr()
{
seekInstruction(true);
}
void DisassemblerGraphView::seek(RVA addr, bool update_viewport) void DisassemblerGraphView::seek(RVA addr, bool update_viewport)
{ {
sent_seek = true; sent_seek = true;
Core()->seek(addr); Core()->seek(addr);
if(update_viewport) if(update_viewport)
{ {
this->viewport()->update(); viewport()->update();
} }
} }
@ -505,11 +580,10 @@ void DisassemblerGraphView::seekPrev()
void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos) void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos)
{ {
RVA instr = this->getInstrForMouseEvent(block, &pos); RVA instr = getInstrForMouseEvent(block, &pos);
if(instr == RVA_INVALID) if(instr == RVA_INVALID)
{ {
return; return;
//
} }
seek(instr, true); seek(instr, true);
@ -521,6 +595,23 @@ void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEve
} }
} }
void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos)
{
RVA instr = getInstrForMouseEvent(block, &pos);
if(instr == RVA_INVALID)
{
return;
}
QList<XrefDescription> refs = Core()->getXRefs(instr, false, false);
if (refs.length()) {
sent_seek = false;
Core()->seek(refs.at(0).to);
}
if (refs.length() > 1) {
qWarning() << "Too many references here. Weird behaviour expected.";
}
}
void DisassemblerGraphView::blockTransitionedTo(GraphView::GraphBlock *to) void DisassemblerGraphView::blockTransitionedTo(GraphView::GraphBlock *to)
{ {
if(transition_dont_seek) if(transition_dont_seek)

View File

@ -5,6 +5,7 @@
#include <QWidget> #include <QWidget>
#include <QPainter> #include <QPainter>
#include <QShortcut>
#include "widgets/GraphView.h" #include "widgets/GraphView.h"
#include "menus/DisassemblyContextMenu.h" #include "menus/DisassemblyContextMenu.h"
@ -136,9 +137,11 @@ class DisassemblerGraphView : public GraphView
public: public:
DisassemblerGraphView(QWidget *parent); DisassemblerGraphView(QWidget *parent);
~DisassemblerGraphView();
std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks; std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks;
virtual void drawBlock(QPainter & p, GraphView::GraphBlock &block) override; virtual void drawBlock(QPainter & p, GraphView::GraphBlock &block) override;
virtual void blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos) override; virtual void blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos) override;
virtual void blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos) override;
virtual GraphView::EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to) override; virtual GraphView::EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to) override;
virtual void blockTransitionedTo(GraphView::GraphBlock *to) override; virtual void blockTransitionedTo(GraphView::GraphBlock *to) override;
@ -157,10 +160,14 @@ public slots:
void takeTrue(); void takeTrue();
void takeFalse(); void takeFalse();
void nextInstr();
void prevInstr();
private slots: private slots:
void seekPrev(); void seekPrev();
private: private:
bool first_draw = true;
bool transition_dont_seek = false; bool transition_dont_seek = false;
bool sent_seek = false; bool sent_seek = false;
@ -168,7 +175,7 @@ private:
// Font data // Font data
CachedFontMetrics* mFontMetrics; CachedFontMetrics* mFontMetrics;
qreal charWidth; qreal charWidth;
qreal charHeight; int charHeight;
int charOffset; int charOffset;
int baseline; int baseline;
@ -179,6 +186,9 @@ private:
RVA getInstrForMouseEvent(GraphBlock &block, QPoint* point); RVA getInstrForMouseEvent(GraphBlock &block, QPoint* point);
DisassemblyBlock *blockForAddress(RVA addr); DisassemblyBlock *blockForAddress(RVA addr);
void seek(RVA addr, bool update_viewport=true); void seek(RVA addr, bool update_viewport=true);
void seekInstruction(bool previous_instr);
QList<QShortcut*> shortcuts;
QColor disassemblyBackgroundColor; QColor disassemblyBackgroundColor;
QColor disassemblySelectedBackgroundColor; QColor disassemblySelectedBackgroundColor;

View File

@ -9,12 +9,12 @@
GraphView::GraphView(QWidget *parent) GraphView::GraphView(QWidget *parent)
: QAbstractScrollArea(parent) : QAbstractScrollArea(parent)
{ {
this->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
this->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
this->horizontalScrollBar()->setSingleStep(this->charWidth); horizontalScrollBar()->setSingleStep(charWidth);
this->verticalScrollBar()->setSingleStep(this->charWidth); verticalScrollBar()->setSingleStep(charWidth);
QSize areaSize = this->viewport()->size(); QSize areaSize = viewport()->size();
this->adjustSize(areaSize.width(), areaSize.height()); adjustSize(areaSize.width(), areaSize.height());
} }
GraphView::~GraphView() GraphView::~GraphView()
@ -53,6 +53,14 @@ void GraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, Q
qWarning() << "Block clicked not overridden!"; qWarning() << "Block clicked not overridden!";
} }
void GraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos)
{
Q_UNUSED(block);
Q_UNUSED(event);
Q_UNUSED(pos);
qWarning() << "Block double clicked not overridden!";
}
void GraphView::blockTransitionedTo(GraphView::GraphBlock *to) void GraphView::blockTransitionedTo(GraphView::GraphBlock *to)
{ {
Q_UNUSED(to); Q_UNUSED(to);
@ -83,9 +91,9 @@ void GraphView::adjustSize(int new_width, int new_height)
//Update scroll bar information //Update scroll bar information
horizontalScrollBar()->setPageStep(new_width); horizontalScrollBar()->setPageStep(new_width);
horizontalScrollBar()->setRange(0, this->width - (new_width/current_scale)); horizontalScrollBar()->setRange(0, width - (new_width/current_scale));
verticalScrollBar()->setPageStep(new_height); verticalScrollBar()->setPageStep(new_height);
verticalScrollBar()->setRange(0, this->height - (new_height/current_scale)); verticalScrollBar()->setRange(0, height - (new_height/current_scale));
horizontalScrollBar()->setValue((int)((double)horizontalScrollBar()->maximum() * hfactor)); horizontalScrollBar()->setValue((int)((double)horizontalScrollBar()->maximum() * hfactor));
verticalScrollBar()->setValue((int)((double)verticalScrollBar()->maximum() * vfactor)); verticalScrollBar()->setValue((int)((double)verticalScrollBar()->maximum() * vfactor));
} }
@ -93,15 +101,15 @@ void GraphView::adjustSize(int new_width, int new_height)
// This calculates the full graph starting at block entry. // This calculates the full graph starting at block entry.
void GraphView::computeGraph(ut64 entry) void GraphView::computeGraph(ut64 entry)
{ {
QSize areaSize = this->viewport()->size(); QSize areaSize = viewport()->size();
// Populate incoming lists // Populate incoming lists
for(auto &blockIt : this->blocks) for(auto &blockIt : blocks)
{ {
GraphBlock &block = blockIt.second; GraphBlock &block = blockIt.second;
for(auto & edge : block.exits) for(auto & edge : block.exits)
{ {
this->blocks[edge].incoming.push_back(block.entry); blocks[edge].incoming.push_back(block.entry);
} }
} }
@ -119,7 +127,7 @@ void GraphView::computeGraph(ut64 entry)
// Pick nodes with single entrypoints // Pick nodes with single entrypoints
while(!queue.empty()) while(!queue.empty())
{ {
GraphBlock &block = this->blocks[queue.front()]; GraphBlock &block = blocks[queue.front()];
queue.pop(); queue.pop();
block_order.push_back(block.entry); block_order.push_back(block.entry);
for(ut64 edge : block.exits) for(ut64 edge : block.exits)
@ -131,22 +139,22 @@ void GraphView::computeGraph(ut64 entry)
} }
// Some edges might not be available // Some edges might not be available
if(!this->blocks.count(edge)) if(!blocks.count(edge))
{ {
continue; continue;
} }
// If this node has no other incoming edges, add it to the graph layout // If this node has no other incoming edges, add it to the graph layout
if(this->blocks[edge].incoming.size() == 1) if(blocks[edge].incoming.size() == 1)
{ {
removeFromVec(this->blocks[edge].incoming, block.entry); removeFromVec(blocks[edge].incoming, block.entry);
block.new_exits.push_back(edge); block.new_exits.push_back(edge);
queue.push(this->blocks[edge].entry); queue.push(blocks[edge].entry);
visited.insert(edge); visited.insert(edge);
changed = true; changed = true;
} else { } else {
// Remove from incoming edges // Remove from incoming edges
removeFromVec(this->blocks[edge].incoming, block.entry); removeFromVec(blocks[edge].incoming, block.entry);
} }
} }
} }
@ -155,7 +163,7 @@ void GraphView::computeGraph(ut64 entry)
ut64 best = 0; ut64 best = 0;
int best_edges; int best_edges;
ut64 best_parent; ut64 best_parent;
for(auto & blockIt : this->blocks) for(auto & blockIt : blocks)
{ {
GraphBlock &block = blockIt.second; GraphBlock &block = blockIt.second;
// Skip blocks we haven't visited yet // Skip blocks we haven't visited yet
@ -170,24 +178,24 @@ void GraphView::computeGraph(ut64 entry)
{ {
continue; continue;
} }
if(!this->blocks.count(edge)) if(!blocks.count(edge))
{ {
continue; continue;
} }
// find best edge // find best edge
if((best == 0) || ((int)this->blocks[edge].incoming.size() < best_edges) || ( if((best == 0) || ((int)blocks[edge].incoming.size() < best_edges) || (
((int)this->blocks[edge].incoming.size() == best_edges) && (edge < best))) ((int)blocks[edge].incoming.size() == best_edges) && (edge < best)))
{ {
best = edge; best = edge;
best_edges = this->blocks[edge].incoming.size(); best_edges = blocks[edge].incoming.size();
best_parent = block.entry; best_parent = block.entry;
} }
} }
} }
if(best != 0) if(best != 0)
{ {
GraphBlock &best_parentb = this->blocks[best_parent]; GraphBlock &best_parentb = blocks[best_parent];
removeFromVec(this->blocks[best].incoming, best_parentb.entry); removeFromVec(blocks[best].incoming, best_parentb.entry);
best_parentb.new_exits.push_back(best); best_parentb.new_exits.push_back(best);
visited.insert(best); visited.insert(best);
queue.push(best); queue.push(best);
@ -195,10 +203,10 @@ void GraphView::computeGraph(ut64 entry)
} }
} }
this->computeGraphLayout(this->blocks[entry]); computeGraphLayout(blocks[entry]);
// Prepare edge routing // Prepare edge routing
GraphBlock &entryb = this->blocks[entry]; GraphBlock &entryb = blocks[entry];
EdgesVector horiz_edges, vert_edges; EdgesVector horiz_edges, vert_edges;
horiz_edges.resize(entryb.row_count + 1); horiz_edges.resize(entryb.row_count + 1);
vert_edges.resize(entryb.row_count + 1); vert_edges.resize(entryb.row_count + 1);
@ -216,7 +224,7 @@ void GraphView::computeGraph(ut64 entry)
} }
} }
for(auto & blockIt : this->blocks) for(auto & blockIt : blocks)
{ {
GraphBlock &block = blockIt.second; GraphBlock &block = blockIt.second;
edge_valid[block.row][block.col + 1] = false; edge_valid[block.row][block.col + 1] = false;
@ -229,8 +237,8 @@ void GraphView::computeGraph(ut64 entry)
GraphBlock &start = block; GraphBlock &start = block;
for(ut64 edge : block.exits) for(ut64 edge : block.exits)
{ {
GraphBlock &end = this->blocks[edge]; GraphBlock &end = blocks[edge];
start.edges.push_back(this->routeEdge(horiz_edges, vert_edges, edge_valid, start, end, QColor(255, 0, 0))); start.edges.push_back(routeEdge(horiz_edges, vert_edges, edge_valid, start, end, QColor(255, 0, 0)));
} }
} }
@ -254,7 +262,7 @@ void GraphView::computeGraph(ut64 entry)
std::vector<int> col_width, row_height; std::vector<int> col_width, row_height;
initVec(col_width, entryb.col_count + 1, 0); initVec(col_width, entryb.col_count + 1, 0);
initVec(row_height, entryb.row_count + 1, 0); initVec(row_height, entryb.row_count + 1, 0);
for(auto & blockIt : this->blocks) for(auto & blockIt : blocks)
{ {
GraphBlock &block = blockIt.second; GraphBlock &block = blockIt.second;
if((int(block.width / 2)) > col_width[block.col]) if((int(block.width / 2)) > col_width[block.col])
@ -269,47 +277,52 @@ void GraphView::computeGraph(ut64 entry)
std::vector<int> col_x, row_y; std::vector<int> col_x, row_y;
initVec(col_x, entryb.col_count, 0); initVec(col_x, entryb.col_count, 0);
initVec(row_y, entryb.row_count, 0); initVec(row_y, entryb.row_count, 0);
initVec(this->col_edge_x, entryb.col_count + 1, 0); initVec(col_edge_x, entryb.col_count + 1, 0);
initVec(this->row_edge_y, entryb.row_count + 1, 0); initVec(row_edge_y, entryb.row_count + 1, 0);
int x = 16; int x = block_horizontal_margin * 2;
for(int i = 0; i < entryb.col_count; i++) for(int i = 0; i < entryb.col_count; i++)
{ {
this->col_edge_x[i] = x; col_edge_x[i] = x;
x += 8 * col_edge_count[i]; x += block_horizontal_margin * col_edge_count[i];
col_x[i] = x; col_x[i] = x;
x += col_width[i]; x += col_width[i];
} }
int y = 16; int y = block_vertical_margin * 2;
for(int i = 0; i < entryb.row_count; i++) for(int i = 0; i < entryb.row_count; i++)
{ {
this->row_edge_y[i] = y; row_edge_y[i] = y;
// TODO: The 1 when row_edge_count is 0 is not needed on the original.. not sure why it's required for us
if(!row_edge_count[i])
{
row_edge_count[i] = 1;
}
y += block_vertical_margin * row_edge_count[i]; y += block_vertical_margin * row_edge_count[i];
row_y[i] = y; row_y[i] = y;
y += row_height[i]; y += row_height[i];
} }
this->col_edge_x[entryb.col_count] = x; col_edge_x[entryb.col_count] = x;
this->row_edge_y[entryb.row_count] = y; row_edge_y[entryb.row_count] = y;
this->width = x + 16 + (8 * col_edge_count[entryb.col_count]); width = x + (block_horizontal_margin * 2) + (block_horizontal_margin * col_edge_count[entryb.col_count]);
this->height = y + 16 + (8 * row_edge_count[entryb.row_count]); height = y + (block_vertical_margin * 2) + (block_vertical_margin * row_edge_count[entryb.row_count]);
//Compute node positions //Compute node positions
for(auto & blockIt : this->blocks) for(auto & blockIt : blocks)
{ {
GraphBlock &block = blockIt.second; GraphBlock &block = blockIt.second;
block.x = int( block.x = int(
(col_x[block.col] + col_width[block.col] + 4 * col_edge_count[block.col + 1]) - (block.width / 2)); (col_x[block.col] + col_width[block.col] + ((block_horizontal_margin / 2) * col_edge_count[block.col + 1])) - (block.width / 2));
if((block.x + block.width) > ( if((block.x + block.width) > (
col_x[block.col] + col_width[block.col] + col_width[block.col + 1] + 8 * col_edge_count[ col_x[block.col] + col_width[block.col] + col_width[block.col + 1] + block_horizontal_margin * col_edge_count[
block.col + 1])) block.col + 1]))
{ {
block.x = int((col_x[block.col] + col_width[block.col] + col_width[block.col + 1] + 8 * col_edge_count[ block.x = int((col_x[block.col] + col_width[block.col] + col_width[block.col + 1] + block_horizontal_margin * col_edge_count[
block.col + 1]) - block.width); block.col + 1]) - block.width);
} }
block.y = row_y[block.row]; block.y = row_y[block.row];
} }
// Precompute coordinates for edges // Precompute coordinates for edges
for(auto & blockIt : this->blocks) for(auto & blockIt : blocks)
{ {
GraphBlock &block = blockIt.second; GraphBlock &block = blockIt.second;
@ -318,7 +331,8 @@ void GraphView::computeGraph(ut64 entry)
auto start = edge.points[0]; auto start = edge.points[0];
auto start_col = start.col; auto start_col = start.col;
auto last_index = edge.start_index; auto last_index = edge.start_index;
auto first_pt = QPoint(this->col_edge_x[start_col] + (8 * last_index) + 4, // This is the start point of the edge.
auto first_pt = QPoint(col_edge_x[start_col] + (block_horizontal_margin * last_index) + (block_horizontal_margin / 2),
block.y + block.height); block.y + block.height);
auto last_pt = first_pt; auto last_pt = first_pt;
QPolygonF pts; QPolygonF pts;
@ -333,9 +347,9 @@ void GraphView::computeGraph(ut64 entry)
QPoint new_pt; QPoint new_pt;
// block_vertical_margin/2 gives the margin from block to the horizontal lines // block_vertical_margin/2 gives the margin from block to the horizontal lines
if(start_col == end_col) if(start_col == end_col)
new_pt = QPoint(last_pt.x(), this->row_edge_y[end_row] + (8 * last_index) + (block_vertical_margin/2)); new_pt = QPoint(last_pt.x(), row_edge_y[end_row] + (block_vertical_margin * last_index) + (block_vertical_margin/2));
else else
new_pt = QPoint(this->col_edge_x[end_col] + (8 * last_index) + 4, last_pt.y()); new_pt = QPoint(col_edge_x[end_col] + (block_horizontal_margin * last_index) + (block_horizontal_margin/2), last_pt.y());
pts.push_back(new_pt); pts.push_back(new_pt);
last_pt = new_pt; last_pt = new_pt;
start_col = end_col; start_col = end_col;
@ -355,6 +369,8 @@ void GraphView::computeGraph(ut64 entry)
pts.append(first_pt); pts.append(first_pt);
edge.arrow_start = pts; edge.arrow_start = pts;
} }
if(ec.end_arrow)
{
pts.clear(); pts.clear();
pts.append(QPoint(new_pt.x() - 3, new_pt.y() - 6)); pts.append(QPoint(new_pt.x() - 3, new_pt.y() - 6));
pts.append(QPoint(new_pt.x() + 3, new_pt.y() - 6)); pts.append(QPoint(new_pt.x() + 3, new_pt.y() - 6));
@ -362,35 +378,55 @@ void GraphView::computeGraph(ut64 entry)
edge.arrow_end = pts; edge.arrow_end = pts;
} }
} }
}
this->ready = true; ready = true;
this->viewport()->update(); viewport()->update();
areaSize = this->viewport()->size(); areaSize = viewport()->size();
this->adjustSize(areaSize.width(), areaSize.height()); adjustSize(areaSize.width(), areaSize.height());
} }
void GraphView::paintEvent(QPaintEvent* event) void GraphView::paintEvent(QPaintEvent* event)
{ {
Q_UNUSED(event); Q_UNUSED(event);
QPainter p(this->viewport()); QPainter p(viewport());
int render_offset_x = -this->horizontalScrollBar()->value() * current_scale; int render_offset_x = -horizontalScrollBar()->value() * current_scale;
int render_offset_y = -this->verticalScrollBar()->value() * current_scale; int render_offset_y = -verticalScrollBar()->value() * current_scale;
int render_width = this->viewport()->size().width() / current_scale; int render_width = viewport()->size().width() / current_scale;
int render_height = this->viewport()->size().height() / current_scale; int render_height = viewport()->size().height() / current_scale;
// Do we have scrollbars?
bool hscrollbar = horizontalScrollBar()->pageStep() < width;
bool vscrollbar = verticalScrollBar()->pageStep() < height;
// Draw background // Draw background
QRect viewportRect(this->viewport()->rect().topLeft(), this->viewport()->rect().bottomRight() - QPoint(1, 1)); QRect viewportRect(viewport()->rect().topLeft(), viewport()->rect().bottomRight() - QPoint(1, 1));
p.setBrush(backgroundColor); p.setBrush(backgroundColor);
p.drawRect(viewportRect); p.drawRect(viewportRect);
p.setBrush(Qt::black); p.setBrush(Qt::black);
unscrolled_render_offset_x = 0;
unscrolled_render_offset_y = 0;
// We do not have a scrollbar on this axis, so we center the view
if(!hscrollbar)
{
unscrolled_render_offset_x = (viewport()->size().width() - (width * current_scale)) / 2;
render_offset_x += unscrolled_render_offset_x;
}
if(!vscrollbar)
{
unscrolled_render_offset_y = (viewport()->size().height() - (height * current_scale)) / 2;
render_offset_y += unscrolled_render_offset_y;
}
p.translate(render_offset_x, render_offset_y); p.translate(render_offset_x, render_offset_y);
p.scale(current_scale, current_scale); p.scale(current_scale, current_scale);
// Draw blocks // Draw blocks
for(auto & blockIt : this->blocks) for(auto & blockIt : blocks)
{ {
GraphBlock &block = blockIt.second; GraphBlock &block = blockIt.second;
@ -429,7 +465,6 @@ void GraphView::paintEvent(QPaintEvent* event)
p.drawConvexPolygon(edge.arrow_end); p.drawConvexPolygon(edge.arrow_end);
} }
} }
} }
} }
@ -446,34 +481,34 @@ void GraphView::computeGraphLayout(GraphBlock &block)
for(size_t i = 0; i < block.new_exits.size(); i++) for(size_t i = 0; i < block.new_exits.size(); i++)
{ {
ut64 edge = block.new_exits[i]; ut64 edge = block.new_exits[i];
GraphBlock &edgeb = this->blocks[edge]; GraphBlock &edgeb = blocks[edge];
this->computeGraphLayout(edgeb); computeGraphLayout(edgeb);
row_count = std::max(edgeb.row_count + 1, row_count); row_count = std::max(edgeb.row_count + 1, row_count);
childColumn = edgeb.col; childColumn = edgeb.col;
} }
if(this->layoutType != LayoutType::Wide && block.new_exits.size() == 2) if(layoutType != LayoutType::Wide && block.new_exits.size() == 2)
{ {
GraphBlock &left = this->blocks[block.new_exits[0]]; GraphBlock &left = blocks[block.new_exits[0]];
GraphBlock &right= this->blocks[block.new_exits[1]]; GraphBlock &right= blocks[block.new_exits[1]];
if(left.new_exits.size() == 0) if(left.new_exits.size() == 0)
{ {
left.col = right.col - 2; left.col = right.col - 2;
int add = left.col < 0 ? - left.col : 0; int add = left.col < 0 ? - left.col : 0;
this->adjustGraphLayout(right, add, 1); adjustGraphLayout(right, add, 1);
this->adjustGraphLayout(left, add, 1); adjustGraphLayout(left, add, 1);
col = right.col_count + add; col = right.col_count + add;
} }
else if(right.new_exits.size() == 0) else if(right.new_exits.size() == 0)
{ {
this->adjustGraphLayout(left, 0, 1); adjustGraphLayout(left, 0, 1);
this->adjustGraphLayout(right, left.col + 2, 1); adjustGraphLayout(right, left.col + 2, 1);
col = std::max(left.col_count, right.col + 2); col = std::max(left.col_count, right.col + 2);
} }
else else
{ {
this->adjustGraphLayout(left, 0, 1); adjustGraphLayout(left, 0, 1);
this->adjustGraphLayout(right, left.col_count, 1); adjustGraphLayout(right, left.col_count, 1);
col = left.col_count + right.col_count; col = left.col_count + right.col_count;
} }
block.col_count = std::max(2, col); block.col_count = std::max(2, col);
@ -490,8 +525,8 @@ void GraphView::computeGraphLayout(GraphBlock &block)
{ {
for(ut64 edge : block.new_exits) for(ut64 edge : block.new_exits)
{ {
this->adjustGraphLayout(this->blocks[edge], col, 1); adjustGraphLayout(blocks[edge], col, 1);
col += this->blocks[edge].col_count; col += blocks[edge].col_count;
} }
if(col >= 2) if(col >= 2)
{ {
@ -536,11 +571,11 @@ GraphView::GraphEdge GraphView::routeEdge(EdgesVector & horiz_edges, EdgesVector
int i = 0; int i = 0;
while(true) while(true)
{ {
if(!this->isEdgeMarked(vert_edges, start.row + 1, start.col + 1, i)) if(!isEdgeMarked(vert_edges, start.row + 1, start.col + 1, i))
break; break;
i += 1; i += 1;
} }
this->markEdge(vert_edges, start.row + 1, start.col + 1, i); markEdge(vert_edges, start.row + 1, start.col + 1, i);
edge.addPoint(start.row + 1, start.col + 1); edge.addPoint(start.row + 1, start.col + 1);
edge.start_index = i; edge.start_index = i;
bool horiz = false; bool horiz = false;
@ -617,7 +652,7 @@ GraphView::GraphEdge GraphView::routeEdge(EdgesVector & horiz_edges, EdgesVector
min_col = start.col + 1; min_col = start.col + 1;
max_col = col; max_col = col;
} }
int index = this->findHorizEdgeIndex(horiz_edges, start.row + 1, min_col, max_col); int index = findHorizEdgeIndex(horiz_edges, start.row + 1, min_col, max_col);
edge.addPoint(start.row + 1, col, index); edge.addPoint(start.row + 1, col, index);
horiz = true; horiz = true;
} }
@ -626,8 +661,8 @@ GraphView::GraphEdge GraphView::routeEdge(EdgesVector & horiz_edges, EdgesVector
{ {
//Not in same row, need to generate a line for moving to the correct row //Not in same row, need to generate a line for moving to the correct row
if(col == (start.col + 1)) if(col == (start.col + 1))
this->markEdge(vert_edges, start.row + 1, start.col + 1, i, false); markEdge(vert_edges, start.row + 1, start.col + 1, i, false);
int index = this->findVertEdgeIndex(vert_edges, col, min_row, max_row); int index = findVertEdgeIndex(vert_edges, col, min_row, max_row);
if(col == (start.col + 1)) if(col == (start.col + 1))
edge.start_index = index; edge.start_index = index;
edge.addPoint(end.row, col, index); edge.addPoint(end.row, col, index);
@ -648,7 +683,7 @@ GraphView::GraphEdge GraphView::routeEdge(EdgesVector & horiz_edges, EdgesVector
min_col = end.col + 1; min_col = end.col + 1;
max_col = col; max_col = col;
} }
int index = this->findHorizEdgeIndex(horiz_edges, end.row, min_col, max_col); int index = findHorizEdgeIndex(horiz_edges, end.row, min_col, max_col);
edge.addPoint(end.row, end.col + 1, index); edge.addPoint(end.row, end.col + 1, index);
horiz = true; horiz = true;
} }
@ -656,7 +691,7 @@ GraphView::GraphEdge GraphView::routeEdge(EdgesVector & horiz_edges, EdgesVector
//If last line was horizontal, choose the ending edge index for the incoming edge //If last line was horizontal, choose the ending edge index for the incoming edge
if(horiz) if(horiz)
{ {
int index = this->findVertEdgeIndex(vert_edges, end.col + 1, end.row, end.row); int index = findVertEdgeIndex(vert_edges, end.col + 1, end.row, end.row);
edge.points[int(edge.points.size()) - 1].index = index; edge.points[int(edge.points.size()) - 1].index = index;
} }
@ -683,7 +718,7 @@ int GraphView::findHorizEdgeIndex(EdgesVector & edges, int row, int min_col, int
//Mark chosen index as used //Mark chosen index as used
for(int col = min_col; col < max_col + 1; col++) for(int col = min_col; col < max_col + 1; col++)
this->markEdge(edges, row, col, i); markEdge(edges, row, col, i);
return i; return i;
} }
@ -707,7 +742,7 @@ int GraphView::findVertEdgeIndex(EdgesVector & edges, int col, int min_row, int
//Mark chosen index as used //Mark chosen index as used
for(int row = min_row; row < max_row + 1; row++) for(int row = min_row; row < max_row + 1; row++)
this->markEdge(edges, row, col, i); markEdge(edges, row, col, i);
return i; return i;
} }
@ -730,30 +765,30 @@ void GraphView::showBlock(GraphBlock *block, bool animated)
target_x = std::max(0, target_x); target_x = std::max(0, target_x);
target_y = std::max(0, target_y); target_y = std::max(0, target_y);
target_x = std::min(this->horizontalScrollBar()->maximum(), target_x); target_x = std::min(horizontalScrollBar()->maximum(), target_x);
target_y = std::min(this->verticalScrollBar()->maximum(), target_y); target_y = std::min(verticalScrollBar()->maximum(), target_y);
if(animated) if(animated)
{ {
QPropertyAnimation *animation_x = new QPropertyAnimation(this->horizontalScrollBar(), "value"); QPropertyAnimation *animation_x = new QPropertyAnimation(horizontalScrollBar(), "value");
animation_x->setDuration(500); animation_x->setDuration(500);
animation_x->setStartValue(this->horizontalScrollBar()->value()); animation_x->setStartValue(horizontalScrollBar()->value());
animation_x->setEndValue(target_x); animation_x->setEndValue(target_x);
animation_x->setEasingCurve(QEasingCurve::InOutQuad); animation_x->setEasingCurve(QEasingCurve::InOutQuad);
animation_x->start(); animation_x->start();
QPropertyAnimation *animation_y = new QPropertyAnimation(this->verticalScrollBar(), "value"); QPropertyAnimation *animation_y = new QPropertyAnimation(verticalScrollBar(), "value");
animation_y->setDuration(500); animation_y->setDuration(500);
animation_y->setStartValue(this->verticalScrollBar()->value()); animation_y->setStartValue(verticalScrollBar()->value());
animation_y->setEndValue(target_y); animation_y->setEndValue(target_y);
animation_y->setEasingCurve(QEasingCurve::InOutQuad); animation_y->setEasingCurve(QEasingCurve::InOutQuad);
animation_y->start(); animation_y->start();
} else { } else {
this->horizontalScrollBar()->setValue(target_x); horizontalScrollBar()->setValue(target_x);
this->verticalScrollBar()->setValue(target_y); verticalScrollBar()->setValue(target_y);
} }
blockTransitionedTo(block); blockTransitionedTo(block);
this->viewport()->update(); viewport()->update();
} }
void GraphView::adjustGraphLayout(GraphBlock &block, int col, int row) void GraphView::adjustGraphLayout(GraphBlock &block, int col, int row)
@ -762,18 +797,18 @@ void GraphView::adjustGraphLayout(GraphBlock &block, int col, int row)
block.row += row; block.row += row;
for(ut64 edge : block.new_exits) for(ut64 edge : block.new_exits)
{ {
this->adjustGraphLayout(this->blocks[edge], col, row); adjustGraphLayout(blocks[edge], col, row);
} }
} }
void GraphView::addBlock(GraphView::GraphBlock block) void GraphView::addBlock(GraphView::GraphBlock block)
{ {
this->blocks[block.entry] = block; blocks[block.entry] = block;
} }
void GraphView::setEntry(ut64 e) void GraphView::setEntry(ut64 e)
{ {
this->entry = e; entry = e;
} }
bool GraphView::checkPointClicked(QPointF &point, int x, int y, bool above_y) bool GraphView::checkPointClicked(QPointF &point, int x, int y, bool above_y)
@ -782,7 +817,7 @@ bool GraphView::checkPointClicked(QPointF &point, int x, int y, bool above_y)
if((point.x() - half_target_size < x) && if((point.x() - half_target_size < x) &&
(point.y() - (above_y ? (2 * half_target_size) : 0) < y) && (point.y() - (above_y ? (2 * half_target_size) : 0) < y) &&
(x < point.x() + half_target_size) && (x < point.x() + half_target_size) &&
(y < point.y() + (above_y ? 0 : (2 * half_target_size)))) (y < point.y() + (above_y ? 0 : (3 * half_target_size))))
{ {
return true; return true;
} }
@ -798,8 +833,8 @@ void GraphView::resizeEvent(QResizeEvent* event)
// Mouse events // Mouse events
void GraphView::mousePressEvent(QMouseEvent *event) void GraphView::mousePressEvent(QMouseEvent *event)
{ {
int x = (event->pos().x() / current_scale) + horizontalScrollBar()->value(); int x = ((event->pos().x() - unscrolled_render_offset_x) / current_scale) + horizontalScrollBar()->value();
int y = (event->pos().y() / current_scale) + verticalScrollBar()->value(); int y = ((event->pos().y() - unscrolled_render_offset_y) / current_scale) + verticalScrollBar()->value();
// Check if a block was clicked // Check if a block was clicked
for(auto & blockIt : blocks) for(auto & blockIt : blocks)
@ -818,7 +853,7 @@ void GraphView::mousePressEvent(QMouseEvent *event)
} }
// Check if a line beginning/end was clicked // Check if a line beginning/end was clicked
for(auto & blockIt : this->blocks) for(auto & blockIt : blocks)
{ {
GraphBlock &block = blockIt.second; GraphBlock &block = blockIt.second;
for(GraphEdge & edge : block.edges) for(GraphEdge & edge : block.edges)
@ -850,25 +885,45 @@ void GraphView::mousePressEvent(QMouseEvent *event)
if(event->button() == Qt::LeftButton) if(event->button() == Qt::LeftButton)
{ {
//Left click outside any block, enter scrolling mode //Left click outside any block, enter scrolling mode
this->scroll_base_x = event->x(); scroll_base_x = event->x();
this->scroll_base_y = event->y(); scroll_base_y = event->y();
this->scroll_mode = true; scroll_mode = true;
this->setCursor(Qt::ClosedHandCursor); setCursor(Qt::ClosedHandCursor);
this->viewport()->grabMouse(); viewport()->grabMouse();
} }
} }
void GraphView::mouseMoveEvent(QMouseEvent* event) void GraphView::mouseMoveEvent(QMouseEvent* event)
{ {
if(this->scroll_mode) if(scroll_mode)
{ {
int x_delta = this->scroll_base_x - event->x(); int x_delta = scroll_base_x - event->x();
int y_delta = this->scroll_base_y - event->y(); int y_delta = scroll_base_y - event->y();
this->scroll_base_x = event->x(); scroll_base_x = event->x();
this->scroll_base_y = event->y(); scroll_base_y = event->y();
this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + x_delta); horizontalScrollBar()->setValue(horizontalScrollBar()->value() + x_delta);
this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() + y_delta); verticalScrollBar()->setValue(verticalScrollBar()->value() + y_delta);
}
}
void GraphView::mouseDoubleClickEvent(QMouseEvent *event)
{
int x = ((event->pos().x() - unscrolled_render_offset_x) / current_scale) + horizontalScrollBar()->value();
int y = ((event->pos().y() - unscrolled_render_offset_y) / current_scale) + verticalScrollBar()->value();
// Check if a block was clicked
for(auto & blockIt : blocks)
{
GraphBlock &block = blockIt.second;
if((block.x <= x) && (block.y <= y) &&
(x <= block.x + block.width) & (y <= block.y + block.height))
{
QPoint pos = QPoint(x - block.x, y - block.y);
blockDoubleClicked(block, event, pos);
return;
}
} }
} }
@ -883,10 +938,10 @@ void GraphView::mouseReleaseEvent(QMouseEvent* event)
if(event->button() != Qt::LeftButton) if(event->button() != Qt::LeftButton)
return; return;
if(this->scroll_mode) if(scroll_mode)
{ {
this->scroll_mode = false; scroll_mode = false;
this->setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);
this->viewport()->releaseMouse(); viewport()->releaseMouse();
} }
} }

View File

@ -6,6 +6,7 @@
#include <QWidget> #include <QWidget>
#include <QAbstractScrollArea> #include <QAbstractScrollArea>
#include <QScrollBar> #include <QScrollBar>
#include <QElapsedTimer>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@ -19,8 +20,8 @@ class GraphView : public QAbstractScrollArea
enum class LayoutType enum class LayoutType
{ {
Wide,
Medium, Medium,
Wide,
Narrow, Narrow,
}; };
public: public:
@ -101,10 +102,17 @@ protected:
QColor backgroundColor = QColor(Qt::white); QColor backgroundColor = QColor(Qt::white);
// The vertical margin between blocks // The vertical margin between blocks
int block_vertical_margin = 32; int block_vertical_margin = 32;
int block_horizontal_margin = 10;
// Padding inside the block
int block_padding = 16;
// Zoom data // Zoom data
double current_scale = 1.0; double current_scale = 1.0;
int unscrolled_render_offset_x = 0;
int unscrolled_render_offset_y = 0;
void addBlock(GraphView::GraphBlock block); void addBlock(GraphView::GraphBlock block);
void setEntry(ut64 e); void setEntry(ut64 e);
void computeGraph(ut64 entry); void computeGraph(ut64 entry);
@ -112,6 +120,7 @@ protected:
// Callbacks that should be overridden // Callbacks that should be overridden
virtual void drawBlock(QPainter & p, GraphView::GraphBlock &block); virtual void drawBlock(QPainter & p, GraphView::GraphBlock &block);
virtual void blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos); virtual void blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos);
virtual void blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos);
virtual void blockTransitionedTo(GraphView::GraphBlock *to); virtual void blockTransitionedTo(GraphView::GraphBlock *to);
virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to); virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to);
@ -158,6 +167,7 @@ private slots:
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
}; };