Global variable actions and Show in action and refactoring (#2295)

* Show in action for global variables and functions
* Copy address of global variable or function referenced by the cursor selection
* Rename global variable
This commit is contained in:
NIRMAL MANOJ C 2020-07-30 14:11:23 +05:30
parent eef9baa300
commit 37fc01478f
6 changed files with 229 additions and 32 deletions

View File

@ -927,12 +927,19 @@ void MainWindow::showMemoryWidget(MemoryWidgetType type)
memoryDockWidget->raiseMemoryWidget(); memoryDockWidget->raiseMemoryWidget();
} }
QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address) QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address, AddressTypeHint addressType)
{ {
QMenu *menu = new QMenu(parent); QMenu *menu = new QMenu(parent);
// Memory dock widgets // Memory dock widgets
for (auto &dock : dockWidgets) { for (auto &dock : dockWidgets) {
if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) { if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {
if (memoryWidget->getType() == MemoryWidgetType::Graph
|| memoryWidget->getType() == MemoryWidgetType::Decompiler)
{
if (addressType == AddressTypeHint::Data) {
continue;
}
}
QAction *action = new QAction(memoryWidget->windowTitle(), menu); QAction *action = new QAction(memoryWidget->windowTitle(), menu);
connect(action, &QAction::triggered, this, [memoryWidget, address]() { connect(action, &QAction::triggered, this, [memoryWidget, address]() {
memoryWidget->getSeekable()->seek(address); memoryWidget->getSeekable()->seek(address);
@ -965,7 +972,9 @@ QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address)
menu->addAction(action); menu->addAction(action);
}; };
createAddNewWidgetAction(tr("New disassembly"), MemoryWidgetType::Disassembly); createAddNewWidgetAction(tr("New disassembly"), MemoryWidgetType::Disassembly);
if (addressType != AddressTypeHint::Data) {
createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph); createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph);
}
createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump); createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump);
return menu; return menu;

View File

@ -121,8 +121,8 @@ public:
QString getUniqueObjectName(const QString &widgetType) const; QString getUniqueObjectName(const QString &widgetType) const;
void showMemoryWidget(); void showMemoryWidget();
void showMemoryWidget(MemoryWidgetType type); void showMemoryWidget(MemoryWidgetType type);
enum class AddressTypeHint { Function, Data, Unknown };
QMenu *createShowInMenu(QWidget *parent, RVA address); QMenu *createShowInMenu(QWidget *parent, RVA address, AddressTypeHint addressType = AddressTypeHint::Unknown);
void setCurrentMemoryWidget(MemoryDockWidget* memoryWidget); void setCurrentMemoryWidget(MemoryDockWidget* memoryWidget);
MemoryDockWidget* getLastMemoryWidget(); MemoryDockWidget* getLastMemoryWidget();

View File

@ -3,6 +3,7 @@
#include "MainWindow.h" #include "MainWindow.h"
#include "dialogs/BreakpointsDialog.h" #include "dialogs/BreakpointsDialog.h"
#include "dialogs/CommentsDialog.h" #include "dialogs/CommentsDialog.h"
#include "common/Configuration.h"
#include <QtCore> #include <QtCore>
#include <QShortcut> #include <QShortcut>
@ -14,27 +15,36 @@
DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow) DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow)
: QMenu(parent), : QMenu(parent),
curHighlightedWord(QString()),
offset(0), offset(0),
isTogglingBreakpoints(false), isTogglingBreakpoints(false),
mainWindow(mainWindow), mainWindow(mainWindow),
annotationHere(nullptr), annotationHere(nullptr),
actionCopy(tr("Copy"), this), actionCopy(tr("Copy"), this),
actionCopyInstructionAddress(tr("Copy instruction address (<address>)"), this),
actionCopyReferenceAddress(tr("Copy address of [flag] (<address>)"), this),
actionShowInSubmenu(tr("Show in"), this),
actionAddComment(tr("Add Comment"), this), actionAddComment(tr("Add Comment"), this),
actionDeleteComment(tr("Delete comment"), this), actionDeleteComment(tr("Delete comment"), this),
actionRenameThingHere(tr("Rename function at cursor"), this), actionRenameThingHere(tr("Rename function at cursor"), this),
actionDeleteName(tr("Delete <name>"), this),
actionToggleBreakpoint(tr("Add/remove breakpoint"), this), actionToggleBreakpoint(tr("Add/remove breakpoint"), this),
actionAdvancedBreakpoint(tr("Advanced breakpoint"), this), actionAdvancedBreakpoint(tr("Advanced breakpoint"), this),
breakpointsInLineMenu(new QMenu(this)), breakpointsInLineMenu(new QMenu(this)),
actionContinueUntil(tr("Continue until line"), this), actionContinueUntil(tr("Continue until line"), this),
actionSetPC(tr("Set PC"), this) actionSetPC(tr("Set PC"), this)
{ {
setActionCopy(); setActionCopy(); // Sets all three copy actions
addSeparator(); addSeparator();
setActionShowInSubmenu();
copySeparator = addSeparator();
setActionAddComment(); setActionAddComment();
setActionDeleteComment(); setActionDeleteComment();
setActionRenameThingHere(); setActionRenameThingHere();
setActionDeleteName();
addSeparator(); addSeparator();
addBreakpointMenu(); addBreakpointMenu();
@ -57,6 +67,11 @@ void DecompilerContextMenu::setAnnotationHere(RCodeAnnotation *annotation)
this->annotationHere = annotation; this->annotationHere = annotation;
} }
void DecompilerContextMenu::setCurHighlightedWord(QString word)
{
this->curHighlightedWord = word;
}
void DecompilerContextMenu::setOffset(RVA offset) void DecompilerContextMenu::setOffset(RVA offset)
{ {
this->offset = offset; this->offset = offset;
@ -86,11 +101,6 @@ void DecompilerContextMenu::setupBreakpointsInLineMenu()
} }
} }
void DecompilerContextMenu::setCanCopy(bool enabled)
{
actionCopy.setVisible(enabled);
}
void DecompilerContextMenu::setShortcutContextInActions(QMenu *menu) void DecompilerContextMenu::setShortcutContextInActions(QMenu *menu)
{ {
for (QAction *action : menu->actions()) { for (QAction *action : menu->actions()) {
@ -117,11 +127,14 @@ bool DecompilerContextMenu::getIsTogglingBreakpoints()
void DecompilerContextMenu::aboutToHideSlot() void DecompilerContextMenu::aboutToHideSlot()
{ {
actionAddComment.setVisible(true); actionAddComment.setVisible(true);
actionRenameThingHere.setVisible(true);
actionDeleteName.setVisible(false);
} }
void DecompilerContextMenu::aboutToShowSlot() void DecompilerContextMenu::aboutToShowSlot()
{ {
if (this->firstOffsetInLine != RVA_MAX) { if (this->firstOffsetInLine != RVA_MAX) {
actionShowInSubmenu.setVisible(true);
QString comment = Core()->cmdRawAt("CC.", this->firstOffsetInLine); QString comment = Core()->cmdRawAt("CC.", this->firstOffsetInLine);
actionAddComment.setVisible(true); actionAddComment.setVisible(true);
if (comment.isEmpty()) { if (comment.isEmpty()) {
@ -132,6 +145,7 @@ void DecompilerContextMenu::aboutToShowSlot()
actionAddComment.setText(tr("Edit Comment")); actionAddComment.setText(tr("Edit Comment"));
} }
} else { } else {
actionShowInSubmenu.setVisible(false);
actionAddComment.setVisible(false); actionAddComment.setVisible(false);
actionDeleteComment.setVisible(false); actionDeleteComment.setVisible(false);
} }
@ -163,22 +177,82 @@ void DecompilerContextMenu::aboutToShowSlot()
QString progCounterName = Core()->getRegisterName("PC").toUpper(); QString progCounterName = Core()->getRegisterName("PC").toUpper();
actionSetPC.setText(tr("Set %1 here").arg(progCounterName)); actionSetPC.setText(tr("Set %1 here").arg(progCounterName));
if (!annotationHere) { // To be considered as invalid if (!annotationHere
|| annotationHere->type ==
R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) { // To be considered as invalid
actionRenameThingHere.setVisible(false); actionRenameThingHere.setVisible(false);
copySeparator->setVisible(false);
} else { } else {
copySeparator->setVisible(true);
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
actionRenameThingHere.setVisible(true); actionRenameThingHere.setVisible(true);
actionRenameThingHere.setText(tr("Rename function %1").arg(QString( actionRenameThingHere.setText(tr("Rename function %1").arg(QString(
annotationHere->function_name.name))); annotationHere->reference.name)));
} }
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) {
RFlagItem *flagDetails = r_flag_get_i(Core()->core()->flags, annotationHere->reference.offset);
if (flagDetails) {
actionRenameThingHere.setText(tr("Rename %1").arg(QString(flagDetails->name)));
actionDeleteName.setText(tr("Remove %1").arg(QString(flagDetails->name)));
actionDeleteName.setVisible(true);
} else {
actionRenameThingHere.setText(tr("Add name to %1").arg(curHighlightedWord));
}
}
}
actionCopyInstructionAddress.setText(tr("Copy instruction address (%1)").arg(RAddressString(
offset)));
bool isReference = false;
if (annotationHere) {
isReference = (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME);
}
if (isReference) {
actionCopyReferenceAddress.setVisible(true);
RVA referenceAddr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(Core()->core()->flags, referenceAddr);
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
actionCopyReferenceAddress.setText(tr("Copy address of %1 (%2)").arg
(QString(annotationHere->reference.name), RAddressString(referenceAddr)));
} else if (flagDetails) {
actionCopyReferenceAddress.setText(tr("Copy address of %1 (%2)").arg
(flagDetails->name, RAddressString(referenceAddr)));
} else {
actionCopyReferenceAddress.setText(tr("Copy address (%1)").arg(RAddressString(referenceAddr)));
}
} else {
actionCopyReferenceAddress.setVisible(false);
}
if (actionShowInSubmenu.menu() != nullptr) {
actionShowInSubmenu.menu()->deleteLater();
}
actionShowInSubmenu.setMenu(mainWindow->createShowInMenu(this, offset));
updateTargetMenuActions();
} }
// Set up actions // Set up actions
void DecompilerContextMenu::setActionCopy()
void DecompilerContextMenu::setActionCopy() // Set all three copy actions
{ {
connect(&actionCopy, &QAction::triggered, this, &DecompilerContextMenu::actionCopyTriggered); connect(&actionCopy, &QAction::triggered, this, &DecompilerContextMenu::actionCopyTriggered);
addAction(&actionCopy); addAction(&actionCopy);
actionCopy.setShortcut(QKeySequence::Copy); actionCopy.setShortcut(QKeySequence::Copy);
connect(&actionCopyInstructionAddress, &QAction::triggered, this,
&DecompilerContextMenu::actionCopyInstructionAddressTriggered);
addAction(&actionCopyInstructionAddress);
connect(&actionCopyReferenceAddress, &QAction::triggered, this,
&DecompilerContextMenu::actionCopyReferenceAddressTriggered);
addAction(&actionCopyReferenceAddress);
actionCopyReferenceAddress.setShortcut({Qt::CTRL + Qt::SHIFT + Qt::Key_C});
}
void DecompilerContextMenu::setActionShowInSubmenu()
{
addAction(&actionShowInSubmenu);
} }
void DecompilerContextMenu::setActionAddComment() void DecompilerContextMenu::setActionAddComment()
@ -198,12 +272,20 @@ void DecompilerContextMenu::setActionDeleteComment()
void DecompilerContextMenu::setActionRenameThingHere() void DecompilerContextMenu::setActionRenameThingHere()
{ {
actionRenameThingHere.setShortcut({Qt::SHIFT + Qt::Key_N}); actionRenameThingHere.setShortcut({Qt::Key_N});
connect(&actionRenameThingHere, &QAction::triggered, this, connect(&actionRenameThingHere, &QAction::triggered, this,
&DecompilerContextMenu::actionRenameThingHereTriggered); &DecompilerContextMenu::actionRenameThingHereTriggered);
addAction(&actionRenameThingHere); addAction(&actionRenameThingHere);
} }
void DecompilerContextMenu::setActionDeleteName()
{
connect(&actionDeleteName, &QAction::triggered, this,
&DecompilerContextMenu::actionDeleteNameTriggered);
addAction(&actionDeleteName);
actionDeleteName.setVisible(false);
}
void DecompilerContextMenu::setActionToggleBreakpoint() void DecompilerContextMenu::setActionToggleBreakpoint()
{ {
connect(&actionToggleBreakpoint, &QAction::triggered, this, connect(&actionToggleBreakpoint, &QAction::triggered, this,
@ -236,6 +318,18 @@ void DecompilerContextMenu::actionCopyTriggered()
emit copy(); emit copy();
} }
void DecompilerContextMenu::actionCopyInstructionAddressTriggered()
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(RAddressString(offset));
}
void DecompilerContextMenu::actionCopyReferenceAddressTriggered()
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(RAddressString(annotationHere->reference.offset));
}
void DecompilerContextMenu::actionAddCommentTriggered() void DecompilerContextMenu::actionAddCommentTriggered()
{ {
CommentsDialog::addOrEditComment(this->firstOffsetInLine, this); CommentsDialog::addOrEditComment(this->firstOffsetInLine, this);
@ -251,14 +345,16 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
if (!annotationHere) { if (!annotationHere) {
return; return;
} }
RCoreLocked core = Core()->core();
bool ok; bool ok;
auto type = annotationHere->type; auto type = annotationHere->type;
if (type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) { if (type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
QString currentName(annotationHere->function_name.name); QString currentName(annotationHere->reference.name);
RVA func_addr = annotationHere->function_name.offset; RVA func_addr = annotationHere->reference.offset;
RAnalFunction *func = Core()->functionAt(func_addr); RAnalFunction *func = Core()->functionAt(func_addr);
if (func == NULL) { if (func == NULL) {
QString function_name = QInputDialog::getText(this, tr("Define this function at %2").arg(RAddressString(func_addr)), QString function_name = QInputDialog::getText(this,
tr("Define this function at %2").arg(RAddressString(func_addr)),
tr("Function name:"), QLineEdit::Normal, currentName, &ok); tr("Function name:"), QLineEdit::Normal, currentName, &ok);
if (ok && !function_name.isEmpty()) { if (ok && !function_name.isEmpty()) {
Core()->createFunctionAt(func_addr, function_name); Core()->createFunctionAt(func_addr, function_name);
@ -271,7 +367,29 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
} }
} }
} else if (type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) {
RVA var_addr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(core->flags, var_addr);
if (flagDetails) {
QString newName = QInputDialog::getText(this, tr("Rename %2").arg(flagDetails->name),
tr("Enter name"), QLineEdit::Normal, flagDetails->name, &ok);
if (ok && !newName.isEmpty()) {
Core()->renameFlag(flagDetails->name, newName);
} }
} else {
QString newName = QInputDialog::getText(this, tr("Add name to %2").arg(curHighlightedWord),
tr("Enter name"), QLineEdit::Normal, curHighlightedWord, &ok);
if (ok && !newName.isEmpty()) {
Core()->addFlag(var_addr, newName, 1);
}
}
}
}
void DecompilerContextMenu::actionDeleteNameTriggered()
{
Core()->delFlag(annotationHere->reference.offset);
} }
void DecompilerContextMenu::actionToggleBreakpointTriggered() void DecompilerContextMenu::actionToggleBreakpointTriggered()
@ -335,3 +453,43 @@ void DecompilerContextMenu::addDebugMenu()
setActionSetPC(); setActionSetPC();
debugMenu->addAction(&actionSetPC); debugMenu->addAction(&actionSetPC);
} }
void DecompilerContextMenu::updateTargetMenuActions()
{
for (auto action : showTargetMenuActions) {
removeAction(action);
auto menu = action->menu();
if (menu) {
menu->deleteLater();
}
action->deleteLater();
}
showTargetMenuActions.clear();
RCoreLocked core = Core()->core();
if (annotationHere && (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME)) {
QString name;
QMenu *menu;
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) {
menu = mainWindow->createShowInMenu(this, annotationHere->reference.offset, MainWindow::AddressTypeHint::Data);
RVA var_addr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(core->flags, var_addr);
if (flagDetails) {
name = tr("Show %1 in").arg(flagDetails->name);
} else {
name = tr("Show %1 in").arg(RAddressString(annotationHere->reference.offset));
}
} else if (annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
menu = mainWindow->createShowInMenu(this, annotationHere->reference.offset,
MainWindow::AddressTypeHint::Function);
name = tr("%1 (%2)").arg(QString(annotationHere->reference.name),
RAddressString(annotationHere->reference.offset));
}
auto action = new QAction(name, this);
showTargetMenuActions.append(action);
action->setMenu(menu);
insertActions(copySeparator, showTargetMenuActions);
}
}

View File

@ -22,8 +22,8 @@ signals:
void copy(); void copy();
public slots: public slots:
void setCurHighlightedWord(QString word);
void setOffset(RVA offset); void setOffset(RVA offset);
void setCanCopy(bool enabled);
void setFirstOffsetInLine(RVA firstOffset); void setFirstOffsetInLine(RVA firstOffset);
void setAvailableBreakpoints(QVector<RVA> offsetList); void setAvailableBreakpoints(QVector<RVA> offsetList);
@ -33,11 +33,14 @@ private slots:
void aboutToHideSlot(); void aboutToHideSlot();
void actionCopyTriggered(); void actionCopyTriggered();
void actionCopyInstructionAddressTriggered();
void actionCopyReferenceAddressTriggered();
void actionAddCommentTriggered(); void actionAddCommentTriggered();
void actionDeleteCommentTriggered(); void actionDeleteCommentTriggered();
void actionRenameThingHereTriggered(); void actionRenameThingHereTriggered();
void actionDeleteNameTriggered();
void actionToggleBreakpointTriggered(); void actionToggleBreakpointTriggered();
void actionAdvancedBreakpointTriggered(); void actionAdvancedBreakpointTriggered();
@ -47,6 +50,7 @@ private slots:
private: private:
// Private variables // Private variables
QString curHighlightedWord;
RVA offset; RVA offset;
RVA firstOffsetInLine; RVA firstOffsetInLine;
bool isTogglingBreakpoints; bool isTogglingBreakpoints;
@ -56,12 +60,18 @@ private:
RCodeAnnotation *annotationHere; RCodeAnnotation *annotationHere;
QAction actionCopy; QAction actionCopy;
QAction actionCopyInstructionAddress;
QAction actionCopyReferenceAddress;
QAction *copySeparator; QAction *copySeparator;
QAction actionShowInSubmenu;
QList<QAction *> showTargetMenuActions;
QAction actionAddComment; QAction actionAddComment;
QAction actionDeleteComment; QAction actionDeleteComment;
QAction actionRenameThingHere; QAction actionRenameThingHere;
QAction actionDeleteName;
QMenu *breakpointMenu; QMenu *breakpointMenu;
QAction actionToggleBreakpoint; QAction actionToggleBreakpoint;
@ -81,10 +91,13 @@ private:
// Set actions // Set actions
void setActionCopy(); void setActionCopy();
void setActionShowInSubmenu();
void setActionAddComment(); void setActionAddComment();
void setActionDeleteComment(); void setActionDeleteComment();
void setActionRenameThingHere(); void setActionRenameThingHere();
void setActionDeleteName();
void setActionToggleBreakpoint(); void setActionToggleBreakpoint();
void setActionAdvancedBreakpoint(); void setActionAdvancedBreakpoint();
@ -113,6 +126,7 @@ private:
// QVector<ThingUsedHere> getThingUsedHere(RVA offset); // QVector<ThingUsedHere> getThingUsedHere(RVA offset);
// void updateTargetMenuActions(const QVector<ThingUsedHere> &targets); // void updateTargetMenuActions(const QVector<ThingUsedHere> &targets);
void updateTargetMenuActions();
}; };
#endif // DECOMPILERCONTEXTMENU_H #endif // DECOMPILERCONTEXTMENU_H

View File

@ -12,6 +12,7 @@
#include <QTextEdit> #include <QTextEdit>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QTextBlock> #include <QTextBlock>
#include <QClipboard>
#include <QObject> #include <QObject>
#include <QTextBlockUserData> #include <QTextBlockUserData>
@ -35,7 +36,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(Config(), &Configuration::fontsUpdated, this, &DecompilerWidget::fontsUpdatedSlot); connect(Config(), &Configuration::fontsUpdated, this, &DecompilerWidget::fontsUpdatedSlot);
connect(Config(), &Configuration::colorsUpdated, this, &DecompilerWidget::colorsUpdatedSlot); connect(Config(), &Configuration::colorsUpdated, this, &DecompilerWidget::colorsUpdatedSlot);
connect(Core(), &CutterCore::registersChanged, this, &DecompilerWidget::highlightPC); connect(Core(), &CutterCore::registersChanged, this, &DecompilerWidget::highlightPC);
connect(mCtxMenu, &DecompilerContextMenu::copy, ui->textEdit, &QPlainTextEdit::copy); connect(mCtxMenu, &DecompilerContextMenu::copy, this, &DecompilerWidget::copy);
decompiledFunctionAddr = RVA_INVALID; decompiledFunctionAddr = RVA_INVALID;
decompilerWasBusy = false; decompilerWasBusy = false;
@ -87,8 +88,6 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(ui->textEdit, &QWidget::customContextMenuRequested, connect(ui->textEdit, &QWidget::customContextMenuRequested,
this, &DecompilerWidget::showDisasContextMenu); this, &DecompilerWidget::showDisasContextMenu);
// refresh the widget when an action in this menu is triggered
connect(mCtxMenu, &QMenu::triggered, this, &DecompilerWidget::refreshDecompiler);
connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::setInfoForBreakpoints); connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::setInfoForBreakpoints);
addActions(mCtxMenu->actions()); addActions(mCtxMenu->actions());
@ -344,7 +343,6 @@ void DecompilerWidget::connectCursorPositionChanged(bool disconnect)
void DecompilerWidget::cursorPositionChanged() void DecompilerWidget::cursorPositionChanged()
{ {
mCtxMenu->setCanCopy(ui->textEdit->textCursor().hasSelection());
// Do not perform seeks along with the cursor while selecting multiple lines // Do not perform seeks along with the cursor while selecting multiple lines
if (!ui->textEdit->textCursor().selectedText().isEmpty()) { if (!ui->textEdit->textCursor().selectedText().isEmpty()) {
return; return;
@ -414,6 +412,7 @@ void DecompilerWidget::updateSelection()
// Highlight all the words in the document same as the current one // Highlight all the words in the document same as the current one
cursor.select(QTextCursor::WordUnderCursor); cursor.select(QTextCursor::WordUnderCursor);
QString searchString = cursor.selectedText(); QString searchString = cursor.selectedText();
mCtxMenu->setCurHighlightedWord(searchString);
extraSelections.append(createSameWordsSelections(ui->textEdit, searchString)); extraSelections.append(createSameWordsSelections(ui->textEdit, searchString));
ui->textEdit->setExtraSelections(extraSelections); ui->textEdit->setExtraSelections(extraSelections);
@ -438,7 +437,6 @@ void DecompilerWidget::colorsUpdatedSlot()
void DecompilerWidget::showDisasContextMenu(const QPoint &pt) void DecompilerWidget::showDisasContextMenu(const QPoint &pt)
{ {
mCtxMenu->exec(ui->textEdit->mapToGlobal(pt)); mCtxMenu->exec(ui->textEdit->mapToGlobal(pt));
doRefresh();
} }
void DecompilerWidget::seekToReference() void DecompilerWidget::seekToReference()
@ -460,7 +458,7 @@ bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event)
if (event->type() == QEvent::MouseButtonPress if (event->type() == QEvent::MouseButtonPress
&& (obj == ui->textEdit || obj == ui->textEdit->viewport())) { && (obj == ui->textEdit || obj == ui->textEdit->viewport())) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::RightButton) { if (mouseEvent->button() == Qt::RightButton && !ui->textEdit->textCursor().hasSelection()) {
ui->textEdit->setTextCursor(ui->textEdit->cursorForPosition(mouseEvent->pos())); ui->textEdit->setTextCursor(ui->textEdit->cursorForPosition(mouseEvent->pos()));
return true; return true;
} }
@ -509,3 +507,20 @@ bool DecompilerWidget::colorLine(QTextEdit::ExtraSelection extraSelection)
ui->textEdit->setExtraSelections(extraSelections); ui->textEdit->setExtraSelections(extraSelections);
return true; return true;
} }
void DecompilerWidget::copy()
{
if (ui->textEdit->textCursor().hasSelection()) {
ui->textEdit->copy();
} else {
QTextCursor cursor = ui->textEdit->textCursor();
QClipboard *clipboard = QApplication::clipboard();
cursor.select(QTextCursor::WordUnderCursor);
if (!cursor.selectedText().isEmpty()) {
clipboard->setText(cursor.selectedText());
} else {
cursor.select(QTextCursor::LineUnderCursor);
clipboard->setText(cursor.selectedText());
}
}
}

View File

@ -32,6 +32,7 @@ public slots:
void highlightPC(); void highlightPC();
private slots: private slots:
void copy();
void fontsUpdatedSlot(); void fontsUpdatedSlot();
void colorsUpdatedSlot(); void colorsUpdatedSlot();
void refreshDecompiler(); void refreshDecompiler();