mirror of
https://github.com/rizinorg/cutter.git
synced 2025-02-21 14:16:08 +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
|
||||
widgets/DisassemblerGraphView.cpp
|
||||
widgets/OverviewView.cpp
|
||||
common/DisassemblyPreview.cpp
|
||||
common/RichTextPainter.cpp
|
||||
dialogs/InitialOptionsDialog.cpp
|
||||
dialogs/AboutDialog.cpp
|
||||
|
@ -167,6 +167,17 @@ public:
|
||||
|
||||
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
|
||||
* 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->checkTransparent->setChecked(Config()->getBitmapTransparentState());
|
||||
ui->blockEntryCheckBox->setChecked(Config()->getGraphBlockEntryOffset());
|
||||
ui->graphPreviewCheckBox->setChecked(Config()->getGraphPreview());
|
||||
ui->bitmapGraphScale->setValue(Config()->getBitmapExportScaleFactor() * 100.0);
|
||||
updateOptionsFromVars();
|
||||
|
||||
@ -84,6 +85,12 @@ void GraphOptionsWidget::on_graphOffsetCheckBox_toggled(bool checked)
|
||||
triggerOptionsChanged();
|
||||
}
|
||||
|
||||
void GraphOptionsWidget::on_graphPreviewCheckBox_toggled( bool checked )
|
||||
{
|
||||
Config()->setGraphPreview(checked);
|
||||
triggerOptionsChanged();
|
||||
}
|
||||
|
||||
void GraphOptionsWidget::checkTransparentStateChanged(int checked)
|
||||
{
|
||||
Config()->setBitmapTransparentState(checked);
|
||||
|
@ -33,6 +33,7 @@ private slots:
|
||||
void on_maxColsSpinBox_valueChanged(int value);
|
||||
void on_minFontSizeSpinBox_valueChanged(int value);
|
||||
void on_graphOffsetCheckBox_toggled(bool checked);
|
||||
void on_graphPreviewCheckBox_toggled(bool checked);
|
||||
|
||||
void checkTransparentStateChanged(int checked);
|
||||
void bitmapGraphScaleValueChanged(double value);
|
||||
|
@ -42,6 +42,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="graphPreviewCheckBox">
|
||||
<property name="text">
|
||||
<string>Show preview when hovering (graph.preview)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "core/MainWindow.h"
|
||||
#include "common/Colors.h"
|
||||
#include "common/Configuration.h"
|
||||
#include "common/DisassemblyPreview.h"
|
||||
#include "common/TempConfig.h"
|
||||
#include "common/SyntaxHighlighter.h"
|
||||
#include "common/BasicBlockHighlighter.h"
|
||||
@ -41,7 +42,12 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
||||
{
|
||||
highlight_token = nullptr;
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
setTooltipStylesheet();
|
||||
|
||||
// Signals that require a refresh all
|
||||
connect(Config(), &Configuration::colorsUpdated, this,
|
||||
&DisassemblerGraphView::setTooltipStylesheet);
|
||||
|
||||
connect(Core(), &CutterCore::refreshAll, this, &DisassemblerGraphView::refreshView);
|
||||
connect(Core(), &CutterCore::commentsChanged, this, &DisassemblerGraphView::refreshView);
|
||||
connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView);
|
||||
@ -140,6 +146,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
|
||||
this->scale_thickness_multiplier = true;
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
void DisassemblerGraphView::connectSeekChanged(bool disconn)
|
||||
@ -524,6 +531,40 @@ GraphView::EdgeConfiguration DisassemblerGraphView::edgeConfiguration(GraphView:
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
RVA addr = seekable->getOffset();
|
||||
@ -951,3 +997,13 @@ bool DisassemblerGraphView::Instr::contains(ut64 addr) const
|
||||
{
|
||||
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;
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
void restoreCurrentBlock() override;
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void showExportDialog() override;
|
||||
void onActionHighlightBITriggered();
|
||||
void onActionUnhighlightBITriggered();
|
||||
void setTooltipStylesheet();
|
||||
|
||||
private:
|
||||
bool transition_dont_seek = false;
|
||||
@ -175,6 +178,7 @@ private:
|
||||
DisassemblyBlock *blockForAddress(RVA addr);
|
||||
void seekLocal(RVA addr, bool update_viewport = true);
|
||||
void seekInstruction(bool previous_instr);
|
||||
RVA readDisassemblyOffset(QTextCursor tc);
|
||||
|
||||
CutterSeekable *seekable = nullptr;
|
||||
QList<QShortcut *> shortcuts;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "DisassemblyWidget.h"
|
||||
#include "menus/DisassemblyContextMenu.h"
|
||||
#include "common/Configuration.h"
|
||||
#include "common/DisassemblyPreview.h"
|
||||
#include "common/Helpers.h"
|
||||
#include "common/TempConfig.h"
|
||||
#include "common/SelectionHighlight.h"
|
||||
@ -22,26 +23,6 @@
|
||||
#include <algorithm>
|
||||
#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)
|
||||
: MemoryDockWidget(MemoryWidgetType::Disassembly, main),
|
||||
mCtxMenu(new DisassemblyContextMenu(this, main)),
|
||||
@ -769,12 +750,7 @@ void DisassemblyWidget::setupColors()
|
||||
.arg(ConfigColor("btext").name()));
|
||||
|
||||
// Read and set a stylesheet for the QToolTip too
|
||||
setStyleSheet(QString{"QToolTip { border-width: 1px; max-width: %1px;"
|
||||
"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()));
|
||||
setStyleSheet(DisassemblyPreview::getToolTipStyleSheet());
|
||||
}
|
||||
|
||||
DisassemblyScrollArea::DisassemblyScrollArea(QWidget *parent) : QAbstractScrollArea(parent) {}
|
||||
|
Loading…
Reference in New Issue
Block a user