2017-12-23 16:42:42 +00:00
|
|
|
|
|
|
|
#include <QList>
|
|
|
|
|
|
|
|
#include "ClassesWidget.h"
|
|
|
|
#include "ui_ClassesWidget.h"
|
|
|
|
#include "utils/Helpers.h"
|
|
|
|
|
|
|
|
ClassesModel::ClassesModel(QList<ClassDescription> *classes, QObject *parent)
|
|
|
|
: QAbstractItemModel(parent),
|
|
|
|
classes(classes)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QModelIndex ClassesModel::index(int row, int column, const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
if (!parent.isValid())
|
|
|
|
return createIndex(row, column, (quintptr)0); // root function nodes have id = 0
|
|
|
|
|
|
|
|
return createIndex(row, column, (quintptr)parent.row() + 1); // sub-nodes have id = class index + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex ClassesModel::parent(const QModelIndex &index) const
|
|
|
|
{
|
|
|
|
if (!index.isValid() || index.column() != 0)
|
|
|
|
return QModelIndex();
|
|
|
|
|
|
|
|
if (index.internalId() == 0) // root function node
|
|
|
|
return QModelIndex();
|
|
|
|
else // sub-node
|
|
|
|
return this->index((int)(index.internalId() - 1), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ClassesModel::rowCount(const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
if (!parent.isValid()) // root
|
|
|
|
{
|
|
|
|
return classes->count();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent.internalId() == 0) // methods/fields
|
|
|
|
{
|
|
|
|
const ClassDescription *cls = &classes->at(parent.row());
|
|
|
|
return cls->methods.length() + cls->fields.length();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0; // below methods/fields
|
|
|
|
}
|
|
|
|
|
|
|
|
int ClassesModel::columnCount(const QModelIndex &) const
|
|
|
|
{
|
|
|
|
return Columns::COUNT;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant ClassesModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
|
|
|
const ClassDescription *cls;
|
|
|
|
const ClassMethodDescription *meth = nullptr;
|
|
|
|
const ClassFieldDescription *field = nullptr;
|
|
|
|
if (index.internalId() == 0) // class row
|
|
|
|
{
|
|
|
|
if (index.row() >= classes->count())
|
|
|
|
{
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
cls = &classes->at(index.row());
|
|
|
|
}
|
|
|
|
else // method/field row
|
|
|
|
{
|
|
|
|
cls = &classes->at(static_cast<int>(index.internalId() - 1));
|
|
|
|
|
|
|
|
if (index.row() >= cls->methods.length() + cls->fields.length())
|
|
|
|
{
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index.row() < cls->methods.length())
|
|
|
|
{
|
|
|
|
meth = &cls->methods[index.row()];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
field = &cls->fields[index.row() - cls->methods.length()];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (meth)
|
|
|
|
{
|
|
|
|
switch (role)
|
|
|
|
{
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column())
|
|
|
|
{
|
|
|
|
case NAME:
|
|
|
|
return meth->name;
|
|
|
|
case TYPE:
|
|
|
|
return tr("method");
|
|
|
|
case OFFSET:
|
|
|
|
return RAddressString(meth->addr);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
case OffsetRole:
|
|
|
|
return QVariant::fromValue(meth->addr);
|
|
|
|
case NameRole:
|
|
|
|
return meth->name;
|
|
|
|
case TypeRole:
|
|
|
|
return QVariant::fromValue(METHOD);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (field)
|
|
|
|
{
|
|
|
|
switch (role)
|
|
|
|
{
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column())
|
|
|
|
{
|
|
|
|
case NAME:
|
|
|
|
return field->name;
|
|
|
|
case TYPE:
|
|
|
|
return tr("field");
|
|
|
|
case OFFSET:
|
|
|
|
return RAddressString(field->addr);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
case OffsetRole:
|
|
|
|
return QVariant::fromValue(field->addr);
|
|
|
|
case NameRole:
|
|
|
|
return field->name;
|
|
|
|
case TypeRole:
|
|
|
|
return QVariant::fromValue(FIELD);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (role)
|
|
|
|
{
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column())
|
|
|
|
{
|
|
|
|
case NAME:
|
|
|
|
return cls->name;
|
|
|
|
case TYPE:
|
|
|
|
return tr("class");
|
|
|
|
case OFFSET:
|
|
|
|
return RAddressString(cls->addr);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
case OffsetRole:
|
|
|
|
return QVariant::fromValue(cls->addr);
|
|
|
|
case NameRole:
|
|
|
|
return cls->name;
|
|
|
|
case TypeRole:
|
|
|
|
return QVariant::fromValue(CLASS);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const
|
|
|
|
{
|
|
|
|
switch (role)
|
|
|
|
{
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (section)
|
|
|
|
{
|
|
|
|
case NAME:
|
|
|
|
return tr("Name");
|
|
|
|
case TYPE:
|
|
|
|
return tr("Type");
|
|
|
|
case OFFSET:
|
|
|
|
return tr("Offset");
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClassesModel::beginReload()
|
|
|
|
{
|
|
|
|
beginResetModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClassesModel::endReload()
|
|
|
|
{
|
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ClassesSortFilterProxyModel::ClassesSortFilterProxyModel(ClassesModel *source_model, QObject *parent)
|
|
|
|
: QSortFilterProxyModel(parent)
|
|
|
|
{
|
|
|
|
setSourceModel(source_model);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClassesSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
QModelIndex index = sourceModel()->index(row, 0, parent);
|
|
|
|
return index.data(ClassesModel::NameRole).toString().contains(filterRegExp());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClassesSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
|
|
|
{
|
|
|
|
switch (left.column())
|
|
|
|
{
|
|
|
|
case ClassesModel::OFFSET:
|
|
|
|
{
|
|
|
|
RVA left_offset = left.data(ClassesModel::OffsetRole).toULongLong();
|
|
|
|
RVA right_offset = right.data(ClassesModel::OffsetRole).toULongLong();
|
|
|
|
if (left_offset != right_offset)
|
|
|
|
return left_offset < right_offset;
|
|
|
|
}
|
|
|
|
// fallthrough
|
|
|
|
case ClassesModel::TYPE:
|
|
|
|
{
|
|
|
|
auto left_type = left.data(ClassesModel::TypeRole).value<ClassesModel::RowType>();
|
|
|
|
auto right_type = right.data(ClassesModel::TypeRole).value<ClassesModel::RowType>();
|
|
|
|
if (left_type != right_type)
|
|
|
|
return left_type < right_type;
|
|
|
|
}
|
|
|
|
// fallthrough
|
|
|
|
case ClassesModel::NAME:
|
|
|
|
default:
|
|
|
|
QString left_name = left.data(ClassesModel::NameRole).toString();
|
|
|
|
QString right_name = right.data(ClassesModel::NameRole).toString();
|
|
|
|
return left_name < right_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-02-04 14:32:18 +00:00
|
|
|
ClassesWidget::ClassesWidget(QWidget *parent) :
|
2017-12-23 16:42:42 +00:00
|
|
|
QDockWidget(parent),
|
2018-02-04 14:32:18 +00:00
|
|
|
ui(new Ui::ClassesWidget)
|
2017-12-23 16:42:42 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
|
|
|
model = new ClassesModel(&classes, this);
|
|
|
|
proxy_model = new ClassesSortFilterProxyModel(model, this);
|
|
|
|
ui->classesTreeView->setModel(proxy_model);
|
|
|
|
ui->classesTreeView->sortByColumn(ClassesModel::TYPE, Qt::AscendingOrder);
|
|
|
|
|
|
|
|
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshClasses()));
|
|
|
|
}
|
|
|
|
|
|
|
|
ClassesWidget::~ClassesWidget() {}
|
|
|
|
|
|
|
|
void ClassesWidget::refreshClasses()
|
|
|
|
{
|
|
|
|
model->beginReload();
|
2018-03-11 11:13:08 +00:00
|
|
|
classes = CutterCore::getInstance()->getAllClassesFromBin();
|
2017-12-23 16:42:42 +00:00
|
|
|
model->endReload();
|
|
|
|
|
|
|
|
ui->classesTreeView->resizeColumnToContents(0);
|
|
|
|
ui->classesTreeView->resizeColumnToContents(1);
|
|
|
|
ui->classesTreeView->resizeColumnToContents(2);
|
|
|
|
|
|
|
|
ui->classesTreeView->setColumnWidth(0, 200);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClassesWidget::on_classesTreeView_doubleClicked(const QModelIndex &index)
|
|
|
|
{
|
|
|
|
RVA offset = index.data(ClassesModel::OffsetRole).value<RVA>();
|
|
|
|
CutterCore::getInstance()->seek(offset);
|
|
|
|
}
|