mirror of
https://github.com/rizinorg/cutter.git
synced 2025-02-21 22:23:46 +00:00
Simplify and move Plugin context menu gui code out of core. (#2039)
This commit is contained in:
parent
a1b5a41e56
commit
1ae78655fa
@ -155,7 +155,7 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
|
||||
|
||||
Plugins()->loadPlugins();
|
||||
|
||||
for (auto *plugin : Plugins()->getPlugins()) {
|
||||
for (auto &plugin : Plugins()->getPlugins()) {
|
||||
plugin->registerDecompilers();
|
||||
}
|
||||
|
||||
@ -248,9 +248,7 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
|
||||
|
||||
CutterApplication::~CutterApplication()
|
||||
{
|
||||
#ifdef CUTTER_ENABLE_PYTHON
|
||||
Plugins()->destroyPlugins();
|
||||
#endif
|
||||
delete mainWindow;
|
||||
#ifdef CUTTER_ENABLE_PYTHON
|
||||
Python()->shutdown();
|
||||
|
@ -7,11 +7,11 @@
|
||||
<primitive-type name="bool"/>
|
||||
|
||||
<object-type name="CutterCore">
|
||||
<enum-type name="ContextMenuType" />
|
||||
</object-type>
|
||||
<object-type name="Configuration" />
|
||||
<object-type name="MainWindow" >
|
||||
<enum-type name="MenuType" />
|
||||
<enum-type name="ContextMenuType" />
|
||||
</object-type>
|
||||
<object-type name="BasicBlockHighlighter" />
|
||||
<object-type name="CutterDockWidget" />
|
||||
|
@ -209,13 +209,6 @@ void CutterCore::initialize()
|
||||
|
||||
// Initialize Async tasks manager
|
||||
asyncTaskManager = new AsyncTaskManager(this);
|
||||
|
||||
// Initialize context menu extensions for plugins
|
||||
disassemblyContextMenuExtensions = new QMenu(tr("Plugins"));
|
||||
disassemblyContextMenuExtensions->menuAction()->setVisible(false);
|
||||
disassemblyContextMenuExtensions->setEnabled(false);
|
||||
addressableContextMenuExtensions = new QMenu(tr("Plugins"));
|
||||
addressableContextMenuExtensions->menuAction()->setVisible(false);
|
||||
}
|
||||
|
||||
CutterCore::~CutterCore()
|
||||
@ -2301,39 +2294,6 @@ QStringList CutterCore::getAnalPluginNames()
|
||||
return ret;
|
||||
}
|
||||
|
||||
QMenu *CutterCore::getContextMenuExtensions(ContextMenuType type)
|
||||
{
|
||||
switch (type) {
|
||||
case ContextMenuType::Disassembly:
|
||||
return disassemblyContextMenuExtensions;
|
||||
case ContextMenuType::Addressable:
|
||||
return addressableContextMenuExtensions;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CutterCore::addContextMenuExtensionAction(ContextMenuType type, QAction *action)
|
||||
{
|
||||
QMenu *menu = getContextMenuExtensions(type);
|
||||
if (menu) {
|
||||
// The submenu should be invisible and disabled while it's empty
|
||||
if (menu->isEmpty()) {
|
||||
menu->menuAction()->setVisible(true);
|
||||
menu->setEnabled(true);
|
||||
}
|
||||
menu->addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
void CutterCore::addContextMenuExtensionSeparator(ContextMenuType type)
|
||||
{
|
||||
QMenu *menu = getContextMenuExtensions(type);
|
||||
if (menu) {
|
||||
menu->addSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList CutterCore::getProjectNames()
|
||||
{
|
||||
CORE_LOCK();
|
||||
|
@ -425,17 +425,6 @@ public:
|
||||
QStringList getAsmPluginNames();
|
||||
QStringList getAnalPluginNames();
|
||||
|
||||
/* Context menu plugins */
|
||||
enum class ContextMenuType { Disassembly, Addressable };
|
||||
/**
|
||||
* @brief Fetches the pointer to a context menu extension of type
|
||||
* @param type - the type of the context menu
|
||||
* @return plugins submenu of the selected context menu
|
||||
*/
|
||||
QMenu *getContextMenuExtensions(ContextMenuType type);
|
||||
void addContextMenuExtensionAction(ContextMenuType type, QAction *action);
|
||||
void addContextMenuExtensionSeparator(ContextMenuType type);
|
||||
|
||||
/* Projects */
|
||||
QStringList getProjectNames();
|
||||
void openProject(const QString &name);
|
||||
@ -649,9 +638,6 @@ private:
|
||||
|
||||
QSharedPointer<R2Task> debugTask;
|
||||
R2TaskDialog *debugTaskDialog;
|
||||
|
||||
QMenu *disassemblyContextMenuExtensions = nullptr;
|
||||
QMenu *addressableContextMenuExtensions = nullptr;
|
||||
};
|
||||
|
||||
class RCoreLocked
|
||||
|
@ -132,6 +132,10 @@ void MainWindow::initUI()
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Initialize context menu extensions for plugins
|
||||
disassemblyContextMenuExtensions = new QMenu(tr("Plugins"), this);
|
||||
addressableContextMenuExtensions = new QMenu(tr("Plugins"), this);
|
||||
|
||||
connect(ui->actionExtraGraph, &QAction::triggered, this, &MainWindow::addExtraGraph);
|
||||
connect(ui->actionExtraDisassembly, &QAction::triggered, this, &MainWindow::addExtraDisassembly);
|
||||
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
|
||||
@ -193,7 +197,7 @@ void MainWindow::initUI()
|
||||
initBackForwardMenu();
|
||||
|
||||
/* Setup plugins interfaces */
|
||||
for (auto plugin : Plugins()->getPlugins()) {
|
||||
for (auto &plugin : Plugins()->getPlugins()) {
|
||||
plugin->setupInterface(this);
|
||||
}
|
||||
|
||||
@ -1660,3 +1664,15 @@ void MainWindow::onZoomReset()
|
||||
{
|
||||
Config()->setZoomFactor(1.0);
|
||||
}
|
||||
|
||||
QMenu *MainWindow::getContextMenuExtensions(ContextMenuType type)
|
||||
{
|
||||
switch (type) {
|
||||
case ContextMenuType::Disassembly:
|
||||
return disassemblyContextMenuExtensions;
|
||||
case ContextMenuType::Addressable:
|
||||
return addressableContextMenuExtensions;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +125,15 @@ public:
|
||||
void setCurrentMemoryWidget(MemoryDockWidget* memoryWidget);
|
||||
MemoryDockWidget* getLastMemoryWidget();
|
||||
|
||||
/* Context menu plugins */
|
||||
enum class ContextMenuType { Disassembly, Addressable };
|
||||
/**
|
||||
* @brief Fetches the pointer to a context menu extension of type
|
||||
* @param type - the type of the context menu
|
||||
* @return plugins submenu of the selected context menu
|
||||
*/
|
||||
QMenu *getContextMenuExtensions(ContextMenuType type);
|
||||
|
||||
public slots:
|
||||
void finalizeOpen();
|
||||
|
||||
@ -263,6 +272,9 @@ private:
|
||||
QDockWidget *breakpointDock = nullptr;
|
||||
QDockWidget *registerRefsDock = nullptr;
|
||||
|
||||
QMenu *disassemblyContextMenuExtensions = nullptr;
|
||||
QMenu *addressableContextMenuExtensions = nullptr;
|
||||
|
||||
void initUI();
|
||||
void initToolBar();
|
||||
void initDocks();
|
||||
|
@ -39,7 +39,7 @@ PluginsOptionsWidget::PluginsOptionsWidget(PreferencesDialog *dialog)
|
||||
tr("Author")
|
||||
});
|
||||
|
||||
for (CutterPlugin *plugin : Plugins()->getPlugins()) {
|
||||
for (auto &plugin : Plugins()->getPlugins()) {
|
||||
auto item = new QTreeWidgetItem();
|
||||
item->setText(0, plugin->getName());
|
||||
item->setText(1, plugin->getDescription());
|
||||
|
@ -40,8 +40,8 @@ AddressableItemContextMenu::AddressableItemContextMenu(QWidget *parent, MainWind
|
||||
addAction(&actionAddcomment);
|
||||
|
||||
addSeparator();
|
||||
pluginMenu = Core()->getContextMenuExtensions(CutterCore::ContextMenuType::Addressable);
|
||||
addMenu(pluginMenu);
|
||||
pluginMenu = mainWindow->getContextMenuExtensions(MainWindow::ContextMenuType::Addressable);
|
||||
pluginMenuAction = addMenu(pluginMenu);
|
||||
addSeparator();
|
||||
|
||||
setHasTarget(hasTarget);
|
||||
@ -105,6 +105,7 @@ void AddressableItemContextMenu::aboutToShowSlot()
|
||||
}
|
||||
actionShowInMenu.setMenu(mainWindow->createShowInMenu(this, offset));
|
||||
|
||||
pluginMenuAction->setVisible(!pluginMenu->isEmpty());
|
||||
for (QAction *pluginAction : pluginMenu->actions()) {
|
||||
pluginAction->setData(QVariant::fromValue(offset));
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ private:
|
||||
virtual void aboutToShowSlot();
|
||||
|
||||
QMenu *pluginMenu;
|
||||
QAction *pluginMenuAction;
|
||||
MainWindow *mainWindow;
|
||||
|
||||
RVA offset;
|
||||
|
@ -156,8 +156,8 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
|
||||
|
||||
addSeparator();
|
||||
|
||||
pluginMenu = Core()->getContextMenuExtensions(CutterCore::ContextMenuType::Disassembly);
|
||||
addMenu(pluginMenu);
|
||||
pluginMenu = mainWindow->getContextMenuExtensions(MainWindow::ContextMenuType::Disassembly);
|
||||
pluginActionMenuAction = addMenu(pluginMenu);
|
||||
|
||||
addSeparator();
|
||||
|
||||
@ -529,6 +529,7 @@ void DisassemblyContextMenu::aboutToShowSlot()
|
||||
QString progCounterName = Core()->getRegisterName("PC").toUpper();
|
||||
actionSetPC.setText("Set " + progCounterName + " here");
|
||||
|
||||
pluginActionMenuAction->setVisible(!pluginMenu->isEmpty());
|
||||
for (QAction *pluginAction : pluginMenu->actions()) {
|
||||
pluginAction->setData(QVariant::fromValue(offset));
|
||||
}
|
||||
|
@ -182,6 +182,7 @@ private:
|
||||
|
||||
QAction showInSubmenu;
|
||||
QList<QAction*> showTargetMenuActions;
|
||||
QAction *pluginActionMenuAction;
|
||||
|
||||
// For creating anonymous entries (that are always visible)
|
||||
QAction *addAnonymousAction(QString name, const char *slot, QKeySequence shortcut);
|
||||
|
@ -33,7 +33,7 @@ PluginManager::~PluginManager()
|
||||
|
||||
void PluginManager::loadPlugins()
|
||||
{
|
||||
assert(plugins.isEmpty());
|
||||
assert(plugins.empty());
|
||||
|
||||
QString userPluginDir = getUserPluginsDirectory();
|
||||
if (!userPluginDir.isEmpty()) {
|
||||
@ -51,7 +51,7 @@ void PluginManager::loadPlugins()
|
||||
void PluginManager::loadPluginsFromDir(const QDir &pluginsDir, bool writable)
|
||||
{
|
||||
qInfo() << "Plugins are loaded from" << pluginsDir.absolutePath();
|
||||
int loadedPlugins = plugins.length();
|
||||
int loadedPlugins = plugins.size();
|
||||
if (!pluginsDir.exists()) {
|
||||
return;
|
||||
}
|
||||
@ -74,16 +74,19 @@ void PluginManager::loadPluginsFromDir(const QDir &pluginsDir, bool writable)
|
||||
}
|
||||
#endif
|
||||
|
||||
loadedPlugins = plugins.length() - loadedPlugins;
|
||||
loadedPlugins = plugins.size() - loadedPlugins;
|
||||
qInfo() << "Loaded" << loadedPlugins << "plugin(s).";
|
||||
}
|
||||
|
||||
void PluginManager::PluginTerminator::operator()(CutterPlugin *plugin) const
|
||||
{
|
||||
plugin->terminate();
|
||||
delete plugin;
|
||||
}
|
||||
|
||||
void PluginManager::destroyPlugins()
|
||||
{
|
||||
for (CutterPlugin *plugin : plugins) {
|
||||
plugin->terminate();
|
||||
delete plugin;
|
||||
}
|
||||
plugins.clear();
|
||||
}
|
||||
|
||||
QVector<QDir> PluginManager::getPluginDirectories() const
|
||||
@ -144,12 +147,12 @@ void PluginManager::loadNativePlugins(const QDir &directory)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
CutterPlugin *cutterPlugin = qobject_cast<CutterPlugin *>(plugin);
|
||||
PluginPtr cutterPlugin{qobject_cast<CutterPlugin *>(plugin)};
|
||||
if (!cutterPlugin) {
|
||||
continue;
|
||||
}
|
||||
cutterPlugin->setupPlugin();
|
||||
plugins.append(cutterPlugin);
|
||||
plugins.push_back(std::move(cutterPlugin));
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,12 +172,12 @@ void PluginManager::loadPythonPlugins(const QDir &directory)
|
||||
} else {
|
||||
moduleName = fileName;
|
||||
}
|
||||
CutterPlugin *cutterPlugin = loadPythonPlugin(moduleName.toLocal8Bit().constData());
|
||||
PluginPtr cutterPlugin{loadPythonPlugin(moduleName.toLocal8Bit().constData())};
|
||||
if (!cutterPlugin) {
|
||||
continue;
|
||||
}
|
||||
cutterPlugin->setupPlugin();
|
||||
plugins.append(cutterPlugin);
|
||||
plugins.push_back(std::move(cutterPlugin));
|
||||
}
|
||||
|
||||
PythonManager::ThreadHolder threadHolder;
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QDir>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class CutterPlugin;
|
||||
|
||||
@ -14,6 +16,13 @@ Q_OBJECT
|
||||
public:
|
||||
static PluginManager *getInstance();
|
||||
|
||||
class PluginTerminator
|
||||
{
|
||||
public:
|
||||
void operator()(CutterPlugin*) const;
|
||||
};
|
||||
using PluginPtr = std::unique_ptr<CutterPlugin, PluginTerminator>;
|
||||
|
||||
PluginManager();
|
||||
~PluginManager();
|
||||
|
||||
@ -27,13 +36,13 @@ public:
|
||||
*/
|
||||
void destroyPlugins();
|
||||
|
||||
const QList<CutterPlugin *> &getPlugins() { return plugins; }
|
||||
const std::vector<PluginPtr> &getPlugins() { return plugins; }
|
||||
|
||||
QVector<QDir> getPluginDirectories() const;
|
||||
QString getUserPluginsDirectory() const;
|
||||
|
||||
private:
|
||||
QList<CutterPlugin *> plugins;
|
||||
std::vector<PluginPtr> plugins;
|
||||
|
||||
void loadNativePlugins(const QDir &directory);
|
||||
void loadPluginsFromDir(const QDir &pluginsDir, bool writable = false);
|
||||
|
@ -42,25 +42,66 @@ class FortuneWidget(cutter.CutterDockWidget):
|
||||
|
||||
|
||||
class CutterSamplePlugin(cutter.CutterPlugin):
|
||||
name = "SamplePlugin"
|
||||
name = "Sample Plugin"
|
||||
description = "A sample plugin written in python."
|
||||
version = "1.0"
|
||||
author = "xarkes and thestr4ng3r :-P"
|
||||
version = "1.1"
|
||||
author = "Cutter developers"
|
||||
|
||||
# Override CutterPlugin methods
|
||||
|
||||
def __init__(self):
|
||||
super(CutterSamplePlugin, self).__init__()
|
||||
self.disassembly_actions = []
|
||||
self.addressable_item_actions = []
|
||||
self.disas_action = None
|
||||
self.addr_submenu = None
|
||||
self.main = None
|
||||
|
||||
def setupPlugin(self):
|
||||
pass
|
||||
|
||||
def setupInterface(self, main):
|
||||
# Dock widget
|
||||
action = QAction("Sample Python Plugin", main)
|
||||
action.setCheckable(True)
|
||||
widget = FortuneWidget(main, action)
|
||||
main.addPluginDockWidget(widget, action)
|
||||
|
||||
# Dissassembly context menu
|
||||
menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly)
|
||||
self.disas_action = menu.addAction("CutterSamplePlugin dissassembly action")
|
||||
self.disas_action.triggered.connect(self.handle_disassembler_action)
|
||||
self.main = main
|
||||
|
||||
# Context menu for tables with addressable items like Flags,Functions,Strings,Search results,...
|
||||
addressable_item_menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable)
|
||||
self.addr_submenu = addressable_item_menu.addMenu("CutterSamplePlugin") # create submenu
|
||||
adrr_action = self.addr_submenu.addAction("Action 1")
|
||||
self.addr_submenu.addSeparator() # can use separator and other qt functionality
|
||||
adrr_action2 = self.addr_submenu.addAction("Action 2")
|
||||
adrr_action.triggered.connect(self.handle_addressable_item_action)
|
||||
adrr_action2.triggered.connect(self.handle_addressable_item_action)
|
||||
|
||||
def terminate(self): # optional
|
||||
print("CutterSamplePlugin shutting down")
|
||||
if self.main:
|
||||
menu = self.main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly)
|
||||
menu.removeAction(self.disas_action)
|
||||
addressable_item_menu = self.main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable)
|
||||
submenu_action = self.addr_submenu.menuAction()
|
||||
addressable_item_menu.removeAction(submenu_action)
|
||||
print("CutterSamplePlugin finished clean up")
|
||||
|
||||
# Plugin methods
|
||||
|
||||
def handle_addressable_item_action(self):
|
||||
# for actions in plugin menu Cutter sets data to current item address
|
||||
submenu_action = self.addr_submenu.menuAction()
|
||||
cutter.message("Context menu action callback 0x{:x}".format(submenu_action.data()))
|
||||
|
||||
def handle_disassembler_action(self):
|
||||
# for actions in plugin menu Cutter sets data to address for current dissasembly line
|
||||
cutter.message("Dissasembly menu action callback 0x{:x}".format(self.disas_action.data()))
|
||||
|
||||
|
||||
# This function will be called by Cutter and should return an instance of the plugin.
|
||||
|
Loading…
Reference in New Issue
Block a user