cutter/src/widgets/VisualNavbar.cpp

309 lines
9.1 KiB
C++
Raw Normal View History

#include "VisualNavbar.h"
#include "core/MainWindow.h"
2018-10-17 07:55:53 +00:00
#include "common/TempConfig.h"
#include <QGraphicsView>
#include <QComboBox>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonParseError>
#include <QToolTip>
2019-03-14 09:28:42 +00:00
#include <QMouseEvent>
#include <array>
#include <cmath>
VisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent) :
2017-10-01 18:08:12 +00:00
QToolBar(main),
graphicsView(new QGraphicsView),
seekGraphicsItem(nullptr),
PCGraphicsItem(nullptr),
2017-10-01 18:08:12 +00:00
main(main)
{
2017-10-01 18:08:12 +00:00
Q_UNUSED(parent);
setObjectName("visualNavbar");
setWindowTitle(tr("Visual navigation bar"));
2017-04-09 19:55:06 +00:00
// setMovable(false);
setContentsMargins(0, 0, 0, 0);
// If line below is used, with the dark theme the paintEvent is not called
// and the result is wrong. Something to do with overwriting the style sheet :/
//setStyleSheet("QToolBar { border: 0px; border-bottom: 0px; border-top: 0px; border-width: 0px;}");
/*
QComboBox *addsCombo = new QComboBox();
addsCombo->addItem("");
addsCombo->addItem("Entry points");
addsCombo->addItem("Marks");
*/
addWidget(this->graphicsView);
//addWidget(addsCombo);
connect(Core(), SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA)));
connect(Core(), SIGNAL(registersChanged()), this, SLOT(drawPCCursor()));
connect(Core(), SIGNAL(refreshAll()), this, SLOT(fetchAndPaintData()));
2018-06-29 12:35:02 +00:00
connect(Core(), SIGNAL(functionsChanged()), this, SLOT(fetchAndPaintData()));
connect(Core(), SIGNAL(flagsChanged()), this, SLOT(fetchAndPaintData()));
graphicsScene = new QGraphicsScene(this);
const QBrush bg = QBrush(QColor(74, 74, 74));
graphicsScene->setBackgroundBrush(bg);
this->graphicsView->setAlignment(Qt::AlignLeft);
2018-10-04 13:37:12 +00:00
this->graphicsView->setMinimumHeight(15);
this->graphicsView->setMaximumHeight(15);
this->graphicsView->setFrameShape(QFrame::NoFrame);
this->graphicsView->setRenderHints(0);
this->graphicsView->setScene(graphicsScene);
this->graphicsView->setRenderHints(QPainter::Antialiasing);
this->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
this->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// So the graphicsView doesn't intercept mouse events.
this->graphicsView->setEnabled(false);
this->graphicsView->setMouseTracking(true);
setMouseTracking(true);
}
2018-07-06 16:00:26 +00:00
unsigned int nextPow2(unsigned int n)
{
unsigned int b = 0;
while (n) {
n >>= 1;
b++;
}
return (1u << b);
}
void VisualNavbar::paintEvent(QPaintEvent *event)
2017-04-09 19:55:06 +00:00
{
2017-10-01 18:08:12 +00:00
Q_UNUSED(event);
QPainter painter(this);
2018-07-06 16:00:26 +00:00
auto w = static_cast<unsigned int>(width());
bool fetch = false;
if (statsWidth < w) {
statsWidth = nextPow2(w);
fetch = true;
2018-09-30 20:00:53 +00:00
} else if (statsWidth > w * 4) {
2018-07-06 16:00:26 +00:00
statsWidth = statsWidth > 0 ? statsWidth / 2 : 0;
fetch = true;
}
if (fetch) {
2018-07-01 12:29:01 +00:00
fetchAndPaintData();
2018-07-06 16:00:26 +00:00
} else if (previousWidth != w) {
this->previousWidth = w;
updateGraphicsScene();
}
}
void VisualNavbar::fetchAndPaintData()
{
2018-07-06 16:00:26 +00:00
fetchStats();
updateGraphicsScene();
}
2018-07-06 16:00:26 +00:00
void VisualNavbar::fetchStats()
{
2018-07-06 16:00:26 +00:00
stats = Core()->getBlockStatistics(statsWidth);
}
2018-09-30 20:00:53 +00:00
enum class DataType : int { Empty, Code, String, Symbol, Count };
2018-07-06 16:00:26 +00:00
void VisualNavbar::updateGraphicsScene()
2017-04-09 19:55:06 +00:00
{
2018-06-29 12:35:02 +00:00
graphicsScene->clear();
xToAddress.clear();
seekGraphicsItem = nullptr;
PCGraphicsItem = nullptr;
2018-06-29 12:35:02 +00:00
graphicsScene->setBackgroundBrush(QBrush(Config()->getColor("gui.navbar.empty")));
2018-06-29 12:35:02 +00:00
if (stats.to <= stats.from) {
return;
}
2018-06-29 12:35:02 +00:00
int w = graphicsView->width();
int h = graphicsView->height();
2018-06-29 12:35:02 +00:00
RVA totalSize = stats.to - stats.from;
2018-07-06 15:32:35 +00:00
RVA beginAddr = stats.from;
2018-07-01 12:29:01 +00:00
2018-06-29 12:35:02 +00:00
double widthPerByte = (double)w / (double)totalSize;
2018-07-06 15:32:35 +00:00
auto xFromAddr = [widthPerByte, beginAddr] (RVA addr) -> double {
return (addr - beginAddr) * widthPerByte;
2018-06-29 12:35:02 +00:00
};
2018-06-29 12:35:02 +00:00
std::array<QBrush, static_cast<int>(DataType::Count)> dataTypeBrushes;
dataTypeBrushes[static_cast<int>(DataType::Code)] = QBrush(Config()->getColor("gui.navbar.code"));
dataTypeBrushes[static_cast<int>(DataType::String)] = QBrush(Config()->getColor("gui.navbar.str"));
dataTypeBrushes[static_cast<int>(DataType::Symbol)] = QBrush(Config()->getColor("gui.navbar.sym"));
2018-06-29 12:35:02 +00:00
DataType lastDataType = DataType::Empty;
QGraphicsRectItem *dataItem = nullptr;
QRectF dataItemRect(0.0, 0.0, 0.0, h);
for (const BlockDescription &block : stats.blocks) {
// Keep track of where which memory segment is mapped so we are able to convert from
// address to X coordinate and vice versa.
XToAddress x2a;
x2a.x_start = xFromAddr(block.addr);
x2a.x_end = xFromAddr(block.addr + block.size);
x2a.address_from = block.addr;
x2a.address_to = block.addr + block.size;
xToAddress.append(x2a);
2018-06-29 12:35:02 +00:00
DataType dataType;
2018-07-06 15:32:35 +00:00
if (block.functions > 0) {
2018-06-29 12:35:02 +00:00
dataType = DataType::Code;
} else if (block.strings > 0) {
dataType = DataType::String;
} else if (block.symbols > 0) {
dataType = DataType::Symbol;
2018-07-06 15:32:35 +00:00
} else if (block.inFunctions > 0) {
dataType = DataType::Code;
2018-06-29 12:35:02 +00:00
} else {
2018-07-01 12:29:01 +00:00
lastDataType = DataType::Empty;
2018-06-29 12:35:02 +00:00
continue;
}
2018-06-29 12:35:02 +00:00
if (dataType == lastDataType) {
2018-07-01 12:29:01 +00:00
double r = xFromAddr(block.addr + block.size);
if (r > dataItemRect.right()) {
dataItemRect.setRight(r);
dataItem->setRect(dataItemRect);
}
2018-06-29 12:35:02 +00:00
dataItem->setRect(dataItemRect);
continue;
}
2018-06-29 12:35:02 +00:00
dataItemRect.setX(xFromAddr(block.addr));
dataItemRect.setRight(xFromAddr(block.addr + block.size));
2018-06-29 12:35:02 +00:00
dataItem = new QGraphicsRectItem();
dataItem->setPen(Qt::NoPen);
dataItem->setBrush(dataTypeBrushes[static_cast<int>(dataType)]);
graphicsScene->addItem(dataItem);
2018-06-29 12:35:02 +00:00
lastDataType = dataType;
}
// Update scene width
2018-07-01 12:29:01 +00:00
graphicsScene->setSceneRect(0, 0, w, h);
drawSeekCursor();
}
void VisualNavbar::drawCursor(RVA addr, QColor color, QGraphicsRectItem *&graphicsItem)
{
double cursor_x = addressToLocalX(addr);
if (graphicsItem != nullptr) {
graphicsScene->removeItem(graphicsItem);
delete graphicsItem;
graphicsItem = nullptr;
}
2018-03-21 20:32:32 +00:00
if (std::isnan(cursor_x)) {
return;
}
int h = this->graphicsView->height();
graphicsItem = new QGraphicsRectItem(cursor_x, 0, 2, h);
graphicsItem->setPen(Qt::NoPen);
graphicsItem->setBrush(QBrush(color));
graphicsScene->addItem(graphicsItem);
}
void VisualNavbar::drawPCCursor()
{
drawCursor(Core()->getProgramCounterValue(), Config()->getColor("gui.navbar.pc"), PCGraphicsItem);
}
void VisualNavbar::drawSeekCursor()
{
drawCursor(Core()->getOffset(), Config()->getColor("gui.navbar.seek"), seekGraphicsItem);
}
void VisualNavbar::on_seekChanged(RVA addr)
{
Q_UNUSED(addr);
// Update cursor
this->drawSeekCursor();
}
void VisualNavbar::mousePressEvent(QMouseEvent *event)
{
qreal x = event->localPos().x();
RVA address = localXToAddress(x);
2018-03-21 20:32:32 +00:00
if (address != RVA_INVALID) {
QToolTip::showText(event->globalPos(), toolTipForAddress(address), this);
2018-03-21 20:32:32 +00:00
if (event->buttons() & Qt::LeftButton) {
event->accept();
Core()->seek(address);
}
}
}
void VisualNavbar::mouseMoveEvent(QMouseEvent *event)
{
event->accept();
mousePressEvent(event);
}
RVA VisualNavbar::localXToAddress(double x)
{
for (const XToAddress &x2a : xToAddress) {
2018-03-21 20:32:32 +00:00
if ((x2a.x_start <= x) && (x <= x2a.x_end)) {
double offset = (x - x2a.x_start) / (x2a.x_end - x2a.x_start);
double size = x2a.address_to - x2a.address_from;
return x2a.address_from + (offset * size);
}
}
return RVA_INVALID;
}
double VisualNavbar::addressToLocalX(RVA address)
{
for (const XToAddress &x2a : xToAddress) {
2018-03-21 20:32:32 +00:00
if ((x2a.address_from <= address) && (address < x2a.address_to)) {
double offset = (double)(address - x2a.address_from) / (double)(x2a.address_to - x2a.address_from);
double size = x2a.x_end - x2a.x_start;
return x2a.x_start + (offset * size);
}
}
return nan("");
}
QList<QString> VisualNavbar::sectionsForAddress(RVA address)
{
QList<QString> ret;
2018-06-29 12:35:02 +00:00
QList<SectionDescription> sections = Core()->getAllSections();
for (const SectionDescription &section : sections) {
2018-06-29 12:35:02 +00:00
if (address >= section.vaddr && address < section.vaddr + section.vsize) {
ret << section.name;
}
}
return ret;
}
QString VisualNavbar::toolTipForAddress(RVA address)
{
QString ret = "Address: " + RAddressString(address);
auto sections = sectionsForAddress(address);
2018-03-21 20:32:32 +00:00
if (sections.count()) {
ret += "\nSections: \n";
bool first = true;
for (const QString &section : sections) {
if (!first) {
2019-03-23 10:54:34 +00:00
ret.append(QLatin1Char('\n'));
} else {
first = false;
}
ret += " " + section;
}
}
return ret;
}