From c8d8e667fb788ff2c9bb1d9deb13c25077a3ac43 Mon Sep 17 00:00:00 2001 From: Itay Cohen Date: Tue, 18 Dec 2018 19:26:38 +0200 Subject: [PATCH] Right click -> Select block in hexdump widget (#1006) * jamieb22 modifications and some sanity checks * improve warning color * Fix writeable hexdump * more sanity checks --- src/Cutter.pro | 9 +- src/dialogs/HexdumpRangeDialog.cpp | 108 ++++++++++++++++++++ src/dialogs/HexdumpRangeDialog.h | 38 +++++++ src/dialogs/HexdumpRangeDialog.ui | 154 +++++++++++++++++++++++++++++ src/widgets/HexdumpWidget.cpp | 72 ++++++++++++-- src/widgets/HexdumpWidget.h | 8 +- src/widgets/HexdumpWidget.ui | 75 +++++++------- 7 files changed, 415 insertions(+), 49 deletions(-) create mode 100644 src/dialogs/HexdumpRangeDialog.cpp create mode 100644 src/dialogs/HexdumpRangeDialog.h create mode 100644 src/dialogs/HexdumpRangeDialog.ui diff --git a/src/Cutter.pro b/src/Cutter.pro index e91936e6..54f4f727 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -219,7 +219,8 @@ SOURCES += \ common/ColorSchemeFileSaver.cpp \ dialogs/EditFunctionDialog.cpp \ widgets/CutterTreeView.cpp \ - widgets/ComboQuickFilterView.cpp + widgets/ComboQuickFilterView.cpp \ + dialogs/HexdumpRangeDialog.cpp HEADERS += \ Cutter.h \ @@ -322,7 +323,8 @@ HEADERS += \ widgets/ColorSchemePrefWidget.h \ dialogs/EditFunctionDialog.h \ widgets/CutterTreeView.h \ - widgets/ComboQuickFilterView.h + widgets/ComboQuickFilterView.h \ + dialogs/HexdumpRangeDialog.h FORMS += \ dialogs/AboutDialog.ui \ @@ -381,7 +383,8 @@ FORMS += \ dialogs/SetFunctionVarTypes.ui \ widgets/ColorSchemePrefWidget.ui \ widgets/CutterTreeView.ui \ - widgets/ComboQuickFilterView.ui + widgets/ComboQuickFilterView.ui \ + dialogs/HexdumpRangeDialog.ui RESOURCES += \ resources.qrc \ diff --git a/src/dialogs/HexdumpRangeDialog.cpp b/src/dialogs/HexdumpRangeDialog.cpp new file mode 100644 index 00000000..319ac973 --- /dev/null +++ b/src/dialogs/HexdumpRangeDialog.cpp @@ -0,0 +1,108 @@ +#include "HexdumpRangeDialog.h" +#include "ui_HexdumpRangeDialog.h" + +HexdumpRangeDialog::HexdumpRangeDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::HexdumpRangeDialog) +{ + ui->setupUi(this); + QRegExpValidator *v = new QRegExpValidator(QRegExp("(?:0[xX])?[0-9a-fA-F]+"), this); + ui->lengthLineEdit->setValidator(v); + ui->startAddressLineEdit->setValidator(v); + ui->endAddressLineEdit->setValidator(v); + + //subscribe to a text change slot + connect(ui->endAddressLineEdit, &QLineEdit::textEdited, this, &HexdumpRangeDialog::textEdited); + connect(ui->lengthLineEdit, &QLineEdit::textEdited, this, &HexdumpRangeDialog::textEdited); + connect(ui->endAddressRadioButton, &QRadioButton::clicked, this, + &HexdumpRangeDialog::on_radioButtonClicked); + connect(ui->lengthRadioButton, &QRadioButton::clicked, this, + &HexdumpRangeDialog::on_radioButtonClicked); + +} + +HexdumpRangeDialog::~HexdumpRangeDialog() +{ + delete ui; +} + +QString HexdumpRangeDialog::getStartAddress() const +{ + return ui->startAddressLineEdit->text(); +} + +QString HexdumpRangeDialog::getEndAddress() const +{ + return ui->endAddressLineEdit->text(); +} + +QString HexdumpRangeDialog::getLength() const +{ + return ui->lengthLineEdit->text(); +} + +bool HexdumpRangeDialog::getEndAddressRadioButtonChecked() const +{ + return ui->endAddressRadioButton->isChecked(); +} + +bool HexdumpRangeDialog::getLengthRadioButtonChecked() const +{ + return ui->lengthRadioButton->isChecked(); +} + +void HexdumpRangeDialog::setStartAddress(ut64 start) +{ + ui->startAddressLineEdit->setText( + QString("0x%1").arg(start, 0, 16)); +} + +void HexdumpRangeDialog::textEdited() +{ + bool warningVisibile = false; + ut64 startAddress = Core()->math(ui->startAddressLineEdit->text()); + ut64 endAddress = 0; + ut64 length = 0; + if (sender() == ui->endAddressLineEdit) { + endAddress = Core()->math(getEndAddress()); + if (endAddress > startAddress) { + length = endAddress - startAddress; + ui->lengthLineEdit->setText( + QString("0x%1").arg(length, 0, 16)); + } + else { + ui->lengthLineEdit->setText("Invalid"); + } + } else if ( sender() == ui->lengthLineEdit) { + //we edited the length, so update the end address to be start address + length + length = Core()->math(getLength()); + endAddress = startAddress + length; + ui->endAddressLineEdit->setText( + QString("0x%1").arg(endAddress, 0, 16)); + } + + length = Core()->math(getLength()); + + // Warn the user for potentially heavy operation + if (length > 0x25000) { + warningVisibile = true; + } + + ui->selectionWarningLabel->setVisible(warningVisibile); + +} + +void HexdumpRangeDialog::on_radioButtonClicked(bool checked) +{ + + if (sender() == ui->endAddressRadioButton && checked == true) { + ui->lengthLineEdit->setEnabled(false); + ui->endAddressLineEdit->setEnabled(true); + ui->endAddressLineEdit->setFocus(); + } else if (sender() == ui->lengthRadioButton && checked == true) { + ui->lengthLineEdit->setEnabled(true); + ui->endAddressLineEdit->setEnabled(false); + ui->lengthLineEdit->setFocus(); + } + +} diff --git a/src/dialogs/HexdumpRangeDialog.h b/src/dialogs/HexdumpRangeDialog.h new file mode 100644 index 00000000..4fb9e71f --- /dev/null +++ b/src/dialogs/HexdumpRangeDialog.h @@ -0,0 +1,38 @@ +#ifndef HEXDUMPRANGEDIALOG_H +#define HEXDUMPRANGEDIALOG_H + +#include "Cutter.h" + +#include +#include + +namespace Ui { +class HexdumpRangeDialog; +} + +class HexdumpRangeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit HexdumpRangeDialog(QWidget *parent = nullptr); + ~HexdumpRangeDialog(); + QString getStartAddress() const; + QString getEndAddress() const; + QString getLength() const; + bool getEndAddressRadioButtonChecked() const; + bool getLengthRadioButtonChecked() const; + void setStartAddress(ut64 start); + +public slots: + void textEdited(); + +private: + Ui::HexdumpRangeDialog *ui; + +private slots: + void on_radioButtonClicked(bool checked); + +}; + +#endif // HEXDUMPRANGEDIALOG_H diff --git a/src/dialogs/HexdumpRangeDialog.ui b/src/dialogs/HexdumpRangeDialog.ui new file mode 100644 index 00000000..eda48670 --- /dev/null +++ b/src/dialogs/HexdumpRangeDialog.ui @@ -0,0 +1,154 @@ + + + HexdumpRangeDialog + + + + 0 + 0 + 455 + 201 + + + + Select Block + + + + + 10 + 160 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 29 + 19 + 401 + 121 + + + + + + + + + End Address: + + + true + + + buttonGroup + + + + + + + + + Start Address: + + + + + + + 18 + + + + + + + 18 + + + + + + + false + + + 10 + + + + + + + Length: + + + buttonGroup + + + + + + + true + + + false + + + <html><head/><body><p><span style=" color:#ff8585;">Big selection might cause a delay</span></p></body></html> + + + + + + + + + + buttonBox + accepted() + HexdumpRangeDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + HexdumpRangeDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + + + diff --git a/src/widgets/HexdumpWidget.cpp b/src/widgets/HexdumpWidget.cpp index 6f53322e..44ae38b5 100644 --- a/src/widgets/HexdumpWidget.cpp +++ b/src/widgets/HexdumpWidget.cpp @@ -12,6 +12,7 @@ #include #include #include +#include HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) : CutterDockWidget(main, action), @@ -46,12 +47,12 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) : ui->openSideViewB->hide(); // hide button at startup since side view is visible - connect(closeButton, &QToolButton::clicked, this, [this]{ - ui->hexSideTab_2->hide(); - ui->openSideViewB->show(); + connect(closeButton, &QToolButton::clicked, this, [this] { + ui->hexSideTab_2->hide(); + ui->openSideViewB->show(); }); - connect(ui->openSideViewB, &QToolButton::clicked, this, [this]{ + connect(ui->openSideViewB, &QToolButton::clicked, this, [this] { ui->hexSideTab_2->show(); ui->openSideViewB->hide(); }); @@ -116,6 +117,7 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) : connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, this, &HexdumpWidget::selectionChanged); connect(seekable, &CutterSeekableWidget::seekChanged, this, &HexdumpWidget::on_seekChanged); + connect(&rangeDialog, &QDialog::accepted, this, &HexdumpWidget::on_rangeDialogAccepted); format = Format::Hex; initParsing(); @@ -312,6 +314,8 @@ void HexdumpWidget::highlightHexWords(const QString &str) void HexdumpWidget::refresh(RVA addr) { + ut64 loadLines = 0; + ut64 curAddrLineOffset = 0; connectScroll(true); updateHeaders(); @@ -326,10 +330,17 @@ void HexdumpWidget::refresh(RVA addr) cols = 16; // TODO: Figure out how to calculate a sane value for this - bufferLines = qhelpers::getMaxFullyDisplayedLines(ui->hexHexText); + bufferLines = qhelpers::getMaxFullyDisplayedLines(ui->hexHexText) * 10; - ut64 loadLines = bufferLines * 3; // total lines to load - ut64 curAddrLineOffset = bufferLines; // line number where seek should be + if (requestedSelectionEndAddress != 0 && requestedSelectionStartAddress != 0 + && requestedSelectionEndAddress > requestedSelectionStartAddress) { + loadLines = ((requestedSelectionEndAddress - requestedSelectionStartAddress) / cols) + + (bufferLines * 2); + curAddrLineOffset = bufferLines; + } else { + loadLines = bufferLines * 3; // total lines to load + curAddrLineOffset = bufferLines; // line number where seek should be + } if (addr < curAddrLineOffset * cols) { curAddrLineOffset = static_cast(addr / cols); @@ -607,6 +618,8 @@ void HexdumpWidget::showHexdumpContextMenu(const QPoint &pt) formatSubmenu->addAction(ui->actionFormatHex); formatSubmenu->addAction(ui->actionFormatOctal); + menu->addAction(ui->actionSelect_Block); + menu->addSeparator(); syncAction.setText(tr("Sync/unsync offset")); menu->addAction(&syncAction); @@ -1017,6 +1030,19 @@ void HexdumpWidget::on_actionFormatOctal_triggered() refresh(); } +void HexdumpWidget::on_actionSelect_Block_triggered() +{ + + //get the current hex address from current cursor location + rangeDialog.setStartAddress( + hexPositionToAddress(ui->hexHexText->textCursor().position())); + rangeDialog.setModal(false); + rangeDialog.show(); + rangeDialog.activateWindow(); + rangeDialog.raise(); + +} + void HexdumpWidget::on_parseTypeComboBox_currentTextChanged(const QString &) { if (ui->parseTypeComboBox->currentIndex() == 0) { @@ -1113,6 +1139,38 @@ void HexdumpWidget::selectHexPreview() } } +void HexdumpWidget::on_rangeDialogAccepted() +{ + int startPosition; + int endPosition; + QTextCursor targetTextCursor; + + requestedSelectionStartAddress = Core()->math(rangeDialog.getStartAddress()); + requestedSelectionEndAddress = rangeDialog.getEndAddressRadioButtonChecked() ? + Core()->math(rangeDialog.getEndAddress()) : + requestedSelectionStartAddress + Core()->math(rangeDialog.getLength()); + + //not sure what the accepted user feedback mechanism is, output to console or a QMessageBox alert + if (requestedSelectionEndAddress <= requestedSelectionStartAddress) { + Core()->message(tr("Error: Could not select range, end address is less then start address")); + return; + } + + //seek to the start address and create a text cursor to highlight the desired range + refresh(requestedSelectionStartAddress); + + //for large selections, won't be able to calculate the endPosition because hexAddressToPosition assumes the address is loaded? + startPosition = hexAddressToPosition(requestedSelectionStartAddress); + endPosition = hexAddressToPosition(requestedSelectionEndAddress) - 1; + + targetTextCursor = ui->hexHexText->textCursor(); + + targetTextCursor.setPosition(startPosition); + targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor); + + ui->hexHexText->setTextCursor(targetTextCursor); +} + void HexdumpWidget::showOffsets(bool show) { if (show) { diff --git a/src/widgets/HexdumpWidget.h b/src/widgets/HexdumpWidget.h index 919c0cb3..b261a8a7 100644 --- a/src/widgets/HexdumpWidget.h +++ b/src/widgets/HexdumpWidget.h @@ -11,6 +11,7 @@ #include "Cutter.h" #include "CutterDockWidget.h" #include "CutterSeekableWidget.h" +#include "dialogs/HexdumpRangeDialog.h" #include "common/Highlighter.h" #include "common/HexAsciiHighlighter.h" #include "common/HexHighlighter.h" @@ -44,7 +45,7 @@ public: public slots: void initParsing(); - + void on_rangeDialogAccepted(); void showOffsets(bool show); void zoomIn(int range = 1); @@ -102,6 +103,9 @@ private: int bufferLines = 0; int cols = 0; + ut64 requestedSelectionStartAddress=0; + ut64 requestedSelectionEndAddress=0; + HexdumpRangeDialog rangeDialog; QAction syncAction; CutterSeekableWidget *seekable; @@ -137,6 +141,8 @@ private slots: void on_actionFormatHex_triggered(); void on_actionFormatOctal_triggered(); + void on_actionSelect_Block_triggered(); + void fontsUpdated(); void colorsUpdatedSlot(); diff --git a/src/widgets/HexdumpWidget.ui b/src/widgets/HexdumpWidget.ui index 684d1d6e..a1fbe4be 100644 --- a/src/widgets/HexdumpWidget.ui +++ b/src/widgets/HexdumpWidget.ui @@ -64,43 +64,6 @@ 0 - - - - - 0 - 0 - - - - false - - - QFrame::NoFrame - - - 0 - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - QTextEdit::NoWrap - - - 3 - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - @@ -222,6 +185,37 @@ + + + + + 0 + 0 + + + + false + + + QFrame::NoFrame + + + 0 + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + @@ -912,9 +906,14 @@ 4 bytes + + + Select Block... + + - + \ No newline at end of file