From 7eb62a976c0c560d43f4e2ed2f7b9b68875240c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 14 Apr 2019 14:18:24 +0200 Subject: [PATCH] Refactor Refresh and Display of Overview (#1453) --- src/core/MainWindow.cpp | 143 +++----------------------- src/core/MainWindow.h | 9 -- src/widgets/DisassemblerGraphView.cpp | 33 +++--- src/widgets/DisassemblerGraphView.h | 5 + src/widgets/GraphView.cpp | 39 +++++-- src/widgets/GraphView.h | 32 ++++-- src/widgets/GraphWidget.cpp | 2 +- src/widgets/GraphWidget.h | 2 +- src/widgets/OverviewView.cpp | 21 ++-- src/widgets/OverviewView.h | 14 ++- src/widgets/OverviewWidget.cpp | 126 +++++++++++++++++++---- src/widgets/OverviewWidget.h | 78 ++++++++++---- 12 files changed, 276 insertions(+), 228 deletions(-) diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index e6d3f5fe..da622b1c 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -240,18 +240,23 @@ void MainWindow::initDocks() hexdumpDock = new HexdumpWidget(this, ui->actionHexdump); pseudocodeDock = new PseudocodeWidget(this, ui->actionPseudocode); consoleDock = new ConsoleWidget(this, ui->actionConsole); + overviewDock = new OverviewWidget(this, ui->actionOverview); overviewDock->hide(); - graphDock = new GraphWidget(this, ui->actionGraph); + connect(overviewDock, &OverviewWidget::isAvailableChanged, this, [this](bool isAvailable) { + ui->actionOverview->setEnabled(isAvailable); + }); + ui->actionOverview->setEnabled(overviewDock->getIsAvailable()); connect(ui->actionOverview, &QAction::toggled, [this](bool checked) { if (checked) { - overviewDock->setUserClosed(false); - forceUpdateOverview(); - if (targetGraphDock) { - toggleOverview(true, targetGraphDock); - } + overviewDock->show(); + } else { + overviewDock->hide(); } }); + + ui->actionOverview->setChecked(overviewDock->getUserOpened()); + graphDock = new GraphWidget(this, ui->actionGraph); sectionsDock = new SectionsWidget(this, ui->actionSections); segmentsDock = new SegmentsWidget(this, ui->actionSegments); entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints); @@ -297,124 +302,9 @@ void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph) if (!overviewDock) { return; } - ui->actionOverview->setEnabled(visibility); - if (overviewDock->getUserClosed() || !visibility) { - return; + if (visibility) { + overviewDock->setTargetGraphWidget(targetGraph); } - targetGraphDock = targetGraph; - connect(targetGraphDock->getGraphView(), SIGNAL(refreshBlock()), this, SLOT(updateOverview())); - connect(targetGraphDock->getGraphView(), SIGNAL(viewRefreshed()), this, SLOT(forceUpdateOverview())); - connect(targetGraphDock->getGraphView(), SIGNAL(viewZoomed()), this, SLOT(updateOverview())); - connect(targetGraphDock, &GraphWidget::graphClose, [this]() { - disconnectOverview(); - enableOverviewMenu(false); - overviewDock->hide(); - }); - connect(overviewDock->getGraphView(), SIGNAL(mouseMoved()), this, SLOT(adjustGraph())); - connect(overviewDock->getGraphView(), SIGNAL(refreshBlock()), this, SLOT(updateOverviewAddr())); - connect(overviewDock, &QDockWidget::dockLocationChanged, this, &MainWindow::forceUpdateOverview); - connect(overviewDock, &OverviewWidget::graphClose, [this]() { - ui->actionOverview->setChecked(false); - if (!core->isGraphEmpty()) { - overviewDock->setUserClosed(true); - } - }); - connect(overviewDock, SIGNAL(resized()), this, SLOT(forceUpdateOverview())); // TODO: remove this, overviewDock handles resize itself! -} - -void MainWindow::disconnectOverview() -{ - if (targetGraphDock) { - disconnect(targetGraphDock->getGraphView(), SIGNAL(refreshBlock()), this, SLOT(updateOverview())); - disconnect(targetGraphDock->getGraphView(), SIGNAL(viewRefreshed()), this, SLOT(updateOverview())); - disconnect(targetGraphDock->getGraphView(), SIGNAL(viewZoomed()), this, SLOT(updateOverview())); - } - if (overviewDock) { - disconnect(overviewDock->getGraphView(), SIGNAL(mouseMoved()), this, SLOT(adjustGraph())); - disconnect(overviewDock->getGraphView(), SIGNAL(refreshBlock()), this, SLOT(updateOverviewAddr())); - disconnect(overviewDock, &QDockWidget::dockLocationChanged, this, &MainWindow::forceUpdateOverview); - disconnect(overviewDock, SIGNAL(resized()), this, SLOT(forceUpdateOverview())); // TODO: remove this, overviewDock handles resize itself! - } -} - -void MainWindow::setOverviewData() -{ - auto &mainGraphView = *targetGraphDock->getGraphView(); - overviewDock->getGraphView()->setData(mainGraphView.getWidth(), mainGraphView.getHeight(), - mainGraphView.getBlocks(), mainGraphView.getEdgeConfigurations()); -} - -bool MainWindow::isOverviewActive() -{ - if (!overviewDock || overviewDock->getUserClosed()) { - return false; - } - if (core->isGraphEmpty()) { - enableOverviewMenu(false); - overviewDock->hide(); - return false; - } - return true; -} - -void MainWindow::updateOverviewAddr() -{ - overviewDock->getGraphView()->currentFcnAddr = targetGraphDock->getGraphView()->currentFcnAddr; -} - -void MainWindow::forceUpdateOverview() -{ - if (!isOverviewActive()) { - return; - } - setOverviewData(); - drawOverview(); -} - -void MainWindow::updateOverview() -{ - if (!isOverviewActive()) { - return; - } - if (overviewDock->getGraphView()->currentFcnAddr != targetGraphDock->getGraphView()->currentFcnAddr) { - setOverviewData(); - } - drawOverview(); -} - -void MainWindow::drawOverview() -{ - qreal curScale = overviewDock->getGraphView()->current_scale; - qreal baseScale = targetGraphDock->getGraphView()->current_scale; - qreal w = targetGraphDock->getGraphView()->viewport()->width() * curScale / baseScale; - qreal h = targetGraphDock->getGraphView()->viewport()->height() * curScale / baseScale; - int graph_offset_x = targetGraphDock->getGraphView()->offset.x(); - int graph_offset_y = targetGraphDock->getGraphView()->offset.y(); - int overview_offset_x = overviewDock->getGraphView()->offset.x(); - int overview_offset_y = overviewDock->getGraphView()->offset.y(); - int rangeRectX = graph_offset_x * curScale - overview_offset_x * curScale; - int rangeRectY = graph_offset_y * curScale - overview_offset_y * curScale; - - overviewDock->getGraphView()->rangeRect = QRectF(rangeRectX, rangeRectY, w, h); - overviewDock->getGraphView()->viewport()->update(); - enableOverviewMenu(true); - overviewDock->show(); -} - -void MainWindow::adjustGraph() -{ - if (!overviewDock) { - return; - } - - qreal curScale = overviewDock->getGraphView()->current_scale; - int rectx = overviewDock->getGraphView()->rangeRect.x(); - int recty = overviewDock->getGraphView()->rangeRect.y(); - int overview_offset_x = overviewDock->getGraphView()->offset.x(); - int overview_offset_y = overviewDock->getGraphView()->offset.y(); - targetGraphDock->getGraphView()->offset.rx() = rectx /curScale + overview_offset_x; - targetGraphDock->getGraphView()->offset.ry() = recty /curScale + overview_offset_y; - targetGraphDock->getGraphView()->viewport()->update(); } void MainWindow::updateTasksIndicator() @@ -931,12 +821,6 @@ void MainWindow::enableDebugWidgetsMenu(bool enable) ui->menuAddDebugWidgets->setEnabled(enable); } -void MainWindow::enableOverviewMenu(bool enable) -{ - ui->actionOverview->setEnabled(enable); - ui->actionOverview->setChecked(enable); -} - void MainWindow::resetToDefaultLayout() { hideAllDocks(); @@ -1005,7 +889,6 @@ void MainWindow::on_actionFunctionsRename_triggered() void MainWindow::on_actionDefault_triggered() { - disconnectOverview(); if (core->currentlyDebugging) { resetToDebugLayout(); } else { diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h index 26566a36..ef77d134 100644 --- a/src/core/MainWindow.h +++ b/src/core/MainWindow.h @@ -186,13 +186,6 @@ private slots: void changeDebugView(); void changeDefinedView(); - void disconnectOverview(); - void updateOverview(); - void forceUpdateOverview(); - void updateOverviewAddr(); - void drawOverview(); - void adjustGraph(); - private: CutterCore *core; @@ -216,7 +209,6 @@ private: HexdumpWidget *hexdumpDock = nullptr; PseudocodeWidget *pseudocodeDock = nullptr; GraphWidget *graphDock = nullptr; - GraphWidget *targetGraphDock = nullptr; OverviewWidget *overviewDock = nullptr; EntrypointWidget *entrypointDock = nullptr; FunctionsWidget *functionsDock = nullptr; @@ -265,7 +257,6 @@ private: void showZenDocks(); void showDebugDocks(); void enableDebugWidgetsMenu(bool enable); - void enableOverviewMenu(bool enable); void toggleDockWidget(QDockWidget *dock_widget, bool show); diff --git a/src/widgets/DisassemblerGraphView.cpp b/src/widgets/DisassemblerGraphView.cpp index 89d9f9d5..a42e5903 100644 --- a/src/widgets/DisassemblerGraphView.cpp +++ b/src/widgets/DisassemblerGraphView.cpp @@ -190,7 +190,7 @@ void DisassemblerGraphView::loadCurrentGraph() highlight_token = nullptr; } - bool emptyGraph = functions.isEmpty(); + emptyGraph = functions.isEmpty(); if (emptyGraph) { // If there's no function to print, just add a message if (!emptyText) { @@ -375,8 +375,8 @@ void DisassemblerGraphView::initFont() void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block) { - int blockX = block.x - offset.x(); - int blockY = block.y - offset.y(); + int blockX = block.x - getViewOffset().x(); + int blockY = block.y - getViewOffset().y(); p.setPen(Qt::black); p.setBrush(Qt::gray); @@ -517,7 +517,7 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block) qreal lineHeightRender = charHeight; for (auto &line : db.header_text.lines) { qreal lineYRender = y; - lineYRender *= current_scale; + lineYRender *= getViewScale(); // Check if line does NOT intersects with view area if (0 > lineYRender + lineHeightRender || render_height < lineYRender) { @@ -543,7 +543,7 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block) } for (auto &line : instr.text.lines) { qreal lineYRender = y; - lineYRender *= current_scale; + lineYRender *= getViewScale(); if (0 > lineYRender + lineHeightRender || render_height < lineYRender) { y += charHeight; @@ -705,16 +705,17 @@ void DisassemblerGraphView::zoom(QPointF mouseRelativePos, double velocity) { mouseRelativePos.rx() *= size().width(); mouseRelativePos.ry() *= size().height(); - mouseRelativePos /= current_scale; + mouseRelativePos /= getViewScale(); - auto globalMouse = mouseRelativePos + offset; - mouseRelativePos *= current_scale; - current_scale *= std::pow(1.25, velocity); - current_scale = std::max(current_scale, 0.3); - mouseRelativePos /= current_scale; + auto globalMouse = mouseRelativePos + getViewOffset(); + mouseRelativePos *= getViewScale(); + qreal newScale = getViewScale() * std::pow(1.25, velocity); + newScale = std::max(newScale, 0.3); + mouseRelativePos /= newScale; + setViewScale(newScale); // Adjusting offset, so that zooming will be approaching to the cursor. - offset = globalMouse.toPoint() - mouseRelativePos.toPoint(); + setViewOffset(globalMouse.toPoint() - mouseRelativePos.toPoint()); viewport()->update(); emit viewZoomed(); @@ -722,7 +723,7 @@ void DisassemblerGraphView::zoom(QPointF mouseRelativePos, double velocity) void DisassemblerGraphView::zoomReset() { - current_scale = 1.0; + setViewScale(1.0); viewport()->update(); emit viewZoomed(); } @@ -992,6 +993,12 @@ void DisassemblerGraphView::wheelEvent(QWheelEvent *event) emit graphMoved(); } +void DisassemblerGraphView::resizeEvent(QResizeEvent *event) +{ + GraphView::resizeEvent(event); + emit resized(); +} + void DisassemblerGraphView::paintEvent(QPaintEvent *event) { // DisassemblerGraphView is always dirty diff --git a/src/widgets/DisassemblerGraphView.h b/src/widgets/DisassemblerGraphView.h index b5ee6a93..d39d1888 100644 --- a/src/widgets/DisassemblerGraphView.h +++ b/src/widgets/DisassemblerGraphView.h @@ -129,6 +129,7 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; + void resizeEvent(QResizeEvent *event) override; void paintEvent(QPaintEvent *event) override; @@ -196,6 +197,10 @@ signals: void viewRefreshed(); void viewZoomed(); void graphMoved(); + void resized(); + +public: + bool isGraphEmpty() { return emptyGraph; } }; #endif // DISASSEMBLERGRAPHVIEW_H diff --git a/src/widgets/GraphView.cpp b/src/widgets/GraphView.cpp index af57d748..f0beec54 100644 --- a/src/widgets/GraphView.cpp +++ b/src/widgets/GraphView.cpp @@ -143,6 +143,18 @@ void GraphView::beginMouseDrag(QMouseEvent *event) viewport()->grabMouse(); } +void GraphView::setViewOffset(QPoint offset) +{ + this->offset = offset; + emit viewOffsetChanged(offset); +} + +void GraphView::setViewScale(qreal scale) +{ + this->current_scale = scale; + emit viewScaleChanged(scale); +} + QSize GraphView::getCacheSize() { return @@ -214,10 +226,6 @@ void GraphView::paintEvent(QPaintEvent *) QPainter p(viewport()); p.drawPixmap(target, pixmap, source); } - - if(cacheWasDirty) { // TODO: does this condition make sense? - emit refreshBlock(); - } } void GraphView::paintGraphCache() @@ -341,20 +349,27 @@ void GraphView::paintGraphCache() void GraphView::center() { - centerX(); - centerY(); + centerX(false); + centerY(false); + emit viewOffsetChanged(offset); } -void GraphView::centerX() +void GraphView::centerX(bool emitSignal) { offset.rx() = -((viewport()->width() - width * current_scale) / 2); offset.rx() /= current_scale; + if (emitSignal) { + emit viewOffsetChanged(offset); + } } -void GraphView::centerY() +void GraphView::centerY(bool emitSignal) { offset.ry() = -((viewport()->height() - height * current_scale) / 2); offset.ry() /= current_scale; + if (emitSignal) { + emit viewOffsetChanged(offset); + } } void GraphView::showBlock(GraphBlock &block) @@ -365,16 +380,17 @@ void GraphView::showBlock(GraphBlock &block) void GraphView::showBlock(GraphBlock *block) { if (width * current_scale <= viewport()->width()) { - centerX(); + centerX(false); } else { int render_width = viewport()->width() / current_scale; offset.rx() = block->x - ((render_width - block->width) / 2); } if (height * current_scale <= viewport()->height()) { - centerY(); + centerY(false); } else { offset.ry() = block->y - 30; } + emit viewOffsetChanged(offset); blockTransitionedTo(block); viewport()->update(); } @@ -463,6 +479,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) if (scroll_mode) { offset.rx() += (scroll_base_x - event->x()) / current_scale; offset.ry() += (scroll_base_y - event->y()) / current_scale; + emit viewOffsetChanged(offset); scroll_base_x = event->x(); scroll_base_y = event->y(); viewport()->update(); @@ -510,6 +527,7 @@ void GraphView::keyPressEvent(QKeyEvent* event) } offset.rx() += dx; offset.ry() += dy; + emit viewOffsetChanged(offset); viewport()->update(); event->accept(); } @@ -540,6 +558,7 @@ void GraphView::wheelEvent(QWheelEvent *event) delta /= current_scale; offset += delta; + emit viewOffsetChanged(offset); viewport()->update(); event->accept(); diff --git a/src/widgets/GraphView.h b/src/widgets/GraphView.h index 8a573a39..e1c46967 100644 --- a/src/widgets/GraphView.h +++ b/src/widgets/GraphView.h @@ -24,8 +24,10 @@ class QOpenGLWidget; class GraphView : public QAbstractScrollArea { Q_OBJECT + signals: - void refreshBlock(); + void viewOffsetChanged(QPoint offset); + void viewScaleChanged(qreal scale); public: using GraphBlock = GraphLayout::GraphBlock; @@ -44,17 +46,12 @@ public: void showBlock(GraphBlock &block); void showBlock(GraphBlock *block); - // Zoom data - qreal current_scale = 1.0; - - QPoint offset = QPoint(0, 0); - /** * @brief keep the current addr of the fcn of Graph * Everytime overview updates its contents, it compares this value with the one in Graph * if they aren't same, then Overview needs to update the pixmap cache. */ - ut64 currentFcnAddr = 0; // TODO: move application specific code out of graph view + ut64 currentFcnAddr = RVA_INVALID; // TODO: move application specific code out of graph view protected: std::unordered_map blocks; @@ -91,17 +88,22 @@ protected: void paintEvent(QPaintEvent *event) override; - void center(); - void centerX(); - void centerY(); int width = 0; int height = 0; private: + void centerX(bool emitSignal); + void centerY(bool emitSignal); + void paintGraphCache(); bool checkPointClicked(QPointF &point, int x, int y, bool above_y = false); + // Zoom data + qreal current_scale = 1.0; + + QPoint offset = QPoint(0, 0); + ut64 entry; std::unique_ptr graphLayoutSystem; @@ -141,6 +143,16 @@ private: QPolygonF recalculatePolygon(QPolygonF polygon); void beginMouseDrag(QMouseEvent *event); + +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); } }; #endif // GRAPHVIEW_H diff --git a/src/widgets/GraphWidget.cpp b/src/widgets/GraphWidget.cpp index 2c3141e4..bb1c62b4 100644 --- a/src/widgets/GraphWidget.cpp +++ b/src/widgets/GraphWidget.cpp @@ -51,7 +51,7 @@ QWidget *GraphWidget::widgetToFocusOnRaise() void GraphWidget::closeEvent(QCloseEvent *event) { CutterDockWidget::closeEvent(event); - emit graphClose(); + emit graphClosed(); } DisassemblerGraphView *GraphWidget::getGraphView() const diff --git a/src/widgets/GraphWidget.h b/src/widgets/GraphWidget.h index 17b92834..010d17e6 100644 --- a/src/widgets/GraphWidget.h +++ b/src/widgets/GraphWidget.h @@ -17,7 +17,7 @@ public: DisassemblerGraphView *getGraphView() const; signals: - void graphClose(); + void graphClosed(); protected: QWidget *widgetToFocusOnRaise() override; diff --git a/src/widgets/OverviewView.cpp b/src/widgets/OverviewView.cpp index a86bf313..359a9642 100644 --- a/src/widgets/OverviewView.cpp +++ b/src/widgets/OverviewView.cpp @@ -24,6 +24,7 @@ void OverviewView::setData(int baseWidth, int baseHeight, edgeConfigurations = baseEdgeConfigurations; scaleAndCenter(); setCacheDirty(); + viewport()->update(); } OverviewView::~OverviewView() @@ -32,11 +33,9 @@ OverviewView::~OverviewView() void OverviewView::scaleAndCenter() { - current_scale = (qreal)viewport()->width() / width; - qreal h_scale = (qreal)viewport()->height() / height; - if (current_scale > h_scale) { - current_scale = h_scale; - } + qreal wScale = (qreal)viewport()->width() / width; + qreal hScale = (qreal)viewport()->height() / height; + setViewScale(std::min(wScale, hScale)); center(); } @@ -48,8 +47,8 @@ void OverviewView::refreshView() void OverviewView::drawBlock(QPainter &p, GraphView::GraphBlock &block) { - int blockX = block.x - offset.x(); - int blockY = block.y - offset.y(); + int blockX = block.x - getViewOffset().x(); + int blockY = block.y - getViewOffset().y(); p.setPen(Qt::black); p.setBrush(Qt::gray); @@ -138,7 +137,7 @@ GraphView::EdgeConfiguration OverviewView::edgeConfiguration(GraphView::GraphBlo auto baseEcIt = edgeConfigurations.find({from.entry, to->entry}); if (baseEcIt != edgeConfigurations.end()) ec = baseEcIt->second; - ec.width_scale = current_scale; + ec.width_scale = getViewScale(); return ec; } @@ -149,3 +148,9 @@ void OverviewView::colorsUpdatedSlot() backgroundColor = ConfigColor("gui.background"); refreshView(); } + +void OverviewView::setRangeRect(QRectF rect) +{ + rangeRect = rect; + viewport()->update(); +} diff --git a/src/widgets/OverviewView.h b/src/widgets/OverviewView.h index 1e5f21f9..c620b936 100644 --- a/src/widgets/OverviewView.h +++ b/src/widgets/OverviewView.h @@ -22,11 +22,6 @@ public: OverviewView(QWidget *parent); ~OverviewView() override; - /** - * @brief a rect on Overview to show where you are on Graph - */ - QRectF rangeRect; - /** * @brief Graph access this function to set minimum set of the data * @param baseWidth width of Graph when it computed the blocks @@ -87,6 +82,11 @@ private: */ QPointF initialDiff; + /** + * @brief a rect on Overview to show where you are on Graph + */ + QRectF rangeRect; + /** * @brief calculate the scale to fit the all nodes in and center them in the viewport */ @@ -124,6 +124,10 @@ private: * @brief edgeConfigurations edge styles computed by DisassemblerGraphView */ DisassemblerGraphView::EdgeConfigurationMapping edgeConfigurations; + +public: + QRectF getRangeRect() { return rangeRect; } + void setRangeRect(QRectF rect); }; #endif // OVERVIEWVIEW_H diff --git a/src/widgets/OverviewWidget.cpp b/src/widgets/OverviewWidget.cpp index 4ec2d302..35053702 100644 --- a/src/widgets/OverviewWidget.cpp +++ b/src/widgets/OverviewWidget.cpp @@ -1,5 +1,6 @@ #include "core/MainWindow.h" #include "OverviewWidget.h" +#include "GraphWidget.h" #include "OverviewView.h" OverviewWidget::OverviewWidget(MainWindow *main, QAction *action) : @@ -10,14 +11,12 @@ OverviewWidget::OverviewWidget(MainWindow *main, QAction *action) : setAllowedAreas(Qt::AllDockWidgetAreas); graphView = new OverviewView(this); setWidget(graphView); - refreshDeferrer = createRefreshDeferrer([this]() { - updateContents(); - }); + targetGraphWidget = nullptr; - connect(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) { - if (visibility) { - updateContents(); - } + connect(graphView, SIGNAL(mouseMoved()), this, SLOT(updateTargetView())); + + graphDataRefreshDeferrer = createRefreshDeferrer([this]() { + updateGraphData(); }); } @@ -26,35 +25,124 @@ OverviewWidget::~OverviewWidget() {} void OverviewWidget::resizeEvent(QResizeEvent *event) { graphView->refreshView(); + updateRangeRect(); QDockWidget::resizeEvent(event); emit resized(); } -void OverviewWidget::updateContents() +void OverviewWidget::showEvent(QShowEvent *event) { - if (!refreshDeferrer->attemptRefresh(nullptr)) { - return; - } - graphView->refreshView(); + CutterDockWidget::showEvent(event); + setUserOpened(true); } void OverviewWidget::closeEvent(QCloseEvent *event) { CutterDockWidget::closeEvent(event); - emit graphClose(); + setUserOpened(false); } -void OverviewWidget::setUserClosed(bool value) +void OverviewWidget::setIsAvailable(bool isAvailable) { - userClosed = value; + if (this->isAvailable == isAvailable) { + return; + } + this->isAvailable = isAvailable; + if(!isAvailable) { + hide(); + } else if(userOpened) { + show(); + } + emit isAvailableChanged(isAvailable); } -bool OverviewWidget::getUserClosed() const +void OverviewWidget::setUserOpened(bool userOpened) { - return userClosed; + if (this->userOpened == userOpened) { + return; + } + this->userOpened = userOpened; + emit userOpenedChanged(userOpened); } -OverviewView *OverviewWidget::getGraphView() const +void OverviewWidget::setTargetGraphWidget(GraphWidget *widget) { - return graphView; + if (widget == targetGraphWidget) { + return; + } + if (targetGraphWidget) { + disconnect(targetGraphWidget->getGraphView(), &DisassemblerGraphView::viewRefreshed, this, &OverviewWidget::updateGraphData); + disconnect(targetGraphWidget->getGraphView(), &DisassemblerGraphView::resized, this, &OverviewWidget::updateRangeRect); + disconnect(targetGraphWidget->getGraphView(), &GraphView::viewOffsetChanged, this, &OverviewWidget::updateRangeRect); + disconnect(targetGraphWidget->getGraphView(), &GraphView::viewScaleChanged, this, &OverviewWidget::updateRangeRect); + disconnect(targetGraphWidget, &GraphWidget::graphClosed, this, &OverviewWidget::targetClosed); + } + targetGraphWidget = widget; + if (targetGraphWidget) { + connect(targetGraphWidget->getGraphView(), &DisassemblerGraphView::viewRefreshed, this, &OverviewWidget::updateGraphData); + connect(targetGraphWidget->getGraphView(), &DisassemblerGraphView::resized, this, &OverviewWidget::updateRangeRect); + connect(targetGraphWidget->getGraphView(), &GraphView::viewOffsetChanged, this, &OverviewWidget::updateRangeRect); + connect(targetGraphWidget->getGraphView(), &GraphView::viewScaleChanged, this, &OverviewWidget::updateRangeRect); + connect(targetGraphWidget, &GraphWidget::graphClosed, this, &OverviewWidget::targetClosed); + } + updateGraphData(); + updateRangeRect(); + setIsAvailable(targetGraphWidget != nullptr); +} + +void OverviewWidget::targetClosed() +{ + setTargetGraphWidget(nullptr); +} + +void OverviewWidget::updateTargetView() +{ + qreal curScale = graphView->getViewScale(); + int rectx = graphView->getRangeRect().x(); + int recty = graphView->getRangeRect().y(); + int overview_offset_x = graphView->getViewOffset().x(); + int overview_offset_y = graphView->getViewOffset().y(); + QPoint newOffset; + newOffset.rx() = rectx / curScale + overview_offset_x; + newOffset.ry() = recty / curScale + overview_offset_y; + targetGraphWidget->getGraphView()->setViewOffset(newOffset); + targetGraphWidget->getGraphView()->viewport()->update(); +} + +void OverviewWidget::updateGraphData() +{ + if (!graphDataRefreshDeferrer->attemptRefresh(nullptr)) { + return; + } + if (targetGraphWidget && !targetGraphWidget->getGraphView()->isGraphEmpty()) { + if (targetGraphWidget->getGraphView()->currentFcnAddr == graphView->currentFcnAddr) { + return; + } + graphView->currentFcnAddr = targetGraphWidget->getGraphView()->currentFcnAddr; + auto &mainGraphView = *targetGraphWidget->getGraphView(); + graphView->setData(mainGraphView.getWidth(), mainGraphView.getHeight(), + mainGraphView.getBlocks(), mainGraphView.getEdgeConfigurations()); + } else { + graphView->currentFcnAddr = RVA_INVALID; + graphView->setData(0, 0, {}, {}); + graphView->setRangeRect(QRectF(0, 0, 0, 0)); + } +} + +void OverviewWidget::updateRangeRect() { + if (targetGraphWidget) { + qreal curScale = graphView->getViewScale(); + qreal baseScale = targetGraphWidget->getGraphView()->getViewScale(); + qreal w = targetGraphWidget->getGraphView()->viewport()->width() * curScale / baseScale; + qreal h = targetGraphWidget->getGraphView()->viewport()->height() * curScale / baseScale; + int graph_offset_x = targetGraphWidget->getGraphView()->getViewOffset().x(); + int graph_offset_y = targetGraphWidget->getGraphView()->getViewOffset().y(); + int overview_offset_x = graphView->getViewOffset().x(); + int overview_offset_y = graphView->getViewOffset().y(); + int rangeRectX = graph_offset_x * curScale - overview_offset_x * curScale; + int rangeRectY = graph_offset_y * curScale - overview_offset_y * curScale; + graphView->setRangeRect(QRectF(rangeRectX, rangeRectY, w, h)); + } else { + graphView->setRangeRect(QRectF(0, 0, 0, 0)); + } } diff --git a/src/widgets/OverviewWidget.h b/src/widgets/OverviewWidget.h index c0c96fa6..cb497d4f 100644 --- a/src/widgets/OverviewWidget.h +++ b/src/widgets/OverviewWidget.h @@ -5,6 +5,7 @@ class MainWindow; class OverviewView; +class GraphWidget; class OverviewWidget : public CutterDockWidget { @@ -14,45 +15,78 @@ public: explicit OverviewWidget(MainWindow *main, QAction *action = nullptr); ~OverviewWidget(); - OverviewView *getGraphView() const; - - /* - * @brief if user closed this widget explicitly - */ - bool getUserClosed() const; - void setUserClosed(bool value); - private: - RefreshDeferrer *refreshDeferrer; + OverviewView *graphView; + bool isAvailable = false; + bool userOpened = false; + + GraphWidget *targetGraphWidget; + + RefreshDeferrer *graphDataRefreshDeferrer; + /** * @brief this takes care of scaling the overview when the widget is resized */ void resizeEvent(QResizeEvent *event) override; + void setIsAvailable(bool isAvailable); + void setUserOpened(bool userOpened); + private slots: - /** - * @brief update the overview - */ - void updateContents(); - /* - * @brief override closeEvent to emit graphClose - */ + void showEvent(QShowEvent *event) override; void closeEvent(QCloseEvent *event) override; + /** + * @brief update the view in the target widget when the range rect in the overview is moved + */ + void updateTargetView(); + + /** + * @brief update the content of the graph (blocks, edges) in the contained graphView from the target widget + */ + void updateGraphData(); + + /** + * @brief update the rect to show the current view in the target widget + */ + void updateRangeRect(); + + void targetClosed(); + signals: /** * @brief emit signal to update the rect */ void resized(); - /* - * @brief emit signal to notify when this widget is closed + + /** + * @sa getIsAvailable() */ - void graphClose(); + void isAvailableChanged(bool isAvailable); -private: - OverviewView *graphView; - bool userClosed = false; + /** + * @sa getUserOpened() + */ + void userOpenedChanged(bool userOpened); +public: + GraphWidget *getTargetGraphWidget() { return targetGraphWidget; } + void setTargetGraphWidget(GraphWidget *widget); + + /** + * @brief whether this widget makes sense to be show, i.e. the menu entry should be enabled + */ + bool getIsAvailable() const { return isAvailable; } + + /** + * @brief whether this widget is desired to be shown in general + * + * Will be false when the user closed the overview explicitly. + * Also corresponds to the checked state of the menu entry for this widget. + */ + bool getUserOpened() const { return userOpened; } + + OverviewView *getGraphView() const { return graphView; } }; #endif // OverviewWIDGET_H