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 unsigned value:");
1245 const QString msgDecimalSigned = tr("Decimal signed value:");
1246 const QString msgOldDecimalUnsigned = tr("Previous decimal unsigned value:");
1247 const QString msgOldDecimalSigned = tr("Previous decimal signed 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 Integer 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 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 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 binary value:")
1300 << numericTableRowSepC;
1301 str.setIntegerBase(2);
1302 str.setFieldWidth(8);
1303 str << littleEndianValueOld;
1304 str.setFieldWidth(0);
1305 str << tableRowEndC << tableRowStartC << tr("Previous octal 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> value:") << numericTableRowSepC
1322 << doubleValue << tableRowEndC;
1323 if (doubleValue != doubleValueOld)
1324 str << tableRowStartC << tr("Previous <i>double</i> 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> value:") << numericTableRowSepC
1336 << floatValue << tableRowEndC;
1337 if (floatValue != floatValueOld)
1338 str << tableRowStartC << tr("Previous <i>float</i> 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