2017-12-13 22:38:46 +00:00
|
|
|
#ifndef GRAPHVIEW_H
|
|
|
|
#define GRAPHVIEW_H
|
|
|
|
|
|
|
|
#include <QObject>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QWidget>
|
|
|
|
#include <QAbstractScrollArea>
|
|
|
|
#include <QScrollBar>
|
2017-12-14 21:07:48 +00:00
|
|
|
#include <QElapsedTimer>
|
2017-12-19 16:59:39 +00:00
|
|
|
#include <QHelpEvent>
|
2017-12-13 22:38:46 +00:00
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <unordered_set>
|
|
|
|
#include <queue>
|
2019-04-04 05:54:42 +00:00
|
|
|
#include <memory>
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-02-22 16:50:45 +00:00
|
|
|
#include "core/Cutter.h"
|
2019-04-04 05:54:42 +00:00
|
|
|
#include "widgets/GraphLayout.h"
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-10-06 17:35:44 +00:00
|
|
|
#if defined(QT_NO_OPENGL) || QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
|
|
|
|
// QOpenGLExtraFunctions were introduced in 5.6
|
|
|
|
#define CUTTER_NO_OPENGL_GRAPH
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef CUTTER_NO_OPENGL_GRAPH
|
2019-04-07 10:53:42 +00:00
|
|
|
class QOpenGLWidget;
|
|
|
|
#endif
|
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
class GraphView : public QAbstractScrollArea
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
2019-04-14 12:18:24 +00:00
|
|
|
|
2019-01-24 17:13:04 +00:00
|
|
|
signals:
|
2019-04-14 12:18:24 +00:00
|
|
|
void viewOffsetChanged(QPoint offset);
|
|
|
|
void viewScaleChanged(qreal scale);
|
2019-01-24 17:13:04 +00:00
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
public:
|
2019-04-04 05:54:42 +00:00
|
|
|
using GraphBlock = GraphLayout::GraphBlock;
|
|
|
|
using GraphEdge = GraphLayout::GraphEdge;
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-08-03 13:10:44 +00:00
|
|
|
enum class Layout {
|
|
|
|
GridNarrow
|
2019-09-19 05:19:50 +00:00
|
|
|
, GridMedium
|
|
|
|
, GridWide
|
2019-08-03 13:10:44 +00:00
|
|
|
#ifdef CUTTER_ENABLE_GRAPHVIZ
|
2019-09-19 05:19:50 +00:00
|
|
|
, GraphvizOrtho
|
|
|
|
, GraphvizOrthoLR
|
|
|
|
, GraphvizPolyline
|
|
|
|
, GraphvizPolylineLR
|
2019-08-03 13:10:44 +00:00
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
struct EdgeConfiguration {
|
2017-12-13 22:38:46 +00:00
|
|
|
QColor color = QColor(128, 128, 128);
|
|
|
|
bool start_arrow = false;
|
|
|
|
bool end_arrow = true;
|
2019-01-24 17:13:04 +00:00
|
|
|
qreal width_scale = 1.0;
|
2019-07-30 18:35:00 +00:00
|
|
|
Qt::PenStyle lineStyle = Qt::PenStyle::SolidLine;
|
2017-12-13 22:38:46 +00:00
|
|
|
};
|
|
|
|
|
2019-01-13 14:40:37 +00:00
|
|
|
explicit GraphView(QWidget *parent);
|
|
|
|
~GraphView() override;
|
2019-04-07 10:53:42 +00:00
|
|
|
|
2019-05-19 10:27:15 +00:00
|
|
|
void showBlock(GraphBlock &block, bool anywhere = false);
|
|
|
|
void showBlock(GraphBlock *block, bool anywhere = false);
|
|
|
|
/**
|
|
|
|
* @brief Move view so that area is visible.
|
|
|
|
* @param rect Rectangle to show
|
|
|
|
* @param anywhere - set to true for minimizing movement
|
|
|
|
*/
|
|
|
|
void showRectangle(const QRect &rect, bool anywhere = false);
|
2019-10-06 17:35:44 +00:00
|
|
|
/**
|
|
|
|
* @brief Get block containing specified point logical coordinates.
|
|
|
|
* @param p positionin graph logical coordinates
|
|
|
|
* @return Block or nullptr if position is outside all blocks.
|
|
|
|
*/
|
|
|
|
GraphView::GraphBlock *getBlockContaining(QPoint p);
|
|
|
|
QPoint viewToLogicalCoordinates(QPoint p);
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-08-03 13:10:44 +00:00
|
|
|
void setGraphLayout(Layout layout);
|
|
|
|
Layout getGraphLayout() const { return graphLayout; }
|
|
|
|
|
2019-09-19 05:19:50 +00:00
|
|
|
void paint(QPainter &p, QPoint offset, QRect area, qreal scale = 1.0, bool interactive = true);
|
|
|
|
|
|
|
|
void saveAsBitmap(QString path, const char *format = nullptr);
|
|
|
|
void saveAsSvg(QString path);
|
2017-12-13 22:38:46 +00:00
|
|
|
protected:
|
|
|
|
std::unordered_map<ut64, GraphBlock> blocks;
|
|
|
|
QColor backgroundColor = QColor(Qt::white);
|
2017-12-14 21:07:48 +00:00
|
|
|
|
|
|
|
// Padding inside the block
|
|
|
|
int block_padding = 16;
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-04-08 06:59:16 +00:00
|
|
|
void setCacheDirty() { cacheDirty = true; }
|
2017-12-14 21:07:48 +00:00
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
void addBlock(GraphView::GraphBlock block);
|
|
|
|
void setEntry(ut64 e);
|
|
|
|
void computeGraph(ut64 entry);
|
|
|
|
|
|
|
|
// Callbacks that should be overridden
|
2019-09-19 05:19:50 +00:00
|
|
|
/**
|
|
|
|
* @brief drawBlock
|
|
|
|
* @param p painter object, not necesarily current widget
|
|
|
|
* @param block
|
|
|
|
* @param interactive - can be used for disabling elemnts during export
|
|
|
|
*/
|
|
|
|
virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive = true);
|
2017-12-13 22:38:46 +00:00
|
|
|
virtual void blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos);
|
2017-12-14 21:07:48 +00:00
|
|
|
virtual void blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos);
|
2017-12-19 16:59:39 +00:00
|
|
|
virtual void blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event, QPoint pos);
|
|
|
|
virtual bool helpEvent(QHelpEvent *event);
|
2017-12-13 22:38:46 +00:00
|
|
|
virtual void blockTransitionedTo(GraphView::GraphBlock *to);
|
2018-05-21 17:33:46 +00:00
|
|
|
virtual void wheelEvent(QWheelEvent *event) override;
|
2019-09-19 05:19:50 +00:00
|
|
|
virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to,
|
|
|
|
bool interactive = true);
|
2019-10-06 17:35:44 +00:00
|
|
|
virtual void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,
|
|
|
|
QPoint pos);
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2018-03-11 10:29:37 +00:00
|
|
|
bool event(QEvent *event) override;
|
2019-10-06 17:35:44 +00:00
|
|
|
void contextMenuEvent(QContextMenuEvent *event) override;
|
2019-01-24 17:13:04 +00:00
|
|
|
|
|
|
|
// Mouse events
|
|
|
|
void mousePressEvent(QMouseEvent *event) override;
|
|
|
|
void mouseMoveEvent(QMouseEvent *event) override;
|
|
|
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
|
|
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
|
|
|
|
2019-04-10 19:07:53 +00:00
|
|
|
void keyPressEvent(QKeyEvent *event) override;
|
|
|
|
|
2019-04-08 06:59:16 +00:00
|
|
|
void paintEvent(QPaintEvent *event) override;
|
|
|
|
|
2019-01-24 17:13:04 +00:00
|
|
|
int width = 0;
|
|
|
|
int height = 0;
|
2019-07-30 18:35:00 +00:00
|
|
|
bool scale_thickness_multiplier = false;
|
2019-03-12 07:37:10 +00:00
|
|
|
|
2019-04-21 16:30:57 +00:00
|
|
|
void clampViewOffset();
|
|
|
|
void setViewOffsetInternal(QPoint pos, bool emitSignal = true);
|
|
|
|
void addViewOffset(QPoint move, bool emitSignal = true);
|
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
private:
|
2019-04-14 12:18:24 +00:00
|
|
|
void centerX(bool emitSignal);
|
|
|
|
void centerY(bool emitSignal);
|
|
|
|
|
2019-04-07 10:53:42 +00:00
|
|
|
void paintGraphCache();
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
bool checkPointClicked(QPointF &point, int x, int y, bool above_y = false);
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-04-14 12:18:24 +00:00
|
|
|
// Zoom data
|
|
|
|
qreal current_scale = 1.0;
|
|
|
|
|
|
|
|
QPoint offset = QPoint(0, 0);
|
|
|
|
|
2017-12-13 22:38:46 +00:00
|
|
|
ut64 entry;
|
|
|
|
|
2019-04-04 05:54:42 +00:00
|
|
|
std::unique_ptr<GraphLayout> graphLayoutSystem;
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2018-06-26 20:23:10 +00:00
|
|
|
bool ready = false;
|
2017-12-13 22:38:46 +00:00
|
|
|
|
|
|
|
// Scrolling data
|
2018-06-26 20:23:10 +00:00
|
|
|
int scroll_base_x = 0;
|
|
|
|
int scroll_base_y = 0;
|
|
|
|
bool scroll_mode = false;
|
2017-12-13 22:38:46 +00:00
|
|
|
|
2019-04-07 10:53:42 +00:00
|
|
|
bool useGL;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief pixmap that caches the graph nodes
|
|
|
|
*/
|
|
|
|
QPixmap pixmap;
|
|
|
|
|
2019-10-06 17:35:44 +00:00
|
|
|
#ifndef CUTTER_NO_OPENGL_GRAPH
|
2019-04-07 10:53:42 +00:00
|
|
|
uint32_t cacheTexture;
|
|
|
|
uint32_t cacheFBO;
|
|
|
|
QSize cacheSize;
|
|
|
|
QOpenGLWidget *glWidget;
|
|
|
|
#endif
|
2019-08-03 13:10:44 +00:00
|
|
|
Layout graphLayout;
|
2019-04-07 10:53:42 +00:00
|
|
|
|
2019-04-08 06:59:16 +00:00
|
|
|
/**
|
|
|
|
* @brief flag to control if the cache is invalid and should be re-created in the next draw
|
|
|
|
*/
|
|
|
|
bool cacheDirty = true;
|
|
|
|
QSize getCacheSize();
|
|
|
|
qreal getCacheDevicePixelRatioF();
|
|
|
|
QSize getRequiredCacheSize();
|
|
|
|
qreal getRequiredCacheDevicePixelRatioF();
|
|
|
|
|
2019-04-07 11:02:35 +00:00
|
|
|
void beginMouseDrag(QMouseEvent *event);
|
2019-04-14 12:18:24 +00:00
|
|
|
public:
|
|
|
|
QPoint getViewOffset() const { return offset; }
|
|
|
|
void setViewOffset(QPoint offset);
|
|
|
|
qreal getViewScale() const { return current_scale; }
|
|
|
|
void setViewScale(qreal scale);
|
|
|
|
|
|
|
|
void center();
|
|
|
|
void centerX() { centerX(true); }
|
|
|
|
void centerY() { centerY(true); }
|
2017-12-13 22:38:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif // GRAPHVIEW_H
|