From 9847836d7325050ca064d0b7e35c3bb16bf9e9cb Mon Sep 17 00:00:00 2001 From: "Thomas (nezza-_-) Roth" Date: Sat, 27 Jan 2018 11:08:05 +0100 Subject: [PATCH] HexdumpWidget enhancements (#235) --- src/utils/Helpers.cpp | 9 + src/utils/Helpers.h | 2 +- src/widgets/HexdumpWidget.cpp | 984 ++++++++++++++++++++-------------- src/widgets/HexdumpWidget.h | 93 ++-- src/widgets/HexdumpWidget.ui | 214 +++++--- 5 files changed, 788 insertions(+), 514 deletions(-) diff --git a/src/utils/Helpers.cpp b/src/utils/Helpers.cpp index 8efc1b2a..5a887b1a 100644 --- a/src/utils/Helpers.cpp +++ b/src/utils/Helpers.cpp @@ -120,6 +120,15 @@ namespace qhelpers widget->setMaximumHeight(max); } + int getMaxFullyDisplayedLines(QTextEdit *textEdit) + { + QFontMetrics fontMetrics(textEdit->document()->defaultFont()); + return (textEdit->height() + - (textEdit->contentsMargins().top() + + textEdit->contentsMargins().bottom() + + (int)(textEdit->document()->documentMargin() * 2))) + / fontMetrics.lineSpacing(); + } int getMaxFullyDisplayedLines(QPlainTextEdit *plainTextEdit) { diff --git a/src/utils/Helpers.h b/src/utils/Helpers.h index 3ad125b0..969c1846 100644 --- a/src/utils/Helpers.h +++ b/src/utils/Helpers.h @@ -41,7 +41,7 @@ namespace qhelpers SizePolicyMinMax forceWidth(QWidget *widget, int width); SizePolicyMinMax forceHeight(QWidget *widget, int height); - + int getMaxFullyDisplayedLines(QTextEdit *textEdit); int getMaxFullyDisplayedLines(QPlainTextEdit *plainTextEdit); QByteArray applyColorToSvg(const QByteArray &data, QColor color); diff --git a/src/widgets/HexdumpWidget.cpp b/src/widgets/HexdumpWidget.cpp index bbbb360b..e2deccc0 100644 --- a/src/widgets/HexdumpWidget.cpp +++ b/src/widgets/HexdumpWidget.cpp @@ -1,28 +1,17 @@ - #include "HexdumpWidget.h" #include "ui_HexdumpWidget.h" -#include "DisassemblerGraphView.h" -#include "MainWindow.h" #include "utils/Helpers.h" +#include "utils/Configuration.h" #include "utils/TempConfig.h" -#include "utils/SvgIconEngine.h" -#include -#include -#include -#include -#include +#include +#include +#include +#include #include -#include -#include -#include - -#include - -const int HexdumpWidget::linesMarginMin = 32; -const int HexdumpWidget::linesMarginDefault = 48; -const int HexdumpWidget::linesMarginMax = 64; +#include +#include HexdumpWidget::HexdumpWidget(QWidget *parent, Qt::WindowFlags flags) : QDockWidget(parent, flags), @@ -49,9 +38,11 @@ HexdumpWidget::HexdumpWidget(QWidget *parent, Qt::WindowFlags flags) : ui->asciiHeaderLabel->setContentsMargins(margin, 0, margin, 0); setupFonts(); + colorsUpdatedSlot(); updateHeaders(); + // Set hexdump context menu ui->hexHexText->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->hexHexText, SIGNAL(customContextMenuRequested(const QPoint &)), @@ -82,6 +73,12 @@ HexdumpWidget::HexdumpWidget(QWidget *parent, Qt::WindowFlags flags) : refresh(Core()->getOffset()); }); + connect(ui->hexHexText, &QTextEdit::selectionChanged, this, &HexdumpWidget::selectionChanged); + connect(ui->hexASCIIText, &QTextEdit::selectionChanged, this, &HexdumpWidget::selectionChanged); + connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexdumpWidget::selectionChanged); + connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, this, &HexdumpWidget::selectionChanged); + + format = Format::Hex; initParsing(); selectHexPreview(); } @@ -92,64 +89,94 @@ HexdumpWidget::HexdumpWidget(const QString &title, QWidget *parent, Qt::WindowFl setWindowTitle(title); } - void HexdumpWidget::setupScrollSync() { /* * For some reason, QScrollBar::valueChanged is not emitted when * the scrolling happened from moving the cursor beyond the visible content, - * so QPlainTextEdit::cursorPositionChanged has to be connected as well. + * so QTextEdit::cursorPositionChanged has to be connected as well. */ auto offsetHexFunc = [this]() { - ui->hexHexText->verticalScrollBar()->setValue(ui->hexOffsetText->verticalScrollBar()->value()); + if(!scroll_disabled) + { + scroll_disabled = true; + ui->hexHexText->verticalScrollBar()->setValue(ui->hexOffsetText->verticalScrollBar()->value()); + scroll_disabled = false; + } }; auto offsetASCIIFunc = [this]() { - ui->hexASCIIText->verticalScrollBar()->setValue(ui->hexOffsetText->verticalScrollBar()->value()); + 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, &QPlainTextEdit::cursorPositionChanged, 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, &QPlainTextEdit::cursorPositionChanged, ui->hexASCIIText->verticalScrollBar(), offsetASCIIFunc); - + connect(ui->hexOffsetText, &QTextEdit::cursorPositionChanged, ui->hexASCIIText->verticalScrollBar(), offsetASCIIFunc); auto hexOffsetFunc = [this]() { - ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value()); + if(!scroll_disabled) + { + scroll_disabled = true; + ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value()); + scroll_disabled = false; + } }; auto hexASCIIFunc = [this]() { - ui->hexASCIIText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value()); + 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, &QPlainTextEdit::cursorPositionChanged, 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, &QPlainTextEdit::cursorPositionChanged, ui->hexASCIIText->verticalScrollBar(), hexASCIIFunc); - + connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, ui->hexASCIIText->verticalScrollBar(), hexASCIIFunc); auto asciiOffsetFunc = [this]() { - ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexASCIIText->verticalScrollBar()->value()); + if(!scroll_disabled) + { + scroll_disabled = true; + ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexASCIIText->verticalScrollBar()->value()); + scroll_disabled = false; + } }; auto asciiHexFunc = [this]() { - ui->hexHexText->verticalScrollBar()->setValue(ui->hexASCIIText->verticalScrollBar()->value()); + 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, &QPlainTextEdit::cursorPositionChanged, 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, &QPlainTextEdit::cursorPositionChanged, ui->hexHexText->verticalScrollBar(), asciiHexFunc); + connect(ui->hexASCIIText, &QTextEdit::cursorPositionChanged, ui->hexHexText->verticalScrollBar(), asciiHexFunc); } - void HexdumpWidget::on_seekChanged(RVA addr) { + if(sent_seek) + { + sent_seek = false; + return; + } refresh(addr); } - void HexdumpWidget::raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType type) { if (type == CutterCore::MemoryWidgetType::Hexdump) @@ -158,20 +185,21 @@ void HexdumpWidget::raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType ty } } -void HexdumpWidget::connectScroll(bool disconnect) +void HexdumpWidget::connectScroll(bool disconnect_) { - if (disconnect) + scroll_disabled = disconnect_; + if (disconnect_) { - this->disconnect(ui->hexASCIIText->verticalScrollBar(), &QScrollBar::valueChanged, this, - &HexdumpWidget::adjustHexdumpLines); - this->disconnect(ui->hexASCIIText, &QPlainTextEdit::cursorPositionChanged, this, - &HexdumpWidget::adjustHexdumpLines); + disconnect(ui->hexHexText->verticalScrollBar(), &QScrollBar::valueChanged, this, + &HexdumpWidget::scrollChanged); + disconnect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexdumpWidget::scrollChanged); } else { - connect(ui->hexASCIIText->verticalScrollBar(), &QScrollBar::valueChanged, this, - &HexdumpWidget::adjustHexdumpLines); - connect(ui->hexASCIIText, &QPlainTextEdit::cursorPositionChanged, this, &HexdumpWidget::adjustHexdumpLines); + connect(ui->hexHexText->verticalScrollBar(), &QScrollBar::valueChanged, this, + &HexdumpWidget::scrollChanged); + connect(ui->hexHexText, &QTextEdit::cursorPositionChanged, this, &HexdumpWidget::scrollChanged); + } } @@ -179,7 +207,9 @@ HexdumpWidget::~HexdumpWidget() {} /* * Text highlight functions + * Currently unused */ +/* void HexdumpWidget::highlightHexCurrentLine() { QList extraSelections; @@ -244,199 +274,50 @@ void HexdumpWidget::highlightHexWords(const QString &str) } cursor.endEditBlock(); } +*/ void HexdumpWidget::refresh(RVA addr) { + connectScroll(true); + updateHeaders(); - if (addr == RVA_INVALID) + if(addr == RVA_INVALID) { addr = Core()->getOffset(); } - int visibleLines = qhelpers::getMaxFullyDisplayedLines(ui->hexHexText); - RCoreLocked lcore = Core()->core(); - - connectScroll(true); - - int cols = lcore->print->cols; - RVA marginBytes = static_cast(linesMarginDefault) * cols; - // lower bound of 0 - if (addr > marginBytes) - { - topOffset = addr - marginBytes; - topOffset = (topOffset / cols) * cols; // align - } - else - { - topOffset = 0; - } + // Align addr to cols + addr -= addr % cols; + + // TODO: Figure out how to calculate a sane value for this + bufferLines = qhelpers::getMaxFullyDisplayedLines(ui->hexHexText); - int fetchLines = visibleLines + linesMarginDefault * 2; - RVA bytes = static_cast(fetchLines) * cols; + RVA cur_addr = addr - (bufferLines * cols); + first_loaded_address = cur_addr; + last_loaded_address = cur_addr + (3 * bufferLines) * cols; + QElapsedTimer getHexdumpTimer; + getHexdumpTimer.start(); + auto hexdump = fetchHexdump(cur_addr, 3 * bufferLines); + ui->hexOffsetText->setText(hexdump[0]); + ui->hexHexText->setText(hexdump[1]); + ui->hexASCIIText->setText(hexdump[2]); - // upper bound of UT64_MAX - RVA bytesLeft = UT64_MAX - topOffset; - if (bytes > bytesLeft) - { - bottomOffset = UT64_MAX; - topOffset = bottomOffset - bytes; - } - else - { - bottomOffset = topOffset + bytes; - } + QTextBlock blockText = ui->hexHexText->document()->findBlockByLineNumber(bufferLines); + QTextCursor cursor(ui->hexHexText->document()->findBlockByLineNumber(bufferLines)); // ln-1 because line number starts from 0 + ui->hexHexText->moveCursor(QTextCursor::End); + ui->hexHexText->setTextCursor(cursor); + updateWidths(); - auto hexdump = fetchHexdump(topOffset, bytes); - - ui->hexOffsetText->setPlainText(hexdump[0]); - ui->hexHexText->setPlainText(hexdump[1]); - ui->hexASCIIText->setPlainText(hexdump[2]); - - - int seekLine = static_cast((addr - topOffset) / cols); - - // Move cursors to desired address - QTextCursor cur = ui->hexOffsetText->textCursor(); - cur.movePosition(QTextCursor::Start); - cur.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, seekLine); - ui->hexOffsetText->setTextCursor(cur); - - cur = ui->hexHexText->textCursor(); - cur.movePosition(QTextCursor::Start); - cur.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, seekLine); - ui->hexHexText->setTextCursor(cur); - - cur = ui->hexASCIIText->textCursor(); - cur.movePosition(QTextCursor::Start); - cur.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, seekLine); - ui->hexASCIIText->setTextCursor(cur); - - ui->hexOffsetText->verticalScrollBar()->setValue(seekLine); - ui->hexHexText->verticalScrollBar()->setValue(seekLine); - ui->hexASCIIText->verticalScrollBar()->setValue(seekLine); - - connectScroll(false); -} - -void HexdumpWidget::appendHexdumpLines(int lines, bool top) -{ - connectScroll(true); - - int cols = Core()->getConfigi("hex.cols"); - RVA bytes = static_cast(lines) * cols; - - if (top) - { - if (bytes > topOffset) - { - bytes = topOffset; - if (bytes == 0) - { - connectScroll(false); - return; - } - } - - topOffset -= bytes; - auto hexdump = fetchHexdump(topOffset, bytes); - - int scroll = ui->hexASCIIText->verticalScrollBar()->value(); - - QTextCursor cur = ui->hexOffsetText->textCursor(); - cur.movePosition(QTextCursor::Start); - cur.insertText(hexdump[0]); - - cur = ui->hexHexText->textCursor(); - cur.movePosition(QTextCursor::Start); - cur.insertText(hexdump[1]); - - cur = ui->hexASCIIText->textCursor(); - cur.movePosition(QTextCursor::Start); - cur.insertText(hexdump[2]); - - int actualLines = static_cast(bytes / cols); - ui->hexOffsetText->verticalScrollBar()->setValue(actualLines + scroll); - ui->hexHexText->verticalScrollBar()->setValue(actualLines + scroll); - ui->hexASCIIText->verticalScrollBar()->setValue(actualLines + scroll); - } - else - { - if (bytes > UT64_MAX - bottomOffset) - { - bytes = UT64_MAX - bottomOffset; - - if (bytes == 0) - { - connectScroll(false); - return; - } - } - - auto hexdump = fetchHexdump(bottomOffset, bytes); - bottomOffset += bytes; - - QTextCursor cur = ui->hexOffsetText->textCursor(); - cur.movePosition(QTextCursor::End); - cur.insertText(hexdump[0]); - - cur = ui->hexHexText->textCursor(); - cur.movePosition(QTextCursor::End); - cur.insertText(hexdump[1]); - - cur = ui->hexASCIIText->textCursor(); - cur.movePosition(QTextCursor::End); - cur.insertText(hexdump[2]); - } - - connectScroll(false); -} - -void HexdumpWidget::removeHexdumpLines(int lines, bool top) -{ - connectScroll(true); - - int cols = Core()->getConfigi("hex.cols"); - - std::array edits = { ui->hexOffsetText, ui->hexHexText, ui->hexASCIIText }; - - int scroll = ui->hexASCIIText->verticalScrollBar()->value(); - - if (top) - { - for (QPlainTextEdit *edit : edits) - { - QTextCursor cur = edit->textCursor(); - cur.movePosition(QTextCursor::Start); - cur.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, lines + 1); - cur.removeSelectedText(); - } - - topOffset += lines * cols; - - ui->hexOffsetText->verticalScrollBar()->setValue(scroll - lines); - ui->hexHexText->verticalScrollBar()->setValue(scroll - lines); - ui->hexASCIIText->verticalScrollBar()->setValue(scroll - lines); - } - else - { - for (QPlainTextEdit *edit : edits) - { - QTextCursor cur = edit->textCursor(); - cur.movePosition(QTextCursor::End); - cur.movePosition(QTextCursor::Up, QTextCursor::KeepAnchor, lines); - cur.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); - cur.removeSelectedText(); - } - - bottomOffset -= lines * cols; - } + // Update other text areas scroll + ui->hexOffsetText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value()); + ui->hexASCIIText->verticalScrollBar()->setValue(ui->hexHexText->verticalScrollBar()->value()); connectScroll(false); } @@ -444,6 +325,7 @@ void HexdumpWidget::removeHexdumpLines(int lines, bool top) void HexdumpWidget::updateHeaders() { int cols = Core()->getConfigi("hex.cols"); + int ascii_cols = cols; bool pairs = Core()->getConfigb("hex.pairs"); QString hexHeaderString; @@ -457,6 +339,21 @@ void HexdumpWidget::updateHeaders() 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 0 && ((pairs && !(i&1)) || !pairs)) @@ -464,8 +361,11 @@ void HexdumpWidget::updateHeaders() hexHeader << " "; } - hexHeader << " " << (i & 0xF); + hexHeader << space << (i & 0xF); + } + for (int i=0; i < ascii_cols; i++) + { asciiHeader << (i & 0xF); } @@ -476,10 +376,6 @@ void HexdumpWidget::updateHeaders() ui->asciiHeaderLabel->setText(asciiHeaderString); } -/* - * Content management functions - */ - void HexdumpWidget::initParsing() { // Fill the plugins combo for the hexdump sidebar @@ -488,175 +384,186 @@ void HexdumpWidget::initParsing() ui->parseEndianComboBox->setCurrentIndex(Core()->getConfigb("cfg.bigendian") ? 1 : 0); } -std::array HexdumpWidget::fetchHexdump(RVA offset, RVA bytes) +std::array HexdumpWidget::fetchHexdump(RVA addr, int lines) { - TempConfig tempConfig; - tempConfig.set("scr.color", false); + RCoreLocked lcore = Core()->core(); + int cols = lcore->print->cols; - QString hexdump = Core()->cmd(QString("px %1 @ %2").arg(QString::number(bytes), QString::number(offset))); + // Main bytes to fetch: + int bytes = cols * lines; - QString offsets; - QString hex; - QString ascii; - int ln = 0; + QString command = QString("pxj %1 @%2").arg( + QString::number(bytes), + RAddressString(addr)); + QJsonArray byte_array = Core()->cmdj(command).array(); - for (const QString &line : hexdump.split("\n")) + QString hexText = ""; + QString offsetText = ""; + QString asciiText = ""; + RVA cur_addr = addr; + for(int i=0; i < lines; i++) { - if (ln++ == 0 || line.trimmed().isEmpty()) - { - continue; - } - - int wc = 0; - for (const QString a : line.split(" ")) - { - switch (wc++) + 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)) { - case 0: - offsets += a + "\n"; - break; - case 1: - hex += a.trimmed() + "\n"; - break; - case 2: - ascii += a + "\n"; - break; + 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 { offsets, hex, ascii }; + return { offsetText, hexText, asciiText}; } -void HexdumpWidget::adjustHexdumpLines() +void HexdumpWidget::selectionChanged() { - QScrollBar *sb = ui->hexASCIIText->verticalScrollBar(); - int topMargin = sb->value() - sb->minimum(); - int bottomMargin = sb->maximum() - sb->value(); - - if (topMargin < linesMarginMin) + if(scroll_disabled) { - int loadLines = linesMarginDefault - topMargin; - appendHexdumpLines(loadLines, true); - } - - if(bottomMargin < linesMarginMin) - { - int loadLines = linesMarginDefault - bottomMargin; - appendHexdumpLines(loadLines, false); - } - - if(topMargin > linesMarginMax) - { - int removeLines = topMargin - linesMarginDefault; - removeHexdumpLines(removeLines, true); - } - - if(bottomMargin > linesMarginMax) - { - int removeLines = bottomMargin - linesMarginDefault; - removeHexdumpLines(removeLines, false); - } -} - -void HexdumpWidget::on_hexHexText_selectionChanged() -{ - // Get selected text - QTextCursor cursor(ui->hexHexText->textCursor()); - QString sel_text = cursor.selectedText(); - - sel_text = sel_text.simplified().remove(' '); - - if (sel_text == "") - { - ui->hexDisasTextEdit->setPlainText(""); - ui->bytesEntropy->setText(""); - ui->bytesMD5->setText(""); - ui->bytesSHA1->setText(""); return; } + connectScroll(true); - // Get selected combos - QString arch = ui->parseArchComboBox->currentText(); - QString bits = ui->parseBitsComboBox->currentText(); - bool bigEndian = ui->parseEndianComboBox->currentIndex() == 1; - - { // scope for TempConfig - TempConfig tempConfig; - tempConfig - .set("asm.arch", arch) - .set("asm.bits", bits) - .set("cfg.bigendian", bigEndian); - - switch(ui->parseTypeComboBox->currentIndex()) + if(sender() == ui->hexHexText) + { + QTextCursor textCursor = ui->hexHexText->textCursor(); + if(!textCursor.hasSelection()) { - case 0: // Disassembly - { - QStringRef disasBytes = sel_text.leftRef((sel_text.length() / 2) * 2); - QString str = ""; - if (disasBytes.length() > 0) - { - QString cmd = "pad "; - str = Core()->cmd(cmd.append(disasBytes)); - } - ui->hexDisasTextEdit->setPlainText(str); - } - break; - case 1: // String - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcs@x:" + sel_text)); - break; - case 2: // Assembler - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pca@x:" + sel_text)); - break; - case 3: // C byte array - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pc@x:" + sel_text)); - break; - case 4: // C half-word - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pch@x:" + sel_text)); - break; - case 5: // C word - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcw@x:" + sel_text)); - break; - case 6: // C dword - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcd@x:" + sel_text)); - break; - case 7: // Python - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcp@x:" + sel_text)); - break; - case 8: // JSON - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcj@x:" + sel_text)); - break; - case 9: // JavaScript - ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcJ@x:" + sel_text)); - break; - default: - ui->hexDisasTextEdit->setPlainText(""); + clearParseWindow(); + RVA adr = hexPositionToAddress(textCursor.position()); + int pos = asciiAddressToPosition(adr); + setTextEditPosition(ui->hexASCIIText, pos); + sent_seek = true; + Core()->seek(adr); + sent_seek = false; + 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); + + updateParseWindow(startAddress, endAddress - startAddress); + + 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); + sent_seek = true; + Core()->seek(startAddress); + sent_seek = false; + } + else + { + QTextCursor textCursor = ui->hexASCIIText->textCursor(); + if(!textCursor.hasSelection()) + { + clearParseWindow(); + RVA adr = asciiPositionToAddress(textCursor.position()); + int pos = hexAddressToPosition(adr); + setTextEditPosition(ui->hexHexText, pos); + connectScroll(false); + sent_seek = true; + Core()->seek(adr); + sent_seek = false; + return; + } + RVA startAddress = asciiPositionToAddress(textCursor.selectionStart()); + RVA endAddress = asciiPositionToAddress(textCursor.selectionEnd()); + + updateParseWindow(startAddress, endAddress - startAddress); + + int startPosition = hexAddressToPosition(startAddress); + int endPosition = hexAddressToPosition(endAddress); + QChar endChar = ui->hexHexText->document()->characterAt(endPosition); + + // 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); + sent_seek = true; + Core()->seek(startAddress); + sent_seek = false; } - // Fill the information tab hashes and entropy - ui->bytesMD5->setText(Core()->cmd("ph md5@x:" + sel_text).trimmed()); - ui->bytesSHA1->setText(Core()->cmd("ph sha1@x:" + sel_text).trimmed()); - ui->bytesEntropy->setText(Core()->cmd("ph entropy@x:" + sel_text).trimmed()); - ui->bytesMD5->setCursorPosition(0); - ui->bytesSHA1->setCursorPosition(0); - + connectScroll(false); + return; } void HexdumpWidget::on_parseArchComboBox_currentTextChanged(const QString &/*arg1*/) { - on_hexHexText_selectionChanged(); + selectionChanged(); } void HexdumpWidget::on_parseBitsComboBox_currentTextChanged(const QString &/*arg1*/) { - on_hexHexText_selectionChanged(); + selectionChanged(); } -/* - * Context menu functions - */ - void HexdumpWidget::showHexdumpContextMenu(const QPoint &pt) { // Set Hexdump popup menu @@ -666,11 +573,27 @@ void HexdumpWidget::showHexdumpContextMenu(const QPoint &pt) menu->addAction(ui->actionHexCopy_ASCII); menu->addAction(ui->actionHexCopy_Text); menu->addSeparator();*/ - QMenu *colSubmenu = menu->addMenu("Columns"); + 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); + // 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); @@ -711,8 +634,6 @@ void HexdumpWidget::showHexASCIIContextMenu(const QPoint &pt) delete menu; } - - void HexdumpWidget::setupFonts() { QFont font = Config()->getFont(); @@ -731,20 +652,265 @@ void HexdumpWidget::setupFonts() void HexdumpWidget::fontsUpdated() { setupFonts(); - adjustHexdumpLines(); } void HexdumpWidget::colorsUpdatedSlot() { - QString styleSheet = QString("QPlainTextEdit { background-color: %1; color: %2; }") + QString styleSheet = QString("QTextEdit { background-color: %1; color: %2; }") .arg(ConfigColor("gui.background").name()) .arg(ConfigColor("btext").name()); ui->hexOffsetText->setStyleSheet(styleSheet); ui->hexHexText->setStyleSheet(styleSheet); ui->hexASCIIText->setStyleSheet(styleSheet); + ui->hexSpacerText->setStyleSheet(styleSheet); } +void HexdumpWidget::clearParseWindow() +{ + ui->hexDisasTextEdit->setPlainText(""); + ui->bytesEntropy->setText(""); + ui->bytesMD5->setText(""); + ui->bytesSHA1->setText(""); +} + +void HexdumpWidget::updateParseWindow(RVA start_address, int size) +{ + + QString address = RAddressString(start_address); + + QString argument = QString("%1 " + address).arg(size); + // Get selected combos + QString arch = ui->parseArchComboBox->currentText(); + QString bits = ui->parseBitsComboBox->currentText(); + bool bigEndian = ui->parseEndianComboBox->currentIndex() == 1; + + { // scope for TempConfig + TempConfig tempConfig; + tempConfig + .set("asm.arch", arch) + .set("asm.bits", bits) + .set("cfg.bigendian", bigEndian); + + switch(ui->parseTypeComboBox->currentIndex()) + { + case 0: // Disassembly + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pda " + argument)); + break; + case 1: // String + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcs " + argument)); + break; + case 2: // Assembler + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pca " + argument)); + break; + case 3: // C byte array + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pc " + argument)); + break; + case 4: // C half-word + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pch " + argument)); + break; + case 5: // C word + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcw " + argument)); + break; + case 6: // C dword + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcd " + argument)); + break; + case 7: // Python + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcp " + argument)); + break; + case 8: // JSON + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcj " + argument)); + break; + case 9: // JavaScript + ui->hexDisasTextEdit->setPlainText(Core()->cmd("pcJ " + argument)); + break; + default: + ui->hexDisasTextEdit->setPlainText(""); + } + } + + // Fill the information tab hashes and entropy + ui->bytesMD5->setText(Core()->cmd("ph md5 " + argument).trimmed()); + ui->bytesSHA1->setText(Core()->cmd("ph sha1 " + argument).trimmed()); + ui->bytesEntropy->setText(Core()->cmd("ph entropy " + argument).trimmed()); + ui->bytesMD5->setCursorPosition(0); + ui->bytesSHA1->setCursorPosition(0); +} + +RVA HexdumpWidget::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 HexdumpWidget::asciiPositionToAddress(int position) +{ + RCoreLocked lcore = Core()->core(); + int cols = lcore->print->cols; + + // 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 HexdumpWidget::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 HexdumpWidget::asciiAddressToPosition(RVA address) +{ + RCoreLocked lcore = Core()->core(); + int cols = lcore->print->cols; + RVA local_address = address - first_loaded_address; + int position = local_address + (local_address / cols); + return position; +} + +void HexdumpWidget::setTextEditPosition(QTextEdit *textEdit, int position) +{ + QTextCursor textCursor = textEdit->textCursor(); + textCursor.setPosition(position); + textEdit->setTextCursor(textCursor); +} + +int HexdumpWidget::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 HexdumpWidget::removeTopLinesWithoutScroll(QTextEdit *textEdit, int lines) +{ + int scroll_val_before = textEdit->verticalScrollBar()->value(); + int height_before = textEdit->document()->size().height(); + + QTextBlock block = textEdit->document()->firstBlock(); + QTextCursor textCursor = textEdit->textCursor(); + for(int i=0; i < lines; i++) + { + QTextCursor cursor(block); + cursor.select(QTextCursor::BlockUnderCursor); + cursor.removeSelectedText(); + block = block.next(); + } + + int height_after = textEdit->document()->size().height(); + textEdit->verticalScrollBar()->setValue(scroll_val_before + (height_after - height_before)); +} + +void HexdumpWidget::removeBottomLinesWithoutScroll(QTextEdit *textEdit, int lines) +{ + QTextBlock block = textEdit->document()->lastBlock(); + QTextCursor textCursor = textEdit->textCursor(); + for(int i=0; i < lines; i++) + { + QTextCursor cursor(block); + cursor.select(QTextCursor::BlockUnderCursor); + cursor.removeSelectedText(); + block = block.previous(); + } +} + +void HexdumpWidget::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 HexdumpWidget::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 HexdumpWidget::scrollChanged() +{ + connectScroll(true); + RCoreLocked lcore = Core()->core(); + int cols = lcore->print->cols; + + int firstLine = getDisplayedLined(ui->hexHexText); + if(firstLine < (bufferLines/2)) + { + auto hexdump = fetchHexdump(first_loaded_address, bufferLines); + first_loaded_address -= bufferLines * cols; + prependWithoutScroll(ui->hexOffsetText, hexdump[0]); + prependWithoutScroll(ui->hexHexText, hexdump[1]); + prependWithoutScroll(ui->hexASCIIText, hexdump[2]); + + removeBottomLinesWithoutScroll(ui->hexOffsetText, bufferLines); + removeBottomLinesWithoutScroll(ui->hexHexText, bufferLines); + removeBottomLinesWithoutScroll(ui->hexASCIIText, bufferLines); + + 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)) + { + auto hexdump = fetchHexdump(last_loaded_address, bufferLines); + last_loaded_address += bufferLines * cols; + removeTopLinesWithoutScroll(ui->hexOffsetText, bufferLines); + removeTopLinesWithoutScroll(ui->hexHexText, bufferLines); + removeTopLinesWithoutScroll(ui->hexASCIIText, bufferLines); + appendWithoutScroll(ui->hexOffsetText, hexdump[0]); + appendWithoutScroll(ui->hexHexText, hexdump[1]); + appendWithoutScroll(ui->hexASCIIText, hexdump[2]); + } + connectScroll(false); +} /* * Actions callback functions @@ -762,14 +928,6 @@ void HexdumpWidget::on_actionHideHexdump_side_panel_triggered() } } -/*void HexdumpWidget::on_actionSend_to_Notepad_triggered() -{ - QTextCursor cursor = ui->disasTextEdit_2->textCursor(); - QString text = cursor.selectedText(); - // TODO - // this->main->sendToNotepad(text); -}*/ - void HexdumpWidget::on_action8columns_triggered() { Core()->setConfig("hex.cols", 8); @@ -812,6 +970,18 @@ void HexdumpWidget::on_action1column_triggered() refresh(); } +void HexdumpWidget::on_actionFormatHex_triggered() +{ + format = Format::Hex; + refresh(); +} + +void HexdumpWidget::on_actionFormatOctal_triggered() +{ + format = Format::Octal; + refresh(); +} + void HexdumpWidget::on_parseTypeComboBox_currentTextChanged(const QString &) { if (ui->parseTypeComboBox->currentIndex() == 0) @@ -822,48 +992,12 @@ void HexdumpWidget::on_parseTypeComboBox_currentTextChanged(const QString &) { ui->hexSideFrame_2->hide(); } - on_hexHexText_selectionChanged(); + selectionChanged(); } void HexdumpWidget::on_parseEndianComboBox_currentTextChanged(const QString &) { - on_hexHexText_selectionChanged(); -} - -QString HexdumpWidget::normalize_addr(QString addr) -{ - QString base = Core()->cmd("s").split("0x")[1].trimmed(); - int len = base.length(); - if (len < 8) - { - int padding = 8 - len; - QString zero = "0"; - QString zeroes = zero.repeated(padding); - QString s = "0x" + zeroes + base; - return s; - } - else - { - return addr.trimmed(); - } -} - -QString HexdumpWidget::normalizeAddr(QString addr) -{ - QString base = addr.split("0x")[1].trimmed(); - int len = base.length(); - if (len < 8) - { - int padding = 8 - len; - QString zero = "0"; - QString zeroes = zero.repeated(padding); - QString s = "0x" + zeroes + base; - return s; - } - else - { - return addr; - } + selectionChanged(); } void HexdumpWidget::on_hexSideTab_2_currentChanged(int /*index*/) @@ -885,6 +1019,7 @@ void HexdumpWidget::on_hexSideTab_2_currentChanged(int /*index*/) */ } + void HexdumpWidget::on_memSideToolButton_clicked() { if (ui->memSideToolButton->isChecked()) @@ -902,8 +1037,7 @@ void HexdumpWidget::on_memSideToolButton_clicked() void HexdumpWidget::resizeEvent(QResizeEvent *event) { QDockWidget::resizeEvent(event); - - adjustHexdumpLines(); + refresh(); } void HexdumpWidget::wheelEvent(QWheelEvent* event) @@ -951,6 +1085,7 @@ void HexdumpWidget::on_copySHA1_clicked() // this->main->addOutput("SHA1 copied to clipboard: " + sha1); } + void HexdumpWidget::selectHexPreview() { // Pre-select arch and bits in the hexdump sidebar @@ -989,6 +1124,8 @@ void HexdumpWidget::zoomIn(int range) ui->hexOffsetText->zoomIn(range); ui->hexASCIIText->zoomIn(range); ui->hexHexText->zoomIn(range); + + updateWidths(); } void HexdumpWidget::zoomOut(int range) @@ -996,4 +1133,19 @@ void HexdumpWidget::zoomOut(int range) ui->hexOffsetText->zoomOut(range); ui->hexASCIIText->zoomOut(range); ui->hexHexText->zoomOut(range); + + updateWidths(); +} + +void HexdumpWidget::updateWidths() +{ + // Update width + ui->hexHexText->document()->adjustSize(); + ui->hexHexText->setFixedWidth(ui->hexHexText->document()->size().width()); + + ui->hexOffsetText->document()->adjustSize(); + ui->hexOffsetText->setFixedWidth(ui->hexOffsetText->document()->size().width()); + + ui->hexASCIIText->document()->adjustSize(); + ui->hexASCIIText->setFixedWidth(ui->hexASCIIText->document()->size().width()); } diff --git a/src/widgets/HexdumpWidget.h b/src/widgets/HexdumpWidget.h index 31772f28..4a34cc98 100644 --- a/src/widgets/HexdumpWidget.h +++ b/src/widgets/HexdumpWidget.h @@ -1,32 +1,28 @@ - #ifndef HEXDUMPWIDGET_H #define HEXDUMPWIDGET_H -#include #include #include #include -#include -#include -#include -#include #include + +#include #include + #include "cutter.h" #include "utils/Highlighter.h" #include "utils/HexAsciiHighlighter.h" #include "utils/HexHighlighter.h" +#include "utils/SvgIconEngine.h" + #include "Dashboard.h" - -namespace Ui -{ - class HexdumpWidget; -} +#include "ui_HexdumpWidget.h" class HexdumpWidget : public QDockWidget { -Q_OBJECT + Q_OBJECT + public: explicit HexdumpWidget(const QString &title, QWidget *parent = nullptr, Qt::WindowFlags flags = 0); @@ -35,16 +31,22 @@ public: Highlighter *highlighter; -//signals: -// void fontChanged(QFont font); + enum Format { + Hex, + Octal, + // TODO: +// HalfWord, +// Word, +// QuadWord, +// Emoji, +// SignedInt1, +// SignedInt2, +// SignedInt4, + }; public slots: void initParsing(); - QString normalize_addr(QString addr); - - QString normalizeAddr(QString addr); - void showOffsets(bool show); void zoomIn(int range = 1); @@ -55,41 +57,67 @@ protected: virtual void wheelEvent(QWheelEvent* event) override; private: - static const int linesMarginMin; - static const int linesMarginDefault; - static const int linesMarginMax; + static const int linesMarginMin = 32; + static const int linesMarginDefault = 48; + static const int linesMarginMax = 64; + + enum Format format = Format::Hex; std::unique_ptr ui; - RVA topOffset; - RVA bottomOffset; + bool sent_seek = false; + bool scroll_disabled = false; + + RVA first_loaded_address = RVA_INVALID; + RVA last_loaded_address = RVA_INVALID; void refresh(RVA addr = RVA_INVALID); - void appendHexdumpLines(int lines, bool top); - void removeHexdumpLines(int lines, bool top); void selectHexPreview(); - void updateHeaders(); + void updateHeaders(); - std::array fetchHexdump(RVA offset, RVA bytes); + std::array fetchHexdump(RVA addr, int lines); - void connectScroll(bool disconnect); + void connectScroll(bool disconnect_); void setupScrollSync(); void setupFonts(); + // 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 updateParseWindow(RVA start_address, int size); + void clearParseWindow(); + + int bufferLines; + private slots: void on_seekChanged(RVA addr); void raisePrioritizedMemoryWidget(CutterCore::MemoryWidgetType type); - void highlightHexCurrentLine(); + // Currently unused/untested + // void highlightHexCurrentLine(); + // void highlightHexWords(const QString &str); - void highlightHexWords(const QString &str); void on_actionHideHexdump_side_panel_triggered(); void showHexdumpContextMenu(const QPoint &pt); void showHexASCIIContextMenu(const QPoint &pt); - void on_hexHexText_selectionChanged(); + void selectionChanged(); + void scrollChanged(); void on_parseArchComboBox_currentTextChanged(const QString &arg1); void on_parseBitsComboBox_currentTextChanged(const QString &arg1); @@ -104,7 +132,8 @@ private slots: void on_action32columns_triggered(); void on_action64columns_triggered(); - void adjustHexdumpLines(); + void on_actionFormatHex_triggered(); + void on_actionFormatOctal_triggered(); void fontsUpdated(); void colorsUpdatedSlot(); diff --git a/src/widgets/HexdumpWidget.ui b/src/widgets/HexdumpWidget.ui index f1942bf7..11d288f6 100644 --- a/src/widgets/HexdumpWidget.ui +++ b/src/widgets/HexdumpWidget.ui @@ -152,47 +152,10 @@ QToolTip { - - + + - - 0 - 0 - - - - false - - - - - - QFrame::NoFrame - - - 0 - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - QPlainTextEdit::NoWrap - - - true - - - - - - - + 0 0 @@ -228,17 +191,92 @@ QToolTip { QAbstractScrollArea::AdjustToContents - QPlainTextEdit::NoWrap + QTextEdit::NoWrap - - true + + 3 + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - + + + + Qt::Vertical + + + + + - + + 0 + 0 + + + + 0 1 2 3 ... + + + + + + + + 0 + 0 + + + + false + + + + + + QFrame::NoFrame + + + 0 + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + + + + + 0123... + + + + + + + Qt::Vertical + + + + + + + Offset + + + + + + + 0 0 @@ -280,31 +318,23 @@ QToolTip { QAbstractScrollArea::AdjustToContents - QPlainTextEdit::NoWrap + QTextEdit::NoWrap - - true + + 3 - - - - Offset + + + + QFrame::NoFrame - - - - - - 0 1 2 3 ... + + QFrame::Plain - - - - - - 0123... + + Qt::NoTextInteraction @@ -974,6 +1004,60 @@ QToolTip { Insert String + + + Hex + + + + + Octal + + + + + Half-word + + + + + Word + + + + + Quad-word + + + + + Emoji + + + + + 1 byte + + + 1 byte + + + + + 2 bytes + + + 2 bytes + + + + + 4 bytes + + + 4 bytes + +