"Fossies" - the Fresh Open Source Software Archive

Member "cutter-1.9.0/src/widgets/HexWidget.cpp" (6 Sep 2019, 38192 Bytes) of package /linux/privat/cutter-1.9.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "HexWidget.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.8.3_vs_1.9.0.

    1 #include "HexWidget.h"
    2 #include "Cutter.h"
    3 #include "Configuration.h"
    4 
    5 #include <QPainter>
    6 #include <QPaintEvent>
    7 #include <QResizeEvent>
    8 #include <QMouseEvent>
    9 #include <QKeyEvent>
   10 #include <QWheelEvent>
   11 #include <QtEndian>
   12 #include <QScrollBar>
   13 #include <QMenu>
   14 #include <QClipboard>
   15 #include <QApplication>
   16 
   17 static const uint64_t MAX_COPY_SIZE = 128 * 1024 * 1024;
   18 static const int MAX_LINE_WIDTH_PRESET = 32;
   19 static const int MAX_LINE_WIDTH_BYTES = 128 * 1024;
   20 
   21 HexWidget::HexWidget(QWidget *parent) :
   22     QScrollArea(parent),
   23     cursorEnabled(true),
   24     cursorOnAscii(false),
   25     updatingSelection(false),
   26     itemByteLen(1),
   27     itemGroupSize(1),
   28     rowSizeBytes(16),
   29     columnMode(ColumnMode::PowerOf2),
   30     itemFormat(ItemFormatHex),
   31     itemBigEndian(false),
   32     addrCharLen(AddrWidth64),
   33     showHeader(true),
   34     showAscii(true),
   35     showExHex(true),
   36     showExAddr(true)
   37 {
   38     setMouseTracking(true);
   39     setFocusPolicy(Qt::FocusPolicy::StrongFocus);
   40     connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, [this]() { viewport()->update(); });
   41 
   42     connect(Config(), &Configuration::colorsUpdated, this, &HexWidget::updateColors);
   43     connect(Config(), &Configuration::fontsUpdated, this, [this]() { setMonospaceFont(Config()->getFont()); });
   44 
   45     auto sizeActionGroup = new QActionGroup(this);
   46     for (int i = 1; i <= 8; i *= 2) {
   47         QAction *action = new QAction(QString::number(i), this);
   48         action->setCheckable(true);
   49         action->setActionGroup(sizeActionGroup);
   50         connect(action, &QAction::triggered, this, [=]() { setItemSize(i); });
   51         actionsItemSize.append(action);
   52     }
   53     actionsItemSize.at(0)->setChecked(true);
   54 
   55     /* Follow the order in ItemFormat enum */
   56     QStringList names;
   57     names << tr("Hexadecimal");
   58     names << tr("Octal");
   59     names << tr("Decimal");
   60     names << tr("Signed decimal");
   61     names << tr("Float");
   62 
   63     auto formatActionGroup = new QActionGroup(this);
   64     for (int i = 0; i < names.length(); ++i) {
   65         QAction *action = new QAction(names.at(i), this);
   66         action->setCheckable(true);
   67         action->setActionGroup(formatActionGroup);
   68         connect(action, &QAction::triggered, this, [=]() { setItemFormat(static_cast<ItemFormat>(i)); });
   69         actionsItemFormat.append(action);
   70     }
   71     actionsItemFormat.at(0)->setChecked(true);
   72     actionsItemFormat.at(ItemFormatFloat)->setEnabled(false);
   73 
   74     rowSizeMenu = new QMenu(tr("Bytes per row"), this);
   75     auto columnsActionGroup = new QActionGroup(this);
   76     for (int i = 1; i <= MAX_LINE_WIDTH_PRESET; i *= 2) {
   77         QAction *action = new QAction(QString::number(i), rowSizeMenu);
   78         action->setCheckable(true);
   79         action->setActionGroup(columnsActionGroup);
   80         connect(action, &QAction::triggered, this, [=]() { setFixedLineSize(i); });
   81         rowSizeMenu->addAction(action);
   82     }
   83     rowSizeMenu->addSeparator();
   84     actionRowSizePowerOf2 = new QAction(tr("Power of 2"), this);
   85     actionRowSizePowerOf2->setCheckable(true);
   86     actionRowSizePowerOf2->setActionGroup(columnsActionGroup);
   87     connect(actionRowSizePowerOf2, &QAction::triggered, this, [=]() { setColumnMode(ColumnMode::PowerOf2); });
   88     rowSizeMenu->addAction(actionRowSizePowerOf2);
   89 
   90     actionItemBigEndian = new QAction(tr("Big Endian"), this);
   91     actionItemBigEndian->setCheckable(true);
   92     actionItemBigEndian->setEnabled(false);
   93     connect(actionItemBigEndian, &QAction::triggered, this, &HexWidget::setItemEndianess);
   94 
   95     actionHexPairs = new QAction(tr("Bytes as pairs"), this);
   96     actionHexPairs->setCheckable(true);
   97     connect(actionHexPairs, &QAction::triggered, this, &HexWidget::onHexPairsModeEnabled);
   98 
   99     actionCopy = new QAction(tr("Copy"), this);
  100     addAction(actionCopy);
  101     actionCopy->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
  102     actionCopy->setShortcut(QKeySequence::Copy);
  103     connect(actionCopy, &QAction::triggered, this, &HexWidget::copy);
  104 
  105     actionCopyAddress = new QAction(tr("Copy address"), this);
  106     actionCopyAddress->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
  107     actionCopyAddress->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
  108     connect(actionCopyAddress, &QAction::triggered, this, &HexWidget::copyAddress);
  109     addAction(actionCopyAddress);
  110 
  111     actionSelectRange = new QAction(tr("Select range"), this);
  112     connect(actionSelectRange, &QAction::triggered, this, [this]() { rangeDialog.open(cursor.address); });
  113     addAction(actionSelectRange);
  114     connect(&rangeDialog, &QDialog::accepted, this, &HexWidget::onRangeDialogAccepted);
  115 
  116     connect(this, &HexWidget::selectionChanged, this, [this](Selection selection) {
  117         actionCopy->setEnabled(!selection.empty);
  118     });
  119 
  120     updateMetrics();
  121     updateItemLength();
  122 
  123     startAddress = 0ULL;
  124     cursor.address = 0ULL;
  125     data.reset(new MemoryData());
  126 
  127     fetchData();
  128     updateCursorMeta();
  129 
  130     connect(&cursor.blinkTimer, &QTimer::timeout, this, &HexWidget::onCursorBlinked);
  131     cursor.setBlinkPeriod(1000);
  132     cursor.startBlinking();
  133 
  134     updateColors();
  135 }
  136 
  137 HexWidget::~HexWidget()
  138 {
  139 
  140 }
  141 
  142 void HexWidget::setMonospaceFont(const QFont &font)
  143 {
  144     if (!(font.styleHint() & QFont::Monospace)) {
  145         /* FIXME: Use default monospace font
  146         setFont(XXX); */
  147     }
  148     QScrollArea::setFont(font);
  149     monospaceFont = font.resolve(this->font());
  150     updateMetrics();
  151     fetchData();
  152     updateCursorMeta();
  153 
  154     viewport()->update();
  155 }
  156 
  157 void HexWidget::setItemSize(int nbytes)
  158 {
  159     static const QVector<int> values({1, 2, 4, 8});
  160 
  161     if (!values.contains(nbytes))
  162         return;
  163 
  164     itemByteLen = nbytes;
  165     if (itemByteLen > rowSizeBytes) {
  166         rowSizeBytes = itemByteLen;
  167     }
  168 
  169     actionsItemFormat.at(ItemFormatFloat)->setEnabled(nbytes >= 4);
  170     actionItemBigEndian->setEnabled(nbytes != 1);
  171 
  172     updateItemLength();
  173     fetchData();
  174     updateCursorMeta();
  175 
  176     viewport()->update();
  177 }
  178 
  179 void HexWidget::setItemFormat(ItemFormat format)
  180 {
  181     itemFormat = format;
  182 
  183     bool sizeEnabled = true;
  184     if (format == ItemFormatFloat)
  185         sizeEnabled = false;
  186     actionsItemSize.at(0)->setEnabled(sizeEnabled);
  187     actionsItemSize.at(1)->setEnabled(sizeEnabled);
  188 
  189 
  190     updateItemLength();
  191     fetchData();
  192     updateCursorMeta();
  193 
  194     viewport()->update();
  195 }
  196 
  197 void HexWidget::setItemGroupSize(int size)
  198 {
  199     itemGroupSize = size;
  200 
  201     updateCounts();
  202     fetchData();
  203     updateCursorMeta();
  204 
  205     viewport()->update();
  206 }
  207 
  208 void HexWidget::updateCounts()
  209 {
  210     actionHexPairs->setEnabled(rowSizeBytes > 1 && itemByteLen == 1
  211                                && itemFormat == ItemFormat::ItemFormatHex);
  212     actionHexPairs->setChecked(Core()->getConfigb("hex.pairs"));
  213     if (actionHexPairs->isChecked() && actionHexPairs->isEnabled()) {
  214         itemGroupSize = 2;
  215     } else {
  216         itemGroupSize = 1;
  217     }
  218 
  219     if (columnMode == ColumnMode::PowerOf2) {
  220         int last_good_size = itemGroupByteLen();
  221         for (int i = itemGroupByteLen(); i <= MAX_LINE_WIDTH_BYTES; i *= 2) {
  222             rowSizeBytes = i;
  223             itemColumns = rowSizeBytes / itemGroupByteLen();
  224             updateAreasPosition();
  225             if (horizontalScrollBar()->maximum() == 0) {
  226                 last_good_size = rowSizeBytes;
  227             } else {
  228                 break;
  229             }
  230         }
  231         rowSizeBytes = last_good_size;
  232     }
  233 
  234     itemColumns = rowSizeBytes / itemGroupByteLen();
  235 
  236     // ensure correct action is selected when changing line size programmatically
  237     if (columnMode == ColumnMode::Fixed) {
  238         int w = 1;
  239         const auto &actions = rowSizeMenu->actions();
  240         for (auto action : actions) {
  241             action->setChecked(false);
  242         }
  243         for (auto action : actions) {
  244             if (w > MAX_LINE_WIDTH_PRESET) {
  245                 break;
  246             }
  247             if (rowSizeBytes == w) {
  248                 action->setChecked(true);
  249             }
  250             w *= 2;
  251         }
  252     } else if (columnMode == ColumnMode::PowerOf2) {
  253         actionRowSizePowerOf2->setChecked(true);
  254     }
  255 
  256     updateAreasPosition();
  257 }
  258 
  259 void HexWidget::setFixedLineSize(int lineSize)
  260 {
  261     if (lineSize < 1 || lineSize < itemGroupByteLen() || lineSize % itemGroupByteLen()) {
  262         updateCounts();
  263         return;
  264     }
  265     rowSizeBytes = lineSize;
  266     columnMode = ColumnMode::Fixed;
  267 
  268     updateCounts();
  269     fetchData();
  270     updateCursorMeta();
  271 
  272     viewport()->update();
  273 }
  274 
  275 void HexWidget::setColumnMode(ColumnMode mode)
  276 {
  277     columnMode = mode;
  278 
  279     updateCounts();
  280     fetchData();
  281     updateCursorMeta();
  282 
  283     viewport()->update();
  284 }
  285 
  286 void HexWidget::selectRange(RVA start, RVA end)
  287 {
  288     BasicCursor endCursor(end);
  289     endCursor += 1;
  290     setCursorAddr(endCursor);
  291     selection.set(start, end);
  292     cursorEnabled = false;
  293     emit selectionChanged(getSelection());
  294 }
  295 
  296 void HexWidget::clearSelection()
  297 {
  298     setCursorAddr(cursor.address, false);
  299     emit selectionChanged(getSelection());
  300 }
  301 
  302 HexWidget::Selection HexWidget::getSelection()
  303 {
  304     return Selection{selection.isEmpty(), selection.start(), selection.end()};
  305 }
  306 
  307 void HexWidget::seek(uint64_t address)
  308 {
  309     setCursorAddr(address);
  310 }
  311 
  312 void HexWidget::refresh()
  313 {
  314     fetchData();
  315     viewport()->update();
  316 }
  317 
  318 void HexWidget::setItemEndianess(bool bigEndian)
  319 {
  320     itemBigEndian = bigEndian;
  321 
  322     updateCursorMeta(); // Update cached item character
  323 
  324     viewport()->update();
  325 }
  326 
  327 void HexWidget::updateColors()
  328 {
  329     borderColor = Config()->getColor("gui.border");
  330     backgroundColor = Config()->getColor("gui.background");
  331     b0x00Color = Config()->getColor("b0x00");
  332     b0x7fColor = Config()->getColor("b0x7f");
  333     b0xffColor = Config()->getColor("b0xff");
  334     printableColor = Config()->getColor("ai.write");
  335     defColor = Config()->getColor("btext");
  336     addrColor = Config()->getColor("func_var_addr");
  337 
  338     updateCursorMeta();
  339     viewport()->update();
  340 }
  341 
  342 void HexWidget::paintEvent(QPaintEvent *event)
  343 {
  344     QPainter painter(viewport());
  345     painter.setFont(monospaceFont);
  346 
  347     int xOffset = horizontalScrollBar()->value();
  348     if (xOffset > 0)
  349         painter.translate(QPoint(-xOffset, 0));
  350 
  351     if (event->rect() == cursor.screenPos) {
  352         /* Cursor blink */
  353         drawCursor(painter);
  354         return;
  355     }
  356 
  357     painter.fillRect(event->rect().translated(xOffset, 0), backgroundColor);
  358 
  359     drawHeader(painter);
  360 
  361     drawAddrArea(painter);
  362     drawItemArea(painter);
  363     drawAsciiArea(painter);
  364 
  365     if (!cursorEnabled)
  366         return;
  367 
  368     drawCursor(painter, true);
  369 }
  370 
  371 void HexWidget::updateWidth()
  372 {
  373     int max = (showAscii ? asciiArea.right() : itemArea.right()) - viewport()->width();
  374     if (max < 0)
  375         max = 0;
  376     else
  377         max += charWidth;
  378     horizontalScrollBar()->setMaximum(max);
  379     horizontalScrollBar()->setSingleStep(charWidth);
  380 }
  381 
  382 void HexWidget::resizeEvent(QResizeEvent *event)
  383 {
  384     int oldByteCount = bytesPerScreen();
  385     updateCounts();
  386 
  387     if (event->oldSize().height() == event->size().height() && oldByteCount == bytesPerScreen())
  388         return;
  389 
  390     updateAreasHeight();
  391     fetchData(); // rowCount was changed
  392     updateCursorMeta();
  393 
  394     viewport()->update();
  395 }
  396 
  397 void HexWidget::mouseMoveEvent(QMouseEvent *event)
  398 {
  399     QPoint pos = event->pos();
  400     pos.rx() += horizontalScrollBar()->value();
  401 
  402     if (!updatingSelection) {
  403         if (itemArea.contains(pos) || asciiArea.contains(pos))
  404             setCursor(Qt::IBeamCursor);
  405         else
  406             setCursor(Qt::ArrowCursor);
  407         return;
  408     }
  409 
  410     auto &area = currentArea();
  411     if (pos.x() < area.left())
  412         pos.setX(area.left());
  413     else if (pos.x() > area.right())
  414         pos.setX(area.right());
  415     auto addr = currentAreaPosToAddr(pos, true);
  416     setCursorAddr(addr, true);
  417 
  418     /* Stop blinking */
  419     cursorEnabled = false;
  420 
  421     viewport()->update();
  422 }
  423 
  424 void HexWidget::mousePressEvent(QMouseEvent *event)
  425 {
  426     QPoint pos(event->pos());
  427     pos.rx() += horizontalScrollBar()->value();
  428 
  429     if (event->button() == Qt::LeftButton) {
  430         bool selectingData = itemArea.contains(pos);
  431         bool selecting = selectingData || asciiArea.contains(pos);
  432         if (selecting) {
  433             updatingSelection = true;
  434             setCursorOnAscii(!selectingData);
  435             auto cursorPosition = currentAreaPosToAddr(pos, true);
  436             setCursorAddr(cursorPosition, event->modifiers() == Qt::ShiftModifier);
  437             viewport()->update();
  438         }
  439     }
  440 }
  441 
  442 void HexWidget::mouseReleaseEvent(QMouseEvent *event)
  443 {
  444     if (event->button() == Qt::LeftButton) {
  445         if (selection.isEmpty()) {
  446             selection.init(cursor.address);
  447             cursorEnabled = true;
  448         }
  449         updatingSelection = false;
  450     }
  451 }
  452 
  453 void HexWidget::wheelEvent(QWheelEvent *event)
  454 {
  455     int dy = event->delta();
  456     int64_t delta = 3 * itemRowByteLen();
  457     if (dy > 0)
  458         delta = -delta;
  459 
  460     if (dy == 0)
  461         return;
  462 
  463     if (delta < 0 && startAddress < static_cast<uint64_t>(-delta)) {
  464         startAddress = 0;
  465     } else if (delta > 0 && data->maxIndex() < static_cast<uint64_t>(bytesPerScreen())) {
  466         startAddress = 0;
  467     } else if (delta > 0
  468                && (data->maxIndex() - startAddress) <= static_cast<uint64_t>(bytesPerScreen() + delta - 1)) {
  469         startAddress = (data->maxIndex() - bytesPerScreen()) + 1;
  470     } else {
  471         startAddress += delta;
  472     }
  473     fetchData();
  474     if (cursor.address >= startAddress && cursor.address <= lastVisibleAddr()) {
  475         /* Don't enable cursor blinking if selection isn't empty */
  476         cursorEnabled = selection.isEmpty();
  477         updateCursorMeta();
  478     } else {
  479         cursorEnabled = false;
  480     }
  481     viewport()->update();
  482 }
  483 
  484 void HexWidget::keyPressEvent(QKeyEvent *event)
  485 {
  486     bool select = false;
  487     auto moveOrSelect = [event, &select](QKeySequence::StandardKey moveSeq, QKeySequence::StandardKey selectSeq) ->bool {
  488         if (event->matches(moveSeq)) {
  489             select = false;
  490             return true;
  491         } else if (event->matches(selectSeq)) {
  492             select = true;
  493             return true;
  494         }
  495         return false;
  496     };
  497     if (moveOrSelect(QKeySequence::MoveToNextLine, QKeySequence::SelectNextLine)) {
  498         moveCursor(itemRowByteLen(), select);
  499     } else if (moveOrSelect(QKeySequence::MoveToPreviousLine, QKeySequence::SelectPreviousLine)) {
  500         moveCursor(-itemRowByteLen(), select);
  501     } else if (moveOrSelect(QKeySequence::MoveToNextChar, QKeySequence::SelectNextChar)) {
  502         moveCursor(cursorOnAscii ? 1 : itemByteLen, select);
  503     } else if (moveOrSelect(QKeySequence::MoveToPreviousChar, QKeySequence::SelectPreviousChar)) {
  504         moveCursor(cursorOnAscii ? -1 : -itemByteLen, select);
  505     } else if (moveOrSelect(QKeySequence::MoveToNextPage, QKeySequence::SelectNextPage)) {
  506         moveCursor(bytesPerScreen(), select);
  507     } else if (moveOrSelect(QKeySequence::MoveToPreviousPage, QKeySequence::SelectPreviousPage)) {
  508         moveCursor(-bytesPerScreen(), select);
  509     } else if (moveOrSelect(QKeySequence::MoveToStartOfLine, QKeySequence::SelectStartOfLine)) {
  510         int linePos = int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));
  511         moveCursor(-linePos, select);
  512     } else if (moveOrSelect(QKeySequence::MoveToEndOfLine, QKeySequence::SelectEndOfLine)) {
  513         int linePos = int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));
  514         moveCursor(itemRowByteLen() - linePos, select);
  515     }
  516     //viewport()->update();
  517 }
  518 
  519 void HexWidget::contextMenuEvent(QContextMenuEvent *event)
  520 {
  521     QPoint pt = event->pos();
  522     bool mouseOutsideSelection = false;
  523     if (event->reason() == QContextMenuEvent::Mouse) {
  524         auto mouseAddr = mousePosToAddr(pt).address;
  525         if (asciiArea.contains(pt)) {
  526             cursorOnAscii = true;
  527         } else if (itemArea.contains(pt)) {
  528             cursorOnAscii = false;
  529         }
  530         if (selection.isEmpty()) {
  531             seek(mouseAddr);
  532         } else {
  533             mouseOutsideSelection = !selection.contains(mouseAddr);
  534         }
  535     }
  536 
  537     auto disableOutsideSelectionActions = [this](bool disable) {
  538         actionCopyAddress->setDisabled(disable);
  539     };
  540 
  541     QMenu *menu = new QMenu();
  542     QMenu *sizeMenu = menu->addMenu(tr("Item size:"));
  543     sizeMenu->addActions(actionsItemSize);
  544     QMenu *formatMenu = menu->addMenu(tr("Item format:"));
  545     formatMenu->addActions(actionsItemFormat);
  546     menu->addMenu(rowSizeMenu);
  547     menu->addAction(actionHexPairs);
  548     menu->addAction(actionItemBigEndian);
  549     menu->addSeparator();
  550     menu->addAction(actionCopy);
  551     disableOutsideSelectionActions(mouseOutsideSelection);
  552     menu->addAction(actionCopyAddress);
  553     menu->addActions(this->actions());
  554     menu->exec(mapToGlobal(pt));
  555     disableOutsideSelectionActions(false);
  556     menu->deleteLater();
  557 }
  558 
  559 void HexWidget::onCursorBlinked()
  560 {
  561     if (!cursorEnabled)
  562         return;
  563     cursor.blink();
  564     QRect cursorRect(cursor.screenPos.x(), cursor.screenPos.y(), cursor.screenPos.width(), cursor.screenPos.height());
  565     viewport()->update(cursorRect.translated(-horizontalScrollBar()->value(), 0));
  566 }
  567 
  568 void HexWidget::onHexPairsModeEnabled(bool enable)
  569 {
  570     // Sync configuration
  571     Core()->setConfig("hex.pairs", enable);
  572     if (enable) {
  573         setItemGroupSize(2);
  574     } else {
  575         setItemGroupSize(1);
  576     }
  577 }
  578 
  579 void HexWidget::copy()
  580 {
  581     if (selection.isEmpty() || selection.size() > MAX_COPY_SIZE)
  582         return;
  583 
  584     QClipboard *clipboard = QApplication::clipboard();
  585     QString range = QString("%1@0x%2").arg(selection.size()).arg(selection.start(), 0, 16);
  586     if (cursorOnAscii) {
  587         clipboard->setText(Core()->cmd("psx " + range));
  588     } else {
  589         clipboard->setText(Core()->cmd("p8 " + range)); //TODO: copy in the format shown
  590     }
  591 }
  592 
  593 void HexWidget::copyAddress()
  594 {
  595     uint64_t addr = cursor.address;
  596     if (!selection.isEmpty()) {
  597         addr = selection.start();
  598     }
  599     QClipboard *clipboard = QApplication::clipboard();
  600     clipboard->setText(RAddressString(addr));
  601 }
  602 
  603 void HexWidget::onRangeDialogAccepted()
  604 {
  605     if (rangeDialog.empty()) {
  606         seek(rangeDialog.getStartAddress());
  607         return;
  608     }
  609     selectRange(rangeDialog.getStartAddress(), rangeDialog.getEndAddress());
  610 }
  611 
  612 void HexWidget::updateItemLength()
  613 {
  614     itemPrefixLen = 0;
  615 
  616     switch (itemFormat) {
  617     case ItemFormatHex:
  618         itemCharLen = 2 * itemByteLen;
  619         if (itemByteLen > 1 && showExHex)
  620             itemPrefixLen = hexPrefix.length();
  621         break;
  622     case ItemFormatOct:
  623         itemCharLen = (itemByteLen * 8 + 3) / 3;
  624         break;
  625     case ItemFormatDec:
  626         switch (itemByteLen) {
  627         case 1:
  628             itemCharLen = 3;
  629             break;
  630         case 2:
  631             itemCharLen = 5;
  632             break;
  633         case 4:
  634             itemCharLen = 10;
  635             break;
  636         case 8:
  637             itemCharLen = 20;
  638             break;
  639         }
  640         break;
  641     case ItemFormatSignedDec:
  642         switch (itemByteLen) {
  643         case 1:
  644             itemCharLen = 4;
  645             break;
  646         case 2:
  647             itemCharLen = 6;
  648             break;
  649         case 4:
  650             itemCharLen = 11;
  651             break;
  652         case 8:
  653             itemCharLen = 20;
  654             break;
  655         }
  656         break;
  657     case ItemFormatFloat:
  658         if (itemByteLen < 4)
  659             itemByteLen = 4;
  660         // FIXME
  661         itemCharLen = 3 * itemByteLen;
  662         break;
  663     }
  664 
  665     itemCharLen += itemPrefixLen;
  666 
  667     updateCounts();
  668 }
  669 
  670 void HexWidget::drawHeader(QPainter &painter)
  671 {
  672     if (!showHeader)
  673         return;
  674 
  675     int offset = 0;
  676     QRectF rect(itemArea.left(), 0, itemWidth(), lineHeight);
  677 
  678     painter.setPen(addrColor);
  679 
  680     for (int j = 0; j < itemColumns; ++j) {
  681         for (int k = 0; k < itemGroupSize; ++k, offset += itemByteLen) {
  682             painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, QString::number(offset, 16).toUpper());
  683             rect.translate(itemWidth(), 0);
  684         }
  685         rect.translate(columnSpacingWidth(), 0);
  686     }
  687 
  688     rect.moveLeft(asciiArea.left());
  689     rect.setWidth(charWidth);
  690     for (int j = 0; j < itemRowByteLen(); ++j) {
  691         painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, QString::number(j % 16, 16).toUpper());
  692         rect.translate(charWidth, 0);
  693     }
  694 }
  695 
  696 void HexWidget::drawCursor(QPainter &painter, bool shadow)
  697 {
  698     if (shadow) {
  699         QPen pen(Qt::gray);
  700         pen.setStyle(Qt::DashLine);
  701         painter.setPen(pen);
  702         shadowCursor.screenPos.setWidth(cursorOnAscii ? itemWidth() : charWidth);
  703         painter.drawRect(shadowCursor.screenPos);
  704         painter.setPen(Qt::SolidLine);
  705     }
  706 
  707     painter.setPen(cursor.cachedColor);
  708     QRectF charRect(cursor.screenPos);
  709     charRect.setWidth(charWidth);
  710     painter.fillRect(charRect, backgroundColor);
  711     painter.drawText(charRect, Qt::AlignVCenter, cursor.cachedChar);
  712     if (cursor.isVisible) {
  713         painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
  714         painter.fillRect(cursor.screenPos, QColor(0xff, 0xff, 0xff));
  715     }
  716 }
  717 
  718 void HexWidget::drawAddrArea(QPainter &painter)
  719 {
  720     uint64_t offset = startAddress;
  721     QString addrString;
  722     QSizeF areaSize((addrCharLen + (showExAddr ? 2 : 0)) * charWidth, lineHeight);
  723     QRectF strRect(addrArea.topLeft(), areaSize);
  724 
  725     painter.setPen(addrColor);
  726     for (int line = 0;
  727             line < visibleLines && offset <= data->maxIndex();
  728             ++line, strRect.translate(0, lineHeight), offset += itemRowByteLen()) {
  729         addrString = QString("%1").arg(offset, addrCharLen, 16, QLatin1Char('0'));
  730         if (showExAddr)
  731             addrString.prepend(hexPrefix);
  732         painter.drawText(strRect, Qt::AlignVCenter, addrString);
  733     }
  734 
  735     painter.setPen(borderColor);
  736 
  737     qreal vLineOffset = itemArea.left() - charWidth;
  738     painter.drawLine(QLineF(vLineOffset, 0, vLineOffset, viewport()->height()));
  739 }
  740 
  741 void HexWidget::drawItemArea(QPainter &painter)
  742 {
  743     QRectF itemRect(itemArea.topLeft(), QSizeF(itemWidth(), lineHeight));
  744     QColor itemColor;
  745     QString itemString;
  746 
  747     fillSelectionBackground(painter);
  748 
  749     uint64_t itemAddr = startAddress;
  750     for (int line = 0; line < visibleLines; ++line) {
  751         itemRect.moveLeft(itemArea.left());
  752         for (int j = 0; j < itemColumns; ++j) {
  753             for (int k = 0; k < itemGroupSize && itemAddr <= data->maxIndex(); ++k, itemAddr += itemByteLen) {
  754                 itemString = renderItem(itemAddr - startAddress, &itemColor);
  755                 if (selection.contains(itemAddr)  && !cursorOnAscii) {
  756                     itemColor = palette().highlightedText().color();
  757                 }
  758                 painter.setPen(itemColor);
  759                 painter.drawText(itemRect, Qt::AlignVCenter, itemString);
  760                 itemRect.translate(itemWidth(), 0);
  761                 if (cursor.address == itemAddr) {
  762                     auto &itemCursor = cursorOnAscii ? shadowCursor : cursor;
  763                     itemCursor.cachedChar = itemString.at(0);
  764                     itemCursor.cachedColor = itemColor;
  765                 }
  766             }
  767             itemRect.translate(columnSpacingWidth(), 0);
  768         }
  769         itemRect.translate(0, lineHeight);
  770     }
  771 
  772     painter.setPen(borderColor);
  773 
  774     qreal vLineOffset = asciiArea.left() - charWidth;
  775     painter.drawLine(QLineF(vLineOffset, 0, vLineOffset, viewport()->height()));
  776 }
  777 
  778 void HexWidget::drawAsciiArea(QPainter &painter)
  779 {
  780     QRectF charRect(asciiArea.topLeft(), QSizeF(charWidth, lineHeight));
  781 
  782     fillSelectionBackground(painter, true);
  783 
  784     uint64_t address = startAddress;
  785     QChar ascii;
  786     QColor color;
  787     for (int line = 0; line < visibleLines; ++line, charRect.translate(0, lineHeight)) {
  788         charRect.moveLeft(asciiArea.left());
  789         for (int j = 0; j < itemRowByteLen() && address <= data->maxIndex(); ++j, ++address) {
  790             ascii = renderAscii(address - startAddress, &color);
  791             if (selection.contains(address) && cursorOnAscii)
  792                 color = palette().highlightedText().color();
  793             painter.setPen(color);
  794             /* Dots look ugly. Use fillRect() instead of drawText(). */
  795             if (ascii == '.') {
  796                 qreal a = cursor.screenPos.width();
  797                 QPointF p = charRect.bottomLeft();
  798                 p.rx() += (charWidth - a) / 2 + 1;
  799                 p.ry() += - 2 * a;
  800                 painter.fillRect(QRectF(p, QSizeF(a, a)), color);
  801             } else {
  802                 painter.drawText(charRect, Qt::AlignVCenter, ascii);
  803             }
  804             charRect.translate(charWidth, 0);
  805             if (cursor.address == address) {
  806                 auto &itemCursor = cursorOnAscii ? cursor : shadowCursor;
  807                 itemCursor.cachedChar = ascii;
  808                 itemCursor.cachedColor = color;
  809             }
  810         }
  811     }
  812 }
  813 
  814 void HexWidget::fillSelectionBackground(QPainter &painter, bool ascii)
  815 {
  816     if (selection.isEmpty()) {
  817         return;
  818     }
  819     const auto parts = rangePolygons(selection.start(), selection.end(), ascii);
  820     for (const auto &shape : qAsConst(parts)) {
  821         QColor highlightColor = palette().color(QPalette::Highlight);
  822         if (ascii == cursorOnAscii) {
  823             painter.setBrush(highlightColor);
  824             painter.drawPolygon(shape);
  825         } else {
  826             painter.setPen(highlightColor);
  827             painter.drawPolyline(shape);
  828         }
  829     }
  830 }
  831 
  832 QVector<QPolygonF> HexWidget::rangePolygons(RVA start, RVA last, bool ascii)
  833 {
  834     if (last < startAddress || start > lastVisibleAddr()) {
  835         return {};
  836     }
  837 
  838     QRectF rect;
  839     const QRectF area = QRectF(ascii ? asciiArea : itemArea);
  840 
  841     /* Convert absolute values to relative */
  842     int startOffset = std::max(uint64_t(start), startAddress) - startAddress;
  843     int endOffset = std::min(uint64_t(last), lastVisibleAddr()) - startAddress;
  844 
  845     QVector<QPolygonF> parts;
  846 
  847     auto getRectangle = [&](int offset) {
  848         return QRectF(ascii ? asciiRectangle(offset) : itemRectangle(offset));
  849     };
  850 
  851     auto startRect = getRectangle(startOffset);
  852     auto endRect = getRectangle(endOffset);
  853     if (!ascii) {
  854         if (int startFraction = startOffset % itemByteLen) {
  855             startRect.setLeft(startRect.left() + startFraction * startRect.width() / itemByteLen);
  856         }
  857         if (int endFraction = itemByteLen - 1 - (endOffset % itemByteLen)) {
  858             endRect.setRight(endRect.right() - endFraction * endRect.width() / itemByteLen);
  859         }
  860     }
  861     if (endOffset - startOffset + 1 <= rowSizeBytes) {
  862         if (startOffset / rowSizeBytes == endOffset / rowSizeBytes) { // single row
  863             rect = startRect;
  864             rect.setRight(endRect.right());
  865             parts.push_back(QPolygonF(rect));
  866         } else {
  867             // two seperate rectangles
  868             rect = startRect;
  869             rect.setRight(area.right());
  870             parts.push_back(QPolygonF(rect));
  871             rect = endRect;
  872             rect.setLeft(area.left());
  873             parts.push_back(QPolygonF(rect));
  874         }
  875     } else {
  876         // single multiline shape
  877         QPolygonF shape;
  878         shape << startRect.topLeft();
  879         rect = getRectangle(startOffset + rowSizeBytes - 1 - startOffset % rowSizeBytes);
  880         shape << rect.topRight();
  881         if (endOffset % rowSizeBytes != rowSizeBytes - 1) {
  882             rect = getRectangle(endOffset - endOffset % rowSizeBytes - 1);
  883             shape << rect.bottomRight() << endRect.topRight();
  884         }
  885         shape << endRect.bottomRight();
  886         shape << getRectangle(endOffset - endOffset % rowSizeBytes).bottomLeft();
  887         if (startOffset % rowSizeBytes) {
  888             rect = getRectangle(startOffset - startOffset % rowSizeBytes + rowSizeBytes);
  889             shape << rect.topLeft() << startRect.bottomLeft();
  890         }
  891         shape << shape.first(); // close the shape
  892         parts.push_back(shape);
  893     }
  894     return parts;
  895 }
  896 
  897 void HexWidget::updateMetrics()
  898 {
  899     QFontMetricsF fontMetrics(this->monospaceFont);
  900     lineHeight = fontMetrics.height();
  901     charWidth = fontMetrics.width(QLatin1Char('F'));
  902 
  903     updateCounts();
  904     updateAreasHeight();
  905 
  906     qreal cursorWidth = std::max(charWidth / 3, 1.);
  907     cursor.screenPos.setHeight(lineHeight);
  908     shadowCursor.screenPos.setHeight(lineHeight);
  909 
  910     cursor.screenPos.setWidth(cursorWidth);
  911     if (cursorOnAscii) {
  912         cursor.screenPos.moveTopLeft(asciiArea.topLeft());
  913 
  914         shadowCursor.screenPos.setWidth(itemWidth());
  915         shadowCursor.screenPos.moveTopLeft(itemArea.topLeft());
  916     } else {
  917         cursor.screenPos.moveTopLeft(itemArea.topLeft());
  918         shadowCursor.screenPos.setWidth(charWidth);
  919         shadowCursor.screenPos.moveTopLeft(asciiArea.topLeft());
  920     }
  921 }
  922 
  923 void HexWidget::updateAreasPosition()
  924 {
  925     const qreal spacingWidth = areaSpacingWidth();
  926 
  927     qreal yOffset = showHeader ? lineHeight : 0;
  928 
  929     addrArea.setTopLeft(QPointF(0, yOffset));
  930     addrArea.setWidth((addrCharLen + (showExAddr ? 2 : 0)) * charWidth);
  931 
  932     itemArea.setTopLeft(QPointF(addrArea.right() + spacingWidth, yOffset));
  933     itemArea.setWidth(itemRowWidth());
  934 
  935     asciiArea.setTopLeft(QPointF(itemArea.right() + spacingWidth, yOffset));
  936     asciiArea.setWidth(asciiRowWidth());
  937 
  938     updateWidth();
  939 }
  940 
  941 void HexWidget::updateAreasHeight()
  942 {
  943     visibleLines = static_cast<int>((viewport()->height() - itemArea.top()) / lineHeight);
  944 
  945     qreal height = visibleLines * lineHeight;
  946     addrArea.setHeight(height);
  947     itemArea.setHeight(height);
  948     asciiArea.setHeight(height);
  949 }
  950 
  951 void HexWidget::moveCursor(int offset, bool select)
  952 {
  953     BasicCursor addr = cursor.address;
  954     addr += offset;
  955     if (addr.address > data->maxIndex()) {
  956         addr.address = data->maxIndex();
  957     }
  958     setCursorAddr(addr, select);
  959 }
  960 
  961 void HexWidget::setCursorAddr(BasicCursor addr, bool select)
  962 {
  963     if (!select) {
  964         bool clearingSelection = !selection.isEmpty();
  965         selection.init(addr);
  966         if (clearingSelection)
  967             emit selectionChanged(getSelection());
  968     }
  969     emit positionChanged(addr.address);
  970 
  971     cursor.address = addr.address;
  972 
  973     /* Pause cursor repainting */
  974     cursorEnabled = false;
  975 
  976     if (select) {
  977         selection.update(addr);
  978         emit selectionChanged(getSelection());
  979     }
  980 
  981     uint64_t addressValue = cursor.address;
  982     /* Update data cache if necessary */
  983     if (!(addressValue >= startAddress && addressValue <= lastVisibleAddr())) {
  984         /* Align start address */
  985         addressValue -= (addressValue % itemRowByteLen());
  986 
  987         if (addressValue > (data->maxIndex() - bytesPerScreen()) + 1) {
  988             addressValue = (data->maxIndex() - bytesPerScreen()) + 1;
  989         }
  990 
  991         /* FIXME: handling Page Up/Down */
  992         if (addressValue == startAddress + bytesPerScreen()) {
  993             startAddress += itemRowByteLen();
  994         } else {
  995             startAddress = addressValue;
  996         }
  997 
  998         fetchData();
  999     }
 1000 
 1001     updateCursorMeta();
 1002 
 1003     /* Draw cursor */
 1004     cursor.isVisible = !select;
 1005     viewport()->update();
 1006 
 1007     /* Resume cursor repainting */
 1008     cursorEnabled = selection.isEmpty();
 1009 }
 1010 
 1011 void HexWidget::updateCursorMeta()
 1012 {
 1013     QPointF point;
 1014     QPointF pointAscii;
 1015 
 1016     int offset = cursor.address - startAddress;
 1017     int itemOffset = offset;
 1018     int asciiOffset;
 1019 
 1020     /* Calc common Y coordinate */
 1021     point.ry() = (itemOffset / itemRowByteLen()) * lineHeight;
 1022     pointAscii.setY(point.y());
 1023     itemOffset %= itemRowByteLen();
 1024     asciiOffset = itemOffset;
 1025 
 1026     /* Calc X coordinate on the item area */
 1027     point.rx() = (itemOffset / itemGroupByteLen()) * columnExWidth();
 1028     itemOffset %= itemGroupByteLen();
 1029     point.rx() += (itemOffset / itemByteLen) * itemWidth();
 1030 
 1031     /* Calc X coordinate on the ascii area */
 1032     pointAscii.rx() = asciiOffset * charWidth;
 1033 
 1034     point += itemArea.topLeft();
 1035     pointAscii += asciiArea.topLeft();
 1036 
 1037     cursor.screenPos.moveTopLeft(cursorOnAscii ? pointAscii : point);
 1038     shadowCursor.screenPos.moveTopLeft(cursorOnAscii ? point : pointAscii);
 1039 }
 1040 
 1041 void HexWidget::setCursorOnAscii(bool ascii)
 1042 {
 1043     cursorOnAscii = ascii;
 1044 }
 1045 
 1046 const QColor HexWidget::itemColor(uint8_t byte)
 1047 {
 1048     QColor color(defColor);
 1049 
 1050     if (byte == 0x00)
 1051         color = b0x00Color;
 1052     else if (byte == 0x7f)
 1053         color = b0x7fColor;
 1054     else if (byte == 0xff)
 1055         color = b0xffColor;
 1056     else if (IS_PRINTABLE(byte)) {
 1057         color = printableColor;
 1058     }
 1059 
 1060     return color;
 1061 }
 1062 
 1063 QVariant HexWidget::readItem(int offset, QColor *color)
 1064 {
 1065     quint8 byte;
 1066     quint16 word;
 1067     quint32 dword;
 1068     quint64 qword;
 1069     float *ptrFloat32;
 1070     double *ptrFloat64;
 1071 
 1072     const void *dataPtr = data->dataPtr(startAddress + offset);
 1073     const bool signedItem = itemFormat == ItemFormatSignedDec;
 1074 
 1075     switch (itemByteLen) {
 1076     case 1:
 1077         byte = *static_cast<const quint8 *>(dataPtr);
 1078         if (color)
 1079             *color = itemColor(byte);
 1080         if (!signedItem)
 1081             return QVariant(static_cast<quint64>(byte));
 1082         return QVariant(static_cast<qint64>(static_cast<qint8>(byte)));
 1083     case 2:
 1084         if (itemBigEndian)
 1085             word = qFromBigEndian<quint16>(dataPtr);
 1086         else
 1087             word = qFromLittleEndian<quint16>(dataPtr);
 1088         if (color)
 1089             *color = defColor;
 1090         if (!signedItem)
 1091             return QVariant(static_cast<quint64>(word));
 1092         return QVariant(static_cast<qint64>(static_cast<qint16>(word)));
 1093     case 4:
 1094         if (itemBigEndian)
 1095             dword = qFromBigEndian<quint32>(dataPtr);
 1096         else
 1097             dword = qFromLittleEndian<quint32>(dataPtr);
 1098         if (color)
 1099             *color = defColor;
 1100         if (itemFormat == ItemFormatFloat) {
 1101             ptrFloat32 = static_cast<float *>(static_cast<void *>(&dword));
 1102             return QVariant(*ptrFloat32);
 1103         }
 1104         if (!signedItem)
 1105             return QVariant(static_cast<quint64>(dword));
 1106         return QVariant(static_cast<qint64>(static_cast<qint32>(dword)));
 1107     case 8:
 1108         if (itemBigEndian)
 1109             qword = qFromBigEndian<quint64>(dataPtr);
 1110         else
 1111             qword = qFromLittleEndian<quint64>(dataPtr);
 1112         if (color)
 1113             *color = defColor;
 1114         if (itemFormat == ItemFormatFloat) {
 1115             ptrFloat64 = static_cast<double *>(static_cast<void *>(&qword));
 1116             return  QVariant(*ptrFloat64);
 1117         }
 1118         if (!signedItem)
 1119             return  QVariant(qword);
 1120         return QVariant(static_cast<qint64>(qword));
 1121     }
 1122 
 1123     return QVariant();
 1124 }
 1125 
 1126 QString HexWidget::renderItem(int offset, QColor *color)
 1127 {
 1128     QString item;
 1129     QVariant itemVal = readItem(offset, color);
 1130     int itemLen = itemCharLen - itemPrefixLen; /* Reserve space for prefix */
 1131 
 1132     //FIXME: handle broken itemVal ( QVariant() )
 1133     switch (itemFormat) {
 1134     case ItemFormatHex:
 1135         item = QString("%1").arg(itemVal.toULongLong(), itemLen, 16, QLatin1Char('0'));
 1136         if (itemByteLen > 1 && showExHex)
 1137             item.prepend(hexPrefix);
 1138         break;
 1139     case ItemFormatOct:
 1140         item = QString("%1").arg(itemVal.toULongLong(), itemLen, 8, QLatin1Char('0'));
 1141         break;
 1142     case ItemFormatDec:
 1143         item = QString("%1").arg(itemVal.toULongLong(), itemLen, 10);
 1144         break;
 1145     case ItemFormatSignedDec:
 1146         item = QString("%1").arg(itemVal.toLongLong(), itemLen, 10);
 1147         break;
 1148     case ItemFormatFloat:
 1149         item = QString("%1").arg(itemVal.toDouble(), itemLen);
 1150         break;
 1151     }
 1152 
 1153     return item;
 1154 }
 1155 
 1156 QChar HexWidget::renderAscii(int offset, QColor *color)
 1157 {
 1158     uchar byte = *static_cast<const uint8_t *>(data->dataPtr(startAddress + offset));
 1159     if (color) {
 1160         *color = itemColor(byte);
 1161     }
 1162     if (!IS_PRINTABLE(byte)) {
 1163         byte = '.';
 1164     }
 1165     return QChar(byte);
 1166 }
 1167 
 1168 void HexWidget::fetchData()
 1169 {
 1170     data->fetch(startAddress, bytesPerScreen());
 1171 }
 1172 
 1173 BasicCursor HexWidget::screenPosToAddr(const QPoint &point,  bool middle) const
 1174 {
 1175     QPointF pt = point - itemArea.topLeft();
 1176 
 1177     int relativeAddress = 0;
 1178     int line = static_cast<int>(pt.y() / lineHeight);
 1179     relativeAddress += line * itemRowByteLen();
 1180     int column = static_cast<int>(pt.x() / columnExWidth());
 1181     relativeAddress += column * itemGroupByteLen();
 1182     pt.rx() -= column * columnExWidth();
 1183     auto roundingOffset = middle ? itemWidth() / 2 : 0;
 1184     relativeAddress += static_cast<int>((pt.x() + roundingOffset) / itemWidth()) * itemByteLen;
 1185     BasicCursor result(startAddress);
 1186     result += relativeAddress;
 1187     return result;
 1188 }
 1189 
 1190 BasicCursor HexWidget::asciiPosToAddr(const QPoint &point, bool middle) const
 1191 {
 1192     QPointF pt = point - asciiArea.topLeft();
 1193 
 1194     int relativeAddress = 0;
 1195     relativeAddress += static_cast<int>(pt.y() / lineHeight) * itemRowByteLen();
 1196     auto roundingOffset = middle ? (charWidth / 2) : 0;
 1197     relativeAddress += static_cast<int>((pt.x() + (roundingOffset)) / charWidth);
 1198     BasicCursor result(startAddress);
 1199     result += relativeAddress;
 1200     return result;
 1201 }
 1202 
 1203 BasicCursor HexWidget::currentAreaPosToAddr(const QPoint &point, bool middle) const
 1204 {
 1205     return cursorOnAscii ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);
 1206 }
 1207 
 1208 BasicCursor HexWidget::mousePosToAddr(const QPoint &point, bool middle) const
 1209 {
 1210     return asciiArea.contains(point) ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);
 1211 }
 1212 
 1213 QRectF HexWidget::itemRectangle(uint offset)
 1214 {
 1215     qreal x;
 1216     qreal y;
 1217 
 1218     qreal width = itemWidth();
 1219     y = (offset / itemRowByteLen()) * lineHeight;
 1220     offset %= itemRowByteLen();
 1221 
 1222     x = (offset / itemGroupByteLen()) * columnExWidth();
 1223     offset %= itemGroupByteLen();
 1224     x += (offset / itemByteLen) * itemWidth();
 1225     if (offset == 0) {
 1226         x -= charWidth / 2;
 1227         width += charWidth / 2;
 1228     }
 1229     if (static_cast<int>(offset) == itemGroupByteLen() - 1) {
 1230         width += charWidth / 2;
 1231     }
 1232 
 1233     x += itemArea.x();
 1234     y += itemArea.y();
 1235 
 1236     return QRectF(x, y, width, lineHeight);
 1237 }
 1238 
 1239 QRectF HexWidget::asciiRectangle(uint offset)
 1240 {
 1241     QPointF p;
 1242 
 1243     p.ry() = (offset / itemRowByteLen()) * lineHeight;
 1244     offset %= itemRowByteLen();
 1245 
 1246     p.rx() = offset * charWidth;
 1247 
 1248     p += asciiArea.topLeft();
 1249 
 1250     return QRectF(p, QSizeF(charWidth, lineHeight));
 1251 }