Add Global Vars widget, dialog, context menus ()

* Add global variables support

- Add Globals widget
- Add global variable add/modify dialog
- Add "Add at" context submenu in Disassembly widget

Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com>

---------

Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com>
This commit is contained in:
Anton Kochkov 2023-08-04 10:17:35 +08:00 committed by GitHub
parent 041118dbd7
commit 3d30892a30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 823 additions and 6 deletions

View File

@ -18,6 +18,7 @@ set(SOURCES
dialogs/CommentsDialog.cpp
dialogs/EditInstructionDialog.cpp
dialogs/FlagDialog.cpp
dialogs/GlobalVariableDialog.cpp
dialogs/RemoteDebugDialog.cpp
dialogs/NativeDebugDialog.cpp
dialogs/XrefsDialog.cpp
@ -36,6 +37,7 @@ set(SOURCES
widgets/ExportsWidget.cpp
widgets/FlagsWidget.cpp
widgets/FunctionsWidget.cpp
widgets/GlobalsWidget.cpp
widgets/ImportsWidget.cpp
widgets/Omnibar.cpp
widgets/RelocsWidget.cpp
@ -172,6 +174,7 @@ set(HEADER_FILES
dialogs/CommentsDialog.h
dialogs/EditInstructionDialog.h
dialogs/FlagDialog.h
dialogs/GlobalVariableDialog.h
dialogs/RemoteDebugDialog.h
dialogs/NativeDebugDialog.h
dialogs/XrefsDialog.h
@ -327,6 +330,7 @@ set(UI_FILES
dialogs/CommentsDialog.ui
dialogs/EditInstructionDialog.ui
dialogs/FlagDialog.ui
dialogs/GlobalVariableDialog.ui
dialogs/RemoteDebugDialog.ui
dialogs/NativeDebugDialog.ui
dialogs/XrefsDialog.ui
@ -338,6 +342,7 @@ set(UI_FILES
widgets/Dashboard.ui
widgets/EntrypointWidget.ui
widgets/FlagsWidget.ui
widgets/GlobalsWidget.ui
widgets/StringsWidget.ui
widgets/HexdumpWidget.ui
dialogs/preferences/PreferencesDialog.ui

View File

@ -1815,6 +1815,32 @@ QList<VariableDescription> CutterCore::getVariables(RVA at)
return ret;
}
QList<GlobalDescription> CutterCore::getAllGlobals()
{
CORE_LOCK();
RzListIter *it;
QList<GlobalDescription> ret;
RzAnalysisVarGlobal *glob;
if (core && core->analysis && core->analysis->typedb) {
const RzList *globals = rz_analysis_var_global_get_all(core->analysis);
CutterRzListForeach (globals, it, RzAnalysisVarGlobal, glob) {
const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);
if (!gtype) {
continue;
}
GlobalDescription global;
global.addr = glob->addr;
global.name = QString(glob->name);
global.type = QString(gtype);
ret << global;
}
}
return ret;
}
QVector<RegisterRefValueDescription> CutterCore::getRegisterRefValues()
{
QVector<RegisterRefValueDescription> result;
@ -4022,6 +4048,99 @@ QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_functi
return xrefList;
}
void CutterCore::addGlobalVariable(RVA offset, QString name, QString typ)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
char *errmsg = NULL;
RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser,
typ.toStdString().c_str(), &errmsg);
if (errmsg) {
qWarning() << tr("Error parsing type: \"%1\" message: ").arg(typ) << errmsg;
free(errmsg);
return;
}
if (!rz_analysis_var_global_create(core->analysis, name.toStdString().c_str(), globType,
offset)) {
qWarning() << tr("Error creating global variable: \"%1\"").arg(name);
return;
}
emit globalVarsChanged();
}
void CutterCore::modifyGlobalVariable(RVA offset, QString name, QString typ)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset);
if (!glob) {
return;
}
// Compare if the name is not the same - also rename it
if (name.compare(glob->name)) {
rz_analysis_var_global_rename(core->analysis, glob->name, name.toStdString().c_str());
}
char *errmsg = NULL;
RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser,
typ.toStdString().c_str(), &errmsg);
if (errmsg) {
qWarning() << tr("Error parsing type: \"%1\" message: ").arg(typ) << errmsg;
free(errmsg);
return;
}
rz_analysis_var_global_set_type(glob, globType);
emit globalVarsChanged();
}
void CutterCore::delGlobalVariable(QString name)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
rz_analysis_var_global_delete_byname(core->analysis, name.toStdString().c_str());
emit globalVarsChanged();
}
void CutterCore::delGlobalVariable(RVA offset)
{
CORE_LOCK();
rz_analysis_var_global_delete_byaddr_at(core->analysis, offset);
emit globalVarsChanged();
}
QString CutterCore::getGlobalVariableType(QString name)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
RzAnalysisVarGlobal *glob =
rz_analysis_var_global_get_byname(core->analysis, name.toStdString().c_str());
if (!glob) {
return QString("");
}
const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);
if (!gtype) {
return QString("");
}
return QString(gtype);
}
QString CutterCore::getGlobalVariableType(RVA offset)
{
CORE_LOCK();
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset);
if (!glob) {
return QString("");
}
const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);
if (!gtype) {
return QString("");
}
return QString(gtype);
}
void CutterCore::addFlag(RVA offset, QString name, RVA size)
{
name = sanitizeStringForCommand(name);
@ -4536,9 +4655,9 @@ char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat form
RzGraph *graph = rz_core_graph(core, type, address);
if (!graph) {
if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph";
qWarning() << tr("Cannot get global graph");
} else {
qWarning() << "Cannot get graph at " << RzAddressString(address);
qWarning() << tr("Cannot get graph at ") << RzAddressString(address);
}
return nullptr;
}

View File

@ -239,6 +239,14 @@ public:
QString nearestFlag(RVA offset, RVA *flagOffsetOut);
void triggerFlagsChanged();
/* Global Variables */
void addGlobalVariable(RVA offset, QString name, QString typ);
void delGlobalVariable(QString name);
void delGlobalVariable(RVA offset);
void modifyGlobalVariable(RVA offset, QString name, QString typ);
QString getGlobalVariableType(QString name);
QString getGlobalVariableType(RVA offset);
/* Edition functions */
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
QString getInstructionBytes(RVA addr);
@ -584,6 +592,7 @@ public:
QList<ExportDescription> getAllExports();
QList<SymbolDescription> getAllSymbols();
QList<HeaderDescription> getAllHeaders();
QList<GlobalDescription> getAllGlobals();
QList<FlirtDescription> getSignaturesDB();
QList<CommentDescription> getAllComments(const QString &filterType);
QList<RelocDescription> getAllRelocs();
@ -750,6 +759,7 @@ signals:
void functionRenamed(const RVA offset, const QString &new_name);
void varsChanged();
void globalVarsChanged();
void functionsChanged();
void flagsChanged();
void commentsChanged(RVA addr);

View File

@ -360,6 +360,13 @@ struct VariableDescription
QString value;
};
struct GlobalDescription
{
RVA addr;
QString type;
QString name;
};
struct RegisterRefValueDescription
{
QString name;
@ -407,6 +414,7 @@ Q_DECLARE_METATYPE(RelocDescription)
Q_DECLARE_METATYPE(StringDescription)
Q_DECLARE_METATYPE(FlagspaceDescription)
Q_DECLARE_METATYPE(FlagDescription)
Q_DECLARE_METATYPE(GlobalDescription)
Q_DECLARE_METATYPE(XrefDescription)
Q_DECLARE_METATYPE(EntrypointDescription)
Q_DECLARE_METATYPE(RzBinPluginDescription)

View File

@ -31,6 +31,7 @@
#include "widgets/DisassemblerGraphView.h"
#include "widgets/GraphView.h"
#include "widgets/GraphWidget.h"
#include "widgets/GlobalsWidget.h"
#include "widgets/OverviewWidget.h"
#include "widgets/OverviewView.h"
#include "widgets/FunctionsWidget.h"
@ -401,6 +402,7 @@ void MainWindow::initDocks()
sectionsDock = new SectionsWidget(this),
segmentsDock = new SegmentsWidget(this),
symbolsDock = new SymbolsWidget(this),
globalsDock = new GlobalsWidget(this),
vTablesDock = new VTablesWidget(this),
flirtDock = new FlirtWidget(this),
rzGraphDock = new RizinGraphWidget(this),
@ -905,6 +907,7 @@ void MainWindow::restoreDocks()
tabifyDockWidget(dashboardDock, headersDock);
tabifyDockWidget(dashboardDock, flirtDock);
tabifyDockWidget(dashboardDock, symbolsDock);
tabifyDockWidget(dashboardDock, globalsDock);
tabifyDockWidget(dashboardDock, classesDock);
tabifyDockWidget(dashboardDock, resourcesDock);
tabifyDockWidget(dashboardDock, vTablesDock);

View File

@ -26,6 +26,7 @@ class FunctionsWidget;
class ImportsWidget;
class ExportsWidget;
class SymbolsWidget;
class GlobalsWidget;
class RelocsWidget;
class CommentsWidget;
class StringsWidget;
@ -240,6 +241,7 @@ private:
TypesWidget *typesDock = nullptr;
SearchWidget *searchDock = nullptr;
SymbolsWidget *symbolsDock = nullptr;
GlobalsWidget *globalsDock = nullptr;
RelocsWidget *relocsDock = nullptr;
CommentsWidget *commentsDock = nullptr;
StringsWidget *stringsDock = nullptr;

View File

@ -0,0 +1,71 @@
#include "GlobalVariableDialog.h"
#include "ui_GlobalVariableDialog.h"
#include <QIntValidator>
#include "core/Cutter.h"
GlobalVariableDialog::GlobalVariableDialog(RVA offset, QWidget *parent)
: QDialog(parent),
ui(new Ui::GlobalVariableDialog),
offset(offset),
globalVariableName(""),
globalVariableOffset(RVA_INVALID)
{
// Setup UI
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
RzAnalysisVarGlobal *globalVariable =
rz_analysis_var_global_get_byaddr_at(Core()->core()->analysis, offset);
if (globalVariable) {
globalVariableName = QString(globalVariable->name);
globalVariableOffset = globalVariable->addr;
}
if (globalVariable) {
ui->nameEdit->setText(globalVariable->name);
QString globalVarType = Core()->getGlobalVariableType(globalVariable->name);
ui->typeEdit->setText(globalVarType);
ui->labelAction->setText(tr("Edit global variable at %1").arg(RzAddressString(offset)));
} else {
ui->labelAction->setText(tr("Add global variable at %1").arg(RzAddressString(offset)));
}
// Connect slots
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
&GlobalVariableDialog::buttonBoxAccepted);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this,
&GlobalVariableDialog::buttonBoxRejected);
}
GlobalVariableDialog::~GlobalVariableDialog() {}
void GlobalVariableDialog::buttonBoxAccepted()
{
QString name = ui->nameEdit->text();
QString typ = ui->typeEdit->text();
if (name.isEmpty()) {
if (globalVariableOffset != RVA_INVALID) {
// Empty name and global variable exists -> delete the global variable
Core()->delGlobalVariable(globalVariableOffset);
} else {
// GlobalVariable was not existing and we gave an empty name, do nothing
}
} else {
if (globalVariableOffset != RVA_INVALID) {
// Name provided and global variable exists -> rename the global variable
Core()->modifyGlobalVariable(globalVariableOffset, name, typ);
} else {
// Name provided and global variable does not exist -> create the global variable
Core()->addGlobalVariable(offset, name, typ);
}
}
close();
this->setResult(QDialog::Accepted);
}
void GlobalVariableDialog::buttonBoxRejected()
{
close();
this->setResult(QDialog::Rejected);
}

View File

@ -0,0 +1,32 @@
#ifndef GLOBALVARIABLEDIALOG_H
#define GLOBALVARIABLEDIALOG_H
#include <QDialog>
#include <memory>
#include "core/CutterCommon.h"
namespace Ui {
class GlobalVariableDialog;
}
class GlobalVariableDialog : public QDialog
{
Q_OBJECT
public:
explicit GlobalVariableDialog(RVA offset, QWidget *parent = nullptr);
~GlobalVariableDialog();
private slots:
void buttonBoxAccepted();
void buttonBoxRejected();
private:
std::unique_ptr<Ui::GlobalVariableDialog> ui;
RVA offset;
QString globalVariableName;
RVA globalVariableOffset;
QString typ;
};
#endif // GLOBALVARIABLEDIALOG_H

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GlobalVariableDialog</class>
<widget class="QDialog" name="GlobalVariableDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>452</width>
<height>121</height>
</rect>
</property>
<property name="windowTitle">
<string>Add Global Variable</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelAction">
<property name="text">
<string>Add global variable at</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="horizontalSpacing">
<number>12</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelName">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit">
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="typeEdit">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>int</string>
</property>
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelType">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -3,6 +3,7 @@
#include "dialogs/EditInstructionDialog.h"
#include "dialogs/CommentsDialog.h"
#include "dialogs/FlagDialog.h"
#include "dialogs/GlobalVariableDialog.h"
#include "dialogs/XrefsDialog.h"
#include "dialogs/EditVariablesDialog.h"
#include "dialogs/SetToDataDialog.h"
@ -34,6 +35,7 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
actionAnalyzeFunction(this),
actionEditFunction(this),
actionRename(this),
actionGlobalVar(this),
actionSetFunctionVarTypes(this),
actionXRefs(this),
actionXRefsForVariables(this),
@ -83,10 +85,6 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
getCommentSequence());
addAction(&actionAddComment);
initAction(&actionRename, tr("Rename or add flag"), SLOT(on_actionRename_triggered()),
getRenameSequence());
addAction(&actionRename);
initAction(&actionSetFunctionVarTypes, tr("Re-type Local Variables"),
SLOT(on_actionSetFunctionVarTypes_triggered()), getRetypeSequence());
addAction(&actionSetFunctionVarTypes);
@ -112,6 +110,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
addSeparator();
addAddAtMenu();
addSetBaseMenu();
addSetBitsMenu();
@ -166,6 +166,19 @@ QWidget *DisassemblyContextMenu::parentForDialog()
return parentWidget();
}
void DisassemblyContextMenu::addAddAtMenu()
{
setAsMenu = addMenu(tr("Add at..."));
initAction(&actionRename, tr("Rename or add flag"), SLOT(on_actionRename_triggered()),
getRenameSequence());
setAsMenu->addAction(&actionRename);
initAction(&actionGlobalVar, tr("Modify or add global variable"),
SLOT(on_actionGlobalVar_triggered()), getGlobalVarSequence());
setAsMenu->addAction(&actionGlobalVar);
}
void DisassemblyContextMenu::addSetBaseMenu()
{
setBaseMenu = addMenu(tr("Set base of immediate value to.."));
@ -479,7 +492,12 @@ void DisassemblyContextMenu::setupRenaming()
// Now, build the renaming menu and show it
buildRenameMenu(tuh);
auto name = RzAddressString(tuh->offset);
actionGlobalVar.setText(tr("Add or change global variable at %1 (used here)").arg(name));
actionRename.setVisible(true);
actionGlobalVar.setVisible(true);
}
void DisassemblyContextMenu::aboutToShowSlot()
@ -655,6 +673,11 @@ QKeySequence DisassemblyContextMenu::getRenameSequence() const
return { Qt::Key_N };
}
QKeySequence DisassemblyContextMenu::getGlobalVarSequence() const
{
return { Qt::Key_G };
}
QKeySequence DisassemblyContextMenu::getRetypeSequence() const
{
return { Qt::Key_Y };
@ -868,6 +891,18 @@ void DisassemblyContextMenu::on_actionRename_triggered()
}
}
void DisassemblyContextMenu::on_actionGlobalVar_triggered()
{
bool ok = false;
GlobalVariableDialog dialog(doRenameInfo.addr, parentForDialog());
ok = dialog.exec();
if (ok) {
// Rebuild menu in case the user presses the rename shortcut directly before clicking
setupRenaming();
}
}
void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered()
{
RzAnalysisFunction *fcn = Core()->functionIn(offset);

View File

@ -45,6 +45,7 @@ private slots:
void on_actionAddComment_triggered();
void on_actionAnalyzeFunction_triggered();
void on_actionRename_triggered();
void on_actionGlobalVar_triggered();
void on_actionSetFunctionVarTypes_triggered();
void on_actionXRefs_triggered();
void on_actionXRefsForVariables_triggered();
@ -78,6 +79,7 @@ private:
QKeySequence getCopySequence() const;
QKeySequence getCommentSequence() const;
QKeySequence getCopyAddressSequence() const;
QKeySequence getGlobalVarSequence() const;
QKeySequence getSetToCodeSequence() const;
QKeySequence getSetAsStringSequence() const;
QKeySequence getSetAsStringAdvanced() const;
@ -118,6 +120,7 @@ private:
QAction actionXRefs;
QAction actionXRefsForVariables;
QAction actionDisplayOptions;
QAction actionGlobalVar;
QAction actionDeleteComment;
QAction actionDeleteFlag;
@ -190,6 +193,7 @@ private:
void addSetAsMenu();
void addSetToDataMenu();
void addEditMenu();
void addAddAtMenu();
void addBreakpointMenu();
void addDebugMenu();

View File

@ -90,6 +90,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main)
connect(Core(), &CutterCore::varsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::flagsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::globalVarsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::commentsChanged, this, &DecompilerWidget::refreshIfChanged);
connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::refreshIfChanged);
connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doRefresh);

View File

@ -52,6 +52,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
connect(Core(), &CutterCore::commentsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::flagsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::globalVarsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::varsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::instructionChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::breakpointsChanged, this, &DisassemblerGraphView::refreshView);

View File

@ -128,6 +128,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main)
connect(Core(), &CutterCore::commentsChanged, this, [this]() { refreshDisasm(); });
connect(Core(), SIGNAL(flagsChanged()), this, SLOT(refreshDisasm()));
connect(Core(), SIGNAL(globalVarsChanged()), this, SLOT(refreshDisasm()));
connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshDisasm()));
connect(Core(), &CutterCore::functionRenamed, this, [this]() { refreshDisasm(); });
connect(Core(), SIGNAL(varsChanged()), this, SLOT(refreshDisasm()));

View File

@ -0,0 +1,228 @@
#include "GlobalsWidget.h"
#include "ui_GlobalsWidget.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
#include "dialogs/GlobalVariableDialog.h"
#include <QMenu>
#include <QShortcut>
GlobalsModel::GlobalsModel(QList<GlobalDescription> *globals, QObject *parent)
: AddressableItemModel<QAbstractListModel>(parent), globals(globals)
{
}
int GlobalsModel::rowCount(const QModelIndex &) const
{
return globals->count();
}
int GlobalsModel::columnCount(const QModelIndex &) const
{
return GlobalsModel::ColumnCount;
}
QVariant GlobalsModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= globals->count()) {
return QVariant();
}
const GlobalDescription &global = globals->at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case GlobalsModel::AddressColumn:
return RzAddressString(global.addr);
case GlobalsModel::TypeColumn:
return QString(global.type).trimmed();
case GlobalsModel::NameColumn:
return global.name;
case GlobalsModel::CommentColumn:
return Core()->getCommentAt(global.addr);
default:
return QVariant();
}
case GlobalsModel::GlobalDescriptionRole:
return QVariant::fromValue(global);
default:
return QVariant();
}
}
QVariant GlobalsModel::headerData(int section, Qt::Orientation, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (section) {
case GlobalsModel::AddressColumn:
return tr("Address");
case GlobalsModel::TypeColumn:
return tr("Type");
case GlobalsModel::NameColumn:
return tr("Name");
case GlobalsModel::CommentColumn:
return tr("Comment");
default:
return QVariant();
}
default:
return QVariant();
}
}
RVA GlobalsModel::address(const QModelIndex &index) const
{
const GlobalDescription &global = globals->at(index.row());
return global.addr;
}
QString GlobalsModel::name(const QModelIndex &index) const
{
const GlobalDescription &global = globals->at(index.row());
return global.name;
}
GlobalsProxyModel::GlobalsProxyModel(GlobalsModel *sourceModel, QObject *parent)
: AddressableFilterProxyModel(sourceModel, parent)
{
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
bool GlobalsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
QModelIndex index = sourceModel()->index(row, 0, parent);
auto global = index.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();
return qhelpers::filterStringContains(global.name, this);
}
bool GlobalsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
auto leftGlobal = left.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();
auto rightGlobal = right.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();
switch (left.column()) {
case GlobalsModel::AddressColumn:
return leftGlobal.addr < rightGlobal.addr;
case GlobalsModel::TypeColumn:
return leftGlobal.type < rightGlobal.type;
case GlobalsModel::NameColumn:
return leftGlobal.name < rightGlobal.name;
case GlobalsModel::CommentColumn:
return Core()->getCommentAt(leftGlobal.addr) < Core()->getCommentAt(rightGlobal.addr);
default:
break;
}
return false;
}
void GlobalsWidget::editGlobal()
{
QModelIndex index = ui->treeView->currentIndex();
if (!index.isValid()) {
return;
}
RVA globalVariableAddress = globalsModel->address(index);
GlobalVariableDialog dialog(globalVariableAddress, parentWidget());
dialog.exec();
}
void GlobalsWidget::deleteGlobal()
{
QModelIndex index = ui->treeView->currentIndex();
if (!index.isValid()) {
return;
}
RVA globalVariableAddress = globalsModel->address(index);
Core()->delGlobalVariable(globalVariableAddress);
}
void GlobalsWidget::showGlobalsContextMenu(const QPoint &pt)
{
QModelIndex index = ui->treeView->indexAt(pt);
QMenu menu(ui->treeView);
if (index.isValid()) {
menu.addAction(actionEditGlobal);
menu.addAction(actionDeleteGlobal);
}
menu.exec(ui->treeView->mapToGlobal(pt));
}
GlobalsWidget::GlobalsWidget(MainWindow *main)
: CutterDockWidget(main), ui(new Ui::GlobalsWidget), tree(new CutterTreeWidget(this))
{
ui->setupUi(this);
ui->quickFilterView->setLabelText(tr("Category"));
setWindowTitle(tr("Globals"));
setObjectName("GlobalsWidget");
// Add status bar which displays the count
tree->addStatusBar(ui->verticalLayout);
// Set single select mode
ui->treeView->setSelectionMode(QAbstractItemView::SingleSelection);
// Setup up the model and the proxy model
globalsModel = new GlobalsModel(&globals, this);
globalsProxyModel = new GlobalsProxyModel(globalsModel, this);
ui->treeView->setModel(globalsProxyModel);
ui->treeView->sortByColumn(GlobalsModel::AddressColumn, Qt::AscendingOrder);
// Setup custom context menu
connect(ui->treeView, &QWidget::customContextMenuRequested, this,
&GlobalsWidget::showGlobalsContextMenu);
ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, globalsProxyModel,
&QSortFilterProxyModel::setFilterWildcard);
connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, this,
[this] { tree->showItemsNumber(globalsProxyModel->rowCount()); });
QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this);
connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,
&ComboQuickFilterView::showFilter);
searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clearShortcut, &QShortcut::activated, ui->quickFilterView,
&ComboQuickFilterView::clearFilter);
clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);
actionEditGlobal = new QAction(tr("Edit Global Variable"), this);
actionDeleteGlobal = new QAction(tr("Delete Global Variable"), this);
connect(actionEditGlobal, &QAction::triggered, [this]() { editGlobal(); });
connect(actionDeleteGlobal, &QAction::triggered, [this]() { deleteGlobal(); });
connect(Core(), &CutterCore::globalVarsChanged, this, &GlobalsWidget::refreshGlobals);
connect(Core(), &CutterCore::codeRebased, this, &GlobalsWidget::refreshGlobals);
connect(Core(), &CutterCore::refreshAll, this, &GlobalsWidget::refreshGlobals);
connect(Core(), &CutterCore::commentsChanged, this,
[this]() { qhelpers::emitColumnChanged(globalsModel, GlobalsModel::CommentColumn); });
}
GlobalsWidget::~GlobalsWidget() {}
void GlobalsWidget::refreshGlobals()
{
globalsModel->beginResetModel();
globals = Core()->getAllGlobals();
globalsModel->endResetModel();
qhelpers::adjustColumns(ui->treeView, GlobalsModel::ColumnCount, 0);
}

View File

@ -0,0 +1,89 @@
#ifndef GLOBALSWIDGET_H
#define GLOBALSWIDGET_H
#include <memory>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "widgets/ListDockWidget.h"
class MainWindow;
class QTreeWidget;
class GlobalsWidget;
namespace Ui {
class GlobalsWidget;
}
class MainWindow;
class QTreeWidgetItem;
class GlobalsModel : public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
friend GlobalsWidget;
private:
QList<GlobalDescription> *globals;
public:
enum Column { AddressColumn = 0, TypeColumn, NameColumn, CommentColumn, ColumnCount };
enum Role { GlobalDescriptionRole = Qt::UserRole };
GlobalsModel(QList<GlobalDescription> *exports, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
};
class GlobalsProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
public:
GlobalsProxyModel(GlobalsModel *sourceModel, QObject *parent = nullptr);
protected:
bool filterAcceptsRow(int row, const QModelIndex &parent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};
class GlobalsWidget : public CutterDockWidget
{
Q_OBJECT
public:
explicit GlobalsWidget(MainWindow *main);
~GlobalsWidget();
private slots:
void refreshGlobals();
void showGlobalsContextMenu(const QPoint &pt);
void editGlobal();
void deleteGlobal();
private:
std::unique_ptr<Ui::GlobalsWidget> ui;
QList<GlobalDescription> globals;
GlobalsModel *globalsModel;
GlobalsProxyModel *globalsProxyModel;
CutterTreeWidget *tree;
QAction *actionEditGlobal;
QAction *actionDeleteGlobal;
};
#endif // GLOBALSWIDGET_H

View File

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GlobalsWidget</class>
<widget class="QDockWidget" name="GlobalsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Global Variables</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="treeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ComboQuickFilterView" name="quickFilterView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<action name="actionEditGlobal">
<property name="text">
<string>Edit Global Variable</string>
</property>
<property name="toolTip">
<string>Edit Global Variable</string>
</property>
</action>
<action name="actionDeleteGlobal">
<property name="text">
<string>Delete Global Variable</string>
</property>
<property name="toolTip">
<string>Delete Global Variable</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ComboQuickFilterView</class>
<extends>QWidget</extends>
<header>widgets/ComboQuickFilterView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -50,6 +50,7 @@ VisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent)
connect(Core(), &CutterCore::refreshAll, this, &VisualNavbar::fetchAndPaintData);
connect(Core(), &CutterCore::functionsChanged, this, &VisualNavbar::fetchAndPaintData);
connect(Core(), &CutterCore::flagsChanged, this, &VisualNavbar::fetchAndPaintData);
connect(Core(), &CutterCore::globalVarsChanged, this, &VisualNavbar::fetchAndPaintData);
graphicsScene = new QGraphicsScene(this);