mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-09 13:35:25 +00:00
259 lines
7.6 KiB
C++
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;
|
|
}
|