#include "XrefsDialog.h" #include "ui_XrefsDialog.h" #include "common/TempConfig.h" #include "common/Helpers.h" #include "core/MainWindow.h" #include XrefsDialog::XrefsDialog(MainWindow *main, QWidget *parent) : QDialog(parent), addr(0), toModel(this), fromModel(this), ui(new Ui::XrefsDialog) { ui->setupUi(this); setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); ui->toTreeWidget->setMainWindow(main); ui->fromTreeWidget->setMainWindow(main); ui->toTreeWidget->setModel(&toModel); ui->fromTreeWidget->setModel(&fromModel); // Modify the splitter's location to show more Disassembly instead of empty space. Not possible via Designer ui->splitter->setSizes(QList() << 100 << 200); // Increase asm text edit margin QTextDocument *asm_docu = ui->previewTextEdit->document(); asm_docu->setDocumentMargin(10); setupPreviewColors(); setupPreviewFont(); // Highlight current line connect(ui->previewTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(setupPreviewFont())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(setupPreviewColors())); connect(ui->toTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &XrefsDialog::onToTreeWidgetItemSelectionChanged); connect(ui->fromTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &XrefsDialog::onFromTreeWidgetItemSelectionChanged); // Don't create recursive xref dialogs auto toContextMenu = ui->toTreeWidget->getItemContextMenu(); connect(toContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close); auto fromContextMenu = ui->fromTreeWidget->getItemContextMenu(); connect(fromContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close); connect(ui->toTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close); connect(ui->fromTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close); } XrefsDialog::~XrefsDialog() { } QString XrefsDialog::normalizeAddr(const QString &addr) const { QString ret = addr; if (addr.length() < 10) { ret = ret.mid(3).rightJustified(8, QLatin1Char('0')); ret.prepend(QLatin1Literal("0x")); } return ret; } void XrefsDialog::setupPreviewFont() { ui->previewTextEdit->setFont(Config()->getFont()); } void XrefsDialog::setupPreviewColors() { ui->previewTextEdit->setStyleSheet(QString("QPlainTextEdit { background-color: %1; color: %2; }") .arg(ConfigColor("gui.background").name()) .arg(ConfigColor("btext").name())); } void XrefsDialog::highlightCurrentLine() { QList extraSelections; if (ui->previewTextEdit->isReadOnly()) { QTextEdit::ExtraSelection selection = QTextEdit::ExtraSelection(); selection.format.setBackground(ConfigColor("lineHighlight")); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = ui->previewTextEdit->textCursor(); selection.cursor.clearSelection(); extraSelections.append(selection); ui->previewTextEdit->setExtraSelections(extraSelections); } } void XrefsDialog::onFromTreeWidgetItemSelectionChanged() { auto index = ui->fromTreeWidget->currentIndex(); if (!ui->fromTreeWidget->selectionModel()->hasSelection() || !index.isValid()) { return; } ui->toTreeWidget->clearSelection(); updatePreview(fromModel.address(index)); } void XrefsDialog::onToTreeWidgetItemSelectionChanged() { auto index = ui->toTreeWidget->currentIndex(); if (!ui->toTreeWidget->selectionModel()->hasSelection() || !index.isValid()) { return; } ui->fromTreeWidget->clearSelection(); updatePreview(toModel.address(index)); } void XrefsDialog::updatePreview(RVA addr) { TempConfig tempConfig; tempConfig.set("scr.html", true); tempConfig.set("scr.color", COLOR_MODE_16M); QString disas = Core()->cmd("pd--20 @ " + QString::number(addr)); ui->previewTextEdit->document()->setHtml(disas); // Does it make any sense? ui->previewTextEdit->find(normalizeAddr(RAddressString(addr)), QTextDocument::FindBackward); ui->previewTextEdit->moveCursor(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); } void XrefsDialog::updateLabels(QString name) { ui->label_xTo->setText(tr("X-Refs to %1:").arg(name)); ui->label_xFrom->setText(tr("X-Refs from %1:").arg(name)); } void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function) { TempConfig tempConfig; tempConfig.set("scr.html", false); tempConfig.set("scr.color", COLOR_MODE_DISABLED); setWindowTitle(tr("X-Refs for %1").arg(name)); updateLabels(name); toModel.readForOffset(addr, true, whole_function); fromModel.readForOffset(addr, false, whole_function); // Adjust columns to content qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0); qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0); // try to select first item from refs or xrefs if (!qhelpers::selectFirstItem(ui->toTreeWidget)) { qhelpers::selectFirstItem(ui->fromTreeWidget); } } QString XrefModel::xrefTypeString(const QString &type) { if (type == "CODE") { return QStringLiteral("Code"); } else if (type == "CALL") { return QStringLiteral("Call"); } else if (type == "DATA") { return QStringLiteral("Data"); } else if (type == "STRING") { return QStringLiteral("String"); } return type; } XrefModel::XrefModel(QObject *parent) : AddressableItemModel(parent) { } void XrefModel::readForOffset(RVA offset, bool to, bool whole_function) { beginResetModel(); this->to = to; xrefs = Core()->getXRefs(offset, to, whole_function); endResetModel(); } int XrefModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return xrefs.size(); } int XrefModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return Columns::COUNT; } QVariant XrefModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= xrefs.count()) { return QVariant(); } const XrefDescription &xref = xrefs.at(index.row()); switch (role) { case Qt::DisplayRole: switch (index.column()) { case OFFSET: return to ? xref.from_str : xref.to_str; case CODE: if (to || xref.type != "DATA") { return Core()->disassembleSingleInstruction(xref.from); } else { return QString(); } case TYPE: return xrefTypeString(xref.type); } return QVariant(); case FlagDescriptionRole: return QVariant::fromValue(xref); default: return QVariant(); } return QVariant(); } QVariant XrefModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation) switch (role) { case Qt::DisplayRole: switch (section) { case OFFSET: return tr("Address"); case CODE: return tr("Code"); case TYPE: return tr("Type"); default: return QVariant(); } default: return QVariant(); } } RVA XrefModel::address(const QModelIndex &index) const { const auto &xref = xrefs.at(index.row()); return to ? xref.from : xref.to; }