2017-12-13 22:38:46 +00:00
|
|
|
#include "GraphView.h"
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QPropertyAnimation>
|
|
|
|
|
|
|
|
|
|
|
|
GraphView::GraphView(QWidget *parent)
|
|
|
|
: QAbstractScrollArea(parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
GraphView::~GraphView()
|
|
|
|
{
|
|
|
|
// TODO: Cleanups
|
|
|
|
}
|
|
|
|
|
|
|
|
// Vector functions
|
|
|
|
template<class T>
|
2018-03-21 20:32:32 +00:00
|
|
|
static void removeFromVec(std::vector<T> &vec, T elem)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
vec.erase(std::remove(vec.begin(), vec.end(), elem), vec.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
2018-03-21 20:32:32 +00:00
|
|
|
static void initVec(std::vector<T> &vec, size_t size, T value)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
vec.resize(size);
|
2018-03-21 20:32:32 +00:00
|
|
|
for (size_t i = 0; i < size; i++)
|
2017-12-13 22:38:46 +00:00
|
|
|
vec[i] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callbacks
|
2018-03-21 20:32:32 +00:00
|
|
|
void GraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
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!";
|
|
|
|
}
|
|
|
|
|
2017-12-14 21:07:48 +00:00
|
|
|
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!";
|
|
|
|
}
|
|
|
|
|
2017-12-19 16:59:39 +00:00
|
|
|
void GraphView::blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event, QPoint pos)
|
|
|
|
{
|
|
|
|
Q_UNUSED(block);
|
|
|
|
Q_UNUSED(event);
|
|
|
|
Q_UNUSED(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GraphView::helpEvent(QHelpEvent *event)
|
|
|
|
{
|
2019-03-23 08:21:06 +00:00
|
|
|
int x = event->pos().x() + offset.x();
|
|
|
|
int y = event->pos().y() - offset.y();
|
2017-12-19 16:59:39 +00:00
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-19 16:59:39 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if ((block.x <= x) && (block.y <= y) &&
|
|
|
|
(x <= block.x + block.width) & (y <= block.y + block.height)) {
|
2017-12-19 16:59:39 +00:00
|
|
|
QPoint pos = QPoint(x - block.x, y - block.y);
|
|
|
|
blockHelpEvent(block, event, pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
void GraphView::blockTransitionedTo(GraphView::GraphBlock *to)
|
|
|
|
{
|
|
|
|
Q_UNUSED(to);
|
|
|
|
qWarning() << "blockTransitionedTo not overridden!";
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
GraphView::EdgeConfiguration GraphView::edgeConfiguration(GraphView::GraphBlock &from,
|
|
|
|
GraphView::GraphBlock *to)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(from);
|
|
|
|
Q_UNUSED(to);
|
|
|
|
qWarning() << "Edge configuration not overridden!";
|
|
|
|
EdgeConfiguration ec;
|
|
|
|
return ec;
|
|
|
|
}
|
|
|
|
|
2017-12-19 16:59:39 +00:00
|
|
|
bool GraphView::event(QEvent *event)
|
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
if (event->type() == QEvent::ToolTip) {
|
|
|
|
if (helpEvent(static_cast<QHelpEvent *>(event))) {
|
2017-12-19 16:59:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return QAbstractScrollArea::event(event);
|
|
|
|
}
|
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
// This calculates the full graph starting at block entry.
|
|
|
|
void GraphView::computeGraph(ut64 entry)
|
|
|
|
{
|
|
|
|
// Populate incoming lists
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &edge : block.exits) {
|
2017-12-14 21:07:48 +00:00
|
|
|
blocks[edge].incoming.push_back(block.entry);
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unordered_set<ut64> visited;
|
|
|
|
visited.insert(entry);
|
|
|
|
std::queue<ut64> queue;
|
|
|
|
std::vector<ut64> block_order;
|
|
|
|
queue.push(entry);
|
|
|
|
|
|
|
|
bool changed = true;
|
2018-03-21 20:32:32 +00:00
|
|
|
while (changed) {
|
2017-12-13 22:38:46 +00:00
|
|
|
changed = false;
|
|
|
|
|
|
|
|
// Pick nodes with single entrypoints
|
2018-03-21 20:32:32 +00:00
|
|
|
while (!queue.empty()) {
|
2017-12-14 21:07:48 +00:00
|
|
|
GraphBlock &block = blocks[queue.front()];
|
2017-12-13 22:38:46 +00:00
|
|
|
queue.pop();
|
|
|
|
block_order.push_back(block.entry);
|
2018-03-21 20:32:32 +00:00
|
|
|
for (ut64 edge : block.exits) {
|
2017-12-13 22:38:46 +00:00
|
|
|
// Skip edge if we already visited it
|
2018-03-21 20:32:32 +00:00
|
|
|
if (visited.count(edge)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some edges might not be available
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!blocks.count(edge)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this node has no other incoming edges, add it to the graph layout
|
2018-03-21 20:32:32 +00:00
|
|
|
if (blocks[edge].incoming.size() == 1) {
|
2017-12-14 21:07:48 +00:00
|
|
|
removeFromVec(blocks[edge].incoming, block.entry);
|
2017-12-13 22:38:46 +00:00
|
|
|
block.new_exits.push_back(edge);
|
2017-12-14 21:07:48 +00:00
|
|
|
queue.push(blocks[edge].entry);
|
2017-12-13 22:38:46 +00:00
|
|
|
visited.insert(edge);
|
|
|
|
changed = true;
|
|
|
|
} else {
|
|
|
|
// Remove from incoming edges
|
2017-12-14 21:07:48 +00:00
|
|
|
removeFromVec(blocks[edge].incoming, block.entry);
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No more nodes satisfy constraints, pick a node to continue constructing the graph
|
|
|
|
ut64 best = 0;
|
|
|
|
int best_edges;
|
|
|
|
ut64 best_parent;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
|
|
|
// Skip blocks we haven't visited yet
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!visited.count(block.entry)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
for (ut64 edge : block.exits) {
|
2017-12-13 22:38:46 +00:00
|
|
|
// If we already visited the exit, skip it
|
2018-03-21 20:32:32 +00:00
|
|
|
if (visited.count(edge)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!blocks.count(edge)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// find best edge
|
2018-03-21 20:32:32 +00:00
|
|
|
if ((best == 0) || ((int)blocks[edge].incoming.size() < best_edges) || (
|
|
|
|
((int)blocks[edge].incoming.size() == best_edges) && (edge < best))) {
|
2017-12-13 22:38:46 +00:00
|
|
|
best = edge;
|
2017-12-14 21:07:48 +00:00
|
|
|
best_edges = blocks[edge].incoming.size();
|
2017-12-13 22:38:46 +00:00
|
|
|
best_parent = block.entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
if (best != 0) {
|
2017-12-14 21:07:48 +00:00
|
|
|
GraphBlock &best_parentb = blocks[best_parent];
|
|
|
|
removeFromVec(blocks[best].incoming, best_parentb.entry);
|
2017-12-13 22:38:46 +00:00
|
|
|
best_parentb.new_exits.push_back(best);
|
|
|
|
visited.insert(best);
|
|
|
|
queue.push(best);
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-14 21:07:48 +00:00
|
|
|
computeGraphLayout(blocks[entry]);
|
2017-12-13 22:38:46 +00:00
|
|
|
|
|
|
|
// Prepare edge routing
|
2017-12-14 21:07:48 +00:00
|
|
|
GraphBlock &entryb = blocks[entry];
|
2017-12-13 22:38:46 +00:00
|
|
|
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);
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int row = 0; row < entryb.row_count + 1; row++) {
|
|
|
|
horiz_edges[row].resize(entryb.col_count + 1);
|
2017-12-13 22:38:46 +00:00
|
|
|
vert_edges[row].resize(entryb.col_count + 1);
|
|
|
|
initVec(edge_valid[row], entryb.col_count + 1, true);
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int col = 0; col < entryb.col_count + 1; col++) {
|
2017-12-13 22:38:46 +00:00
|
|
|
horiz_edges[row][col].clear();
|
|
|
|
vert_edges[row][col].clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
|
|
|
edge_valid[block.row][block.col + 1] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform edge routing
|
2018-03-21 20:32:32 +00:00
|
|
|
for (ut64 block_id : block_order) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blocks[block_id];
|
|
|
|
GraphBlock &start = block;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (ut64 edge : block.exits) {
|
2017-12-14 21:07:48 +00:00
|
|
|
GraphBlock &end = blocks[edge];
|
2018-03-21 20:32:32 +00:00
|
|
|
start.edges.push_back(routeEdge(horiz_edges, vert_edges, edge_valid, start, end, QColor(255, 0,
|
|
|
|
0)));
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2018-03-21 20:32:32 +00:00
|
|
|
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])
|
2017-12-13 22:38:46 +00:00
|
|
|
row_edge_count[row] = int(horiz_edges[row][col].size());
|
2018-03-21 20:32:32 +00:00
|
|
|
if (int(vert_edges[row][col].size()) > col_edge_count[col])
|
2017-12-13 22:38:46 +00:00
|
|
|
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);
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
2018-03-21 20:32:32 +00:00
|
|
|
if ((int(block.width / 2)) > col_width[block.col])
|
2017-12-13 22:38:46 +00:00
|
|
|
col_width[block.col] = int(block.width / 2);
|
2018-03-21 20:32:32 +00:00
|
|
|
if ((int(block.width / 2)) > col_width[block.col + 1])
|
2017-12-13 22:38:46 +00:00
|
|
|
col_width[block.col + 1] = int(block.width / 2);
|
2018-03-21 20:32:32 +00:00
|
|
|
if (int(block.height) > row_height[block.row])
|
2017-12-13 22:38:46 +00:00
|
|
|
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);
|
2017-12-14 21:07:48 +00:00
|
|
|
initVec(col_edge_x, entryb.col_count + 1, 0);
|
|
|
|
initVec(row_edge_y, entryb.row_count + 1, 0);
|
|
|
|
int x = block_horizontal_margin * 2;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int i = 0; i < entryb.col_count; i++) {
|
2017-12-14 21:07:48 +00:00
|
|
|
col_edge_x[i] = x;
|
|
|
|
x += block_horizontal_margin * col_edge_count[i];
|
2017-12-13 22:38:46 +00:00
|
|
|
col_x[i] = x;
|
|
|
|
x += col_width[i];
|
|
|
|
}
|
2017-12-14 21:07:48 +00:00
|
|
|
int y = block_vertical_margin * 2;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int i = 0; i < entryb.row_count; i++) {
|
2017-12-14 21:07:48 +00:00
|
|
|
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
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!row_edge_count[i]) {
|
2017-12-14 21:07:48 +00:00
|
|
|
row_edge_count[i] = 1;
|
|
|
|
}
|
2017-12-13 22:38:46 +00:00
|
|
|
y += block_vertical_margin * row_edge_count[i];
|
|
|
|
row_y[i] = y;
|
|
|
|
y += row_height[i];
|
|
|
|
}
|
2017-12-14 21:07:48 +00:00
|
|
|
col_edge_x[entryb.col_count] = x;
|
|
|
|
row_edge_y[entryb.row_count] = y;
|
2018-03-21 20:32:32 +00:00
|
|
|
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]);
|
2017-12-13 22:38:46 +00:00
|
|
|
|
|
|
|
//Compute node positions
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
|
|
|
block.x = int(
|
2018-03-21 20:32:32 +00:00
|
|
|
(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);
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
block.y = row_y[block.row];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Precompute coordinates for edges
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
for (GraphEdge &edge : block.edges) {
|
2017-12-13 22:38:46 +00:00
|
|
|
auto start = edge.points[0];
|
|
|
|
auto start_col = start.col;
|
|
|
|
auto last_index = edge.start_index;
|
2017-12-14 21:07:48 +00:00
|
|
|
// This is the start point of the edge.
|
2018-03-21 20:32:32 +00:00
|
|
|
auto first_pt = QPoint(col_edge_x[start_col] + (block_horizontal_margin * last_index) +
|
|
|
|
(block_horizontal_margin / 2),
|
2017-12-13 22:38:46 +00:00
|
|
|
block.y + block.height);
|
|
|
|
auto last_pt = first_pt;
|
|
|
|
QPolygonF pts;
|
|
|
|
pts.append(last_pt);
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int i = 0; i < int(edge.points.size()); i++) {
|
2017-12-13 22:38:46 +00:00
|
|
|
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
|
2018-03-21 20:32:32 +00:00
|
|
|
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));
|
2017-12-13 22:38:46 +00:00
|
|
|
else
|
2018-03-21 20:32:32 +00:00
|
|
|
new_pt = QPoint(col_edge_x[end_col] + (block_horizontal_margin * last_index) +
|
|
|
|
(block_horizontal_margin / 2), last_pt.y());
|
2017-12-13 22:38:46 +00:00
|
|
|
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;
|
2018-03-21 20:32:32 +00:00
|
|
|
if (ec.start_arrow) {
|
2017-12-13 22:38:46 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
if (ec.end_arrow) {
|
2017-12-14 21:07:48 +00:00
|
|
|
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;
|
|
|
|
}
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-14 21:07:48 +00:00
|
|
|
ready = true;
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2017-12-14 21:07:48 +00:00
|
|
|
viewport()->update();
|
2019-02-16 17:17:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QPolygonF GraphView::recalculatePolygon(QPolygonF polygon)
|
|
|
|
{
|
|
|
|
QPolygonF ret;
|
|
|
|
for (int i = 0; i < polygon.size(); i++) {
|
2019-03-23 08:21:06 +00:00
|
|
|
ret << QPointF(polygon[i].x() - offset.x(), polygon[i].y() - offset.y());
|
2019-02-16 17:17:11 +00:00
|
|
|
}
|
|
|
|
return ret;
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
void GraphView::paintEvent(QPaintEvent *event)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(event);
|
2019-04-03 08:55:39 +00:00
|
|
|
qreal dpr = devicePixelRatioF();
|
|
|
|
if (useCache && qFuzzyCompare(dpr, pixmap.devicePixelRatioF())) {
|
2019-03-12 07:37:10 +00:00
|
|
|
drawGraph();
|
|
|
|
return;
|
|
|
|
}
|
2019-04-03 08:55:39 +00:00
|
|
|
pixmap = QPixmap(int(viewport()->width() * dpr), int(viewport()->height() * dpr));
|
|
|
|
pixmap.setDevicePixelRatio(dpr);
|
2019-03-12 07:37:10 +00:00
|
|
|
QPainter p(&pixmap);
|
2018-07-18 10:15:10 +00:00
|
|
|
|
2018-05-21 17:33:46 +00:00
|
|
|
p.setRenderHint(QPainter::Antialiasing);
|
2018-07-18 10:15:10 +00:00
|
|
|
|
2019-02-16 17:17:11 +00:00
|
|
|
int render_width = viewport()->width();
|
|
|
|
int render_height = viewport()->height();
|
2017-12-14 21:07:48 +00:00
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
p.setBrush(backgroundColor);
|
2019-04-03 08:55:39 +00:00
|
|
|
p.drawRect(viewport()->rect());
|
2017-12-13 22:38:46 +00:00
|
|
|
p.setBrush(Qt::black);
|
|
|
|
|
|
|
|
p.scale(current_scale, current_scale);
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
|
|
|
|
2019-02-16 17:17:11 +00:00
|
|
|
qreal blockX = block.x * current_scale;
|
|
|
|
qreal blockY = block.y * current_scale;
|
|
|
|
qreal blockWidth = block.width * current_scale;
|
|
|
|
qreal blockHeight = block.height * current_scale;
|
2019-02-05 15:21:02 +00:00
|
|
|
|
|
|
|
// Check if block is visible by checking if block intersects with view area
|
2019-03-23 08:21:06 +00:00
|
|
|
if (offset.x() * current_scale < blockX + blockWidth
|
|
|
|
&& blockX < offset.x() * current_scale + render_width
|
|
|
|
&& offset.y() * current_scale < blockY + blockHeight
|
|
|
|
&& blockY < offset.y() * current_scale + render_height) {
|
2017-12-13 22:38:46 +00:00
|
|
|
drawBlock(p, block);
|
|
|
|
}
|
|
|
|
|
|
|
|
p.setBrush(Qt::gray);
|
|
|
|
|
|
|
|
// Always draw edges
|
|
|
|
// TODO: Only draw edges if they are actually visible ...
|
|
|
|
// Draw edges
|
2018-03-21 20:32:32 +00:00
|
|
|
for (GraphEdge &edge : block.edges) {
|
2019-02-16 17:17:11 +00:00
|
|
|
QPolygonF polyline = recalculatePolygon(edge.polyline);
|
|
|
|
QPolygonF arrow_start = recalculatePolygon(edge.arrow_start);
|
|
|
|
QPolygonF arrow_end = recalculatePolygon(edge.arrow_end);
|
2017-12-13 22:38:46 +00:00
|
|
|
EdgeConfiguration ec = edgeConfiguration(block, edge.dest);
|
|
|
|
QPen pen(edge.color);
|
2019-02-05 15:21:02 +00:00
|
|
|
pen.setWidth(pen.width() / ec.width_scale);
|
2017-12-13 22:38:46 +00:00
|
|
|
p.setPen(pen);
|
|
|
|
p.setBrush(edge.color);
|
2019-02-16 17:17:11 +00:00
|
|
|
p.drawPolyline(polyline);
|
2017-12-13 22:38:46 +00:00
|
|
|
pen.setStyle(Qt::SolidLine);
|
|
|
|
p.setPen(pen);
|
2018-03-21 20:32:32 +00:00
|
|
|
if (ec.start_arrow) {
|
2019-02-16 17:17:11 +00:00
|
|
|
p.drawConvexPolygon(arrow_start);
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
if (ec.end_arrow) {
|
2019-02-16 17:17:11 +00:00
|
|
|
p.drawConvexPolygon(arrow_end);
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-12 07:37:10 +00:00
|
|
|
drawGraph();
|
2019-01-24 17:13:04 +00:00
|
|
|
emit refreshBlock();
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 07:37:10 +00:00
|
|
|
void GraphView::drawGraph()
|
|
|
|
{
|
|
|
|
QRectF target(0.0, 0.0, viewport()->width(), viewport()->height());
|
2019-04-03 08:55:39 +00:00
|
|
|
QRectF source(0.0, 0.0, viewport()->width() * pixmap.devicePixelRatioF(),
|
|
|
|
viewport()->height() * pixmap.devicePixelRatioF());
|
2019-03-12 07:37:10 +00:00
|
|
|
QPainter p(viewport());
|
|
|
|
p.drawPixmap(target, pixmap, source);
|
|
|
|
}
|
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
// 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
|
2018-03-21 20:32:32 +00:00
|
|
|
for (size_t i = 0; i < block.new_exits.size(); i++) {
|
2017-12-13 22:38:46 +00:00
|
|
|
ut64 edge = block.new_exits[i];
|
2017-12-14 21:07:48 +00:00
|
|
|
GraphBlock &edgeb = blocks[edge];
|
|
|
|
computeGraphLayout(edgeb);
|
2017-12-13 22:38:46 +00:00
|
|
|
row_count = std::max(edgeb.row_count + 1, row_count);
|
|
|
|
childColumn = edgeb.col;
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (layoutType != LayoutType::Wide && block.new_exits.size() == 2) {
|
2017-12-14 21:07:48 +00:00
|
|
|
GraphBlock &left = blocks[block.new_exits[0]];
|
2018-03-21 20:32:32 +00:00
|
|
|
GraphBlock &right = blocks[block.new_exits[1]];
|
|
|
|
if (left.new_exits.size() == 0) {
|
2017-12-13 22:38:46 +00:00
|
|
|
left.col = right.col - 2;
|
|
|
|
int add = left.col < 0 ? - left.col : 0;
|
2017-12-14 21:07:48 +00:00
|
|
|
adjustGraphLayout(right, add, 1);
|
|
|
|
adjustGraphLayout(left, add, 1);
|
2017-12-13 22:38:46 +00:00
|
|
|
col = right.col_count + add;
|
2018-03-21 20:32:32 +00:00
|
|
|
} else if (right.new_exits.size() == 0) {
|
2017-12-14 21:07:48 +00:00
|
|
|
adjustGraphLayout(left, 0, 1);
|
|
|
|
adjustGraphLayout(right, left.col + 2, 1);
|
2017-12-13 22:38:46 +00:00
|
|
|
col = std::max(left.col_count, right.col + 2);
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-14 21:07:48 +00:00
|
|
|
adjustGraphLayout(left, 0, 1);
|
|
|
|
adjustGraphLayout(right, left.col_count, 1);
|
2017-12-13 22:38:46 +00:00
|
|
|
col = left.col_count + right.col_count;
|
|
|
|
}
|
|
|
|
block.col_count = std::max(2, col);
|
2018-03-21 20:32:32 +00:00
|
|
|
if (layoutType == LayoutType::Medium) {
|
2017-12-13 22:38:46 +00:00
|
|
|
block.col = (left.col + right.col) / 2;
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-13 22:38:46 +00:00
|
|
|
block.col = singleChild ? childColumn : (col - 2) / 2;
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
|
|
|
for (ut64 edge : block.new_exits) {
|
2017-12-14 21:07:48 +00:00
|
|
|
adjustGraphLayout(blocks[edge], col, 1);
|
|
|
|
col += blocks[edge].col_count;
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
if (col >= 2) {
|
2017-12-13 22:38:46 +00:00
|
|
|
// Place this node centered over the child nodes
|
|
|
|
block.col = singleChild ? childColumn : (col - 2) / 2;
|
|
|
|
block.col_count = col;
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-13 22:38:46 +00:00
|
|
|
//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
|
2018-03-21 20:32:32 +00:00
|
|
|
bool GraphView::isEdgeMarked(EdgesVector &edges, int row, int col, int index)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
if (index >= int(edges[row][col].size()))
|
2017-12-13 22:38:46 +00:00
|
|
|
return false;
|
|
|
|
return edges[row][col][index];
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
void GraphView::markEdge(EdgesVector &edges, int row, int col, int index, bool used)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
while (int(edges[row][col].size()) <= index)
|
2017-12-13 22:38:46 +00:00
|
|
|
edges[row][col].push_back(false);
|
|
|
|
edges[row][col][index] = used;
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
GraphView::GraphEdge GraphView::routeEdge(EdgesVector &horiz_edges, EdgesVector &vert_edges,
|
|
|
|
Matrix<bool> &edge_valid, GraphBlock &start, GraphBlock &end, QColor color)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
GraphEdge edge;
|
|
|
|
edge.color = color;
|
|
|
|
edge.dest = &end;
|
|
|
|
|
|
|
|
//Find edge index for initial outgoing line
|
|
|
|
int i = 0;
|
2018-03-21 20:32:32 +00:00
|
|
|
while (true) {
|
|
|
|
if (!isEdgeMarked(vert_edges, start.row + 1, start.col + 1, i))
|
2017-12-13 22:38:46 +00:00
|
|
|
break;
|
|
|
|
i += 1;
|
|
|
|
}
|
2017-12-14 21:07:48 +00:00
|
|
|
markEdge(vert_edges, start.row + 1, start.col + 1, i);
|
2017-12-13 22:38:46 +00:00
|
|
|
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;
|
2018-03-21 20:32:32 +00:00
|
|
|
if (end.row < (start.row + 1)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
min_row = end.row;
|
|
|
|
max_row = start.row + 1;
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-13 22:38:46 +00:00
|
|
|
min_row = start.row + 1;
|
|
|
|
max_row = end.row;
|
|
|
|
}
|
|
|
|
int col = start.col + 1;
|
2018-03-21 20:32:32 +00:00
|
|
|
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()))
|
2017-12-13 22:38:46 +00:00
|
|
|
return false;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int row = min_row; row < max_row; row++) {
|
|
|
|
if (!edge_valid[row][column]) {
|
2017-12-13 22:38:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!checkColumn(col)) {
|
|
|
|
if (checkColumn(end.col + 1)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
col = end.col + 1;
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-13 22:38:46 +00:00
|
|
|
int ofs = 0;
|
2018-03-21 20:32:32 +00:00
|
|
|
while (true) {
|
2017-12-13 22:38:46 +00:00
|
|
|
col = start.col + 1 - ofs;
|
2018-03-21 20:32:32 +00:00
|
|
|
if (checkColumn(col)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
col = start.col + 1 + ofs;
|
2018-03-21 20:32:32 +00:00
|
|
|
if (checkColumn(col)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ofs += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (col != (start.col + 1)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
//Not in same column, need to generate a line for moving to the correct column
|
|
|
|
int min_col, max_col;
|
2018-03-21 20:32:32 +00:00
|
|
|
if (col < (start.col + 1)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
min_col = col;
|
|
|
|
max_col = start.col + 1;
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-13 22:38:46 +00:00
|
|
|
min_col = start.col + 1;
|
|
|
|
max_col = col;
|
|
|
|
}
|
2017-12-14 21:07:48 +00:00
|
|
|
int index = findHorizEdgeIndex(horiz_edges, start.row + 1, min_col, max_col);
|
2017-12-13 22:38:46 +00:00
|
|
|
edge.addPoint(start.row + 1, col, index);
|
|
|
|
horiz = true;
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (end.row != (start.row + 1)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
//Not in same row, need to generate a line for moving to the correct row
|
2018-03-21 20:32:32 +00:00
|
|
|
if (col == (start.col + 1))
|
2017-12-14 21:07:48 +00:00
|
|
|
markEdge(vert_edges, start.row + 1, start.col + 1, i, false);
|
|
|
|
int index = findVertEdgeIndex(vert_edges, col, min_row, max_row);
|
2018-03-21 20:32:32 +00:00
|
|
|
if (col == (start.col + 1))
|
2017-12-13 22:38:46 +00:00
|
|
|
edge.start_index = index;
|
|
|
|
edge.addPoint(end.row, col, index);
|
|
|
|
horiz = false;
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (col != (end.col + 1)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
//Not in ending column, need to generate a line for moving to the correct column
|
|
|
|
int min_col, max_col;
|
2018-03-21 20:32:32 +00:00
|
|
|
if (col < (end.col + 1)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
min_col = col;
|
|
|
|
max_col = end.col + 1;
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-13 22:38:46 +00:00
|
|
|
min_col = end.col + 1;
|
|
|
|
max_col = col;
|
|
|
|
}
|
2017-12-14 21:07:48 +00:00
|
|
|
int index = findHorizEdgeIndex(horiz_edges, end.row, min_col, max_col);
|
2017-12-13 22:38:46 +00:00
|
|
|
edge.addPoint(end.row, end.col + 1, index);
|
|
|
|
horiz = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//If last line was horizontal, choose the ending edge index for the incoming edge
|
2018-03-21 20:32:32 +00:00
|
|
|
if (horiz) {
|
2017-12-14 21:07:48 +00:00
|
|
|
int index = findVertEdgeIndex(vert_edges, end.col + 1, end.row, end.row);
|
2017-12-13 22:38:46 +00:00
|
|
|
edge.points[int(edge.points.size()) - 1].index = index;
|
|
|
|
}
|
|
|
|
|
|
|
|
return edge;
|
|
|
|
}
|
|
|
|
|
2017-12-19 16:59:39 +00:00
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
int GraphView::findHorizEdgeIndex(EdgesVector &edges, int row, int min_col, int max_col)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
//Find a valid index
|
|
|
|
int i = 0;
|
2018-03-21 20:32:32 +00:00
|
|
|
while (true) {
|
2017-12-13 22:38:46 +00:00
|
|
|
bool valid = true;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int col = min_col; col < max_col + 1; col++)
|
|
|
|
if (isEdgeMarked(edges, row, col, i)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
valid = false;
|
|
|
|
break;
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
if (valid)
|
2017-12-13 22:38:46 +00:00
|
|
|
break;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Mark chosen index as used
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int col = min_col; col < max_col + 1; col++)
|
2017-12-14 21:07:48 +00:00
|
|
|
markEdge(edges, row, col, i);
|
2017-12-13 22:38:46 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
int GraphView::findVertEdgeIndex(EdgesVector &edges, int col, int min_row, int max_row)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
//Find a valid index
|
|
|
|
int i = 0;
|
2018-03-21 20:32:32 +00:00
|
|
|
while (true) {
|
2017-12-13 22:38:46 +00:00
|
|
|
bool valid = true;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int row = min_row; row < max_row + 1; row++)
|
|
|
|
if (isEdgeMarked(edges, row, col, i)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
valid = false;
|
|
|
|
break;
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
if (valid)
|
2017-12-13 22:38:46 +00:00
|
|
|
break;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Mark chosen index as used
|
2018-03-21 20:32:32 +00:00
|
|
|
for (int row = min_row; row < max_row + 1; row++)
|
2017-12-14 21:07:48 +00:00
|
|
|
markEdge(edges, row, col, i);
|
2017-12-13 22:38:46 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2019-02-16 17:17:11 +00:00
|
|
|
void GraphView::center()
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
2019-02-16 17:17:11 +00:00
|
|
|
centerX();
|
|
|
|
centerY();
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 17:17:11 +00:00
|
|
|
void GraphView::centerX()
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
2019-03-23 08:21:06 +00:00
|
|
|
offset.rx() = -((viewport()->width() - width * current_scale) / 2);
|
|
|
|
offset.rx() /= current_scale;
|
2019-02-16 17:17:11 +00:00
|
|
|
}
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-02-16 17:17:11 +00:00
|
|
|
void GraphView::centerY()
|
|
|
|
{
|
2019-03-23 08:21:06 +00:00
|
|
|
offset.ry() = -((viewport()->height() - height * current_scale) / 2);
|
|
|
|
offset.ry() /= current_scale;
|
2019-02-16 17:17:11 +00:00
|
|
|
}
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-02-16 17:17:11 +00:00
|
|
|
void GraphView::showBlock(GraphBlock &block)
|
|
|
|
{
|
|
|
|
showBlock(&block);
|
|
|
|
}
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-02-16 17:17:11 +00:00
|
|
|
void GraphView::showBlock(GraphBlock *block)
|
|
|
|
{
|
|
|
|
if (width * current_scale <= viewport()->width()) {
|
|
|
|
centerX();
|
2017-12-13 22:38:46 +00:00
|
|
|
} else {
|
2019-02-16 17:17:11 +00:00
|
|
|
int render_width = viewport()->width() / current_scale;
|
2019-03-23 08:21:06 +00:00
|
|
|
offset.rx() = block->x - ((render_width - block->width) / 2);
|
2019-02-16 17:17:11 +00:00
|
|
|
}
|
|
|
|
if (height * current_scale <= viewport()->height()) {
|
|
|
|
centerY();
|
|
|
|
} else {
|
2019-03-23 08:21:06 +00:00
|
|
|
offset.ry() = block->y - 30;
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
blockTransitionedTo(block);
|
2017-12-14 21:07:48 +00:00
|
|
|
viewport()->update();
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphView::adjustGraphLayout(GraphBlock &block, int col, int row)
|
|
|
|
{
|
|
|
|
block.col += col;
|
|
|
|
block.row += row;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (ut64 edge : block.new_exits) {
|
2017-12-14 21:07:48 +00:00
|
|
|
adjustGraphLayout(blocks[edge], col, row);
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GraphView::addBlock(GraphView::GraphBlock block)
|
|
|
|
{
|
2017-12-14 21:07:48 +00:00
|
|
|
blocks[block.entry] = block;
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
|
2017-12-19 16:59:39 +00:00
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
void GraphView::setEntry(ut64 e)
|
|
|
|
{
|
2017-12-14 21:07:48 +00:00
|
|
|
entry = e;
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool GraphView::checkPointClicked(QPointF &point, int x, int y, bool above_y)
|
|
|
|
{
|
|
|
|
int half_target_size = 5;
|
2018-03-21 20:32:32 +00:00
|
|
|
if ((point.x() - half_target_size < x) &&
|
2017-12-13 22:38:46 +00:00
|
|
|
(point.y() - (above_y ? (2 * half_target_size) : 0) < y) &&
|
|
|
|
(x < point.x() + half_target_size) &&
|
2018-03-21 20:32:32 +00:00
|
|
|
(y < point.y() + (above_y ? 0 : (3 * half_target_size)))) {
|
2017-12-13 22:38:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mouse events
|
|
|
|
void GraphView::mousePressEvent(QMouseEvent *event)
|
|
|
|
{
|
2019-03-23 08:21:06 +00:00
|
|
|
int x = event->pos().x() / current_scale + offset.x();
|
|
|
|
int y = event->pos().y() / current_scale + offset.y();
|
2017-12-13 22:38:46 +00:00
|
|
|
|
|
|
|
// Check if a block was clicked
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if ((block.x <= x) && (block.y <= y) &&
|
|
|
|
(x <= block.x + block.width) & (y <= block.y + block.height)) {
|
2017-12-13 22:38:46 +00:00
|
|
|
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
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-13 22:38:46 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
2018-03-21 20:32:32 +00:00
|
|
|
for (GraphEdge &edge : block.edges) {
|
|
|
|
if (edge.polyline.length() < 2) {
|
2017-12-13 22:38:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
QPointF start = edge.polyline.first();
|
|
|
|
QPointF end = edge.polyline.last();
|
2018-03-21 20:32:32 +00:00
|
|
|
if (checkPointClicked(start, x, y)) {
|
2019-02-16 17:17:11 +00:00
|
|
|
showBlock(edge.dest);
|
2017-12-13 22:38:46 +00:00
|
|
|
// TODO: Callback to child
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
if (checkPointClicked(end, x, y, true)) {
|
2019-02-16 17:17:11 +00:00
|
|
|
showBlock(block);
|
2017-12-13 22:38:46 +00:00
|
|
|
// TODO: Callback to child
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No block was clicked
|
2018-03-21 20:32:32 +00:00
|
|
|
if (event->button() == Qt::LeftButton) {
|
2017-12-13 22:38:46 +00:00
|
|
|
//Left click outside any block, enter scrolling mode
|
2017-12-14 21:07:48 +00:00
|
|
|
scroll_base_x = event->x();
|
|
|
|
scroll_base_y = event->y();
|
|
|
|
scroll_mode = true;
|
|
|
|
setCursor(Qt::ClosedHandCursor);
|
|
|
|
viewport()->grabMouse();
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
void GraphView::mouseMoveEvent(QMouseEvent *event)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
if (scroll_mode) {
|
2019-03-23 08:21:06 +00:00
|
|
|
offset.rx() += (scroll_base_x - event->x()) / current_scale;
|
|
|
|
offset.ry() += (scroll_base_y - event->y()) / current_scale;
|
2017-12-14 21:07:48 +00:00
|
|
|
scroll_base_x = event->x();
|
|
|
|
scroll_base_y = event->y();
|
2019-02-16 17:17:11 +00:00
|
|
|
viewport()->update();
|
2017-12-14 21:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GraphView::mouseDoubleClickEvent(QMouseEvent *event)
|
|
|
|
{
|
2019-03-23 08:21:06 +00:00
|
|
|
int x = event->pos().x() / current_scale + offset.x();
|
|
|
|
int y = event->pos().y() / current_scale + offset.y();
|
2017-12-14 21:07:48 +00:00
|
|
|
|
|
|
|
// Check if a block was clicked
|
2018-03-21 20:32:32 +00:00
|
|
|
for (auto &blockIt : blocks) {
|
2017-12-14 21:07:48 +00:00
|
|
|
GraphBlock &block = blockIt.second;
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if ((block.x <= x) && (block.y <= y) &&
|
|
|
|
(x <= block.x + block.width) & (y <= block.y + block.height)) {
|
2017-12-14 21:07:48 +00:00
|
|
|
QPoint pos = QPoint(x - block.x, y - block.y);
|
|
|
|
blockDoubleClicked(block, event, pos);
|
|
|
|
return;
|
|
|
|
}
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
void GraphView::mouseReleaseEvent(QMouseEvent *event)
|
2017-12-13 22:38:46 +00:00
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
// if(event->button() == Qt::ForwardButton)
|
|
|
|
// gotoNextSlot();
|
|
|
|
// else if(event->button() == Qt::BackButton)
|
|
|
|
// gotoPreviousSlot();
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (event->button() != Qt::LeftButton)
|
2017-12-13 22:38:46 +00:00
|
|
|
return;
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (scroll_mode) {
|
2017-12-14 21:07:48 +00:00
|
|
|
scroll_mode = false;
|
|
|
|
setCursor(Qt::ArrowCursor);
|
|
|
|
viewport()->releaseMouse();
|
2017-12-13 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-21 17:33:46 +00:00
|
|
|
|
|
|
|
void GraphView::wheelEvent(QWheelEvent *event)
|
|
|
|
{
|
2019-03-23 08:21:06 +00:00
|
|
|
QPoint delta = -event->angleDelta();
|
|
|
|
|
|
|
|
delta /= current_scale;
|
|
|
|
offset += delta;
|
|
|
|
|
2019-02-16 17:17:11 +00:00
|
|
|
viewport()->update();
|
2018-05-21 17:33:46 +00:00
|
|
|
event->accept();
|
|
|
|
}
|