mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-18 10:56:11 +00:00
Add signature widget for flirts (#2881)
* Added flirt view and removed zignature one * Added menu for apply signature from file and create new sig/pat files * Updated rizin to dev branch
This commit is contained in:
parent
75c334596e
commit
46d29c1e85
@ -89,8 +89,8 @@ Show VTables
|
|||||||
|
|
||||||
**Steps:** Windows -> Info... -> VTables
|
**Steps:** Windows -> Info... -> VTables
|
||||||
|
|
||||||
Show Zignatures
|
Show Signatures
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
**Description:** Cutter has its own format of signatures, called Zignatures. This widget lists all the loaded Zignatures.
|
**Description:** Cutter supports the creation and the utilization of signatures. This widget lists all the signatures available to cutter.
|
||||||
|
|
||||||
**Steps:** Windows -> Info... -> Zignatures
|
**Steps:** Windows -> Info... -> Signatures
|
2
rizin
2
rizin
@ -1 +1 @@
|
|||||||
Subproject commit 0e87fbd8107dfe463924b498c84687cfc5fe2b7f
|
Subproject commit 789a283dd565dadebd17ab33cbfb5ec06d87323b
|
@ -42,6 +42,7 @@ set(SOURCES
|
|||||||
widgets/SymbolsWidget.cpp
|
widgets/SymbolsWidget.cpp
|
||||||
menus/DisassemblyContextMenu.cpp
|
menus/DisassemblyContextMenu.cpp
|
||||||
menus/DecompilerContextMenu.cpp
|
menus/DecompilerContextMenu.cpp
|
||||||
|
menus/FlirtContextMenu.cpp
|
||||||
widgets/DisassemblyWidget.cpp
|
widgets/DisassemblyWidget.cpp
|
||||||
widgets/HexdumpWidget.cpp
|
widgets/HexdumpWidget.cpp
|
||||||
common/Configuration.cpp
|
common/Configuration.cpp
|
||||||
@ -73,7 +74,7 @@ set(SOURCES
|
|||||||
common/JsonTreeItem.cpp
|
common/JsonTreeItem.cpp
|
||||||
common/JsonModel.cpp
|
common/JsonModel.cpp
|
||||||
dialogs/VersionInfoDialog.cpp
|
dialogs/VersionInfoDialog.cpp
|
||||||
widgets/ZignaturesWidget.cpp
|
widgets/FlirtWidget.cpp
|
||||||
common/AsyncTask.cpp
|
common/AsyncTask.cpp
|
||||||
dialogs/AsyncTaskDialog.cpp
|
dialogs/AsyncTaskDialog.cpp
|
||||||
widgets/StackWidget.cpp
|
widgets/StackWidget.cpp
|
||||||
@ -190,6 +191,7 @@ set(HEADER_FILES
|
|||||||
widgets/SymbolsWidget.h
|
widgets/SymbolsWidget.h
|
||||||
menus/DisassemblyContextMenu.h
|
menus/DisassemblyContextMenu.h
|
||||||
menus/DecompilerContextMenu.h
|
menus/DecompilerContextMenu.h
|
||||||
|
menus/FlirtContextMenu.h
|
||||||
widgets/DisassemblyWidget.h
|
widgets/DisassemblyWidget.h
|
||||||
widgets/HexdumpWidget.h
|
widgets/HexdumpWidget.h
|
||||||
common/Configuration.h
|
common/Configuration.h
|
||||||
@ -221,7 +223,7 @@ set(HEADER_FILES
|
|||||||
common/JsonTreeItem.h
|
common/JsonTreeItem.h
|
||||||
common/JsonModel.h
|
common/JsonModel.h
|
||||||
dialogs/VersionInfoDialog.h
|
dialogs/VersionInfoDialog.h
|
||||||
widgets/ZignaturesWidget.h
|
widgets/FlirtWidget.h
|
||||||
common/AsyncTask.h
|
common/AsyncTask.h
|
||||||
dialogs/AsyncTaskDialog.h
|
dialogs/AsyncTaskDialog.h
|
||||||
widgets/StackWidget.h
|
widgets/StackWidget.h
|
||||||
@ -341,7 +343,7 @@ set(UI_FILES
|
|||||||
widgets/SearchWidget.ui
|
widgets/SearchWidget.ui
|
||||||
dialogs/RizinPluginsDialog.ui
|
dialogs/RizinPluginsDialog.ui
|
||||||
dialogs/VersionInfoDialog.ui
|
dialogs/VersionInfoDialog.ui
|
||||||
widgets/ZignaturesWidget.ui
|
widgets/FlirtWidget.ui
|
||||||
dialogs/AsyncTaskDialog.ui
|
dialogs/AsyncTaskDialog.ui
|
||||||
dialogs/RizinTaskDialog.ui
|
dialogs/RizinTaskDialog.ui
|
||||||
widgets/StackWidget.ui
|
widgets/StackWidget.ui
|
||||||
|
@ -1016,6 +1016,40 @@ RVA CutterCore::getOffset()
|
|||||||
return core_->offset;
|
return core_->offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CutterCore::applySignature(const QString &filepath)
|
||||||
|
{
|
||||||
|
CORE_LOCK();
|
||||||
|
int old_cnt, new_cnt;
|
||||||
|
const char *arch = rz_config_get(core->config, "asm.arch");
|
||||||
|
ut8 expected_arch = rz_core_flirt_arch_from_name(arch);
|
||||||
|
if (expected_arch == RZ_FLIRT_SIG_ARCH_ANY && filepath.endsWith(".sig", Qt::CaseInsensitive)) {
|
||||||
|
QMessageBox::warning(
|
||||||
|
nullptr, tr("Signatures"),
|
||||||
|
tr("Cannot apply signature because the requested arch is not supported by .sig "
|
||||||
|
"files\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
old_cnt = rz_flag_count(core->flags, "flirt");
|
||||||
|
rz_sign_flirt_apply(core->analysis, filepath.toStdString().c_str(), expected_arch);
|
||||||
|
new_cnt = rz_flag_count(core->flags, "flirt");
|
||||||
|
QMessageBox::information(nullptr, tr("Signatures"),
|
||||||
|
tr("Found %1 signatures!").arg(new_cnt - old_cnt));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CutterCore::createSignature(const QString &filepath)
|
||||||
|
{
|
||||||
|
CORE_LOCK();
|
||||||
|
ut32 n_modules = 0;
|
||||||
|
if (!rz_core_flirt_create_file(core, filepath.toStdString().c_str(), &n_modules)) {
|
||||||
|
QMessageBox::warning(
|
||||||
|
nullptr, tr("Signatures"),
|
||||||
|
tr("Cannot create signature file (check the console for more details).\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QMessageBox::information(nullptr, tr("Signatures"),
|
||||||
|
tr("Written %1 signatures to %2").arg(n_modules).arg(filepath));
|
||||||
|
}
|
||||||
|
|
||||||
ut64 CutterCore::math(const QString &expr)
|
ut64 CutterCore::math(const QString &expr)
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
@ -2967,35 +3001,34 @@ QList<HeaderDescription> CutterCore::getAllHeaders()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ZignatureDescription> CutterCore::getAllZignatures()
|
QList<FlirtDescription> CutterCore::getSignaturesDB()
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
QList<ZignatureDescription> zignatures;
|
QList<FlirtDescription> sigdb;
|
||||||
|
const char *sigdb_path = rz_config_get(core->config, "flirt.sigdb.path");
|
||||||
QJsonArray zignaturesArray = cmdj("zj").array();
|
if (RZ_STR_ISEMPTY(sigdb_path)) {
|
||||||
|
return sigdb;
|
||||||
for (const QJsonValue &value : zignaturesArray) {
|
|
||||||
QJsonObject zignatureObject = value.toObject();
|
|
||||||
|
|
||||||
ZignatureDescription zignature;
|
|
||||||
|
|
||||||
zignature.name = zignatureObject[RJsonKey::name].toString();
|
|
||||||
zignature.bytes = zignatureObject[RJsonKey::bytes].toString();
|
|
||||||
zignature.offset = zignatureObject[RJsonKey::offset].toVariant().toULongLong();
|
|
||||||
for (const QJsonValue &ref : zignatureObject[RJsonKey::refs].toArray()) {
|
|
||||||
zignature.refs << ref.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject graphObject = zignatureObject[RJsonKey::graph].toObject();
|
|
||||||
zignature.cc = graphObject[RJsonKey::cc].toVariant().toULongLong();
|
|
||||||
zignature.nbbs = graphObject[RJsonKey::nbbs].toVariant().toULongLong();
|
|
||||||
zignature.edges = graphObject[RJsonKey::edges].toVariant().toULongLong();
|
|
||||||
zignature.ebbs = graphObject[RJsonKey::ebbs].toVariant().toULongLong();
|
|
||||||
|
|
||||||
zignatures << zignature;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return zignatures;
|
RzList *list = rz_sign_sigdb_load_database(sigdb_path, true);
|
||||||
|
void *ptr = NULL;
|
||||||
|
RzListIter *iter = NULL;
|
||||||
|
|
||||||
|
rz_list_foreach(list, iter, ptr)
|
||||||
|
{
|
||||||
|
RzSigDBEntry *sig = static_cast<RzSigDBEntry *>(ptr);
|
||||||
|
FlirtDescription flirt;
|
||||||
|
flirt.bin_name = sig->bin_name;
|
||||||
|
flirt.arch_name = sig->arch_name;
|
||||||
|
flirt.base_name = sig->base_name;
|
||||||
|
flirt.short_path = sig->short_path;
|
||||||
|
flirt.file_path = sig->file_path;
|
||||||
|
flirt.details = sig->details;
|
||||||
|
flirt.n_modules = QString::number(sig->n_modules);
|
||||||
|
flirt.arch_bits = QString::number(sig->arch_bits);
|
||||||
|
sigdb << flirt;
|
||||||
|
}
|
||||||
|
return sigdb;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<CommentDescription> CutterCore::getAllComments(const QString &filterType)
|
QList<CommentDescription> CutterCore::getAllComments(const QString &filterType)
|
||||||
|
@ -316,6 +316,10 @@ public:
|
|||||||
RVA prevOpAddr(RVA startAddr, int count);
|
RVA prevOpAddr(RVA startAddr, int count);
|
||||||
RVA nextOpAddr(RVA startAddr, int count);
|
RVA nextOpAddr(RVA startAddr, int count);
|
||||||
|
|
||||||
|
/* SigDB / Flirt functions */
|
||||||
|
void applySignature(const QString &filepath);
|
||||||
|
void createSignature(const QString &filepath);
|
||||||
|
|
||||||
/* Math functions */
|
/* Math functions */
|
||||||
ut64 math(const QString &expr);
|
ut64 math(const QString &expr);
|
||||||
ut64 num(const QString &expr);
|
ut64 num(const QString &expr);
|
||||||
@ -553,7 +557,7 @@ public:
|
|||||||
QList<ExportDescription> getAllExports();
|
QList<ExportDescription> getAllExports();
|
||||||
QList<SymbolDescription> getAllSymbols();
|
QList<SymbolDescription> getAllSymbols();
|
||||||
QList<HeaderDescription> getAllHeaders();
|
QList<HeaderDescription> getAllHeaders();
|
||||||
QList<ZignatureDescription> getAllZignatures();
|
QList<FlirtDescription> getSignaturesDB();
|
||||||
QList<CommentDescription> getAllComments(const QString &filterType);
|
QList<CommentDescription> getAllComments(const QString &filterType);
|
||||||
QList<RelocDescription> getAllRelocs();
|
QList<RelocDescription> getAllRelocs();
|
||||||
QList<StringDescription> getAllStrings();
|
QList<StringDescription> getAllStrings();
|
||||||
|
@ -60,16 +60,16 @@ struct HeaderDescription
|
|||||||
QString name;
|
QString name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ZignatureDescription
|
struct FlirtDescription
|
||||||
{
|
{
|
||||||
QString name;
|
QString bin_name;
|
||||||
QString bytes;
|
QString arch_name;
|
||||||
RVA cc;
|
QString arch_bits;
|
||||||
RVA nbbs;
|
QString base_name;
|
||||||
RVA edges;
|
QString short_path;
|
||||||
RVA ebbs;
|
QString file_path;
|
||||||
RVA offset;
|
QString details;
|
||||||
QStringList refs;
|
QString n_modules;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeDescription
|
struct TypeDescription
|
||||||
@ -413,7 +413,7 @@ Q_DECLARE_METATYPE(ResourcesDescription)
|
|||||||
Q_DECLARE_METATYPE(VTableDescription)
|
Q_DECLARE_METATYPE(VTableDescription)
|
||||||
Q_DECLARE_METATYPE(TypeDescription)
|
Q_DECLARE_METATYPE(TypeDescription)
|
||||||
Q_DECLARE_METATYPE(HeaderDescription)
|
Q_DECLARE_METATYPE(HeaderDescription)
|
||||||
Q_DECLARE_METATYPE(ZignatureDescription)
|
Q_DECLARE_METATYPE(FlirtDescription)
|
||||||
Q_DECLARE_METATYPE(SearchDescription)
|
Q_DECLARE_METATYPE(SearchDescription)
|
||||||
Q_DECLARE_METATYPE(SectionDescription)
|
Q_DECLARE_METATYPE(SectionDescription)
|
||||||
Q_DECLARE_METATYPE(SegmentDescription)
|
Q_DECLARE_METATYPE(SegmentDescription)
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
#include "widgets/ResourcesWidget.h"
|
#include "widgets/ResourcesWidget.h"
|
||||||
#include "widgets/VTablesWidget.h"
|
#include "widgets/VTablesWidget.h"
|
||||||
#include "widgets/HeadersWidget.h"
|
#include "widgets/HeadersWidget.h"
|
||||||
#include "widgets/ZignaturesWidget.h"
|
#include "widgets/FlirtWidget.h"
|
||||||
#include "widgets/DebugActions.h"
|
#include "widgets/DebugActions.h"
|
||||||
#include "widgets/MemoryMapWidget.h"
|
#include "widgets/MemoryMapWidget.h"
|
||||||
#include "widgets/BreakpointWidget.h"
|
#include "widgets/BreakpointWidget.h"
|
||||||
@ -399,7 +399,7 @@ void MainWindow::initDocks()
|
|||||||
segmentsDock = new SegmentsWidget(this),
|
segmentsDock = new SegmentsWidget(this),
|
||||||
symbolsDock = new SymbolsWidget(this),
|
symbolsDock = new SymbolsWidget(this),
|
||||||
vTablesDock = new VTablesWidget(this),
|
vTablesDock = new VTablesWidget(this),
|
||||||
zignaturesDock = new ZignaturesWidget(this),
|
flirtDock = new FlirtWidget(this),
|
||||||
rzGraphDock = new RizinGraphWidget(this),
|
rzGraphDock = new RizinGraphWidget(this),
|
||||||
callGraphDock = new CallGraphWidget(this, false),
|
callGraphDock = new CallGraphWidget(this, false),
|
||||||
globalCallGraphDock = new CallGraphWidget(this, true),
|
globalCallGraphDock = new CallGraphWidget(this, true),
|
||||||
@ -896,7 +896,7 @@ void MainWindow::restoreDocks()
|
|||||||
tabifyDockWidget(dashboardDock, typesDock);
|
tabifyDockWidget(dashboardDock, typesDock);
|
||||||
tabifyDockWidget(dashboardDock, searchDock);
|
tabifyDockWidget(dashboardDock, searchDock);
|
||||||
tabifyDockWidget(dashboardDock, headersDock);
|
tabifyDockWidget(dashboardDock, headersDock);
|
||||||
tabifyDockWidget(dashboardDock, zignaturesDock);
|
tabifyDockWidget(dashboardDock, flirtDock);
|
||||||
tabifyDockWidget(dashboardDock, symbolsDock);
|
tabifyDockWidget(dashboardDock, symbolsDock);
|
||||||
tabifyDockWidget(dashboardDock, classesDock);
|
tabifyDockWidget(dashboardDock, classesDock);
|
||||||
tabifyDockWidget(dashboardDock, resourcesDock);
|
tabifyDockWidget(dashboardDock, resourcesDock);
|
||||||
@ -1741,6 +1741,50 @@ void MainWindow::on_actionExport_as_code_triggered()
|
|||||||
fileOut << Core()->cmd(cmd + " $s @ 0");
|
fileOut << Core()->cmd(cmd + " $s @ 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionApplySigFromFile_triggered()
|
||||||
|
{
|
||||||
|
QStringList filters;
|
||||||
|
filters << tr("Signature File (*.sig)");
|
||||||
|
filters << tr("Pattern File (*.pat)");
|
||||||
|
|
||||||
|
QFileDialog dialog(this);
|
||||||
|
dialog.setWindowTitle(tr("Apply Signature From File"));
|
||||||
|
dialog.setNameFilters(filters);
|
||||||
|
|
||||||
|
if (!dialog.exec()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &sigfile = QDir::toNativeSeparators(dialog.selectedFiles().first());
|
||||||
|
|
||||||
|
if (!sigfile.isEmpty()) {
|
||||||
|
core->applySignature(sigfile);
|
||||||
|
this->refreshAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionCreateNewSig_triggered()
|
||||||
|
{
|
||||||
|
QStringList filters;
|
||||||
|
filters << tr("Signature File (*.sig)");
|
||||||
|
filters << tr("Pattern File (*.pat)");
|
||||||
|
|
||||||
|
QFileDialog dialog(this, tr("Create New Signature File"));
|
||||||
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||||
|
dialog.setFileMode(QFileDialog::AnyFile);
|
||||||
|
dialog.setNameFilters(filters);
|
||||||
|
dialog.selectFile("");
|
||||||
|
dialog.setDefaultSuffix("sig");
|
||||||
|
if (!dialog.exec())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QString &sigfile = QDir::toNativeSeparators(dialog.selectedFiles().first());
|
||||||
|
|
||||||
|
if (!sigfile.isEmpty()) {
|
||||||
|
core->createSignature(sigfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionGrouped_dock_dragging_triggered(bool checked)
|
void MainWindow::on_actionGrouped_dock_dragging_triggered(bool checked)
|
||||||
{
|
{
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
|
||||||
|
@ -44,7 +44,7 @@ class ResourcesWidget;
|
|||||||
class VTablesWidget;
|
class VTablesWidget;
|
||||||
class TypesWidget;
|
class TypesWidget;
|
||||||
class HeadersWidget;
|
class HeadersWidget;
|
||||||
class ZignaturesWidget;
|
class FlirtWidget;
|
||||||
class SearchWidget;
|
class SearchWidget;
|
||||||
class QDockWidget;
|
class QDockWidget;
|
||||||
class DisassemblyWidget;
|
class DisassemblyWidget;
|
||||||
@ -190,6 +190,10 @@ private slots:
|
|||||||
|
|
||||||
void on_actionExport_as_code_triggered();
|
void on_actionExport_as_code_triggered();
|
||||||
|
|
||||||
|
void on_actionApplySigFromFile_triggered();
|
||||||
|
|
||||||
|
void on_actionCreateNewSig_triggered();
|
||||||
|
|
||||||
void on_actionGrouped_dock_dragging_triggered(bool checked);
|
void on_actionGrouped_dock_dragging_triggered(bool checked);
|
||||||
|
|
||||||
void updateTasksIndicator();
|
void updateTasksIndicator();
|
||||||
@ -243,7 +247,7 @@ private:
|
|||||||
SdbWidget *sdbDock = nullptr;
|
SdbWidget *sdbDock = nullptr;
|
||||||
SectionsWidget *sectionsDock = nullptr;
|
SectionsWidget *sectionsDock = nullptr;
|
||||||
SegmentsWidget *segmentsDock = nullptr;
|
SegmentsWidget *segmentsDock = nullptr;
|
||||||
ZignaturesWidget *zignaturesDock = nullptr;
|
FlirtWidget *flirtDock = nullptr;
|
||||||
ConsoleWidget *consoleDock = nullptr;
|
ConsoleWidget *consoleDock = nullptr;
|
||||||
ClassesWidget *classesDock = nullptr;
|
ClassesWidget *classesDock = nullptr;
|
||||||
ResourcesWidget *resourcesDock = nullptr;
|
ResourcesWidget *resourcesDock = nullptr;
|
||||||
|
@ -77,6 +77,9 @@
|
|||||||
<addaction name="actionImportPDB"/>
|
<addaction name="actionImportPDB"/>
|
||||||
<addaction name="actionAnalyze"/>
|
<addaction name="actionAnalyze"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionApplySigFromFile"/>
|
||||||
|
<addaction name="actionCreateNewSig"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
<addaction name="menuSetMode"/>
|
<addaction name="menuSetMode"/>
|
||||||
<addaction name="actionCommitChanges"/>
|
<addaction name="actionCommitChanges"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
@ -740,6 +743,16 @@
|
|||||||
<string>Export as code</string>
|
<string>Export as code</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionApplySigFromFile">
|
||||||
|
<property name="text">
|
||||||
|
<string>Apply Signature From File</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionCreateNewSig">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create New Signature File</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionExtraHexdump">
|
<action name="actionExtraHexdump">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Add Hexdump</string>
|
<string>Add Hexdump</string>
|
||||||
|
62
src/menus/FlirtContextMenu.cpp
Normal file
62
src/menus/FlirtContextMenu.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "FlirtContextMenu.h"
|
||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
FlirtContextMenu::FlirtContextMenu(QWidget *parent, MainWindow *mainWindow)
|
||||||
|
: QMenu(parent), mainWindow(mainWindow)
|
||||||
|
{
|
||||||
|
actionCopyLine = new QAction(tr("Copy Line"), this);
|
||||||
|
actionApplySignature = new QAction(tr("Apply Signature File"), this);
|
||||||
|
|
||||||
|
connect(actionCopyLine, &QAction::triggered, this, &FlirtContextMenu::onActionCopyLine);
|
||||||
|
connect(actionApplySignature, &QAction::triggered, this,
|
||||||
|
&FlirtContextMenu::onActionApplySignature);
|
||||||
|
|
||||||
|
addAction(actionCopyLine);
|
||||||
|
addSeparator();
|
||||||
|
addAction(actionApplySignature);
|
||||||
|
|
||||||
|
setHasTarget(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlirtContextMenu::~FlirtContextMenu() {}
|
||||||
|
|
||||||
|
void FlirtContextMenu::setTarget(const FlirtDescription &flirt)
|
||||||
|
{
|
||||||
|
this->entry = flirt;
|
||||||
|
setHasTarget(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlirtContextMenu::clearTarget()
|
||||||
|
{
|
||||||
|
setHasTarget(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlirtContextMenu::onActionCopyLine()
|
||||||
|
{
|
||||||
|
auto clipboard = QApplication::clipboard();
|
||||||
|
QString text = entry.bin_name + "\t" + entry.arch_name + "\t" + entry.arch_bits + "\t"
|
||||||
|
+ entry.n_modules + "\t" + entry.base_name + "\t" + entry.details;
|
||||||
|
clipboard->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlirtContextMenu::onActionApplySignature()
|
||||||
|
{
|
||||||
|
if (this->hasTarget) {
|
||||||
|
Core()->applySignature(entry.file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlirtContextMenu::setHasTarget(bool hasTarget)
|
||||||
|
{
|
||||||
|
this->hasTarget = hasTarget;
|
||||||
|
for (const auto &action : this->actions()) {
|
||||||
|
action->setEnabled(hasTarget);
|
||||||
|
}
|
||||||
|
}
|
40
src/menus/FlirtContextMenu.h
Normal file
40
src/menus/FlirtContextMenu.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef FLIRT_CONTEXTMENU_H
|
||||||
|
#define FLIRT_CONTEXTMENU_H
|
||||||
|
|
||||||
|
#include "core/Cutter.h"
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QKeySequence>
|
||||||
|
|
||||||
|
class MainWindow;
|
||||||
|
|
||||||
|
class CUTTER_EXPORT FlirtContextMenu : public QMenu
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FlirtContextMenu(QWidget *parent, MainWindow *mainWindow);
|
||||||
|
~FlirtContextMenu();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setTarget(const FlirtDescription &flirt);
|
||||||
|
void clearTarget();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onActionCopyLine();
|
||||||
|
void onActionApplySignature();
|
||||||
|
|
||||||
|
QMenu *pluginMenu;
|
||||||
|
QAction *pluginMenuAction;
|
||||||
|
MainWindow *mainWindow;
|
||||||
|
|
||||||
|
bool hasTarget = false;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setHasTarget(bool hasTarget);
|
||||||
|
QAction *actionApplySignature;
|
||||||
|
QAction *actionCopyLine;
|
||||||
|
QAction *actionShowContents;
|
||||||
|
|
||||||
|
FlirtDescription entry;
|
||||||
|
};
|
||||||
|
#endif // FlirtCONTEXTMENU_H
|
188
src/widgets/FlirtWidget.cpp
Normal file
188
src/widgets/FlirtWidget.cpp
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
#include "FlirtWidget.h"
|
||||||
|
#include "ui_FlirtWidget.h"
|
||||||
|
#include "core/MainWindow.h"
|
||||||
|
#include "common/Helpers.h"
|
||||||
|
|
||||||
|
FlirtModel::FlirtModel(QList<FlirtDescription> *sigdb, QObject *parent)
|
||||||
|
: QAbstractListModel(parent), sigdb(sigdb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlirtModel::rowCount(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return sigdb->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlirtModel::columnCount(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return FlirtModel::ColumnCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant FlirtModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (index.row() >= sigdb->count())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
const FlirtDescription &entry = sigdb->at(index.row());
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (index.column()) {
|
||||||
|
case BinTypeColumn:
|
||||||
|
return entry.bin_name;
|
||||||
|
case ArchNameColumn:
|
||||||
|
return entry.arch_name;
|
||||||
|
case ArchBitsColumn:
|
||||||
|
return entry.arch_bits;
|
||||||
|
case NumModulesColumn:
|
||||||
|
return entry.n_modules;
|
||||||
|
case NameColumn:
|
||||||
|
return entry.base_name;
|
||||||
|
case DetailsColumn:
|
||||||
|
return entry.details;
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
case FlirtDescriptionRole:
|
||||||
|
return QVariant::fromValue(entry);
|
||||||
|
|
||||||
|
case Qt::ToolTipRole: {
|
||||||
|
return entry.short_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant FlirtModel::headerData(int section, Qt::Orientation, int role) const
|
||||||
|
{
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (section) {
|
||||||
|
case BinTypeColumn:
|
||||||
|
return tr("Bin");
|
||||||
|
case ArchNameColumn:
|
||||||
|
return tr("Arch");
|
||||||
|
case ArchBitsColumn:
|
||||||
|
return tr("Bits");
|
||||||
|
case NumModulesColumn:
|
||||||
|
return tr("# Funcs");
|
||||||
|
case NameColumn:
|
||||||
|
return tr("Name");
|
||||||
|
case DetailsColumn:
|
||||||
|
return tr("Details");
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlirtProxyModel::FlirtProxyModel(FlirtModel *sourceModel, QObject *parent)
|
||||||
|
: QSortFilterProxyModel(parent)
|
||||||
|
{
|
||||||
|
setSourceModel(sourceModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlirtProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
QModelIndex index = sourceModel()->index(row, 0, parent);
|
||||||
|
FlirtDescription entry = index.data(FlirtModel::FlirtDescriptionRole).value<FlirtDescription>();
|
||||||
|
return qhelpers::filterStringContains(entry.base_name, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlirtProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||||
|
{
|
||||||
|
FlirtDescription leftEntry =
|
||||||
|
left.data(FlirtModel::FlirtDescriptionRole).value<FlirtDescription>();
|
||||||
|
FlirtDescription rightEntry =
|
||||||
|
right.data(FlirtModel::FlirtDescriptionRole).value<FlirtDescription>();
|
||||||
|
|
||||||
|
switch (left.column()) {
|
||||||
|
case FlirtModel::BinTypeColumn:
|
||||||
|
return leftEntry.bin_name < rightEntry.bin_name;
|
||||||
|
case FlirtModel::ArchNameColumn:
|
||||||
|
return leftEntry.arch_name < rightEntry.arch_name;
|
||||||
|
case FlirtModel::ArchBitsColumn:
|
||||||
|
return leftEntry.arch_bits < rightEntry.arch_bits;
|
||||||
|
case FlirtModel::NumModulesColumn:
|
||||||
|
return leftEntry.n_modules < rightEntry.n_modules;
|
||||||
|
case FlirtModel::NameColumn:
|
||||||
|
return leftEntry.base_name < rightEntry.base_name;
|
||||||
|
case FlirtModel::DetailsColumn:
|
||||||
|
return leftEntry.details < rightEntry.details;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftEntry.bin_name < rightEntry.bin_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlirtWidget::FlirtWidget(MainWindow *main)
|
||||||
|
: CutterDockWidget(main),
|
||||||
|
ui(new Ui::FlirtWidget),
|
||||||
|
blockMenu(new FlirtContextMenu(this, mainWindow))
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
model = new FlirtModel(&sigdb, this);
|
||||||
|
proxyModel = new FlirtProxyModel(model, this);
|
||||||
|
ui->flirtTreeView->setModel(proxyModel);
|
||||||
|
ui->flirtTreeView->sortByColumn(FlirtModel::BinTypeColumn, Qt::AscendingOrder);
|
||||||
|
|
||||||
|
setScrollMode();
|
||||||
|
|
||||||
|
this->connect(this, &QWidget::customContextMenuRequested, this,
|
||||||
|
&FlirtWidget::showItemContextMenu);
|
||||||
|
this->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
|
this->connect(ui->flirtTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
|
||||||
|
&FlirtWidget::onSelectedItemChanged);
|
||||||
|
connect(Core(), &CutterCore::refreshAll, this, &FlirtWidget::refreshFlirt);
|
||||||
|
|
||||||
|
this->addActions(this->blockMenu->actions());
|
||||||
|
}
|
||||||
|
|
||||||
|
FlirtWidget::~FlirtWidget() {}
|
||||||
|
|
||||||
|
void FlirtWidget::refreshFlirt()
|
||||||
|
{
|
||||||
|
model->beginResetModel();
|
||||||
|
sigdb = Core()->getSignaturesDB();
|
||||||
|
model->endResetModel();
|
||||||
|
|
||||||
|
ui->flirtTreeView->resizeColumnToContents(0);
|
||||||
|
ui->flirtTreeView->resizeColumnToContents(1);
|
||||||
|
ui->flirtTreeView->resizeColumnToContents(2);
|
||||||
|
ui->flirtTreeView->resizeColumnToContents(3);
|
||||||
|
ui->flirtTreeView->resizeColumnToContents(4);
|
||||||
|
ui->flirtTreeView->resizeColumnToContents(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlirtWidget::setScrollMode()
|
||||||
|
{
|
||||||
|
qhelpers::setVerticalScrollMode(ui->flirtTreeView);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlirtWidget::onSelectedItemChanged(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
if (index.isValid()) {
|
||||||
|
const FlirtDescription &entry = sigdb.at(index.row());
|
||||||
|
blockMenu->setTarget(entry);
|
||||||
|
} else {
|
||||||
|
blockMenu->clearTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlirtWidget::showItemContextMenu(const QPoint &pt)
|
||||||
|
{
|
||||||
|
auto index = ui->flirtTreeView->currentIndex();
|
||||||
|
if (index.isValid()) {
|
||||||
|
const FlirtDescription &entry = sigdb.at(index.row());
|
||||||
|
blockMenu->setTarget(entry);
|
||||||
|
blockMenu->exec(this->mapToGlobal(pt));
|
||||||
|
}
|
||||||
|
}
|
88
src/widgets/FlirtWidget.h
Normal file
88
src/widgets/FlirtWidget.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#ifndef FLIRT_WIDGET_H
|
||||||
|
#define FLIRT_WIDGET_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "core/Cutter.h"
|
||||||
|
#include "CutterDockWidget.h"
|
||||||
|
#include "menus/FlirtContextMenu.h"
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
class MainWindow;
|
||||||
|
class QTreeWidget;
|
||||||
|
class QTreeWidgetItem;
|
||||||
|
class FlirtWidget;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class FlirtWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlirtModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend FlirtWidget;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Column {
|
||||||
|
BinTypeColumn = 0,
|
||||||
|
ArchNameColumn,
|
||||||
|
ArchBitsColumn,
|
||||||
|
NumModulesColumn,
|
||||||
|
NameColumn,
|
||||||
|
DetailsColumn,
|
||||||
|
ColumnCount
|
||||||
|
};
|
||||||
|
enum Role { FlirtDescriptionRole = Qt::UserRole };
|
||||||
|
|
||||||
|
FlirtModel(QList<FlirtDescription> *sigdb, QObject *parent = 0);
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<FlirtDescription> *sigdb;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FlirtProxyModel : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FlirtProxyModel(FlirtModel *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 FlirtWidget : public CutterDockWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FlirtWidget(MainWindow *main);
|
||||||
|
~FlirtWidget();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void refreshFlirt();
|
||||||
|
void onSelectedItemChanged(const QModelIndex &index);
|
||||||
|
void showItemContextMenu(const QPoint &pt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui::FlirtWidget> ui;
|
||||||
|
|
||||||
|
FlirtModel *model;
|
||||||
|
FlirtProxyModel *proxyModel;
|
||||||
|
QList<FlirtDescription> sigdb;
|
||||||
|
FlirtContextMenu *blockMenu;
|
||||||
|
|
||||||
|
void setScrollMode();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FLIRT_WIDGET_H
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>ZignaturesWidget</class>
|
<class>FlirtWidget</class>
|
||||||
<widget class="QDockWidget" name="ZignaturesWidget">
|
<widget class="QDockWidget" name="FlirtWidget">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string notr="true">Zignatures</string>
|
<string notr="true">Signatures</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="dockWidgetContents">
|
<widget class="QWidget" name="dockWidgetContents">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="CutterTreeView" name="zignaturesTreeView">
|
<widget class="CutterTreeView" name="flirtTreeView">
|
||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
<string notr="true">CutterTreeView::item
|
<string notr="true">CutterTreeView::item
|
||||||
{
|
{
|
@ -1,152 +0,0 @@
|
|||||||
#include "ZignaturesWidget.h"
|
|
||||||
#include "ui_ZignaturesWidget.h"
|
|
||||||
#include "core/MainWindow.h"
|
|
||||||
#include "common/Helpers.h"
|
|
||||||
|
|
||||||
ZignaturesModel::ZignaturesModel(QList<ZignatureDescription> *zignatures, QObject *parent)
|
|
||||||
: QAbstractListModel(parent), zignatures(zignatures)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int ZignaturesModel::rowCount(const QModelIndex &) const
|
|
||||||
{
|
|
||||||
return zignatures->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ZignaturesModel::columnCount(const QModelIndex &) const
|
|
||||||
{
|
|
||||||
return ZignaturesModel::ColumnCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ZignaturesModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (index.row() >= zignatures->count())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
const ZignatureDescription &zignature = zignatures->at(index.row());
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case Qt::DisplayRole:
|
|
||||||
switch (index.column()) {
|
|
||||||
case OffsetColumn:
|
|
||||||
return RzAddressString(zignature.offset);
|
|
||||||
case NameColumn:
|
|
||||||
return zignature.name;
|
|
||||||
case ValueColumn:
|
|
||||||
return zignature.bytes;
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
case ZignatureDescriptionRole:
|
|
||||||
return QVariant::fromValue(zignature);
|
|
||||||
|
|
||||||
case Qt::ToolTipRole: {
|
|
||||||
QString tmp = QString("Graph:\n\n Cyclomatic complexity: " + RzSizeString(zignature.cc)
|
|
||||||
+ "\n Nodes / basicblocks: " + RzSizeString(zignature.nbbs)
|
|
||||||
+ "\n Edges: " + RzSizeString(zignature.edges)
|
|
||||||
+ "\n Ebbs: " + RzSizeString(zignature.ebbs) + "\n\nRefs:\n");
|
|
||||||
for (const QString &ref : zignature.refs) {
|
|
||||||
tmp.append("\n " + ref);
|
|
||||||
}
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ZignaturesModel::headerData(int section, Qt::Orientation, int role) const
|
|
||||||
{
|
|
||||||
switch (role) {
|
|
||||||
case Qt::DisplayRole:
|
|
||||||
switch (section) {
|
|
||||||
case OffsetColumn:
|
|
||||||
return tr("Offset");
|
|
||||||
case NameColumn:
|
|
||||||
return tr("Name");
|
|
||||||
case ValueColumn:
|
|
||||||
return tr("Bytes");
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ZignaturesProxyModel::ZignaturesProxyModel(ZignaturesModel *sourceModel, QObject *parent)
|
|
||||||
: QSortFilterProxyModel(parent)
|
|
||||||
{
|
|
||||||
setSourceModel(sourceModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ZignaturesProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
QModelIndex index = sourceModel()->index(row, 0, parent);
|
|
||||||
ZignatureDescription item =
|
|
||||||
index.data(ZignaturesModel::ZignatureDescriptionRole).value<ZignatureDescription>();
|
|
||||||
return qhelpers::filterStringContains(item.name, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ZignaturesProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
|
||||||
{
|
|
||||||
ZignatureDescription leftZignature =
|
|
||||||
left.data(ZignaturesModel::ZignatureDescriptionRole).value<ZignatureDescription>();
|
|
||||||
ZignatureDescription rightZignature =
|
|
||||||
right.data(ZignaturesModel::ZignatureDescriptionRole).value<ZignatureDescription>();
|
|
||||||
|
|
||||||
switch (left.column()) {
|
|
||||||
case ZignaturesModel::OffsetColumn:
|
|
||||||
return leftZignature.offset < rightZignature.offset;
|
|
||||||
case ZignaturesModel::NameColumn:
|
|
||||||
return leftZignature.name < rightZignature.name;
|
|
||||||
case ZignaturesModel::ValueColumn:
|
|
||||||
return leftZignature.bytes < rightZignature.bytes;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return leftZignature.offset < rightZignature.offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
ZignaturesWidget::ZignaturesWidget(MainWindow *main)
|
|
||||||
: CutterDockWidget(main), ui(new Ui::ZignaturesWidget)
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
zignaturesModel = new ZignaturesModel(&zignatures, this);
|
|
||||||
zignaturesProxyModel = new ZignaturesProxyModel(zignaturesModel, this);
|
|
||||||
ui->zignaturesTreeView->setModel(zignaturesProxyModel);
|
|
||||||
ui->zignaturesTreeView->sortByColumn(ZignaturesModel::OffsetColumn, Qt::AscendingOrder);
|
|
||||||
|
|
||||||
setScrollMode();
|
|
||||||
|
|
||||||
connect(Core(), &CutterCore::refreshAll, this, &ZignaturesWidget::refreshZignatures);
|
|
||||||
}
|
|
||||||
|
|
||||||
ZignaturesWidget::~ZignaturesWidget() {}
|
|
||||||
|
|
||||||
void ZignaturesWidget::refreshZignatures()
|
|
||||||
{
|
|
||||||
zignaturesModel->beginResetModel();
|
|
||||||
zignatures = Core()->getAllZignatures();
|
|
||||||
zignaturesModel->endResetModel();
|
|
||||||
|
|
||||||
ui->zignaturesTreeView->resizeColumnToContents(0);
|
|
||||||
ui->zignaturesTreeView->resizeColumnToContents(1);
|
|
||||||
ui->zignaturesTreeView->resizeColumnToContents(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ZignaturesWidget::setScrollMode()
|
|
||||||
{
|
|
||||||
qhelpers::setVerticalScrollMode(ui->zignaturesTreeView);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ZignaturesWidget::on_zignaturesTreeView_doubleClicked(const QModelIndex &index)
|
|
||||||
{
|
|
||||||
ZignatureDescription item =
|
|
||||||
index.data(ZignaturesModel::ZignatureDescriptionRole).value<ZignatureDescription>();
|
|
||||||
Core()->seekAndShow(item.offset);
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
#ifndef ZIGNATURESWIDGET_H
|
|
||||||
#define ZIGNATURESWIDGET_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "core/Cutter.h"
|
|
||||||
#include "CutterDockWidget.h"
|
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
|
|
||||||
class MainWindow;
|
|
||||||
class QTreeWidget;
|
|
||||||
class QTreeWidgetItem;
|
|
||||||
class ZignaturesWidget;
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class ZignaturesWidget;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ZignaturesModel : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
friend ZignaturesWidget;
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum Column { OffsetColumn = 0, NameColumn, ValueColumn, ColumnCount };
|
|
||||||
enum Role { ZignatureDescriptionRole = Qt::UserRole };
|
|
||||||
|
|
||||||
ZignaturesModel(QList<ZignatureDescription> *zignatures, QObject *parent = 0);
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
|
||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<ZignatureDescription> *zignatures;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ZignaturesProxyModel : public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
ZignaturesProxyModel(ZignaturesModel *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 ZignaturesWidget : public CutterDockWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ZignaturesWidget(MainWindow *main);
|
|
||||||
~ZignaturesWidget();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void on_zignaturesTreeView_doubleClicked(const QModelIndex &index);
|
|
||||||
|
|
||||||
void refreshZignatures();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Ui::ZignaturesWidget> ui;
|
|
||||||
|
|
||||||
ZignaturesModel *zignaturesModel;
|
|
||||||
ZignaturesProxyModel *zignaturesProxyModel;
|
|
||||||
QList<ZignatureDescription> zignatures;
|
|
||||||
|
|
||||||
void setScrollMode();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // ZIGNATURESWIDGET_H
|
|
Loading…
Reference in New Issue
Block a user