diff --git a/src/gui/Src/Graph/GraphEdge.h b/src/gui/Src/Graph/GraphEdge.h new file mode 100644 index 00000000..53dd979a --- /dev/null +++ b/src/gui/Src/Graph/GraphEdge.h @@ -0,0 +1,253 @@ +#ifndef _GRAPH_EDGE_H +#define _GRAPH_EDGE_H + +#include +#include +#include + +class GraphEdge : public QAbstractGraphicsShapeItem +{ +public: + GraphEdge(QPointF start, QPointF end, ogdf::DPolyline bends, QRectF sourceRect, QRectF targetRect) : QAbstractGraphicsShapeItem() + { + QList linePoints = calculateLine(start, end, bends, sourceRect, targetRect); + for(auto p : linePoints) + qDebug() << p; + QList arrowPoints = calculateArrow(linePoints); + _boundingRect = calculateBoundingRect(linePoints, arrowPoints); + preparePainterPaths(linePoints, arrowPoints); + } + + QRectF boundingRect() const + { + return _boundingRect; + } + + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) + { + Q_UNUSED(option); + Q_UNUSED(widget); + + //save painter + painter->save(); + +#if _DEBUG + //draw bounding rect + painter->setPen(QPen(Qt::red, 1)); + painter->drawRect(boundingRect()); +#endif //_DEBUG + + //set painter options + painter->setRenderHint(QPainter::Antialiasing); + + int lineSize = 2; + + //draw line + painter->setPen(QPen(Qt::green, lineSize)); + painter->drawPath(_line); + + //draw arrow + painter->setPen(QPen(Qt::green, lineSize)); + painter->drawPath(_arrow); + + //restore painter + painter->restore(); + } + + qreal calculateDistance(QPointF p1, QPointF p2) + { + QPointF d = p2 - p1; + return sqrt(d.x() * d.x() + d.y() * d.y()); + } + + QPointF calculateNearestIntersect(QRectF rect, QPointF p1, QPointF p2) + { + /*qDebug() << "calculateNearest"; + qDebug() << "rect" << rect.topLeft() << rect.bottomRight(); + qDebug() << "p1" << p1; + qDebug() << "p2" << p2;*/ + + //y=a*x+b + //a = dy/dx = (p1.y-p2.y)/(p1.x-p2.x) + //b = p1.y - p1.x; + + qreal div = p1.x()-p2.x(); + + if(div == 0) + { + QPointF i1(p1.x(), rect.top()); + //qDebug() << "i1" << i1; + QPointF i2(p1.x(), rect.bottom()); + //qDebug() << "i2" << i2; + + if(p2.y() < p1.y()) + return i1; + else + return i2; + } + else + { + QPointF result; + qreal bestDist = 10e99; + + qreal a = (p1.y()-p2.y()) / div; + qreal b = p1.y() - a * p1.x(); + //qDebug() << "a" << a << "b" << b; + + //intersect 1 + //rect.top() = a*x+b; + //x = (b - rect.top()) / -a + QPointF i1((b - rect.top()) / -a, rect.top()); + //qDebug() << "i1" << i1; + //qDebug() << "consider?" << rect.contains(i1); + if(rect.contains(i1)) + { + qreal dist = calculateDistance(p2, i1); + if(dist < bestDist) + { + bestDist = dist; + result = i1; + } + } + + //intersect 2 + //rect.bottom() = a*x+b + //x = (b - rect.bottom()) / -a + QPointF i2((b - rect.bottom()) / -a, rect.bottom()); + //qDebug() << "i2" << i2; + //qDebug() << "consider?" << rect.contains(i2); + if(rect.contains(i2)) + { + qreal dist = calculateDistance(p2, i2); + if(dist < bestDist) + { + bestDist = dist; + result = i2; + } + } + + //intersect 3 + //x=rect.left() + QPointF i3(rect.left(), a * rect.left() + b); + //qDebug() << "i3" << i3; + //qDebug() << "consider?" << rect.contains(i3); + if(rect.contains(i3)) + { + qreal dist = calculateDistance(p2, i3); + if(dist < bestDist) + { + bestDist = dist; + result = i3; + } + } + + //intersect 4 + //x=rect.right() + QPointF i4(rect.right(), a * rect.right() + b); + //qDebug() << "i4" << i4; + //qDebug() << "consider?" << rect.contains(i4); + if(rect.contains(i4)) + { + qreal dist = calculateDistance(p2, i4); + if(dist < bestDist) + { + bestDist = dist; + result = i4; + } + } + return result; + } + //qDebug() << " "; + } + + QList calculateLine(QPointF start, QPointF end, ogdf::DPolyline bends, QRectF sourceRect, QRectF targetRect) + { + QList linePoints; + linePoints << start; + for(auto p : bends) + linePoints << QPointF(p.m_x, p.m_y); + linePoints << end; + + QPointF nearestI = calculateNearestIntersect(sourceRect, linePoints[0], linePoints[1]); + linePoints[0]=nearestI; + int len = linePoints.length(); + nearestI = calculateNearestIntersect(targetRect, linePoints[len-1], linePoints[len-2]); + linePoints[len-1]=nearestI; + + return linePoints; + } + + QList calculateArrow(const QList & linePoints) + { + //arrow + int len=linePoints.length(); + QLineF perpLine = QLineF(linePoints[len-1], linePoints[len-2]).normalVector(); + + qreal arrowLen = 6; + + QLineF a; + a.setP1(linePoints[len-1]); + a.setAngle(perpLine.angle() - 45); + a.setLength(arrowLen); + + QLineF b; + b.setP1(linePoints[len-1]); + b.setAngle(perpLine.angle() - 135); + b.setLength(arrowLen); + + QLineF c; + c.setP1(a.p2()); + c.setP2(b.p2()); + + QList arrowPoints; + arrowPoints << a.p1() << a.p2() << b.p1() << b.p2() << c.p1() << c.p2(); + return arrowPoints; + } + + QRectF calculateBoundingRect(const QList & linePoints, const QList & arrowPoints) + { + QList allPoints; + allPoints << linePoints << arrowPoints; + //find top-left and bottom-right points for the bounding rect + QPointF topLeft = allPoints[0]; + QPointF bottomRight = topLeft; + for(auto p : allPoints) + { + qreal x = p.x(); + qreal y = p.y(); + + if(x < topLeft.x()) + topLeft.setX(x); + if(y < topLeft.y()) + topLeft.setY(y); + + if(x > bottomRight.x()) + bottomRight.setX(x); + if(y > bottomRight.y()) + bottomRight.setY(y); + } + return QRectF(topLeft, bottomRight); + } + + void preparePainterPaths(const QList & linePoints, const QList & arrowPoints) + { + //edge line + QPolygonF polyLine; + for(auto p : linePoints) + polyLine << p; + _line.addPolygon(polyLine); + + //arrow + QPolygonF polyArrow; + for(auto p : arrowPoints) + polyArrow << p; + _arrow.addPolygon(polyArrow); + } + +private: + QPainterPath _line; + QPainterPath _arrow; + QRectF _boundingRect; +}; + +#endif //_GRAPH_EDGE_H diff --git a/src/gui/Src/Graph/GraphNode.h b/src/gui/Src/Graph/GraphNode.h new file mode 100644 index 00000000..17662a9d --- /dev/null +++ b/src/gui/Src/Graph/GraphNode.h @@ -0,0 +1,94 @@ +#ifndef _GRAPH_NODE_H +#define _GRAPH_NODE_H + +#include +#include +#include + +class GraphNode : public QWidget +{ +public: + GraphNode() + { + } + + GraphNode(QString label) + { + setLabel(label); + setStyleSheet("border: 1px solid blue"); + setContentsMargins(0,0,0,0); + } + + GraphNode(const GraphNode & other) + { + setLabel(other._label); + } + + GraphNode & operator=(const GraphNode & other) + { + setLabel(other._label); + return *this; + } + + QRectF boundingRect() const + { + return QRectF(0, 0, _cachedWidth, _cachedHeight); + } + + void paintEvent(QPaintEvent* event) + { + Q_UNUSED(event); + + QPainter painter(this); + + painter.save(); + + //draw bounding rectangle + QRectF rect = boundingRect(); + painter.setPen(Qt::red); + //painter.drawRect(rect); + + //draw node contents + painter.setPen(Qt::black); + painter.setFont(this->_font); + QRect textRect = QRect(_spacingX, _spacingY, rect.width() - _spacingX, rect.height() - _spacingY); + painter.drawText(textRect, _label); + + painter.restore(); + } + + void mousePressEvent(QMouseEvent* event) + { + Q_UNUSED(event); + + QMessageBox::information(nullptr, "clicked", _label); + } + + void setLabel(const QString & label) + { + _label = label; + updateCache(); + } + + void updateCache() + { + QFontMetrics metrics(this->_font); + _cachedWidth = metrics.width(this->_label) + _spacingX * 2; + _cachedHeight = metrics.height() + _spacingY * 2; + } + + QString label() + { + return _label; + } + +private: + QString _label; + QFont _font = QFont("Lucida Console", 8, QFont::Normal, false); + const qreal _spacingX = 3; + const qreal _spacingY = 3; + qreal _cachedWidth; + qreal _cachedHeight; +}; + +#endif //_GRAPH_NODE_H diff --git a/src/gui/Src/Graph/Node.h b/src/gui/Src/Graph/Node.h new file mode 100644 index 00000000..b51e998f --- /dev/null +++ b/src/gui/Src/Graph/Node.h @@ -0,0 +1,71 @@ +#ifndef _NODE_H +#define _NODE_H + +#include +#include + +template +class Node +{ +public: + explicit Node(ogdf::Graph* G, ogdf::GraphAttributes* GA, ogdf::node ogdfNode, T data) + { + this->_G = G; + this->_GA = GA; + this->_ogdfNode = ogdfNode; + this->_data = data; + } + + Node* setLeft(Node* left) + { + return left ? this->_left = makeNodeWithEdge(left) : nullptr; + } + + Node* setRight(Node* right) + { + return right ? this->_right = makeNodeWithEdge(right) : nullptr; + } + + Node* setData(T data) + { + this->_data = data; + return this; + } + + Node* left() + { + return this->_left; + } + + Node* right() + { + return this->_right; + } + + T data() + { + return this->_data; + } + + ogdf::node ogdfNode() + { + return this->_ogdfNode; + } + +private: + Node* _left; + Node* _right; + T _data; + ogdf::Graph* _G; + ogdf::GraphAttributes* _GA; + ogdf::node _ogdfNode; + + Node* makeNodeWithEdge(Node* node) + { + auto edge = this->_G->newEdge(this->_ogdfNode, node->_ogdfNode); + this->_GA->arrowType(edge) = ogdf::EdgeArrow::eaLast; + return node; + } +}; + +#endif //_NODE_H diff --git a/src/gui/Src/Graph/Tree.h b/src/gui/Src/Graph/Tree.h new file mode 100644 index 00000000..74f7c83c --- /dev/null +++ b/src/gui/Src/Graph/Tree.h @@ -0,0 +1,44 @@ +#ifndef _TREE_H +#define _TREE_H + +#include "Node.h" + +#include +#include +#include +#include +#include + +template +class Tree +{ +public: + explicit Tree(ogdf::Graph* G, ogdf::GraphAttributes* GA) + { + this->_G = G; + this->_GA = GA; + } + + Node* newNode(T data) + { + auto ogdfNode = _G->newNode(); + auto node = new Node(_G, _GA, ogdfNode, data); + _ogdfDataMap[ogdfNode] = node; + _nodePool.push_back(std::unique_ptr>(node)); + return node; + } + + Node* findNode(ogdf::node ogdfNode) + { + auto found = _ogdfDataMap.find(ogdfNode); + return found != _ogdfDataMap.end() ? found->second : nullptr; + } + +private: + ogdf::Graph* _G; + ogdf::GraphAttributes* _GA; + std::vector>> _nodePool; + std::map*> _ogdfDataMap; +}; + +#endif //_TREE_H diff --git a/src/gui/Src/Gui/GraphView.cpp b/src/gui/Src/Gui/GraphView.cpp new file mode 100644 index 00000000..5152d77b --- /dev/null +++ b/src/gui/Src/Gui/GraphView.cpp @@ -0,0 +1,138 @@ +#include "GraphView.h" +#include "ui_GraphView.h" + +#include "Tree.h" +#include "GraphEdge.h" +#include "GraphNode.h" + +#include +#include +#include +#include +#include + +#include "Bridge.h" + +GraphView::GraphView(QWidget *parent) : + QWidget(parent), + ui(new Ui::GraphView) +{ + ui->setupUi(this); + setupGraph(); +} + +void GraphView::setupGraph() +{ + using namespace ogdf; + + //initialize graph + Graph G; + GraphAttributes GA(G, GraphAttributes::nodeGraphics | + GraphAttributes::edgeGraphics | + GraphAttributes::nodeLabel | + GraphAttributes::nodeStyle | + GraphAttributes::edgeType | + GraphAttributes::edgeArrow | + GraphAttributes::edgeStyle); + + //add nodes + Tree tree(&G, &GA); + auto root = tree.newNode(new GraphNode("rp")); + auto left = root->setLeft(tree.newNode(new GraphNode("left 1"))); + left->setLeft(tree.newNode(new GraphNode("left 2"))); + left->setRight(tree.newNode(new GraphNode("right 2"))); + auto right = root->setRight(tree.newNode(new GraphNode("right 1"))); + right->setLeft(tree.newNode(new GraphNode("left 3")))->setRight(left); + right->setRight(tree.newNode(new GraphNode("right 3")))->setRight(tree.newNode(new GraphNode("nice long text :)")))->setLeft(root); + + //adjust node size + node v; + forall_nodes(v, G) + { + auto node = tree.findNode(v); + if (node) + { + auto rect = node->data()->boundingRect(); + GA.width(v) = rect.width(); + GA.height(v) = rect.height(); + } + } + + //do layout + OptimalHierarchyLayout* OHL = new OptimalHierarchyLayout; + OHL->nodeDistance(25.0); + OHL->layerDistance(50.0); + OHL->weightBalancing(0.0); + OHL->weightSegments(0.0); + + SugiyamaLayout SL; + SL.setRanking(new OptimalRanking); + SL.setCrossMin(new MedianHeuristic); + SL.alignSiblings(false); + SL.setLayout(OHL); + SL.call(GA); + + QGraphicsScene* scene = new QGraphicsScene(this); + + //draw widget contents (nodes) + forall_nodes(v, G) + { + auto node = tree.findNode(v); + if (node) + { + //draw node using x,y + auto rect = node->data()->boundingRect(); + qreal x = GA.x(v) - (rect.width()/2); + qreal y = GA.y(v) - (rect.height()/2); + node->data()->setGeometry(x, y, rect.width(), rect.height()); + scene->addWidget(node->data()); + } + } + + //draw edges + edge e; + forall_edges(e, G) + { + const auto bends = GA.bends(e); + const auto source = e->source(); + const auto target = e->target(); + + GraphNode* sourceGraphNode = tree.findNode(source)->data(); + GraphNode* targetGraphNode = tree.findNode(target)->data(); + qDebug() << "edge" << sourceGraphNode->label() << "->" << targetGraphNode->label(); + + QRectF sourceRect = sourceGraphNode->geometry(); + sourceRect.adjust(-4, -4, 4, 4); + QRectF targetRect = targetGraphNode->geometry(); + targetRect.adjust(-4, -4, 4, 4); + + QPointF start(GA.x(source), GA.y(source)); + QPointF end(GA.x(target), GA.y(target)); + GraphEdge* edge = new GraphEdge(start, end, bends, sourceRect, targetRect); + + scene->addItem(edge); + } + + //draw scene + scene->setBackgroundBrush(QBrush(Qt::darkGray)); + + ui->graphicsView->setScene(scene); + + //make sure there is some spacing + QRectF sceneRect = ui->graphicsView->sceneRect(); + sceneRect.adjust(-20, -20, 20, 20); + ui->graphicsView->setSceneRect(sceneRect); + + ui->graphicsView->show(); + + + + //qDebug() << "sceneRect()" << ui->graphicsView->sceneRect(); + + GraphIO::drawSVG(GA, "test.svg"); +} + +GraphView::~GraphView() +{ + delete ui; +} diff --git a/src/gui/Src/Gui/GraphView.h b/src/gui/Src/Gui/GraphView.h new file mode 100644 index 00000000..800403c7 --- /dev/null +++ b/src/gui/Src/Gui/GraphView.h @@ -0,0 +1,25 @@ +#ifndef GRAPHVIEW_H +#define GRAPHVIEW_H + +#include + +namespace Ui +{ +class GraphView; +} + +class GraphView : public QWidget +{ + Q_OBJECT + +public: + explicit GraphView(QWidget *parent = 0); + ~GraphView(); + +private: + void setupGraph(); + + Ui::GraphView *ui; +}; + +#endif // GRAPHVIEW_H diff --git a/src/gui/Src/Gui/GraphView.ui b/src/gui/Src/Gui/GraphView.ui new file mode 100644 index 00000000..cc3206cb --- /dev/null +++ b/src/gui/Src/Gui/GraphView.ui @@ -0,0 +1,24 @@ + + + GraphView + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + + + + + diff --git a/src/gui/Src/Gui/MainWindow.cpp b/src/gui/Src/Gui/MainWindow.cpp index 05e79f98..4a9fec11 100644 --- a/src/gui/Src/Gui/MainWindow.cpp +++ b/src/gui/Src/Gui/MainWindow.cpp @@ -141,6 +141,11 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi mNotesManager->setWindowTitle("Notes"); mNotesManager->setWindowIcon(QIcon(":/icons/images/notes.png")); + // Graph view + mGraphView = new GraphView(this); + mGraphView->setWindowTitle("Graph"); + mGraphView->setWindowIcon(QIcon(":/icons/images/graph.png")); + // Create the tab widget mTabWidget = new MHTabWidget(NULL); @@ -157,6 +162,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi addQWidgetTab(mReferenceManager); addQWidgetTab(mThreadView); addQWidgetTab(mSnowmanView); + addQWidgetTab(mGraphView); setCentralWidget(mTabWidget); diff --git a/src/gui/Src/Gui/MainWindow.h b/src/gui/Src/Gui/MainWindow.h index be0cb8bc..9ed57866 100644 --- a/src/gui/Src/Gui/MainWindow.h +++ b/src/gui/Src/Gui/MainWindow.h @@ -24,6 +24,7 @@ #include "MainWindowCloseThread.h" #include "TimeWastedCounter.h" #include "NotesManager.h" +#include "GraphView.h" namespace Ui { @@ -131,6 +132,7 @@ private: CalculatorDialog* mCalculatorDialog; SnowmanView* mSnowmanView; NotesManager* mNotesManager; + GraphView* mGraphView; StatusLabel* mStatusLabel; StatusLabel* mLastLogLabel; diff --git a/src/gui/Src/ThirdPartyLibs/snowman/SnowmanView.h b/src/gui/Src/ThirdPartyLibs/snowman/SnowmanView.h index ce0ff1c8..57eff423 100644 --- a/src/gui/Src/ThirdPartyLibs/snowman/SnowmanView.h +++ b/src/gui/Src/ThirdPartyLibs/snowman/SnowmanView.h @@ -2,6 +2,7 @@ #define SNOWMANVIEW_H #include +#include "Imports.h" class SnowmanView : public QWidget { diff --git a/src/gui/images/graph.png b/src/gui/images/graph.png new file mode 100644 index 00000000..9b418337 Binary files /dev/null and b/src/gui/images/graph.png differ diff --git a/src/gui/resource.qrc b/src/gui/resource.qrc index 77ba020e..58dab5c2 100644 --- a/src/gui/resource.qrc +++ b/src/gui/resource.qrc @@ -65,5 +65,6 @@ images/entropy.png images/notes.png images/win-help.png + images/graph.png diff --git a/src/gui/x64dbg.pro b/src/gui/x64dbg.pro index 4a0b965c..2fa667ba 100644 --- a/src/gui/x64dbg.pro +++ b/src/gui/x64dbg.pro @@ -8,13 +8,17 @@ ## Pre-defined global variables ## +OGDF_INCLUDE_DIR = C:/OGDF/include + !contains(QMAKE_HOST.arch, x86_64) { X64_BIN_DIR = ../../bin/x32 # Relative BIN path, 32-bit X64_GEN_DIR = ../gui_build/out32 # QMake temporary generated files, placed inside the build folder. (OBJ, UI, MOC) + OGDF_BIN_DIR = C:/OGDF/Win32/Release # OGDF TARGET = x32gui # Build x32gui } else { X64_BIN_DIR = ../../bin/x64 # Relative BIN path, 64-bit X64_GEN_DIR = ../gui_build/out64 # QMake temporary generated files, placed inside the build folder. (OBJ, UI, MOC) + OGDF_BIN_DIR = C:/OGDF/x64/Release # OGDF TARGET = x64gui # Build x64gui } @@ -61,7 +65,9 @@ INCLUDEPATH += \ Src/Bridge \ Src/Global \ Src/Utils \ - ../capstone_wrapper + Src/Graph \ + ../capstone_wrapper \ + $${OGDF_INCLUDE_DIR} # Resources, sources, headers, and forms RESOURCES += \ @@ -139,7 +145,8 @@ SOURCES += \ Src/Gui/EntropyDialog.cpp \ Src/Gui/NotesManager.cpp \ Src/Gui/NotepadView.cpp \ - Src/Gui/CPUMultiDump.cpp + Src/Gui/CPUMultiDump.cpp \ + Src/Gui/GraphView.cpp HEADERS += \ @@ -220,7 +227,14 @@ HEADERS += \ Src/Gui/NotepadView.h \ Src/Utils/MenuBuilder.h \ Src/Utils/QActionLambda.h \ - Src/Gui/CPUMultiDump.h + Src/Gui/CPUMultiDump.h \ + Src/BasicView/HeaderButton.h \ + Src/ThirdPartyLibs/snowman/SnowmanView.h \ + Src/Graph/GraphEdge.h \ + Src/Graph/GraphNode.h \ + Src/Graph/Node.h \ + Src/Graph/Tree.h \ + Src/Gui/GraphView.h FORMS += \ Src/Gui/MainWindow.ui \ @@ -245,7 +259,8 @@ FORMS += \ Src/Gui/SelectFields.ui \ Src/Gui/YaraRuleSelectionDialog.ui \ Src/Gui/DataCopyDialog.ui \ - Src/Gui/EntropyDialog.ui + Src/Gui/EntropyDialog.ui \ + Src/Gui/GraphView.ui ## ## Libraries @@ -257,9 +272,11 @@ LIBS += -luser32 LIBS += -L"$$PWD/../dbg/capstone/" -lcapstone_x86 LIBS += -L"$$PWD/Src/ThirdPartyLibs/snowman/" -lsnowman_x86 LIBS += -L"$${X64_BIN_DIR}/" -lx32bridge -lcapstone_wrapper + LIBS += -L"$${OGDF_BIN_DIR}/" -lcoin -logdf } else { # Windows x64 (64bit) specific build LIBS += -L"$$PWD/../dbg/capstone/" -lcapstone_x64 LIBS += -L"$$PWD/Src/ThirdPartyLibs/snowman/" -lsnowman_x64 LIBS += -L"$${X64_BIN_DIR}/" -lx64bridge -lcapstone_wrapper + LIBS += -L"$${OGDF_BIN_DIR}/" -lcoin -logdf }