1
0
Fork 0

GUI: Display and edit breakpoints in graph view (closes #1266) (#1688)

* GUI: working prototype for ActionHelperProxy
* GUI: fully implemented ActionHelperProxy
* GUI: display breakpoints in Graph view
* GUI: move breakpoint context menu into separate class
* GUI: add breakpoint context menu to graph view
This commit is contained in:
Georgeto 2017-08-19 15:34:53 +02:00 committed by Duncan Ogilvie
parent 2bd32aee32
commit 451807f8ae
11 changed files with 428 additions and 202 deletions

View File

@ -555,6 +555,13 @@ void AppearanceDialog::colorInfoListInit()
colorInfoListAppend(tr("Unconditional branch line"), "GraphJmpColor", "");
colorInfoListAppend(tr("True branch line"), "GraphBrtrueColor", "");
colorInfoListAppend(tr("False branch line"), "GraphBrfalseColor", "");
#ifdef _WIN64
colorInfoListAppend(tr("RIP"), "GraphCipColor", "");
#else //x86
colorInfoListAppend(tr("EIP"), "GraphCipColor", "");
#endif //_WIN64
colorInfoListAppend(tr("Breakpoint"), "GraphBreakpointColor", "");
colorInfoListAppend(tr("Disabled Breakpoint"), "GraphDisabledBreakpointColor", "");
colorInfoListAppend(tr("Other:"), "", "");
colorInfoListAppend(tr("Current Thread"), "ThreadCurrentColor", "ThreadCurrentBackgroundColor");

View File

@ -0,0 +1,197 @@
#include "BreakpointMenu.h"
#include "MenuBuilder.h"
#include <QAction>
#include <QMessageBox>
#include "StringUtil.h"
#include "MiscUtil.h"
#include "Breakpoints.h"
BreakpointMenu::BreakpointMenu(QWidget* parent, ActionHelperFuncs funcs, GetSelectionFunc getSelection)
: QObject(parent), ActionHelperProxy(funcs), mGetSelection(getSelection)
{
}
void BreakpointMenu::build(MenuBuilder* builder)
{
QAction* toggleBreakpointAction = makeShortcutAction(DIcon("breakpoint_toggle.png"), tr("Toggle"), std::bind(&BreakpointMenu::toggleInt3BPActionSlot, this), "ActionToggleBreakpoint");
QAction* editSoftwareBreakpointAction = makeShortcutAction(DIcon("breakpoint_edit_alt.png"), tr("Edit"), std::bind(&BreakpointMenu::editSoftBpActionSlot, this), "ActionEditBreakpoint");
QAction* setHwBreakpointAction = makeShortcutAction(DIcon("breakpoint_execute.png"), tr("Set Hardware on Execution"), std::bind(&BreakpointMenu::toggleHwBpActionSlot, this), "ActionSetHwBpE");
QAction* removeHwBreakpointAction = makeShortcutAction(DIcon("breakpoint_remove.png"), tr("Remove Hardware"), std::bind(&BreakpointMenu::toggleHwBpActionSlot, this), "ActionRemoveHwBp");
QMenu* replaceSlotMenu = makeMenu(DIcon("breakpoint_execute.png"), tr("Set Hardware on Execution"));
QAction* replaceSlot0Action = makeMenuAction(replaceSlotMenu, DIcon("breakpoint_execute_slot1.png"), tr("Replace Slot 0 (Free)"), std::bind(&BreakpointMenu::setHwBpOnSlot0ActionSlot, this));
QAction* replaceSlot1Action = makeMenuAction(replaceSlotMenu, DIcon("breakpoint_execute_slot2.png"), tr("Replace Slot 1 (Free)"), std::bind(&BreakpointMenu::setHwBpOnSlot1ActionSlot, this));
QAction* replaceSlot2Action = makeMenuAction(replaceSlotMenu, DIcon("breakpoint_execute_slot3.png"), tr("Replace Slot 2 (Free)"), std::bind(&BreakpointMenu::setHwBpOnSlot2ActionSlot, this));
QAction* replaceSlot3Action = makeMenuAction(replaceSlotMenu, DIcon("breakpoint_execute_slot4.png"), tr("Replace Slot 3 (Free)"), std::bind(&BreakpointMenu::setHwBpOnSlot3ActionSlot, this));
builder->addMenu(makeMenu(DIcon("breakpoint.png"), tr("Breakpoint")), [ = ](QMenu * menu)
{
BPXTYPE bpType = DbgGetBpxTypeAt(mGetSelection());
if((bpType & bp_normal) == bp_normal)
menu->addAction(editSoftwareBreakpointAction);
menu->addAction(toggleBreakpointAction);
if((bpType & bp_hardware) == bp_hardware)
{
menu->addAction(removeHwBreakpointAction);
}
else
{
BPMAP bpList;
DbgGetBpList(bp_hardware, &bpList);
//get enabled hwbp count
int enabledCount = bpList.count;
for(int i = 0; i < bpList.count; i++)
if(!bpList.bp[i].enabled)
enabledCount--;
if(enabledCount < 4)
{
menu->addAction(setHwBreakpointAction);
}
else
{
for(int i = 0; i < 4; i++)
{
switch(bpList.bp[i].slot)
{
case 0:
replaceSlot0Action->setText(tr("Replace Slot %1 (0x%2)").arg(1).arg(ToPtrString(bpList.bp[i].addr)));
break;
case 1:
replaceSlot1Action->setText(tr("Replace Slot %1 (0x%2)").arg(2).arg(ToPtrString(bpList.bp[i].addr)));
break;
case 2:
replaceSlot2Action->setText(tr("Replace Slot %1 (0x%2)").arg(3).arg(ToPtrString(bpList.bp[i].addr)));
break;
case 3:
replaceSlot3Action->setText(tr("Replace Slot %1 (0x%2)").arg(4).arg(ToPtrString(bpList.bp[i].addr)));
break;
default:
break;
}
}
menu->addMenu(replaceSlotMenu);
}
if(bpList.count)
BridgeFree(bpList.bp);
}
return true;
});
}
void BreakpointMenu::toggleInt3BPActionSlot()
{
if(!DbgIsDebugging())
return;
duint wVA = mGetSelection();
BPXTYPE wBpType = DbgGetBpxTypeAt(wVA);
QString wCmd;
if((wBpType & bp_normal) == bp_normal)
{
wCmd = "bc " + ToPtrString(wVA);
}
else
{
if(DbgFunctions()->IsDepEnabled() && !DbgFunctions()->MemIsCodePage(wVA, false))
{
QMessageBox msgyn(QMessageBox::Warning, tr("Current address is not executable"),
tr("Setting software breakpoint here may result in crash. Do you really want to continue?"), QMessageBox::Yes | QMessageBox::No, (QWidget*)parent());
msgyn.setWindowIcon(DIcon("compile-warning.png"));
msgyn.setParent((QWidget*)parent(), Qt::Dialog);
msgyn.setWindowFlags(msgyn.windowFlags() & (~Qt::WindowContextHelpButtonHint));
if(msgyn.exec() == QMessageBox::No)
return;
}
wCmd = "bp " + ToPtrString(wVA);
}
DbgCmdExec(wCmd.toUtf8().constData());
//emit Disassembly::repainted();
}
void BreakpointMenu::editSoftBpActionSlot()
{
Breakpoints::editBP(bp_normal, ToHexString(mGetSelection()), (QWidget*)parent());
}
void BreakpointMenu::toggleHwBpActionSlot()
{
duint wVA = mGetSelection();
BPXTYPE wBpType = DbgGetBpxTypeAt(wVA);
QString wCmd;
if((wBpType & bp_hardware) == bp_hardware)
{
wCmd = "bphwc " + ToPtrString(wVA);
}
else
{
wCmd = "bphws " + ToPtrString(wVA);
}
DbgCmdExec(wCmd.toUtf8().constData());
}
void BreakpointMenu::setHwBpOnSlot0ActionSlot()
{
setHwBpAt(mGetSelection(), 0);
}
void BreakpointMenu::setHwBpOnSlot1ActionSlot()
{
setHwBpAt(mGetSelection(), 1);
}
void BreakpointMenu::setHwBpOnSlot2ActionSlot()
{
setHwBpAt(mGetSelection(), 2);
}
void BreakpointMenu::setHwBpOnSlot3ActionSlot()
{
setHwBpAt(mGetSelection(), 3);
}
void BreakpointMenu::setHwBpAt(duint va, int slot)
{
int wI = 0;
int wSlotIndex = -1;
BPMAP wBPList;
QString wCmd = "";
DbgGetBpList(bp_hardware, &wBPList);
// Find index of slot slot in the list
for(wI = 0; wI < wBPList.count; wI++)
{
if(wBPList.bp[wI].slot == (unsigned short)slot)
{
wSlotIndex = wI;
break;
}
}
if(wSlotIndex < 0) // Slot not used
{
wCmd = "bphws " + ToPtrString(va);
DbgCmdExec(wCmd.toUtf8().constData());
}
else // Slot used
{
wCmd = "bphwc " + ToPtrString((duint)(wBPList.bp[wSlotIndex].addr));
DbgCmdExec(wCmd.toUtf8().constData());
Sleep(200);
wCmd = "bphws " + ToPtrString(va);
DbgCmdExec(wCmd.toUtf8().constData());
}
if(wBPList.count)
BridgeFree(wBPList.bp);
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <QObject>
#include <functional>
#include "ActionHelpers.h"
#include "Imports.h"
class BreakpointMenu : public QObject, public ActionHelperProxy
{
Q_OBJECT
public:
using GetSelectionFunc = std::function<duint()>;
explicit BreakpointMenu(QWidget* parent, ActionHelperFuncs funcs, GetSelectionFunc getSelection);
void build(MenuBuilder* builder);
public slots:
void toggleInt3BPActionSlot();
void editSoftBpActionSlot();
void toggleHwBpActionSlot();
void setHwBpOnSlot0ActionSlot();
void setHwBpOnSlot1ActionSlot();
void setHwBpOnSlot2ActionSlot();
void setHwBpOnSlot3ActionSlot();
void setHwBpAt(duint va, int slot);
private:
GetSelectionFunc mGetSelection;
};

View File

@ -25,6 +25,7 @@
#include "DataCopyDialog.h"
#include "SnowmanView.h"
#include "MemoryPage.h"
#include "BreakpointMenu.h"
CPUDisassembly::CPUDisassembly(CPUWidget* parent) : Disassembly(parent)
{
@ -95,7 +96,7 @@ void CPUDisassembly::mouseDoubleClickEvent(QMouseEvent* event)
// (Opcodes) Set INT3 breakpoint
case 1:
toggleInt3BPActionSlot();
mBreakpointMenu->toggleInt3BPActionSlot();
break;
// (Disassembly) Assemble dialog
@ -269,73 +270,11 @@ void CPUDisassembly::setupRightClickContextMenu()
return DbgFunctions()->PatchInRange(start, end); //something patched in selected range
});
QAction* toggleBreakpointAction = makeShortcutAction(DIcon("breakpoint_toggle.png"), tr("Toggle"), SLOT(toggleInt3BPActionSlot()), "ActionToggleBreakpoint");
QAction* editSoftwareBreakpointAction = makeShortcutAction(DIcon("breakpoint_edit_alt.png"), tr("Edit"), SLOT(editSoftBpActionSlot()), "ActionEditBreakpoint");
QAction* setHwBreakpointAction = makeShortcutAction(DIcon("breakpoint_execute.png"), tr("Set Hardware on Execution"), SLOT(toggleHwBpActionSlot()), "ActionSetHwBpE");
QAction* removeHwBreakpointAction = makeShortcutAction(DIcon("breakpoint_remove.png"), tr("Remove Hardware"), SLOT(toggleHwBpActionSlot()), "ActionRemoveHwBp");
QMenu* replaceSlotMenu = makeMenu(DIcon("breakpoint_execute.png"), tr("Set Hardware on Execution"));
QAction* replaceSlot0Action = makeMenuAction(replaceSlotMenu, DIcon("breakpoint_execute_slot1.png"), tr("Replace Slot 0 (Free)"), SLOT(setHwBpOnSlot0ActionSlot()));
QAction* replaceSlot1Action = makeMenuAction(replaceSlotMenu, DIcon("breakpoint_execute_slot2.png"), tr("Replace Slot 1 (Free)"), SLOT(setHwBpOnSlot1ActionSlot()));
QAction* replaceSlot2Action = makeMenuAction(replaceSlotMenu, DIcon("breakpoint_execute_slot3.png"), tr("Replace Slot 2 (Free)"), SLOT(setHwBpOnSlot2ActionSlot()));
QAction* replaceSlot3Action = makeMenuAction(replaceSlotMenu, DIcon("breakpoint_execute_slot4.png"), tr("Replace Slot 3 (Free)"), SLOT(setHwBpOnSlot3ActionSlot()));
mMenuBuilder->addMenu(makeMenu(DIcon("breakpoint.png"), tr("Breakpoint")), [ = ](QMenu * menu)
mBreakpointMenu = new BreakpointMenu(this, getActionHelperFuncs(), [this]()
{
BPXTYPE bpType = DbgGetBpxTypeAt(rvaToVa(getInitialSelection()));
if((bpType & bp_normal) == bp_normal)
menu->addAction(editSoftwareBreakpointAction);
menu->addAction(toggleBreakpointAction);
if((bpType & bp_hardware) == bp_hardware)
{
menu->addAction(removeHwBreakpointAction);
}
else
{
BPMAP bpList;
DbgGetBpList(bp_hardware, &bpList);
//get enabled hwbp count
int enabledCount = bpList.count;
for(int i = 0; i < bpList.count; i++)
if(!bpList.bp[i].enabled)
enabledCount--;
if(enabledCount < 4)
{
menu->addAction(setHwBreakpointAction);
}
else
{
for(int i = 0; i < 4; i++)
{
switch(bpList.bp[i].slot)
{
case 0:
replaceSlot0Action->setText(tr("Replace Slot %1 (0x%2)").arg(1).arg(ToPtrString(bpList.bp[i].addr)));
break;
case 1:
replaceSlot1Action->setText(tr("Replace Slot %1 (0x%2)").arg(2).arg(ToPtrString(bpList.bp[i].addr)));
break;
case 2:
replaceSlot2Action->setText(tr("Replace Slot %1 (0x%2)").arg(3).arg(ToPtrString(bpList.bp[i].addr)));
break;
case 3:
replaceSlot3Action->setText(tr("Replace Slot %1 (0x%2)").arg(4).arg(ToPtrString(bpList.bp[i].addr)));
break;
default:
break;
}
}
menu->addMenu(replaceSlotMenu);
}
if(bpList.count)
BridgeFree(bpList.bp);
}
return true;
return rvaToVa(getInitialSelection());
});
mBreakpointMenu->build(mMenuBuilder);
mMenuBuilder->addMenu(makeMenu(DIcon("dump.png"), tr("&Follow in Dump")), [this](QMenu * menu)
{
@ -706,114 +645,7 @@ void CPUDisassembly::gotoOriginSlot()
}
void CPUDisassembly::toggleInt3BPActionSlot()
{
if(!DbgIsDebugging())
return;
duint wVA = rvaToVa(getInitialSelection());
BPXTYPE wBpType = DbgGetBpxTypeAt(wVA);
QString wCmd;
if((wBpType & bp_normal) == bp_normal)
{
wCmd = "bc " + ToPtrString(wVA);
}
else
{
if(DbgFunctions()->IsDepEnabled() && !DbgFunctions()->MemIsCodePage(wVA, false))
{
QMessageBox msgyn(QMessageBox::Warning, tr("Current address is not executable"),
tr("Setting software breakpoint here may result in crash. Do you really want to continue?"), QMessageBox::Yes | QMessageBox::No, this);
msgyn.setWindowIcon(DIcon("compile-warning.png"));
msgyn.setParent(this, Qt::Dialog);
msgyn.setWindowFlags(msgyn.windowFlags() & (~Qt::WindowContextHelpButtonHint));
if(msgyn.exec() == QMessageBox::No)
return;
}
wCmd = "bp " + ToPtrString(wVA);
}
DbgCmdExec(wCmd.toUtf8().constData());
//emit Disassembly::repainted();
}
void CPUDisassembly::toggleHwBpActionSlot()
{
duint wVA = rvaToVa(getInitialSelection());
BPXTYPE wBpType = DbgGetBpxTypeAt(wVA);
QString wCmd;
if((wBpType & bp_hardware) == bp_hardware)
{
wCmd = "bphwc " + ToPtrString(wVA);
}
else
{
wCmd = "bphws " + ToPtrString(wVA);
}
DbgCmdExec(wCmd.toUtf8().constData());
}
void CPUDisassembly::setHwBpOnSlot0ActionSlot()
{
setHwBpAt(rvaToVa(getInitialSelection()), 0);
}
void CPUDisassembly::setHwBpOnSlot1ActionSlot()
{
setHwBpAt(rvaToVa(getInitialSelection()), 1);
}
void CPUDisassembly::setHwBpOnSlot2ActionSlot()
{
setHwBpAt(rvaToVa(getInitialSelection()), 2);
}
void CPUDisassembly::setHwBpOnSlot3ActionSlot()
{
setHwBpAt(rvaToVa(getInitialSelection()), 3);
}
void CPUDisassembly::setHwBpAt(duint va, int slot)
{
int wI = 0;
int wSlotIndex = -1;
BPMAP wBPList;
QString wCmd = "";
DbgGetBpList(bp_hardware, &wBPList);
// Find index of slot slot in the list
for(wI = 0; wI < wBPList.count; wI++)
{
if(wBPList.bp[wI].slot == (unsigned short)slot)
{
wSlotIndex = wI;
break;
}
}
if(wSlotIndex < 0) // Slot not used
{
wCmd = "bphws " + ToPtrString(va);
DbgCmdExec(wCmd.toUtf8().constData());
}
else // Slot used
{
wCmd = "bphwc " + ToPtrString((duint)(wBPList.bp[wSlotIndex].addr));
DbgCmdExec(wCmd.toUtf8().constData());
Sleep(200);
wCmd = "bphws " + ToPtrString(va);
DbgCmdExec(wCmd.toUtf8().constData());
}
if(wBPList.count)
BridgeFree(wBPList.bp);
}
void CPUDisassembly::setNewOriginHereActionSlot()
{
@ -1935,11 +1767,6 @@ void CPUDisassembly::labelHelpSlot()
}
}
void CPUDisassembly::editSoftBpActionSlot()
{
Breakpoints::editBP(bp_normal, ToHexString(rvaToVa(getInitialSelection())), this);
}
void CPUDisassembly::ActionTraceRecordBitSlot()
{
if(!DbgIsDebugging())

View File

@ -2,6 +2,7 @@
#define CPUDISASSEMBLY_H
#include "Disassembly.h"
#include "BreakpointMenu.h"
// Needed forward declaration for parent container class
class CPUWidget;
@ -24,7 +25,6 @@ public:
void setupRightClickContextMenu();
void addFollowReferenceMenuItem(QString name, dsint value, QMenu* menu, bool isReferences, bool isFollowInCPU);
void setupFollowReferenceMenu(dsint wVA, QMenu* menu, bool isReferences, bool isFollowInCPU);
void setHwBpAt(duint va, int slot);
void copySelectionSlot(bool copyBytes);
void copySelectionToFileSlot(bool copyBytes);
@ -37,12 +37,6 @@ signals:
void displayGraphWidget();
public slots:
void toggleInt3BPActionSlot();
void toggleHwBpActionSlot();
void setHwBpOnSlot0ActionSlot();
void setHwBpOnSlot1ActionSlot();
void setHwBpOnSlot2ActionSlot();
void setHwBpOnSlot3ActionSlot();
void setNewOriginHereActionSlot();
void gotoOriginSlot();
void setLabelSlot();
@ -105,7 +99,6 @@ public slots:
void ActionTraceRecordDisableSlot();
void displayWarningSlot(QString title, QString text);
void labelHelpSlot();
void editSoftBpActionSlot();
void analyzeSingleFunctionSlot();
void removeAnalysisSelectionSlot();
void removeAnalysisModuleSlot();
@ -174,6 +167,7 @@ private:
MenuBuilder* mMenuBuilder;
MenuBuilder* mHighlightMenuBuilder;
bool mHighlightContextMenu = false;
BreakpointMenu* mBreakpointMenu;
};
#endif // CPUDISASSEMBLY_H

View File

@ -14,6 +14,7 @@
#include <QMimeData>
#include <QFileDialog>
#include <QMessageBox>
#include "BreakpointMenu.h"
DisassemblerGraphView::DisassemblerGraphView(QWidget* parent)
: QAbstractScrollArea(parent),
@ -259,12 +260,34 @@ void DisassemblerGraphView::paintNormal(QPainter & p, QRect & viewportRect, int
{
for(auto & line : instr.text.lines)
{
if(instr.addr == mCip)
int rectSize = qRound(this->charWidth);
if(rectSize % 2)
rectSize++;
// Assume charWidth <= charHeight
QRectF bpRect(x - rectSize / 3.0, y + (this->charHeight - rectSize) / 2.0, rectSize, rectSize);
bool isbp = DbgGetBpxTypeAt(instr.addr) != bp_none;
bool isbpdisabled = DbgIsBpDisabled(instr.addr);
bool iscip = instr.addr == mCip;
if(isbp || isbpdisabled)
{
p.setPen(mCipColor);
p.fillRect(x, y, this->charWidth, this->charHeight, mCipBackgroundColor);
p.drawText(x, y, this->charWidth, this->charHeight, 0, QString("\xE2\x80\xA2"));
if(iscip)
{
// Left half is cip
bpRect.setWidth(bpRect.width() / 2);
p.fillRect(bpRect, mCipColor);
// Right half is breakpoint
bpRect.translate(bpRect.width(), 0);
}
p.fillRect(bpRect, isbp ? mBreakpointColor : mDisabledBreakpointColor);
}
else if(iscip)
p.fillRect(bpRect, mCipColor);
RichTextPainter::paintRichText(&p, x + this->charWidth, y, block.width - this->charWidth, this->charHeight, 0, line, mFontMetrics);
y += this->charHeight;
}
@ -358,7 +381,7 @@ void DisassemblerGraphView::paintOverview(QPainter & p, QRect & viewportRect, in
pen.setColor(graphNodeColor);
p.setPen(pen);
if(isCip)
p.setBrush(mCipBackgroundColor);
p.setBrush(mCipColor);
else if(traceCount)
{
// Color depending on how often a sequence of code is executed
@ -1625,6 +1648,11 @@ void DisassemblerGraphView::setupContextMenu()
});
mMenuBuilder->addSeparator();
auto breakpointMenu = new BreakpointMenu(this, getActionHelperFuncs(), [this]()
{
return cur_instr;
});
breakpointMenu->build(mMenuBuilder);
mMenuBuilder->addAction(makeShortcutAction(DIcon("comment.png"), tr("&Comment"), SLOT(setCommentSlot()), "ActionSetComment"));
mMenuBuilder->addAction(makeShortcutAction(DIcon("label.png"), tr("&Label"), SLOT(setLabelSlot()), "ActionSetLabel"));
MenuBuilder* gotoMenu = new MenuBuilder(this);
@ -1656,8 +1684,6 @@ void DisassemblerGraphView::setupContextMenu()
mediumLayout->setChecked(true);
mMenuBuilder->addMenu(makeMenu(DIcon("layout.png"), tr("Layout")), layoutMenu);
mMenuBuilder->addSeparator();
mMenuBuilder->loadFromConfig();
}
@ -1699,8 +1725,6 @@ void DisassemblerGraphView::colorsUpdatedSlot()
mCommentBackgroundColor = ConfigColor("DisassemblyCommentBackgroundColor");
mLabelColor = ConfigColor("DisassemblyLabelColor");
mLabelBackgroundColor = ConfigColor("DisassemblyLabelBackgroundColor");
mCipBackgroundColor = ConfigColor("DisassemblyCipBackgroundColor");
mCipColor = ConfigColor("DisassemblyCipColor");
mAddressColor = ConfigColor("DisassemblyAddressColor");
mAddressBackgroundColor = ConfigColor("DisassemblyAddressBackgroundColor");
@ -1712,6 +1736,9 @@ void DisassemblerGraphView::colorsUpdatedSlot()
backgroundColor = ConfigColor("GraphBackgroundColor");
if(!backgroundColor.alpha())
backgroundColor = disassemblySelectionColor;
mCipColor = ConfigColor("GraphCipColor");
mBreakpointColor = ConfigColor("GraphBreakpointColor");
mDisabledBreakpointColor = ConfigColor("GraphDisabledBreakpointColor");
fontChanged();
loadCurrentGraph();

View File

@ -332,11 +332,12 @@ private:
QColor mCommentBackgroundColor;
QColor mLabelColor;
QColor mLabelBackgroundColor;
QColor mCipColor;
QColor mCipBackgroundColor;
QColor graphNodeColor;
QColor mAddressColor;
QColor mAddressBackgroundColor;
QColor mCipColor;
QColor mBreakpointColor;
QColor mDisabledBreakpointColor;
BridgeCFGraph currentGraph;
std::unordered_map<duint, duint> currentBlockMap;

View File

@ -2,6 +2,35 @@
#define ACTIONHELPERS_H
#include <QAction>
#include <functional>
#include "Configuration.h"
//TODO: find the right "const &" "&", "&&" "" etc for passing around std::function
using SlotFunc = std::function<void()>;
using MakeMenuFunc1 = std::function<QMenu*(const QString &)>;
using MakeMenuFunc2 = std::function<QMenu*(const QIcon &, const QString &)>;
using MakeActionFunc1 = std::function<QAction*(const QString &, const SlotFunc &)>;
using MakeActionFunc2 = std::function<QAction*(const QIcon &, const QString &, const SlotFunc &)>;
using MakeShortcutActionFunc1 = std::function<QAction*(const QString &, const SlotFunc &, const char*)>;
using MakeShortcutActionFunc2 = std::function<QAction*(const QIcon &, const QString &, const SlotFunc &, const char*)>;
using MakeMenuActionFunc1 = std::function<QAction*(QMenu*, const QString &, const SlotFunc &)>;
using MakeMenuActionFunc2 = std::function<QAction*(QMenu*, const QIcon &, const QString &, const SlotFunc &)>;
using MakeShortcutMenuActionFunc1 = std::function<QAction*(QMenu*, const QString &, const SlotFunc &, const char*)>;
using MakeShortcutMenuActionFunc2 = std::function<QAction*(QMenu*, const QIcon &, const QString &, const SlotFunc &, const char*)>;
struct ActionHelperFuncs
{
MakeMenuFunc1 makeMenu1;
MakeMenuFunc2 makeMenu2;
MakeActionFunc1 makeAction1;
MakeActionFunc2 makeAction2;
MakeShortcutActionFunc1 makeShortcutAction1;
MakeShortcutActionFunc2 makeShortcutAction2;
MakeMenuActionFunc1 makeMenuAction1;
MakeMenuActionFunc2 makeMenuAction2;
MakeShortcutMenuActionFunc1 makeShortcutMenuAction1;
MakeShortcutMenuActionFunc2 makeShortcutMenuAction2;
};
template<class Base>
class ActionHelper
@ -32,7 +61,6 @@ public:
}
private:
inline QAction* connectAction(QAction* action, const char* slot)
{
QObject::connect(action, SIGNAL(triggered(bool)), getBase(), slot);
@ -42,6 +70,7 @@ private:
template<class T> // lambda or base member pointer
inline QAction* connectAction(QAction* action, T callback)
{
//in case of a lambda getBase() is used as the 'context' object and not the 'receiver'
QObject::connect(action, &QAction::triggered, getBase(), callback);
return action;
}
@ -60,7 +89,53 @@ private:
menu->addAction(action);
return action;
}
protected:
inline ActionHelperFuncs getActionHelperFuncs()
{
ActionHelperFuncs funcs;
funcs.makeMenu1 = [this](const QString & title)
{
return makeMenu(title);
};
funcs.makeMenu2 = [this](const QIcon & icon, const QString & title)
{
return makeMenu(icon, title);
};
funcs.makeAction1 = [this](const QString & text, const SlotFunc & slot)
{
return makeAction(text, slot);
};
funcs.makeAction2 = [this](const QIcon & icon, const QString & text, const SlotFunc & slot)
{
return makeAction(icon, text, slot);
};
funcs.makeShortcutAction1 = [this](const QString & text, const SlotFunc & slot, const char* shortcut)
{
return makeShortcutAction(text, slot, shortcut);
};
funcs.makeShortcutAction2 = [this](const QIcon & icon, const QString & text, const SlotFunc & slot, const char* shortcut)
{
return makeShortcutAction(icon, text, slot, shortcut);
};
funcs.makeMenuAction1 = [this](QMenu * menu, const QString & text, const SlotFunc & slot)
{
return makeMenuAction(menu, text, slot);
};
funcs.makeMenuAction2 = [this](QMenu * menu, const QIcon & icon, const QString & text, const SlotFunc & slot)
{
return makeMenuAction(menu, icon, text, slot);
};
funcs.makeShortcutMenuAction1 = [this](QMenu * menu, const QString & text, const SlotFunc & slot, const char* shortcut)
{
return makeShortcutMenuAction(menu, text, slot, shortcut);
};
funcs.makeShortcutMenuAction2 = [this](QMenu * menu, const QIcon & icon, const QString & text, const SlotFunc & slot, const char* shortcut)
{
return makeShortcutMenuAction(menu, icon, text, slot, shortcut);
};
return funcs;
}
inline QMenu* makeMenu(const QString & title)
{
@ -126,4 +201,66 @@ private:
std::vector<ActionShortcut> actionShortcutPairs;
};
class MenuBuilder;
class ActionHelperProxy
{
ActionHelperFuncs funcs;
public:
ActionHelperProxy(ActionHelperFuncs funcs)
: funcs(funcs) { }
protected:
inline QMenu* makeMenu(const QString & title)
{
return funcs.makeMenu1(title);
}
inline QMenu* makeMenu(const QIcon & icon, const QString & title)
{
return funcs.makeMenu2(icon, title);
}
inline QAction* makeAction(const QString & text, const SlotFunc & slot)
{
return funcs.makeAction1(text, slot);
}
inline QAction* makeAction(const QIcon & icon, const QString & text, const SlotFunc & slot)
{
return funcs.makeAction2(icon, text, slot);
}
inline QAction* makeShortcutAction(const QString & text, const SlotFunc & slot, const char* shortcut)
{
return funcs.makeShortcutAction1(text, slot, shortcut);
}
inline QAction* makeShortcutAction(const QIcon & icon, const QString & text, const SlotFunc & slot, const char* shortcut)
{
return funcs.makeShortcutAction2(icon, text, slot, shortcut);
}
inline QAction* makeMenuAction(QMenu* menu, const QString & text, const SlotFunc & slot)
{
return funcs.makeMenuAction1(menu, text, slot);
}
inline QAction* makeMenuAction(QMenu* menu, const QIcon & icon, const QString & text, const SlotFunc & slot)
{
return funcs.makeMenuAction2(menu, icon, text, slot);
}
inline QAction* makeShortcutMenuAction(QMenu* menu, const QString & text, const SlotFunc & slot, const char* shortcut)
{
return funcs.makeShortcutMenuAction1(menu, text, slot, shortcut);
}
inline QAction* makeShortcutMenuAction(QMenu* menu, const QIcon & icon, const QString & text, const SlotFunc & slot, const char* shortcut)
{
return funcs.makeShortcutMenuAction2(menu, icon, text, slot, shortcut);
}
};
#endif

View File

@ -218,6 +218,9 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
defaultColors.insert("GraphBackgroundColor", Qt::transparent);
defaultColors.insert("GraphNodeColor", QColor("#000000"));
defaultColors.insert("GraphNodeBackgroundColor", Qt::transparent);
defaultColors.insert("GraphCipColor", QColor("#000000"));
defaultColors.insert("GraphBreakpointColor", QColor("#FF0000"));
defaultColors.insert("GraphDisabledBreakpointColor", QColor("#00AA00"));
defaultColors.insert("ThreadCurrentColor", QColor("#FFFFFF"));
defaultColors.insert("ThreadCurrentBackgroundColor", QColor("#000000"));

View File

@ -178,7 +178,8 @@ SOURCES += \
Src/Utils/MRUList.cpp \
Src/Gui/LocalVarsView.cpp \
Src/Gui/MessagesBreakpoints.cpp \
Src/Gui/AboutDialog.cpp
Src/Gui/AboutDialog.cpp \
Src/Gui/BreakpointMenu.cpp
HEADERS += \
@ -291,7 +292,8 @@ HEADERS += \
Src/Utils/MRUList.h \
Src/Gui/LocalVarsView.h \
Src/Gui/MessagesBreakpoints.h \
Src/Gui/AboutDialog.h
Src/Gui/AboutDialog.h \
Src/Gui/BreakpointMenu.h
FORMS += \

View File

@ -218,7 +218,8 @@ SOURCES += \
gui/Src/Utils/MRUList.cpp \
gui/Src/Gui/LocalVarsView.cpp \
gui/Src/Gui/MessagesBreakpoints.cpp \
gui/Src/Gui/AboutDialog.cpp
gui/Src/Gui/AboutDialog.cpp \
gui/Src/Gui/BreakpointMenu.cpp
HEADERS += \
gui/Src/Exports.h \
@ -447,7 +448,8 @@ HEADERS += \
gui/Src/Utils/MRUList.h \
gui/Src/Gui/LocalVarsView.h \
gui/Src/Gui/MessagesBreakpoints.h \
gui/Src/Gui/AboutDialog.h
gui/Src/Gui/AboutDialog.h \
gui/Src/Gui/BreakpointMenu.h
FORMS += \
gui/Src/Gui/AppearanceDialog.ui \