diff --git a/src/Cutter.pro b/src/Cutter.pro
index 80486acc..904107fc 100644
--- a/src/Cutter.pro
+++ b/src/Cutter.pro
@@ -272,7 +272,6 @@ SOURCES += \
menus/DisassemblyContextMenu.cpp \
widgets/DisassemblyWidget.cpp \
widgets/HexdumpWidget.cpp \
- widgets/HexTextView.cpp \
common/Configuration.cpp \
common/Colors.cpp \
dialogs/SaveProjectDialog.cpp \
@@ -349,7 +348,8 @@ SOURCES += \
common/CrashHandler.cpp \
common/BugReporting.cpp \
common/HighDpiPixmap.cpp \
- widgets/GraphGridLayout.cpp
+ widgets/GraphGridLayout.cpp \
+ widgets/HexWidget.cpp
HEADERS += \
core/Cutter.h \
@@ -392,7 +392,6 @@ HEADERS += \
menus/DisassemblyContextMenu.h \
widgets/DisassemblyWidget.h \
widgets/HexdumpWidget.h \
- widgets/HexTextView.h \
common/Configuration.h \
common/Colors.h \
dialogs/SaveProjectDialog.h \
@@ -475,7 +474,8 @@ HEADERS += \
common/BugReporting.h \
common/HighDpiPixmap.h \
widgets/GraphLayout.h \
- widgets/GraphGridLayout.h
+ widgets/GraphGridLayout.h \
+ widgets/HexWidget.h
FORMS += \
dialogs/AboutDialog.ui \
@@ -501,7 +501,6 @@ FORMS += \
widgets/StringsWidget.ui \
widgets/SymbolsWidget.ui \
widgets/HexdumpWidget.ui \
- widgets/HexTextView.ui \
dialogs/SaveProjectDialog.ui \
dialogs/preferences/PreferencesDialog.ui \
dialogs/preferences/AppearanceOptionsWidget.ui \
diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp
index 40deffe6..1db71c52 100644
--- a/src/core/Cutter.cpp
+++ b/src/core/Cutter.cpp
@@ -2785,4 +2785,22 @@ QString CutterCore::getHexdumpPreview(RVA address, int size)
return ansiEscapeToHtml(hexdump(address, size, HexdumpFormats::Normal)).replace(QLatin1Char('\n'), "
");
}
+QByteArray CutterCore::ioRead(RVA addr, int len)
+{
+ CORE_LOCK();
+
+ QByteArray array;
+
+ if (len <= 0)
+ return array;
+
+ /* Zero-copy */
+ array.resize(len);
+ if (!r_io_read_at(core_->io, addr, (uint8_t *)array.data(), len)) {
+ qWarning() << "Can't read data" << addr << len;
+ array.fill(0xff);
+ }
+
+ return array;
+}
diff --git a/src/core/Cutter.h b/src/core/Cutter.h
index 3c2d023c..58c6b73f 100644
--- a/src/core/Cutter.h
+++ b/src/core/Cutter.h
@@ -263,6 +263,8 @@ public:
void loadPDB(const QString &file);
+ QByteArray ioRead(RVA addr, int len);
+
QList getSeekHistory();
/* Plugins */
diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp
index 880fbef1..9283557e 100644
--- a/src/core/MainWindow.cpp
+++ b/src/core/MainWindow.cpp
@@ -65,6 +65,7 @@
#include "widgets/BacktraceWidget.h"
#include "widgets/HexdumpWidget.h"
#include "widgets/PseudocodeWidget.h"
+#include "widgets/HexWidget.h"
// Qt Headers
#include
diff --git a/src/widgets/HexTextView.cpp b/src/widgets/HexTextView.cpp
deleted file mode 100644
index f5180d03..00000000
--- a/src/widgets/HexTextView.cpp
+++ /dev/null
@@ -1,999 +0,0 @@
-#include "HexTextView.h"
-#include "ui_HexTextView.h"
-
-#include "common/Helpers.h"
-#include "common/Configuration.h"
-#include "common/TempConfig.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-HexTextView::HexTextView(QWidget *parent) :
- QScrollArea(parent),
- ui(new Ui::HexTextView)
-{
- ui->setupUi(this);
-
- // Setup hex highlight
- //connect(ui->hexHexText, SIGNAL(cursorPositionChanged()), this, SLOT(highlightHexCurrentLine()));
- //highlightHexCurrentLine();
-
- int margin = static_cast(ui->hexOffsetText->document()->documentMargin());
- ui->offsetHeaderLabel->setContentsMargins(margin, 0, margin, 0);
-
- margin = static_cast(ui->hexHexText->document()->documentMargin());
- ui->hexHeaderLabel->setContentsMargins(margin, 0, margin, 0);
-
- margin = static_cast(ui->hexASCIIText->document()->documentMargin());
- ui->asciiHeaderLabel->setContentsMargins(margin, 0, margin, 0);
-
- setupFonts();
-
- updateHeaders();
-
- // Set hexdump context menu
- ui->hexHexText->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(ui->hexHexText, SIGNAL(customContextMenuRequested(const QPoint &)),
- this, SLOT(showHexdumpContextMenu(const QPoint &)));
- ui->hexASCIIText->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(ui->hexASCIIText, SIGNAL(customContextMenuRequested(const QPoint &)),
- this, SLOT(showHexASCIIContextMenu(const QPoint &)));
-
- setupScrollSync();
-
- // Control Disasm and Hex scroll to add more contents
- connectScroll(false);
-
- connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated()));
-
- connect(ui->hexHexText, &QTextEdit::selectionChanged, this, &HexTextView::onSelectionChanged);
- connect(ui->hexASCIIText, &QTextEdit::selectionChanged, this, &HexTextView::onSelectionChanged);
- connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexTextView::onSelectionChanged);
- connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, this,
- &HexTextView::onSelectionChanged);
- connect(&rangeDialog, &QDialog::accepted, this, &HexTextView::on_rangeDialogAccepted);
-
- addAction(ui->actionResetZoom);
- connect(ui->actionResetZoom, &QAction::triggered, this, &HexTextView::zoomReset);
- defaultFontSize = ui->hexHexText->font().pointSizeF();
-
- addAction(ui->actionCopyAddressAtCursor);
- ui->actionCopyAddressAtCursor->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
-
- format = Format::Hex;
-}
-
-void HexTextView::setupScrollSync()
-{
- /*
- * For some reason, QScrollBar::valueChanged is not emitted when
- * the scrolling happened from moving the cursor beyond the visible content,
- * so QTextEdit::cursorPositionChanged has to be connected as well.
- */
-
- auto offsetHexFunc = [this]() {
- if (!scroll_disabled) {
- scroll_disabled = true;
- ui->hexHexText->verticalScrollBar()->setValue(ui->hexOffsetText->verticalScrollBar()->value());
- scroll_disabled = false;
- }
- };
-
- auto offsetASCIIFunc = [this]() {
- if (!scroll_disabled) {
- scroll_disabled = true;
- ui->hexASCIIText->verticalScrollBar()->setValue(ui->hexOffsetText->verticalScrollBar()->value());
- scroll_disabled = false;
- }
- };
-
- connect(ui->hexOffsetText->verticalScrollBar(), &QScrollBar::valueChanged,
- ui->hexHexText->verticalScrollBar(), offsetHexFunc);
- connect(ui->hexOffsetText, &QTextEdit::cursorPositionChanged, ui->hexHexText->verticalScrollBar(),
- offsetHexFunc);
- connect(ui->hexOffsetText->verticalScrollBar(), &QScrollBar::valueChanged,
- ui->hexASCIIText->verticalScrollBar(), offsetASCIIFunc);
- connect(ui->hexOffsetText, &QTextEdit::cursorPositionChanged, ui->hexASCIIText->verticalScrollBar(),
- offsetASCIIFunc);
-
- auto hexOffsetFunc = [this]() {
- if (!scroll_disabled) {
- scroll_disabled = true;
- ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value());
- scroll_disabled = false;
- }
- };
-
- auto hexASCIIFunc = [this]() {
- if (!scroll_disabled) {
- scroll_disabled = true;
- ui->hexASCIIText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value());
- scroll_disabled = false;
- }
- };
-
- connect(ui->hexHexText->verticalScrollBar(), &QScrollBar::valueChanged,
- ui->hexOffsetText->verticalScrollBar(), hexOffsetFunc);
- connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, ui->hexOffsetText->verticalScrollBar(),
- hexOffsetFunc);
- connect(ui->hexHexText->verticalScrollBar(), &QScrollBar::valueChanged,
- ui->hexASCIIText->verticalScrollBar(), hexASCIIFunc);
- connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, ui->hexASCIIText->verticalScrollBar(),
- hexASCIIFunc);
-
- auto asciiOffsetFunc = [this]() {
- if (!scroll_disabled) {
- scroll_disabled = true;
- ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexASCIIText->verticalScrollBar()->value());
- scroll_disabled = false;
- }
- };
-
- auto asciiHexFunc = [this]() {
- if (!scroll_disabled) {
- scroll_disabled = true;
- ui->hexHexText->verticalScrollBar()->setValue(ui->hexASCIIText->verticalScrollBar()->value());
- scroll_disabled = false;
- }
- };
-
- connect(ui->hexASCIIText->verticalScrollBar(), &QScrollBar::valueChanged,
- ui->hexOffsetText->verticalScrollBar(), asciiOffsetFunc);
- connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, ui->hexOffsetText->verticalScrollBar(),
- asciiOffsetFunc);
- connect(ui->hexASCIIText->verticalScrollBar(), &QScrollBar::valueChanged,
- ui->hexHexText->verticalScrollBar(), asciiHexFunc);
- connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, ui->hexHexText->verticalScrollBar(),
- asciiHexFunc);
-}
-
-void HexTextView::connectScroll(bool disconnect_)
-{
- scroll_disabled = disconnect_;
- if (disconnect_) {
- disconnect(ui->hexHexText->verticalScrollBar(), &QScrollBar::valueChanged, this,
- &HexTextView::scrollChanged);
- disconnect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexTextView::scrollChanged);
- } else {
- connect(ui->hexHexText->verticalScrollBar(), &QScrollBar::valueChanged, this,
- &HexTextView::scrollChanged);
- connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexTextView::scrollChanged);
-
- }
-}
-
-HexTextView::~HexTextView() {}
-
-/*
- * Text highlight functions
- * Currently unused
- */
-/*
-void HexTextView::highlightHexCurrentLine()
-{
- QList extraSelections;
-
- if (!ui->hexHexText->isReadOnly())
- {
- QTextEdit::ExtraSelection selection;
-
- QColor lineColor = QColor(190, 144, 212);
-
- selection.format.setBackground(lineColor);
- selection.format.setProperty(QTextFormat::FullWidthSelection, true);
- selection.cursor = ui->hexHexText->textCursor();
- selection.cursor.clearSelection();
- extraSelections.append(selection);
- }
-
- QTextCursor cursor = ui->hexHexText->textCursor();
- cursor.select(QTextCursor::WordUnderCursor);
-
- QTextEdit::ExtraSelection currentWord;
-
- QColor blueColor = QColor(Qt::blue).lighter(160);
- currentWord.format.setBackground(blueColor);
-
- currentWord.cursor = cursor;
- extraSelections.append(currentWord);
-
- ui->hexHexText->setExtraSelections(extraSelections);
-
- highlightHexWords(cursor.selectedText());
-}
-
-
-void HexTextView::highlightHexWords(const QString &str)
-{
- QString searchString = str;
- QTextDocument *document = ui->hexHexText->document();
-
- document->undo();
-
- QTextCursor highlightCursor(document);
- QTextCursor cursor(document);
-
- cursor.beginEditBlock();
-
- QColor blueColor = QColor(Qt::blue).lighter(160);
-
- QTextCharFormat plainFormat(highlightCursor.charFormat());
- QTextCharFormat colorFormat = plainFormat;
- colorFormat.setBackground(blueColor);
-
- while (!highlightCursor.isNull() && !highlightCursor.atEnd())
- {
- highlightCursor = document->find(searchString, highlightCursor, QTextDocument::FindWholeWords);
-
- if (!highlightCursor.isNull())
- {
- highlightCursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
- highlightCursor.mergeCharFormat(colorFormat);
- }
- }
- cursor.endEditBlock();
-}
-*/
-
-void HexTextView::refresh(RVA addr)
-{
- if (addr == RVA_INVALID) {
- addr = currentPos;
- } else {
- currentPos = addr;
- }
-
- ut64 loadLines = 0;
- ut64 curAddrLineOffset = 0;
- connectScroll(true);
-
- updateHeaders();
-
-
-
- cols = Core()->getConfigi("hex.cols");
- // Avoid divison by 0
- if (cols == 0)
- cols = 16;
-
- // TODO: Figure out how to calculate a sane value for this
- bufferLines = qhelpers::getMaxFullyDisplayedLines(ui->hexHexText) * 10;
-
- 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);
- }
-
- if (addr > RVA_MAX - curAddrLineOffset * cols) {
- curAddrLineOffset = static_cast(loadLines - (RVA_MAX - addr) / cols);
- }
-
- first_loaded_address = addr - curAddrLineOffset * cols;
- last_loaded_address = addr + (loadLines - curAddrLineOffset) * cols;
-
- auto hexdump = fetchHexdump(first_loaded_address, loadLines);
-
- ui->hexOffsetText->setText(hexdump[0]);
- ui->hexHexText->setText(hexdump[1]);
- ui->hexASCIIText->setPlainText(hexdump[2]);
-
- QTextCursor cursor(ui->hexHexText->document()->findBlockByLineNumber(curAddrLineOffset));
- ui->hexHexText->moveCursor(QTextCursor::End);
- ui->hexHexText->ensureCursorVisible();
- ui->hexHexText->setTextCursor(cursor);
- ui->hexHexText->ensureCursorVisible();
-
- // Set the backgorund color of the current seek
- QTextCursor offsetCursor(ui->hexOffsetText->document()->findBlockByLineNumber(curAddrLineOffset));
- QTextBlockFormat formatTmp = offsetCursor.blockFormat();
- formatTmp.setBackground(QColor(64, 129, 160));
- offsetCursor.setBlockFormat(formatTmp);
-
- updateWidths();
-
- // Update other text areas scroll
- ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value());
- ui->hexASCIIText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value());
-
- connectScroll(false);
-}
-
-void HexTextView::updateHeaders()
-{
- int cols = Core()->getConfigi("hex.cols");
- int ascii_cols = cols;
- bool pairs = Core()->getConfigb("hex.pairs");
-
- QString hexHeaderString;
- QString asciiHeaderString;
-
- QTextStream hexHeader(&hexHeaderString);
- QTextStream asciiHeader(&asciiHeaderString);
-
- hexHeader.setIntegerBase(16);
- hexHeader.setNumberFlags(QTextStream::UppercaseDigits);
- asciiHeader.setIntegerBase(16);
- asciiHeader.setNumberFlags(QTextStream::UppercaseDigits);
-
- // Custom spacing for the header
- QString space = " ";
- switch (format) {
- case Hex:
- space = space.repeated(1);
- break;
- case Octal:
- space = space.repeated(2);
- break;
- default:
- qWarning() << "Unknown format in hexdump!";
- break;
- }
-
- for (int i = 0; i < cols; i++) {
- if (i > 0 && ((pairs && !(i & 1)) || !pairs)) {
- hexHeader << " ";
- }
-
- hexHeader << space << (i & 0xF);
- }
-
- for (int i = 0; i < ascii_cols; i++) {
- asciiHeader << (i & 0xF);
- }
-
- hexHeader.flush();
- asciiHeader.flush();
-
- ui->hexHeaderLabel->setText(hexHeaderString);
- ui->asciiHeaderLabel->setText(asciiHeaderString);
-}
-
-
-std::array HexTextView::fetchHexdump(RVA addr, int lines)
-{
- // Main bytes to fetch:
- int bytes = cols * lines;
-
- QString command = QString("pxj %1 @%2").arg(
- QString::number(bytes),
- RAddressString(addr));
- QJsonArray byte_array = Core()->cmdj(command).array();
-
- QString hexText = "";
- QString offsetText = "";
- QString asciiText = "";
- RVA cur_addr = addr;
- for (int i = 0; i < lines; i++) {
- for (int j = 0; j < cols; j++) {
- int b = byte_array[(i * cols) + j].toInt();
- if ((j > 0) && (j < cols)) {
- hexText += " ";
- }
- // Non printable
- if ((b < 0x20) || (b > 0x7E)) {
- asciiText += ".";
- } else {
- asciiText += (char)b;
- }
-
- switch (format) {
- case Octal:
- hexText += QString::number(b, 8).rightJustified(3, '0');
- break;
- case Hex:
- default:
- hexText += QString::number(b, 16).rightJustified(2, '0');
- break;
- }
- }
- offsetText += RAddressString(cur_addr) + "\n";
- hexText += "\n";
- asciiText += "\n";
- cur_addr += cols;
- }
-
- return {{offsetText, hexText, asciiText}};
-}
-
-void HexTextView::onSelectionChanged()
-{
- if (scroll_disabled) {
- return;
- }
- connectScroll(true);
-
- if (sender() == ui->hexHexText) {
- QTextCursor textCursor = ui->hexHexText->textCursor();
- if (!textCursor.hasSelection()) {
- RVA adr = hexPositionToAddress(textCursor.position());
- int pos = asciiAddressToPosition(adr);
- setTextEditPosition(ui->hexASCIIText, pos);
-
- selection = {true, 0, 0};
- emit selectionChanged(selection);
- currentPos = adr;
- emit positionChanged(adr);
- connectScroll(false);
- return;
- }
-
- int selectionStart = textCursor.selectionStart();
- int selectionEnd = textCursor.selectionEnd();
-
- QChar start = ui->hexHexText->document()->characterAt(selectionStart);
- QChar end = ui->hexHexText->document()->characterAt(selectionEnd);
-
- // This adjusts the selection to make sense with the chosen format
- switch (format) {
- case Hex:
- // Handle the spaces/newlines (if it's at the start, move forward,
- // if it's at the end, move back)
-
- if (!start.isLetterOrNumber()) {
- selectionStart += 1;
- } else if (ui->hexHexText->document()->characterAt(selectionStart - 1).isLetterOrNumber()) {
- selectionStart += 2;
- }
-
- if (!end.isLetterOrNumber()) {
- selectionEnd += 1;
- }
- break;
- case Octal:
- if (!start.isLetterOrNumber()) {
- selectionStart += 1;
- }
- if (!end.isLetterOrNumber()) {
- selectionEnd += 1;
- }
- break;
- }
-
- // In hextext we have the spaces that we need to somehow handle.
- RVA startAddress = hexPositionToAddress(selectionStart);
- RVA endAddress = hexPositionToAddress(selectionEnd);
-
- int startPosition = asciiAddressToPosition(startAddress);
- int endPosition = asciiAddressToPosition(endAddress);
- QTextCursor targetTextCursor = ui->hexASCIIText->textCursor();
- targetTextCursor.setPosition(startPosition);
- targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
- ui->hexASCIIText->setTextCursor(targetTextCursor);
-
- selection = {false, startAddress, endAddress > startAddress ? endAddress - 1 : endAddress};
- emit selectionChanged(selection);
- currentPos = startAddress;
- emit positionChanged(startAddress);
- } else {
- QTextCursor textCursor = ui->hexASCIIText->textCursor();
- if (!textCursor.hasSelection()) {
- RVA adr = asciiPositionToAddress(textCursor.position());
- int pos = hexAddressToPosition(adr);
- setTextEditPosition(ui->hexHexText, pos);
- connectScroll(false);
- selection = {false, 0, 0};
- emit selectionChanged(selection);
- currentPos = adr;
- emit positionChanged(adr);
- return;
- }
- RVA startAddress = asciiPositionToAddress(textCursor.selectionStart());
- RVA endAddress = asciiPositionToAddress(textCursor.selectionEnd());
-
- int startPosition = hexAddressToPosition(startAddress);
- int endPosition = hexAddressToPosition(endAddress);
-
- // End position -1 because the position we get above is for the next
- // entry, so including the space/newline
- endPosition -= 1;
- QTextCursor targetTextCursor = ui->hexHexText->textCursor();
- targetTextCursor.setPosition(startPosition);
- targetTextCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
- ui->hexHexText->setTextCursor(targetTextCursor);
-
- selection = {false, startAddress, endAddress > startAddress ? endAddress - 1 : endAddress};
- emit selectionChanged(selection);
- currentPos = startAddress;
- emit positionChanged(startAddress);
- }
-
- connectScroll(false);
- return;
-}
-
-
-
-void HexTextView::showHexdumpContextMenu(const QPoint &pt)
-{
- // Set Hexdump popup menu
- QMenu *menu = ui->hexHexText->createStandardContextMenu();
- menu->clear();
-
- /*menu->addAction(ui->actionHexCopy_Hexpair);
- menu->addAction(ui->actionHexCopy_ASCII);
- menu->addAction(ui->actionHexCopy_Text);
- menu->addSeparator();*/
- QMenu *colSubmenu = menu->addMenu(tr("Columns"));
- colSubmenu->addAction(ui->action4columns);
- colSubmenu->addAction(ui->action8columns);
- colSubmenu->addAction(ui->action16columns);
- colSubmenu->addAction(ui->action32columns);
-
- QMenu *formatSubmenu = menu->addMenu(tr("Format"));
- formatSubmenu->addAction(ui->actionFormatHex);
- formatSubmenu->addAction(ui->actionFormatOctal);
-
- menu->addAction(ui->actionSelect_Block);
-
- menu->addSeparator();
- menu->addActions(this->actions());
-
- // TODO:
- // formatSubmenu->addAction(ui->actionFormatHalfWord);
- // formatSubmenu->addAction(ui->actionFormatWord);
- // formatSubmenu->addAction(ui->actionFormatQuadWord);
- // formatSubmenu->addAction(ui->actionFormatEmoji);
-
- // TODO:
- // QMenu *signedIntFormatSubmenu = formatSubmenu->addMenu(tr("Signed integer"));
- // signedIntFormatSubmenu->addAction(ui->actionFormatSignedInt1);
- // signedIntFormatSubmenu->addAction(ui->actionFormatSignedInt2);
- // signedIntFormatSubmenu->addAction(ui->actionFormatSignedInt4);
-
- /*menu->addSeparator();
- menu->addAction(ui->actionHexEdit);
- menu->addAction(ui->actionHexPaste);
- menu->addSeparator();
- menu->addAction(ui->actionHexInsert_Hex);
- menu->addAction(ui->actionHexInsert_String);*/
-
- ui->hexHexText->setContextMenuPolicy(Qt::CustomContextMenu);
-
- menu->exec(ui->hexHexText->mapToGlobal(pt));
- delete menu;
-}
-
-void HexTextView::showHexASCIIContextMenu(const QPoint &pt)
-{
- // Set Hex ASCII popup menu
- QMenu *menu = ui->hexASCIIText->createStandardContextMenu();
- menu->clear();
- /*menu->addAction(ui->actionHexCopy_Hexpair);
- menu->addAction(ui->actionHexCopy_ASCII);
- menu->addAction(ui->actionHexCopy_Text);
- menu->addSeparator();*/
- QMenu *colSubmenu = menu->addMenu("Columns");
- colSubmenu->addAction(ui->action4columns);
- colSubmenu->addAction(ui->action8columns);
- colSubmenu->addAction(ui->action16columns);
- colSubmenu->addAction(ui->action32columns);
- /*menu->addSeparator();
- menu->addAction(ui->actionHexEdit);
- menu->addAction(ui->actionHexPaste);
- menu->addSeparator();
- menu->addAction(ui->actionHexInsert_Hex);
- menu->addAction(ui->actionHexInsert_String);*/
-
- ui->hexASCIIText->setContextMenuPolicy(Qt::CustomContextMenu);
-
- menu->exec(ui->hexASCIIText->mapToGlobal(pt));
- delete menu;
-}
-
-void HexTextView::setupFonts()
-{
- QFont font = Config()->getFont();
-
- ui->hexOffsetText->setFont(font);
- ui->hexHexText->setFont(font);
- ui->hexASCIIText->setFont(font);
-
- ui->offsetHeaderLabel->setFont(font);
- ui->hexHeaderLabel->setFont(font);
- ui->asciiHeaderLabel->setFont(font);
-}
-
-HexTextView::Selection HexTextView::getSelection()
-{
- return selection;
-}
-
-RVA HexTextView::position()
-{
- return currentPos;
-}
-
-void HexTextView::fontsUpdated()
-{
- setupFonts();
-}
-
-
-RVA HexTextView::hexPositionToAddress(int position)
-{
- switch (format) {
-
- case Octal:
- return first_loaded_address + (position / 4);
- case Hex:
- default:
- return first_loaded_address + (position / 3);
- }
- return RVA_INVALID;
- // In hex each byte takes up 2 characters + 1 spacer (including newline as spacer)
-
-}
-
-RVA HexTextView::asciiPositionToAddress(int position)
-{
- // Each row adds one byte (because of the newline), so cols + 1 gets rid of that offset
- return first_loaded_address + (position - (position / (cols + 1)));
-}
-
-int HexTextView::hexAddressToPosition(RVA address)
-{
- // This strictly assumes that the address is actually loaded.
- switch (format) {
-
- case Octal:
- return (address - first_loaded_address) * 4;
- case Hex:
- default:
- return (address - first_loaded_address) * 3;
- }
-}
-
-int HexTextView::asciiAddressToPosition(RVA address)
-{
- RVA local_address = address - first_loaded_address;
- int position = local_address + (local_address / cols);
- return position;
-}
-
-void HexTextView::setTextEditPosition(QTextEdit *textEdit, int position)
-{
- QTextCursor textCursor = textEdit->textCursor();
- textCursor.setPosition(position);
- textEdit->setTextCursor(textCursor);
-}
-
-int HexTextView::getDisplayedLined(QTextEdit *textEdit, bool bottom)
-{
- //int start_pos = textEdit->cursorForPosition(QPoint(0, 0)).position();
- QPoint top_right(textEdit->viewport()->x(), textEdit->viewport()->y());
- QPoint bottom_right(textEdit->viewport()->width(), textEdit->viewport()->height() - 1);
- QPoint point = top_right;
- if (bottom) {
- point = bottom_right;
- }
-
- QTextCursor textCursor = textEdit->cursorForPosition(point);
- //QTextBlock textBlock = textCursor.block();
- //QTextLayout *textLayout = textBlock.layout();
- //const int relativePos = textCursor.position() - textBlock.position();
- //int end_pos = textEdit->cursorForPosition(bottom_right).position();
- return textCursor.blockNumber();
-}
-
-void HexTextView::removeTopLinesWithoutScroll(QTextEdit *textEdit, int lines)
-{
- int scroll_val_before = textEdit->verticalScrollBar()->value();
- int height_before = textEdit->document()->size().height();
-
- QTextBlock block = textEdit->document()->firstBlock();
- for (int i = 0; i < lines; i++) {
- QTextCursor cursor(block);
- block = block.next();
- cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
- cursor.removeSelectedText();
- }
-
- int height_after = textEdit->document()->size().height();
- textEdit->verticalScrollBar()->setValue(scroll_val_before + (height_after - height_before));
-}
-
-void HexTextView::removeBottomLinesWithoutScroll(QTextEdit *textEdit, int lines)
-{
- QTextBlock block = textEdit->document()->lastBlock().previous();
- QTextCursor textCursor = textEdit->textCursor();
- for (int i = 0; i < lines; i++) {
- QTextCursor cursor(block);
- block = block.previous();
- cursor.select(QTextCursor::BlockUnderCursor);
- cursor.removeSelectedText();
- }
-}
-
-void HexTextView::prependWithoutScroll(QTextEdit *textEdit, QString text)
-{
- // TODO: Keep selection (already works for append)
- QTextCursor textCursor = textEdit->textCursor();
- int current_positon = textCursor.position();
-
- //int scroll_max_before = textEdit->verticalScrollBar()->maximum();
- int scroll_val_before = textEdit->verticalScrollBar()->value();
- int height_before = textEdit->document()->size().height();
- textEdit->moveCursor(QTextCursor::Start);
- textEdit->insertPlainText(text);
- textCursor.setPosition(text.length() + current_positon);
- textEdit->setTextCursor(textCursor);
- int height_after = textEdit->document()->size().height();
- //int scroll_max_after = textEdit->verticalScrollBar()->maximum();
- //int scroll_val_after = textEdit->verticalScrollBar()->maximum();
- textEdit->verticalScrollBar()->setValue(scroll_val_before + (height_after - height_before));
-}
-
-void HexTextView::appendWithoutScroll(QTextEdit *textEdit, QString text)
-{
- int scroll_val_before = textEdit->verticalScrollBar()->value();
- QTextCursor textCursor = textEdit->textCursor();
- textEdit->moveCursor(QTextCursor::End);
- textEdit->insertPlainText(text);
- textEdit->setTextCursor(textCursor);
- textEdit->verticalScrollBar()->setValue(scroll_val_before);
-}
-
-void HexTextView::scrollChanged()
-{
- connectScroll(true);
-
- int firstLine = getDisplayedLined(ui->hexHexText);
- if (firstLine < (bufferLines / 2)) {
- int loadLines = bufferLines;
- RVA shift = static_cast(loadLines * cols);
- if (shift > first_loaded_address) {
- loadLines = static_cast(first_loaded_address / cols);
- shift = first_loaded_address;
- }
- first_loaded_address -= shift;
- last_loaded_address -= shift;
-
- if (loadLines > 0) {
- auto hexdump = fetchHexdump(first_loaded_address, loadLines);
- prependWithoutScroll(ui->hexOffsetText, hexdump[0]);
- prependWithoutScroll(ui->hexHexText, hexdump[1]);
- prependWithoutScroll(ui->hexASCIIText, hexdump[2]);
-
- removeBottomLinesWithoutScroll(ui->hexOffsetText, loadLines);
- removeBottomLinesWithoutScroll(ui->hexHexText, loadLines);
- removeBottomLinesWithoutScroll(ui->hexASCIIText, loadLines);
-
- ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value());
- ui->hexASCIIText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value());
- }
- }
-
- int blocks = ui->hexHexText->document()->blockCount();
- int lastLine = getDisplayedLined(ui->hexHexText, true);
- if (blocks - lastLine < (bufferLines / 2)) {
- int loadLines = bufferLines;
- RVA shift = static_cast(loadLines * cols);
- if (last_loaded_address > RVA_MAX - shift) {
- shift = RVA_MAX - last_loaded_address;
- loadLines = static_cast(shift / cols);
- }
-
- if (loadLines > 0) {
- auto hexdump = fetchHexdump(last_loaded_address, loadLines);
- last_loaded_address += shift;
- first_loaded_address += shift;
-
- removeTopLinesWithoutScroll(ui->hexOffsetText, loadLines);
- removeTopLinesWithoutScroll(ui->hexHexText, loadLines);
- removeTopLinesWithoutScroll(ui->hexASCIIText, loadLines);
- appendWithoutScroll(ui->hexOffsetText, hexdump[0]);
- appendWithoutScroll(ui->hexHexText, hexdump[1]);
- appendWithoutScroll(ui->hexASCIIText, hexdump[2]);
- }
- }
- connectScroll(false);
-}
-
-/*
- * Actions callback functions
- */
-
-void HexTextView::on_actionCopyAddressAtCursor_triggered()
-{
- auto addr = hexPositionToAddress(ui->hexHexText->textCursor().position());
-
- QClipboard *clipboard = QApplication::clipboard();
- clipboard->setText(RAddressString(addr));
-}
-
-
-void HexTextView::on_action8columns_triggered()
-{
- Core()->setConfig("hex.cols", 8);
- refresh();
-}
-
-void HexTextView::on_action16columns_triggered()
-{
- Core()->setConfig("hex.cols", 16);
- refresh();
-}
-
-void HexTextView::on_action4columns_triggered()
-{
- Core()->setConfig("hex.cols", 4);
- refresh();
-}
-
-void HexTextView::on_action32columns_triggered()
-{
- Core()->setConfig("hex.cols", 32);
- refresh();
-}
-
-void HexTextView::on_action64columns_triggered()
-{
- Core()->setConfig("hex.cols", 64);
- refresh();
-}
-
-void HexTextView::on_action2columns_triggered()
-{
- Core()->setConfig("hex.cols", 2);
- refresh();
-}
-
-void HexTextView::on_action1column_triggered()
-{
- Core()->setConfig("hex.cols", 1);
- refresh();
-}
-
-void HexTextView::on_actionFormatHex_triggered()
-{
- format = Format::Hex;
- refresh();
-}
-
-void HexTextView::on_actionFormatOctal_triggered()
-{
- format = Format::Octal;
- refresh();
-}
-
-void HexTextView::on_actionSelect_Block_triggered()
-{
- rangeDialog.open(hexPositionToAddress(ui->hexHexText->textCursor().position()));
-}
-
-
-
-void HexTextView::resizeEvent(QResizeEvent *event)
-{
- QScrollArea::resizeEvent(event);
- refresh();
-}
-
-void HexTextView::wheelEvent(QWheelEvent *event)
-{
- if ( Qt::ControlModifier == event->modifiers() ) {
- const QPoint numDegrees = event->angleDelta() / 8;
- if (!numDegrees.isNull()) {
- const QPoint numSteps = numDegrees / 15;
- if ( 0 != numSteps.y() ) {
- zoomIn(numSteps.y() > 0 ? 1 : -1);
- }
- }
- event->accept();
- return;
- }
-
- event->ignore();
-}
-
-void HexTextView::selectRange(RVA start, RVA end)
-{
- int startPosition;
- int endPosition;
- QTextCursor targetTextCursor;
-
- requestedSelectionStartAddress = start;
- requestedSelectionEndAddress = end;
-
- //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 HexTextView::on_rangeDialogAccepted()
-{
- if (rangeDialog.empty()) {
- refresh(rangeDialog.getStartAddress());
- return;
- }
-
- requestedSelectionStartAddress = rangeDialog.getStartAddress();
- requestedSelectionEndAddress = rangeDialog.getEndAddress();
-
- selectRange(requestedSelectionStartAddress, requestedSelectionEndAddress);
-}
-
-void HexTextView::showOffsets(bool show)
-{
- if (show) {
- ui->hexOffsetText->show();
- Core()->setConfig("asm.offset", 1);
- } else {
- ui->hexOffsetText->hide();
- Core()->setConfig("asm.offset", 0);
- }
-}
-
-void HexTextView::zoomIn(int range)
-{
- ui->hexHexText->zoomIn(range);
- syncScale();
-}
-
-void HexTextView::zoomOut(int range)
-{
- zoomIn(-range);
-}
-
-void HexTextView::zoomReset()
-{
- QFont font(ui->hexHexText->font());
- font.setPointSizeF(defaultFontSize);
- ui->hexHexText->setFont(font);
- syncScale();
-}
-
-void HexTextView::updateWidths()
-{
- // Update width
- auto idealWidth = ui->hexHexText->document()->idealWidth();
- ui->hexHexText->document()->setTextWidth(idealWidth);
-
- ui->hexOffsetText->document()->adjustSize();
- ui->hexOffsetText->setFixedWidth(ui->hexOffsetText->document()->size().width() + 1);
-
- ui->hexASCIIText->document()->adjustSize();
- ui->hexASCIIText->setMinimumWidth(ui->hexASCIIText->document()->size().width());
-}
-
-void HexTextView::syncScale()
-{
- ui->hexOffsetText->setFont(ui->hexHexText->font());
- ui->hexASCIIText->setFont(ui->hexHexText->font());
- ui->offsetHeaderLabel->setFont(ui->hexHexText->font());
- ui->hexHeaderLabel->setFont(ui->hexHexText->font());
- ui->asciiHeaderLabel->setFont(ui->hexHexText->font());
- updateWidths();
-}
diff --git a/src/widgets/HexTextView.h b/src/widgets/HexTextView.h
deleted file mode 100644
index 9384ff25..00000000
--- a/src/widgets/HexTextView.h
+++ /dev/null
@@ -1,158 +0,0 @@
-#ifndef HexTextView_H
-#define HexTextView_H
-
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include "core/Cutter.h"
-#include "MemoryDockWidget.h"
-#include "common/CutterSeekable.h"
-#include "dialogs/HexdumpRangeDialog.h"
-#include "common/Highlighter.h"
-#include "common/HexAsciiHighlighter.h"
-#include "common/HexHighlighter.h"
-#include "common/SvgIconEngine.h"
-
-#include "Dashboard.h"
-
-namespace Ui {
- class HexTextView;
-}
-
-class RefreshDeferrer;
-
-class HexTextView : public QScrollArea
-{
- Q_OBJECT
-public:
- explicit HexTextView(QWidget *parent);
- ~HexTextView();
- Highlighter *highlighter;
- enum Format {
- Hex,
- Octal,
- // TODO:
-// HalfWord,
-// Word,
-// QuadWord,
-// Emoji,
-// SignedInt1,
-// SignedInt2,
-// SignedInt4,
- };
- void refresh(RVA addr = RVA_INVALID);
- /**
- * @brief Select non empty inclusive range [start; end]
- * @param start
- * @param end
- */
- void selectRange(RVA start, RVA end);
- void setupFonts();
-
- struct Selection {
- bool empty;
- RVA startAddress;
- RVA endAddress;
- };
-
- Selection getSelection();
- RVA position();
-public slots:
- void on_rangeDialogAccepted();
- void showOffsets(bool show);
-
- void zoomIn(int range = 1);
- void zoomOut(int range = 1);
- void zoomReset();
-signals:
- void selectionChanged(Selection selection);
- void positionChanged(RVA start);
-protected:
- virtual void resizeEvent(QResizeEvent *event) override;
- virtual void wheelEvent(QWheelEvent *event) override;
-
-private:
- enum Format format = Format::Hex;
-
- std::unique_ptr ui;
-
- bool sent_seek = false;
- bool scroll_disabled = false;
-
- RVA first_loaded_address = RVA_INVALID;
- RVA last_loaded_address = RVA_INVALID;
-
- void updateHeaders();
-
- std::array fetchHexdump(RVA addr, int lines);
-
- void connectScroll(bool disconnect_);
- void setupScrollSync();
-
-
-
- // If bottom = false gets the FIRST displayed line, otherwise the LAST displayed
- // line.
- int getDisplayedLined(QTextEdit *textEdit, bool bottom = false);
-
- static void removeTopLinesWithoutScroll(QTextEdit *textEdit, int lines);
- static void removeBottomLinesWithoutScroll(QTextEdit *textEdit, int lines);
- static void prependWithoutScroll(QTextEdit *textEdit, QString text);
- static void appendWithoutScroll(QTextEdit *textEdit, QString text);
- static void setTextEditPosition(QTextEdit *textEdit, int position);
-
- RVA hexPositionToAddress(int position);
- RVA asciiPositionToAddress(int position);
- int hexAddressToPosition(RVA address);
- int asciiAddressToPosition(RVA address);
- void updateWidths();
- void syncScale();
-
- void updateParseWindow(RVA start_address, int size);
- void clearParseWindow();
-
- int bufferLines = 0;
- int cols = 0;
- ut64 requestedSelectionStartAddress=0;
- ut64 requestedSelectionEndAddress=0;
- HexdumpRangeDialog rangeDialog;
- RVA currentPos = 0;
- qreal defaultFontSize;
- Selection selection;
-
-private slots:
- // Currently unused/untested
- // void highlightHexCurrentLine();
- // void highlightHexWords(const QString &str);
-
- void showHexdumpContextMenu(const QPoint &pt);
- void showHexASCIIContextMenu(const QPoint &pt);
-
- void onSelectionChanged();
- void scrollChanged();
-
- void on_actionCopyAddressAtCursor_triggered();
-
- void on_action1column_triggered();
- void on_action2columns_triggered();
- void on_action4columns_triggered();
- void on_action8columns_triggered();
- void on_action16columns_triggered();
- void on_action32columns_triggered();
- void on_action64columns_triggered();
-
- void on_actionFormatHex_triggered();
- void on_actionFormatOctal_triggered();
-
- void on_actionSelect_Block_triggered();
-
- void fontsUpdated();
-};
-
-#endif // HexTextView_H
diff --git a/src/widgets/HexTextView.ui b/src/widgets/HexTextView.ui
deleted file mode 100644
index ebefbd49..00000000
--- a/src/widgets/HexTextView.ui
+++ /dev/null
@@ -1,418 +0,0 @@
-
-
- HexTextView
-
-
- QFrame::NoFrame
-
-
- true
-
-
-
-
- 0
- 0
- 515
- 770
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- 0 1 2 3 ...
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- false
-
-
- QFrame::NoFrame
-
-
- 0
-
-
- Qt::ScrollBarAlwaysOff
-
-
- Qt::ScrollBarAlwaysOff
-
-
- QAbstractScrollArea::AdjustToContents
-
-
- Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- 0123...
-
-
-
- -
-
-
- Offset
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- Anonymous Pro
- 13
-
-
-
- false
-
-
- QFrame::NoFrame
-
-
- 0
-
-
- Qt::ScrollBarAlwaysOff
-
-
- Qt::ScrollBarAlwaysOff
-
-
- QAbstractScrollArea::AdjustToContents
-
-
- QTextEdit::NoWrap
-
-
- 3
-
-
- Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- false
-
-
- QFrame::NoFrame
-
-
- 0
-
-
- Qt::ScrollBarAlwaysOff
-
-
- Qt::ScrollBarAlwaysOff
-
-
- QAbstractScrollArea::AdjustToContents
-
-
- Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
-
-
-
-
-
-
-
- Undefine
-
-
- Undefine
-
-
-
-
- Copy all
-
-
- Copy all
-
-
-
-
- Copy bytes
-
-
- Copy bytes
-
-
-
-
- Copy disasm
-
-
- Copy disasm
-
-
-
-
- Copy Hexpair
-
-
- Copy Hexpair
-
-
-
-
- Copy ASCII
-
-
- Copy ASCII
-
-
-
-
- Copy Text
-
-
- Copy Text
-
-
-
-
- Copy Address
-
-
- Copy Address at Cursor
-
-
-
-
- 1
-
-
- 1
-
-
-
-
- 2
-
-
- 2
-
-
-
-
- 4
-
-
- 4
-
-
-
-
- 8
-
-
- 8
-
-
-
-
- 16
-
-
- 16
-
-
-
-
- 32
-
-
- 32
-
-
-
-
- 64
-
-
- 64
-
-
-
-
- Edit
-
-
- Edit
-
-
-
-
- Paste
-
-
- Paste
-
-
-
-
- Insert Hex
-
-
- Insert Hex
-
-
-
-
- Insert String
-
-
- Insert String
-
-
-
-
- Hex
-
-
-
-
- Octal
-
-
-
-
- Half-word
-
-
-
-
- Word
-
-
-
-
- Quad-word
-
-
-
-
- Emoji
-
-
-
-
- 1 byte
-
-
- 1 byte
-
-
-
-
- 2 bytes
-
-
- 2 bytes
-
-
-
-
- 4 bytes
-
-
- 4 bytes
-
-
-
-
- Select Block...
-
-
-
-
- true
-
-
- Reset zoom
-
-
- Ctrl+0
-
-
- Qt::WindowShortcut
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/widgets/HexWidget.cpp b/src/widgets/HexWidget.cpp
new file mode 100644
index 00000000..fbbcdcc5
--- /dev/null
+++ b/src/widgets/HexWidget.cpp
@@ -0,0 +1,1190 @@
+#include "HexWidget.h"
+#include "Cutter.h"
+#include "Configuration.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static const uint64_t MAX_COPY_SIZE = 128 * 1024 * 1024;
+static const int MAX_LINE_WIDTH_PRESET = 32;
+static const int MAX_LINE_WIDTH_BYTES = 128 * 1024;
+
+HexWidget::HexWidget(QWidget *parent) :
+ QScrollArea(parent),
+ cursorEnabled(true),
+ cursorOnAscii(false),
+ updatingSelection(false),
+ itemByteLen(1),
+ itemGroupSize(1),
+ rowSizeBytes(16),
+ columnMode(ColumnMode::PowerOf2),
+ itemFormat(ItemFormatHex),
+ itemBigEndian(false),
+ addrCharLen(AddrWidth64),
+ showHeader(true),
+ showAscii(true),
+ showExHex(true),
+ showExAddr(true)
+{
+ setMouseTracking(true);
+ setFocusPolicy(Qt::FocusPolicy::StrongFocus);
+ connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, [this]() { viewport()->update(); });
+
+ connect(Config(), &Configuration::colorsUpdated, this, &HexWidget::updateColors);
+ connect(Config(), &Configuration::fontsUpdated, this, [this]() { setMonospaceFont(Config()->getFont()); });
+
+ auto sizeActionGroup = new QActionGroup(this);
+ for (int i = 1; i <= 8; i *= 2) {
+ QAction *action = new QAction(QString::number(i), this);
+ action->setCheckable(true);
+ action->setActionGroup(sizeActionGroup);
+ connect(action, &QAction::triggered, this, [=]() { setItemSize(i); });
+ actionsItemSize.append(action);
+ }
+ actionsItemSize.at(0)->setChecked(true);
+
+ /* Follow the order in ItemFormat enum */
+ QStringList names;
+ names << tr("Hexadecimal");
+ names << tr("Octal");
+ names << tr("Decimal");
+ names << tr("Signed decimal");
+ names << tr("Float");
+
+ auto formatActionGroup = new QActionGroup(this);
+ for (int i = 0; i < names.length(); ++i) {
+ QAction *action = new QAction(names.at(i), this);
+ action->setCheckable(true);
+ action->setActionGroup(formatActionGroup);
+ connect(action, &QAction::triggered, this, [=]() { setItemFormat(static_cast(i)); });
+ actionsItemFormat.append(action);
+ }
+ actionsItemFormat.at(0)->setChecked(true);
+ actionsItemFormat.at(ItemFormatFloat)->setEnabled(false);
+
+ rowSizeMenu = new QMenu(tr("Bytes per row"), this);
+ auto columnsActionGroup = new QActionGroup(this);
+ for (int i = 1; i <= MAX_LINE_WIDTH_PRESET; i *= 2) {
+ QAction *action = new QAction(QString::number(i), rowSizeMenu);
+ action->setCheckable(true);
+ action->setActionGroup(columnsActionGroup);
+ connect(action, &QAction::triggered, this, [=]() { setFixedLineSize(i); });
+ rowSizeMenu->addAction(action);
+ }
+ rowSizeMenu->addSeparator();
+ actionRowSizePowerOf2 = new QAction(tr("Power of 2"), this);
+ actionRowSizePowerOf2->setCheckable(true);
+ actionRowSizePowerOf2->setActionGroup(columnsActionGroup);
+ connect(actionRowSizePowerOf2, &QAction::triggered, this, [=]() { setColumnMode(ColumnMode::PowerOf2); });
+ rowSizeMenu->addAction(actionRowSizePowerOf2);
+
+ actionItemBigEndian = new QAction(tr("Big Endian"), this);
+ actionItemBigEndian->setCheckable(true);
+ actionItemBigEndian->setEnabled(false);
+ connect(actionItemBigEndian, &QAction::triggered, this, &HexWidget::setItemEndianess);
+
+ actionHexPairs = new QAction(tr("Bytes as pairs"), this);
+ actionHexPairs->setCheckable(true);
+ connect(actionHexPairs, &QAction::triggered, this, &HexWidget::onHexPairsModeEnabled);
+
+ actionCopy = new QAction(tr("Copy"), this);
+ addAction(actionCopy);
+ actionCopy->setShortcut(QKeySequence::Copy);
+ connect(actionCopy, &QAction::triggered, this, &HexWidget::copy);
+
+ actionCopyAddress = new QAction(tr("Copy address"), this);
+ actionCopyAddress->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
+ connect(actionCopyAddress, &QAction::triggered, this, &HexWidget::copyAddress);
+ addAction(actionCopyAddress);
+
+ actionSelectRange = new QAction(tr("Select range"), this);
+ connect(actionSelectRange, &QAction::triggered, this, [this]() { rangeDialog.open(cursor.address); });
+ addAction(actionSelectRange);
+ connect(&rangeDialog, &QDialog::accepted, this, &HexWidget::onRangeDialogAccepted);
+
+ connect(this, &HexWidget::selectionChanged, this, [this](Selection selection) {
+ actionCopy->setEnabled(!selection.empty);
+ });
+
+ updateMetrics();
+ updateItemLength();
+
+ startAddress = 0ULL;
+ cursor.address = 0ULL;
+ data = new MemoryData();
+
+ fetchData();
+ updateCursorMeta();
+
+ connect(&cursor.blinkTimer, &QTimer::timeout, this, &HexWidget::onCursorBlinked);
+ cursor.setBlinkPeriod(1000);
+ cursor.startBlinking();
+
+ updateColors();
+}
+
+HexWidget::~HexWidget()
+{
+
+}
+
+void HexWidget::setMonospaceFont(const QFont &font)
+{
+ if (!(font.styleHint() & QFont::Monospace)) {
+ /* FIXME: Use default monospace font
+ setFont(XXX); */
+ }
+ QScrollArea::setFont(font);
+ monospaceFont = font;
+ updateMetrics();
+ fetchData();
+ updateCursorMeta();
+
+ viewport()->update();
+}
+
+void HexWidget::setItemSize(int nbytes)
+{
+ static const QVector values({1, 2, 4, 8});
+
+ if (!values.contains(nbytes))
+ return;
+
+ itemByteLen = nbytes;
+ if (itemByteLen > rowSizeBytes) {
+ rowSizeBytes = itemByteLen;
+ }
+
+ actionsItemFormat.at(ItemFormatFloat)->setEnabled(nbytes >= 4);
+ actionItemBigEndian->setEnabled(nbytes != 1);
+
+ updateItemLength();
+ fetchData();
+ updateCursorMeta();
+
+ viewport()->update();
+}
+
+void HexWidget::setItemFormat(ItemFormat format)
+{
+ itemFormat = format;
+
+ bool sizeEnabled = true;
+ if (format == ItemFormatFloat)
+ sizeEnabled = false;
+ actionsItemSize.at(0)->setEnabled(sizeEnabled);
+ actionsItemSize.at(1)->setEnabled(sizeEnabled);
+
+
+ updateItemLength();
+ fetchData();
+ updateCursorMeta();
+
+ viewport()->update();
+}
+
+void HexWidget::setItemGroupSize(int size)
+{
+ itemGroupSize = size;
+
+ updateCounts();
+ fetchData();
+ updateCursorMeta();
+
+ viewport()->update();
+}
+
+void HexWidget::updateCounts()
+{
+ actionHexPairs->setEnabled(rowSizeBytes > 1 && itemByteLen == 1
+ && itemFormat == ItemFormat::ItemFormatHex);
+ if (actionHexPairs->isChecked() && actionHexPairs->isEnabled()) {
+ itemGroupSize = 2;
+ } else {
+ itemGroupSize = 1;
+ }
+
+ if (columnMode == ColumnMode::PowerOf2) {
+ int last_good_size = itemGroupByteLen();
+ for (int i = itemGroupByteLen(); i <= MAX_LINE_WIDTH_BYTES; i *= 2) {
+ rowSizeBytes = i;
+ itemColumns = rowSizeBytes / itemGroupByteLen();
+ updateAreasPosition();
+ if (horizontalScrollBar()->maximum() == 0) {
+ last_good_size = rowSizeBytes;
+ } else {
+ break;
+ }
+ }
+ rowSizeBytes = last_good_size;
+ }
+
+ itemColumns = rowSizeBytes / itemGroupByteLen();
+
+ // ensure correct action is selected when changing line size programmatically
+ if (columnMode == ColumnMode::Fixed) {
+ int w = 1;
+ const auto &actions = rowSizeMenu->actions();
+ for (auto action : actions) {
+ action->setChecked(false);
+ }
+ for (auto action : actions) {
+ if (w > MAX_LINE_WIDTH_PRESET) {
+ break;
+ }
+ if (rowSizeBytes == w) {
+ action->setChecked(true);
+ }
+ w *= 2;
+ }
+ } else if (columnMode == ColumnMode::PowerOf2) {
+ actionRowSizePowerOf2->setChecked(true);
+ }
+
+ updateAreasPosition();
+}
+
+void HexWidget::setFixedLineSize(int lineSize)
+{
+ if (lineSize < 1 || lineSize < itemGroupByteLen() || lineSize % itemGroupByteLen()) {
+ updateCounts();
+ return;
+ }
+ rowSizeBytes = lineSize;
+ columnMode = ColumnMode::Fixed;
+
+ updateCounts();
+ fetchData();
+ updateCursorMeta();
+
+ viewport()->update();
+}
+
+void HexWidget::setColumnMode(ColumnMode mode)
+{
+ columnMode = mode;
+
+ updateCounts();
+ fetchData();
+ updateCursorMeta();
+
+ viewport()->update();
+}
+
+void HexWidget::selectRange(RVA start, RVA end)
+{
+ BasicCursor endCursor(end);
+ endCursor += 1;
+ setCursorAddr(endCursor);
+ selection.set(start, end);
+ cursorEnabled = false;
+ emit selectionChanged(getSelection());
+}
+
+void HexWidget::clearSelection()
+{
+ setCursorAddr(cursor.address, false);
+ emit selectionChanged(getSelection());
+}
+
+HexWidget::Selection HexWidget::getSelection()
+{
+ return Selection{selection.isEmpty(), selection.start(), selection.end()};
+}
+
+void HexWidget::seek(uint64_t address)
+{
+ setCursorAddr(address);
+}
+
+void HexWidget::refresh()
+{
+ fetchData();
+ viewport()->update();
+}
+
+void HexWidget::setItemEndianess(bool bigEndian)
+{
+ itemBigEndian = bigEndian;
+
+ updateCursorMeta(); // Update cached item character
+
+ viewport()->update();
+}
+
+void HexWidget::updateColors()
+{
+ borderColor = Config()->getColor("gui.border");
+ backgroundColor = Config()->getColor("gui.background");
+ b0x00Color = Config()->getColor("b0x00");
+ b0x7fColor = Config()->getColor("b0x7f");
+ b0xffColor = Config()->getColor("b0xff");
+ printableColor = Config()->getColor("ai.write");
+ defColor = Config()->getColor("btext");
+ addrColor = Config()->getColor("func_var_addr");
+
+ updateCursorMeta();
+ viewport()->update();
+}
+
+void HexWidget::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(viewport());
+ painter.setFont(monospaceFont);
+
+ int xOffset = horizontalScrollBar()->value();
+ if (xOffset > 0)
+ painter.translate(QPoint(-xOffset, 0));
+
+ if (event->rect() == cursor.screenPos) {
+ /* Cursor blink */
+ drawCursor(painter);
+ return;
+ }
+
+ painter.fillRect(event->rect().translated(xOffset, 0), backgroundColor);
+
+ drawHeader(painter);
+
+ drawAddrArea(painter);
+ drawItemArea(painter);
+ drawAsciiArea(painter);
+
+ if (!cursorEnabled)
+ return;
+
+ drawCursor(painter, true);
+}
+
+void HexWidget::updateWidth()
+{
+ int max = (showAscii ? asciiArea.right() : itemArea.right()) - viewport()->width();
+ if (max < 0)
+ max = 0;
+ else
+ max += charWidth;
+ horizontalScrollBar()->setMaximum(max);
+ horizontalScrollBar()->setSingleStep(charWidth);
+}
+
+void HexWidget::resizeEvent(QResizeEvent *event)
+{
+ int oldByteCount = bytesPerScreen();
+ updateCounts();
+
+ if (event->oldSize().height() == event->size().height() && oldByteCount == bytesPerScreen())
+ return;
+
+ updateAreasHeight();
+ fetchData(); // rowCount was changed
+ updateCursorMeta();
+
+ viewport()->update();
+}
+
+void HexWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ QPoint pos = event->pos();
+ pos.rx() += horizontalScrollBar()->value();
+
+ if (!updatingSelection) {
+ if (itemArea.contains(pos) || asciiArea.contains(pos))
+ setCursor(Qt::IBeamCursor);
+ else
+ setCursor(Qt::ArrowCursor);
+ return;
+ }
+
+ auto &area = currentArea();
+ if (pos.x() < area.left())
+ pos.setX(area.left());
+ else if (pos.x() > area.right())
+ pos.setX(area.right());
+ auto addr = currentAreaPosToAddr(pos, true);
+ setCursorAddr(addr, true);
+
+ /* Stop blinking */
+ cursorEnabled = false;
+
+ viewport()->update();
+}
+
+void HexWidget::mousePressEvent(QMouseEvent *event)
+{
+ QPoint pos(event->pos());
+ pos.rx() += horizontalScrollBar()->value();
+
+ if (event->button() == Qt::LeftButton) {
+ bool selectingData = itemArea.contains(pos);
+ bool selecting = selectingData || asciiArea.contains(pos);
+ if (selecting) {
+ updatingSelection = true;
+ setCursorOnAscii(!selectingData);
+ auto cursorPosition = currentAreaPosToAddr(pos, true);
+ setCursorAddr(cursorPosition, event->modifiers() == Qt::ShiftModifier);
+ viewport()->update();
+ }
+ }
+}
+
+void HexWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton) {
+ if (selection.isEmpty())
+ selection.init(cursor.address);
+ updatingSelection = false;
+ }
+}
+
+void HexWidget::wheelEvent(QWheelEvent *event)
+{
+ int dy = event->delta();
+ int64_t delta = 3 * itemRowByteLen();
+ if (dy > 0)
+ delta = -delta;
+
+ if (dy == 0)
+ return;
+
+ if (delta < 0 && startAddress < -delta) {
+ startAddress = 0;
+ } else if (delta > 0 && data->maxIndex() < bytesPerScreen()) {
+ startAddress = 0;
+ } else if (delta > 0
+ && (data->maxIndex() - startAddress) <= static_cast(bytesPerScreen() + delta - 1)) {
+ startAddress = (data->maxIndex() - bytesPerScreen()) + 1;
+ } else {
+ startAddress += delta;
+ }
+ fetchData();
+ if (cursor.address >= startAddress && cursor.address <= lastVisibleAddr()) {
+ /* Don't enable cursor blinking if selection isn't empty */
+ cursorEnabled = selection.isEmpty();
+ updateCursorMeta();
+ } else {
+ cursorEnabled = false;
+ }
+ viewport()->update();
+}
+
+void HexWidget::keyPressEvent(QKeyEvent *event)
+{
+ bool select = false;
+ auto moveOrSelect = [event, &select](QKeySequence::StandardKey moveSeq, QKeySequence::StandardKey selectSeq) ->bool {
+ if (event->matches(moveSeq)) {
+ select = false;
+ return true;
+ } else if (event->matches(selectSeq)) {
+ select = true;
+ return true;
+ }
+ return false;
+ };
+ if (moveOrSelect(QKeySequence::MoveToNextLine, QKeySequence::SelectNextLine)) {
+ moveCursor(itemRowByteLen(), select);
+ } else if (moveOrSelect(QKeySequence::MoveToPreviousLine, QKeySequence::SelectPreviousLine)) {
+ moveCursor(-itemRowByteLen(), select);
+ } else if (moveOrSelect(QKeySequence::MoveToNextChar, QKeySequence::SelectNextChar)) {
+ moveCursor(cursorOnAscii ? 1 : itemByteLen, select);
+ } else if (moveOrSelect(QKeySequence::MoveToPreviousChar, QKeySequence::SelectPreviousChar)) {
+ moveCursor(cursorOnAscii ? -1 : -itemByteLen, select);
+ } else if (moveOrSelect(QKeySequence::MoveToNextPage, QKeySequence::SelectNextPage)) {
+ moveCursor(bytesPerScreen(), select);
+ } else if (moveOrSelect(QKeySequence::MoveToPreviousPage, QKeySequence::SelectPreviousPage)) {
+ moveCursor(-bytesPerScreen(), select);
+ } else if (moveOrSelect(QKeySequence::MoveToStartOfLine, QKeySequence::SelectStartOfLine)) {
+ int linePos = int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));
+ moveCursor(-linePos, select);
+ } else if (moveOrSelect(QKeySequence::MoveToEndOfLine, QKeySequence::SelectEndOfLine)) {
+ int linePos = int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));
+ moveCursor(itemRowByteLen() - linePos, select);
+ }
+ //viewport()->update();
+}
+
+void HexWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ QPoint pt = event->pos();
+ if (event->reason() == QContextMenuEvent::Mouse) {
+ auto mouseAddr = mousePosToAddr(pt).address;
+ if (selection.isEmpty() || !(mouseAddr >= selection.start() && mouseAddr <= selection.end())) {
+ cursorOnAscii = asciiArea.contains(pt);
+ seek(mouseAddr);
+ }
+ }
+
+ QMenu *menu = new QMenu();
+ QMenu *sizeMenu = menu->addMenu(tr("Item size:"));
+ sizeMenu->addActions(actionsItemSize);
+ QMenu *formatMenu = menu->addMenu(tr("Item format:"));
+ formatMenu->addActions(actionsItemFormat);
+ menu->addMenu(rowSizeMenu);
+ menu->addAction(actionHexPairs);
+ menu->addAction(actionItemBigEndian);
+ menu->addSeparator();
+ menu->addAction(actionCopy);
+ menu->addAction(actionCopyAddress);
+ menu->addActions(this->actions());
+ menu->exec(mapToGlobal(pt));
+ menu->deleteLater();
+}
+
+void HexWidget::onCursorBlinked()
+{
+ if (!cursorEnabled)
+ return;
+ cursor.blink();
+ viewport()->update(cursor.screenPos.translated(-horizontalScrollBar()->value(), 0));
+}
+
+void HexWidget::onHexPairsModeEnabled(bool enable)
+{
+ if (enable) {
+ setItemGroupSize(2);
+ } else {
+ setItemGroupSize(1);
+ }
+}
+
+void HexWidget::copy()
+{
+ if (selection.isEmpty() || selection.size() > MAX_COPY_SIZE)
+ return;
+
+ QClipboard *clipboard = QApplication::clipboard();
+ QString range = QString("%1@0x%2").arg(selection.size()).arg(selection.start(), 0, 16);
+ if (cursorOnAscii) {
+ clipboard->setText(Core()->cmd("psx " + range));
+ } else {
+ clipboard->setText(Core()->cmd("p8 " + range)); //TODO: copy in the format shown
+ }
+}
+
+void HexWidget::copyAddress()
+{
+ uint64_t addr = cursor.address;
+ if (!selection.isEmpty()) {
+ addr = selection.start();
+ }
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(RAddressString(addr));
+}
+
+void HexWidget::onRangeDialogAccepted()
+{
+ if (rangeDialog.empty()) {
+ seek(rangeDialog.getStartAddress());
+ return;
+ }
+ selectRange(rangeDialog.getStartAddress(), rangeDialog.getEndAddress());
+}
+
+void HexWidget::updateItemLength()
+{
+ itemPrefixLen = 0;
+
+ switch (itemFormat) {
+ case ItemFormatHex:
+ itemCharLen = 2 * itemByteLen;
+ if (itemByteLen > 1 && showExHex)
+ itemPrefixLen = hexPrefix.length();
+ break;
+ case ItemFormatOct:
+ itemCharLen = (itemByteLen * 8 + 3) / 3;
+ break;
+ case ItemFormatDec:
+ switch (itemByteLen) {
+ case 1:
+ itemCharLen = 3;
+ break;
+ case 2:
+ itemCharLen = 5;
+ break;
+ case 4:
+ itemCharLen = 10;
+ break;
+ case 8:
+ itemCharLen = 20;
+ break;
+ }
+ break;
+ case ItemFormatSignedDec:
+ switch (itemByteLen) {
+ case 1:
+ itemCharLen = 4;
+ break;
+ case 2:
+ itemCharLen = 6;
+ break;
+ case 4:
+ itemCharLen = 11;
+ break;
+ case 8:
+ itemCharLen = 20;
+ break;
+ }
+ break;
+ case ItemFormatFloat:
+ if (itemByteLen < 4)
+ itemByteLen = 4;
+ // FIXME
+ itemCharLen = 3 * itemByteLen;
+ break;
+ }
+
+ itemCharLen += itemPrefixLen;
+
+ updateCounts();
+}
+
+void HexWidget::drawHeader(QPainter &painter)
+{
+ if (!showHeader)
+ return;
+
+ int offset = 0;
+ QRect rect(itemArea.left(), 0, itemWidth(), lineHeight);
+
+ painter.setPen(addrColor);
+
+ for (int j = 0; j < itemColumns; ++j) {
+ for (int k = 0; k < itemGroupSize; ++k, offset += itemByteLen) {
+ painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, QString::number(offset, 16).toUpper());
+ rect.translate(itemWidth(), 0);
+ }
+ rect.translate(columnSpacingWidth(), 0);
+ }
+
+ rect.moveLeft(asciiArea.left());
+ rect.setWidth(charWidth);
+ for (int j = 0; j < itemRowByteLen(); ++j) {
+ painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, QString::number(j % 16, 16).toUpper());
+ rect.translate(charWidth, 0);
+ }
+}
+
+void HexWidget::drawCursor(QPainter &painter, bool shadow)
+{
+ if (shadow) {
+ QPen pen(Qt::gray);
+ pen.setStyle(Qt::DashLine);
+ painter.setPen(pen);
+ painter.drawRect(shadowCursor.screenPos);
+ painter.setPen(Qt::SolidLine);
+ }
+
+ painter.setPen(cursor.cachedColor);
+ QRect charRect(cursor.screenPos);
+ charRect.setWidth(charWidth);
+ painter.fillRect(charRect, backgroundColor);
+ painter.drawText(charRect, Qt::AlignVCenter, cursor.cachedChar);
+ if (cursor.isVisible) {
+ painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
+ painter.fillRect(cursor.screenPos, QColor(0xff, 0xff, 0xff));
+ }
+}
+
+void HexWidget::drawAddrArea(QPainter &painter)
+{
+ uint64_t offset = startAddress;
+ QString addrString;
+ QSize areaSize((addrCharLen + (showExAddr ? 2 : 0)) * charWidth, lineHeight);
+ QRect strRect(addrArea.topLeft(), areaSize);
+
+ painter.setPen(addrColor);
+ for (int line = 0;
+ line < visibleLines && offset <= data->maxIndex();
+ ++line, strRect.translate(0, lineHeight), offset += itemRowByteLen()) {
+ addrString = QString("%1").arg(offset, addrCharLen, 16, QLatin1Char('0'));
+ if (showExAddr)
+ addrString.prepend(hexPrefix);
+ painter.drawText(strRect, Qt::AlignVCenter, addrString);
+ }
+
+ painter.setPen(borderColor);
+
+ int vLineOffset = itemArea.left() - charWidth;
+ painter.drawLine(vLineOffset, 0, vLineOffset, viewport()->height());
+}
+
+void HexWidget::drawItemArea(QPainter &painter)
+{
+ QRect itemRect(itemArea.topLeft(), QSize(itemWidth(), lineHeight));
+ QColor itemColor;
+ QString itemString;
+
+ fillSelectionBackground(painter);
+
+ uint64_t itemAddr = startAddress;
+ for (int line = 0; line < visibleLines; ++line) {
+ itemRect.moveLeft(itemArea.left());
+ for (int j = 0; j < itemColumns; ++j) {
+ for (int k = 0; k < itemGroupSize && itemAddr <= data->maxIndex(); ++k, itemAddr += itemByteLen) {
+ itemString = renderItem(itemAddr - startAddress, &itemColor);
+ if (selection.contains(itemAddr))
+ itemColor = palette().highlightedText().color();
+ painter.setPen(itemColor);
+ painter.drawText(itemRect, Qt::AlignVCenter, itemString);
+ itemRect.translate(itemWidth(), 0);
+ if (cursor.address == itemAddr) {
+ auto &itemCursor = cursorOnAscii ? shadowCursor : cursor;
+ itemCursor.cachedChar = itemString.at(0);
+ itemCursor.cachedColor = itemColor;
+ }
+ }
+ itemRect.translate(columnSpacingWidth(), 0);
+ }
+ itemRect.translate(0, lineHeight);
+ }
+
+ painter.setPen(borderColor);
+
+ int vLineOffset = asciiArea.left() - charWidth;
+ painter.drawLine(vLineOffset, 0, vLineOffset, viewport()->height());
+}
+
+void HexWidget::drawAsciiArea(QPainter &painter)
+{
+ QRect charRect(asciiArea.topLeft(), QSize(charWidth, lineHeight));
+
+ fillSelectionBackground(painter, true);
+
+ uint64_t address = startAddress;
+ QChar ascii;
+ QColor color;
+ for (int line = 0; line < visibleLines; ++line, charRect.translate(0, lineHeight)) {
+ charRect.moveLeft(asciiArea.left());
+ for (int j = 0; j < itemRowByteLen() && address <= data->maxIndex(); ++j, ++address) {
+ ascii = renderAscii(address - startAddress, &color);
+ if (selection.contains(address))
+ color = palette().highlightedText().color();
+ painter.setPen(color);
+ /* Dots look ugly. Use fillRect() instead of drawText(). */
+ if (ascii == '.') {
+ int a = cursor.screenPos.width();
+ int x = charRect.left() + (charWidth - a) / 2 + 1;
+ int y = charRect.bottom() - 2 * a;
+ painter.fillRect(x, y, a, a, color);
+ } else {
+ painter.drawText(charRect, Qt::AlignVCenter, ascii);
+ }
+ charRect.translate(charWidth, 0);
+ if (cursor.address == address) {
+ auto &itemCursor = cursorOnAscii ? cursor : shadowCursor;
+ itemCursor.cachedChar = ascii;
+ itemCursor.cachedColor = color;
+ }
+ }
+ }
+}
+
+void HexWidget::fillSelectionBackground(QPainter &painter, bool ascii)
+{
+ QRect rect;
+ const QRect *area = ascii ? &asciiArea : &itemArea;
+
+ int startOffset = -1;
+ int endOffset = -1;
+
+ if (!selection.intersects(startAddress, lastVisibleAddr())) {
+ return;
+ }
+
+ /* Convert absolute values to relative */
+ startOffset = std::max(selection.start(), startAddress) - startAddress;
+ endOffset = std::min(selection.end(), lastVisibleAddr()) - startAddress;
+
+ /* Align values */
+ int startOffset2 = (startOffset + itemRowByteLen()) & ~(itemRowByteLen() - 1);
+ int endOffset2 = endOffset & ~(itemRowByteLen() - 1);
+
+ QColor highlightColor = palette().color(QPalette::Highlight);
+
+ /* Fill top/bottom parts */
+ if (startOffset2 <= endOffset2) {
+ /* Fill the top part even if it's a whole line */
+ rect = ascii ? asciiRectangle(startOffset) : itemRectangle(startOffset);
+ rect.setRight(area->right());
+ painter.fillRect(rect, highlightColor);
+ /* Fill the bottom part even if it's a whole line */
+ rect = ascii ? asciiRectangle(endOffset) : itemRectangle(endOffset);
+ rect.setLeft(area->left());
+ painter.fillRect(rect, highlightColor);
+ /* Required for calculating the bottomRight() of the main part */
+ --endOffset2;
+ } else {
+ startOffset2 = startOffset;
+ endOffset2 = endOffset;
+ }
+
+ /* Fill the main part */
+ if (startOffset2 <= endOffset2) {
+ if (ascii) {
+ rect = asciiRectangle(startOffset2);
+ rect.setBottomRight(asciiRectangle(endOffset2).bottomRight());
+ } else {
+ rect = itemRectangle(startOffset2);
+ rect.setBottomRight(itemRectangle(endOffset2).bottomRight());
+ }
+ painter.fillRect(rect, highlightColor);
+ }
+}
+
+void HexWidget::updateMetrics()
+{
+ lineHeight = fontMetrics().height();
+ charWidth = fontMetrics().width(QLatin1Char('F'));
+
+ updateCounts();
+ updateAreasHeight();
+
+ int cursorWidth = charWidth / 3;
+ if (cursorWidth == 0)
+ cursorWidth = 1;
+ cursor.screenPos.setHeight(lineHeight);
+ shadowCursor.screenPos.setHeight(lineHeight);
+
+ cursor.screenPos.setWidth(cursorWidth);
+ if (cursorOnAscii) {
+ cursor.screenPos.moveTopLeft(asciiArea.topLeft());
+
+ shadowCursor.screenPos.setWidth(itemWidth());
+ shadowCursor.screenPos.moveTopLeft(itemArea.topLeft());
+ } else {
+ cursor.screenPos.moveTopLeft(itemArea.topLeft());
+
+ shadowCursor.screenPos.setWidth(charWidth);
+ shadowCursor.screenPos.moveTopLeft(asciiArea.topLeft());
+ }
+}
+
+void HexWidget::updateAreasPosition()
+{
+ const int spacingWidth = areaSpacingWidth();
+
+ int yOffset = showHeader ? lineHeight : 0;
+
+ addrArea.setTopLeft(QPoint(0, yOffset));
+ addrArea.setWidth((addrCharLen + (showExAddr ? 2 : 0)) * charWidth);
+
+ itemArea.setTopLeft(QPoint(addrArea.right() + spacingWidth, yOffset));
+ itemArea.setWidth(itemRowWidth());
+
+ asciiArea.setTopLeft(QPoint(itemArea.right() + spacingWidth, yOffset));
+ asciiArea.setWidth(asciiRowWidth());
+
+ updateWidth();
+}
+
+void HexWidget::updateAreasHeight()
+{
+ visibleLines = (viewport()->height() - itemArea.top()) / lineHeight;
+
+ int height = visibleLines * lineHeight;
+ addrArea.setHeight(height);
+ itemArea.setHeight(height);
+ asciiArea.setHeight(height);
+}
+
+void HexWidget::moveCursor(int offset, bool select)
+{
+ BasicCursor addr = cursor.address;
+ addr += offset;
+ if (addr.address > data->maxIndex()) {
+ addr.address = data->maxIndex();
+ }
+ setCursorAddr(addr, select);
+}
+
+void HexWidget::setCursorAddr(BasicCursor addr, bool select)
+{
+ if (!select) {
+ bool clearingSelection = !selection.isEmpty();
+ selection.init(addr);
+ if (clearingSelection)
+ emit selectionChanged(getSelection());
+ }
+ emit positionChanged(addr.address);
+
+ cursor.address = addr.address;
+
+ /* Pause cursor repainting */
+ cursorEnabled = false;
+
+ if (select) {
+ selection.update(addr);
+ emit selectionChanged(getSelection());
+ }
+
+ uint64_t addressValue = cursor.address;
+ /* Update data cache if necessary */
+ if (!(addressValue >= startAddress && addressValue <= lastVisibleAddr())) {
+ /* Align start address */
+ addressValue -= (addressValue % itemRowByteLen());
+
+ if (addressValue > (data->maxIndex() - bytesPerScreen()) + 1) {
+ addressValue = (data->maxIndex() - bytesPerScreen()) + 1;
+ }
+
+ /* FIXME: handling Page Up/Down */
+ if (addressValue == startAddress + bytesPerScreen()) {
+ startAddress += itemRowByteLen();
+ } else {
+ startAddress = addressValue;
+ }
+
+ fetchData();
+ }
+
+ updateCursorMeta();
+
+ /* Draw cursor */
+ cursor.isVisible = !select;
+ viewport()->update();
+
+ /* Resume cursor repainting */
+ cursorEnabled = selection.isEmpty();
+}
+
+void HexWidget::updateCursorMeta()
+{
+ QPoint point;
+ QPoint pointAscii;
+
+ int offset = cursor.address - startAddress;
+ int itemOffset = offset;
+ int asciiOffset;
+
+ /* Calc common Y coordinate */
+ point.ry() = (itemOffset / itemRowByteLen()) * lineHeight;
+ pointAscii.setY(point.y());
+ itemOffset %= itemRowByteLen();
+ asciiOffset = itemOffset;
+
+ /* Calc X coordinate on the item area */
+ point.rx() = (itemOffset / itemGroupByteLen()) * columnExWidth();
+ itemOffset %= itemGroupByteLen();
+ point.rx() += (itemOffset / itemByteLen) * itemWidth();
+
+ /* Calc X coordinate on the ascii area */
+ pointAscii.rx() = asciiOffset * charWidth;
+
+ point += itemArea.topLeft();
+ pointAscii += asciiArea.topLeft();
+
+ cursor.screenPos.moveTopLeft(cursorOnAscii ? pointAscii : point);
+ shadowCursor.screenPos.moveTopLeft(cursorOnAscii ? point : pointAscii);
+}
+
+void HexWidget::setCursorOnAscii(bool ascii)
+{
+ cursorOnAscii = ascii;
+}
+
+const QColor HexWidget::itemColor(uint8_t byte)
+{
+ QColor color(defColor);
+
+ if (byte == 0x00)
+ color = b0x00Color;
+ else if (byte == 0x7f)
+ color = b0x7fColor;
+ else if (byte == 0xff)
+ color = b0xffColor;
+ else if (IS_PRINTABLE(byte)) {
+ color = printableColor;
+ }
+
+ return color;
+}
+
+QVariant HexWidget::readItem(int offset, QColor *color)
+{
+ quint8 byte;
+ quint16 word;
+ quint32 dword;
+ quint64 qword;
+ float *ptrFloat32;
+ double *ptrFloat64;
+
+ const void *dataPtr = data->dataPtr(startAddress + offset);
+ const bool signedItem = itemFormat == ItemFormatSignedDec;
+
+ switch (itemByteLen) {
+ case 1:
+ byte = *static_cast(dataPtr);
+ if (color)
+ *color = itemColor(byte);
+ if (!signedItem)
+ return QVariant(static_cast(byte));
+ return QVariant(static_cast(static_cast(byte)));
+ case 2:
+ if (itemBigEndian)
+ word = qFromBigEndian(dataPtr);
+ else
+ word = qFromLittleEndian(dataPtr);
+ if (color)
+ *color = defColor;
+ if (!signedItem)
+ return QVariant(static_cast(word));
+ return QVariant(static_cast(static_cast(word)));
+ case 4:
+ if (itemBigEndian)
+ dword = qFromBigEndian(dataPtr);
+ else
+ dword = qFromLittleEndian(dataPtr);
+ if (color)
+ *color = defColor;
+ if (itemFormat == ItemFormatFloat) {
+ ptrFloat32 = static_cast(static_cast(&dword));
+ return QVariant(*ptrFloat32);
+ }
+ if (!signedItem)
+ return QVariant(static_cast(dword));
+ return QVariant(static_cast(static_cast(dword)));
+ case 8:
+ if (itemBigEndian)
+ qword = qFromBigEndian(dataPtr);
+ else
+ qword = qFromLittleEndian(dataPtr);
+ if (color)
+ *color = defColor;
+ if (itemFormat == ItemFormatFloat) {
+ ptrFloat64 = static_cast(static_cast(&qword));
+ return QVariant(*ptrFloat64);
+ }
+ if (!signedItem)
+ return QVariant(qword);
+ return QVariant(static_cast(qword));
+ }
+
+ return QVariant();
+}
+
+QString HexWidget::renderItem(int offset, QColor *color)
+{
+ QString item;
+ QVariant itemVal = readItem(offset, color);
+ int itemLen = itemCharLen - itemPrefixLen; /* Reserve space for prefix */
+
+ //FIXME: handle broken itemVal ( QVariant() )
+ switch (itemFormat) {
+ case ItemFormatHex:
+ item = QString("%1").arg(itemVal.toULongLong(), itemLen, 16, QLatin1Char('0'));
+ if (itemByteLen > 1 && showExHex)
+ item.prepend(hexPrefix);
+ break;
+ case ItemFormatOct:
+ item = QString("%1").arg(itemVal.toULongLong(), itemLen, 8, QLatin1Char('0'));
+ break;
+ case ItemFormatDec:
+ item = QString("%1").arg(itemVal.toULongLong(), itemLen, 10);
+ break;
+ case ItemFormatSignedDec:
+ item = QString("%1").arg(itemVal.toLongLong(), itemLen, 10);
+ break;
+ case ItemFormatFloat:
+ item = QString("%1").arg(itemVal.toDouble(), itemLen);
+ break;
+ }
+
+ return item;
+}
+
+QChar HexWidget::renderAscii(int offset, QColor *color)
+{
+ uchar byte = *static_cast(data->dataPtr(startAddress + offset));
+ if (color) {
+ *color = itemColor(byte);
+ }
+ if (!IS_PRINTABLE(byte)) {
+ byte = '.';
+ }
+ return QChar(byte);
+}
+
+void HexWidget::fetchData()
+{
+ data->fetch(startAddress, bytesPerScreen());
+}
+
+BasicCursor HexWidget::screenPosToAddr(const QPoint &point, bool middle) const
+{
+ QPoint pt = point - itemArea.topLeft();
+
+ int relativeAddress = 0;
+ relativeAddress += (pt.y() / lineHeight) * itemRowByteLen();
+ relativeAddress += (pt.x() / columnExWidth()) * itemGroupByteLen();
+ pt.rx() %= columnExWidth();
+ auto roundingOffset = middle ? itemWidth() / 2 : 0;
+ relativeAddress += ((pt.x() + roundingOffset) / itemWidth()) * itemByteLen;
+ BasicCursor result(startAddress);
+ result += relativeAddress;
+ return result;
+}
+
+BasicCursor HexWidget::asciiPosToAddr(const QPoint &point, bool middle) const
+{
+ QPoint pt = point - asciiArea.topLeft();
+
+ int relativeAddress = 0;
+ relativeAddress += (pt.y() / lineHeight) * itemRowByteLen();
+ auto roundingOffset = middle ? (charWidth / 2) : 0;
+ relativeAddress += (pt.x() + (roundingOffset)) / charWidth;
+ BasicCursor result(startAddress);
+ result += relativeAddress;
+ return result;
+}
+
+BasicCursor HexWidget::currentAreaPosToAddr(const QPoint &point, bool middle) const
+{
+ return cursorOnAscii ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);
+}
+
+BasicCursor HexWidget::mousePosToAddr(const QPoint &point, bool middle) const
+{
+ return asciiArea.contains(point) ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);
+}
+
+QRect HexWidget::itemRectangle(uint offset)
+{
+ int x;
+ int y;
+
+ y = (offset / itemRowByteLen()) * lineHeight;
+ offset %= itemRowByteLen();
+
+ x = (offset / itemGroupByteLen()) * columnExWidth();
+ offset %= itemGroupByteLen();
+ x += (offset / itemByteLen) * itemWidth();
+
+ x += itemArea.x();
+ y += itemArea.y();
+
+ return QRect(x, y, itemWidth(), lineHeight);
+}
+
+QRect HexWidget::asciiRectangle(uint offset)
+{
+ int x;
+ int y;
+
+ y = (offset / itemRowByteLen()) * lineHeight;
+ offset %= itemRowByteLen();
+
+ x = offset * charWidth;
+
+ x += asciiArea.x();
+ y += asciiArea.y();
+
+ return QRect(x, y, charWidth, lineHeight);
+}
diff --git a/src/widgets/HexWidget.h b/src/widgets/HexWidget.h
new file mode 100644
index 00000000..c19a2c51
--- /dev/null
+++ b/src/widgets/HexWidget.h
@@ -0,0 +1,463 @@
+#ifndef HEXWIDGET_H
+#define HEXWIDGET_H
+
+#include "Cutter.h"
+#include "dialogs/HexdumpRangeDialog.h"
+#include
+#include
+#include
+
+struct BasicCursor
+{
+ uint64_t address;
+ bool pastEnd;
+ BasicCursor(uint64_t pos) : address(pos), pastEnd(false) {}
+ BasicCursor() : address(0), pastEnd(false) {}
+ BasicCursor &operator+=(int64_t offset)
+ {
+ if (offset < 0 && uint64_t(-offset) > address) {
+ address = 0;
+ pastEnd = false;
+ } else if (offset > 0 && uint64_t(offset) > (UINT64_MAX - address)) {
+ address = UINT64_MAX;
+ pastEnd = true;
+ } else {
+ address += uint64_t(offset);
+ pastEnd = false;
+ }
+ return *this;
+ }
+ BasicCursor &operator+=(int offset)
+ {
+ *this += int64_t(offset);
+ return *this;
+ }
+ BasicCursor &operator+=(uint64_t offset)
+ {
+ if (uint64_t(offset) > (UINT64_MAX - address)) {
+ address = UINT64_MAX;
+ pastEnd = true;
+ } else {
+ address += offset;
+ pastEnd = false;
+ }
+ return *this;
+ }
+ bool operator<(const BasicCursor &r)
+ {
+ return address < r.address || (pastEnd < r.pastEnd);
+ }
+};
+
+struct HexCursor
+{
+ HexCursor() { isVisible = false; onAsciiArea = false; }
+
+ bool isVisible;
+ bool onAsciiArea;
+ QTimer blinkTimer;
+ QRect screenPos;
+ uint64_t address;
+ QString cachedChar;
+ QColor cachedColor;
+
+ void blink() { isVisible = !isVisible; }
+ void setBlinkPeriod(int msec) { blinkTimer.setInterval(msec / 2); }
+ void startBlinking() { blinkTimer.start(); }
+ void stopBlinking() { blinkTimer.stop(); }
+};
+
+class AbstractData
+{
+public:
+ virtual ~AbstractData() {}
+ virtual void fetch(uint64_t addr, int len) = 0;
+ virtual const void *dataPtr(uint64_t addr) = 0;
+ virtual uint64_t maxIndex() = 0;
+};
+
+class BufferData : public AbstractData
+{
+public:
+ BufferData()
+ {
+ m_buffer.fill(0, 1);
+ }
+
+ BufferData(const QByteArray &buffer)
+ {
+ if (buffer.isEmpty()) {
+ m_buffer.fill(0, 1);
+ } else {
+ m_buffer = buffer;
+ }
+ }
+
+ ~BufferData() override {}
+
+ void fetch(uint64_t addr, int len) override {}
+
+ const void *dataPtr(uint64_t addr) override
+ {
+ return m_buffer.constData() + addr;
+ }
+
+ uint64_t maxIndex() override
+ {
+ return m_buffer.size() - 1;
+ }
+
+private:
+ QByteArray m_buffer;
+};
+
+class MemoryData : public AbstractData
+{
+public:
+ MemoryData() {}
+ ~MemoryData() override {}
+
+ void fetch(uint64_t address, int length) override
+ {
+ // FIXME: reuse data if possible
+ uint64_t alignedAddr = address & ~(4096ULL - 1);
+ int offset = address - alignedAddr;
+ int len = (offset + length + (4096 - 1)) & ~(4096 - 1);
+ m_firstBlockAddr = alignedAddr;
+ m_blocks.clear();
+ uint64_t addr = alignedAddr;
+ for (int i = 0; i < len / 4096; ++i, addr += 4096) {
+ m_blocks.append(Core()->ioRead(addr, 4096));
+ }
+ }
+
+ const void *dataPtr(uint64_t addr) override
+ {
+ int totalOffset = addr - m_firstBlockAddr;
+ int blockId = totalOffset / 4096;
+ int blockOffset = totalOffset % 4096;
+ return static_cast(m_blocks.at(blockId).constData() + blockOffset);
+ }
+
+ virtual uint64_t maxIndex() override
+ {
+ return UINT64_MAX;
+ }
+
+private:
+ QVector m_blocks;
+ uint64_t m_firstBlockAddr;
+};
+
+class HexSelection
+{
+public:
+ HexSelection() { m_empty = true; }
+
+ inline void init(BasicCursor addr)
+ {
+ m_empty = true;
+ m_init = addr;
+ }
+
+ void set(uint64_t start, uint64_t end)
+ {
+ m_empty = false;
+ m_init = m_start = start;
+ m_end = end;
+ }
+
+ void update(BasicCursor addr)
+ {
+ m_empty = false;
+ if (m_init < addr) {
+ m_start = m_init.address;
+ m_end = addr.address;
+ if (!addr.pastEnd)
+ m_end -= 1;
+ } else if (addr < m_init) {
+ m_start = addr.address;
+ m_end = m_init.address;
+ if (!m_init.pastEnd)
+ m_end -= 1;
+ } else {
+ m_start = m_end = m_init.address;
+ m_empty = true;
+ }
+ }
+
+ bool intersects(uint64_t start, uint64_t end)
+ {
+ return !m_empty && m_end >= start && m_start <= end;
+ }
+
+ bool contains(uint64_t pos) const
+ {
+ return !m_empty && m_start <= pos && pos <= m_end;
+ }
+
+ uint64_t size()
+ {
+ uint64_t size = 0;
+ if (!isEmpty())
+ size = m_end - m_start + 1;
+ return size;
+ }
+
+ inline bool isEmpty() { return m_empty; }
+ inline uint64_t start() { return m_start; }
+ inline uint64_t end() { return m_end; }
+
+private:
+ BasicCursor m_init;
+ uint64_t m_start;
+ uint64_t m_end;
+ bool m_empty;
+};
+
+class HexWidget : public QScrollArea
+{
+ Q_OBJECT
+
+public:
+ explicit HexWidget(QWidget *parent = nullptr);
+ ~HexWidget();
+
+ void setMonospaceFont(const QFont &font);
+
+ enum AddrWidth { AddrWidth32 = 8, AddrWidth64 = 16 };
+ enum ItemSize { ItemSizeByte = 1, ItemSizeWord = 2, ItemSizeDword = 4, ItemSizeQword = 8 };
+ enum ItemFormat { ItemFormatHex, ItemFormatOct, ItemFormatDec, ItemFormatSignedDec, ItemFormatFloat };
+ enum class ColumnMode { Fixed, PowerOf2 };
+
+ void setItemSize(int nbytes);
+ void setItemFormat(ItemFormat format);
+ void setItemEndianess(bool bigEndian);
+ void setItemGroupSize(int size);
+ /**
+ * @brief Sets line size in bytes.
+ * Changes column mode to fixed. Command can be rejected if current item format is bigger than requested size.
+ * @param bytes line size in bytes.
+ */
+ void setFixedLineSize(int bytes);
+ void setColumnMode(ColumnMode mode);
+
+ /**
+ * @brief Select non empty inclusive range [start; end]
+ * @param start
+ * @param end
+ */
+ void selectRange(RVA start, RVA end);
+ void clearSelection();
+
+ struct Selection {
+ bool empty;
+ RVA startAddress;
+ RVA endAddress;
+ };
+ Selection getSelection();
+public slots:
+ void seek(uint64_t address);
+ void refresh();
+ void updateColors();
+signals:
+ void selectionChanged(Selection selection);
+ void positionChanged(RVA start);
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void wheelEvent(QWheelEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void contextMenuEvent(QContextMenuEvent *event) override;
+
+private slots:
+ void onCursorBlinked();
+ void onHexPairsModeEnabled(bool enable);
+ void copy();
+ void copyAddress();
+ void onRangeDialogAccepted();
+
+private:
+ void updateItemLength();
+ void updateCounts();
+ void drawHeader(QPainter &painter);
+ void drawCursor(QPainter &painter, bool shadow = false);
+ void drawAddrArea(QPainter &painter);
+ void drawItemArea(QPainter &painter);
+ void drawAsciiArea(QPainter &painter);
+ void fillSelectionBackground(QPainter &painter, bool ascii = false);
+ void updateMetrics();
+ void updateAreasPosition();
+ void updateAreasHeight();
+ void moveCursor(int offset, bool select = false);
+ void setCursorAddr(BasicCursor addr, bool select = false);
+ void updateCursorMeta();
+ void setCursorOnAscii(bool ascii);
+ const QColor itemColor(uint8_t byte);
+ QVariant readItem(int offset, QColor *color = nullptr);
+ QString renderItem(int offset, QColor *color = nullptr);
+ QChar renderAscii(int offset, QColor *color = nullptr);
+ void fetchData();
+ /**
+ * @brief Convert mouse position to address.
+ * @param point mouse position in widget
+ * @param middle start next position from middle of symbol. Use middle=true for vertical cursror position between symbols,
+ * middle=false for insert mode cursor and getting symbol under cursor.
+ * @return
+ */
+ BasicCursor screenPosToAddr(const QPoint &point, bool middle = false) const;
+ BasicCursor asciiPosToAddr(const QPoint &point, bool middle = false) const;
+ BasicCursor currentAreaPosToAddr(const QPoint &point, bool middle = false) const;
+ BasicCursor mousePosToAddr(const QPoint &point, bool middle = false) const;
+ QRect itemRectangle(uint offset);
+ QRect asciiRectangle(uint offset);
+ void updateWidth();
+
+ inline int itemWidth() const
+ {
+ return itemCharLen * charWidth;
+ }
+
+ inline int itemGroupCharLen() const
+ {
+ return itemCharLen * itemGroupSize;
+ }
+
+ inline int columnExCharLen() const
+ {
+ return itemGroupCharLen() + columnSpacing;
+ }
+
+ inline int itemGroupByteLen() const
+ {
+ return itemByteLen * itemGroupSize;
+ }
+
+ inline int columnWidth() const
+ {
+ return itemGroupCharLen() * charWidth;
+ }
+
+ inline int columnExWidth() const
+ {
+ return columnExCharLen() * charWidth;
+ }
+
+ inline int columnSpacingWidth() const
+ {
+ return columnSpacing * charWidth;
+ }
+
+ inline int itemRowCharLen() const
+ {
+ return itemColumns * columnExCharLen() - columnSpacing;
+ }
+
+ inline int itemRowByteLen() const
+ {
+ return rowSizeBytes;
+ }
+
+ inline int bytesPerScreen() const
+ {
+ return itemRowByteLen() * visibleLines;
+ }
+
+ inline int itemRowWidth() const
+ {
+ return itemRowCharLen() * charWidth;
+ }
+
+ inline int asciiRowWidth() const
+ {
+ return itemRowByteLen() * charWidth;
+ }
+
+ inline int areaSpacingWidth() const
+ {
+ return areaSpacing * charWidth;
+ }
+
+ inline uint64_t lastVisibleAddr() const
+ {
+ return (startAddress - 1) + bytesPerScreen();
+ }
+
+ const QRect ¤tArea() const
+ {
+ return cursorOnAscii ? asciiArea : itemArea;
+ }
+
+ bool cursorEnabled;
+ bool cursorOnAscii;
+ HexCursor cursor;
+ HexCursor shadowCursor;
+
+ HexSelection selection;
+ bool updatingSelection;
+
+ QRect addrArea;
+ QRect itemArea;
+ QRect asciiArea;
+
+ int itemByteLen;
+ int itemGroupSize; ///< Items per group (default: 1), 2 in case of hexpair mode
+ int rowSizeBytes; ///< Line size in bytes
+ int itemColumns; ///< Number of columns, single column consists of itemGroupSize items
+ int itemCharLen;
+ int itemPrefixLen;
+ ColumnMode columnMode;
+
+ ItemFormat itemFormat;
+
+ bool itemBigEndian;
+
+ int visibleLines;
+ uint64_t startAddress;
+ int charWidth;
+ int byteWidth;
+ int lineHeight;
+ int addrCharLen;
+ int addrAreaWidth;
+ QFont monospaceFont;
+
+ bool showHeader;
+ bool showAscii;
+ bool showExHex;
+ bool showExAddr;
+
+ QColor borderColor;
+ QColor backgroundColor;
+ QColor defColor;
+ QColor addrColor;
+ QColor b0x00Color;
+ QColor b0x7fColor;
+ QColor b0xffColor;
+ QColor printableColor;
+
+ HexdumpRangeDialog rangeDialog;
+
+ /* Spacings in characters */
+ const int columnSpacing = 1;
+ const int areaSpacing = 2;
+
+ const QString hexPrefix = QStringLiteral("0x");
+
+ QMenu* rowSizeMenu;
+ QAction* actionRowSizePowerOf2;
+ QList actionsItemSize;
+ QList actionsItemFormat;
+ QAction *actionItemBigEndian;
+ QAction *actionHexPairs;
+ QAction *actionCopy;
+ QAction *actionCopyAddress;
+ QAction *actionSelectRange;
+
+ AbstractData *data;
+};
+
+#endif // HEXWIDGET_H
diff --git a/src/widgets/HexdumpWidget.cpp b/src/widgets/HexdumpWidget.cpp
index 899c1ddb..eb3042ff 100644
--- a/src/widgets/HexdumpWidget.cpp
+++ b/src/widgets/HexdumpWidget.cpp
@@ -95,19 +95,17 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
}
});
- connect(Core(), &CutterCore::refreshAll, this, [this]() {
- refresh(seekable->getOffset());
- });
+ connect(Core(), &CutterCore::refreshAll, this, [this]() { refresh(); });
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &HexdumpWidget::onSeekChanged);
- connect(ui->hexTextView, &HexTextView::positionChanged, this, [this](RVA addr) {
+ connect(ui->hexTextView, &HexWidget::positionChanged, this, [this](RVA addr) {
if (!sent_seek) {
sent_seek = true;
seekable->seek(addr);
sent_seek = false;
}
});
- connect(ui->hexTextView, &HexTextView::selectionChanged, this, &HexdumpWidget::selectionChanged);
+ connect(ui->hexTextView, &HexWidget::selectionChanged, this, &HexdumpWidget::selectionChanged);
initParsing();
selectHexPreview();
@@ -124,13 +122,23 @@ void HexdumpWidget::onSeekChanged(RVA addr)
HexdumpWidget::~HexdumpWidget() {}
+void HexdumpWidget::refresh()
+{
+ refresh(RVA_INVALID);
+}
+
void HexdumpWidget::refresh(RVA addr)
{
if (!refreshDeferrer->attemptRefresh(addr == RVA_INVALID ? nullptr : new RVA(addr))) {
return;
}
sent_seek = true;
- ui->hexTextView->refresh(addr);
+ if (addr != RVA_INVALID) {
+ ui->hexTextView->seek(addr);
+ } else {
+ ui->hexTextView->refresh();
+ refreshSelectionInfo();
+ }
sent_seek = false;
}
@@ -143,7 +151,7 @@ void HexdumpWidget::initParsing()
ui->parseEndianComboBox->setCurrentIndex(Core()->getConfigb("cfg.bigendian") ? 1 : 0);
}
-void HexdumpWidget::selectionChanged(HexTextView::Selection selection)
+void HexdumpWidget::selectionChanged(HexWidget::Selection selection)
{
if (selection.empty) {
clearParseWindow();
@@ -179,7 +187,7 @@ void HexdumpWidget::setupFonts()
{
QFont font = Config()->getFont();
ui->hexDisasTextEdit->setFont(font);
- ui->hexTextView->setupFonts();
+ ui->hexTextView->setMonospaceFont(font);
}
void HexdumpWidget::refreshSelectionInfo()
@@ -318,6 +326,10 @@ void HexdumpWidget::resizeEvent(QResizeEvent *event)
refresh();
}
+QWidget *HexdumpWidget::widgetToFocusOnRaise()
+{
+ return ui->hexTextView;
+}
void HexdumpWidget::on_copyMD5_clicked()
{
diff --git a/src/widgets/HexdumpWidget.h b/src/widgets/HexdumpWidget.h
index 3386943e..939f3e1a 100644
--- a/src/widgets/HexdumpWidget.h
+++ b/src/widgets/HexdumpWidget.h
@@ -16,12 +16,12 @@
#include "common/HexAsciiHighlighter.h"
#include "common/HexHighlighter.h"
#include "common/SvgIconEngine.h"
-#include "HexTextView.h"
+#include "HexWidget.h"
#include "Dashboard.h"
namespace Ui {
- class HexdumpWidget;
+class HexdumpWidget;
}
class RefreshDeferrer;
@@ -31,8 +31,8 @@ class HexdumpWidget : public MemoryDockWidget
Q_OBJECT
public:
explicit HexdumpWidget(MainWindow *main, QAction *action = nullptr);
- ~HexdumpWidget();
- Highlighter *highlighter;
+ ~HexdumpWidget() override;
+ Highlighter *highlighter;
public slots:
void initParsing();
@@ -40,7 +40,7 @@ public slots:
void toggleSync();
protected:
virtual void resizeEvent(QResizeEvent *event) override;
-
+ QWidget *widgetToFocusOnRaise() override;
private:
std::unique_ptr ui;
@@ -48,7 +48,8 @@ private:
RefreshDeferrer *refreshDeferrer;
- void refresh(RVA addr = RVA_INVALID);
+ void refresh();
+ void refresh(RVA addr);
void selectHexPreview();
void setupFonts();
@@ -65,7 +66,7 @@ private slots:
void on_actionHideHexdump_side_panel_triggered();
- void selectionChanged(HexTextView::Selection selection);
+ void selectionChanged(HexWidget::Selection selection);
void on_parseArchComboBox_currentTextChanged(const QString &arg1);
void on_parseBitsComboBox_currentTextChanged(const QString &arg1);
diff --git a/src/widgets/HexdumpWidget.ui b/src/widgets/HexdumpWidget.ui
index 8e60a342..cd54b900 100644
--- a/src/widgets/HexdumpWidget.ui
+++ b/src/widgets/HexdumpWidget.ui
@@ -32,7 +32,7 @@
Qt::Horizontal
-
+
QFrame::NoFrame
@@ -674,9 +674,9 @@
- HexTextView
+ HexWidget
QScrollArea
-
+
1