diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index ebfb08c8..082add7d 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -927,12 +927,19 @@ void MainWindow::showMemoryWidget(MemoryWidgetType type) memoryDockWidget->raiseMemoryWidget(); } -QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address) +QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address, AddressTypeHint addressType) { QMenu *menu = new QMenu(parent); // Memory dock widgets for (auto &dock : dockWidgets) { if (auto memoryWidget = qobject_cast(dock)) { + if (memoryWidget->getType() == MemoryWidgetType::Graph + || memoryWidget->getType() == MemoryWidgetType::Decompiler) + { + if (addressType == AddressTypeHint::Data) { + continue; + } + } QAction *action = new QAction(memoryWidget->windowTitle(), menu); connect(action, &QAction::triggered, this, [memoryWidget, address]() { memoryWidget->getSeekable()->seek(address); @@ -965,7 +972,9 @@ QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address) menu->addAction(action); }; createAddNewWidgetAction(tr("New disassembly"), MemoryWidgetType::Disassembly); - createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph); + if (addressType != AddressTypeHint::Data) { + createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph); + } createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump); return menu; diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h index f5aeb0d7..4919b957 100644 --- a/src/core/MainWindow.h +++ b/src/core/MainWindow.h @@ -121,8 +121,8 @@ public: QString getUniqueObjectName(const QString &widgetType) const; void showMemoryWidget(); void showMemoryWidget(MemoryWidgetType type); - - QMenu *createShowInMenu(QWidget *parent, RVA address); + enum class AddressTypeHint { Function, Data, Unknown }; + QMenu *createShowInMenu(QWidget *parent, RVA address, AddressTypeHint addressType = AddressTypeHint::Unknown); void setCurrentMemoryWidget(MemoryDockWidget* memoryWidget); MemoryDockWidget* getLastMemoryWidget(); diff --git a/src/menus/DecompilerContextMenu.cpp b/src/menus/DecompilerContextMenu.cpp index 6afb74e1..ff1da195 100644 --- a/src/menus/DecompilerContextMenu.cpp +++ b/src/menus/DecompilerContextMenu.cpp @@ -3,6 +3,7 @@ #include "MainWindow.h" #include "dialogs/BreakpointsDialog.h" #include "dialogs/CommentsDialog.h" +#include "common/Configuration.h" #include #include @@ -14,27 +15,36 @@ DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow) : QMenu(parent), + curHighlightedWord(QString()), offset(0), isTogglingBreakpoints(false), mainWindow(mainWindow), annotationHere(nullptr), actionCopy(tr("Copy"), this), + actionCopyInstructionAddress(tr("Copy instruction address (
)"), this), + actionCopyReferenceAddress(tr("Copy address of [flag] (
)"), this), + actionShowInSubmenu(tr("Show in"), this), actionAddComment(tr("Add Comment"), this), actionDeleteComment(tr("Delete comment"), this), actionRenameThingHere(tr("Rename function at cursor"), this), + actionDeleteName(tr("Delete "), this), actionToggleBreakpoint(tr("Add/remove breakpoint"), this), actionAdvancedBreakpoint(tr("Advanced breakpoint"), this), breakpointsInLineMenu(new QMenu(this)), actionContinueUntil(tr("Continue until line"), this), actionSetPC(tr("Set PC"), this) { - setActionCopy(); + setActionCopy(); // Sets all three copy actions addSeparator(); + setActionShowInSubmenu(); + copySeparator = addSeparator(); + setActionAddComment(); setActionDeleteComment(); setActionRenameThingHere(); + setActionDeleteName(); addSeparator(); addBreakpointMenu(); @@ -57,6 +67,11 @@ void DecompilerContextMenu::setAnnotationHere(RCodeAnnotation *annotation) this->annotationHere = annotation; } +void DecompilerContextMenu::setCurHighlightedWord(QString word) +{ + this->curHighlightedWord = word; +} + void DecompilerContextMenu::setOffset(RVA offset) { this->offset = offset; @@ -86,11 +101,6 @@ void DecompilerContextMenu::setupBreakpointsInLineMenu() } } -void DecompilerContextMenu::setCanCopy(bool enabled) -{ - actionCopy.setVisible(enabled); -} - void DecompilerContextMenu::setShortcutContextInActions(QMenu *menu) { for (QAction *action : menu->actions()) { @@ -117,11 +127,14 @@ bool DecompilerContextMenu::getIsTogglingBreakpoints() void DecompilerContextMenu::aboutToHideSlot() { actionAddComment.setVisible(true); + actionRenameThingHere.setVisible(true); + actionDeleteName.setVisible(false); } void DecompilerContextMenu::aboutToShowSlot() { if (this->firstOffsetInLine != RVA_MAX) { + actionShowInSubmenu.setVisible(true); QString comment = Core()->cmdRawAt("CC.", this->firstOffsetInLine); actionAddComment.setVisible(true); if (comment.isEmpty()) { @@ -132,6 +145,7 @@ void DecompilerContextMenu::aboutToShowSlot() actionAddComment.setText(tr("Edit Comment")); } } else { + actionShowInSubmenu.setVisible(false); actionAddComment.setVisible(false); actionDeleteComment.setVisible(false); } @@ -163,22 +177,82 @@ void DecompilerContextMenu::aboutToShowSlot() QString progCounterName = Core()->getRegisterName("PC").toUpper(); 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); + copySeparator->setVisible(false); } else { - actionRenameThingHere.setVisible(true); - actionRenameThingHere.setText(tr("Rename function %1").arg(QString( - annotationHere->function_name.name))); + copySeparator->setVisible(true); + if (annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) { + actionRenameThingHere.setVisible(true); + actionRenameThingHere.setText(tr("Rename function %1").arg(QString( + 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 -void DecompilerContextMenu::setActionCopy() + +void DecompilerContextMenu::setActionCopy() // Set all three copy actions { connect(&actionCopy, &QAction::triggered, this, &DecompilerContextMenu::actionCopyTriggered); addAction(&actionCopy); 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() @@ -198,12 +272,20 @@ void DecompilerContextMenu::setActionDeleteComment() void DecompilerContextMenu::setActionRenameThingHere() { - actionRenameThingHere.setShortcut({Qt::SHIFT + Qt::Key_N}); + actionRenameThingHere.setShortcut({Qt::Key_N}); connect(&actionRenameThingHere, &QAction::triggered, this, &DecompilerContextMenu::actionRenameThingHereTriggered); addAction(&actionRenameThingHere); } +void DecompilerContextMenu::setActionDeleteName() +{ + connect(&actionDeleteName, &QAction::triggered, this, + &DecompilerContextMenu::actionDeleteNameTriggered); + addAction(&actionDeleteName); + actionDeleteName.setVisible(false); +} + void DecompilerContextMenu::setActionToggleBreakpoint() { connect(&actionToggleBreakpoint, &QAction::triggered, this, @@ -236,6 +318,18 @@ void DecompilerContextMenu::actionCopyTriggered() 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() { CommentsDialog::addOrEditComment(this->firstOffsetInLine, this); @@ -251,29 +345,53 @@ void DecompilerContextMenu::actionRenameThingHereTriggered() if (!annotationHere) { return; } + RCoreLocked core = Core()->core(); bool ok; auto type = annotationHere->type; if (type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) { - QString currentName(annotationHere->function_name.name); - RVA func_addr = annotationHere->function_name.offset; + QString currentName(annotationHere->reference.name); + RVA func_addr = annotationHere->reference.offset; RAnalFunction *func = Core()->functionAt(func_addr); if (func == NULL) { - QString function_name = QInputDialog::getText(this, tr("Define this function at %2").arg(RAddressString(func_addr)), - tr("Function name:"), QLineEdit::Normal, currentName, &ok); + QString function_name = QInputDialog::getText(this, + tr("Define this function at %2").arg(RAddressString(func_addr)), + tr("Function name:"), QLineEdit::Normal, currentName, &ok); if (ok && !function_name.isEmpty()) { Core()->createFunctionAt(func_addr, function_name); } } else { QString newName = QInputDialog::getText(this, tr("Rename function %2").arg(currentName), - tr("Function name:"), QLineEdit::Normal, currentName, &ok); + tr("Function name:"), QLineEdit::Normal, currentName, &ok); if (ok && !newName.isEmpty()) { - Core()->renameFunction(func_addr, newName); - } + Core()->renameFunction(func_addr, newName); + } } - + + } 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() { if (!this->availableBreakpoints.isEmpty()) { @@ -335,3 +453,43 @@ void DecompilerContextMenu::addDebugMenu() setActionSetPC(); 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); + } +} diff --git a/src/menus/DecompilerContextMenu.h b/src/menus/DecompilerContextMenu.h index 26553ccd..5d3c7c37 100644 --- a/src/menus/DecompilerContextMenu.h +++ b/src/menus/DecompilerContextMenu.h @@ -22,8 +22,8 @@ signals: void copy(); public slots: + void setCurHighlightedWord(QString word); void setOffset(RVA offset); - void setCanCopy(bool enabled); void setFirstOffsetInLine(RVA firstOffset); void setAvailableBreakpoints(QVector offsetList); @@ -33,11 +33,14 @@ private slots: void aboutToHideSlot(); void actionCopyTriggered(); + void actionCopyInstructionAddressTriggered(); + void actionCopyReferenceAddressTriggered(); void actionAddCommentTriggered(); void actionDeleteCommentTriggered(); void actionRenameThingHereTriggered(); + void actionDeleteNameTriggered(); void actionToggleBreakpointTriggered(); void actionAdvancedBreakpointTriggered(); @@ -47,6 +50,7 @@ private slots: private: // Private variables + QString curHighlightedWord; RVA offset; RVA firstOffsetInLine; bool isTogglingBreakpoints; @@ -56,12 +60,18 @@ private: RCodeAnnotation *annotationHere; QAction actionCopy; + QAction actionCopyInstructionAddress; + QAction actionCopyReferenceAddress; QAction *copySeparator; + QAction actionShowInSubmenu; + QList showTargetMenuActions; + QAction actionAddComment; QAction actionDeleteComment; QAction actionRenameThingHere; + QAction actionDeleteName; QMenu *breakpointMenu; QAction actionToggleBreakpoint; @@ -81,10 +91,13 @@ private: // Set actions void setActionCopy(); + void setActionShowInSubmenu(); + void setActionAddComment(); void setActionDeleteComment(); void setActionRenameThingHere(); + void setActionDeleteName(); void setActionToggleBreakpoint(); void setActionAdvancedBreakpoint(); @@ -113,6 +126,7 @@ private: // QVector getThingUsedHere(RVA offset); // void updateTargetMenuActions(const QVector &targets); + void updateTargetMenuActions(); }; #endif // DECOMPILERCONTEXTMENU_H \ No newline at end of file diff --git a/src/widgets/DecompilerWidget.cpp b/src/widgets/DecompilerWidget.cpp index bba012ea..7645f996 100644 --- a/src/widgets/DecompilerWidget.cpp +++ b/src/widgets/DecompilerWidget.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) : connect(Config(), &Configuration::fontsUpdated, this, &DecompilerWidget::fontsUpdatedSlot); connect(Config(), &Configuration::colorsUpdated, this, &DecompilerWidget::colorsUpdatedSlot); 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; decompilerWasBusy = false; @@ -87,8 +88,6 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) : connect(ui->textEdit, &QWidget::customContextMenuRequested, 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); addActions(mCtxMenu->actions()); @@ -284,7 +283,7 @@ void DecompilerWidget::decompilationFinished(RAnnotatedCode *codeDecompiled) ui->progressLabel->setVisible(false); ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled); updateRefreshButton(); - + mCtxMenu->setAnnotationHere(nullptr); this->code.reset(codeDecompiled); QString codeString = QString::fromUtf8(this->code->code); @@ -344,7 +343,6 @@ void DecompilerWidget::connectCursorPositionChanged(bool disconnect) void DecompilerWidget::cursorPositionChanged() { - mCtxMenu->setCanCopy(ui->textEdit->textCursor().hasSelection()); // Do not perform seeks along with the cursor while selecting multiple lines if (!ui->textEdit->textCursor().selectedText().isEmpty()) { return; @@ -414,6 +412,7 @@ void DecompilerWidget::updateSelection() // Highlight all the words in the document same as the current one cursor.select(QTextCursor::WordUnderCursor); QString searchString = cursor.selectedText(); + mCtxMenu->setCurHighlightedWord(searchString); extraSelections.append(createSameWordsSelections(ui->textEdit, searchString)); ui->textEdit->setExtraSelections(extraSelections); @@ -438,7 +437,6 @@ void DecompilerWidget::colorsUpdatedSlot() void DecompilerWidget::showDisasContextMenu(const QPoint &pt) { mCtxMenu->exec(ui->textEdit->mapToGlobal(pt)); - doRefresh(); } void DecompilerWidget::seekToReference() @@ -460,7 +458,7 @@ bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event) if (event->type() == QEvent::MouseButtonPress && (obj == ui->textEdit || obj == ui->textEdit->viewport())) { QMouseEvent *mouseEvent = static_cast(event); - if (mouseEvent->button() == Qt::RightButton) { + if (mouseEvent->button() == Qt::RightButton && !ui->textEdit->textCursor().hasSelection()) { ui->textEdit->setTextCursor(ui->textEdit->cursorForPosition(mouseEvent->pos())); return true; } @@ -509,3 +507,20 @@ bool DecompilerWidget::colorLine(QTextEdit::ExtraSelection extraSelection) ui->textEdit->setExtraSelections(extraSelections); 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()); + } + } +} diff --git a/src/widgets/DecompilerWidget.h b/src/widgets/DecompilerWidget.h index eedd2df7..518603b3 100644 --- a/src/widgets/DecompilerWidget.h +++ b/src/widgets/DecompilerWidget.h @@ -32,6 +32,7 @@ public slots: void highlightPC(); private slots: + void copy(); void fontsUpdatedSlot(); void colorsUpdatedSlot(); void refreshDecompiler();