cutter/src/dialogs/XrefsDialog.cpp

259 lines
7.6 KiB
C++

#include "XrefsDialog.h"
#include "ui_XrefsDialog.h"
#include "common/TempConfig.h"
#include "common/Helpers.h"
#include "core/MainWindow.h"
#include <QJsonArray>
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<int>() << 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()->getBaseFont());
}
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<QTextEdit::ExtraSelection> 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);
tempConfig.set("asm.lines", false);
tempConfig.set("asm.bytes", false);
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);
// Automatically select the first line
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;
}