#include "VisualNavbar.h" #include "MainWindow.h" #include "utils/TempConfig.h" #include #include #include #include #include #include #include #include #include #include VisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent) : QToolBar(main), graphicsView(new QGraphicsView), cursorGraphicsItem(nullptr), main(main) { Q_UNUSED(parent); setObjectName("visualNavbar"); setWindowTitle(tr("Visual navigation bar")); // 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(refreshAll()), this, SLOT(fetchAndPaintData())); 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); this->graphicsView->setMinimumHeight(20); this->graphicsView->setMaximumHeight(20); 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); } void VisualNavbar::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); if (previousWidth != this->width()) { fetchAndPaintData(); previousWidth = this->width(); } } void VisualNavbar::fetchAndPaintData() { fetchData(); fillData(); } void VisualNavbar::fetchData() { statsWidth = graphicsView->width(); stats = Core()->getBlockStatistics((unsigned int)statsWidth); } enum class DataType: int { Empty, Code, String, Symbol, Count }; void VisualNavbar::fillData() { graphicsScene->clear(); xToAddress.clear(); cursorGraphicsItem = nullptr; graphicsScene->setBackgroundBrush(QBrush(Config()->getColor("gui.navbar.empty"))); if (stats.to <= stats.from) { return; } int w = graphicsView->width(); int h = graphicsView->height(); RVA totalSize = stats.to - stats.from; RVA beginAddr = stats.from; double widthPerByte = (double)w / (double)totalSize; auto xFromAddr = [widthPerByte, beginAddr] (RVA addr) -> double { return (addr - beginAddr) * widthPerByte; }; std::array(DataType::Count)> dataTypeBrushes; dataTypeBrushes[static_cast(DataType::Code)] = QBrush(Config()->getColor("gui.navbar.code")); dataTypeBrushes[static_cast(DataType::String)] = QBrush(Config()->getColor("gui.navbar.str")); dataTypeBrushes[static_cast(DataType::Symbol)] = QBrush(Config()->getColor("gui.navbar.sym")); 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); DataType dataType; if (block.functions > 0) { dataType = DataType::Code; } else if (block.strings > 0) { dataType = DataType::String; } else if (block.symbols > 0) { dataType = DataType::Symbol; } else if (block.inFunctions > 0) { dataType = DataType::Code; } else { lastDataType = DataType::Empty; continue; } if (dataType == lastDataType) { double r = xFromAddr(block.addr + block.size); if (r > dataItemRect.right()) { dataItemRect.setRight(r); dataItem->setRect(dataItemRect); } dataItem->setRect(dataItemRect); continue; } dataItemRect.setX(xFromAddr(block.addr)); dataItemRect.setRight(xFromAddr(block.addr + block.size)); dataItem = new QGraphicsRectItem(); dataItem->setPen(Qt::NoPen); dataItem->setBrush(dataTypeBrushes[static_cast(dataType)]); graphicsScene->addItem(dataItem); lastDataType = dataType; } // Update scene width graphicsScene->setSceneRect(0, 0, w, h); drawCursor(); } void VisualNavbar::drawCursor() { RVA offset = Core()->getOffset(); double cursor_x = addressToLocalX(offset); if (cursorGraphicsItem != nullptr) { graphicsScene->removeItem(cursorGraphicsItem); delete cursorGraphicsItem; cursorGraphicsItem = nullptr; } if (std::isnan(cursor_x)) { return; } int h = this->graphicsView->height(); cursorGraphicsItem = new QGraphicsRectItem(cursor_x, 0, 2, h); cursorGraphicsItem->setPen(Qt::NoPen); cursorGraphicsItem->setBrush(QBrush(Config()->getColor("gui.navbar.err"))); graphicsScene->addItem(cursorGraphicsItem); } void VisualNavbar::on_seekChanged(RVA addr) { Q_UNUSED(addr); // Update cursor this->drawCursor(); } void VisualNavbar::mousePressEvent(QMouseEvent *event) { qreal x = event->localPos().x(); RVA address = localXToAddress(x); if (address != RVA_INVALID) { QToolTip::showText(event->globalPos(), toolTipForAddress(address), this); 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 (auto x2a : xToAddress) { 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 (auto x2a : xToAddress) { 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 VisualNavbar::sectionsForAddress(RVA address) { QList ret; QList sections = Core()->getAllSections(); for (const auto §ion : sections) { 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); if (sections.count()) { ret += "\nSections: \n"; bool first = true; for (auto section : sections) { if (!first) { ret += "\n"; } else { first = false; } ret += " " + section; } } return ret; }