2017-10-10 10:17:05 +00:00
|
|
|
#include "DisassemblyContextMenu.h"
|
2017-12-14 13:42:24 +00:00
|
|
|
#include "dialogs/preferences/PreferencesDialog.h"
|
2018-02-12 20:12:13 +00:00
|
|
|
#include "dialogs/EditInstructionDialog.h"
|
2017-10-10 10:17:05 +00:00
|
|
|
#include "dialogs/CommentsDialog.h"
|
|
|
|
#include "dialogs/FlagDialog.h"
|
|
|
|
#include "dialogs/RenameDialog.h"
|
|
|
|
#include "dialogs/XrefsDialog.h"
|
2018-12-19 08:39:23 +00:00
|
|
|
#include "dialogs/EditVariablesDialog.h"
|
2018-08-04 18:05:56 +00:00
|
|
|
#include "dialogs/SetToDataDialog.h"
|
2018-10-22 09:16:56 +00:00
|
|
|
#include "dialogs/EditFunctionDialog.h"
|
2019-03-04 21:45:17 +00:00
|
|
|
#include "dialogs/LinkTypeDialog.h"
|
2019-12-18 14:26:51 +00:00
|
|
|
#include "dialogs/EditStringDialog.h"
|
2020-01-04 18:05:49 +00:00
|
|
|
#include "dialogs/BreakpointsDialog.h"
|
2019-07-19 19:21:12 +00:00
|
|
|
#include "MainWindow.h"
|
2019-03-16 12:41:45 +00:00
|
|
|
|
2017-10-10 10:17:05 +00:00
|
|
|
#include <QtCore>
|
2017-11-03 11:31:20 +00:00
|
|
|
#include <QShortcut>
|
2018-02-12 20:12:13 +00:00
|
|
|
#include <QJsonArray>
|
2018-05-03 07:53:01 +00:00
|
|
|
#include <QClipboard>
|
|
|
|
#include <QApplication>
|
2019-03-16 12:41:45 +00:00
|
|
|
#include <QPushButton>
|
2017-10-10 10:17:05 +00:00
|
|
|
|
2019-08-14 18:47:30 +00:00
|
|
|
DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *mainWindow)
|
2017-11-19 17:49:29 +00:00
|
|
|
: QMenu(parent),
|
|
|
|
offset(0),
|
2019-07-19 19:21:12 +00:00
|
|
|
canCopy(false),
|
2019-10-06 17:35:44 +00:00
|
|
|
mainWindow(mainWindow),
|
|
|
|
actionEditInstruction(this),
|
|
|
|
actionNopInstruction(this),
|
|
|
|
actionJmpReverse(this),
|
|
|
|
actionEditBytes(this),
|
|
|
|
actionCopy(this),
|
|
|
|
actionCopyAddr(this),
|
|
|
|
actionAddComment(this),
|
|
|
|
actionAddFlag(this),
|
|
|
|
actionAnalyzeFunction(this),
|
|
|
|
actionEditFunction(this),
|
|
|
|
actionRename(this),
|
|
|
|
actionRenameUsedHere(this),
|
|
|
|
actionSetFunctionVarTypes(this),
|
|
|
|
actionXRefs(this),
|
|
|
|
actionDisplayOptions(this),
|
|
|
|
actionDeleteComment(this),
|
|
|
|
actionDeleteFlag(this),
|
|
|
|
actionDeleteFunction(this),
|
|
|
|
actionLinkType(this),
|
|
|
|
actionSetBaseBinary(this),
|
|
|
|
actionSetBaseOctal(this),
|
|
|
|
actionSetBaseDecimal(this),
|
|
|
|
actionSetBaseHexadecimal(this),
|
|
|
|
actionSetBasePort(this),
|
|
|
|
actionSetBaseIPAddr(this),
|
|
|
|
actionSetBaseSyscall(this),
|
|
|
|
actionSetBaseString(this),
|
|
|
|
actionSetBits16(this),
|
|
|
|
actionSetBits32(this),
|
|
|
|
actionSetBits64(this),
|
|
|
|
actionContinueUntil(this),
|
|
|
|
actionAddBreakpoint(this),
|
2020-01-04 18:05:49 +00:00
|
|
|
actionAdvancedBreakpoint(this),
|
2019-10-06 17:35:44 +00:00
|
|
|
actionSetPC(this),
|
|
|
|
actionSetToCode(this),
|
2019-12-18 14:26:51 +00:00
|
|
|
actionSetAsStringAuto(this),
|
|
|
|
actionSetAsStringRemove(this),
|
|
|
|
actionSetAsStringAdvanced(this),
|
2019-10-06 17:35:44 +00:00
|
|
|
actionSetToDataEx(this),
|
|
|
|
actionSetToDataByte(this),
|
|
|
|
actionSetToDataWord(this),
|
|
|
|
actionSetToDataDword(this),
|
|
|
|
actionSetToDataQword(this),
|
|
|
|
showInSubmenu(this)
|
2018-08-04 18:05:56 +00:00
|
|
|
{
|
|
|
|
initAction(&actionCopy, tr("Copy"), SLOT(on_actionCopy_triggered()), getCopySequence());
|
|
|
|
addAction(&actionCopy);
|
|
|
|
|
2019-08-14 18:47:30 +00:00
|
|
|
initAction(&actionCopyAddr, tr("Copy address"), SLOT(on_actionCopyAddr_triggered()),
|
|
|
|
getCopyAddressSequence());
|
2018-08-04 18:05:56 +00:00
|
|
|
addAction(&actionCopyAddr);
|
|
|
|
|
2019-07-19 19:21:12 +00:00
|
|
|
initAction(&showInSubmenu, tr("Show in"), nullptr);
|
|
|
|
addAction(&showInSubmenu);
|
|
|
|
|
2019-04-25 11:38:53 +00:00
|
|
|
copySeparator = addSeparator();
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(&actionAddComment, tr("Add Comment"),
|
|
|
|
SLOT(on_actionAddComment_triggered()), getCommentSequence());
|
|
|
|
addAction(&actionAddComment);
|
|
|
|
|
|
|
|
initAction(&actionAddFlag, tr("Add Flag"),
|
|
|
|
SLOT(on_actionAddFlag_triggered()), getAddFlagSequence());
|
|
|
|
addAction(&actionAddFlag);
|
|
|
|
|
|
|
|
initAction(&actionRename, tr("Rename"),
|
|
|
|
SLOT(on_actionRename_triggered()), getRenameSequence());
|
|
|
|
addAction(&actionRename);
|
|
|
|
|
2018-10-22 09:16:56 +00:00
|
|
|
initAction(&actionEditFunction, tr("Edit function"),
|
2019-09-25 13:58:58 +00:00
|
|
|
SLOT(on_actionEditFunction_triggered()), getEditFunctionSequence());
|
2018-10-22 09:16:56 +00:00
|
|
|
addAction(&actionEditFunction);
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(&actionRenameUsedHere, tr("Rename Flag/Fcn/Var Used Here"),
|
|
|
|
SLOT(on_actionRenameUsedHere_triggered()), getRenameUsedHereSequence());
|
|
|
|
addAction(&actionRenameUsedHere);
|
|
|
|
|
2018-10-03 20:10:53 +00:00
|
|
|
initAction(&actionSetFunctionVarTypes, tr("Re-type function local vars"),
|
|
|
|
SLOT(on_actionSetFunctionVarTypes_triggered()), getRetypeSequence());
|
|
|
|
addAction(&actionSetFunctionVarTypes);
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(&actionDeleteComment, tr("Delete comment"), SLOT(on_actionDeleteComment_triggered()));
|
|
|
|
addAction(&actionDeleteComment);
|
|
|
|
|
|
|
|
initAction(&actionDeleteFlag, tr("Delete flag"), SLOT(on_actionDeleteFlag_triggered()));
|
|
|
|
addAction(&actionDeleteFlag);
|
|
|
|
|
2018-09-26 12:43:37 +00:00
|
|
|
initAction(&actionDeleteFunction, tr("Undefine function"),
|
2019-09-25 13:58:58 +00:00
|
|
|
SLOT(on_actionDeleteFunction_triggered()), getUndefineFunctionSequence());
|
2018-08-04 18:05:56 +00:00
|
|
|
addAction(&actionDeleteFunction);
|
|
|
|
|
2019-06-12 07:08:15 +00:00
|
|
|
initAction(&actionAnalyzeFunction, tr("Define function here"),
|
2019-09-25 13:58:58 +00:00
|
|
|
SLOT(on_actionAnalyzeFunction_triggered()), getDefineNewFunctionSequence());
|
2018-09-26 12:43:37 +00:00
|
|
|
addAction(&actionAnalyzeFunction);
|
2018-08-18 19:28:04 +00:00
|
|
|
|
2018-12-03 15:16:17 +00:00
|
|
|
addSeparator();
|
2018-10-22 09:16:56 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
addSetBaseMenu();
|
|
|
|
|
|
|
|
addSetBitsMenu();
|
|
|
|
|
2019-02-24 17:25:38 +00:00
|
|
|
structureOffsetMenu = addMenu(tr("Structure offset"));
|
|
|
|
connect(structureOffsetMenu, SIGNAL(triggered(QAction*)),
|
|
|
|
this, SLOT(on_actionStructureOffsetMenu_triggered(QAction*)));
|
|
|
|
|
2019-03-04 21:45:17 +00:00
|
|
|
initAction(&actionLinkType, tr("Link Type to Address"),
|
|
|
|
SLOT(on_actionLinkType_triggered()), getLinkTypeSequence());
|
|
|
|
addAction(&actionLinkType);
|
|
|
|
|
2019-06-12 07:08:15 +00:00
|
|
|
addSetAsMenu();
|
2018-08-04 18:05:56 +00:00
|
|
|
|
|
|
|
addSeparator();
|
|
|
|
|
|
|
|
initAction(&actionXRefs, tr("Show X-Refs"),
|
|
|
|
SLOT(on_actionXRefs_triggered()), getXRefSequence());
|
|
|
|
addAction(&actionXRefs);
|
|
|
|
|
|
|
|
initAction(&actionDisplayOptions, tr("Show Options"),
|
|
|
|
SLOT(on_actionDisplayOptions_triggered()), getDisplayOptionsSequence());
|
|
|
|
|
|
|
|
addSeparator();
|
|
|
|
|
|
|
|
addEditMenu();
|
|
|
|
|
|
|
|
addSeparator();
|
|
|
|
|
2020-01-29 16:58:05 +00:00
|
|
|
addBreakpointMenu();
|
2018-08-04 18:05:56 +00:00
|
|
|
addDebugMenu();
|
|
|
|
|
2020-01-24 09:49:52 +00:00
|
|
|
addSeparator();
|
|
|
|
|
2020-02-04 09:02:34 +00:00
|
|
|
if (mainWindow) {
|
|
|
|
pluginMenu = mainWindow->getContextMenuExtensions(MainWindow::ContextMenuType::Disassembly);
|
|
|
|
pluginActionMenuAction = addMenu(pluginMenu);
|
|
|
|
}
|
2020-01-24 09:49:52 +00:00
|
|
|
|
|
|
|
addSeparator();
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(this, &DisassemblyContextMenu::aboutToShow,
|
|
|
|
this, &DisassemblyContextMenu::aboutToShowSlot);
|
|
|
|
}
|
|
|
|
|
|
|
|
DisassemblyContextMenu::~DisassemblyContextMenu()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::addSetBaseMenu()
|
|
|
|
{
|
|
|
|
setBaseMenu = addMenu(tr("Set Immediate Base to..."));
|
|
|
|
|
|
|
|
initAction(&actionSetBaseBinary, tr("Binary"));
|
2017-11-28 13:50:41 +00:00
|
|
|
setBaseMenu->addAction(&actionSetBaseBinary);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBaseBinary, &QAction::triggered, this, [this] { setBase("b"); });
|
|
|
|
|
|
|
|
initAction(&actionSetBaseOctal, tr("Octal"));
|
2017-11-28 13:50:41 +00:00
|
|
|
setBaseMenu->addAction(&actionSetBaseOctal);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBaseOctal, &QAction::triggered, this, [this] { setBase("o"); });
|
|
|
|
|
|
|
|
initAction(&actionSetBaseDecimal, tr("Decimal"));
|
2017-11-28 13:50:41 +00:00
|
|
|
setBaseMenu->addAction(&actionSetBaseDecimal);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBaseDecimal, &QAction::triggered, this, [this] { setBase("d"); });
|
|
|
|
|
|
|
|
initAction(&actionSetBaseHexadecimal, tr("Hexadecimal"));
|
2017-11-28 13:50:41 +00:00
|
|
|
setBaseMenu->addAction(&actionSetBaseHexadecimal);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBaseHexadecimal, &QAction::triggered, this, [this] { setBase("h"); });
|
|
|
|
|
|
|
|
initAction(&actionSetBasePort, tr("Network Port"));
|
2017-11-28 13:50:41 +00:00
|
|
|
setBaseMenu->addAction(&actionSetBasePort);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBasePort, &QAction::triggered, this, [this] { setBase("p"); });
|
|
|
|
|
|
|
|
initAction(&actionSetBaseIPAddr, tr("IP Address"));
|
2017-11-28 13:50:41 +00:00
|
|
|
setBaseMenu->addAction(&actionSetBaseIPAddr);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBaseIPAddr, &QAction::triggered, this, [this] { setBase("i"); });
|
|
|
|
|
|
|
|
initAction(&actionSetBaseSyscall, tr("Syscall"));
|
2017-11-28 13:50:41 +00:00
|
|
|
setBaseMenu->addAction(&actionSetBaseSyscall);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBaseSyscall, &QAction::triggered, this, [this] { setBase("S"); });
|
|
|
|
|
|
|
|
initAction(&actionSetBaseString, tr("String"));
|
2017-11-28 13:50:41 +00:00
|
|
|
setBaseMenu->addAction(&actionSetBaseString);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBaseString, &QAction::triggered, this, [this] { setBase("s"); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::addSetBitsMenu()
|
|
|
|
{
|
|
|
|
setBitsMenu = addMenu(tr("Set current bits to..."));
|
2018-02-12 09:48:06 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(&actionSetBits16, "16");
|
2018-02-12 09:48:06 +00:00
|
|
|
setBitsMenu->addAction(&actionSetBits16);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBits16, &QAction::triggered, this, [this] { setBits(16); });
|
|
|
|
|
|
|
|
initAction(&actionSetBits32, "32");
|
2018-02-12 09:48:06 +00:00
|
|
|
setBitsMenu->addAction(&actionSetBits32);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBits32, &QAction::triggered, this, [this] { setBits(32); });
|
|
|
|
|
|
|
|
initAction(&actionSetBits64, "64");
|
2018-02-12 09:48:06 +00:00
|
|
|
setBitsMenu->addAction(&actionSetBits64);
|
2018-08-04 18:05:56 +00:00
|
|
|
connect(&actionSetBits64, &QAction::triggered, this, [this] { setBits(64); });
|
|
|
|
}
|
2018-02-12 09:48:06 +00:00
|
|
|
|
2019-06-12 07:08:15 +00:00
|
|
|
|
|
|
|
void DisassemblyContextMenu::addSetAsMenu()
|
|
|
|
{
|
|
|
|
setAsMenu = addMenu(tr("Set as..."));
|
|
|
|
|
|
|
|
initAction(&actionSetToCode, tr("Code"),
|
|
|
|
SLOT(on_actionSetToCode_triggered()), getSetToCodeSequence());
|
|
|
|
setAsMenu->addAction(&actionSetToCode);
|
|
|
|
|
2019-12-18 14:26:51 +00:00
|
|
|
setAsString = setAsMenu->addMenu(tr("String..."));
|
|
|
|
|
|
|
|
initAction(&actionSetAsStringAuto, tr("Auto-detect"),
|
2019-06-12 07:08:15 +00:00
|
|
|
SLOT(on_actionSetAsString_triggered()), getSetAsStringSequence());
|
2019-12-18 14:26:51 +00:00
|
|
|
initAction(&actionSetAsStringRemove, tr("Remove"),
|
|
|
|
SLOT(on_actionSetAsStringRemove_triggered()));
|
2020-02-15 08:46:19 +00:00
|
|
|
initAction(&actionSetAsStringAdvanced, tr("Advanced"),
|
2019-12-18 14:26:51 +00:00
|
|
|
SLOT(on_actionSetAsStringAdvanced_triggered()));
|
|
|
|
|
|
|
|
|
|
|
|
setAsString->addAction(&actionSetAsStringAuto);
|
|
|
|
setAsString->addAction(&actionSetAsStringRemove);
|
|
|
|
setAsString->addAction(&actionSetAsStringAdvanced);
|
2019-06-12 07:08:15 +00:00
|
|
|
|
|
|
|
addSetToDataMenu();
|
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::addSetToDataMenu()
|
|
|
|
{
|
2019-06-12 07:08:15 +00:00
|
|
|
setToDataMenu = setAsMenu->addMenu(tr("Data..."));
|
2017-11-28 13:13:22 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(&actionSetToDataByte, tr("Byte"));
|
|
|
|
setToDataMenu->addAction(&actionSetToDataByte);
|
|
|
|
connect(&actionSetToDataByte, &QAction::triggered, this, [this] { setToData(1); });
|
|
|
|
|
|
|
|
initAction(&actionSetToDataWord, tr("Word"));
|
|
|
|
setToDataMenu->addAction(&actionSetToDataWord);
|
|
|
|
connect(&actionSetToDataWord, &QAction::triggered, this, [this] { setToData(2); });
|
|
|
|
|
|
|
|
initAction(&actionSetToDataDword, tr("Dword"));
|
|
|
|
setToDataMenu->addAction(&actionSetToDataDword);
|
|
|
|
connect(&actionSetToDataDword, &QAction::triggered, this, [this] { setToData(4); });
|
|
|
|
|
|
|
|
initAction(&actionSetToDataQword, tr("Qword"));
|
|
|
|
setToDataMenu->addAction(&actionSetToDataQword);
|
|
|
|
connect(&actionSetToDataQword, &QAction::triggered, this, [this] { setToData(8); });
|
|
|
|
|
|
|
|
initAction(&actionSetToDataEx, "...",
|
|
|
|
SLOT(on_actionSetToDataEx_triggered()), getSetToDataExSequence());
|
|
|
|
setToDataMenu->addAction(&actionSetToDataEx);
|
|
|
|
|
2019-10-06 17:35:44 +00:00
|
|
|
auto switchAction = new QAction(this);
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(switchAction, "Switch Data",
|
|
|
|
SLOT(on_actionSetToData_triggered()), getSetToDataSequence());
|
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::addEditMenu()
|
|
|
|
{
|
|
|
|
editMenu = addMenu(tr("Edit"));
|
|
|
|
|
|
|
|
initAction(&actionEditInstruction, tr("Instruction"), SLOT(on_actionEditInstruction_triggered()));
|
2018-02-12 20:12:13 +00:00
|
|
|
editMenu->addAction(&actionEditInstruction);
|
2018-08-04 18:05:56 +00:00
|
|
|
|
|
|
|
initAction(&actionNopInstruction, tr("Nop Instruction"), SLOT(on_actionNopInstruction_triggered()));
|
2018-03-05 14:20:55 +00:00
|
|
|
editMenu->addAction(&actionNopInstruction);
|
2018-02-12 20:12:13 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(&actionEditBytes, tr("Bytes"), SLOT(on_actionEditBytes_triggered()));
|
|
|
|
editMenu->addAction(&actionEditBytes);
|
2018-06-12 08:43:14 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(&actionJmpReverse, tr("Reverse Jump"), SLOT(on_actionJmpReverse_triggered()));
|
|
|
|
editMenu->addAction(&actionJmpReverse);
|
2017-11-28 13:50:41 +00:00
|
|
|
}
|
|
|
|
|
2020-01-29 16:58:05 +00:00
|
|
|
void DisassemblyContextMenu::addBreakpointMenu()
|
2017-12-10 20:40:15 +00:00
|
|
|
{
|
2020-01-29 16:58:05 +00:00
|
|
|
breakpointMenu = addMenu(tr("Breakpoint"));
|
2018-08-04 18:05:56 +00:00
|
|
|
|
|
|
|
initAction(&actionAddBreakpoint, tr("Add/remove breakpoint"),
|
|
|
|
SLOT(on_actionAddBreakpoint_triggered()), getAddBPSequence());
|
2020-01-29 16:58:05 +00:00
|
|
|
breakpointMenu->addAction(&actionAddBreakpoint);
|
2020-01-04 18:05:49 +00:00
|
|
|
initAction(&actionAdvancedBreakpoint, tr("Advanced breakpoint"),
|
|
|
|
SLOT(on_actionAdvancedBreakpoint_triggered()), QKeySequence(Qt::CTRL+Qt::Key_F2));
|
2020-01-29 16:58:05 +00:00
|
|
|
breakpointMenu->addAction(&actionAdvancedBreakpoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::addDebugMenu()
|
|
|
|
{
|
|
|
|
debugMenu = addMenu(tr("Debug"));
|
2018-08-04 18:05:56 +00:00
|
|
|
|
|
|
|
initAction(&actionContinueUntil, tr("Continue until line"),
|
|
|
|
SLOT(on_actionContinueUntil_triggered()));
|
|
|
|
debugMenu->addAction(&actionContinueUntil);
|
|
|
|
|
|
|
|
initAction(&actionSetPC, "Set PC", SLOT(on_actionSetPC_triggered()));
|
|
|
|
debugMenu->addAction(&actionSetPC);
|
2017-12-10 20:40:15 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 18:47:30 +00:00
|
|
|
QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
|
|
|
|
{
|
|
|
|
QVector<ThingUsedHere> result;
|
|
|
|
const QJsonArray array = Core()->cmdj("anj @ " + QString::number(offset)).array();
|
|
|
|
result.reserve(array.size());
|
|
|
|
for (const auto &thing : array) {
|
|
|
|
auto obj = thing.toObject();
|
|
|
|
RVA offset = obj["offset"].toVariant().toULongLong();
|
2020-02-15 17:31:11 +00:00
|
|
|
QString name;
|
|
|
|
|
|
|
|
// If real names display is enabled, show flag's real name instead of full flag name
|
|
|
|
if (Config()->getConfigBool("asm.flags.real") && obj.contains("realname")) {
|
|
|
|
name = obj["realname"].toString();
|
|
|
|
} else {
|
|
|
|
name = obj["name"].toString();
|
|
|
|
}
|
|
|
|
|
2019-08-14 18:47:30 +00:00
|
|
|
QString typeString = obj["type"].toString();
|
|
|
|
ThingUsedHere::Type type = ThingUsedHere::Type::Address;
|
|
|
|
if (typeString == "var") {
|
|
|
|
type = ThingUsedHere::Type::Var;
|
|
|
|
} else if (typeString == "flag") {
|
|
|
|
type = ThingUsedHere::Type::Flag;
|
|
|
|
} else if (typeString == "function") {
|
|
|
|
type = ThingUsedHere::Type::Function;
|
|
|
|
} else if (typeString == "address") {
|
|
|
|
type = ThingUsedHere::Type::Address;
|
|
|
|
}
|
|
|
|
result.push_back(ThingUsedHere{name, offset, type});
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::updateTargetMenuActions(const QVector<ThingUsedHere> &targets)
|
|
|
|
{
|
|
|
|
for (auto action : showTargetMenuActions) {
|
|
|
|
removeAction(action);
|
|
|
|
auto menu = action->menu();
|
|
|
|
if (menu) {
|
|
|
|
menu->deleteLater();
|
|
|
|
}
|
|
|
|
action->deleteLater();
|
|
|
|
}
|
|
|
|
showTargetMenuActions.clear();
|
|
|
|
for (auto &target : targets) {
|
|
|
|
QString name;
|
|
|
|
if (target.name.isEmpty()) {
|
|
|
|
name = tr("%1 (used here)").arg(RAddressString(target.offset));
|
|
|
|
} else {
|
|
|
|
name = tr("%1 (%2)").arg(target.name, RAddressString(target.offset));
|
|
|
|
}
|
|
|
|
auto action = new QAction(name, this);
|
|
|
|
showTargetMenuActions.append(action);
|
|
|
|
auto menu = mainWindow->createShowInMenu(this, target.offset);
|
|
|
|
action->setMenu(menu);
|
|
|
|
QAction *copyAddress = new QAction(tr("Copy address"), menu);
|
|
|
|
RVA offset = target.offset;
|
|
|
|
connect(copyAddress, &QAction::triggered, copyAddress, [offset]() {
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setText(RAddressString(offset));
|
|
|
|
});
|
|
|
|
menu->addSeparator();
|
|
|
|
menu->addAction(copyAddress);
|
|
|
|
}
|
|
|
|
insertActions(copySeparator, showTargetMenuActions);
|
|
|
|
}
|
|
|
|
|
2017-11-28 13:50:41 +00:00
|
|
|
void DisassemblyContextMenu::setOffset(RVA offset)
|
|
|
|
{
|
|
|
|
this->offset = offset;
|
2019-09-04 16:39:33 +00:00
|
|
|
|
|
|
|
this->actionSetFunctionVarTypes.setVisible(true);
|
2017-11-28 13:50:41 +00:00
|
|
|
}
|
|
|
|
|
2017-12-02 15:43:21 +00:00
|
|
|
void DisassemblyContextMenu::setCanCopy(bool enabled)
|
|
|
|
{
|
|
|
|
this->canCopy = enabled;
|
|
|
|
}
|
|
|
|
|
2019-03-04 21:45:17 +00:00
|
|
|
void DisassemblyContextMenu::setCurHighlightedWord(const QString &text)
|
|
|
|
{
|
|
|
|
this->curHighlightedWord = text;
|
|
|
|
}
|
|
|
|
|
2017-11-28 13:50:41 +00:00
|
|
|
void DisassemblyContextMenu::aboutToShowSlot()
|
|
|
|
{
|
|
|
|
// check if set immediate base menu makes sense
|
2018-03-21 20:32:32 +00:00
|
|
|
QJsonObject instObject = Core()->cmdj("aoj @ " + QString::number(
|
2018-10-27 15:55:22 +00:00
|
|
|
offset)).array().first().toObject();
|
2017-11-28 13:50:41 +00:00
|
|
|
auto keys = instObject.keys();
|
|
|
|
bool immBase = keys.contains("val") || keys.contains("ptr");
|
2018-08-04 18:05:56 +00:00
|
|
|
setBaseMenu->menuAction()->setVisible(immBase);
|
|
|
|
setBitsMenu->menuAction()->setVisible(true);
|
2017-12-01 10:46:13 +00:00
|
|
|
|
2019-02-24 17:25:38 +00:00
|
|
|
// Create structure offset menu if it makes sense
|
|
|
|
QString memBaseReg; // Base register
|
|
|
|
QVariant memDisp; // Displacement
|
|
|
|
if (instObject.contains("opex") && instObject["opex"].toObject().contains("operands")) {
|
|
|
|
// Loop through both the operands of the instruction
|
|
|
|
for (const QJsonValue value: instObject["opex"].toObject()["operands"].toArray()) {
|
|
|
|
QJsonObject operand = value.toObject();
|
|
|
|
if (operand.contains("type") && operand["type"].toString() == "mem" &&
|
|
|
|
operand.contains("base") && !operand["base"].toString().contains("bp") &&
|
|
|
|
operand.contains("disp") && operand["disp"].toVariant().toLongLong() > 0) {
|
|
|
|
|
|
|
|
// The current operand is the one which has an immediate displacement
|
|
|
|
memBaseReg = operand["base"].toString();
|
|
|
|
memDisp = operand["disp"].toVariant();
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (memBaseReg.isEmpty()) {
|
|
|
|
// hide structure offset menu
|
|
|
|
structureOffsetMenu->menuAction()->setVisible(false);
|
|
|
|
} else {
|
|
|
|
// show structure offset menu
|
|
|
|
structureOffsetMenu->menuAction()->setVisible(true);
|
|
|
|
structureOffsetMenu->clear();
|
|
|
|
|
2019-09-15 11:41:12 +00:00
|
|
|
// Get the possible offsets using the "ahts" command
|
|
|
|
// TODO: add ahtj command to radare2 and then use it here
|
|
|
|
QStringList ret = Core()->cmdList("ahts " + memDisp.toString());
|
2019-03-23 10:54:34 +00:00
|
|
|
for (const QString &val : ret) {
|
2019-02-24 17:25:38 +00:00
|
|
|
if (val.isEmpty()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
structureOffsetMenu->addAction("[" + memBaseReg + " + " + val + "]")->setData(val);
|
|
|
|
}
|
|
|
|
if (structureOffsetMenu->isEmpty()) {
|
|
|
|
// No possible offset was found so hide the menu
|
|
|
|
structureOffsetMenu->menuAction()->setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-18 19:28:04 +00:00
|
|
|
actionAnalyzeFunction.setVisible(true);
|
2017-12-11 13:07:12 +00:00
|
|
|
|
2020-03-15 12:51:49 +00:00
|
|
|
// Show the option to remove a defined string only if a string is defined in this address
|
|
|
|
QString stringDefinition = Core()->cmd("Cs. @ " + RAddressString(offset));
|
|
|
|
actionSetAsStringRemove.setVisible(!stringDefinition.isEmpty());
|
|
|
|
|
2017-12-10 18:13:37 +00:00
|
|
|
QString comment = Core()->cmd("CC." + RAddressString(offset));
|
2018-03-21 20:32:32 +00:00
|
|
|
if (comment.isNull() || comment.isEmpty()) {
|
2017-12-11 13:07:12 +00:00
|
|
|
actionDeleteComment.setVisible(false);
|
2017-12-01 10:46:13 +00:00
|
|
|
actionAddComment.setText(tr("Add Comment"));
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-11 13:07:12 +00:00
|
|
|
actionDeleteComment.setVisible(true);
|
2017-12-01 10:46:13 +00:00
|
|
|
actionAddComment.setText(tr("Edit Comment"));
|
|
|
|
}
|
2017-12-02 15:43:21 +00:00
|
|
|
|
|
|
|
actionCopy.setVisible(canCopy);
|
|
|
|
copySeparator->setVisible(canCopy);
|
2017-12-03 20:23:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
RCore *core = Core()->core();
|
2020-01-15 19:19:24 +00:00
|
|
|
RAnalFunction *fcn = Core()->functionAt(offset);
|
|
|
|
RAnalFunction *in_fcn = Core()->functionIn(offset);
|
2017-12-03 20:23:02 +00:00
|
|
|
RFlagItem *f = r_flag_get_i (core->flags, offset);
|
2017-12-11 13:07:12 +00:00
|
|
|
|
|
|
|
actionDeleteFlag.setVisible(f ? true : false);
|
|
|
|
actionDeleteFunction.setVisible(fcn ? true : false);
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (fcn) {
|
2018-08-18 19:28:04 +00:00
|
|
|
actionAnalyzeFunction.setVisible(false);
|
2017-12-03 20:23:02 +00:00
|
|
|
actionRename.setVisible(true);
|
|
|
|
actionRename.setText(tr("Rename function \"%1\"").arg(fcn->name));
|
2018-03-21 20:32:32 +00:00
|
|
|
} else if (f) {
|
2020-02-15 17:31:11 +00:00
|
|
|
QString name;
|
|
|
|
|
|
|
|
// Check if Realname is enabled. If yes, show it instead of the full flag-name.
|
|
|
|
if (Config()->getConfigBool("asm.flags.real") && f->realname) {
|
|
|
|
name = f->realname;
|
|
|
|
} else {
|
|
|
|
name = f->name;
|
|
|
|
}
|
|
|
|
|
2017-12-03 20:23:02 +00:00
|
|
|
actionRename.setVisible(true);
|
2020-02-15 17:31:11 +00:00
|
|
|
actionRename.setText(tr("Rename flag \"%1\"").arg(name));
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-03 20:23:02 +00:00
|
|
|
actionRename.setVisible(false);
|
|
|
|
}
|
2017-11-30 14:16:39 +00:00
|
|
|
|
2018-11-05 21:51:27 +00:00
|
|
|
// Only show retype for local vars if in a function
|
2018-10-27 15:55:22 +00:00
|
|
|
if (in_fcn) {
|
2019-09-04 16:39:33 +00:00
|
|
|
auto vars = Core()->getVariables(offset);
|
|
|
|
actionSetFunctionVarTypes.setVisible(!vars.empty());
|
2018-10-22 22:31:50 +00:00
|
|
|
actionEditFunction.setVisible(true);
|
|
|
|
actionEditFunction.setText(tr("Edit function \"%1\"").arg(in_fcn->name));
|
2018-10-27 15:55:22 +00:00
|
|
|
} else {
|
2018-10-03 20:10:53 +00:00
|
|
|
actionSetFunctionVarTypes.setVisible(false);
|
2018-10-22 22:31:50 +00:00
|
|
|
actionEditFunction.setVisible(false);
|
2018-10-03 20:10:53 +00:00
|
|
|
}
|
|
|
|
|
2017-11-30 14:16:39 +00:00
|
|
|
|
2018-11-05 21:51:27 +00:00
|
|
|
// Only show "rename X used here" if there is something to rename
|
2019-08-14 18:47:30 +00:00
|
|
|
auto thingsUsedHere = getThingUsedHere(offset);
|
|
|
|
if (!thingsUsedHere.isEmpty()) {
|
2017-11-30 14:16:39 +00:00
|
|
|
actionRenameUsedHere.setVisible(true);
|
2019-08-14 18:47:30 +00:00
|
|
|
auto &thingUsedHere = thingsUsedHere.first();
|
|
|
|
if (thingUsedHere.type == ThingUsedHere::Type::Address) {
|
|
|
|
RVA offset = thingUsedHere.offset;
|
2017-11-30 21:18:09 +00:00
|
|
|
actionRenameUsedHere.setText(tr("Add flag at %1 (used here)").arg(RAddressString(offset)));
|
2019-08-14 18:47:30 +00:00
|
|
|
} else if (thingUsedHere.type == ThingUsedHere::Type::Function) {
|
|
|
|
actionRenameUsedHere.setText(tr("Rename \"%1\"").arg(thingUsedHere.name));
|
|
|
|
} else {
|
|
|
|
actionRenameUsedHere.setText(tr("Rename \"%1\" (used here)").arg(thingUsedHere.name));
|
2017-11-30 21:18:09 +00:00
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-11-30 14:16:39 +00:00
|
|
|
actionRenameUsedHere.setVisible(false);
|
|
|
|
}
|
2019-08-14 18:47:30 +00:00
|
|
|
updateTargetMenuActions(thingsUsedHere);
|
2018-03-10 07:27:09 +00:00
|
|
|
|
2018-11-05 21:51:27 +00:00
|
|
|
// Decide to show Reverse jmp option
|
2018-03-10 07:27:09 +00:00
|
|
|
showReverseJmpQuery();
|
|
|
|
|
2019-07-19 19:21:12 +00:00
|
|
|
if (showInSubmenu.menu() != nullptr) {
|
|
|
|
showInSubmenu.menu()->deleteLater();
|
|
|
|
}
|
|
|
|
showInSubmenu.setMenu(mainWindow->createShowInMenu(this, offset));
|
|
|
|
|
2018-11-05 21:51:27 +00:00
|
|
|
// Only show debug options if we are currently debugging
|
2018-08-04 18:05:56 +00:00
|
|
|
debugMenu->menuAction()->setVisible(Core()->currentlyDebugging);
|
2020-01-04 18:05:49 +00:00
|
|
|
bool hasBreakpoint = Core()->breakpointIndexAt(offset) > -1;
|
|
|
|
actionAddBreakpoint.setText(hasBreakpoint ?
|
|
|
|
tr("Remove breakpoint") : tr("Add breakpoint"));
|
|
|
|
actionAdvancedBreakpoint.setText(hasBreakpoint ?
|
|
|
|
tr("Edit breakpoint") : tr("Advanced breakpoint"));
|
2019-12-06 17:57:11 +00:00
|
|
|
QString progCounterName = Core()->getRegisterName("PC").toUpper();
|
2018-07-23 23:13:46 +00:00
|
|
|
actionSetPC.setText("Set " + progCounterName + " here");
|
2020-01-24 09:49:52 +00:00
|
|
|
|
2020-02-04 09:02:34 +00:00
|
|
|
if (pluginMenu) {
|
|
|
|
pluginActionMenuAction->setVisible(!pluginMenu->isEmpty());
|
|
|
|
for (QAction *pluginAction : pluginMenu->actions()) {
|
|
|
|
pluginAction->setData(QVariant::fromValue(offset));
|
|
|
|
}
|
2020-01-24 09:49:52 +00:00
|
|
|
}
|
2017-12-02 15:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QKeySequence DisassemblyContextMenu::getCopySequence() const
|
|
|
|
{
|
|
|
|
return QKeySequence::Copy;
|
2017-11-28 13:50:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QKeySequence DisassemblyContextMenu::getCommentSequence() const
|
|
|
|
{
|
2018-01-16 14:29:33 +00:00
|
|
|
return {Qt::Key_Semicolon};
|
2017-11-28 13:50:41 +00:00
|
|
|
}
|
|
|
|
|
2019-04-25 11:38:53 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getCopyAddressSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::CTRL + Qt::SHIFT + Qt::Key_C};
|
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getSetToCodeSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_C};
|
|
|
|
}
|
|
|
|
|
2018-11-16 21:27:07 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getSetAsStringSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_A};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getSetToDataSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_D};
|
|
|
|
}
|
|
|
|
|
|
|
|
QKeySequence DisassemblyContextMenu::getSetToDataExSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_Asterisk};
|
|
|
|
}
|
|
|
|
|
2017-11-28 13:50:41 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getAddFlagSequence() const
|
|
|
|
{
|
|
|
|
return {}; //TODO insert correct sequence
|
|
|
|
}
|
|
|
|
|
|
|
|
QKeySequence DisassemblyContextMenu::getRenameSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_N};
|
|
|
|
}
|
|
|
|
|
2017-11-30 14:16:39 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getRenameUsedHereSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::SHIFT + Qt::Key_N};
|
|
|
|
}
|
|
|
|
|
2018-10-03 20:10:53 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getRetypeSequence() const
|
|
|
|
{
|
2018-10-23 05:06:26 +00:00
|
|
|
return {Qt::Key_Y};
|
2018-10-03 20:10:53 +00:00
|
|
|
}
|
|
|
|
|
2017-11-28 13:50:41 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getXRefSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_X};
|
|
|
|
}
|
|
|
|
|
|
|
|
QKeySequence DisassemblyContextMenu::getDisplayOptionsSequence() const
|
|
|
|
{
|
|
|
|
return {}; //TODO insert correct sequence
|
2017-10-10 10:17:05 +00:00
|
|
|
}
|
|
|
|
|
2019-03-04 21:45:17 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getLinkTypeSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_L};
|
|
|
|
}
|
|
|
|
|
2018-07-17 07:26:20 +00:00
|
|
|
QList<QKeySequence> DisassemblyContextMenu::getAddBPSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_F2, Qt::CTRL + Qt::Key_B};
|
|
|
|
}
|
|
|
|
|
2019-09-25 13:58:58 +00:00
|
|
|
QKeySequence DisassemblyContextMenu::getDefineNewFunctionSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_P};
|
|
|
|
}
|
|
|
|
|
|
|
|
QKeySequence DisassemblyContextMenu::getEditFunctionSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::SHIFT + Qt::Key_P};
|
|
|
|
}
|
|
|
|
|
|
|
|
QKeySequence DisassemblyContextMenu::getUndefineFunctionSequence() const
|
|
|
|
{
|
|
|
|
return {Qt::Key_U};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-12 20:12:13 +00:00
|
|
|
void DisassemblyContextMenu::on_actionEditInstruction_triggered()
|
|
|
|
{
|
2020-03-19 09:36:36 +00:00
|
|
|
if (!ioModesController.prepareForWriting()) {
|
|
|
|
return;
|
|
|
|
}
|
2019-03-23 06:32:31 +00:00
|
|
|
EditInstructionDialog e(EDIT_TEXT, this);
|
|
|
|
e.setWindowTitle(tr("Edit Instruction at %1").arg(RAddressString(offset)));
|
2018-02-12 20:12:13 +00:00
|
|
|
|
2018-09-14 17:20:54 +00:00
|
|
|
QString oldInstructionOpcode = Core()->getInstructionOpcode(offset);
|
|
|
|
QString oldInstructionBytes = Core()->getInstructionBytes(offset);
|
|
|
|
|
2019-03-23 06:32:31 +00:00
|
|
|
e.setInstruction(oldInstructionOpcode);
|
2018-02-12 20:12:13 +00:00
|
|
|
|
2019-03-23 06:32:31 +00:00
|
|
|
if (e.exec()) {
|
|
|
|
QString userInstructionOpcode = e.getInstruction();
|
2018-09-14 17:20:54 +00:00
|
|
|
if (userInstructionOpcode != oldInstructionOpcode) {
|
|
|
|
Core()->editInstruction(offset, userInstructionOpcode);
|
2018-02-12 20:12:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 14:20:55 +00:00
|
|
|
void DisassemblyContextMenu::on_actionNopInstruction_triggered()
|
|
|
|
{
|
2020-03-19 09:36:36 +00:00
|
|
|
if (!ioModesController.prepareForWriting()) {
|
|
|
|
return;
|
2018-09-14 17:20:54 +00:00
|
|
|
}
|
2020-03-19 09:36:36 +00:00
|
|
|
Core()->nopInstruction(offset);
|
2018-03-05 14:20:55 +00:00
|
|
|
}
|
|
|
|
|
2018-03-10 07:27:09 +00:00
|
|
|
void DisassemblyContextMenu::showReverseJmpQuery()
|
|
|
|
{
|
|
|
|
QString type;
|
|
|
|
|
|
|
|
QJsonArray array = Core()->cmdj("pdj 1 @ " + RAddressString(offset)).array();
|
2018-03-21 20:32:32 +00:00
|
|
|
if (array.isEmpty()) {
|
2018-03-10 07:27:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
type = array.first().toObject()["type"].toString();
|
2018-03-21 20:32:32 +00:00
|
|
|
if (type == "cjmp") {
|
2018-03-10 07:27:09 +00:00
|
|
|
actionJmpReverse.setVisible(true);
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2018-03-10 07:27:09 +00:00
|
|
|
actionJmpReverse.setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::on_actionJmpReverse_triggered()
|
|
|
|
{
|
2020-03-19 09:36:36 +00:00
|
|
|
if (!ioModesController.prepareForWriting()) {
|
|
|
|
return;
|
2018-09-14 17:20:54 +00:00
|
|
|
}
|
2020-03-19 09:36:36 +00:00
|
|
|
Core()->jmpReverse(offset);
|
2018-03-10 07:27:09 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 20:12:13 +00:00
|
|
|
void DisassemblyContextMenu::on_actionEditBytes_triggered()
|
|
|
|
{
|
2020-03-19 09:36:36 +00:00
|
|
|
if (!ioModesController.prepareForWriting()) {
|
|
|
|
return;
|
|
|
|
}
|
2019-03-23 06:32:31 +00:00
|
|
|
EditInstructionDialog e(EDIT_BYTES, this);
|
|
|
|
e.setWindowTitle(tr("Edit Bytes at %1").arg(RAddressString(offset)));
|
2018-02-12 20:12:13 +00:00
|
|
|
|
2018-09-14 17:20:54 +00:00
|
|
|
QString oldBytes = Core()->getInstructionBytes(offset);
|
2019-03-23 06:32:31 +00:00
|
|
|
e.setInstruction(oldBytes);
|
2018-02-12 20:12:13 +00:00
|
|
|
|
2019-03-23 06:32:31 +00:00
|
|
|
if (e.exec()) {
|
|
|
|
QString bytes = e.getInstruction();
|
2018-03-21 20:32:32 +00:00
|
|
|
if (bytes != oldBytes) {
|
2018-02-12 20:12:13 +00:00
|
|
|
Core()->editBytes(offset, bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-02 15:43:21 +00:00
|
|
|
void DisassemblyContextMenu::on_actionCopy_triggered()
|
|
|
|
{
|
|
|
|
emit copy();
|
|
|
|
}
|
|
|
|
|
2018-05-03 07:53:01 +00:00
|
|
|
void DisassemblyContextMenu::on_actionCopyAddr_triggered()
|
|
|
|
{
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setText(RAddressString(offset));
|
|
|
|
}
|
|
|
|
|
2018-06-12 08:43:14 +00:00
|
|
|
void DisassemblyContextMenu::on_actionAddBreakpoint_triggered()
|
|
|
|
{
|
2018-07-17 07:26:20 +00:00
|
|
|
Core()->toggleBreakpoint(offset);
|
2018-06-12 08:43:14 +00:00
|
|
|
}
|
|
|
|
|
2020-01-04 18:05:49 +00:00
|
|
|
void DisassemblyContextMenu::on_actionAdvancedBreakpoint_triggered()
|
|
|
|
{
|
|
|
|
int index = Core()->breakpointIndexAt(offset);
|
|
|
|
if (index >= 0) {
|
|
|
|
BreakpointsDialog::editBreakpoint(Core()->getBreakpointAt(offset), this);
|
|
|
|
} else {
|
|
|
|
BreakpointsDialog::createNewBreakpoint(offset, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-12 08:43:14 +00:00
|
|
|
void DisassemblyContextMenu::on_actionContinueUntil_triggered()
|
|
|
|
{
|
|
|
|
Core()->continueUntilDebug(RAddressString(offset));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::on_actionSetPC_triggered()
|
|
|
|
{
|
|
|
|
QString progCounterName = Core()->getRegisterName("PC");
|
2019-12-06 17:57:11 +00:00
|
|
|
Core()->setRegister(progCounterName, RAddressString(offset).toUpper());
|
2018-06-12 08:43:14 +00:00
|
|
|
}
|
|
|
|
|
2017-10-10 10:17:05 +00:00
|
|
|
void DisassemblyContextMenu::on_actionAddComment_triggered()
|
|
|
|
{
|
2019-09-01 21:30:25 +00:00
|
|
|
CommentsDialog::addOrEditComment(offset, this);
|
2017-10-10 10:17:05 +00:00
|
|
|
}
|
|
|
|
|
2018-08-18 19:28:04 +00:00
|
|
|
void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered()
|
2017-12-10 20:40:15 +00:00
|
|
|
{
|
2019-09-05 08:22:05 +00:00
|
|
|
RenameDialog dialog(mainWindow);
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setWindowTitle(tr("Analyze function at %1").arg(RAddressString(offset)));
|
|
|
|
dialog.setPlaceholderText(tr("Function name"));
|
|
|
|
if (dialog.exec()) {
|
|
|
|
QString function_name = dialog.getName();
|
2017-12-10 20:40:15 +00:00
|
|
|
Core()->createFunctionAt(offset, function_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-10 10:17:05 +00:00
|
|
|
void DisassemblyContextMenu::on_actionAddFlag_triggered()
|
|
|
|
{
|
2019-03-23 06:32:31 +00:00
|
|
|
FlagDialog dialog(offset, this->parentWidget());
|
|
|
|
dialog.exec();
|
2017-10-10 10:17:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::on_actionRename_triggered()
|
|
|
|
{
|
2017-11-26 16:53:05 +00:00
|
|
|
RCore *core = Core()->core();
|
2017-11-08 09:02:39 +00:00
|
|
|
|
2019-09-05 08:22:05 +00:00
|
|
|
RenameDialog dialog(mainWindow);
|
2017-11-26 16:53:05 +00:00
|
|
|
|
2020-01-15 19:19:24 +00:00
|
|
|
RAnalFunction *fcn = Core()->functionIn (offset);
|
2017-12-03 20:23:02 +00:00
|
|
|
RFlagItem *f = r_flag_get_i (core->flags, offset);
|
2018-03-21 20:32:32 +00:00
|
|
|
if (fcn) {
|
2017-12-03 20:23:02 +00:00
|
|
|
/* Rename function */
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setWindowTitle(tr("Rename function %1").arg(fcn->name));
|
|
|
|
dialog.setName(fcn->name);
|
|
|
|
if (dialog.exec()) {
|
|
|
|
QString new_name = dialog.getName();
|
2017-12-03 20:23:02 +00:00
|
|
|
Core()->renameFunction(fcn->name, new_name);
|
2017-11-26 16:53:05 +00:00
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
} else if (f) {
|
2017-12-03 20:23:02 +00:00
|
|
|
/* Rename current flag */
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setWindowTitle(tr("Rename flag %1").arg(f->name));
|
|
|
|
dialog.setName(f->name);
|
|
|
|
if (dialog.exec()) {
|
|
|
|
QString new_name = dialog.getName();
|
2017-12-03 20:23:02 +00:00
|
|
|
Core()->renameFlag(f->name, new_name);
|
2017-11-26 16:53:05 +00:00
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2017-12-03 20:23:02 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-10-10 10:17:05 +00:00
|
|
|
}
|
|
|
|
|
2017-11-30 14:16:39 +00:00
|
|
|
void DisassemblyContextMenu::on_actionRenameUsedHere_triggered()
|
|
|
|
{
|
2019-08-14 18:47:30 +00:00
|
|
|
auto array = getThingUsedHere(offset);
|
2018-03-21 20:32:32 +00:00
|
|
|
if (array.isEmpty()) {
|
2017-11-30 14:16:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-14 18:47:30 +00:00
|
|
|
auto thingUsedHere = array.first();
|
2017-11-30 21:18:09 +00:00
|
|
|
|
2019-09-05 08:22:05 +00:00
|
|
|
RenameDialog dialog(mainWindow);
|
2017-11-30 21:18:09 +00:00
|
|
|
|
2017-11-30 21:30:51 +00:00
|
|
|
QString oldName;
|
2019-08-14 18:47:30 +00:00
|
|
|
auto type = thingUsedHere.type;
|
2017-11-30 21:30:51 +00:00
|
|
|
|
2019-08-14 18:47:30 +00:00
|
|
|
if (type == ThingUsedHere::Type::Address) {
|
|
|
|
RVA offset = thingUsedHere.offset;
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setWindowTitle(tr("Add flag at %1").arg(RAddressString(offset)));
|
|
|
|
dialog.setName("label." + QString::number(offset, 16));
|
2018-03-21 20:32:32 +00:00
|
|
|
} else {
|
2019-08-14 18:47:30 +00:00
|
|
|
oldName = thingUsedHere.name;
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setWindowTitle(tr("Rename %1").arg(oldName));
|
|
|
|
dialog.setName(oldName);
|
2017-11-30 21:18:09 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 18:47:30 +00:00
|
|
|
|
2019-03-23 06:32:31 +00:00
|
|
|
if (dialog.exec()) {
|
|
|
|
QString newName = dialog.getName().trimmed();
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!newName.isEmpty()) {
|
2017-11-30 21:18:09 +00:00
|
|
|
Core()->cmd("an " + newName + " @ " + QString::number(offset));
|
2017-11-30 21:30:51 +00:00
|
|
|
|
2019-10-09 05:46:48 +00:00
|
|
|
if (type == ThingUsedHere::Type::Address || type == ThingUsedHere::Type::Flag) {
|
2017-11-30 21:30:51 +00:00
|
|
|
Core()->triggerFlagsChanged();
|
2019-08-14 18:47:30 +00:00
|
|
|
} else if (type == ThingUsedHere::Type::Var) {
|
2017-11-30 21:30:51 +00:00
|
|
|
Core()->triggerVarsChanged();
|
2019-08-14 18:47:30 +00:00
|
|
|
} else if (type == ThingUsedHere::Type::Function) {
|
2017-11-30 21:30:51 +00:00
|
|
|
Core()->triggerFunctionRenamed(oldName, newName);
|
|
|
|
}
|
2017-11-30 21:18:09 +00:00
|
|
|
}
|
2017-11-30 14:16:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 20:10:53 +00:00
|
|
|
void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered()
|
|
|
|
{
|
2020-01-15 19:19:24 +00:00
|
|
|
RAnalFunction *fcn = Core()->functionIn(offset);
|
2018-10-03 20:10:53 +00:00
|
|
|
|
2018-12-19 08:39:23 +00:00
|
|
|
if (!fcn) {
|
2019-08-14 18:47:30 +00:00
|
|
|
QMessageBox::critical(this, tr("Re-type function local vars"),
|
|
|
|
tr("You must be in a function to define variable types."));
|
2018-12-19 08:39:23 +00:00
|
|
|
return;
|
2018-10-03 20:10:53 +00:00
|
|
|
}
|
|
|
|
|
2019-09-04 16:39:33 +00:00
|
|
|
EditVariablesDialog dialog(Core()->getOffset(), curHighlightedWord, this);
|
|
|
|
if (dialog.empty()) { // don't show the dialog if there are no variables
|
|
|
|
return;
|
|
|
|
}
|
2018-12-19 08:39:23 +00:00
|
|
|
dialog.exec();
|
2018-10-03 20:10:53 +00:00
|
|
|
}
|
|
|
|
|
2017-10-10 10:17:05 +00:00
|
|
|
void DisassemblyContextMenu::on_actionXRefs_triggered()
|
|
|
|
{
|
2019-09-01 21:30:25 +00:00
|
|
|
XrefsDialog dialog(mainWindow, nullptr);
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.fillRefsForAddress(offset, RAddressString(offset), false);
|
|
|
|
dialog.exec();
|
2017-10-10 10:17:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::on_actionDisplayOptions_triggered()
|
|
|
|
{
|
2019-03-23 06:32:31 +00:00
|
|
|
PreferencesDialog dialog(this->window());
|
|
|
|
dialog.showSection(PreferencesDialog::Section::Disassembly);
|
|
|
|
dialog.exec();
|
2017-10-10 10:17:05 +00:00
|
|
|
}
|
2017-11-28 13:13:22 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::on_actionSetToCode_triggered()
|
2017-12-11 13:07:12 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
Core()->setToCode(offset);
|
2017-12-11 13:07:12 +00:00
|
|
|
}
|
|
|
|
|
2018-11-16 21:27:07 +00:00
|
|
|
void DisassemblyContextMenu::on_actionSetAsString_triggered()
|
|
|
|
{
|
|
|
|
Core()->setAsString(offset);
|
|
|
|
}
|
|
|
|
|
2019-12-18 14:26:51 +00:00
|
|
|
void DisassemblyContextMenu::on_actionSetAsStringRemove_triggered()
|
|
|
|
{
|
|
|
|
Core()->removeString(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DisassemblyContextMenu::on_actionSetAsStringAdvanced_triggered()
|
|
|
|
{
|
|
|
|
EditStringDialog dialog(parentWidget());
|
|
|
|
const int predictedStrSize = Core()->getString(offset).size();
|
|
|
|
dialog.setStringSizeValue(predictedStrSize);
|
|
|
|
dialog.setStringStartAddress(offset);
|
|
|
|
|
|
|
|
if(!dialog.exec())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t strAddr = 0U;
|
|
|
|
if( !dialog.getStringStartAddress(strAddr) ) {
|
|
|
|
QMessageBox::critical(this->window(), tr("Wrong address"), tr("Can't edit string at this address"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
CutterCore::StringTypeFormats coreStringType = CutterCore::StringTypeFormats::None;
|
|
|
|
|
|
|
|
const auto strSize = dialog.getStringSizeValue();
|
|
|
|
const auto strType = dialog.getStringType();
|
|
|
|
switch(strType)
|
|
|
|
{
|
|
|
|
case EditStringDialog::StringType::Auto:
|
|
|
|
coreStringType = CutterCore::StringTypeFormats::None;
|
|
|
|
break;
|
|
|
|
case EditStringDialog::StringType::ASCII_LATIN1:
|
|
|
|
coreStringType = CutterCore::StringTypeFormats::ASCII_LATIN1;
|
|
|
|
break;
|
|
|
|
case EditStringDialog::StringType::UTF8:
|
|
|
|
coreStringType = CutterCore::StringTypeFormats::UTF8;
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
Core()->setAsString(strAddr, strSize, coreStringType);
|
|
|
|
}
|
2018-11-16 21:27:07 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::on_actionSetToData_triggered()
|
2017-12-11 13:07:12 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
int size = Core()->sizeofDataMeta(offset);
|
|
|
|
if (size > 8 || (size && (size & (size - 1)))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (size == 0 || size == 8) {
|
|
|
|
size = 1;
|
|
|
|
} else {
|
|
|
|
size *= 2;
|
|
|
|
}
|
|
|
|
setToData(size);
|
2017-11-28 13:13:22 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::on_actionSetToDataEx_triggered()
|
2017-11-28 13:13:22 +00:00
|
|
|
{
|
2019-03-23 06:32:31 +00:00
|
|
|
SetToDataDialog dialog(offset, this->window());
|
|
|
|
if (!dialog.exec()) {
|
2018-08-04 18:05:56 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-03-23 06:32:31 +00:00
|
|
|
setToData(dialog.getItemSize(), dialog.getItemCount());
|
2017-11-28 13:13:22 +00:00
|
|
|
}
|
|
|
|
|
2019-02-24 17:25:38 +00:00
|
|
|
void DisassemblyContextMenu::on_actionStructureOffsetMenu_triggered(QAction *action)
|
|
|
|
{
|
|
|
|
Core()->applyStructureOffset(action->data().toString(), offset);
|
|
|
|
}
|
|
|
|
|
2019-03-04 21:45:17 +00:00
|
|
|
void DisassemblyContextMenu::on_actionLinkType_triggered()
|
|
|
|
{
|
2019-09-05 08:22:05 +00:00
|
|
|
LinkTypeDialog dialog(mainWindow);
|
2019-04-15 11:14:46 +00:00
|
|
|
if (!dialog.setDefaultAddress(curHighlightedWord)) {
|
|
|
|
dialog.setDefaultAddress(RAddressString(offset));
|
|
|
|
}
|
2019-03-04 21:45:17 +00:00
|
|
|
dialog.exec();
|
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::on_actionDeleteComment_triggered()
|
2017-11-28 13:13:22 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
Core()->delComment(offset);
|
2017-11-28 13:13:22 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::on_actionDeleteFlag_triggered()
|
2017-11-28 13:13:22 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
Core()->delFlag(offset);
|
2017-11-28 13:13:22 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::on_actionDeleteFunction_triggered()
|
2017-11-28 13:13:22 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
Core()->delFunction(offset);
|
2017-11-28 13:13:22 +00:00
|
|
|
}
|
|
|
|
|
2018-10-22 09:16:56 +00:00
|
|
|
void DisassemblyContextMenu::on_actionEditFunction_triggered()
|
|
|
|
{
|
|
|
|
RCore *core = Core()->core();
|
2019-09-05 08:22:05 +00:00
|
|
|
EditFunctionDialog dialog(mainWindow);
|
2018-10-22 22:31:50 +00:00
|
|
|
RAnalFunction *fcn = r_anal_get_fcn_in(core->anal, offset, 0);
|
2018-10-22 09:16:56 +00:00
|
|
|
|
2018-10-22 22:31:50 +00:00
|
|
|
if (fcn) {
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setWindowTitle(tr("Edit function %1").arg(fcn->name));
|
|
|
|
dialog.setNameText(fcn->name);
|
2018-10-22 09:16:56 +00:00
|
|
|
|
2018-10-22 22:31:50 +00:00
|
|
|
QString startAddrText = "0x" + QString::number(fcn->addr, 16);
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setStartAddrText(startAddrText);
|
2018-10-22 09:16:56 +00:00
|
|
|
|
2018-10-22 22:31:50 +00:00
|
|
|
QString stackSizeText;
|
|
|
|
stackSizeText.sprintf("%d", fcn->stack);
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setStackSizeText(stackSizeText);
|
2018-10-22 09:16:56 +00:00
|
|
|
|
2018-10-22 22:31:50 +00:00
|
|
|
QStringList callConList = Core()->cmd("afcl").split("\n");
|
|
|
|
callConList.removeLast();
|
2019-03-23 06:32:31 +00:00
|
|
|
dialog.setCallConList(callConList);
|
|
|
|
dialog.setCallConSelected(fcn->cc);
|
2018-10-22 09:16:56 +00:00
|
|
|
|
|
|
|
|
2019-03-23 06:32:31 +00:00
|
|
|
if (dialog.exec()) {
|
|
|
|
QString new_name = dialog.getNameText();
|
2018-10-22 22:31:50 +00:00
|
|
|
Core()->renameFunction(fcn->name, new_name);
|
2019-03-23 06:32:31 +00:00
|
|
|
QString new_start_addr = dialog.getStartAddrText();
|
2018-10-22 22:31:50 +00:00
|
|
|
fcn->addr = Core()->math(new_start_addr);
|
2019-03-23 06:32:31 +00:00
|
|
|
QString new_stack_size = dialog.getStackSizeText();
|
2018-10-22 22:31:50 +00:00
|
|
|
fcn->stack = int(Core()->math(new_stack_size));
|
2019-03-23 06:32:31 +00:00
|
|
|
Core()->cmd("afc " + dialog.getCallConSelected());
|
2018-10-22 22:31:50 +00:00
|
|
|
emit Core()->functionsChanged();
|
|
|
|
}
|
2018-10-22 09:16:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::setBase(QString base)
|
2017-11-28 13:13:22 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
Core()->setImmediateBase(base, offset);
|
2017-11-28 13:13:22 +00:00
|
|
|
}
|
2017-12-10 20:40:15 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::setBits(int bits)
|
2018-02-12 09:48:06 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
Core()->setCurrentBits(bits, offset);
|
2018-02-12 09:48:06 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::setToData(int size, int repeat)
|
2018-02-12 09:48:06 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
Core()->setToData(offset, size, repeat);
|
2018-02-12 09:48:06 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
QAction *DisassemblyContextMenu::addAnonymousAction(QString name, const char *slot,
|
2018-10-27 15:55:22 +00:00
|
|
|
QKeySequence keySequence)
|
2018-02-12 09:48:06 +00:00
|
|
|
{
|
2019-08-14 18:47:30 +00:00
|
|
|
auto action = new QAction(this);
|
2018-08-04 18:05:56 +00:00
|
|
|
addAction(action);
|
|
|
|
anonymousActions.append(action);
|
|
|
|
initAction(action, name, slot, keySequence);
|
|
|
|
return action;
|
2018-02-12 09:48:06 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::initAction(QAction *action, QString name, const char *slot)
|
2017-12-10 20:40:15 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
action->setParent(this);
|
2019-05-09 12:59:18 +00:00
|
|
|
parentWidget()->addAction(action);
|
2018-08-04 18:05:56 +00:00
|
|
|
action->setText(name);
|
|
|
|
if (slot) {
|
|
|
|
connect(action, SIGNAL(triggered(bool)), this, slot);
|
|
|
|
}
|
2017-12-10 20:40:15 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::initAction(QAction *action, QString name,
|
|
|
|
const char *slot, QKeySequence keySequence)
|
2017-12-10 20:40:15 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(action, name, slot);
|
|
|
|
if (keySequence.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
2017-12-10 20:40:15 +00:00
|
|
|
action->setShortcut(keySequence);
|
2019-05-01 16:15:33 +00:00
|
|
|
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
2017-12-10 20:40:15 +00:00
|
|
|
}
|
2018-07-17 07:26:20 +00:00
|
|
|
|
2018-08-04 18:05:56 +00:00
|
|
|
void DisassemblyContextMenu::initAction(QAction *action, QString name,
|
|
|
|
const char *slot, QList<QKeySequence> keySequenceList)
|
2018-07-17 07:26:20 +00:00
|
|
|
{
|
2018-08-04 18:05:56 +00:00
|
|
|
initAction(action, name, slot);
|
|
|
|
if (keySequenceList.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
action->setShortcuts(keySequenceList);
|
2019-05-01 16:15:33 +00:00
|
|
|
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
2018-08-04 18:05:56 +00:00
|
|
|
}
|