1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "bineditorwidget.h"
27 #include "bineditorservice.h"
28 #include "markup.h"
29 
30 #include <coreplugin/coreconstants.h>
31 #include <coreplugin/editormanager/editormanager.h>
32 #include <coreplugin/editormanager/ieditor.h>
33 
34 #include <texteditor/behaviorsettings.h>
35 #include <texteditor/fontsettings.h>
36 #include <texteditor/texteditorconstants.h>
37 #include <texteditor/texteditorsettings.h>
38 
39 #include <utils/fadingindicator.h>
40 #include <utils/fileutils.h>
41 #include <utils/qtcassert.h>
42 
43 #include <QByteArrayMatcher>
44 #include <QDebug>
45 #include <QFile>
46 #include <QTemporaryFile>
47 #include <QVariant>
48 
49 #include <QApplication>
50 #include <QAction>
51 #include <QClipboard>
52 #include <QFontMetrics>
53 #include <QHelpEvent>
54 #include <QMenu>
55 #include <QMessageBox>
56 #include <QPainter>
57 #include <QPointer>
58 #include <QScrollBar>
59 #include <QToolTip>
60 #include <QWheelEvent>
61 
62 using namespace Core;
63 
64 namespace BinEditor {
65 namespace Internal {
66 
67 const QChar MidpointChar(u'\u00B7');
68 
calculateHexPattern(const QByteArray & pattern)69 static QByteArray calculateHexPattern(const QByteArray &pattern)
70 {
71     QByteArray result;
72     if (pattern.size() % 2 == 0) {
73         bool ok = true;
74         int i = 0;
75         while (i < pattern.size()) {
76             ushort s = pattern.mid(i, 2).toUShort(&ok, 16);
77             if (!ok)
78                 return QByteArray();
79             result.append(s);
80             i += 2;
81         }
82     }
83     return result;
84 }
85 
86 class BinEditorWidgetPrivate : public EditorService
87 {
88 public:
BinEditorWidgetPrivate(BinEditorWidget * widget)89     BinEditorWidgetPrivate(BinEditorWidget *widget) : q(widget) {}
~BinEditorWidgetPrivate()90     ~BinEditorWidgetPrivate() override { if (m_aboutToBeDestroyedHandler) m_aboutToBeDestroyedHandler(); }
91 
widget()92     QWidget *widget() override { return q; }
editor()93     IEditor *editor() override { return q->editor(); }
94 
setReadOnly(bool on)95     void setReadOnly(bool on) override { q->setReadOnly(on); }
setNewWindowRequestAllowed(bool on)96     void setNewWindowRequestAllowed(bool on) override { q->setNewWindowRequestAllowed(on); }
97 
setFinished()98     void setFinished() override
99     {
100         q->setReadOnly(true);
101         m_fetchDataHandler = {};
102         m_newWindowRequestHandler = {};
103         m_newRangeRequestHandler = {};
104         m_dataChangedHandler = {};
105         m_watchPointRequestHandler = {};
106     }
107 
setSizes(quint64 address,qint64 range,int blockSize)108     void setSizes(quint64 address, qint64 range, int blockSize) override { q->setSizes(address, range, blockSize); }
setCursorPosition(qint64 pos)109     void setCursorPosition(qint64 pos) override { q->setCursorPosition(pos); }
updateContents()110     void updateContents() override { q->updateContents(); }
addData(quint64 address,const QByteArray & data)111     void addData(quint64 address, const QByteArray &data) override { q->addData(address, data); }
112 
clearMarkup()113     void clearMarkup() override { m_markup.clear(); }
addMarkup(quint64 a,quint64 l,const QColor & c,const QString & t)114     void addMarkup(quint64 a, quint64 l, const QColor &c, const QString &t) override { m_markup.append(Markup(a, l, c, t)); }
commitMarkup()115     void commitMarkup() override { q->setMarkup(m_markup); }
116 
setFetchDataHandler(const std::function<void (quint64)> & cb)117     void setFetchDataHandler(const std::function<void(quint64)> &cb) override { m_fetchDataHandler = cb; }
setNewWindowRequestHandler(const std::function<void (quint64)> & cb)118     void setNewWindowRequestHandler(const std::function<void(quint64)> &cb) override { m_newWindowRequestHandler = cb; }
setNewRangeRequestHandler(const std::function<void (quint64)> & cb)119     void setNewRangeRequestHandler(const std::function<void(quint64)> &cb) override { m_newRangeRequestHandler = cb; }
setDataChangedHandler(const std::function<void (quint64,const QByteArray &)> & cb)120     void setDataChangedHandler(const std::function<void(quint64, const QByteArray &)> &cb) override { m_dataChangedHandler = cb; }
setWatchPointRequestHandler(const std::function<void (quint64,uint)> & cb)121     void setWatchPointRequestHandler(const std::function<void(quint64, uint)> &cb) override { m_watchPointRequestHandler = cb; }
setAboutToBeDestroyedHandler(const std::function<void ()> & cb)122     void setAboutToBeDestroyedHandler(const std::function<void()> & cb) override { m_aboutToBeDestroyedHandler = cb; }
123 
fetchData(quint64 address)124     void fetchData(quint64 address) { if (m_fetchDataHandler) m_fetchDataHandler(address); }
requestNewWindow(quint64 address)125     void requestNewWindow(quint64 address) { if (m_newWindowRequestHandler) m_newWindowRequestHandler(address); }
requestWatchPoint(quint64 address,int size)126     void requestWatchPoint(quint64 address, int size) { if (m_watchPointRequestHandler) m_watchPointRequestHandler(address, size); }
requestNewRange(quint64 address)127     void requestNewRange(quint64 address) { if (m_newRangeRequestHandler) m_newRangeRequestHandler(address); }
announceChangedData(quint64 address,const QByteArray & ba)128     void announceChangedData(quint64 address, const QByteArray &ba) { if (m_dataChangedHandler) m_dataChangedHandler(address, ba); }
129 
130 private:
131     BinEditorWidget *q;
132     std::function<void(quint64)> m_fetchDataHandler;
133     std::function<void(quint64)> m_newWindowRequestHandler;
134     std::function<void(quint64)> m_newRangeRequestHandler;
135     std::function<void(quint64, const QByteArray &)> m_dataChangedHandler;
136     std::function<void(quint64, uint)> m_watchPointRequestHandler;
137     std::function<void()> m_aboutToBeDestroyedHandler;
138     QList<Markup> m_markup;
139 };
140 
BinEditorWidget(QWidget * parent)141 BinEditorWidget::BinEditorWidget(QWidget *parent)
142     : QAbstractScrollArea(parent), d(new BinEditorWidgetPrivate(this))
143 {
144     m_bytesPerLine = 16;
145     m_ieditor = nullptr;
146     m_baseAddr = 0;
147     m_blockSize = 4096;
148     m_size = 0;
149     m_addressBytes = 4;
150     init();
151     m_unmodifiedState = 0;
152     m_readOnly = false;
153     m_hexCursor = true;
154     m_cursorPosition = 0;
155     m_anchorPosition = 0;
156     m_lowNibble = false;
157     m_cursorVisible = false;
158     m_caseSensitiveSearch = false;
159     m_canRequestNewWindow = false;
160     setFocusPolicy(Qt::WheelFocus);
161     setFrameStyle(QFrame::Plain);
162 
163     // Font settings
164     setFontSettings(TextEditor::TextEditorSettings::fontSettings());
165     connect(TextEditor::TextEditorSettings::instance(),
166             &TextEditor::TextEditorSettings::fontSettingsChanged,
167             this, &BinEditorWidget::setFontSettings);
168 
169 }
170 
~BinEditorWidget()171 BinEditorWidget::~BinEditorWidget()
172 {
173     delete d;
174 }
175 
editorService() const176 EditorService *BinEditorWidget::editorService() const
177 {
178     return d;
179 }
180 
init()181 void BinEditorWidget::init()
182 {
183     const int addressStringWidth =
184         2*m_addressBytes + (m_addressBytes - 1) / 2;
185     m_addressString = QString(addressStringWidth, QLatin1Char(':'));
186     QFontMetrics fm(fontMetrics());
187     m_descent = fm.descent();
188     m_ascent = fm.ascent();
189     m_lineHeight = fm.lineSpacing();
190     m_charWidth = fm.horizontalAdvance(QChar(QLatin1Char('M')));
191     m_margin = m_charWidth;
192     m_columnWidth = 2 * m_charWidth + fm.horizontalAdvance(QChar(QLatin1Char(' ')));
193     m_numLines = m_size / m_bytesPerLine + 1;
194     m_numVisibleLines = viewport()->height() / m_lineHeight;
195     m_textWidth = m_bytesPerLine * m_charWidth + m_charWidth;
196     int numberWidth = fm.horizontalAdvance(QChar(QLatin1Char('9')));
197     m_labelWidth = 2*m_addressBytes * numberWidth + (m_addressBytes - 1)/2 * m_charWidth;
198 
199     int expectedCharWidth = m_columnWidth / 3;
200     const char *hex = "0123456789abcdef";
201     m_isMonospacedFont = true;
202     while (*hex) {
203         if (fm.horizontalAdvance(QLatin1Char(*hex)) != expectedCharWidth) {
204             m_isMonospacedFont = false;
205             break;
206         }
207         ++hex;
208     }
209 
210     if (m_isMonospacedFont && fm.horizontalAdvance(QLatin1String("M M ")) != m_charWidth * 4) {
211         // On Qt/Mac, monospace font widths may have a fractional component
212         // This breaks the assumption that width("MMM") == width('M') * 3
213 
214         m_isMonospacedFont = false;
215         m_columnWidth = fm.horizontalAdvance(QLatin1String("MMM"));
216         m_labelWidth = m_addressBytes == 4
217             ? fm.horizontalAdvance(QLatin1String("MMMM:MMMM"))
218             : fm.horizontalAdvance(QLatin1String("MMMM:MMMM:MMMM:MMMM"));
219     }
220 
221     horizontalScrollBar()->setRange(0, 2 * m_margin + m_bytesPerLine * m_columnWidth
222                                     + m_labelWidth + m_textWidth - viewport()->width());
223     horizontalScrollBar()->setPageStep(viewport()->width());
224     verticalScrollBar()->setRange(0, m_numLines - m_numVisibleLines);
225     verticalScrollBar()->setPageStep(m_numVisibleLines);
226     ensureCursorVisible();
227 }
228 
229 
addData(quint64 addr,const QByteArray & data)230 void BinEditorWidget::addData(quint64 addr, const QByteArray &data)
231 {
232     QTC_ASSERT(data.size() == m_blockSize, return);
233     if (addr >= m_baseAddr && addr <= m_baseAddr + m_size - 1) {
234         if (m_data.size() * m_blockSize >= 64 * 1024 * 1024)
235             m_data.clear();
236         const qint64 translatedBlock = (addr - m_baseAddr) / m_blockSize;
237         m_data.insert(translatedBlock, data);
238         m_requests.remove(translatedBlock);
239         viewport()->update();
240     }
241 }
242 
requestDataAt(qint64 pos) const243 bool BinEditorWidget::requestDataAt(qint64 pos) const
244 {
245     qint64 block = pos / m_blockSize;
246     BlockMap::const_iterator it = m_modifiedData.find(block);
247     if (it != m_modifiedData.constEnd())
248         return true;
249     it = m_data.find(block);
250     if (it != m_data.end())
251         return true;
252     if (m_requests.contains(block))
253         return false;
254     m_requests.insert(block);
255     d->fetchData((m_baseAddr / m_blockSize + block) * m_blockSize);
256     return true;
257 }
258 
requestOldDataAt(qint64 pos) const259 bool BinEditorWidget::requestOldDataAt(qint64 pos) const
260 {
261     qint64 block = pos / m_blockSize;
262     BlockMap::const_iterator it = m_oldData.find(block);
263     return it != m_oldData.end();
264 }
265 
dataAt(qint64 pos,bool old) const266 char BinEditorWidget::dataAt(qint64 pos, bool old) const
267 {
268     qint64 block = pos / m_blockSize;
269     int offset = static_cast<int>(pos - block * m_blockSize);
270     return blockData(block, old).at(offset);
271 }
272 
changeDataAt(qint64 pos,char c)273 void BinEditorWidget::changeDataAt(qint64 pos, char c)
274 {
275     qint64 block = pos / m_blockSize;
276     BlockMap::iterator it = m_modifiedData.find(block);
277     int offset = static_cast<int>(pos - block * m_blockSize);
278     if (it != m_modifiedData.end()) {
279         it.value()[offset] = c;
280     } else {
281         it = m_data.find(block);
282         if (it != m_data.end()) {
283             QByteArray data = it.value();
284             data[offset] = c;
285             m_modifiedData.insert(block, data);
286         }
287     }
288 
289     d->announceChangedData(m_baseAddr + pos, QByteArray(1, c));
290 }
291 
dataMid(qint64 from,int length,bool old) const292 QByteArray BinEditorWidget::dataMid(qint64 from, int length, bool old) const
293 {
294     qint64 end = from + length;
295     qint64 block = from / m_blockSize;
296 
297     QByteArray data;
298     data.reserve(length);
299     do {
300         data += blockData(block++, old);
301     } while (block * m_blockSize < end);
302 
303     return data.mid(from - ((from / m_blockSize) * m_blockSize), length);
304 }
305 
blockData(qint64 block,bool old) const306 QByteArray BinEditorWidget::blockData(qint64 block, bool old) const
307 {
308     if (old) {
309         BlockMap::const_iterator it = m_modifiedData.find(block);
310         return it != m_modifiedData.constEnd()
311                 ? it.value() : m_oldData.value(block, m_emptyBlock);
312     }
313     BlockMap::const_iterator it = m_modifiedData.find(block);
314     return it != m_modifiedData.constEnd()
315             ? it.value() : m_data.value(block, m_emptyBlock);
316 }
317 
setFontSettings(const TextEditor::FontSettings & fs)318 void BinEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
319 {
320     setFont(fs.toTextCharFormat(TextEditor::C_TEXT).font());
321 }
322 
setBlinkingCursorEnabled(bool enable)323 void BinEditorWidget::setBlinkingCursorEnabled(bool enable)
324 {
325     if (enable && QApplication::cursorFlashTime() > 0)
326         m_cursorBlinkTimer.start(QApplication::cursorFlashTime() / 2, this);
327     else
328         m_cursorBlinkTimer.stop();
329     m_cursorVisible = enable;
330     updateLines();
331 }
332 
focusInEvent(QFocusEvent *)333 void BinEditorWidget::focusInEvent(QFocusEvent *)
334 {
335     setBlinkingCursorEnabled(true);
336 }
337 
focusOutEvent(QFocusEvent *)338 void BinEditorWidget::focusOutEvent(QFocusEvent *)
339 {
340     setBlinkingCursorEnabled(false);
341 }
342 
timerEvent(QTimerEvent * e)343 void BinEditorWidget::timerEvent(QTimerEvent *e)
344 {
345     if (e->timerId() == m_autoScrollTimer.timerId()) {
346         QRect visible = viewport()->rect();
347         QPoint pos;
348         const QPoint globalPos = QCursor::pos();
349         pos = viewport()->mapFromGlobal(globalPos);
350         QMouseEvent ev(QEvent::MouseMove, pos, globalPos,
351             Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
352         mouseMoveEvent(&ev);
353         int deltaY = qMax(pos.y() - visible.top(),
354                           visible.bottom() - pos.y()) - visible.height();
355         int deltaX = qMax(pos.x() - visible.left(),
356                           visible.right() - pos.x()) - visible.width();
357         int delta = qMax(deltaX, deltaY);
358         if (delta >= 0) {
359             if (delta < 7)
360                 delta = 7;
361             int timeout = 4900 / (delta * delta);
362             m_autoScrollTimer.start(timeout, this);
363 
364             if (deltaY > 0)
365                 verticalScrollBar()->triggerAction(pos.y() < visible.center().y() ?
366                                        QAbstractSlider::SliderSingleStepSub
367                                        : QAbstractSlider::SliderSingleStepAdd);
368             if (deltaX > 0)
369                 horizontalScrollBar()->triggerAction(pos.x() < visible.center().x() ?
370                                        QAbstractSlider::SliderSingleStepSub
371                                        : QAbstractSlider::SliderSingleStepAdd);
372         }
373     } else if (e->timerId() == m_cursorBlinkTimer.timerId()) {
374         m_cursorVisible = !m_cursorVisible;
375         updateLines();
376     }
377     QAbstractScrollArea::timerEvent(e);
378 }
379 
380 
setModified(bool modified)381 void BinEditorWidget::setModified(bool modified)
382 {
383     int unmodifiedState = modified ? -1 : m_undoStack.size();
384     if (unmodifiedState == m_unmodifiedState)
385         return;
386     m_unmodifiedState = unmodifiedState;
387     emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
388 }
389 
isModified() const390 bool BinEditorWidget::isModified() const
391 {
392     return (m_undoStack.size() != m_unmodifiedState);
393 }
394 
setReadOnly(bool readOnly)395 void BinEditorWidget::setReadOnly(bool readOnly)
396 {
397     m_readOnly = readOnly;
398 }
399 
isReadOnly() const400 bool BinEditorWidget::isReadOnly() const
401 {
402     return m_readOnly;
403 }
404 
save(QString * errorString,const QString & oldFileName,const QString & newFileName)405 bool BinEditorWidget::save(QString *errorString, const QString &oldFileName, const QString &newFileName)
406 {
407     if (oldFileName != newFileName) {
408         QString tmpName;
409         {
410             QTemporaryFile tmp(newFileName + QLatin1String("_XXXXXX.new"));
411             if (!tmp.open())
412                 return false;
413             tmpName = tmp.fileName();
414         }
415         if (!QFile::copy(oldFileName, tmpName))
416             return false;
417         if (QFile::exists(newFileName) && !QFile::remove(newFileName))
418             return false;
419         if (!QFile::rename(tmpName, newFileName))
420             return false;
421     }
422     Utils::FileSaver saver(Utils::FilePath::fromString(newFileName),
423                            QIODevice::ReadWrite); // QtBug: WriteOnly truncates.
424     if (!saver.hasError()) {
425         QFile *output = saver.file();
426         const qint64 size = output->size();
427         for (BlockMap::const_iterator it = m_modifiedData.constBegin();
428             it != m_modifiedData.constEnd(); ++it) {
429             if (!saver.setResult(output->seek(it.key() * m_blockSize)))
430                 break;
431             if (!saver.write(it.value()))
432                 break;
433             if (!saver.setResult(output->flush()))
434                 break;
435         }
436 
437         // We may have padded the displayed data, so we have to make sure
438         // changes to that area are not actually written back to disk.
439         if (!saver.hasError())
440             saver.setResult(output->resize(size));
441     }
442     if (!saver.finalize(errorString))
443         return false;
444 
445     setModified(false);
446     return true;
447 }
448 
setSizes(quint64 startAddr,qint64 range,int blockSize)449 void BinEditorWidget::setSizes(quint64 startAddr, qint64 range, int blockSize)
450 {
451     int newBlockSize = blockSize;
452     QTC_ASSERT(blockSize, return);
453     QTC_ASSERT((blockSize/m_bytesPerLine) * m_bytesPerLine == blockSize,
454                blockSize = (blockSize/m_bytesPerLine + 1) * m_bytesPerLine);
455     // Users can edit data in the range
456     // [startAddr - range/2, startAddr + range/2].
457     quint64 newBaseAddr = quint64(range/2) > startAddr ? 0 : startAddr - range/2;
458     newBaseAddr = (newBaseAddr / blockSize) * blockSize;
459 
460     const quint64 maxRange = Q_UINT64_C(0xffffffffffffffff) - newBaseAddr + 1;
461     qint64 newSize = newBaseAddr != 0 && quint64(range) >= maxRange
462               ? maxRange : range;
463     int newAddressBytes = (newBaseAddr + newSize < quint64(1) << 32
464                    && newBaseAddr + newSize >= newBaseAddr) ? 4 : 8;
465 
466 
467 
468     if (newBlockSize == m_blockSize
469             && newBaseAddr == m_baseAddr
470             && newSize == m_size
471             && newAddressBytes == m_addressBytes)
472         return;
473 
474     m_blockSize = blockSize;
475     m_emptyBlock = QByteArray(blockSize, '\0');
476     m_data.clear();
477     m_modifiedData.clear();
478     m_requests.clear();
479 
480     m_baseAddr = newBaseAddr;
481     m_size = newSize;
482     m_addressBytes = newAddressBytes;
483 
484     m_unmodifiedState = 0;
485     m_undoStack.clear();
486     m_redoStack.clear();
487     init();
488 
489     setCursorPosition(startAddr - m_baseAddr);
490     viewport()->update();
491 }
492 
resizeEvent(QResizeEvent *)493 void BinEditorWidget::resizeEvent(QResizeEvent *)
494 {
495     init();
496 }
497 
scrollContentsBy(int dx,int dy)498 void BinEditorWidget::scrollContentsBy(int dx, int dy)
499 {
500     viewport()->scroll(isRightToLeft() ? -dx : dx, dy * m_lineHeight);
501     const QScrollBar * const scrollBar = verticalScrollBar();
502     const int scrollPos = scrollBar->value();
503     if (dy <= 0 && scrollPos == scrollBar->maximum())
504         d->requestNewRange(baseAddress() + m_size);
505     else if (dy >= 0 && scrollPos == scrollBar->minimum())
506         d->requestNewRange(baseAddress());
507 }
508 
changeEvent(QEvent * e)509 void BinEditorWidget::changeEvent(QEvent *e)
510 {
511     QAbstractScrollArea::changeEvent(e);
512     if (e->type() == QEvent::ActivationChange) {
513         if (!isActiveWindow())
514             m_autoScrollTimer.stop();
515     }
516     init();
517     viewport()->update();
518 }
519 
520 
wheelEvent(QWheelEvent * e)521 void BinEditorWidget::wheelEvent(QWheelEvent *e)
522 {
523     if (e->modifiers() & Qt::ControlModifier) {
524         if (!TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming) {
525             // When the setting is disabled globally,
526             // we have to skip calling QAbstractScrollArea::wheelEvent()
527             // that changes zoom in it.
528             return;
529         }
530 
531         const float delta = e->angleDelta().y() / 120.f;
532         if (delta != 0)
533             zoomF(delta);
534         return;
535     }
536     QAbstractScrollArea::wheelEvent(e);
537 }
538 
cursorRect() const539 QRect BinEditorWidget::cursorRect() const
540 {
541     int topLine = verticalScrollBar()->value();
542     int line = m_cursorPosition / m_bytesPerLine;
543     int y = (line - topLine) * m_lineHeight;
544     int xoffset = horizontalScrollBar()->value();
545     int column = m_cursorPosition % m_bytesPerLine;
546     int x = m_hexCursor
547             ? (-xoffset + m_margin + m_labelWidth + column * m_columnWidth)
548             : (-xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth
549                + m_charWidth + column * m_charWidth);
550     int w = m_hexCursor ? m_columnWidth : m_charWidth;
551     return QRect(x, y, w, m_lineHeight);
552 }
553 
posAt(const QPoint & pos,bool includeEmptyArea) const554 Utils::optional<qint64> BinEditorWidget::posAt(const QPoint &pos, bool includeEmptyArea) const
555 {
556     const int xoffset = horizontalScrollBar()->value();
557     int x = xoffset + pos.x() - m_margin - m_labelWidth;
558     if (!includeEmptyArea && x < 0)
559         return Utils::nullopt;
560     int column = qMin(15, qMax(0,x) / m_columnWidth);
561     const qint64 topLine = verticalScrollBar()->value();
562     const qint64 line = topLine + pos.y() / m_lineHeight;
563 
564     // "clear text" area
565     if (x > m_bytesPerLine * m_columnWidth + m_charWidth/2) {
566         x -= m_bytesPerLine * m_columnWidth + m_charWidth;
567         for (column = 0; column < 16; ++column) {
568             const qint64 dataPos = line * m_bytesPerLine + column;
569             if (dataPos < 0 || dataPos >= m_size)
570                 break;
571             QChar qc(QLatin1Char(dataAt(dataPos)));
572             if (!qc.isPrint())
573                 qc = MidpointChar;
574             x -= fontMetrics().horizontalAdvance(qc);
575             if (x <= 0)
576                 break;
577         }
578         if (!includeEmptyArea && x > 0) // right of the text area
579             return Utils::nullopt;
580     }
581 
582     const qint64 bytePos = line * m_bytesPerLine + column;
583     if (!includeEmptyArea && bytePos >= m_size)
584         return Utils::nullopt;
585     return qMin(m_size - 1, bytePos);
586 }
587 
inTextArea(const QPoint & pos) const588 bool BinEditorWidget::inTextArea(const QPoint &pos) const
589 {
590     int xoffset = horizontalScrollBar()->value();
591     int x = xoffset + pos.x() - m_margin - m_labelWidth;
592     return (x > m_bytesPerLine * m_columnWidth + m_charWidth/2);
593 }
594 
updateLines()595 void BinEditorWidget::updateLines()
596 {
597     updateLines(m_cursorPosition, m_cursorPosition);
598 }
599 
updateLines(int fromPosition,int toPosition)600 void BinEditorWidget::updateLines(int fromPosition, int toPosition)
601 {
602     int topLine = verticalScrollBar()->value();
603     int firstLine = qMin(fromPosition, toPosition) / m_bytesPerLine;
604     int lastLine = qMax(fromPosition, toPosition) / m_bytesPerLine;
605     int y = (firstLine - topLine) * m_lineHeight;
606     int h = (lastLine - firstLine + 1 ) * m_lineHeight;
607 
608     viewport()->update(0, y, viewport()->width(), h);
609 }
610 
dataIndexOf(const QByteArray & pattern,qint64 from,bool caseSensitive) const611 int BinEditorWidget::dataIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive) const
612 {
613     int trailing = pattern.size();
614     if (trailing > m_blockSize)
615         return -1;
616 
617     QByteArray buffer;
618     buffer.resize(m_blockSize + trailing);
619     QByteArrayMatcher matcher(pattern);
620 
621     qint64 block = from / m_blockSize;
622     const int end = qMin<qint64>(from + SearchStride, m_size);
623     while (from < end) {
624         if (!requestDataAt(block * m_blockSize))
625             return -1;
626         QByteArray data = blockData(block);
627         char *b = buffer.data();
628         ::memcpy(b, b + m_blockSize, trailing);
629         ::memcpy(b + trailing, data.constData(), m_blockSize);
630 
631         if (!caseSensitive)
632             buffer = buffer.toLower();
633 
634         int pos = matcher.indexIn(buffer, from - (block * m_blockSize) + trailing);
635         if (pos >= 0)
636             return pos + block * m_blockSize - trailing;
637         ++block;
638         from = block * m_blockSize - trailing;
639     }
640     return end == m_size ? -1 : -2;
641 }
642 
dataLastIndexOf(const QByteArray & pattern,qint64 from,bool caseSensitive) const643 int BinEditorWidget::dataLastIndexOf(const QByteArray &pattern, qint64 from, bool caseSensitive) const
644 {
645     int trailing = pattern.size();
646     if (trailing > m_blockSize)
647         return -1;
648 
649     QByteArray buffer;
650     buffer.resize(m_blockSize + trailing);
651 
652     if (from == -1)
653         from = m_size;
654     int block = from / m_blockSize;
655     const int lowerBound = qMax(qint64(0), from - SearchStride);
656     while (from > lowerBound) {
657         if (!requestDataAt(qint64(block) * m_blockSize))
658             return -1;
659         QByteArray data = blockData(block);
660         char *b = buffer.data();
661         ::memcpy(b + m_blockSize, b, trailing);
662         ::memcpy(b, data.constData(), m_blockSize);
663 
664         if (!caseSensitive)
665             buffer = buffer.toLower();
666 
667         int pos = buffer.lastIndexOf(pattern, from - (block * m_blockSize));
668         if (pos >= 0)
669             return pos + block * m_blockSize;
670         --block;
671         from = qint64(block) * m_blockSize + (m_blockSize-1) + trailing;
672     }
673     return lowerBound == 0 ? -1 : -2;
674 }
675 
676 
find(const QByteArray & pattern_arg,qint64 from,QTextDocument::FindFlags findFlags)677 int BinEditorWidget::find(const QByteArray &pattern_arg, qint64 from,
678                     QTextDocument::FindFlags findFlags)
679 {
680     if (pattern_arg.isEmpty())
681         return 0;
682 
683     QByteArray pattern = pattern_arg;
684 
685     bool caseSensitiveSearch = (findFlags & QTextDocument::FindCaseSensitively);
686 
687     if (!caseSensitiveSearch)
688         pattern = pattern.toLower();
689 
690     bool backwards = (findFlags & QTextDocument::FindBackward);
691     int found = backwards ? dataLastIndexOf(pattern, from, caseSensitiveSearch)
692                 : dataIndexOf(pattern, from, caseSensitiveSearch);
693 
694     int foundHex = -1;
695     QByteArray hexPattern = calculateHexPattern(pattern_arg);
696     if (!hexPattern.isEmpty()) {
697         foundHex = backwards ? dataLastIndexOf(hexPattern, from)
698                    : dataIndexOf(hexPattern, from);
699     }
700 
701     qint64 pos = foundHex == -1 || (found >= 0 && (foundHex == -2 || found < foundHex))
702               ? found : foundHex;
703 
704     if (pos >= m_size)
705         pos = -1;
706 
707     if (pos >= 0) {
708         setCursorPosition(pos);
709         setCursorPosition(pos + (found == pos ? pattern.size() : hexPattern.size()) - 1, KeepAnchor);
710     }
711     return pos;
712 }
713 
findPattern(const QByteArray & data,const QByteArray & dataHex,int from,int offset,int * match)714 int BinEditorWidget::findPattern(const QByteArray &data, const QByteArray &dataHex,
715     int from, int offset, int *match)
716 {
717     if (m_searchPattern.isEmpty())
718         return -1;
719     int normal = m_searchPattern.isEmpty()
720         ? -1 : data.indexOf(m_searchPattern, from - offset);
721     int hex = m_searchPatternHex.isEmpty()
722         ? -1 : dataHex.indexOf(m_searchPatternHex, from - offset);
723 
724     if (normal >= 0 && (hex < 0 || normal < hex)) {
725         if (match)
726             *match = m_searchPattern.length();
727         return normal + offset;
728     }
729     if (hex >= 0) {
730         if (match)
731             *match = m_searchPatternHex.length();
732         return hex + offset;
733     }
734 
735     return -1;
736 }
737 
738 
drawItems(QPainter * painter,int x,int y,const QString & itemString)739 void BinEditorWidget::drawItems(QPainter *painter, int x, int y, const QString &itemString)
740 {
741     if (m_isMonospacedFont) {
742         painter->drawText(x, y, itemString);
743     } else {
744         for (int i = 0; i < m_bytesPerLine; ++i)
745             painter->drawText(x + i*m_columnWidth, y, itemString.mid(i*3, 2));
746     }
747 }
748 
drawChanges(QPainter * painter,int x,int y,const char * changes)749 void BinEditorWidget::drawChanges(QPainter *painter, int x, int y, const char *changes)
750 {
751     const QBrush red(QColor(250, 150, 150));
752     for (int i = 0; i < m_bytesPerLine; ++i) {
753         if (changes[i]) {
754             painter->fillRect(x + i*m_columnWidth, y - m_ascent,
755                 2*m_charWidth, m_lineHeight, red);
756         }
757     }
758 }
759 
addressString(quint64 address)760 QString BinEditorWidget::addressString(quint64 address)
761 {
762     QChar *addressStringData = m_addressString.data();
763     const char *hex = "0123456789abcdef";
764 
765     // Take colons into account.
766     const int indices[16] = {
767         0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18
768     };
769 
770     for (int b = 0; b < m_addressBytes; ++b) {
771         addressStringData[indices[2*m_addressBytes - 1 - b*2]] =
772             QLatin1Char(hex[(address >> (8*b)) & 0xf]);
773         addressStringData[indices[2*m_addressBytes - 2 - b*2]] =
774             QLatin1Char(hex[(address >> (8*b + 4)) & 0xf]);
775     }
776     return m_addressString;
777 }
778 
paintCursorBorder(QPainter * painter,const QRect & cursorRect)779 static void paintCursorBorder(QPainter *painter, const QRect &cursorRect)
780 {
781     painter->save();
782     QPen borderPen(Qt::red);
783     borderPen.setJoinStyle(Qt::MiterJoin);
784     painter->setPen(borderPen);
785     painter->drawRect(QRectF(cursorRect).adjusted(0.5, 0.5, -0.5, -0.5));
786     painter->restore();
787 }
788 
paintEvent(QPaintEvent * e)789 void BinEditorWidget::paintEvent(QPaintEvent *e)
790 {
791     QPainter painter(viewport());
792     const int topLine = verticalScrollBar()->value();
793     const int xoffset = horizontalScrollBar()->value();
794     const int x1 = -xoffset + m_margin + m_labelWidth - m_charWidth/2 - 1;
795     const int x2 = -xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth + m_charWidth/2;
796     painter.drawLine(x1, 0, x1, viewport()->height());
797     painter.drawLine(x2, 0, x2, viewport()->height());
798 
799     int viewport_height = viewport()->height();
800     for (int i = 0; i < 8; ++i) {
801         int bg_x = -xoffset +  m_margin + (2 * i + 1) * m_columnWidth + m_labelWidth;
802         QRect r(bg_x - m_charWidth/2, 0, m_columnWidth, viewport_height);
803         painter.fillRect(e->rect() & r, palette().alternateBase());
804     }
805 
806     int matchLength = 0;
807 
808     QByteArray patternData, patternDataHex;
809     int patternOffset = qMax(0, topLine*m_bytesPerLine - m_searchPattern.size());
810     if (!m_searchPattern.isEmpty()) {
811         patternData = dataMid(patternOffset, m_numVisibleLines * m_bytesPerLine + (topLine*m_bytesPerLine - patternOffset));
812         patternDataHex = patternData;
813         if (!m_caseSensitiveSearch)
814             patternData = patternData.toLower();
815     }
816 
817     int foundPatternAt = findPattern(patternData, patternDataHex, patternOffset, patternOffset, &matchLength);
818 
819     int selStart, selEnd;
820     if (m_cursorPosition >= m_anchorPosition) {
821         selStart = m_anchorPosition;
822         selEnd = m_cursorPosition;
823     } else {
824         selStart = m_cursorPosition;
825         selEnd = m_anchorPosition;
826     }
827 
828     QString itemString(m_bytesPerLine*3, QLatin1Char(' '));
829     QChar *itemStringData = itemString.data();
830     char changedString[160] = {false};
831     QTC_ASSERT((size_t)m_bytesPerLine < sizeof(changedString), return);
832     const char *hex = "0123456789abcdef";
833 
834     painter.setPen(palette().text().color());
835     const QFontMetrics &fm = painter.fontMetrics();
836     for (int i = 0; i <= m_numVisibleLines; ++i) {
837         qint64 line = topLine + i;
838         if (line >= m_numLines)
839             break;
840 
841         const quint64 lineAddress = m_baseAddr + line * m_bytesPerLine;
842         int y = i * m_lineHeight + m_ascent;
843         if (y - m_ascent > e->rect().bottom())
844             break;
845         if (y + m_descent < e->rect().top())
846             continue;
847 
848         painter.drawText(-xoffset, i * m_lineHeight + m_ascent,
849                          addressString(lineAddress));
850 
851         int cursor = -1;
852         if (line * m_bytesPerLine <= m_cursorPosition
853                 && m_cursorPosition < line * m_bytesPerLine + m_bytesPerLine)
854             cursor = m_cursorPosition - line * m_bytesPerLine;
855 
856         bool hasData = requestDataAt(line * m_bytesPerLine);
857         bool hasOldData = requestOldDataAt(line * m_bytesPerLine);
858         bool isOld = hasOldData && !hasData;
859 
860         QString printable;
861 
862         if (hasData || hasOldData) {
863             for (int c = 0; c < m_bytesPerLine; ++c) {
864                 qint64 pos = line * m_bytesPerLine + c;
865                 if (pos >= m_size)
866                     break;
867                 QChar qc(QLatin1Char(dataAt(pos, isOld)));
868                 if (qc.unicode() >= 127 || !qc.isPrint())
869                     qc = MidpointChar;
870                 printable += qc;
871             }
872         } else {
873             printable = QString(m_bytesPerLine, QLatin1Char(' '));
874         }
875 
876         QRect selectionRect;
877         QRect printableSelectionRect;
878 
879         bool isFullySelected = (selStart < selEnd && selStart <= line*m_bytesPerLine && (line+1)*m_bytesPerLine <= selEnd);
880         bool somethingChanged = false;
881 
882         if (hasData || hasOldData) {
883             for (int c = 0; c < m_bytesPerLine; ++c) {
884                 qint64 pos = line * m_bytesPerLine + c;
885                 if (pos >= m_size) {
886                     while (c < m_bytesPerLine) {
887                         itemStringData[c*3] = itemStringData[c*3+1] = QLatin1Char(' ');
888                         ++c;
889                     }
890                     break;
891                 }
892                 if (foundPatternAt >= 0 && pos >= foundPatternAt + matchLength)
893                     foundPatternAt = findPattern(patternData, patternDataHex, foundPatternAt + matchLength, patternOffset, &matchLength);
894 
895 
896                 const uchar value = uchar(dataAt(pos, isOld));
897                 itemStringData[c*3] = QLatin1Char(hex[value >> 4]);
898                 itemStringData[c*3+1] = QLatin1Char(hex[value & 0xf]);
899                 if (hasOldData && !isOld && value != uchar(dataAt(pos, true))) {
900                     changedString[c] = true;
901                     somethingChanged = true;
902                 }
903 
904                 int item_x = -xoffset +  m_margin + c * m_columnWidth + m_labelWidth;
905 
906                 QColor color;
907                 foreach (const Markup &m, m_markup) {
908                     if (m.covers(lineAddress + c)) {
909                         color = m.color;
910                         break;
911                     }
912                 }
913                 if (foundPatternAt >= 0 && pos >= foundPatternAt && pos < foundPatternAt + matchLength)
914                     color = QColor(0xffef0b);
915 
916                 if (color.isValid()) {
917                     painter.fillRect(item_x - m_charWidth/2, y-m_ascent, m_columnWidth, m_lineHeight, color);
918                     int printable_item_x = -xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth + m_charWidth
919                                            + fm.horizontalAdvance(printable.left(c));
920                     painter.fillRect(printable_item_x, y-m_ascent,
921                                      fm.horizontalAdvance(printable.at(c)),
922                                      m_lineHeight, color);
923                 }
924 
925                 if (!isFullySelected && pos >= selStart && pos <= selEnd) {
926                     selectionRect |= QRect(item_x - m_charWidth/2, y-m_ascent, m_columnWidth, m_lineHeight);
927                     int printable_item_x = -xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth + m_charWidth
928                                            + fm.horizontalAdvance(printable.left(c));
929                     printableSelectionRect |= QRect(printable_item_x, y-m_ascent,
930                                                     fm.horizontalAdvance(printable.at(c)),
931                                                     m_lineHeight);
932                 }
933             }
934         }
935 
936         int x = -xoffset +  m_margin + m_labelWidth;
937 
938         if (isFullySelected) {
939             painter.save();
940             painter.fillRect(x - m_charWidth/2, y-m_ascent, m_bytesPerLine*m_columnWidth, m_lineHeight, palette().highlight());
941             painter.setPen(palette().highlightedText().color());
942             drawItems(&painter, x, y, itemString);
943             painter.restore();
944         } else {
945             if (somethingChanged)
946                 drawChanges(&painter, x, y, changedString);
947             drawItems(&painter, x, y, itemString);
948             if (!selectionRect.isEmpty()) {
949                 painter.save();
950                 painter.fillRect(selectionRect, palette().highlight());
951                 painter.setPen(palette().highlightedText().color());
952                 painter.setClipRect(selectionRect);
953                 drawItems(&painter, x, y, itemString);
954                 painter.restore();
955             }
956         }
957 
958         if (cursor >= 0) {
959             int w = fm.boundingRect(itemString.mid(cursor*3, 2)).width();
960             QRect cursorRect(x + cursor * m_columnWidth, y - m_ascent, w + 1, m_lineHeight);
961             paintCursorBorder(&painter, cursorRect);
962             if (m_hexCursor && m_cursorVisible) {
963                 if (m_lowNibble)
964                     cursorRect.adjust(fm.horizontalAdvance(itemString.left(1)), 0, 0, 0);
965                 painter.fillRect(cursorRect, Qt::red);
966                 painter.save();
967                 painter.setClipRect(cursorRect);
968                 painter.setPen(Qt::white);
969                 drawItems(&painter, x, y, itemString);
970                 painter.restore();
971             }
972         }
973 
974         int text_x = -xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth + m_charWidth;
975 
976         if (isFullySelected) {
977                 painter.save();
978                 painter.fillRect(text_x, y-m_ascent, fm.horizontalAdvance(printable), m_lineHeight,
979                                  palette().highlight());
980                 painter.setPen(palette().highlightedText().color());
981                 painter.drawText(text_x, y, printable);
982                 painter.restore();
983         } else {
984             painter.drawText(text_x, y, printable);
985             if (!printableSelectionRect.isEmpty()) {
986                 painter.save();
987                 painter.fillRect(printableSelectionRect, palette().highlight());
988                 painter.setPen(palette().highlightedText().color());
989                 painter.setClipRect(printableSelectionRect);
990                 painter.drawText(text_x, y, printable);
991                 painter.restore();
992             }
993         }
994 
995         if (cursor >= 0 && !printable.isEmpty()) {
996             QRect cursorRect(text_x + fm.horizontalAdvance(printable.left(cursor)),
997                              y-m_ascent,
998                              fm.horizontalAdvance(printable.at(cursor)),
999                              m_lineHeight);
1000             if (m_hexCursor || !m_cursorVisible) {
1001                 paintCursorBorder(&painter, cursorRect);
1002             } else {
1003                 painter.save();
1004                 painter.setClipRect(cursorRect);
1005                 painter.fillRect(cursorRect, Qt::red);
1006                 painter.setPen(Qt::white);
1007                 painter.drawText(text_x, y, printable);
1008                 painter.restore();
1009             }
1010         }
1011     }
1012 }
1013 
1014 
cursorPosition() const1015 qint64 BinEditorWidget::cursorPosition() const
1016 {
1017     return m_cursorPosition;
1018 }
1019 
setCursorPosition(qint64 pos,MoveMode moveMode)1020 void BinEditorWidget::setCursorPosition(qint64 pos, MoveMode moveMode)
1021 {
1022     pos = qMin(m_size - 1, qMax(qint64(0), pos));
1023     int oldCursorPosition = m_cursorPosition;
1024 
1025     m_lowNibble = false;
1026     m_cursorPosition = pos;
1027     if (moveMode == MoveAnchor) {
1028         updateLines(m_anchorPosition, oldCursorPosition);
1029         m_anchorPosition = m_cursorPosition;
1030     }
1031 
1032     updateLines(oldCursorPosition, m_cursorPosition);
1033     ensureCursorVisible();
1034     emit cursorPositionChanged(m_cursorPosition);
1035 }
1036 
1037 
ensureCursorVisible()1038 void BinEditorWidget::ensureCursorVisible()
1039 {
1040     QRect cr = cursorRect();
1041     QRect vr = viewport()->rect();
1042     if (!vr.contains(cr)) {
1043         if (cr.top() < vr.top())
1044             verticalScrollBar()->setValue(m_cursorPosition / m_bytesPerLine);
1045         else if (cr.bottom() > vr.bottom())
1046             verticalScrollBar()->setValue(m_cursorPosition / m_bytesPerLine - m_numVisibleLines + 1);
1047     }
1048 }
1049 
mousePressEvent(QMouseEvent * e)1050 void BinEditorWidget::mousePressEvent(QMouseEvent *e)
1051 {
1052     if (e->button() != Qt::LeftButton)
1053         return;
1054     MoveMode moveMode = e->modifiers() & Qt::ShiftModifier ? KeepAnchor : MoveAnchor;
1055     setCursorPosition(posAt(e->pos()).value(), moveMode);
1056     setBlinkingCursorEnabled(true);
1057     if (m_hexCursor == inTextArea(e->pos())) {
1058         m_hexCursor = !m_hexCursor;
1059         updateLines();
1060     }
1061 }
1062 
mouseMoveEvent(QMouseEvent * e)1063 void BinEditorWidget::mouseMoveEvent(QMouseEvent *e)
1064 {
1065     if (!(e->buttons() & Qt::LeftButton))
1066         return;
1067     setCursorPosition(posAt(e->pos()).value(), KeepAnchor);
1068     if (m_hexCursor == inTextArea(e->pos())) {
1069         m_hexCursor = !m_hexCursor;
1070         updateLines();
1071     }
1072     QRect visible = viewport()->rect();
1073     if (visible.contains(e->pos()))
1074         m_autoScrollTimer.stop();
1075     else if (!m_autoScrollTimer.isActive())
1076         m_autoScrollTimer.start(100, this);
1077 }
1078 
mouseReleaseEvent(QMouseEvent *)1079 void BinEditorWidget::mouseReleaseEvent(QMouseEvent *)
1080 {
1081     if (m_autoScrollTimer.isActive()) {
1082         m_autoScrollTimer.stop();
1083         ensureCursorVisible();
1084     }
1085 }
1086 
selectAll()1087 void BinEditorWidget::selectAll()
1088 {
1089     setCursorPosition(0);
1090     setCursorPosition(m_size-1, KeepAnchor);
1091 }
1092 
clear()1093 void BinEditorWidget::clear()
1094 {
1095     m_baseAddr = 0;
1096     m_data.clear();
1097     m_oldData.clear();
1098     m_modifiedData.clear();
1099     m_requests.clear();
1100     m_size = 0;
1101     m_addressBytes = 4;
1102 
1103     m_unmodifiedState = 0;
1104     m_undoStack.clear();
1105     m_redoStack.clear();
1106 
1107     init();
1108     m_cursorPosition = 0;
1109     verticalScrollBar()->setValue(0);
1110 
1111     emit cursorPositionChanged(m_cursorPosition);
1112     viewport()->update();
1113 }
1114 
event(QEvent * e)1115 bool BinEditorWidget::event(QEvent *e)
1116 {
1117     switch (e->type()) {
1118     case QEvent::KeyPress:
1119         switch (static_cast<QKeyEvent*>(e)->key()) {
1120         case Qt::Key_Tab:
1121         case Qt::Key_Backtab:
1122             m_hexCursor = !m_hexCursor;
1123             setBlinkingCursorEnabled(true);
1124             ensureCursorVisible();
1125             e->accept();
1126             return true;
1127         case Qt::Key_Down: {
1128             const QScrollBar * const scrollBar = verticalScrollBar();
1129             const int maximum = scrollBar->maximum();
1130             if (maximum && scrollBar->value() >= maximum - 1) {
1131                 d->requestNewRange(baseAddress() + m_size);
1132                 return true;
1133             }
1134             break;
1135         }
1136         default:;
1137         }
1138         break;
1139     case QEvent::ToolTip: {
1140         const QHelpEvent *helpEvent = static_cast<const QHelpEvent *>(e);
1141         const QString tt = toolTip(helpEvent);
1142         if (tt.isEmpty())
1143             QToolTip::hideText();
1144         else
1145             QToolTip::showText(helpEvent->globalPos(), tt, this);
1146         e->accept();
1147         return true;
1148     }
1149     default:
1150         break;
1151     }
1152 
1153     return QAbstractScrollArea::event(e);
1154 }
1155 
toolTip(const QHelpEvent * helpEvent) const1156 QString BinEditorWidget::toolTip(const QHelpEvent *helpEvent) const
1157 {
1158     qint64 selStart = selectionStart();
1159     qint64 selEnd = selectionEnd();
1160     qint64 byteCount = std::min(8LL, selEnd - selStart + 1);
1161 
1162     // check even position against selection line by line
1163     bool insideSelection = false;
1164     qint64 startInLine = selStart;
1165     do {
1166         const qint64 lineIndex = startInLine / m_bytesPerLine;
1167         const qint64 endOfLine = (lineIndex + 1) * m_bytesPerLine - 1;
1168         const qint64 endInLine = std::min(selEnd, endOfLine);
1169         const QPoint &startPoint = offsetToPos(startInLine);
1170         const QPoint &endPoint = offsetToPos(endInLine) + QPoint(m_columnWidth, 0);
1171         QRect selectionLineRect(startPoint, endPoint);
1172         selectionLineRect.setHeight(m_lineHeight);
1173         if (selectionLineRect.contains(helpEvent->pos())) {
1174             insideSelection = true;
1175             break;
1176         }
1177         startInLine = endInLine + 1;
1178     } while (startInLine <= selEnd);
1179     if (!insideSelection) {
1180         // show popup for byte under cursor
1181         Utils::optional<qint64> pos = posAt(helpEvent->pos(), /*includeEmptyArea*/false);
1182         if (!pos)
1183             return QString();
1184         selStart = pos.value();
1185         byteCount = 1;
1186     }
1187 
1188     quint64 bigEndianValue, littleEndianValue;
1189     quint64 bigEndianValueOld, littleEndianValueOld;
1190     asIntegers(selStart, byteCount, bigEndianValue, littleEndianValue);
1191     asIntegers(selStart, byteCount, bigEndianValueOld, littleEndianValueOld, true);
1192     QString littleEndianSigned;
1193     QString bigEndianSigned;
1194     QString littleEndianSignedOld;
1195     QString bigEndianSignedOld;
1196     int intSize = 0;
1197     switch (byteCount) {
1198     case 8: case 7: case 6: case 5:
1199         littleEndianSigned = QString::number(static_cast<qint64>(littleEndianValue));
1200         bigEndianSigned = QString::number(static_cast<qint64>(bigEndianValue));
1201         littleEndianSignedOld = QString::number(static_cast<qint64>(littleEndianValueOld));
1202         bigEndianSignedOld = QString::number(static_cast<qint64>(bigEndianValueOld));
1203         intSize = 8;
1204         break;
1205     case 4: case 3:
1206         littleEndianSigned = QString::number(static_cast<qint32>(littleEndianValue));
1207         bigEndianSigned = QString::number(static_cast<qint32>(bigEndianValue));
1208         littleEndianSignedOld = QString::number(static_cast<qint32>(littleEndianValueOld));
1209         bigEndianSignedOld = QString::number(static_cast<qint32>(bigEndianValueOld));
1210         intSize = 4;
1211         break;
1212     case 2:
1213         littleEndianSigned = QString::number(static_cast<qint16>(littleEndianValue));
1214         bigEndianSigned = QString::number(static_cast<qint16>(bigEndianValue));
1215         littleEndianSignedOld = QString::number(static_cast<qint16>(littleEndianValueOld));
1216         bigEndianSignedOld = QString::number(static_cast<qint16>(bigEndianValueOld));
1217         intSize = 2;
1218         break;
1219     case 1:
1220         littleEndianSigned = QString::number(static_cast<qint8>(littleEndianValue));
1221         bigEndianSigned = QString::number(static_cast<qint8>(bigEndianValue));
1222         littleEndianSignedOld = QString::number(static_cast<qint8>(littleEndianValueOld));
1223         bigEndianSignedOld = QString::number(static_cast<qint8>(bigEndianValueOld));
1224         intSize = 1;
1225         break;
1226     }
1227 
1228     const quint64 address = m_baseAddr + selStart;
1229     const char tableRowStartC[] = "<tr><td>";
1230     const char tableRowEndC[] = "</td></tr>";
1231     const char numericTableRowSepC[] = "</td><td align=\"right\">";
1232 
1233     QString msg;
1234     QTextStream str(&msg);
1235     str << "<html><head/><body><p align=\"center\"><b>"
1236         << tr("Memory at 0x%1").arg(address, 0, 16) << "</b></p>";
1237 
1238     foreach (const Markup &m, m_markup) {
1239         if (m.covers(address) && !m.toolTip.isEmpty()) {
1240             str << "<p>" <<  m.toolTip << "</p><br>";
1241             break;
1242         }
1243     }
1244     const QString msgDecimalUnsigned = tr("Decimal&nbsp;unsigned&nbsp;value:");
1245     const QString msgDecimalSigned = tr("Decimal&nbsp;signed&nbsp;value:");
1246     const QString msgOldDecimalUnsigned = tr("Previous&nbsp;decimal&nbsp;unsigned&nbsp;value:");
1247     const QString msgOldDecimalSigned = tr("Previous&nbsp;decimal&nbsp;signed&nbsp;value:");
1248 
1249     // Table showing little vs. big endian integers for multi-byte
1250     if (intSize > 1) {
1251         str << "<table><tr><th>"
1252             << tr("%1-bit&nbsp;Integer&nbsp;Type").arg(8 * intSize) << "</th><th>"
1253             << tr("Little Endian") << "</th><th>" << tr("Big Endian") << "</th></tr>";
1254         str << tableRowStartC << msgDecimalUnsigned
1255             << numericTableRowSepC << littleEndianValue << numericTableRowSepC
1256             << bigEndianValue << tableRowEndC <<  tableRowStartC << msgDecimalSigned
1257             << numericTableRowSepC << littleEndianSigned << numericTableRowSepC
1258             << bigEndianSigned << tableRowEndC;
1259         if (bigEndianValue != bigEndianValueOld) {
1260             str << tableRowStartC << msgOldDecimalUnsigned
1261                 << numericTableRowSepC << littleEndianValueOld << numericTableRowSepC
1262                 << bigEndianValueOld << tableRowEndC << tableRowStartC
1263                 << msgOldDecimalSigned << numericTableRowSepC << littleEndianSignedOld
1264                 << numericTableRowSepC << bigEndianSignedOld << tableRowEndC;
1265         }
1266         str << "</table>";
1267     }
1268 
1269     switch (byteCount) {
1270     case 1:
1271         // 1 byte: As octal, decimal, etc.
1272         str << "<table>";
1273         str << tableRowStartC << msgDecimalUnsigned << numericTableRowSepC
1274             << littleEndianValue << tableRowEndC;
1275         if (littleEndianValue & 0x80) {
1276             str << tableRowStartC << msgDecimalSigned << numericTableRowSepC
1277                 << littleEndianSigned << tableRowEndC;
1278         }
1279         str << tableRowStartC << tr("Binary&nbsp;value:") << numericTableRowSepC;
1280         str.setIntegerBase(2);
1281         str.setFieldWidth(8);
1282         str.setPadChar(QLatin1Char('0'));
1283         str << littleEndianValue;
1284         str.setFieldWidth(0);
1285         str << tableRowEndC << tableRowStartC
1286             << tr("Octal&nbsp;value:") << numericTableRowSepC;
1287         str.setIntegerBase(8);
1288         str.setFieldWidth(3);
1289         str << littleEndianValue << tableRowEndC;
1290         str.setIntegerBase(10);
1291         str.setFieldWidth(0);
1292         if (littleEndianValue != littleEndianValueOld) {
1293             str << tableRowStartC << msgOldDecimalUnsigned << numericTableRowSepC
1294                 << littleEndianValueOld << tableRowEndC;
1295             if (littleEndianValueOld & 0x80) {
1296                 str << tableRowStartC << msgOldDecimalSigned << numericTableRowSepC
1297                     << littleEndianSignedOld << tableRowEndC;
1298             }
1299             str << tableRowStartC << tr("Previous&nbsp;binary&nbsp;value:")
1300                 << numericTableRowSepC;
1301             str.setIntegerBase(2);
1302             str.setFieldWidth(8);
1303             str << littleEndianValueOld;
1304             str.setFieldWidth(0);
1305             str << tableRowEndC << tableRowStartC << tr("Previous&nbsp;octal&nbsp;value:")
1306                 << numericTableRowSepC;
1307             str.setIntegerBase(8);
1308             str.setFieldWidth(3);
1309             str << littleEndianValueOld << tableRowEndC;
1310         }
1311         str.setIntegerBase(10);
1312         str.setFieldWidth(0);
1313         str << "</table>";
1314         break;
1315     // Double value
1316     case sizeof(double): {
1317         str << "<br><table>";
1318         double doubleValue, doubleValueOld;
1319         asDouble(selStart, doubleValue, false);
1320         asDouble(selStart, doubleValueOld, true);
1321         str << tableRowStartC << tr("<i>double</i>&nbsp;value:") << numericTableRowSepC
1322             << doubleValue << tableRowEndC;
1323         if (doubleValue != doubleValueOld)
1324             str << tableRowStartC << tr("Previous <i>double</i>&nbsp;value:") << numericTableRowSepC
1325                 << doubleValueOld << tableRowEndC;
1326         str << "</table>";
1327     }
1328     break;
1329     // Float value
1330     case sizeof(float): {
1331         str << "<br><table>";
1332         float floatValue, floatValueOld;
1333         asFloat(selStart, floatValue, false);
1334         asFloat(selStart, floatValueOld, true);
1335         str << tableRowStartC << tr("<i>float</i>&nbsp;value:") << numericTableRowSepC
1336             << floatValue << tableRowEndC;
1337         if (floatValue != floatValueOld)
1338             str << tableRowStartC << tr("Previous <i>float</i>&nbsp;value:") << numericTableRowSepC
1339                 << floatValueOld << tableRowEndC;
1340 
1341         str << "</table>";
1342     }
1343     break;
1344     }
1345     str << "</body></html>";
1346     return msg;
1347 }
1348 
keyPressEvent(QKeyEvent * e)1349 void BinEditorWidget::keyPressEvent(QKeyEvent *e)
1350 {
1351 
1352     if (e == QKeySequence::SelectAll) {
1353             e->accept();
1354             selectAll();
1355             return;
1356     } else if (e == QKeySequence::Copy) {
1357         e->accept();
1358         copy();
1359         return;
1360     } else if (e == QKeySequence::Undo) {
1361         e->accept();
1362         undo();
1363         return;
1364     } else if (e == QKeySequence::Redo) {
1365         e->accept();
1366         redo();
1367         return;
1368     }
1369 
1370 
1371     MoveMode moveMode = e->modifiers() & Qt::ShiftModifier ? KeepAnchor : MoveAnchor;
1372     bool ctrlPressed = e->modifiers() & Qt::ControlModifier;
1373     switch (e->key()) {
1374     case Qt::Key_Up:
1375         if (ctrlPressed)
1376             verticalScrollBar()->triggerAction(QScrollBar::SliderSingleStepSub);
1377         else
1378             setCursorPosition(m_cursorPosition - m_bytesPerLine, moveMode);
1379         break;
1380     case Qt::Key_Down:
1381         if (ctrlPressed)
1382             verticalScrollBar()->triggerAction(QScrollBar::SliderSingleStepAdd);
1383         else
1384             setCursorPosition(m_cursorPosition + m_bytesPerLine, moveMode);
1385         break;
1386     case Qt::Key_Right:
1387         setCursorPosition(m_cursorPosition + 1, moveMode);
1388         break;
1389     case Qt::Key_Left:
1390         setCursorPosition(m_cursorPosition - 1, moveMode);
1391         break;
1392     case Qt::Key_PageUp:
1393     case Qt::Key_PageDown: {
1394         int line = qMax(qint64(0), m_cursorPosition / m_bytesPerLine - verticalScrollBar()->value());
1395         verticalScrollBar()->triggerAction(e->key() == Qt::Key_PageUp ?
1396                                            QScrollBar::SliderPageStepSub : QScrollBar::SliderPageStepAdd);
1397         if (!ctrlPressed)
1398             setCursorPosition((verticalScrollBar()->value() + line) * m_bytesPerLine + m_cursorPosition % m_bytesPerLine, moveMode);
1399     } break;
1400 
1401     case Qt::Key_Home: {
1402         int pos;
1403         if (ctrlPressed)
1404             pos = 0;
1405         else
1406             pos = m_cursorPosition/m_bytesPerLine * m_bytesPerLine;
1407         setCursorPosition(pos, moveMode);
1408     } break;
1409     case Qt::Key_End: {
1410         int pos;
1411         if (ctrlPressed)
1412             pos = m_size;
1413         else
1414             pos = m_cursorPosition/m_bytesPerLine * m_bytesPerLine + 15;
1415         setCursorPosition(pos, moveMode);
1416     } break;
1417     default:
1418         if (m_readOnly)
1419             break;
1420         {
1421         QString text = e->text();
1422         for (int i = 0; i < text.length(); ++i) {
1423             QChar c = text.at(i);
1424             if (m_hexCursor) {
1425                 c = c.toLower();
1426                 int nibble = -1;
1427                 if (c.unicode() >= 'a' && c.unicode() <= 'f')
1428                     nibble = c.unicode() - 'a' + 10;
1429                 else if (c.unicode() >= '0' && c.unicode() <= '9')
1430                     nibble = c.unicode() - '0';
1431                 if (nibble < 0)
1432                     continue;
1433                 if (m_lowNibble) {
1434                     changeData(m_cursorPosition, nibble + (dataAt(m_cursorPosition) & 0xf0));
1435                     m_lowNibble = false;
1436                     setCursorPosition(m_cursorPosition + 1);
1437                 } else {
1438                     changeData(m_cursorPosition, (nibble << 4) + (dataAt(m_cursorPosition) & 0x0f), true);
1439                     m_lowNibble = true;
1440                     updateLines();
1441                 }
1442             } else {
1443                 if (c.unicode() >= 128 || !c.isPrint())
1444                     continue;
1445                 changeData(m_cursorPosition, c.unicode(), m_cursorPosition + 1);
1446                 setCursorPosition(m_cursorPosition + 1);
1447             }
1448             setBlinkingCursorEnabled(true);
1449         }
1450     }
1451     }
1452 
1453     e->accept();
1454 }
1455 
showZoomIndicator(QWidget * editor,const int newZoom)1456 static void showZoomIndicator(QWidget *editor, const int newZoom)
1457 {
1458     Utils::FadingIndicator::showText(editor,
1459                                      QCoreApplication::translate("BinEditorWidget::TextEditorWidget",
1460                                                                  "Zoom: %1%").arg(newZoom),
1461                                      Utils::FadingIndicator::SmallText);
1462 }
1463 
zoomF(float delta)1464 void BinEditorWidget::zoomF(float delta)
1465 {
1466     float step = 10.f * delta;
1467     // Ensure we always zoom a minimal step in-case the resolution is more than 16x
1468     if (step > 0 && step < 1)
1469         step = 1;
1470     else if (step < 0 && step > -1)
1471         step = -1;
1472 
1473     const int newZoom = TextEditor::TextEditorSettings::increaseFontZoom(int(step));
1474     showZoomIndicator(this, newZoom);
1475 }
1476 
copy(bool raw)1477 void BinEditorWidget::copy(bool raw)
1478 {
1479     int selStart = selectionStart();
1480     int selEnd = selectionEnd();
1481     const int selectionLength = selEnd - selStart + 1;
1482     if (selectionLength >> 22) {
1483         QMessageBox::warning(this, tr("Copying Failed"),
1484                              tr("You cannot copy more than 4 MB of binary data."));
1485         return;
1486     }
1487     QByteArray data = dataMid(selStart, selectionLength);
1488     if (raw) {
1489         data.replace(0, ' ');
1490         QApplication::clipboard()->setText(QString::fromLatin1(data));
1491         return;
1492     }
1493     QString hexString;
1494     const char * const hex = "0123456789abcdef";
1495     hexString.reserve(3 * data.size());
1496     for (int i = 0; i < data.size(); ++i) {
1497         const uchar val = static_cast<uchar>(data[i]);
1498         hexString.append(QLatin1Char(hex[val >> 4])).append(QLatin1Char(hex[val & 0xf])).append(QLatin1Char(' '));
1499     }
1500     hexString.chop(1);
1501     QApplication::clipboard()->setText(hexString);
1502 }
1503 
highlightSearchResults(const QByteArray & pattern,QTextDocument::FindFlags findFlags)1504 void BinEditorWidget::highlightSearchResults(const QByteArray &pattern, QTextDocument::FindFlags findFlags)
1505 {
1506     if (m_searchPattern == pattern)
1507         return;
1508     m_searchPattern = pattern;
1509     m_caseSensitiveSearch = (findFlags & QTextDocument::FindCaseSensitively);
1510     if (!m_caseSensitiveSearch)
1511         m_searchPattern = m_searchPattern.toLower();
1512     m_searchPatternHex = calculateHexPattern(pattern);
1513     viewport()->update();
1514 }
1515 
changeData(int position,uchar character,bool highNibble)1516 void BinEditorWidget::changeData(int position, uchar character, bool highNibble)
1517 {
1518     if (!requestDataAt(position))
1519         return;
1520     m_redoStack.clear();
1521     if (m_unmodifiedState > m_undoStack.size())
1522         m_unmodifiedState = -1;
1523     BinEditorEditCommand cmd;
1524     cmd.position = position;
1525     cmd.character = (uchar) dataAt(position);
1526     cmd.highNibble = highNibble;
1527 
1528     if (!highNibble
1529             && !m_undoStack.isEmpty()
1530             && m_undoStack.top().position == position
1531             && m_undoStack.top().highNibble) {
1532         // compress
1533         cmd.character = m_undoStack.top().character;
1534         m_undoStack.pop();
1535     }
1536 
1537     changeDataAt(position, (char) character);
1538     bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState);
1539     m_undoStack.push(cmd);
1540     if (emitModificationChanged)
1541         emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
1542 
1543     if (m_undoStack.size() == 1)
1544         emit undoAvailable(true);
1545 }
1546 
1547 
undo()1548 void BinEditorWidget::undo()
1549 {
1550     if (m_undoStack.isEmpty())
1551         return;
1552     bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState);
1553     BinEditorEditCommand cmd = m_undoStack.pop();
1554     emitModificationChanged |= (m_undoStack.size() == m_unmodifiedState);
1555     uchar c = dataAt(cmd.position);
1556     changeDataAt(cmd.position, (char)cmd.character);
1557     cmd.character = c;
1558     m_redoStack.push(cmd);
1559     setCursorPosition(cmd.position);
1560     if (emitModificationChanged)
1561         emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
1562     if (m_undoStack.isEmpty())
1563         emit undoAvailable(false);
1564     if (m_redoStack.size() == 1)
1565         emit redoAvailable(true);
1566 }
1567 
redo()1568 void BinEditorWidget::redo()
1569 {
1570     if (m_redoStack.isEmpty())
1571         return;
1572     BinEditorEditCommand cmd = m_redoStack.pop();
1573     uchar c = dataAt(cmd.position);
1574     changeDataAt(cmd.position, (char)cmd.character);
1575     cmd.character = c;
1576     bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState);
1577     m_undoStack.push(cmd);
1578     setCursorPosition(cmd.position + 1);
1579     if (emitModificationChanged)
1580         emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
1581     if (m_undoStack.size() == 1)
1582         emit undoAvailable(true);
1583     if (m_redoStack.isEmpty())
1584         emit redoAvailable(false);
1585 }
1586 
contextMenuEvent(QContextMenuEvent * event)1587 void BinEditorWidget::contextMenuEvent(QContextMenuEvent *event)
1588 {
1589     const int selStart = selectionStart();
1590     const int byteCount = selectionEnd() - selStart + 1;
1591 
1592     QPointer<QMenu> contextMenu(new QMenu(this));
1593 
1594     auto copyAsciiAction = new QAction(tr("Copy Selection as ASCII Characters"), contextMenu);
1595     auto copyHexAction = new QAction(tr("Copy Selection as Hex Values"), contextMenu);
1596     auto copyBeValue = new QAction(contextMenu);
1597     auto copyLeValue = new QAction(contextMenu);
1598     auto jumpToBeAddressHereAction = new QAction(contextMenu);
1599     auto jumpToBeAddressNewWindowAction = new QAction(contextMenu);
1600     auto jumpToLeAddressHereAction = new QAction(contextMenu);
1601     auto jumpToLeAddressNewWindowAction = new QAction(contextMenu);
1602     auto addWatchpointAction = new QAction(tr("Set Data Breakpoint on Selection"), contextMenu);
1603     contextMenu->addAction(copyAsciiAction);
1604     contextMenu->addAction(copyHexAction);
1605     contextMenu->addAction(addWatchpointAction);
1606 
1607     addWatchpointAction->setEnabled(byteCount > 0 && byteCount <= 32);
1608 
1609     quint64 beAddress = 0;
1610     quint64 leAddress = 0;
1611     if (byteCount <= 8) {
1612         asIntegers(selStart, byteCount, beAddress, leAddress);
1613         copyBeValue->setText(tr("Copy 0x%1").arg(QString::number(beAddress, 16)));
1614         contextMenu->addAction(copyBeValue);
1615         // If the menu entries would be identical, show only one of them.
1616         if (beAddress != leAddress) {
1617             copyLeValue->setText(tr("Copy 0x%1").arg(QString::number(leAddress, 16)));
1618             contextMenu->addAction(copyLeValue);
1619         }
1620         setupJumpToMenuAction(contextMenu, jumpToBeAddressHereAction,
1621                               jumpToBeAddressNewWindowAction, beAddress);
1622 
1623         if (beAddress != leAddress) {
1624             setupJumpToMenuAction(contextMenu, jumpToLeAddressHereAction,
1625                                   jumpToLeAddressNewWindowAction, leAddress);
1626         }
1627     } else {
1628         jumpToBeAddressHereAction->setText(tr("Jump to Address in This Window"));
1629         jumpToBeAddressNewWindowAction->setText(tr("Jump to Address in New Window"));
1630         copyBeValue->setText(tr("Copy Value"));
1631         jumpToBeAddressHereAction->setEnabled(false);
1632         jumpToBeAddressNewWindowAction->setEnabled(false);
1633         copyBeValue->setEnabled(false);
1634         contextMenu->addAction(copyBeValue);
1635         contextMenu->addAction(jumpToBeAddressHereAction);
1636         contextMenu->addAction(jumpToBeAddressNewWindowAction);
1637     }
1638 
1639     QAction *action = contextMenu->exec(event->globalPos());
1640     if (!contextMenu)
1641         return;
1642 
1643     if (action == copyAsciiAction)
1644         copy(true);
1645     else if (action == copyHexAction)
1646         copy(false);
1647     else if (action == copyBeValue)
1648         QApplication::clipboard()->setText("0x" + QString::number(beAddress, 16));
1649     else if (action == copyLeValue)
1650         QApplication::clipboard()->setText("0x" + QString::number(leAddress, 16));
1651     else if (action == jumpToBeAddressHereAction)
1652         jumpToAddress(beAddress);
1653     else if (action == jumpToLeAddressHereAction)
1654         jumpToAddress(leAddress);
1655     else if (action == jumpToBeAddressNewWindowAction)
1656         d->requestNewWindow(beAddress);
1657     else if (action == jumpToLeAddressNewWindowAction)
1658         d->requestNewWindow(leAddress);
1659     else if (action == addWatchpointAction)
1660         d->requestWatchPoint(m_baseAddr + selStart, byteCount);
1661     delete contextMenu;
1662 }
1663 
setupJumpToMenuAction(QMenu * menu,QAction * actionHere,QAction * actionNew,quint64 addr)1664 void BinEditorWidget::setupJumpToMenuAction(QMenu *menu, QAction *actionHere,
1665                                       QAction *actionNew, quint64 addr)
1666 {
1667     actionHere->setText(tr("Jump to Address 0x%1 in This Window")
1668                         .arg(QString::number(addr, 16)));
1669     actionNew->setText(tr("Jump to Address 0x%1 in New Window")
1670                         .arg(QString::number(addr, 16)));
1671     menu->addAction(actionHere);
1672     menu->addAction(actionNew);
1673     if (!m_canRequestNewWindow)
1674         actionNew->setEnabled(false);
1675 }
1676 
jumpToAddress(quint64 address)1677 void BinEditorWidget::jumpToAddress(quint64 address)
1678 {
1679     if (address >= m_baseAddr && address < m_baseAddr + m_size)
1680         setCursorPosition(address - m_baseAddr);
1681     else
1682         d->requestNewRange(address);
1683 }
1684 
setNewWindowRequestAllowed(bool c)1685 void BinEditorWidget::setNewWindowRequestAllowed(bool c)
1686 {
1687     m_canRequestNewWindow = c;
1688 }
1689 
updateContents()1690 void BinEditorWidget::updateContents()
1691 {
1692     m_oldData = m_data;
1693     m_data.clear();
1694     m_modifiedData.clear();
1695     m_requests.clear();
1696     for (auto it = m_oldData.constBegin(), et = m_oldData.constEnd(); it != et; ++it)
1697         d->fetchData(m_baseAddr + it.key());
1698 }
1699 
offsetToPos(qint64 offset) const1700 QPoint BinEditorWidget::offsetToPos(qint64 offset) const
1701 {
1702     const int x = m_labelWidth + (offset % m_bytesPerLine) * m_columnWidth;
1703     const int y = (offset / m_bytesPerLine  - verticalScrollBar()->value()) * m_lineHeight;
1704     return QPoint(x, y);
1705 }
1706 
asFloat(qint64 offset,float & value,bool old) const1707 void BinEditorWidget::asFloat(qint64 offset, float &value, bool old) const
1708 {
1709     value = 0;
1710     const QByteArray data = dataMid(offset, sizeof(float), old);
1711     QTC_ASSERT(data.size() ==  sizeof(float), return);
1712     const float *f = reinterpret_cast<const float *>(data.constData());
1713     value = *f;
1714 }
1715 
asDouble(qint64 offset,double & value,bool old) const1716 void BinEditorWidget::asDouble(qint64 offset, double &value, bool old) const
1717 {
1718     value = 0;
1719     const QByteArray data = dataMid(offset, sizeof(double), old);
1720     QTC_ASSERT(data.size() ==  sizeof(double), return);
1721     const double *f = reinterpret_cast<const double *>(data.constData());
1722     value = *f;
1723 }
1724 
asIntegers(qint64 offset,int count,quint64 & bigEndianValue,quint64 & littleEndianValue,bool old) const1725 void BinEditorWidget::asIntegers(qint64 offset, int count, quint64 &bigEndianValue,
1726     quint64 &littleEndianValue, bool old) const
1727 {
1728     bigEndianValue = littleEndianValue = 0;
1729     const QByteArray &data = dataMid(offset, count, old);
1730     for (int pos = 0; pos < data.size(); ++pos) {
1731         const quint64 val = static_cast<quint64>(data.at(pos)) & 0xff;
1732         littleEndianValue += val << (pos * 8);
1733         bigEndianValue += val << ((count - pos - 1) * 8);
1734     }
1735 }
1736 
setMarkup(const QList<Markup> & markup)1737 void BinEditorWidget::setMarkup(const QList<Markup> &markup)
1738 {
1739     m_markup = markup;
1740     viewport()->update();
1741 }
1742 
1743 } // namespace Internal
1744 } // namespace BinEditor
1745