"Fossies" - the Fresh Open Source Software Archive

Member "cutter-1.10.3/src/widgets/HexWidget.cpp" (8 May 2020, 49143 Bytes) of package /linux/privat/cutter-1.10.3.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.10.2_vs_1.10.3.

    1 #include "HexWidget.h"
    2 #include "Cutter.h"
    3 #include "Configuration.h"
    4 #include "dialogs/WriteCommandsDialogs.h"
    5 
    6 #include <QPainter>
    7 #include <QPaintEvent>
    8 #include <QResizeEvent>
    9 #include <QMouseEvent>
   10 #include <QKeyEvent>
   11 #include <QWheelEvent>
   12 #include <QtEndian>
   13 #include <QScrollBar>
   14 #include <QMenu>
   15 #include <QClipboard>
   16 #include <QApplication>
   17 #include <QInputDialog>
   18 #include <QPushButton>
   19 #include <QJsonObject>
   20 #include <QJsonArray>
   21 #include <QRegularExpression>
   22 #include <QToolTip>
   23 
   24 static constexpr uint64_t MAX_COPY_SIZE = 128 * 1024 * 1024;
   25 static constexpr int MAX_LINE_WIDTH_PRESET = 32;
   26 static constexpr int MAX_LINE_WIDTH_BYTES = 128 * 1024;
   27 
   28 HexWidget::HexWidget(QWidget *parent) :
   29     QScrollArea(parent),
   30     cursorEnabled(true),
   31     cursorOnAscii(false),
   32     updatingSelection(false),
   33     itemByteLen(1),
   34     itemGroupSize(1),
   35     rowSizeBytes(16),
   36     columnMode(ColumnMode::PowerOf2),
   37     itemFormat(ItemFormatHex),
   38     itemBigEndian(false),
   39     addrCharLen(AddrWidth64),
   40     showHeader(true),
   41     showAscii(true),
   42     showExHex(true),
   43     showExAddr(true)
   44 {
   45     setMouseTracking(true);
   46     setFocusPolicy(Qt::FocusPolicy::StrongFocus);
   47     connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, [this]() { viewport()->update(); });
   48 
   49     connect(Config(), &Configuration::colorsUpdated, this, &HexWidget::updateColors);
   50     connect(Config(), &Configuration::fontsUpdated, this, [this]() { setMonospaceFont(
   51         Config()->getFont()); });
   52 
   53     auto sizeActionGroup = new QActionGroup(this);
   54     for (int i = 1; i <= 8; i *= 2) {
   55         QAction *action = new QAction(QString::number(i), this);
   56         action->setCheckable(true);
   57         action->setActionGroup(sizeActionGroup);
   58         connect(action, &QAction::triggered, this, [=]() { setItemSize(i); });
   59         actionsItemSize.append(action);
   60     }
   61     actionsItemSize.at(0)->setChecked(true);
   62 
   63     /* Follow the order in ItemFormat enum */
   64     QStringList names;
   65     names << tr("Hexadecimal");
   66     names << tr("Octal");
   67     names << tr("Decimal");
   68     names << tr("Signed decimal");
   69     names << tr("Float");
   70 
   71     auto formatActionGroup = new QActionGroup(this);
   72     for (int i = 0; i < names.length(); ++i) {
   73         QAction *action = new QAction(names.at(i), this);
   74         action->setCheckable(true);
   75         action->setActionGroup(formatActionGroup);
   76         connect(action, &QAction::triggered, this, [=]() { setItemFormat(static_cast<ItemFormat>(i)); });
   77         actionsItemFormat.append(action);
   78     }
   79     actionsItemFormat.at(0)->setChecked(true);
   80     actionsItemFormat.at(ItemFormatFloat)->setEnabled(false);
   81 
   82     rowSizeMenu = new QMenu(tr("Bytes per row"), this);
   83     auto columnsActionGroup = new QActionGroup(this);
   84     for (int i = 1; i <= MAX_LINE_WIDTH_PRESET; i *= 2) {
   85         QAction *action = new QAction(QString::number(i), rowSizeMenu);
   86         action->setCheckable(true);
   87         action->setActionGroup(columnsActionGroup);
   88         connect(action, &QAction::triggered, this, [=]() { setFixedLineSize(i); });
   89         rowSizeMenu->addAction(action);
   90     }
   91     rowSizeMenu->addSeparator();
   92     actionRowSizePowerOf2 = new QAction(tr("Power of 2"), this);
   93     actionRowSizePowerOf2->setCheckable(true);
   94     actionRowSizePowerOf2->setActionGroup(columnsActionGroup);
   95     connect(actionRowSizePowerOf2, &QAction::triggered, this, [=]() { setColumnMode(ColumnMode::PowerOf2); });
   96     rowSizeMenu->addAction(actionRowSizePowerOf2);
   97 
   98     actionItemBigEndian = new QAction(tr("Big Endian"), this);
   99     actionItemBigEndian->setCheckable(true);
  100     actionItemBigEndian->setEnabled(false);
  101     connect(actionItemBigEndian, &QAction::triggered, this, &HexWidget::setItemEndianess);
  102 
  103     actionHexPairs = new QAction(tr("Bytes as pairs"), this);
  104     actionHexPairs->setCheckable(true);
  105     connect(actionHexPairs, &QAction::triggered, this, &HexWidget::onHexPairsModeEnabled);
  106 
  107     actionCopy = new QAction(tr("Copy"), this);
  108     addAction(actionCopy);
  109     actionCopy->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
  110     actionCopy->setShortcut(QKeySequence::Copy);
  111     connect(actionCopy, &QAction::triggered, this, &HexWidget::copy);
  112 
  113     actionCopyAddress = new QAction(tr("Copy address"), this);
  114     actionCopyAddress->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
  115     actionCopyAddress->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
  116     connect(actionCopyAddress, &QAction::triggered, this, &HexWidget::copyAddress);
  117     addAction(actionCopyAddress);
  118 
  119     actionSelectRange = new QAction(tr("Select range"), this);
  120     connect(actionSelectRange, &QAction::triggered, this, [this]() { rangeDialog.open(cursor.address); });
  121     addAction(actionSelectRange);
  122     connect(&rangeDialog, &QDialog::accepted, this, &HexWidget::onRangeDialogAccepted);
  123 
  124     actionsWriteString.reserve(5);
  125     QAction* actionWriteString = new QAction(tr("Write string"), this);
  126     connect(actionWriteString, &QAction::triggered, this, &HexWidget::w_writeString);
  127     actionsWriteString.append(actionWriteString);
  128 
  129     QAction* actionWriteLenString = new QAction(tr("Write length and string"), this);
  130     connect(actionWriteLenString, &QAction::triggered, this, &HexWidget::w_writePascalString);
  131     actionsWriteString.append(actionWriteLenString);
  132 
  133     QAction* actionWriteWideString = new QAction(tr("Write wide string"), this);
  134     connect(actionWriteWideString, &QAction::triggered, this, &HexWidget::w_writeWideString);
  135     actionsWriteString.append(actionWriteWideString);
  136 
  137     QAction* actionWriteCString = new QAction(tr("Write zero terminated string"), this);
  138     connect(actionWriteCString, &QAction::triggered, this, &HexWidget::w_writeCString);
  139     actionsWriteString.append(actionWriteCString);
  140 
  141     QAction* actionWrite64 = new QAction(tr("Write De\\Encoded Base64 string"), this);
  142     connect(actionWrite64, &QAction::triggered, this, &HexWidget::w_write64);
  143     actionsWriteString.append(actionWrite64);
  144 
  145     actionsWriteOther.reserve(4);
  146     QAction* actionWriteZeros = new QAction(tr("Write zeros"), this);
  147     connect(actionWriteZeros, &QAction::triggered, this, &HexWidget::w_writeZeros);
  148     actionsWriteOther.append(actionWriteZeros);
  149 
  150     QAction* actionWriteRandom = new QAction(tr("Write random bytes"), this);
  151     connect(actionWriteRandom, &QAction::triggered, this, &HexWidget::w_writeRandom);
  152     actionsWriteOther.append(actionWriteRandom);
  153 
  154     QAction* actionDuplicateFromOffset = new QAction(tr("Duplicate from offset"), this);
  155     connect(actionDuplicateFromOffset, &QAction::triggered, this, &HexWidget::w_duplFromOffset);
  156     actionsWriteOther.append(actionDuplicateFromOffset);
  157 
  158     QAction* actionIncDec = new QAction(tr("Increment/Decrement"), this);
  159     connect(actionIncDec, &QAction::triggered, this, &HexWidget::w_increaseDecrease);
  160     actionsWriteOther.append(actionIncDec);
  161 
  162     connect(this, &HexWidget::selectionChanged, this, [this](Selection selection) {
  163         actionCopy->setEnabled(!selection.empty);
  164     });
  165 
  166     updateMetrics();
  167     updateItemLength();
  168 
  169     startAddress = 0ULL;
  170     cursor.address = 0ULL;
  171     data.reset(new MemoryData());
  172     oldData.reset(new MemoryData());
  173 
  174     fetchData();
  175     updateCursorMeta();
  176 
  177     connect(&cursor.blinkTimer, &QTimer::timeout, this, &HexWidget::onCursorBlinked);
  178     cursor.setBlinkPeriod(1000);
  179     cursor.startBlinking();
  180 
  181     updateColors();
  182 }
  183 
  184 HexWidget::~HexWidget()
  185 {
  186 
  187 }
  188 
  189 void HexWidget::setMonospaceFont(const QFont &font)
  190 {
  191     if (!(font.styleHint() & QFont::Monospace)) {
  192         /* FIXME: Use default monospace font
  193         setFont(XXX); */
  194     }
  195     QScrollArea::setFont(font);
  196     monospaceFont = font.resolve(this->font());
  197     updateMetrics();
  198     fetchData();
  199     updateCursorMeta();
  200 
  201     viewport()->update();
  202 }
  203 
  204 void HexWidget::setItemSize(int nbytes)
  205 {
  206     static const QVector<int> values({1, 2, 4, 8});
  207 
  208     if (!values.contains(nbytes))
  209         return;
  210 
  211     itemByteLen = nbytes;
  212     if (itemByteLen > rowSizeBytes) {
  213         rowSizeBytes = itemByteLen;
  214     }
  215 
  216     actionsItemFormat.at(ItemFormatFloat)->setEnabled(nbytes >= 4);
  217     actionItemBigEndian->setEnabled(nbytes != 1);
  218 
  219     updateItemLength();
  220     fetchData();
  221     updateCursorMeta();
  222 
  223     viewport()->update();
  224 }
  225 
  226 void HexWidget::setItemFormat(ItemFormat format)
  227 {
  228     itemFormat = format;
  229 
  230     bool sizeEnabled = true;
  231     if (format == ItemFormatFloat)
  232         sizeEnabled = false;
  233     actionsItemSize.at(0)->setEnabled(sizeEnabled);
  234     actionsItemSize.at(1)->setEnabled(sizeEnabled);
  235 
  236 
  237     updateItemLength();
  238     fetchData();
  239     updateCursorMeta();
  240 
  241     viewport()->update();
  242 }
  243 
  244 void HexWidget::setItemGroupSize(int size)
  245 {
  246     itemGroupSize = size;
  247 
  248     updateCounts();
  249     fetchData();
  250     updateCursorMeta();
  251 
  252     viewport()->update();
  253 }
  254 
  255 /**
  256  * @brief Checks if Item at the address changed compared to the last read data.
  257  * @param address Address of Item to be compared.
  258  * @return True if Item is different, False if Item is equal or last read didn't contain the address.
  259  * @see HexWidget#readItem
  260  *
  261  * Checks if current Item at the address changed compared to the last read data.
  262  * It is assumed that the current read data buffer contains the address.
  263  */
  264 bool HexWidget::isItemDifferentAt(uint64_t address) {
  265     char oldItem[sizeof(uint64_t)] = {};
  266     char newItem[sizeof(uint64_t)] = {};
  267     if (data->copy(newItem, address, static_cast<size_t>(itemByteLen)) &&
  268         oldData->copy(oldItem, address, static_cast<size_t>(itemByteLen))) {
  269         return memcmp(oldItem, newItem, sizeof(oldItem)) != 0;
  270     }
  271     return false;
  272 }
  273 
  274 void HexWidget::updateCounts()
  275 {
  276     actionHexPairs->setEnabled(rowSizeBytes > 1 && itemByteLen == 1
  277                                && itemFormat == ItemFormat::ItemFormatHex);
  278     actionHexPairs->setChecked(Core()->getConfigb("hex.pairs"));
  279     if (actionHexPairs->isChecked() && actionHexPairs->isEnabled()) {
  280         itemGroupSize = 2;
  281     } else {
  282         itemGroupSize = 1;
  283     }
  284 
  285     if (columnMode == ColumnMode::PowerOf2) {
  286         int last_good_size = itemGroupByteLen();
  287         for (int i = itemGroupByteLen(); i <= MAX_LINE_WIDTH_BYTES; i *= 2) {
  288             rowSizeBytes = i;
  289             itemColumns = rowSizeBytes / itemGroupByteLen();
  290             updateAreasPosition();
  291             if (horizontalScrollBar()->maximum() == 0) {
  292                 last_good_size = rowSizeBytes;
  293             } else {
  294                 break;
  295             }
  296         }
  297         rowSizeBytes = last_good_size;
  298     }
  299 
  300     itemColumns = rowSizeBytes / itemGroupByteLen();
  301 
  302     // ensure correct action is selected when changing line size programmatically
  303     if (columnMode == ColumnMode::Fixed) {
  304         int w = 1;
  305         const auto &actions = rowSizeMenu->actions();
  306         for (auto action : actions) {
  307             action->setChecked(false);
  308         }
  309         for (auto action : actions) {
  310             if (w > MAX_LINE_WIDTH_PRESET) {
  311                 break;
  312             }
  313             if (rowSizeBytes == w) {
  314                 action->setChecked(true);
  315             }
  316             w *= 2;
  317         }
  318     } else if (columnMode == ColumnMode::PowerOf2) {
  319         actionRowSizePowerOf2->setChecked(true);
  320     }
  321 
  322     updateAreasPosition();
  323 }
  324 
  325 void HexWidget::setFixedLineSize(int lineSize)
  326 {
  327     if (lineSize < 1 || lineSize < itemGroupByteLen() || lineSize % itemGroupByteLen()) {
  328         updateCounts();
  329         return;
  330     }
  331     rowSizeBytes = lineSize;
  332     columnMode = ColumnMode::Fixed;
  333 
  334     updateCounts();
  335     fetchData();
  336     updateCursorMeta();
  337 
  338     viewport()->update();
  339 }
  340 
  341 void HexWidget::setColumnMode(ColumnMode mode)
  342 {
  343     columnMode = mode;
  344 
  345     updateCounts();
  346     fetchData();
  347     updateCursorMeta();
  348 
  349     viewport()->update();
  350 }
  351 
  352 void HexWidget::selectRange(RVA start, RVA end)
  353 {
  354     BasicCursor endCursor(end);
  355     endCursor += 1;
  356     setCursorAddr(endCursor);
  357     selection.set(start, end);
  358     cursorEnabled = false;
  359     emit selectionChanged(getSelection());
  360 }
  361 
  362 void HexWidget::clearSelection()
  363 {
  364     setCursorAddr(cursor.address, false);
  365     emit selectionChanged(getSelection());
  366 }
  367 
  368 HexWidget::Selection HexWidget::getSelection()
  369 {
  370     return Selection{selection.isEmpty(), selection.start(), selection.end()};
  371 }
  372 
  373 void HexWidget::seek(uint64_t address)
  374 {
  375     setCursorAddr(address);
  376 }
  377 
  378 void HexWidget::refresh()
  379 {
  380     fetchData();
  381     viewport()->update();
  382 }
  383 
  384 void HexWidget::setItemEndianess(bool bigEndian)
  385 {
  386     itemBigEndian = bigEndian;
  387 
  388     updateCursorMeta(); // Update cached item character
  389 
  390     viewport()->update();
  391 }
  392 
  393 void HexWidget::updateColors()
  394 {
  395     borderColor = Config()->getColor("gui.border");
  396     backgroundColor = Config()->getColor("gui.background");
  397     b0x00Color = Config()->getColor("b0x00");
  398     b0x7fColor = Config()->getColor("b0x7f");
  399     b0xffColor = Config()->getColor("b0xff");
  400     printableColor = Config()->getColor("ai.write");
  401     defColor = Config()->getColor("btext");
  402     addrColor = Config()->getColor("func_var_addr");
  403     diffColor = Config()->getColor("graph.diff.unmatch");
  404 
  405     updateCursorMeta();
  406     viewport()->update();
  407 }
  408 
  409 void HexWidget::paintEvent(QPaintEvent *event)
  410 {
  411     QPainter painter(viewport());
  412     painter.setFont(monospaceFont);
  413 
  414     int xOffset = horizontalScrollBar()->value();
  415     if (xOffset > 0)
  416         painter.translate(QPoint(-xOffset, 0));
  417 
  418     if (event->rect() == cursor.screenPos) {
  419         /* Cursor blink */
  420         drawCursor(painter);
  421         return;
  422     }
  423 
  424     painter.fillRect(event->rect().translated(xOffset, 0), backgroundColor);
  425 
  426     drawHeader(painter);
  427 
  428     drawAddrArea(painter);
  429     drawItemArea(painter);
  430     drawAsciiArea(painter);
  431 
  432     if (!cursorEnabled)
  433         return;
  434 
  435     drawCursor(painter, true);
  436 }
  437 
  438 void HexWidget::updateWidth()
  439 {
  440     int max = (showAscii ? asciiArea.right() : itemArea.right()) - viewport()->width();
  441     if (max < 0)
  442         max = 0;
  443     else
  444         max += charWidth;
  445     horizontalScrollBar()->setMaximum(max);
  446     horizontalScrollBar()->setSingleStep(charWidth);
  447 }
  448 
  449 void HexWidget::resizeEvent(QResizeEvent *event)
  450 {
  451     int oldByteCount = bytesPerScreen();
  452     updateCounts();
  453 
  454     if (event->oldSize().height() == event->size().height() && oldByteCount == bytesPerScreen())
  455         return;
  456 
  457     updateAreasHeight();
  458     fetchData(); // rowCount was changed
  459     updateCursorMeta();
  460 
  461     viewport()->update();
  462 }
  463 
  464 void HexWidget::mouseMoveEvent(QMouseEvent *event)
  465 {
  466     QPoint pos = event->pos();
  467     pos.rx() += horizontalScrollBar()->value();
  468 
  469     auto mouseAddr = mousePosToAddr(pos).address;
  470 
  471     QString metaData = getFlagsAndComment(mouseAddr);
  472     if (!metaData.isEmpty() && itemArea.contains(pos)) {
  473         QToolTip::showText(event->globalPos(), metaData.replace(",", ", "), this);
  474     } else {
  475         QToolTip::hideText();
  476     }
  477 
  478     if (!updatingSelection) {
  479         if (itemArea.contains(pos) || asciiArea.contains(pos))
  480             setCursor(Qt::IBeamCursor);
  481         else
  482             setCursor(Qt::ArrowCursor);
  483         return;
  484     }
  485 
  486     auto &area = currentArea();
  487     if (pos.x() < area.left())
  488         pos.setX(area.left());
  489     else if (pos.x() > area.right())
  490         pos.setX(area.right());
  491     auto addr = currentAreaPosToAddr(pos, true);
  492     setCursorAddr(addr, true);
  493 
  494     /* Stop blinking */
  495     cursorEnabled = false;
  496 
  497     viewport()->update();
  498 }
  499 
  500 void HexWidget::mousePressEvent(QMouseEvent *event)
  501 {
  502     QPoint pos(event->pos());
  503     pos.rx() += horizontalScrollBar()->value();
  504 
  505     if (event->button() == Qt::LeftButton) {
  506         bool selectingData = itemArea.contains(pos);
  507         bool selecting = selectingData || asciiArea.contains(pos);
  508         if (selecting) {
  509             updatingSelection = true;
  510             setCursorOnAscii(!selectingData);
  511             auto cursorPosition = currentAreaPosToAddr(pos, true);
  512             setCursorAddr(cursorPosition, event->modifiers() == Qt::ShiftModifier);
  513             viewport()->update();
  514         }
  515     }
  516 }
  517 
  518 void HexWidget::mouseReleaseEvent(QMouseEvent *event)
  519 {
  520     if (event->button() == Qt::LeftButton) {
  521         if (selection.isEmpty()) {
  522             selection.init(cursor.address);
  523             cursorEnabled = true;
  524         }
  525         updatingSelection = false;
  526     }
  527 }
  528 
  529 void HexWidget::wheelEvent(QWheelEvent *event)
  530 {
  531     int dy = event->delta();
  532     int64_t delta = 3 * itemRowByteLen();
  533     if (dy > 0)
  534         delta = -delta;
  535 
  536     if (dy == 0)
  537         return;
  538 
  539     if (delta < 0 && startAddress < static_cast<uint64_t>(-delta)) {
  540         startAddress = 0;
  541     } else if (delta > 0 && data->maxIndex() < static_cast<uint64_t>(bytesPerScreen())) {
  542         startAddress = 0;
  543     } else {
  544         startAddress += delta;
  545     }
  546     fetchData();
  547     if ((data->maxIndex() - startAddress) <= static_cast<uint64_t>(bytesPerScreen() + delta - 1)) {
  548         startAddress = (data->maxIndex() - bytesPerScreen()) + 1;
  549     }
  550     if (cursor.address >= startAddress && cursor.address <= lastVisibleAddr()) {
  551         /* Don't enable cursor blinking if selection isn't empty */
  552         cursorEnabled = selection.isEmpty();
  553         updateCursorMeta();
  554     } else {
  555         cursorEnabled = false;
  556     }
  557     viewport()->update();
  558 }
  559 
  560 void HexWidget::keyPressEvent(QKeyEvent *event)
  561 {
  562     bool select = false;
  563     auto moveOrSelect = [event, &select](QKeySequence::StandardKey moveSeq, QKeySequence::StandardKey selectSeq) ->bool {
  564         if (event->matches(moveSeq)) {
  565             select = false;
  566             return true;
  567         } else if (event->matches(selectSeq)) {
  568             select = true;
  569             return true;
  570         }
  571         return false;
  572     };
  573     if (moveOrSelect(QKeySequence::MoveToNextLine, QKeySequence::SelectNextLine)) {
  574         moveCursor(itemRowByteLen(), select);
  575     } else if (moveOrSelect(QKeySequence::MoveToPreviousLine, QKeySequence::SelectPreviousLine)) {
  576         moveCursor(-itemRowByteLen(), select);
  577     } else if (moveOrSelect(QKeySequence::MoveToNextChar, QKeySequence::SelectNextChar)) {
  578         moveCursor(cursorOnAscii ? 1 : itemByteLen, select);
  579     } else if (moveOrSelect(QKeySequence::MoveToPreviousChar, QKeySequence::SelectPreviousChar)) {
  580         moveCursor(cursorOnAscii ? -1 : -itemByteLen, select);
  581     } else if (moveOrSelect(QKeySequence::MoveToNextPage, QKeySequence::SelectNextPage)) {
  582         moveCursor(bytesPerScreen(), select);
  583     } else if (moveOrSelect(QKeySequence::MoveToPreviousPage, QKeySequence::SelectPreviousPage)) {
  584         moveCursor(-bytesPerScreen(), select);
  585     } else if (moveOrSelect(QKeySequence::MoveToStartOfLine, QKeySequence::SelectStartOfLine)) {
  586         int linePos = int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));
  587         moveCursor(-linePos, select);
  588     } else if (moveOrSelect(QKeySequence::MoveToEndOfLine, QKeySequence::SelectEndOfLine)) {
  589         int linePos = int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));
  590         moveCursor(itemRowByteLen() - linePos, select);
  591     }
  592     //viewport()->update();
  593 }
  594 
  595 void HexWidget::contextMenuEvent(QContextMenuEvent *event)
  596 {
  597     QPoint pt = event->pos();
  598     bool mouseOutsideSelection = false;
  599     if (event->reason() == QContextMenuEvent::Mouse) {
  600         auto mouseAddr = mousePosToAddr(pt).address;
  601         if (asciiArea.contains(pt)) {
  602             cursorOnAscii = true;
  603         } else if (itemArea.contains(pt)) {
  604             cursorOnAscii = false;
  605         }
  606         if (selection.isEmpty()) {
  607             seek(mouseAddr);
  608         } else {
  609             mouseOutsideSelection = !selection.contains(mouseAddr);
  610         }
  611     }
  612 
  613     auto disableOutsideSelectionActions = [this](bool disable) {
  614         actionCopyAddress->setDisabled(disable);
  615     };
  616 
  617     QMenu *menu = new QMenu();
  618     QMenu *sizeMenu = menu->addMenu(tr("Item size:"));
  619     sizeMenu->addActions(actionsItemSize);
  620     QMenu *formatMenu = menu->addMenu(tr("Item format:"));
  621     formatMenu->addActions(actionsItemFormat);
  622     menu->addMenu(rowSizeMenu);
  623     menu->addAction(actionHexPairs);
  624     menu->addAction(actionItemBigEndian);
  625     QMenu *writeMenu = menu->addMenu(tr("Edit"));
  626     writeMenu->addActions(actionsWriteString);
  627     writeMenu->addSeparator();
  628     writeMenu->addActions(actionsWriteOther);
  629     menu->addSeparator();
  630     menu->addAction(actionCopy);
  631     disableOutsideSelectionActions(mouseOutsideSelection);
  632     menu->addAction(actionCopyAddress);
  633     menu->addActions(this->actions());
  634     menu->exec(mapToGlobal(pt));
  635     disableOutsideSelectionActions(false);
  636     menu->deleteLater();
  637 }
  638 
  639 void HexWidget::onCursorBlinked()
  640 {
  641     if (!cursorEnabled)
  642         return;
  643     cursor.blink();
  644     QRect cursorRect(cursor.screenPos.x(), cursor.screenPos.y(), cursor.screenPos.width(), cursor.screenPos.height());
  645     viewport()->update(cursorRect.translated(-horizontalScrollBar()->value(), 0));
  646 }
  647 
  648 void HexWidget::onHexPairsModeEnabled(bool enable)
  649 {
  650     // Sync configuration
  651     Core()->setConfig("hex.pairs", enable);
  652     if (enable) {
  653         setItemGroupSize(2);
  654     } else {
  655         setItemGroupSize(1);
  656     }
  657 }
  658 
  659 void HexWidget::copy()
  660 {
  661     if (selection.isEmpty() || selection.size() > MAX_COPY_SIZE)
  662         return;
  663 
  664     QClipboard *clipboard = QApplication::clipboard();
  665     if (cursorOnAscii) {
  666         clipboard->setText(Core()->cmdRawAt(QString("psx %1")
  667                                     .arg(selection.size()),
  668                                     selection.start()).trimmed());
  669     } else {
  670         clipboard->setText(Core()->cmdRawAt(QString("p8 %1")
  671                                     .arg(selection.size()),
  672                                     selection.start()).trimmed()); //TODO: copy in the format shown
  673     }
  674 }
  675 
  676 void HexWidget::copyAddress()
  677 {
  678     uint64_t addr = cursor.address;
  679     if (!selection.isEmpty()) {
  680         addr = selection.start();
  681     }
  682     QClipboard *clipboard = QApplication::clipboard();
  683     clipboard->setText(RAddressString(addr));
  684 }
  685 
  686 void HexWidget::onRangeDialogAccepted()
  687 {
  688     if (rangeDialog.empty()) {
  689         seek(rangeDialog.getStartAddress());
  690         return;
  691     }
  692     selectRange(rangeDialog.getStartAddress(), rangeDialog.getEndAddress());
  693 }
  694 
  695 void HexWidget::w_writeString()
  696 {
  697     if (!ioModesController.prepareForWriting()) {
  698         return;
  699     }
  700     bool ok = false;
  701     QInputDialog d;
  702     d.setInputMode(QInputDialog::InputMode::TextInput);
  703     QString str = d.getText(this, tr("Write string"),
  704                             tr("String:"), QLineEdit::Normal, "", &ok);
  705     if (ok && !str.isEmpty()) {
  706         Core()->cmdRawAt(QString("w %1")
  707                             .arg(str),
  708                             getLocationAddress());
  709         refresh();
  710     }
  711 }
  712 
  713 void HexWidget::w_increaseDecrease()
  714 {
  715     if (!ioModesController.prepareForWriting()) {
  716         return;
  717     }
  718     IncrementDecrementDialog d;
  719     int ret = d.exec();
  720     if (ret == QDialog::Rejected) {
  721         return;
  722     }
  723     QString mode = d.getMode() == IncrementDecrementDialog::Increase ? "+" : "-";
  724     Core()->cmdRawAt(QString("w%1%2 %3")
  725                         .arg(QString::number(d.getNBytes()))
  726                         .arg(mode)
  727                         .arg(QString::number(d.getValue())),
  728                         getLocationAddress());
  729     refresh();
  730 }
  731 
  732 void HexWidget::w_writeZeros()
  733 {
  734     if (!ioModesController.prepareForWriting()) {
  735         return;
  736     }
  737     bool ok = false;
  738     QInputDialog d;
  739 
  740     int size = 1;
  741     if (!selection.isEmpty() && selection.size() <= INT_MAX) {
  742         size = static_cast<int>(selection.size());
  743     }
  744 
  745     QString str = QString::number(d.getInt(this, tr("Write zeros"),
  746                                            tr("Number of zeros:"), size, 1, 0x7FFFFFFF, 1, &ok));
  747     if (ok && !str.isEmpty()) {
  748         Core()->cmdRawAt(QString("w0 %1")
  749                             .arg(str),
  750                             getLocationAddress());
  751         refresh();
  752     }
  753 }
  754 
  755 void HexWidget::w_write64()
  756 {
  757     if (!ioModesController.prepareForWriting()) {
  758         return;
  759     }
  760     Base64EnDecodedWriteDialog d;
  761     int ret = d.exec();
  762     if (ret == QDialog::Rejected) {
  763         return;
  764     }
  765     QString mode = d.getMode() == Base64EnDecodedWriteDialog::Encode ? "e" : "d";
  766     QByteArray str = d.getData();
  767 
  768     if (mode == "d" && (QString(str).contains(QRegularExpression("[^a-zA-Z0-9+/=]")) ||
  769         str.length() % 4 != 0 || str.isEmpty())) {
  770         QMessageBox::critical(this, tr("Error"),
  771                               tr("Error occured during decoding your input.\n"
  772                                  "Please, make sure, that it is a valid base64 string and try again."));
  773         return;
  774     }
  775 
  776     Core()->cmdRawAt(QString("w6%1 %2")
  777                         .arg(mode)
  778                         .arg((mode == "e" ? str.toHex() : str).toStdString().c_str()),
  779                         getLocationAddress());
  780     refresh();
  781 }
  782 
  783 void HexWidget::w_writeRandom()
  784 {
  785     if (!ioModesController.prepareForWriting()) {
  786         return;
  787     }
  788     bool ok = false;
  789     QInputDialog d;
  790 
  791     int size = 1;
  792     if (!selection.isEmpty() && selection.size() <= INT_MAX) {
  793         size = static_cast<int>(selection.size());
  794     }
  795     QString nbytes = QString::number(d.getInt(this, tr("Write random"),
  796                                            tr("Number of bytes:"), size, 1, 0x7FFFFFFF, 1, &ok));
  797     if (ok && !nbytes.isEmpty()) {
  798         Core()->cmdRawAt(QString("wr %1")
  799                             .arg(nbytes),
  800                             getLocationAddress());
  801         refresh();
  802     }
  803 }
  804 
  805 void HexWidget::w_duplFromOffset()
  806 {
  807     if (!ioModesController.prepareForWriting()) {
  808         return;
  809     }
  810     DuplicateFromOffsetDialog d;
  811     int ret = d.exec();
  812     if (ret == QDialog::Rejected) {
  813         return;
  814     }
  815     RVA copyFrom = d.getOffset();
  816     QString nBytes = QString::number(d.getNBytes());
  817     Core()->cmdRawAt(QString("wd %1 %2")
  818                             .arg(copyFrom)
  819                             .arg(nBytes),
  820                             getLocationAddress());
  821     refresh();
  822 }
  823 
  824 void HexWidget::w_writePascalString()
  825 {
  826     if (!ioModesController.prepareForWriting()) {
  827         return;
  828     }
  829     bool ok = false;
  830     QInputDialog d;
  831     d.setInputMode(QInputDialog::InputMode::TextInput);
  832     QString str = d.getText(this, tr("Write Pascal string"),
  833                             tr("String:"), QLineEdit::Normal, "", &ok);
  834     if (ok && !str.isEmpty()) {
  835         Core()->cmdRawAt(QString("ws %1")
  836                             .arg(str),
  837                             getLocationAddress());
  838         refresh();
  839     }
  840 }
  841 
  842 void HexWidget::w_writeWideString()
  843 {
  844     if (!ioModesController.prepareForWriting()) {
  845         return;
  846     }
  847     bool ok = false;
  848     QInputDialog d;
  849     d.setInputMode(QInputDialog::InputMode::TextInput);
  850     QString str = d.getText(this, tr("Write wide string"),
  851                             tr("String:"), QLineEdit::Normal, "", &ok);
  852     if (ok && !str.isEmpty()) {
  853         Core()->cmdRawAt(QString("ww %1")
  854                             .arg(str),
  855                             getLocationAddress());
  856         refresh();
  857     }
  858 }
  859 
  860 void HexWidget::w_writeCString()
  861 {
  862     if (!ioModesController.prepareForWriting()) {
  863         return;
  864     }
  865     bool ok = false;
  866     QInputDialog d;
  867     d.setInputMode(QInputDialog::InputMode::TextInput);
  868     QString str = d.getText(this, tr("Write zero-terminated string"),
  869                             tr("String:"), QLineEdit::Normal, "", &ok);
  870     if (ok && !str.isEmpty()) {
  871         Core()->cmdRawAt(QString("wz %1")
  872                             .arg(str),
  873                             getLocationAddress());
  874         refresh();
  875     }
  876 }
  877 
  878 void HexWidget::updateItemLength()
  879 {
  880     itemPrefixLen = 0;
  881 
  882     switch (itemFormat) {
  883     case ItemFormatHex:
  884         itemCharLen = 2 * itemByteLen;
  885         if (itemByteLen > 1 && showExHex)
  886             itemPrefixLen = hexPrefix.length();
  887         break;
  888     case ItemFormatOct:
  889         itemCharLen = (itemByteLen * 8 + 3) / 3;
  890         break;
  891     case ItemFormatDec:
  892         switch (itemByteLen) {
  893         case 1:
  894             itemCharLen = 3;
  895             break;
  896         case 2:
  897             itemCharLen = 5;
  898             break;
  899         case 4:
  900             itemCharLen = 10;
  901             break;
  902         case 8:
  903             itemCharLen = 20;
  904             break;
  905         }
  906         break;
  907     case ItemFormatSignedDec:
  908         switch (itemByteLen) {
  909         case 1:
  910             itemCharLen = 4;
  911             break;
  912         case 2:
  913             itemCharLen = 6;
  914             break;
  915         case 4:
  916             itemCharLen = 11;
  917             break;
  918         case 8:
  919             itemCharLen = 20;
  920             break;
  921         }
  922         break;
  923     case ItemFormatFloat:
  924         if (itemByteLen < 4)
  925             itemByteLen = 4;
  926         // FIXME
  927         itemCharLen = 3 * itemByteLen;
  928         break;
  929     }
  930 
  931     itemCharLen += itemPrefixLen;
  932 
  933     updateCounts();
  934 }
  935 
  936 void HexWidget::drawHeader(QPainter &painter)
  937 {
  938     if (!showHeader)
  939         return;
  940 
  941     int offset = 0;
  942     QRectF rect(itemArea.left(), 0, itemWidth(), lineHeight);
  943 
  944     painter.setPen(addrColor);
  945 
  946     for (int j = 0; j < itemColumns; ++j) {
  947         for (int k = 0; k < itemGroupSize; ++k, offset += itemByteLen) {
  948             painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, QString::number(offset, 16).toUpper());
  949             rect.translate(itemWidth(), 0);
  950         }
  951         rect.translate(columnSpacingWidth(), 0);
  952     }
  953 
  954     rect.moveLeft(asciiArea.left());
  955     rect.setWidth(charWidth);
  956     for (int j = 0; j < itemRowByteLen(); ++j) {
  957         painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight, QString::number(j % 16, 16).toUpper());
  958         rect.translate(charWidth, 0);
  959     }
  960 }
  961 
  962 void HexWidget::drawCursor(QPainter &painter, bool shadow)
  963 {
  964     if (shadow) {
  965         QPen pen(Qt::gray);
  966         pen.setStyle(Qt::DashLine);
  967         painter.setPen(pen);
  968         shadowCursor.screenPos.setWidth(cursorOnAscii ? itemWidth() : charWidth);
  969         painter.drawRect(shadowCursor.screenPos);
  970         painter.setPen(Qt::SolidLine);
  971     }
  972 
  973     painter.setPen(cursor.cachedColor);
  974     QRectF charRect(cursor.screenPos);
  975     charRect.setWidth(charWidth);
  976     painter.fillRect(charRect, backgroundColor);
  977     painter.drawText(charRect, Qt::AlignVCenter, cursor.cachedChar);
  978     if (cursor.isVisible) {
  979         painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
  980         painter.fillRect(cursor.screenPos, QColor(0xff, 0xff, 0xff));
  981     }
  982 }
  983 
  984 void HexWidget::drawAddrArea(QPainter &painter)
  985 {
  986     uint64_t offset = startAddress;
  987     QString addrString;
  988     QSizeF areaSize((addrCharLen + (showExAddr ? 2 : 0)) * charWidth, lineHeight);
  989     QRectF strRect(addrArea.topLeft(), areaSize);
  990 
  991     painter.setPen(addrColor);
  992     for (int line = 0;
  993             line < visibleLines && offset <= data->maxIndex();
  994             ++line, strRect.translate(0, lineHeight), offset += itemRowByteLen()) {
  995         addrString = QString("%1").arg(offset, addrCharLen, 16, QLatin1Char('0'));
  996         if (showExAddr)
  997             addrString.prepend(hexPrefix);
  998         painter.drawText(strRect, Qt::AlignVCenter, addrString);
  999     }
 1000 
 1001     painter.setPen(borderColor);
 1002 
 1003     qreal vLineOffset = itemArea.left() - charWidth;
 1004     painter.drawLine(QLineF(vLineOffset, 0, vLineOffset, viewport()->height()));
 1005 }
 1006 
 1007 void HexWidget::drawItemArea(QPainter &painter)
 1008 {
 1009     QRectF itemRect(itemArea.topLeft(), QSizeF(itemWidth(), lineHeight));
 1010     QColor itemColor;
 1011     QString itemString;
 1012 
 1013     fillSelectionBackground(painter);
 1014 
 1015     uint64_t itemAddr = startAddress;
 1016     for (int line = 0; line < visibleLines; ++line) {
 1017         itemRect.moveLeft(itemArea.left());
 1018         for (int j = 0; j < itemColumns; ++j) {
 1019             for (int k = 0; k < itemGroupSize && itemAddr <= data->maxIndex(); ++k, itemAddr += itemByteLen) {
 1020                 itemString = renderItem(itemAddr - startAddress, &itemColor);
 1021 
 1022                 if (!getFlagsAndComment(itemAddr).isEmpty()) {
 1023                     QColor markerColor(borderColor);
 1024                     markerColor.setAlphaF(0.5);
 1025                     const auto shape = rangePolygons(itemAddr, itemAddr, false)[0];
 1026                     painter.setPen(markerColor);
 1027                     painter.drawPolyline(shape);
 1028                 }
 1029                 if (selection.contains(itemAddr)  && !cursorOnAscii) {
 1030                     itemColor = palette().highlightedText().color();
 1031                 }
 1032                 if (isItemDifferentAt(itemAddr)) {
 1033                     itemColor.setRgb(diffColor.rgb());
 1034                 }
 1035                 painter.setPen(itemColor);
 1036                 painter.drawText(itemRect, Qt::AlignVCenter, itemString);
 1037                 itemRect.translate(itemWidth(), 0);
 1038                 if (cursor.address == itemAddr) {
 1039                     auto &itemCursor = cursorOnAscii ? shadowCursor : cursor;
 1040                     itemCursor.cachedChar = itemString.at(0);
 1041                     itemCursor.cachedColor = itemColor;
 1042                 }
 1043             }
 1044             itemRect.translate(columnSpacingWidth(), 0);
 1045         }
 1046         itemRect.translate(0, lineHeight);
 1047     }
 1048 
 1049     painter.setPen(borderColor);
 1050 
 1051     qreal vLineOffset = asciiArea.left() - charWidth;
 1052     painter.drawLine(QLineF(vLineOffset, 0, vLineOffset, viewport()->height()));
 1053 }
 1054 
 1055 void HexWidget::drawAsciiArea(QPainter &painter)
 1056 {
 1057     QRectF charRect(asciiArea.topLeft(), QSizeF(charWidth, lineHeight));
 1058 
 1059     fillSelectionBackground(painter, true);
 1060 
 1061     uint64_t address = startAddress;
 1062     QChar ascii;
 1063     QColor color;
 1064     for (int line = 0; line < visibleLines; ++line, charRect.translate(0, lineHeight)) {
 1065         charRect.moveLeft(asciiArea.left());
 1066         for (int j = 0; j < itemRowByteLen() && address <= data->maxIndex(); ++j, ++address) {
 1067             ascii = renderAscii(address - startAddress, &color);
 1068             if (selection.contains(address) && cursorOnAscii) {
 1069                 color = palette().highlightedText().color();
 1070             }
 1071             if (isItemDifferentAt(address)) {
 1072                 color.setRgb(diffColor.rgb());
 1073             }
 1074             painter.setPen(color);
 1075             /* Dots look ugly. Use fillRect() instead of drawText(). */
 1076             if (ascii == '.') {
 1077                 qreal a = cursor.screenPos.width();
 1078                 QPointF p = charRect.bottomLeft();
 1079                 p.rx() += (charWidth - a) / 2 + 1;
 1080                 p.ry() += - 2 * a;
 1081                 painter.fillRect(QRectF(p, QSizeF(a, a)), color);
 1082             } else {
 1083                 painter.drawText(charRect, Qt::AlignVCenter, ascii);
 1084             }
 1085             charRect.translate(charWidth, 0);
 1086             if (cursor.address == address) {
 1087                 auto &itemCursor = cursorOnAscii ? cursor : shadowCursor;
 1088                 itemCursor.cachedChar = ascii;
 1089                 itemCursor.cachedColor = color;
 1090             }
 1091         }
 1092     }
 1093 }
 1094 
 1095 void HexWidget::fillSelectionBackground(QPainter &painter, bool ascii)
 1096 {
 1097     if (selection.isEmpty()) {
 1098         return;
 1099     }
 1100     const auto parts = rangePolygons(selection.start(), selection.end(), ascii);
 1101     for (const auto &shape : parts) {
 1102         QColor highlightColor = palette().color(QPalette::Highlight);
 1103         if (ascii == cursorOnAscii) {
 1104             painter.setBrush(highlightColor);
 1105             painter.drawPolygon(shape);
 1106         } else {
 1107             painter.setPen(highlightColor);
 1108             painter.drawPolyline(shape);
 1109         }
 1110     }
 1111 }
 1112 
 1113 QVector<QPolygonF> HexWidget::rangePolygons(RVA start, RVA last, bool ascii)
 1114 {
 1115     if (last < startAddress || start > lastVisibleAddr()) {
 1116         return {};
 1117     }
 1118 
 1119     QRectF rect;
 1120     const QRectF area = QRectF(ascii ? asciiArea : itemArea);
 1121 
 1122     /* Convert absolute values to relative */
 1123     int startOffset = std::max(uint64_t(start), startAddress) - startAddress;
 1124     int endOffset = std::min(uint64_t(last), lastVisibleAddr()) - startAddress;
 1125 
 1126     QVector<QPolygonF> parts;
 1127 
 1128     auto getRectangle = [&](int offset) {
 1129         return QRectF(ascii ? asciiRectangle(offset) : itemRectangle(offset));
 1130     };
 1131 
 1132     auto startRect = getRectangle(startOffset);
 1133     auto endRect = getRectangle(endOffset);
 1134     if (!ascii) {
 1135         if (int startFraction = startOffset % itemByteLen) {
 1136             startRect.setLeft(startRect.left() + startFraction * startRect.width() / itemByteLen);
 1137         }
 1138         if (int endFraction = itemByteLen - 1 - (endOffset % itemByteLen)) {
 1139             endRect.setRight(endRect.right() - endFraction * endRect.width() / itemByteLen);
 1140         }
 1141     }
 1142     if (endOffset - startOffset + 1 <= rowSizeBytes) {
 1143         if (startOffset / rowSizeBytes == endOffset / rowSizeBytes) { // single row
 1144             rect = startRect;
 1145             rect.setRight(endRect.right());
 1146             parts.push_back(QPolygonF(rect));
 1147         } else {
 1148             // two separate rectangles
 1149             rect = startRect;
 1150             rect.setRight(area.right());
 1151             parts.push_back(QPolygonF(rect));
 1152             rect = endRect;
 1153             rect.setLeft(area.left());
 1154             parts.push_back(QPolygonF(rect));
 1155         }
 1156     } else {
 1157         // single multiline shape
 1158         QPolygonF shape;
 1159         shape << startRect.topLeft();
 1160         rect = getRectangle(startOffset + rowSizeBytes - 1 - startOffset % rowSizeBytes);
 1161         shape << rect.topRight();
 1162         if (endOffset % rowSizeBytes != rowSizeBytes - 1) {
 1163             rect = getRectangle(endOffset - endOffset % rowSizeBytes - 1);
 1164             shape << rect.bottomRight() << endRect.topRight();
 1165         }
 1166         shape << endRect.bottomRight();
 1167         shape << getRectangle(endOffset - endOffset % rowSizeBytes).bottomLeft();
 1168         if (startOffset % rowSizeBytes) {
 1169             rect = getRectangle(startOffset - startOffset % rowSizeBytes + rowSizeBytes);
 1170             shape << rect.topLeft() << startRect.bottomLeft();
 1171         }
 1172         shape << shape.first(); // close the shape
 1173         parts.push_back(shape);
 1174     }
 1175     return parts;
 1176 }
 1177 
 1178 void HexWidget::updateMetrics()
 1179 {
 1180     QFontMetricsF fontMetrics(this->monospaceFont);
 1181     lineHeight = fontMetrics.height();
 1182     charWidth = fontMetrics.width(QLatin1Char('F'));
 1183 
 1184     updateCounts();
 1185     updateAreasHeight();
 1186 
 1187     qreal cursorWidth = std::max(charWidth / 3, 1.);
 1188     cursor.screenPos.setHeight(lineHeight);
 1189     shadowCursor.screenPos.setHeight(lineHeight);
 1190 
 1191     cursor.screenPos.setWidth(cursorWidth);
 1192     if (cursorOnAscii) {
 1193         cursor.screenPos.moveTopLeft(asciiArea.topLeft());
 1194 
 1195         shadowCursor.screenPos.setWidth(itemWidth());
 1196         shadowCursor.screenPos.moveTopLeft(itemArea.topLeft());
 1197     } else {
 1198         cursor.screenPos.moveTopLeft(itemArea.topLeft());
 1199         shadowCursor.screenPos.setWidth(charWidth);
 1200         shadowCursor.screenPos.moveTopLeft(asciiArea.topLeft());
 1201     }
 1202 }
 1203 
 1204 void HexWidget::updateAreasPosition()
 1205 {
 1206     const qreal spacingWidth = areaSpacingWidth();
 1207 
 1208     qreal yOffset = showHeader ? lineHeight : 0;
 1209 
 1210     addrArea.setTopLeft(QPointF(0, yOffset));
 1211     addrArea.setWidth((addrCharLen + (showExAddr ? 2 : 0)) * charWidth);
 1212 
 1213     itemArea.setTopLeft(QPointF(addrArea.right() + spacingWidth, yOffset));
 1214     itemArea.setWidth(itemRowWidth());
 1215 
 1216     asciiArea.setTopLeft(QPointF(itemArea.right() + spacingWidth, yOffset));
 1217     asciiArea.setWidth(asciiRowWidth());
 1218 
 1219     updateWidth();
 1220 }
 1221 
 1222 void HexWidget::updateAreasHeight()
 1223 {
 1224     visibleLines = static_cast<int>((viewport()->height() - itemArea.top()) / lineHeight);
 1225 
 1226     qreal height = visibleLines * lineHeight;
 1227     addrArea.setHeight(height);
 1228     itemArea.setHeight(height);
 1229     asciiArea.setHeight(height);
 1230 }
 1231 
 1232 void HexWidget::moveCursor(int offset, bool select)
 1233 {
 1234     BasicCursor addr = cursor.address;
 1235     addr += offset;
 1236     if (addr.address > data->maxIndex()) {
 1237         addr.address = data->maxIndex();
 1238     }
 1239     setCursorAddr(addr, select);
 1240 }
 1241 
 1242 void HexWidget::setCursorAddr(BasicCursor addr, bool select)
 1243 {
 1244     if (!select) {
 1245         bool clearingSelection = !selection.isEmpty();
 1246         selection.init(addr);
 1247         if (clearingSelection)
 1248             emit selectionChanged(getSelection());
 1249     }
 1250     emit positionChanged(addr.address);
 1251 
 1252     cursor.address = addr.address;
 1253 
 1254     /* Pause cursor repainting */
 1255     cursorEnabled = false;
 1256 
 1257     if (select) {
 1258         selection.update(addr);
 1259         emit selectionChanged(getSelection());
 1260     }
 1261 
 1262     uint64_t addressValue = cursor.address;
 1263     /* Update data cache if necessary */
 1264     if (!(addressValue >= startAddress && addressValue <= lastVisibleAddr())) {
 1265         /* Align start address */
 1266         addressValue -= (addressValue % itemRowByteLen());
 1267 
 1268         /* FIXME: handling Page Up/Down */
 1269         if (addressValue == startAddress + bytesPerScreen()) {
 1270             startAddress += itemRowByteLen();
 1271         } else {
 1272             startAddress = addressValue;
 1273         }
 1274 
 1275         fetchData();
 1276 
 1277         if (startAddress > (data->maxIndex() - bytesPerScreen()) + 1) {
 1278             startAddress = (data->maxIndex() - bytesPerScreen()) + 1;
 1279         }
 1280     }
 1281 
 1282     updateCursorMeta();
 1283 
 1284     /* Draw cursor */
 1285     cursor.isVisible = !select;
 1286     viewport()->update();
 1287 
 1288     /* Resume cursor repainting */
 1289     cursorEnabled = selection.isEmpty();
 1290 }
 1291 
 1292 void HexWidget::updateCursorMeta()
 1293 {
 1294     QPointF point;
 1295     QPointF pointAscii;
 1296 
 1297     int offset = cursor.address - startAddress;
 1298     int itemOffset = offset;
 1299     int asciiOffset;
 1300 
 1301     /* Calc common Y coordinate */
 1302     point.ry() = (itemOffset / itemRowByteLen()) * lineHeight;
 1303     pointAscii.setY(point.y());
 1304     itemOffset %= itemRowByteLen();
 1305     asciiOffset = itemOffset;
 1306 
 1307     /* Calc X coordinate on the item area */
 1308     point.rx() = (itemOffset / itemGroupByteLen()) * columnExWidth();
 1309     itemOffset %= itemGroupByteLen();
 1310     point.rx() += (itemOffset / itemByteLen) * itemWidth();
 1311 
 1312     /* Calc X coordinate on the ascii area */
 1313     pointAscii.rx() = asciiOffset * charWidth;
 1314 
 1315     point += itemArea.topLeft();
 1316     pointAscii += asciiArea.topLeft();
 1317 
 1318     cursor.screenPos.moveTopLeft(cursorOnAscii ? pointAscii : point);
 1319     shadowCursor.screenPos.moveTopLeft(cursorOnAscii ? point : pointAscii);
 1320 }
 1321 
 1322 void HexWidget::setCursorOnAscii(bool ascii)
 1323 {
 1324     cursorOnAscii = ascii;
 1325 }
 1326 
 1327 const QColor HexWidget::itemColor(uint8_t byte)
 1328 {
 1329     QColor color(defColor);
 1330 
 1331     if (byte == 0x00)
 1332         color = b0x00Color;
 1333     else if (byte == 0x7f)
 1334         color = b0x7fColor;
 1335     else if (byte == 0xff)
 1336         color = b0xffColor;
 1337     else if (IS_PRINTABLE(byte)) {
 1338         color = printableColor;
 1339     }
 1340 
 1341     return color;
 1342 }
 1343 
 1344 template<class T>
 1345 static T fromBigEndian(const void * src)
 1346 {
 1347 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
 1348     return qFromBigEndian<T>(src);
 1349 #else
 1350     T result;
 1351     memcpy(&result, src, sizeof(T));
 1352     return qFromBigEndian<T>(result);
 1353 #endif
 1354 }
 1355 
 1356 template<class T>
 1357 static T fromLittleEndian(const void * src)
 1358 {
 1359 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
 1360     return qFromLittleEndian<T>(src);
 1361 #else
 1362     T result;
 1363     memcpy(&result, src, sizeof(T));
 1364     return qFromLittleEndian<T>(result);
 1365 #endif
 1366 }
 1367 
 1368 QVariant HexWidget::readItem(int offset, QColor *color)
 1369 {
 1370     quint8 byte;
 1371     quint16 word;
 1372     quint32 dword;
 1373     quint64 qword;
 1374     float float32;
 1375     double float64;
 1376 
 1377     quint8 bytes[sizeof(uint64_t)];
 1378     data->copy(bytes, startAddress + offset, static_cast<size_t>(itemByteLen));
 1379     const bool signedItem = itemFormat == ItemFormatSignedDec;
 1380 
 1381     if (color) {
 1382         *color = defColor;
 1383     }
 1384 
 1385     switch (itemByteLen) {
 1386     case 1:
 1387         byte = bytes[0];
 1388         if (color)
 1389             *color = itemColor(byte);
 1390         if (!signedItem)
 1391             return QVariant(static_cast<quint64>(byte));
 1392         return QVariant(static_cast<qint64>(static_cast<qint8>(byte)));
 1393     case 2:
 1394         if (itemBigEndian)
 1395             word = fromBigEndian<quint16>(bytes);
 1396         else
 1397             word = fromLittleEndian<quint16>(bytes);
 1398 
 1399         if (!signedItem)
 1400             return QVariant(static_cast<quint64>(word));
 1401         return QVariant(static_cast<qint64>(static_cast<qint16>(word)));
 1402     case 4:
 1403         if (itemBigEndian)
 1404             dword = fromBigEndian<quint32>(bytes);
 1405         else
 1406             dword = fromLittleEndian<quint32>(bytes);
 1407 
 1408         if (itemFormat == ItemFormatFloat) {
 1409             memcpy(&float32, &dword, sizeof(float32));
 1410             return QVariant(float32);
 1411         }
 1412         if (!signedItem)
 1413             return QVariant(static_cast<quint64>(dword));
 1414         return QVariant(static_cast<qint64>(static_cast<qint32>(dword)));
 1415     case 8:
 1416         if (itemBigEndian)
 1417             qword = fromBigEndian<quint64>(bytes);
 1418         else
 1419             qword = fromLittleEndian<quint64>(bytes);
 1420         if (itemFormat == ItemFormatFloat) {
 1421             memcpy(&float64, &qword, sizeof(float64));
 1422             return  QVariant(float64);
 1423         }
 1424         if (!signedItem)
 1425             return  QVariant(qword);
 1426         return QVariant(static_cast<qint64>(qword));
 1427     }
 1428 
 1429     return QVariant();
 1430 }
 1431 
 1432 QString HexWidget::renderItem(int offset, QColor *color)
 1433 {
 1434     QString item;
 1435     QVariant itemVal = readItem(offset, color);
 1436     int itemLen = itemCharLen - itemPrefixLen; /* Reserve space for prefix */
 1437 
 1438     //FIXME: handle broken itemVal ( QVariant() )
 1439     switch (itemFormat) {
 1440     case ItemFormatHex:
 1441         item = QString("%1").arg(itemVal.toULongLong(), itemLen, 16, QLatin1Char('0'));
 1442         if (itemByteLen > 1 && showExHex)
 1443             item.prepend(hexPrefix);
 1444         break;
 1445     case ItemFormatOct:
 1446         item = QString("%1").arg(itemVal.toULongLong(), itemLen, 8, QLatin1Char('0'));
 1447         break;
 1448     case ItemFormatDec:
 1449         item = QString("%1").arg(itemVal.toULongLong(), itemLen, 10);
 1450         break;
 1451     case ItemFormatSignedDec:
 1452         item = QString("%1").arg(itemVal.toLongLong(), itemLen, 10);
 1453         break;
 1454     case ItemFormatFloat:
 1455         item = QString("%1").arg(itemVal.toDouble(), itemLen);
 1456         break;
 1457     }
 1458 
 1459     return item;
 1460 }
 1461 
 1462 QChar HexWidget::renderAscii(int offset, QColor *color)
 1463 {
 1464     uchar byte;
 1465     data->copy(&byte, startAddress + offset, sizeof(byte));
 1466     if (color) {
 1467         *color = itemColor(byte);
 1468     }
 1469     if (!IS_PRINTABLE(byte)) {
 1470         byte = '.';
 1471     }
 1472     return QChar(byte);
 1473 }
 1474 
 1475 /**
 1476  * @brief Gets the available flags and comment at a specific address.
 1477  * @param address Address of Item to be checked.
 1478  * @return String containing the flags and comment available at the address.
 1479  */
 1480 QString HexWidget::getFlagsAndComment(uint64_t address)
 1481 {
 1482     QString flagNames = Core()->listFlagsAsStringAt(address);
 1483     QString metaData = flagNames.isEmpty() ? "" : "Flags: " + flagNames.trimmed();
 1484 
 1485     QString comment = Core()->getCommentAt(address);
 1486     if (!comment.isEmpty()) {
 1487         if (!metaData.isEmpty()) {
 1488             metaData.append("\n");
 1489         }
 1490         metaData.append("Comment: " + comment.trimmed());
 1491     }
 1492 
 1493     return metaData;
 1494 }
 1495 
 1496 void HexWidget::fetchData()
 1497 {
 1498     data.swap(oldData);
 1499     data->fetch(startAddress, bytesPerScreen());
 1500 }
 1501 
 1502 BasicCursor HexWidget::screenPosToAddr(const QPoint &point,  bool middle) const
 1503 {
 1504     QPointF pt = point - itemArea.topLeft();
 1505 
 1506     int relativeAddress = 0;
 1507     int line = static_cast<int>(pt.y() / lineHeight);
 1508     relativeAddress += line * itemRowByteLen();
 1509     int column = static_cast<int>(pt.x() / columnExWidth());
 1510     relativeAddress += column * itemGroupByteLen();
 1511     pt.rx() -= column * columnExWidth();
 1512     auto roundingOffset = middle ? itemWidth() / 2 : 0;
 1513     relativeAddress += static_cast<int>((pt.x() + roundingOffset) / itemWidth()) * itemByteLen;
 1514     BasicCursor result(startAddress);
 1515     result += relativeAddress;
 1516     return result;
 1517 }
 1518 
 1519 BasicCursor HexWidget::asciiPosToAddr(const QPoint &point, bool middle) const
 1520 {
 1521     QPointF pt = point - asciiArea.topLeft();
 1522 
 1523     int relativeAddress = 0;
 1524     relativeAddress += static_cast<int>(pt.y() / lineHeight) * itemRowByteLen();
 1525     auto roundingOffset = middle ? (charWidth / 2) : 0;
 1526     relativeAddress += static_cast<int>((pt.x() + (roundingOffset)) / charWidth);
 1527     BasicCursor result(startAddress);
 1528     result += relativeAddress;
 1529     return result;
 1530 }
 1531 
 1532 BasicCursor HexWidget::currentAreaPosToAddr(const QPoint &point, bool middle) const
 1533 {
 1534     return cursorOnAscii ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);
 1535 }
 1536 
 1537 BasicCursor HexWidget::mousePosToAddr(const QPoint &point, bool middle) const
 1538 {
 1539     return asciiArea.contains(point) ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);
 1540 }
 1541 
 1542 QRectF HexWidget::itemRectangle(int offset)
 1543 {
 1544     qreal x;
 1545     qreal y;
 1546 
 1547     qreal width = itemWidth();
 1548     y = (offset / itemRowByteLen()) * lineHeight;
 1549     offset %= itemRowByteLen();
 1550 
 1551     x = (offset / itemGroupByteLen()) * columnExWidth();
 1552     offset %= itemGroupByteLen();
 1553     x += (offset / itemByteLen) * itemWidth();
 1554     if (offset == 0) {
 1555         x -= charWidth / 2;
 1556         width += charWidth / 2;
 1557     }
 1558     if (static_cast<int>(offset) == itemGroupByteLen() - 1) {
 1559         width += charWidth / 2;
 1560     }
 1561 
 1562     x += itemArea.x();
 1563     y += itemArea.y();
 1564 
 1565     return QRectF(x, y, width, lineHeight);
 1566 }
 1567 
 1568 QRectF HexWidget::asciiRectangle(int offset)
 1569 {
 1570     QPointF p;
 1571 
 1572     p.ry() = (offset / itemRowByteLen()) * lineHeight;
 1573     offset %= itemRowByteLen();
 1574 
 1575     p.rx() = offset * charWidth;
 1576 
 1577     p += asciiArea.topLeft();
 1578 
 1579     return QRectF(p, QSizeF(charWidth, lineHeight));
 1580 }
 1581 
 1582 RVA HexWidget::getLocationAddress() {
 1583     return !selection.isEmpty() ? selection.start() : cursor.address;
 1584 }