mirror of
https://github.com/rizinorg/cutter.git
synced 2025-02-22 06:33:46 +00:00
Support for previewing in the Graph widget (#2797)
Common functionality has been moved to the DisassemblyPreview namespace.
This commit is contained in:
parent
6438cc4d50
commit
d85383fcfc
@ -8,6 +8,7 @@ set(SOURCES
|
|||||||
dialogs/WriteCommandsDialogs.cpp
|
dialogs/WriteCommandsDialogs.cpp
|
||||||
widgets/DisassemblerGraphView.cpp
|
widgets/DisassemblerGraphView.cpp
|
||||||
widgets/OverviewView.cpp
|
widgets/OverviewView.cpp
|
||||||
|
common/DisassemblyPreview.cpp
|
||||||
common/RichTextPainter.cpp
|
common/RichTextPainter.cpp
|
||||||
dialogs/InitialOptionsDialog.cpp
|
dialogs/InitialOptionsDialog.cpp
|
||||||
dialogs/AboutDialog.cpp
|
dialogs/AboutDialog.cpp
|
||||||
|
@ -167,6 +167,17 @@ public:
|
|||||||
|
|
||||||
void setGraphMinFontSize(int sz) { s.setValue("graph.minfontsize", sz); }
|
void setGraphMinFontSize(int sz) { s.setValue("graph.minfontsize", sz); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the boolean setting for preview in Graph
|
||||||
|
* @return True if preview checkbox is checked, false otherwise
|
||||||
|
*/
|
||||||
|
bool getGraphPreview() { return s.value("graph.preview").toBool(); }
|
||||||
|
/**
|
||||||
|
* @brief Set the boolean setting for preview in Graph
|
||||||
|
* @param checked is a boolean that represents the preview checkbox
|
||||||
|
*/
|
||||||
|
void setGraphPreview(bool checked) { s.setValue("graph.preview", checked); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Getters and setters for the transaparent option state and scale factor for bitmap
|
* @brief Getters and setters for the transaparent option state and scale factor for bitmap
|
||||||
* graph exports.
|
* graph exports.
|
||||||
|
84
src/common/DisassemblyPreview.cpp
Normal file
84
src/common/DisassemblyPreview.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include "DisassemblyPreview.h"
|
||||||
|
#include "Configuration.h"
|
||||||
|
#include "widgets/GraphView.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QToolTip>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
|
DisassemblyTextBlockUserData::DisassemblyTextBlockUserData(const DisassemblyLine &line)
|
||||||
|
: line { line }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DisassemblyTextBlockUserData *getUserData(const QTextBlock &block)
|
||||||
|
{
|
||||||
|
QTextBlockUserData *userData = block.userData();
|
||||||
|
if (!userData) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<DisassemblyTextBlockUserData *>(userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DisassemblyPreview::getToolTipStyleSheet()
|
||||||
|
{
|
||||||
|
return QString { "QToolTip { border-width: 1px; max-width: %1px;"
|
||||||
|
"opacity: 230; background-color: %2;"
|
||||||
|
"color: %3; border-color: %3;}" }
|
||||||
|
.arg(400)
|
||||||
|
.arg(Config()->getColor("gui.tooltip.background").name())
|
||||||
|
.arg(Config()->getColor("gui.tooltip.foreground").name());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DisassemblyPreview::showDisasPreview(QWidget *parent, const QPoint &pointOfEvent,
|
||||||
|
const RVA offsetFrom)
|
||||||
|
{
|
||||||
|
QProcessEnvironment env;
|
||||||
|
QPoint point = pointOfEvent;
|
||||||
|
|
||||||
|
QList<XrefDescription> refs = Core()->getXRefs(offsetFrom, false, false);
|
||||||
|
if (refs.length()) {
|
||||||
|
if (refs.length() > 1) {
|
||||||
|
qWarning() << QObject::tr(
|
||||||
|
"More than one (%1) references here. Weird behaviour expected.")
|
||||||
|
.arg(refs.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
RVA offsetTo = refs.at(0).to; // This is the offset we want to preview
|
||||||
|
|
||||||
|
if (Q_UNLIKELY(offsetFrom != refs.at(0).from)) {
|
||||||
|
qWarning() << QObject::tr("offsetFrom (%1) differs from refs.at(0).from (%(2))")
|
||||||
|
.arg(offsetFrom)
|
||||||
|
.arg(refs.at(0).from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only if the offset we point *to* is different from the one the cursor is currently
|
||||||
|
* on *and* the former is a valid offset, we are allowed to get a preview of offsetTo
|
||||||
|
*/
|
||||||
|
if (offsetTo != offsetFrom && offsetTo != RVA_INVALID) {
|
||||||
|
QStringList disasmPreview = Core()->getDisassemblyPreview(offsetTo, 10);
|
||||||
|
|
||||||
|
// Last check to make sure the returned preview isn't an empty text (QStringList)
|
||||||
|
if (!disasmPreview.isEmpty()) {
|
||||||
|
const QFont &fnt = Config()->getFont();
|
||||||
|
|
||||||
|
QFontMetrics fm { fnt };
|
||||||
|
|
||||||
|
QString tooltip =
|
||||||
|
QString { "<html><div style=\"font-family: %1; font-size: %2pt; "
|
||||||
|
"white-space: nowrap;\"><div style=\"margin-bottom: "
|
||||||
|
"10px;\"><strong>Disassembly Preview</strong>:<br>%3<div>" }
|
||||||
|
.arg(fnt.family())
|
||||||
|
.arg(qMax(8, fnt.pointSize() - 1))
|
||||||
|
.arg(disasmPreview.join("<br>"));
|
||||||
|
|
||||||
|
QToolTip::showText(point, tooltip, parent, QRect {}, 3500);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
38
src/common/DisassemblyPreview.h
Normal file
38
src/common/DisassemblyPreview.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef DISASSEMBLYPREVIEW_H
|
||||||
|
#define DISASSEMBLYPREVIEW_H
|
||||||
|
|
||||||
|
#include <QTextBlockUserData>
|
||||||
|
|
||||||
|
#include "core/CutterDescriptions.h"
|
||||||
|
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
class DisassemblyTextBlockUserData : public QTextBlockUserData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DisassemblyLine line;
|
||||||
|
|
||||||
|
explicit DisassemblyTextBlockUserData(const DisassemblyLine &line);
|
||||||
|
};
|
||||||
|
|
||||||
|
DisassemblyTextBlockUserData *getUserData(const QTextBlock &block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Namespace to define relevant functions
|
||||||
|
*
|
||||||
|
* @ingroup DisassemblyPreview
|
||||||
|
*/
|
||||||
|
namespace DisassemblyPreview {
|
||||||
|
/*!
|
||||||
|
* @brief Get the QString that defines the stylesheet for tooltip
|
||||||
|
* @return A QString for the stylesheet
|
||||||
|
*/
|
||||||
|
QString getToolTipStyleSheet();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Show a QToolTip that previews the disassembly that is pointed to
|
||||||
|
* @return True if the tooltip is shown
|
||||||
|
*/
|
||||||
|
bool showDisasPreview(QWidget *parent, const QPoint &pointOfEvent, const RVA offsetFrom);
|
||||||
|
}
|
||||||
|
#endif
|
@ -15,6 +15,7 @@ GraphOptionsWidget::GraphOptionsWidget(PreferencesDialog *dialog)
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->checkTransparent->setChecked(Config()->getBitmapTransparentState());
|
ui->checkTransparent->setChecked(Config()->getBitmapTransparentState());
|
||||||
ui->blockEntryCheckBox->setChecked(Config()->getGraphBlockEntryOffset());
|
ui->blockEntryCheckBox->setChecked(Config()->getGraphBlockEntryOffset());
|
||||||
|
ui->graphPreviewCheckBox->setChecked(Config()->getGraphPreview());
|
||||||
ui->bitmapGraphScale->setValue(Config()->getBitmapExportScaleFactor() * 100.0);
|
ui->bitmapGraphScale->setValue(Config()->getBitmapExportScaleFactor() * 100.0);
|
||||||
updateOptionsFromVars();
|
updateOptionsFromVars();
|
||||||
|
|
||||||
@ -84,6 +85,12 @@ void GraphOptionsWidget::on_graphOffsetCheckBox_toggled(bool checked)
|
|||||||
triggerOptionsChanged();
|
triggerOptionsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphOptionsWidget::on_graphPreviewCheckBox_toggled( bool checked )
|
||||||
|
{
|
||||||
|
Config()->setGraphPreview(checked);
|
||||||
|
triggerOptionsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void GraphOptionsWidget::checkTransparentStateChanged(int checked)
|
void GraphOptionsWidget::checkTransparentStateChanged(int checked)
|
||||||
{
|
{
|
||||||
Config()->setBitmapTransparentState(checked);
|
Config()->setBitmapTransparentState(checked);
|
||||||
|
@ -33,6 +33,7 @@ private slots:
|
|||||||
void on_maxColsSpinBox_valueChanged(int value);
|
void on_maxColsSpinBox_valueChanged(int value);
|
||||||
void on_minFontSizeSpinBox_valueChanged(int value);
|
void on_minFontSizeSpinBox_valueChanged(int value);
|
||||||
void on_graphOffsetCheckBox_toggled(bool checked);
|
void on_graphOffsetCheckBox_toggled(bool checked);
|
||||||
|
void on_graphPreviewCheckBox_toggled(bool checked);
|
||||||
|
|
||||||
void checkTransparentStateChanged(int checked);
|
void checkTransparentStateChanged(int checked);
|
||||||
void bitmapGraphScaleValueChanged(double value);
|
void bitmapGraphScaleValueChanged(double value);
|
||||||
|
@ -42,6 +42,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="graphPreviewCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show preview when hovering (graph.preview)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "core/MainWindow.h"
|
#include "core/MainWindow.h"
|
||||||
#include "common/Colors.h"
|
#include "common/Colors.h"
|
||||||
#include "common/Configuration.h"
|
#include "common/Configuration.h"
|
||||||
|
#include "common/DisassemblyPreview.h"
|
||||||
#include "common/TempConfig.h"
|
#include "common/TempConfig.h"
|
||||||
#include "common/SyntaxHighlighter.h"
|
#include "common/SyntaxHighlighter.h"
|
||||||
#include "common/BasicBlockHighlighter.h"
|
#include "common/BasicBlockHighlighter.h"
|
||||||
@ -41,7 +42,12 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
|||||||
{
|
{
|
||||||
highlight_token = nullptr;
|
highlight_token = nullptr;
|
||||||
auto *layout = new QVBoxLayout(this);
|
auto *layout = new QVBoxLayout(this);
|
||||||
|
setTooltipStylesheet();
|
||||||
|
|
||||||
// Signals that require a refresh all
|
// Signals that require a refresh all
|
||||||
|
connect(Config(), &Configuration::colorsUpdated, this,
|
||||||
|
&DisassemblerGraphView::setTooltipStylesheet);
|
||||||
|
|
||||||
connect(Core(), &CutterCore::refreshAll, this, &DisassemblerGraphView::refreshView);
|
connect(Core(), &CutterCore::refreshAll, this, &DisassemblerGraphView::refreshView);
|
||||||
connect(Core(), &CutterCore::commentsChanged, this, &DisassemblerGraphView::refreshView);
|
connect(Core(), &CutterCore::commentsChanged, this, &DisassemblerGraphView::refreshView);
|
||||||
connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView);
|
connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView);
|
||||||
@ -140,6 +146,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
|||||||
layout->setAlignment(Qt::AlignTop);
|
layout->setAlignment(Qt::AlignTop);
|
||||||
|
|
||||||
this->scale_thickness_multiplier = true;
|
this->scale_thickness_multiplier = true;
|
||||||
|
installEventFilter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblerGraphView::connectSeekChanged(bool disconn)
|
void DisassemblerGraphView::connectSeekChanged(bool disconn)
|
||||||
@ -524,6 +531,40 @@ GraphView::EdgeConfiguration DisassemblerGraphView::edgeConfiguration(GraphView:
|
|||||||
return ec;
|
return ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DisassemblerGraphView::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::Type::ToolTip && Config()->getGraphPreview()) {
|
||||||
|
|
||||||
|
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
|
||||||
|
QPoint pointOfEvent = helpEvent->globalPos();
|
||||||
|
QPoint point = viewToLogicalCoordinates(helpEvent->pos());
|
||||||
|
|
||||||
|
GraphBlock *block = getBlockContaining(point);
|
||||||
|
|
||||||
|
if (block == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// offsetFrom is the address which on top the cursor triggered this
|
||||||
|
RVA offsetFrom = RVA_INVALID;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getAddrForMouseEvent() doesn't work for jmps, like
|
||||||
|
* getInstrForMouseEvent() with false as a 3rd argument.
|
||||||
|
*/
|
||||||
|
Instr *inst = getInstrForMouseEvent(*block, &point, true);
|
||||||
|
if (inst != nullptr) {
|
||||||
|
offsetFrom = inst->addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't preview anything for a small scale
|
||||||
|
if (getViewScale() >= 0.8) {
|
||||||
|
return DisassemblyPreview::showDisasPreview(this, pointOfEvent, offsetFrom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CutterGraphView::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
RVA DisassemblerGraphView::getAddrForMouseEvent(GraphBlock &block, QPoint *point)
|
RVA DisassemblerGraphView::getAddrForMouseEvent(GraphBlock &block, QPoint *point)
|
||||||
{
|
{
|
||||||
DisassemblyBlock &db = disassembly_blocks[block.entry];
|
DisassemblyBlock &db = disassembly_blocks[block.entry];
|
||||||
@ -702,6 +743,11 @@ void DisassemblerGraphView::takeFalse()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DisassemblerGraphView::setTooltipStylesheet()
|
||||||
|
{
|
||||||
|
setStyleSheet(DisassemblyPreview::getToolTipStyleSheet());
|
||||||
|
}
|
||||||
|
|
||||||
void DisassemblerGraphView::seekInstruction(bool previous_instr)
|
void DisassemblerGraphView::seekInstruction(bool previous_instr)
|
||||||
{
|
{
|
||||||
RVA addr = seekable->getOffset();
|
RVA addr = seekable->getOffset();
|
||||||
@ -951,3 +997,13 @@ bool DisassemblerGraphView::Instr::contains(ut64 addr) const
|
|||||||
{
|
{
|
||||||
return this->addr <= addr && (addr - this->addr) < size;
|
return this->addr <= addr && (addr - this->addr) < size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RVA DisassemblerGraphView::readDisassemblyOffset(QTextCursor tc)
|
||||||
|
{
|
||||||
|
auto userData = getUserData(tc.block());
|
||||||
|
if (!userData) {
|
||||||
|
return RVA_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userData->line.offset;
|
||||||
|
}
|
||||||
|
@ -139,10 +139,13 @@ protected:
|
|||||||
QPoint pos) override;
|
QPoint pos) override;
|
||||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
void restoreCurrentBlock() override;
|
void restoreCurrentBlock() override;
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showExportDialog() override;
|
void showExportDialog() override;
|
||||||
void onActionHighlightBITriggered();
|
void onActionHighlightBITriggered();
|
||||||
void onActionUnhighlightBITriggered();
|
void onActionUnhighlightBITriggered();
|
||||||
|
void setTooltipStylesheet();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool transition_dont_seek = false;
|
bool transition_dont_seek = false;
|
||||||
@ -175,6 +178,7 @@ private:
|
|||||||
DisassemblyBlock *blockForAddress(RVA addr);
|
DisassemblyBlock *blockForAddress(RVA addr);
|
||||||
void seekLocal(RVA addr, bool update_viewport = true);
|
void seekLocal(RVA addr, bool update_viewport = true);
|
||||||
void seekInstruction(bool previous_instr);
|
void seekInstruction(bool previous_instr);
|
||||||
|
RVA readDisassemblyOffset(QTextCursor tc);
|
||||||
|
|
||||||
CutterSeekable *seekable = nullptr;
|
CutterSeekable *seekable = nullptr;
|
||||||
QList<QShortcut *> shortcuts;
|
QList<QShortcut *> shortcuts;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "DisassemblyWidget.h"
|
#include "DisassemblyWidget.h"
|
||||||
#include "menus/DisassemblyContextMenu.h"
|
#include "menus/DisassemblyContextMenu.h"
|
||||||
#include "common/Configuration.h"
|
#include "common/Configuration.h"
|
||||||
|
#include "common/DisassemblyPreview.h"
|
||||||
#include "common/Helpers.h"
|
#include "common/Helpers.h"
|
||||||
#include "common/TempConfig.h"
|
#include "common/TempConfig.h"
|
||||||
#include "common/SelectionHighlight.h"
|
#include "common/SelectionHighlight.h"
|
||||||
@ -22,26 +23,6 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
static const int kMaxTooltipWidth = 400;
|
|
||||||
|
|
||||||
class DisassemblyTextBlockUserData : public QTextBlockUserData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DisassemblyLine line;
|
|
||||||
|
|
||||||
explicit DisassemblyTextBlockUserData(const DisassemblyLine &line) { this->line = line; }
|
|
||||||
};
|
|
||||||
|
|
||||||
static DisassemblyTextBlockUserData *getUserData(const QTextBlock &block)
|
|
||||||
{
|
|
||||||
QTextBlockUserData *userData = block.userData();
|
|
||||||
if (!userData) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<DisassemblyTextBlockUserData *>(userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
DisassemblyWidget::DisassemblyWidget(MainWindow *main)
|
DisassemblyWidget::DisassemblyWidget(MainWindow *main)
|
||||||
: MemoryDockWidget(MemoryWidgetType::Disassembly, main),
|
: MemoryDockWidget(MemoryWidgetType::Disassembly, main),
|
||||||
mCtxMenu(new DisassemblyContextMenu(this, main)),
|
mCtxMenu(new DisassemblyContextMenu(this, main)),
|
||||||
@ -769,12 +750,7 @@ void DisassemblyWidget::setupColors()
|
|||||||
.arg(ConfigColor("btext").name()));
|
.arg(ConfigColor("btext").name()));
|
||||||
|
|
||||||
// Read and set a stylesheet for the QToolTip too
|
// Read and set a stylesheet for the QToolTip too
|
||||||
setStyleSheet(QString{"QToolTip { border-width: 1px; max-width: %1px;"
|
setStyleSheet(DisassemblyPreview::getToolTipStyleSheet());
|
||||||
"opacity: 230; background-color: %2;"
|
|
||||||
"color: %3; border-color: %3;}"}
|
|
||||||
.arg(kMaxTooltipWidth)
|
|
||||||
.arg(Config()->getColor("gui.tooltip.background").name())
|
|
||||||
.arg(Config()->getColor("gui.tooltip.foreground").name()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DisassemblyScrollArea::DisassemblyScrollArea(QWidget *parent) : QAbstractScrollArea(parent) {}
|
DisassemblyScrollArea::DisassemblyScrollArea(QWidget *parent) : QAbstractScrollArea(parent) {}
|
||||||
|
Loading…
Reference in New Issue
Block a user