1
0
Fork 0

GUI: copy graph view from cutter

This commit is contained in:
Duncan Ogilvie 2018-02-15 11:03:54 +01:00
parent b10968e956
commit ddbbd3bbf6
No known key found for this signature in database
GPG Key ID: FC89E0AAA0C1AAD8
9 changed files with 4203 additions and 2102 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,74 +1,36 @@
#ifndef DISASSEMBLERGRAPHVIEW_H
#define DISASSEMBLERGRAPHVIEW_H
#include <QObject>
// Based on the DisassemblerGraphView from x64dbg
#include <QWidget>
#include <QAbstractScrollArea>
#include <QPaintEvent>
#include <QTimer>
#include <QSize>
#include <QResizeEvent>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <QMutex>
#include "Bridge.h"
#include <QPainter>
#include <QShortcut>
#include "GraphView.h"
#include "RichTextPainter.h"
#include "QBeaEngine.h"
#include "ActionHelpers.h"
#include "VaHistory.h"
#include "CachedFontMetrics.h"
class MenuBuilder;
class CachedFontMetrics;
class GotoDialog;
class XrefBrowseDialog;
using RVA = size_t;
class DisassemblerGraphView : public QAbstractScrollArea, public ActionHelper<DisassemblerGraphView>
class DisassemblerGraphView : public GraphView
{
Q_OBJECT
public:
struct DisassemblerBlock;
struct Point
{
int row; //point[0]
int col; //point[1]
int index; //point[2]
};
struct DisassemblerEdge
{
QColor color;
DisassemblerBlock* dest;
std::vector<Point> points;
int start_index = 0;
QPolygonF polyline;
QPolygonF arrow;
void addPoint(int row, int col, int index = 0)
{
Point point = {row, col, 0};
this->points.push_back(point);
if(int(this->points.size()) > 1)
this->points[this->points.size() - 2].index = index;
}
};
struct Token
{
int start; //token[0]
int length; //token[1]
QString type; //token[2]
duint addr; //token[3]
ut64 addr; //token[3]
QString name; //token[4]
};
struct HighlightToken
{
QString type; //highlight_token[0]
duint addr; //highlight_token[1]
ut64 addr; //highlight_token[1]
QString name; //highlight_token[2]
bool equalsToken(const Token & token)
@ -123,65 +85,48 @@ public:
result += t.text;
}
}
return std::move(result);
return result;
}
};
struct Instr
{
duint addr = 0;
ut64 addr = 0;
ut64 size = 0;
Text text;
Text fullText;
std::vector<unsigned char> opcode; //instruction bytes
};
struct Block
struct DisassemblyBlock
{
Text header_text;
std::vector<Instr> instrs;
std::vector<duint> exits;
duint entry = 0;
duint true_path = 0;
duint false_path = 0;
ut64 entry = 0;
ut64 true_path = 0;
ut64 false_path = 0;
bool terminal = false;
bool indirectcall = false;
};
struct DisassemblerBlock
{
DisassemblerBlock() {}
explicit DisassemblerBlock(Block & block)
: block(block) {}
Block block;
std::vector<DisassemblerEdge> edges;
std::vector<duint> incoming;
std::vector<duint> new_exits;
qreal x = 0.0;
qreal y = 0.0;
int width = 0;
int height = 0;
int col = 0;
int col_count = 0;
int row = 0;
int row_count = 0;
};
struct Function
{
bool ready;
duint entry;
std::vector<Block> blocks;
ut64 entry;
ut64 update_id;
std::vector<DisassemblyBlock> blocks;
};
struct Analysis
{
duint entry = 0;
std::unordered_map<duint, Function> functions;
ut64 entry = 0;
std::unordered_map<ut64, Function> functions;
bool ready = false;
ut64 update_id = 0;
QString status = "Analyzing...";
bool find_instr(duint addr, duint & func, duint & instr)
bool find_instr(ut64 addr, ut64 & func, ut64 & instr)
{
//TODO implement
Q_UNUSED(addr);
@ -193,132 +138,67 @@ public:
//dummy class
};
enum class LayoutType
{
Wide,
Medium,
Narrow,
};
DisassemblerGraphView(QWidget* parent = nullptr);
public:
DisassemblerGraphView(QWidget* parent);
~DisassemblerGraphView();
void initFont();
void adjustSize(int width, int height);
void resizeEvent(QResizeEvent* event);
duint get_cursor_pos();
void set_cursor_pos(duint addr);
std::tuple<duint, duint> get_selection_range();
void set_selection_range(std::tuple<duint, duint> range);
void copy_address();
//void analysis_thread_proc();
//void closeRequest();
void paintNormal(QPainter & p, QRect & viewportRect, int xofs, int yofs);
void paintOverview(QPainter & p, QRect & viewportRect, int xofs, int yofs);
void paintEvent(QPaintEvent* event);
bool isMouseEventInBlock(QMouseEvent* event);
duint getInstrForMouseEvent(QMouseEvent* event);
bool getTokenForMouseEvent(QMouseEvent* event, Token & token);
bool find_instr(duint addr, Instr & instr);
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void prepareGraphNode(DisassemblerBlock & block);
void adjustGraphLayout(DisassemblerBlock & block, int col, int row);
void computeGraphLayout(DisassemblerBlock & block);
void setupContextMenu();
void keyPressEvent(QKeyEvent* event);
std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks;
virtual void drawBlock(QPainter & p, GraphView::GraphBlock & block) override;
virtual void blockClicked(GraphView::GraphBlock & block, QMouseEvent* event, QPoint pos) override;
virtual void blockDoubleClicked(GraphView::GraphBlock & block, QMouseEvent* event, QPoint pos) override;
virtual bool helpEvent(QHelpEvent* event) override;
virtual void blockHelpEvent(GraphView::GraphBlock & block, QHelpEvent* event, QPoint pos) override;
virtual GraphView::EdgeConfiguration edgeConfiguration(GraphView::GraphBlock & from, GraphView::GraphBlock* to) override;
virtual void blockTransitionedTo(GraphView::GraphBlock* to) override;
template<typename T>
using Matrix = std::vector<std::vector<T>>;
using EdgesVector = Matrix<std::vector<bool>>;
bool isEdgeMarked(EdgesVector & edges, int row, int col, int index);
void markEdge(EdgesVector & edges, int row, int col, int index, bool used = true);
int findHorizEdgeIndex(EdgesVector & edges, int row, int min_col, int max_col);
int findVertEdgeIndex(EdgesVector & edges, int col, int min_row, int max_row);
DisassemblerEdge routeEdge(EdgesVector & horiz_edges, EdgesVector & vert_edges, Matrix<bool> & edge_valid, DisassemblerBlock & start, DisassemblerBlock & end, QColor color);
void renderFunction(Function & func);
void show_cur_instr(bool force = false);
bool navigate(duint addr);
void fontChanged();
void setGraphLayout(LayoutType layout);
VaHistory mHistory;
signals:
void displaySnowmanWidget();
void loadCurrentGraph();
// bool navigate(ut64 addr);
public slots:
void loadGraphSlot(BridgeCFGraphList* graph, duint addr);
void graphAtSlot(duint addr);
void updateGraphSlot();
void followDisassemblerSlot();
void refreshView();
void colorsUpdatedSlot();
void fontsUpdatedSlot();
void shortcutsUpdatedSlot();
void toggleOverviewSlot();
void toggleSummarySlot();
void selectionGetSlot(SELECTIONDATA* selection);
void tokenizerConfigUpdatedSlot();
void loadCurrentGraph();
void disassembleAtSlot(dsint va, dsint cip);
void gotoExpressionSlot();
void gotoOriginSlot();
void gotoPreviousSlot();
void gotoNextSlot();
void toggleSyncOriginSlot();
void followActionSlot();
void refreshSlot();
void saveImageSlot();
void setCommentSlot();
void setLabelSlot();
void xrefSlot();
void decompileSlot();
void onSeekChanged(RVA addr);
void zoomIn();
void zoomOut();
void zoomReset();
void takeTrue();
void takeFalse();
void nextInstr();
void prevInstr();
private slots:
void seekPrev();
private:
QString status;
Analysis analysis;
duint function;
QTimer* updateTimer;
int baseline;
bool first_draw = true;
bool transition_dont_seek = false;
bool sent_seek = false;
HighlightToken* highlight_token;
// Font data
CachedFontMetrics* mFontMetrics;
qreal charWidth;
int charHeight;
int charOffset;
int width;
int height;
int renderWidth;
int renderHeight;
int renderXOfs;
int renderYOfs;
duint cur_instr;
int scroll_base_x;
int scroll_base_y;
bool scroll_mode;
bool ready;
int* desired_pos;
std::unordered_map<duint, DisassemblerBlock> blocks;
HighlightToken* highlight_token;
std::vector<int> col_edge_x;
std::vector<int> row_edge_y;
CachedFontMetrics* mFontMetrics;
MenuBuilder* mMenuBuilder;
bool drawOverview;
bool onlySummary;
bool syncOrigin;
int overviewXOfs;
int overviewYOfs;
qreal overviewScale;
duint mCip;
bool forceCenter;
bool saveGraph;
bool mHistoryLock; //Don't add a history while going to previous/next
LayoutType layoutType;
int baseline;
QAction* mToggleOverview;
QAction* mToggleSummary;
QAction* mToggleSyncOrigin;
//DisassemblyContextMenu* mMenu;
void initFont();
void prepareGraphNode(GraphBlock & block);
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 seekInstruction(bool previous_instr);
//QList<QShortcut*> shortcuts;
QColor disassemblyBackgroundColor;
QColor disassemblySelectedBackgroundColor;
QColor disassemblySelectionColor;
QColor disassemblyTracedColor;
QColor disassemblyTracedSelectionColor;
@ -327,7 +207,6 @@ private:
QColor brfalseColor;
QColor retShadowColor;
QColor indirectcallShadowColor;
QColor backgroundColor;
QColor mAutoCommentColor;
QColor mAutoCommentBackgroundColor;
QColor mCommentColor;
@ -340,14 +219,6 @@ private:
QColor mCipColor;
QColor mBreakpointColor;
QColor mDisabledBreakpointColor;
BridgeCFGraph currentGraph;
std::unordered_map<duint, duint> currentBlockMap;
QBeaEngine disasm;
GotoDialog* mGoto;
XrefBrowseDialog* mXrefDlg;
void addReferenceAction(QMenu* menu, duint addr);
};
#endif // DISASSEMBLERGRAPHVIEW_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,353 @@
#ifndef DISASSEMBLERGRAPHVIEWOLD_H
#define DISASSEMBLERGRAPHVIEWOLD_H
#include <QObject>
#include <QWidget>
#include <QAbstractScrollArea>
#include <QPaintEvent>
#include <QTimer>
#include <QSize>
#include <QResizeEvent>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <QMutex>
#include "Bridge.h"
#include "RichTextPainter.h"
#include "QBeaEngine.h"
#include "ActionHelpers.h"
#include "VaHistory.h"
class MenuBuilder;
class CachedFontMetrics;
class GotoDialog;
class XrefBrowseDialog;
class DisassemblerGraphViewOld : public QAbstractScrollArea, public ActionHelper<DisassemblerGraphViewOld>
{
Q_OBJECT
public:
struct DisassemblerBlock;
struct Point
{
int row; //point[0]
int col; //point[1]
int index; //point[2]
};
struct DisassemblerEdge
{
QColor color;
DisassemblerBlock* dest;
std::vector<Point> points;
int start_index = 0;
QPolygonF polyline;
QPolygonF arrow;
void addPoint(int row, int col, int index = 0)
{
Point point = {row, col, 0};
this->points.push_back(point);
if(int(this->points.size()) > 1)
this->points[this->points.size() - 2].index = index;
}
};
struct Token
{
int start; //token[0]
int length; //token[1]
QString type; //token[2]
duint addr; //token[3]
QString name; //token[4]
};
struct HighlightToken
{
QString type; //highlight_token[0]
duint addr; //highlight_token[1]
QString name; //highlight_token[2]
bool equalsToken(const Token & token)
{
return this->type == token.type &&
this->addr == token.addr &&
this->name == token.name;
}
static HighlightToken* fromToken(const Token & token)
{
//TODO: memory leaks
auto result = new HighlightToken();
result->type = token.type;
result->addr = token.addr;
result->name = token.name;
return result;
}
};
struct Text
{
std::vector<RichTextPainter::List> lines;
Text() {}
Text(const QString & text, QColor color, QColor background)
{
RichTextPainter::List richText;
RichTextPainter::CustomRichText_t rt;
rt.highlight = false;
rt.text = text;
rt.textColor = color;
rt.textBackground = background;
rt.flags = rt.textBackground.alpha() ? RichTextPainter::FlagAll : RichTextPainter::FlagColor;
richText.push_back(rt);
lines.push_back(richText);
}
Text(const RichTextPainter::List & richText)
{
lines.push_back(richText);
}
QString ToQString() const
{
QString result;
for(auto & line : lines)
{
for(auto & t : line)
{
result += t.text;
}
}
return std::move(result);
}
};
struct Instr
{
duint addr = 0;
Text text;
std::vector<unsigned char> opcode; //instruction bytes
};
struct Block
{
Text header_text;
std::vector<Instr> instrs;
std::vector<duint> exits;
duint entry = 0;
duint true_path = 0;
duint false_path = 0;
bool terminal = false;
bool indirectcall = false;
};
struct DisassemblerBlock
{
DisassemblerBlock() {}
explicit DisassemblerBlock(Block & block)
: block(block) {}
Block block;
std::vector<DisassemblerEdge> edges;
std::vector<duint> incoming;
std::vector<duint> new_exits;
qreal x = 0.0;
qreal y = 0.0;
int width = 0;
int height = 0;
int col = 0;
int col_count = 0;
int row = 0;
int row_count = 0;
};
struct Function
{
bool ready;
duint entry;
std::vector<Block> blocks;
};
struct Analysis
{
duint entry = 0;
std::unordered_map<duint, Function> functions;
bool ready = false;
QString status = "Analyzing...";
bool find_instr(duint addr, duint & func, duint & instr)
{
//TODO implement
Q_UNUSED(addr);
Q_UNUSED(func);
Q_UNUSED(instr);
return false;
}
//dummy class
};
enum class LayoutType
{
Wide,
Medium,
Narrow,
};
DisassemblerGraphViewOld(QWidget* parent = nullptr);
~DisassemblerGraphViewOld();
void initFont();
void adjustSize(int width, int height);
void resizeEvent(QResizeEvent* event);
duint get_cursor_pos();
void set_cursor_pos(duint addr);
std::tuple<duint, duint> get_selection_range();
void set_selection_range(std::tuple<duint, duint> range);
void copy_address();
//void analysis_thread_proc();
//void closeRequest();
void paintNormal(QPainter & p, QRect & viewportRect, int xofs, int yofs);
void paintOverview(QPainter & p, QRect & viewportRect, int xofs, int yofs);
void paintEvent(QPaintEvent* event);
bool isMouseEventInBlock(QMouseEvent* event);
duint getInstrForMouseEvent(QMouseEvent* event);
bool getTokenForMouseEvent(QMouseEvent* event, Token & token);
bool find_instr(duint addr, Instr & instr);
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void prepareGraphNode(DisassemblerBlock & block);
void adjustGraphLayout(DisassemblerBlock & block, int col, int row);
void computeGraphLayout(DisassemblerBlock & block);
void setupContextMenu();
void keyPressEvent(QKeyEvent* event);
template<typename T>
using Matrix = std::vector<std::vector<T>>;
using EdgesVector = Matrix<std::vector<bool>>;
bool isEdgeMarked(EdgesVector & edges, int row, int col, int index);
void markEdge(EdgesVector & edges, int row, int col, int index, bool used = true);
int findHorizEdgeIndex(EdgesVector & edges, int row, int min_col, int max_col);
int findVertEdgeIndex(EdgesVector & edges, int col, int min_row, int max_row);
DisassemblerEdge routeEdge(EdgesVector & horiz_edges, EdgesVector & vert_edges, Matrix<bool> & edge_valid, DisassemblerBlock & start, DisassemblerBlock & end, QColor color);
void renderFunction(Function & func);
void show_cur_instr(bool force = false);
bool navigate(duint addr);
void fontChanged();
void setGraphLayout(LayoutType layout);
VaHistory mHistory;
signals:
void displaySnowmanWidget();
public slots:
void loadGraphSlot(BridgeCFGraphList* graph, duint addr);
void graphAtSlot(duint addr);
void updateGraphSlot();
void followDisassemblerSlot();
void colorsUpdatedSlot();
void fontsUpdatedSlot();
void shortcutsUpdatedSlot();
void toggleOverviewSlot();
void toggleSummarySlot();
void selectionGetSlot(SELECTIONDATA* selection);
void tokenizerConfigUpdatedSlot();
void loadCurrentGraph();
void disassembleAtSlot(dsint va, dsint cip);
void gotoExpressionSlot();
void gotoOriginSlot();
void gotoPreviousSlot();
void gotoNextSlot();
void toggleSyncOriginSlot();
void followActionSlot();
void refreshSlot();
void saveImageSlot();
void setCommentSlot();
void setLabelSlot();
void xrefSlot();
void decompileSlot();
private:
QString status;
Analysis analysis;
duint function;
QTimer* updateTimer;
int baseline;
qreal charWidth;
int charHeight;
int charOffset;
int width;
int height;
int renderWidth;
int renderHeight;
int renderXOfs;
int renderYOfs;
duint cur_instr;
int scroll_base_x;
int scroll_base_y;
bool scroll_mode;
bool ready;
int* desired_pos;
std::unordered_map<duint, DisassemblerBlock> blocks;
HighlightToken* highlight_token;
std::vector<int> col_edge_x;
std::vector<int> row_edge_y;
CachedFontMetrics* mFontMetrics;
MenuBuilder* mMenuBuilder;
bool drawOverview;
bool onlySummary;
bool syncOrigin;
int overviewXOfs;
int overviewYOfs;
qreal overviewScale;
duint mCip;
bool forceCenter;
bool saveGraph;
bool mHistoryLock; //Don't add a history while going to previous/next
LayoutType layoutType;
QAction* mToggleOverview;
QAction* mToggleSummary;
QAction* mToggleSyncOrigin;
QColor disassemblyBackgroundColor;
QColor disassemblySelectionColor;
QColor disassemblyTracedColor;
QColor disassemblyTracedSelectionColor;
QColor jmpColor;
QColor brtrueColor;
QColor brfalseColor;
QColor retShadowColor;
QColor indirectcallShadowColor;
QColor backgroundColor;
QColor mAutoCommentColor;
QColor mAutoCommentBackgroundColor;
QColor mCommentColor;
QColor mCommentBackgroundColor;
QColor mLabelColor;
QColor mLabelBackgroundColor;
QColor graphNodeColor;
QColor mAddressColor;
QColor mAddressBackgroundColor;
QColor mCipColor;
QColor mBreakpointColor;
QColor mDisabledBreakpointColor;
BridgeCFGraph currentGraph;
std::unordered_map<duint, duint> currentBlockMap;
QBeaEngine disasm;
GotoDialog* mGoto;
XrefBrowseDialog* mXrefDlg;
void addReferenceAction(QMenu* menu, duint addr);
};
#endif // DISASSEMBLERGRAPHVIEWOLD_H

View File

@ -0,0 +1,992 @@
#include "GraphView.h"
#include <vector>
#include <QPainter>
#include <QMouseEvent>
#include <QPropertyAnimation>
#include <QDebug>
GraphView::GraphView(QWidget* parent)
: QAbstractScrollArea(parent)
{
setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
horizontalScrollBar()->setSingleStep(charWidth);
verticalScrollBar()->setSingleStep(charWidth);
QSize areaSize = viewport()->size();
adjustSize(areaSize.width(), areaSize.height());
}
GraphView::~GraphView()
{
// TODO: Cleanups
}
// Vector functions
template<class T>
static void removeFromVec(std::vector<T> & vec, T elem)
{
vec.erase(std::remove(vec.begin(), vec.end(), elem), vec.end());
}
template<class T>
static void initVec(std::vector<T> & vec, size_t size, T value)
{
vec.resize(size);
for(size_t i = 0; i < size; i++)
vec[i] = value;
}
// Callbacks
void GraphView::drawBlock(QPainter & p, GraphView::GraphBlock & block)
{
Q_UNUSED(p);
Q_UNUSED(block);
qWarning() << "Draw block not overriden!";
}
void GraphView::blockClicked(GraphView::GraphBlock & block, QMouseEvent* event, QPoint pos)
{
Q_UNUSED(block);
Q_UNUSED(event);
Q_UNUSED(pos);
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::blockHelpEvent(GraphView::GraphBlock & block, QHelpEvent* event, QPoint pos)
{
Q_UNUSED(block);
Q_UNUSED(event);
Q_UNUSED(pos);
}
bool GraphView::helpEvent(QHelpEvent* 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();
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);
blockHelpEvent(block, event, pos);
return true;
}
}
return false;
}
void GraphView::blockTransitionedTo(GraphView::GraphBlock* to)
{
Q_UNUSED(to);
qWarning() << "blockTransitionedTo not overridden!";
}
GraphView::EdgeConfiguration GraphView::edgeConfiguration(GraphView::GraphBlock & from, GraphView::GraphBlock* to)
{
Q_UNUSED(from);
Q_UNUSED(to);
qWarning() << "Edge configuration not overridden!";
EdgeConfiguration ec;
return ec;
}
void GraphView::adjustSize(int new_width, int new_height)
{
double hfactor = 0.0;
double vfactor = 0.0;
if(horizontalScrollBar()->maximum())
{
hfactor = (double)horizontalScrollBar()->value() / (double)horizontalScrollBar()->maximum();
}
if(verticalScrollBar()->maximum())
{
vfactor = (double)verticalScrollBar()->value() / (double)verticalScrollBar()->maximum();
}
//Update scroll bar information
horizontalScrollBar()->setPageStep(new_width);
horizontalScrollBar()->setRange(0, width - (new_width / current_scale));
verticalScrollBar()->setPageStep(new_height);
verticalScrollBar()->setRange(0, height - (new_height / current_scale));
horizontalScrollBar()->setValue((int)((double)horizontalScrollBar()->maximum() * hfactor));
verticalScrollBar()->setValue((int)((double)verticalScrollBar()->maximum() * vfactor));
}
bool GraphView::event(QEvent* event)
{
if(event->type() == QEvent::ToolTip)
{
if(helpEvent(static_cast<QHelpEvent*>(event)))
{
return true;
}
}
return QAbstractScrollArea::event(event);
}
// This calculates the full graph starting at block entry.
void GraphView::computeGraph(ut64 entry)
{
QSize areaSize = viewport()->size();
// Populate incoming lists
for(auto & blockIt : blocks)
{
GraphBlock & block = blockIt.second;
for(auto & edge : block.exits)
{
blocks[edge].incoming.push_back(block.entry);
}
}
std::unordered_set<ut64> visited;
visited.insert(entry);
std::queue<ut64> queue;
std::vector<ut64> block_order;
queue.push(entry);
bool changed = true;
while(changed)
{
changed = false;
// Pick nodes with single entrypoints
while(!queue.empty())
{
GraphBlock & block = blocks[queue.front()];
queue.pop();
block_order.push_back(block.entry);
for(ut64 edge : block.exits)
{
// Skip edge if we already visited it
if(visited.count(edge))
{
continue;
}
// Some edges might not be available
if(!blocks.count(edge))
{
continue;
}
// If this node has no other incoming edges, add it to the graph layout
if(blocks[edge].incoming.size() == 1)
{
removeFromVec(blocks[edge].incoming, block.entry);
block.new_exits.push_back(edge);
queue.push(blocks[edge].entry);
visited.insert(edge);
changed = true;
}
else
{
// Remove from incoming edges
removeFromVec(blocks[edge].incoming, block.entry);
}
}
}
// No more nodes satisfy constraints, pick a node to continue constructing the graph
ut64 best = 0;
int best_edges;
ut64 best_parent;
for(auto & blockIt : blocks)
{
GraphBlock & block = blockIt.second;
// Skip blocks we haven't visited yet
if(!visited.count(block.entry))
{
continue;
}
for(ut64 edge : block.exits)
{
// If we already visited the exit, skip it
if(visited.count(edge))
{
continue;
}
if(!blocks.count(edge))
{
continue;
}
// find best edge
if((best == 0) || ((int)blocks[edge].incoming.size() < best_edges) || (
((int)blocks[edge].incoming.size() == best_edges) && (edge < best)))
{
best = edge;
best_edges = blocks[edge].incoming.size();
best_parent = block.entry;
}
}
}
if(best != 0)
{
GraphBlock & best_parentb = blocks[best_parent];
removeFromVec(blocks[best].incoming, best_parentb.entry);
best_parentb.new_exits.push_back(best);
visited.insert(best);
queue.push(best);
changed = true;
}
}
computeGraphLayout(blocks[entry]);
// Prepare edge routing
GraphBlock & entryb = blocks[entry];
EdgesVector horiz_edges, vert_edges;
horiz_edges.resize(entryb.row_count + 1);
vert_edges.resize(entryb.row_count + 1);
Matrix<bool> edge_valid;
edge_valid.resize(entryb.row_count + 1);
for(int row = 0; row < entryb.row_count + 1; row++)
{
horiz_edges[row].resize(entryb.col_count + 1);
vert_edges[row].resize(entryb.col_count + 1);
initVec(edge_valid[row], entryb.col_count + 1, true);
for(int col = 0; col < entryb.col_count + 1; col++)
{
horiz_edges[row][col].clear();
vert_edges[row][col].clear();
}
}
for(auto & blockIt : blocks)
{
GraphBlock & block = blockIt.second;
edge_valid[block.row][block.col + 1] = false;
}
// Perform edge routing
for(ut64 block_id : block_order)
{
GraphBlock & block = blocks[block_id];
GraphBlock & start = block;
for(ut64 edge : block.exits)
{
GraphBlock & end = blocks[edge];
start.edges.push_back(routeEdge(horiz_edges, vert_edges, edge_valid, start, end, QColor(255, 0, 0)));
}
}
// Compute edge counts for each row and column
std::vector<int> col_edge_count, row_edge_count;
initVec(col_edge_count, entryb.col_count + 1, 0);
initVec(row_edge_count, entryb.row_count + 1, 0);
for(int row = 0; row < entryb.row_count + 1; row++)
{
for(int col = 0; col < entryb.col_count + 1; col++)
{
if(int(horiz_edges[row][col].size()) > row_edge_count[row])
row_edge_count[row] = int(horiz_edges[row][col].size());
if(int(vert_edges[row][col].size()) > col_edge_count[col])
col_edge_count[col] = int(vert_edges[row][col].size());
}
}
//Compute row and column sizes
std::vector<int> col_width, row_height;
initVec(col_width, entryb.col_count + 1, 0);
initVec(row_height, entryb.row_count + 1, 0);
for(auto & blockIt : blocks)
{
GraphBlock & block = blockIt.second;
if((int(block.width / 2)) > col_width[block.col])
col_width[block.col] = int(block.width / 2);
if((int(block.width / 2)) > col_width[block.col + 1])
col_width[block.col + 1] = int(block.width / 2);
if(int(block.height) > row_height[block.row])
row_height[block.row] = int(block.height);
}
// Compute row and column positions
std::vector<int> col_x, row_y;
initVec(col_x, entryb.col_count, 0);
initVec(row_y, entryb.row_count, 0);
initVec(col_edge_x, entryb.col_count + 1, 0);
initVec(row_edge_y, entryb.row_count + 1, 0);
int x = block_horizontal_margin * 2;
for(int i = 0; i < entryb.col_count; i++)
{
col_edge_x[i] = x;
x += block_horizontal_margin * col_edge_count[i];
col_x[i] = x;
x += col_width[i];
}
int y = block_vertical_margin * 2;
for(int i = 0; i < entryb.row_count; i++)
{
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];
row_y[i] = y;
y += row_height[i];
}
col_edge_x[entryb.col_count] = x;
row_edge_y[entryb.row_count] = y;
width = x + (block_horizontal_margin * 2) + (block_horizontal_margin * col_edge_count[entryb.col_count]);
height = y + (block_vertical_margin * 2) + (block_vertical_margin * row_edge_count[entryb.row_count]);
//Compute node positions
for(auto & blockIt : blocks)
{
GraphBlock & block = blockIt.second;
block.x = int(
(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) > (
col_x[block.col] + col_width[block.col] + col_width[block.col + 1] + block_horizontal_margin * col_edge_count[
block.col + 1]))
{
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.y = row_y[block.row];
}
// Precompute coordinates for edges
for(auto & blockIt : blocks)
{
GraphBlock & block = blockIt.second;
for(GraphEdge & edge : block.edges)
{
auto start = edge.points[0];
auto start_col = start.col;
auto last_index = edge.start_index;
// 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);
auto last_pt = first_pt;
QPolygonF pts;
pts.append(last_pt);
for(int i = 0; i < int(edge.points.size()); i++)
{
auto end = edge.points[i];
auto end_row = end.row;
auto end_col = end.col;
auto last_index = end.index;
QPoint new_pt;
// block_vertical_margin/2 gives the margin from block to the horizontal lines
if(start_col == end_col)
new_pt = QPoint(last_pt.x(), row_edge_y[end_row] + (block_vertical_margin * last_index) + (block_vertical_margin / 2));
else
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);
last_pt = new_pt;
start_col = end_col;
}
EdgeConfiguration ec = edgeConfiguration(block, edge.dest);
auto new_pt = QPoint(last_pt.x(), edge.dest->y - 1);
pts.push_back(new_pt);
edge.polyline = pts;
edge.color = ec.color;
if(ec.start_arrow)
{
pts.clear();
pts.append(QPoint(first_pt.x() - 3, first_pt.y() + 6));
pts.append(QPoint(first_pt.x() + 3, first_pt.y() + 6));
pts.append(first_pt);
edge.arrow_start = pts;
}
if(ec.end_arrow)
{
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(new_pt);
edge.arrow_end = pts;
}
}
}
ready = true;
viewport()->update();
areaSize = viewport()->size();
adjustSize(areaSize.width(), areaSize.height());
}
void GraphView::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QPainter p(viewport());
int render_offset_x = -horizontalScrollBar()->value() * current_scale;
int render_offset_y = -verticalScrollBar()->value() * current_scale;
int render_width = viewport()->size().width() / 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
QRect viewportRect(viewport()->rect().topLeft(), viewport()->rect().bottomRight() - QPoint(1, 1));
p.setBrush(backgroundColor);
p.drawRect(viewportRect);
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.scale(current_scale, current_scale);
// Draw blocks
for(auto & blockIt : blocks)
{
GraphBlock & block = blockIt.second;
// Check if block is visible
if((block.x + block.width > -render_offset_x) ||
(block.y + block.height > -render_offset_y) ||
(-render_offset_x + render_width > block.x) ||
(-render_offset_y + render_height > block.y))
{
// Only draw block if it is visible
drawBlock(p, block);
}
p.setBrush(Qt::gray);
// Always draw edges
// TODO: Only draw edges if they are actually visible ...
// Draw edges
for(GraphEdge & edge : block.edges)
{
EdgeConfiguration ec = edgeConfiguration(block, edge.dest);
QPen pen(edge.color);
// if(blockSelected)
// pen.setStyle(Qt::DashLine);
p.setPen(pen);
p.setBrush(edge.color);
p.drawPolyline(edge.polyline);
pen.setStyle(Qt::SolidLine);
p.setPen(pen);
if(ec.start_arrow)
{
p.drawConvexPolygon(edge.arrow_start);
}
if(ec.end_arrow)
{
p.drawConvexPolygon(edge.arrow_end);
}
}
}
}
// Prepare graph
// This computes the position and (row/col based) size of the block
// Recursively calls itself for each child of the GraphBlock
void GraphView::computeGraphLayout(GraphBlock & block)
{
int col = 0;
int row_count = 1;
int childColumn = 0;
bool singleChild = block.new_exits.size() == 1;
// Compute all children nodes
for(size_t i = 0; i < block.new_exits.size(); i++)
{
ut64 edge = block.new_exits[i];
GraphBlock & edgeb = blocks[edge];
computeGraphLayout(edgeb);
row_count = std::max(edgeb.row_count + 1, row_count);
childColumn = edgeb.col;
}
if(layoutType != LayoutType::Wide && block.new_exits.size() == 2)
{
GraphBlock & left = blocks[block.new_exits[0]];
GraphBlock & right = blocks[block.new_exits[1]];
if(left.new_exits.size() == 0)
{
left.col = right.col - 2;
int add = left.col < 0 ? - left.col : 0;
adjustGraphLayout(right, add, 1);
adjustGraphLayout(left, add, 1);
col = right.col_count + add;
}
else if(right.new_exits.size() == 0)
{
adjustGraphLayout(left, 0, 1);
adjustGraphLayout(right, left.col + 2, 1);
col = std::max(left.col_count, right.col + 2);
}
else
{
adjustGraphLayout(left, 0, 1);
adjustGraphLayout(right, left.col_count, 1);
col = left.col_count + right.col_count;
}
block.col_count = std::max(2, col);
if(layoutType == LayoutType::Medium)
{
block.col = (left.col + right.col) / 2;
}
else
{
block.col = singleChild ? childColumn : (col - 2) / 2;
}
}
else
{
for(ut64 edge : block.new_exits)
{
adjustGraphLayout(blocks[edge], col, 1);
col += blocks[edge].col_count;
}
if(col >= 2)
{
// Place this node centered over the child nodes
block.col = singleChild ? childColumn : (col - 2) / 2;
block.col_count = col;
}
else
{
//No child nodes, set single node's width (nodes are 2 columns wide to allow
//centering over a branch)
block.col = 0;
block.col_count = 2;
}
}
block.row = 0;
block.row_count = row_count;
}
// Edge computing stuff
bool GraphView::isEdgeMarked(EdgesVector & edges, int row, int col, int index)
{
if(index >= int(edges[row][col].size()))
return false;
return edges[row][col][index];
}
void GraphView::markEdge(EdgesVector & edges, int row, int col, int index, bool used)
{
while(int(edges[row][col].size()) <= index)
edges[row][col].push_back(false);
edges[row][col][index] = used;
}
GraphView::GraphEdge GraphView::routeEdge(EdgesVector & horiz_edges, EdgesVector & vert_edges, Matrix<bool> & edge_valid, GraphBlock & start, GraphBlock & end, QColor color)
{
GraphEdge edge;
edge.color = color;
edge.dest = &end;
//Find edge index for initial outgoing line
int i = 0;
while(true)
{
if(!isEdgeMarked(vert_edges, start.row + 1, start.col + 1, i))
break;
i += 1;
}
markEdge(vert_edges, start.row + 1, start.col + 1, i);
edge.addPoint(start.row + 1, start.col + 1);
edge.start_index = i;
bool horiz = false;
//Find valid column for moving vertically to the target node
int min_row, max_row;
if(end.row < (start.row + 1))
{
min_row = end.row;
max_row = start.row + 1;
}
else
{
min_row = start.row + 1;
max_row = end.row;
}
int col = start.col + 1;
if(min_row != max_row)
{
auto checkColumn = [min_row, max_row, &edge_valid](int column)
{
if(column < 0 || column >= int(edge_valid[min_row].size()))
return false;
for(int row = min_row; row < max_row; row++)
{
if(!edge_valid[row][column])
{
return false;
}
}
return true;
};
if(!checkColumn(col))
{
if(checkColumn(end.col + 1))
{
col = end.col + 1;
}
else
{
int ofs = 0;
while(true)
{
col = start.col + 1 - ofs;
if(checkColumn(col))
{
break;
}
col = start.col + 1 + ofs;
if(checkColumn(col))
{
break;
}
ofs += 1;
}
}
}
}
if(col != (start.col + 1))
{
//Not in same column, need to generate a line for moving to the correct column
int min_col, max_col;
if(col < (start.col + 1))
{
min_col = col;
max_col = start.col + 1;
}
else
{
min_col = start.col + 1;
max_col = col;
}
int index = findHorizEdgeIndex(horiz_edges, start.row + 1, min_col, max_col);
edge.addPoint(start.row + 1, col, index);
horiz = true;
}
if(end.row != (start.row + 1))
{
//Not in same row, need to generate a line for moving to the correct row
if(col == (start.col + 1))
markEdge(vert_edges, start.row + 1, start.col + 1, i, false);
int index = findVertEdgeIndex(vert_edges, col, min_row, max_row);
if(col == (start.col + 1))
edge.start_index = index;
edge.addPoint(end.row, col, index);
horiz = false;
}
if(col != (end.col + 1))
{
//Not in ending column, need to generate a line for moving to the correct column
int min_col, max_col;
if(col < (end.col + 1))
{
min_col = col;
max_col = end.col + 1;
}
else
{
min_col = end.col + 1;
max_col = col;
}
int index = findHorizEdgeIndex(horiz_edges, end.row, min_col, max_col);
edge.addPoint(end.row, end.col + 1, index);
horiz = true;
}
//If last line was horizontal, choose the ending edge index for the incoming edge
if(horiz)
{
int index = findVertEdgeIndex(vert_edges, end.col + 1, end.row, end.row);
edge.points[int(edge.points.size()) - 1].index = index;
}
return edge;
}
int GraphView::findHorizEdgeIndex(EdgesVector & edges, int row, int min_col, int max_col)
{
//Find a valid index
int i = 0;
while(true)
{
bool valid = true;
for(int col = min_col; col < max_col + 1; col++)
if(isEdgeMarked(edges, row, col, i))
{
valid = false;
break;
}
if(valid)
break;
i++;
}
//Mark chosen index as used
for(int col = min_col; col < max_col + 1; col++)
markEdge(edges, row, col, i);
return i;
}
int GraphView::findVertEdgeIndex(EdgesVector & edges, int col, int min_row, int max_row)
{
//Find a valid index
int i = 0;
while(true)
{
bool valid = true;
for(int row = min_row; row < max_row + 1; row++)
if(isEdgeMarked(edges, row, col, i))
{
valid = false;
break;
}
if(valid)
break;
i++;
}
//Mark chosen index as used
for(int row = min_row; row < max_row + 1; row++)
markEdge(edges, row, col, i);
return i;
}
void GraphView::showBlock(GraphBlock & block, bool animated)
{
showBlock(&block, animated);
}
void GraphView::showBlock(GraphBlock* block, bool animated)
{
int render_width = viewport()->size().width() / current_scale;
// Show block middle of X
int target_x = (block->x + (block->width / 2)) - (render_width / 2);
int show_block_offset_y = 30;
// But beginning of Y (so we show the top of the block)
int target_y = block->y - show_block_offset_y;
target_x = std::max(0, target_x);
target_y = std::max(0, target_y);
target_x = std::min(horizontalScrollBar()->maximum(), target_x);
target_y = std::min(verticalScrollBar()->maximum(), target_y);
if(animated)
{
QPropertyAnimation* animation_x = new QPropertyAnimation(horizontalScrollBar(), "value");
animation_x->setDuration(500);
animation_x->setStartValue(horizontalScrollBar()->value());
animation_x->setEndValue(target_x);
animation_x->setEasingCurve(QEasingCurve::InOutQuad);
animation_x->start();
QPropertyAnimation* animation_y = new QPropertyAnimation(verticalScrollBar(), "value");
animation_y->setDuration(500);
animation_y->setStartValue(verticalScrollBar()->value());
animation_y->setEndValue(target_y);
animation_y->setEasingCurve(QEasingCurve::InOutQuad);
animation_y->start();
}
else
{
horizontalScrollBar()->setValue(target_x);
verticalScrollBar()->setValue(target_y);
}
blockTransitionedTo(block);
viewport()->update();
}
void GraphView::adjustGraphLayout(GraphBlock & block, int col, int row)
{
block.col += col;
block.row += row;
for(ut64 edge : block.new_exits)
{
adjustGraphLayout(blocks[edge], col, row);
}
}
void GraphView::addBlock(GraphView::GraphBlock block)
{
blocks[block.entry] = block;
}
void GraphView::setEntry(ut64 e)
{
entry = e;
}
bool GraphView::checkPointClicked(QPointF & point, int x, int y, bool above_y)
{
int half_target_size = 5;
if((point.x() - half_target_size < x) &&
(point.y() - (above_y ? (2 * half_target_size) : 0) < y) &&
(x < point.x() + half_target_size) &&
(y < point.y() + (above_y ? 0 : (3 * half_target_size))))
{
return true;
}
return false;
}
void GraphView::resizeEvent(QResizeEvent* event)
{
adjustSize(event->size().width(), event->size().height());
}
// Mouse events
void GraphView::mousePressEvent(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);
blockClicked(block, event, pos);
// Don't do anything else here! blockClicked might seek and
// all our data is invalid then.
return;
}
}
// Check if a line beginning/end was clicked
for(auto & blockIt : blocks)
{
GraphBlock & block = blockIt.second;
for(GraphEdge & edge : block.edges)
{
if(edge.polyline.length() < 2)
{
continue;
}
QPointF start = edge.polyline.first();
QPointF end = edge.polyline.last();
if(checkPointClicked(start, x, y))
{
showBlock(edge.dest, true);
// TODO: Callback to child
return;
break;
}
if(checkPointClicked(end, x, y, true))
{
showBlock(block, true);
// TODO: Callback to child
return;
break;
}
}
}
// No block was clicked
if(event->button() == Qt::LeftButton)
{
//Left click outside any block, enter scrolling mode
scroll_base_x = event->x();
scroll_base_y = event->y();
scroll_mode = true;
setCursor(Qt::ClosedHandCursor);
viewport()->grabMouse();
}
}
void GraphView::mouseMoveEvent(QMouseEvent* event)
{
if(scroll_mode)
{
int x_delta = scroll_base_x - event->x();
int y_delta = scroll_base_y - event->y();
scroll_base_x = event->x();
scroll_base_y = event->y();
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + x_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;
}
}
}
void GraphView::mouseReleaseEvent(QMouseEvent* event)
{
// TODO
// if(event->button() == Qt::ForwardButton)
// gotoNextSlot();
// else if(event->button() == Qt::BackButton)
// gotoPreviousSlot();
if(event->button() != Qt::LeftButton)
return;
if(scroll_mode)
{
scroll_mode = false;
setCursor(Qt::ArrowCursor);
viewport()->releaseMouse();
}
}

182
src/gui/Src/Gui/GraphView.h Normal file
View File

@ -0,0 +1,182 @@
#ifndef GRAPHVIEW_H
#define GRAPHVIEW_H
#include <QObject>
#include <QPainter>
#include <QWidget>
#include <QAbstractScrollArea>
#include <QScrollBar>
#include <QElapsedTimer>
#include <QHelpEvent>
#include <unordered_map>
#include <unordered_set>
#include <queue>
//#include "cutter.h"
using ut64 = size_t;
class GraphView : public QAbstractScrollArea
{
Q_OBJECT
enum class LayoutType
{
Medium,
Wide,
Narrow,
};
public:
struct GraphBlock;
struct Point
{
int row; //point[0]
int col; //point[1]
int index; //point[2]
};
struct GraphEdge
{
QColor color;
GraphBlock* dest;
std::vector<Point> points;
int start_index = 0;
QPolygonF polyline;
QPolygonF arrow_start;
QPolygonF arrow_end;
void addPoint(int row, int col, int index = 0)
{
Point point = {row, col, 0};
this->points.push_back(point);
if(int(this->points.size()) > 1)
this->points[this->points.size() - 2].index = index;
}
};
struct GraphBlock
{
qreal x = 0.0;
qreal y = 0.0;
int width = 0;
int height = 0;
// This is a unique identifier, e.g. offset in the case of r2 blocks
ut64 entry;
// This contains unique identifiers to entries
// Outgoing edges
std::vector<ut64> exits;
// Incoming edges
std::vector<ut64> incoming;
// TODO what is this
std::vector<ut64> new_exits;
// Number of rows in block
int row_count = 0;
// Number of columns in block
int col_count = 0;
// Column in which the block is
int col = 0;
// Row in which the block is
int row = 0;
// Edges
std::vector<GraphEdge> edges;
};
struct EdgeConfiguration
{
QColor color = QColor(128, 128, 128);
bool start_arrow = false;
bool end_arrow = true;
};
GraphView(QWidget* parent);
~GraphView();
void paintEvent(QPaintEvent* event) override;
// Show a block centered. Animates to it if animated=true
void showBlock(GraphBlock & block, bool animated = false);
void showBlock(GraphBlock* block, bool animated = false);
protected:
std::unordered_map<ut64, GraphBlock> blocks;
QColor backgroundColor = QColor(Qt::white);
// The vertical margin between blocks
int block_vertical_margin = 20;
int block_horizontal_margin = 10;
// Padding inside the block
int block_padding = 16;
// Zoom data
double current_scale = 1.0;
int unscrolled_render_offset_x = 0;
int unscrolled_render_offset_y = 0;
void addBlock(GraphView::GraphBlock block);
void setEntry(ut64 e);
void computeGraph(ut64 entry);
// Callbacks that should be overridden
virtual void drawBlock(QPainter & p, GraphView::GraphBlock & block);
virtual void blockClicked(GraphView::GraphBlock & block, QMouseEvent* event, QPoint pos);
virtual void blockDoubleClicked(GraphView::GraphBlock & block, QMouseEvent* event, QPoint pos);
virtual void blockHelpEvent(GraphView::GraphBlock & block, QHelpEvent* event, QPoint pos);
virtual bool helpEvent(QHelpEvent* event);
virtual void blockTransitionedTo(GraphView::GraphBlock* to);
virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock & from, GraphView::GraphBlock* to);
void adjustSize(int new_width, int new_height);
bool event(QEvent* event);
private:
bool checkPointClicked(QPointF & point, int x, int y, bool above_y = false);
ut64 entry;
void computeGraphLayout(GraphBlock & block);
void adjustGraphLayout(GraphBlock & block, int col, int row);
// Layout type
LayoutType layoutType;
int width;
int height;
bool ready;
// Scrolling data
int scroll_base_x;
int scroll_base_y;
bool scroll_mode;
// Todo: remove charheight/charwidth cause it should be handled in child class
qreal charWidth = 10.0;
// Edge computing stuff
template<typename T>
using Matrix = std::vector<std::vector<T>>;
using EdgesVector = Matrix<std::vector<bool>>;
std::vector<int> col_edge_x;
std::vector<int> row_edge_y;
bool isEdgeMarked(EdgesVector & edges, int row, int col, int index);
void markEdge(EdgesVector & edges, int row, int col, int index, bool used = true);
int findHorizEdgeIndex(EdgesVector & edges, int row, int min_col, int max_col);
int findVertEdgeIndex(EdgesVector & edges, int col, int min_row, int max_row);
GraphEdge routeEdge(EdgesVector & horiz_edges, EdgesVector & vert_edges, Matrix<bool> & edge_valid, GraphBlock & start, GraphBlock & end, QColor color);
private slots:
void resizeEvent(QResizeEvent* event) override;
// Mouse events
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
};
#endif // GRAPHVIEW_H

View File

@ -17,6 +17,7 @@
#include "FavouriteTools.h"
#include "CPUDisassembly.h"
#include "CloseDialog.h"
#include <QVBoxLayout>
#include "CommandLineEdit.h"
#include "TabWidget.h"
#include "CPUWidget.h"
@ -40,7 +41,7 @@
#include "TimeWastedCounter.h"
#include "NotesManager.h"
#include "SettingsDialog.h"
#include "DisassemblerGraphView.h"
#include "DisassemblerGraphViewOld.h"
#include "CPUMultiDump.h"
#include "CPUStack.h"
#include "GotoDialog.h"
@ -201,7 +202,7 @@ MainWindow::MainWindow(QWidget* parent)
mHandlesView->setWindowIcon(DIcon("handles.png"));
// Graph view
mGraphView = new DisassemblerGraphView(this);
mGraphView = new DisassemblerGraphViewOld(this);
mGraphView->setWindowTitle(tr("Graph"));
mGraphView->setWindowIcon(DIcon("graph.png"));
@ -220,13 +221,18 @@ MainWindow::MainWindow(QWidget* parent)
mWidgetList.push_back(WidgetInfo(mNotesManager, "NotesTab"));
mWidgetList.push_back(WidgetInfo(mBreakpointsView, "BreakpointsTab"));
mWidgetList.push_back(WidgetInfo(mMemMapView, "MemoryMapTab"));
mWidgetList.push_back(WidgetInfo(mCallStackView, "CallStackTab"));
//mWidgetList.push_back(WidgetInfo(mCallStackView, "CallStackTab"));
mWidgetList.push_back(WidgetInfo(mSEHChainView, "SEHTab"));
mWidgetList.push_back(WidgetInfo(mScriptView, "ScriptTab"));
mWidgetList.push_back(WidgetInfo(mSymbolView, "SymbolsTab"));
mWidgetList.push_back(WidgetInfo(mSourceViewManager, "SourceTab"));
mWidgetList.push_back(WidgetInfo(mReferenceManager, "ReferencesTab"));
mWidgetList.push_back(WidgetInfo(mThreadView, "ThreadsTab"));
QVBoxLayout* x = new QVBoxLayout();
x->addWidget(mThreadView);
x->addWidget(mCallStackView);
QWidget* w = new QWidget();
w->setLayout(x);
mWidgetList.push_back(WidgetInfo(w, "ThreadsTab"));
mWidgetList.push_back(WidgetInfo(mSnowmanView, "SnowmanTab"));
mWidgetList.push_back(WidgetInfo(mHandlesView, "HandlesTab"));
mWidgetList.push_back(WidgetInfo(mTraceBrowser, "TraceTab"));

View File

@ -29,7 +29,7 @@ class MainWindowCloseThread;
class TimeWastedCounter;
class NotesManager;
class SettingsDialog;
class DisassemblerGraphView;
class DisassemblerGraphViewOld;
class SimpleTraceDialog;
class MRUList;
class UpdateChecker;
@ -179,7 +179,7 @@ private:
QWidget* mSnowmanView;
HandlesView* mHandlesView;
NotesManager* mNotesManager;
DisassemblerGraphView* mGraphView;
DisassemblerGraphViewOld* mGraphView;
TraceBrowser* mTraceBrowser;
SimpleTraceDialog* mSimpleTraceDialog;
UpdateChecker* mUpdateChecker;

View File

@ -168,7 +168,6 @@ SOURCES += \
Src/Gui/WatchView.cpp \
Src/Gui/FavouriteTools.cpp \
Src/Gui/BrowseDialog.cpp \
Src/Gui/DisassemblerGraphView.cpp \
Src/Gui/DisassemblyPopup.cpp \
Src/Gui/VirtualModDialog.cpp \
Src/BasicView/LabeledSplitter.cpp \
@ -189,7 +188,10 @@ SOURCES += \
Src/Tracer/TraceBrowser.cpp \
Src/Tracer/TraceFileReader.cpp \
Src/Tracer/TraceFileSearch.cpp \
Src/Gui/MultiItemsSelectWindow.cpp
Src/Gui/MultiItemsSelectWindow.cpp \
Src/Gui/DisassemblerGraphViewOld.cpp \
Src/Gui/GraphView.cpp \
Src/Gui/DisassemblerGraphView.cpp
HEADERS += \
@ -290,7 +292,6 @@ HEADERS += \
Src/Gui/WatchView.h \
Src/Gui/FavouriteTools.h \
Src/Gui/BrowseDialog.h \
Src/Gui/DisassemblerGraphView.h \
Src/Utils/ActionHelpers.h \
Src/Gui/DisassemblyPopup.h \
Src/Gui/VirtualModDialog.h \
@ -313,7 +314,10 @@ HEADERS += \
Src/Tracer/TraceFileReader.h \
Src/Tracer/TraceFileReaderInternal.h \
Src/Tracer/TraceFileSearch.h \
Src/Gui/MultiItemsSelectWindow.h
Src/Gui/MultiItemsSelectWindow.h \
Src/Gui/DisassemblerGraphViewOld.h \
Src/Gui/GraphView.h \
Src/Gui/DisassemblerGraphView.h
FORMS += \