bineditor.cpp 54.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
hjk's avatar
hjk committed
3
4
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
31
32
33
#include "bineditor.h"

#include <texteditor/fontsettings.h>
#include <texteditor/texteditorconstants.h>
34
#include <coreplugin/editormanager/ieditor.h>
35
#include <utils/fileutils.h>
36
#include <utils/qtcassert.h>
con's avatar
con committed
37

38
39
40
41
42
#include <QByteArrayMatcher>
#include <QDebug>
#include <QFile>
#include <QTemporaryFile>
#include <QVariant>
ck's avatar
ck committed
43

44
45
46
47
48
49
50
51
52
53
54
#include <QApplication>
#include <QAction>
#include <QClipboard>
#include <QFontMetrics>
#include <QHelpEvent>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QScrollBar>
#include <QToolTip>
#include <QWheelEvent>
con's avatar
con committed
55

56
57
58
59
60
61
62
63
64
65
66
67
// QByteArray::toLower() is broken, it stops at the first \0
static void lower(QByteArray &ba)
{
    char *data = ba.data();
    char *end = data + ba.size();
    while (data != end) {
        if (*data >= 0x41 && *data <= 0x5A)
            *data += 0x20;
        ++data;
    }
}

con's avatar
con committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
static QByteArray calculateHexPattern(const QByteArray &pattern)
{
    QByteArray result;
    if (pattern.size() % 2 == 0) {
        bool ok = true;
        int i = 0;
        while (i < pattern.size()) {
            ushort s = pattern.mid(i, 2).toUShort(&ok, 16);
            if (!ok) {
                return QByteArray();
            }
            result.append(s);
            i += 2;
        }
    }
    return result;
}

hjk's avatar
hjk committed
86
87
namespace BINEditor {

con's avatar
con committed
88
89
90
BinEditor::BinEditor(QWidget *parent)
    : QAbstractScrollArea(parent)
{
hjk's avatar
hjk committed
91
    m_bytesPerLine = 16;
con's avatar
con committed
92
    m_ieditor = 0;
ck's avatar
ck committed
93
    m_baseAddr = 0;
94
    m_blockSize = 4096;
con's avatar
con committed
95
    m_size = 0;
96
    m_addressBytes = 4;
con's avatar
con committed
97
98
    init();
    m_unmodifiedState = 0;
99
    m_readOnly = false;
con's avatar
con committed
100
101
102
103
104
    m_hexCursor = true;
    m_cursorPosition = 0;
    m_anchorPosition = 0;
    m_lowNibble = false;
    m_cursorVisible = false;
105
    m_caseSensitiveSearch = false;
106
    m_canRequestNewWindow = false;
con's avatar
con committed
107
    setFocusPolicy(Qt::WheelFocus);
hjk's avatar
hjk committed
108
    setFrameStyle(QFrame::Plain);
con's avatar
con committed
109
110
111
112
113
114
115
116
}

BinEditor::~BinEditor()
{
}

void BinEditor::init()
{
117
118
119
    const int addressStringWidth =
        2*m_addressBytes + (m_addressBytes - 1) / 2;
    m_addressString = QString(addressStringWidth, QLatin1Char(':'));
con's avatar
con committed
120
121
122
123
124
125
126
    QFontMetrics fm(fontMetrics());
    m_margin = 4;
    m_descent = fm.descent();
    m_ascent = fm.ascent();
    m_lineHeight = fm.lineSpacing();
    m_charWidth = fm.width(QChar(QLatin1Char('M')));
    m_columnWidth = 2 * m_charWidth + fm.width(QChar(QLatin1Char(' ')));
hjk's avatar
hjk committed
127
    m_numLines = m_size / m_bytesPerLine + 1;
con's avatar
con committed
128
    m_numVisibleLines = viewport()->height() / m_lineHeight;
hjk's avatar
hjk committed
129
    m_textWidth = m_bytesPerLine * m_charWidth + m_charWidth;
con's avatar
con committed
130
    int m_numberWidth = fm.width(QChar(QLatin1Char('9')));
131
132
    m_labelWidth =
        2*m_addressBytes * m_numberWidth + (m_addressBytes - 1)/2 * m_charWidth;
con's avatar
con committed
133
134
135
136
137
138
139
140
141
142
143
144

    int expectedCharWidth = m_columnWidth / 3;
    const char *hex = "0123456789abcdef";
    m_isMonospacedFont = true;
    while (*hex) {
        if (fm.width(QLatin1Char(*hex)) != expectedCharWidth) {
            m_isMonospacedFont = false;
            break;
        }
        ++hex;
    }

145
    if (m_isMonospacedFont && fm.width(QLatin1String("M M ")) != m_charWidth * 4) {
146
147
148
149
        // On Qt/Mac, monospace font widths may have a fractional component
        // This breaks the assumption that width("MMM") == width('M') * 3

        m_isMonospacedFont = false;
150
        m_columnWidth = fm.width(QLatin1String("MMM"));
151
        m_labelWidth = m_addressBytes == 4
152
153
            ? fm.width(QLatin1String("MMMM:MMMM"))
            : fm.width(QLatin1String("MMMM:MMMM:MMMM:MMMM"));
154
155
    }

hjk's avatar
hjk committed
156
    horizontalScrollBar()->setRange(0, 2 * m_margin + m_bytesPerLine * m_columnWidth
con's avatar
con committed
157
158
159
160
                                    + m_labelWidth + m_textWidth - viewport()->width());
    horizontalScrollBar()->setPageStep(viewport()->width());
    verticalScrollBar()->setRange(0, m_numLines - m_numVisibleLines);
    verticalScrollBar()->setPageStep(m_numVisibleLines);
161
    ensureCursorVisible();
con's avatar
con committed
162
163
164
}


hjk's avatar
hjk committed
165
void BinEditor::addData(quint64 block, const QByteArray &data)
166
167
{
    Q_ASSERT(data.size() == m_blockSize);
ck's avatar
ck committed
168
169
    const quint64 addr = block * m_blockSize;
    if (addr >= m_baseAddr && addr <= m_baseAddr + m_size - 1) {
hjk's avatar
hjk committed
170
171
        if (m_data.size() * m_blockSize >= 64 * 1024 * 1024)
            m_data.clear();
ck's avatar
ck committed
172
        const int translatedBlock = (addr - m_baseAddr) / m_blockSize;
hjk's avatar
hjk committed
173
174
        m_data.insert(translatedBlock, data);
        m_requests.remove(translatedBlock);
ck's avatar
ck committed
175
176
        viewport()->update();
    }
177
178
}

hjk's avatar
hjk committed
179
bool BinEditor::requestDataAt(int pos) const
180
181
{
    int block = pos / m_blockSize;
hjk's avatar
hjk committed
182
    BlockMap::const_iterator it = m_modifiedData.find(block);
ck's avatar
ck committed
183
184
    if (it != m_modifiedData.constEnd())
        return true;
hjk's avatar
hjk committed
185
186
    it = m_data.find(block);
    if (it != m_data.end())
187
        return true;
hjk's avatar
hjk committed
188
189
    if (!m_requests.contains(block)) {
        m_requests.insert(block);
190
        emit const_cast<BinEditor*>(this)->
hjk's avatar
hjk committed
191
192
            dataRequested(editor(), m_baseAddr / m_blockSize + block);
        return true;
193
    }
194
195
    return false;
}
196

197
198
199
bool BinEditor::requestOldDataAt(int pos) const
{
    int block = pos / m_blockSize;
hjk's avatar
hjk committed
200
201
    BlockMap::const_iterator it = m_oldData.find(block);
    return it != m_oldData.end();
202
203
}

204
char BinEditor::dataAt(int pos, bool old) const
205
206
{
    int block = pos / m_blockSize;
hjk's avatar
hjk committed
207
    return blockData(block, old).at(pos - block*m_blockSize);
208
209
210
211
212
}

void BinEditor::changeDataAt(int pos, char c)
{
    int block = pos / m_blockSize;
hjk's avatar
hjk committed
213
    BlockMap::iterator it = m_modifiedData.find(block);
ck's avatar
ck committed
214
215
216
    if (it != m_modifiedData.end()) {
        it.value()[pos - (block*m_blockSize)] = c;
    } else {
hjk's avatar
hjk committed
217
218
        it = m_data.find(block);
        if (it != m_data.end()) {
ck's avatar
ck committed
219
220
221
222
223
            QByteArray data = it.value();
            data[pos - (block*m_blockSize)] = c;
            m_modifiedData.insert(block, data);
        }
    }
hjk's avatar
hjk committed
224
225

    emit dataChanged(editor(), m_baseAddr + pos, QByteArray(1, c));
226
227
}

228
QByteArray BinEditor::dataMid(int from, int length, bool old) const
229
{
230
231
232
233
    int end = from + length;
    int block = from / m_blockSize;

    QByteArray data;
234
    data.reserve(length);
235
    do {
236
        data += blockData(block++, old);
237
238
239
    } while (block * m_blockSize < end);

    return data.mid(from - ((from / m_blockSize) * m_blockSize), length);
240
241
}

242
QByteArray BinEditor::blockData(int block, bool old) const
243
{
244
    if (old) {
hjk's avatar
hjk committed
245
        BlockMap::const_iterator it = m_modifiedData.find(block);
246
        return it != m_modifiedData.constEnd()
hjk's avatar
hjk committed
247
                ? it.value() : m_oldData.value(block, m_emptyBlock);
248
    }
hjk's avatar
hjk committed
249
    BlockMap::const_iterator it = m_modifiedData.find(block);
ck's avatar
ck committed
250
    return it != m_modifiedData.constEnd()
hjk's avatar
hjk committed
251
            ? it.value() : m_data.value(block, m_emptyBlock);
252
253
}

con's avatar
con committed
254
255
void BinEditor::setFontSettings(const TextEditor::FontSettings &fs)
{
256
    setFont(fs.toTextCharFormat(TextEditor::C_TEXT).font());
con's avatar
con committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
}

void BinEditor::setBlinkingCursorEnabled(bool enable)
{
    if (enable && QApplication::cursorFlashTime() > 0)
        m_cursorBlinkTimer.start(QApplication::cursorFlashTime() / 2, this);
    else
        m_cursorBlinkTimer.stop();
    m_cursorVisible = enable;
    updateLines();
}

void BinEditor::focusInEvent(QFocusEvent *)
{
    setBlinkingCursorEnabled(true);
}

void BinEditor::focusOutEvent(QFocusEvent *)
{
    setBlinkingCursorEnabled(false);
}

void BinEditor::timerEvent(QTimerEvent *e)
{
    if (e->timerId() == m_autoScrollTimer.timerId()) {
        QRect visible = viewport()->rect();
        QPoint pos;
        const QPoint globalPos = QCursor::pos();
        pos = viewport()->mapFromGlobal(globalPos);
hjk's avatar
hjk committed
286
287
        QMouseEvent ev(QEvent::MouseMove, pos, globalPos,
            Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
con's avatar
con committed
288
        mouseMoveEvent(&ev);
hjk's avatar
hjk committed
289
290
291
292
        int deltaY = qMax(pos.y() - visible.top(),
                          visible.bottom() - pos.y()) - visible.height();
        int deltaX = qMax(pos.x() - visible.left(),
                          visible.right() - pos.x()) - visible.width();
con's avatar
con committed
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
        int delta = qMax(deltaX, deltaY);
        if (delta >= 0) {
            if (delta < 7)
                delta = 7;
            int timeout = 4900 / (delta * delta);
            m_autoScrollTimer.start(timeout, this);

            if (deltaY > 0)
                verticalScrollBar()->triggerAction(pos.y() < visible.center().y() ?
                                       QAbstractSlider::SliderSingleStepSub
                                       : QAbstractSlider::SliderSingleStepAdd);
            if (deltaX > 0)
                horizontalScrollBar()->triggerAction(pos.x() < visible.center().x() ?
                                       QAbstractSlider::SliderSingleStepSub
                                       : QAbstractSlider::SliderSingleStepAdd);
        }
    } else if (e->timerId() == m_cursorBlinkTimer.timerId()) {
        m_cursorVisible = !m_cursorVisible;
        updateLines();
    }
    QAbstractScrollArea::timerEvent(e);
}


void BinEditor::setModified(bool modified)
{
    int unmodifiedState = modified ? -1 : m_undoStack.size();
    if (unmodifiedState == m_unmodifiedState)
        return;
    m_unmodifiedState = unmodifiedState;
    emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
}

bool BinEditor::isModified() const
{
    return (m_undoStack.size() != m_unmodifiedState);
}

331
332
333
334
335
336
337
338
339
340
void BinEditor::setReadOnly(bool readOnly)
{
    m_readOnly = readOnly;
}

bool BinEditor::isReadOnly() const
{
    return m_readOnly;
}

341
bool BinEditor::save(QString *errorString, const QString &oldFileName, const QString &newFileName)
342
{
hjk's avatar
hjk committed
343
344
345
    if (oldFileName != newFileName) {
        QString tmpName;
        {
346
            QTemporaryFile tmp(newFileName + QLatin1String("_XXXXXX.new"));
hjk's avatar
hjk committed
347
            if (!tmp.open())
ck's avatar
ck committed
348
                return false;
hjk's avatar
hjk committed
349
            tmpName = tmp.fileName();
ck's avatar
ck committed
350
        }
hjk's avatar
hjk committed
351
        if (!QFile::copy(oldFileName, tmpName))
ck's avatar
ck committed
352
            return false;
hjk's avatar
hjk committed
353
        if (QFile::exists(newFileName) && !QFile::remove(newFileName))
ck's avatar
ck committed
354
            return false;
hjk's avatar
hjk committed
355
        if (!QFile::rename(tmpName, newFileName))
ck's avatar
ck committed
356
            return false;
hjk's avatar
hjk committed
357
    }
358
359
360
361
362
363
364
365
366
367
368
369
370
    Utils::FileSaver saver(newFileName, QIODevice::ReadWrite); // QtBug: WriteOnly truncates.
    if (!saver.hasError()) {
        QFile *output = saver.file();
        const qint64 size = output->size();
        for (BlockMap::const_iterator it = m_modifiedData.constBegin();
            it != m_modifiedData.constEnd(); ++it) {
            if (!saver.setResult(output->seek(it.key() * m_blockSize)))
                break;
            if (!saver.write(it.value()))
                break;
            if (!saver.setResult(output->flush()))
                break;
        }
hjk's avatar
hjk committed
371

372
373
374
375
376
377
        // We may have padded the displayed data, so we have to make sure
        // changes to that area are not actually written back to disk.
        if (!saver.hasError())
            saver.setResult(output->resize(size));
    }
    if (!saver.finalize(errorString))
hjk's avatar
hjk committed
378
379
        return false;

ck's avatar
ck committed
380
    setModified(false);
381
382
383
    return true;
}

384
void BinEditor::setSizes(quint64 startAddr, int range, int blockSize)
385
{
Daniel Teske's avatar
Daniel Teske committed
386
    int newBlockSize = blockSize;
hjk's avatar
hjk committed
387
388
    QTC_ASSERT((blockSize/m_bytesPerLine) * m_bytesPerLine == blockSize,
               blockSize = (blockSize/m_bytesPerLine + 1) * m_bytesPerLine);
hjk's avatar
hjk committed
389
    // Users can edit data in the range
ck's avatar
ck committed
390
    // [startAddr - range/2, startAddr + range/2].
Daniel Teske's avatar
Daniel Teske committed
391
392
    quint64 newBaseAddr = quint64(range/2) > startAddr ? 0 : startAddr - range/2;
    newBaseAddr = (newBaseAddr / blockSize) * blockSize;
hjk's avatar
hjk committed
393

Daniel Teske's avatar
Daniel Teske committed
394
395
    const quint64 maxRange = Q_UINT64_C(0xffffffffffffffff) - newBaseAddr + 1;
    int newSize = newBaseAddr != 0 && quint64(range) >= maxRange
hjk's avatar
hjk committed
396
              ? maxRange : range;
Daniel Teske's avatar
Daniel Teske committed
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
    int newAddressBytes = (newBaseAddr + newSize < quint64(1) << 32
                   && newBaseAddr + newSize >= newBaseAddr) ? 4 : 8;



    if (newBlockSize == m_blockSize
            && newBaseAddr == m_baseAddr
            && newSize == m_size
            && newAddressBytes == m_addressBytes)
        return;

    m_blockSize = blockSize;
    m_emptyBlock = QByteArray(blockSize, '\0');
    m_modifiedData.clear();
    m_requests.clear();

    m_baseAddr = newBaseAddr;
    m_size = newSize;
    m_addressBytes = newAddressBytes;
416
417
418
419
420
421

    m_unmodifiedState = 0;
    m_undoStack.clear();
    m_redoStack.clear();
    init();

422
    setCursorPosition(startAddr - m_baseAddr);
423
    viewport()->update();
424
425
}

con's avatar
con committed
426
427
428
429
430
431
432
433
void BinEditor::resizeEvent(QResizeEvent *)
{
    init();
}

void BinEditor::scrollContentsBy(int dx, int dy)
{
    viewport()->scroll(isRightToLeft() ? -dx : dx, dy * m_lineHeight);
hjk's avatar
hjk committed
434
435
436
437
438
439
    const QScrollBar * const scrollBar = verticalScrollBar();
    const int scrollPos = scrollBar->value();
    if (dy <= 0 && scrollPos == scrollBar->maximum())
        emit newRangeRequested(editor(), baseAddress() + m_size);
    else if (dy >= 0 && scrollPos == scrollBar->minimum())
        emit newRangeRequested(editor(), baseAddress());
con's avatar
con committed
440
441
442
443
444
}

void BinEditor::changeEvent(QEvent *e)
{
    QAbstractScrollArea::changeEvent(e);
hjk's avatar
hjk committed
445
    if (e->type() == QEvent::ActivationChange) {
con's avatar
con committed
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
        if (!isActiveWindow())
            m_autoScrollTimer.stop();
    }
    init();
    viewport()->update();
}


void BinEditor::wheelEvent(QWheelEvent *e)
{
    if (e->modifiers() & Qt::ControlModifier) {
        const int delta = e->delta();
        if (delta < 0)
            zoomOut();
        else if (delta > 0)
            zoomIn();
        return;
    }
    QAbstractScrollArea::wheelEvent(e);
}



QRect BinEditor::cursorRect() const
{
    int topLine = verticalScrollBar()->value();
hjk's avatar
hjk committed
472
    int line = m_cursorPosition / m_bytesPerLine;
con's avatar
con committed
473
474
    int y = (line - topLine) * m_lineHeight;
    int xoffset = horizontalScrollBar()->value();
hjk's avatar
hjk committed
475
    int column = m_cursorPosition % m_bytesPerLine;
hjk's avatar
hjk committed
476
477
    int x = m_hexCursor
            ? (-xoffset + m_margin + m_labelWidth + column * m_columnWidth)
hjk's avatar
hjk committed
478
            : (-xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth
hjk's avatar
hjk committed
479
               + m_charWidth + column * m_charWidth);
con's avatar
con committed
480
481
482
483
484
485
486
487
488
489
490
491
492
    int w = m_hexCursor ? m_columnWidth : m_charWidth;
    return QRect(x, y, w, m_lineHeight);
}

int BinEditor::posAt(const QPoint &pos) const
{
    int xoffset = horizontalScrollBar()->value();
    int x = xoffset + pos.x() - m_margin - m_labelWidth;
    int column = qMin(15, qMax(0,x) / m_columnWidth);
    int topLine = verticalScrollBar()->value();
    int line = pos.y() / m_lineHeight;


hjk's avatar
hjk committed
493
494
    if (x > m_bytesPerLine * m_columnWidth + m_charWidth/2) {
        x -= m_bytesPerLine * m_columnWidth + m_charWidth;
con's avatar
con committed
495
        for (column = 0; column < 15; ++column) {
hjk's avatar
hjk committed
496
            int dataPos = (topLine + line) * m_bytesPerLine + column;
497
            if (dataPos < 0 || dataPos >= m_size)
con's avatar
con committed
498
                break;
499
            QChar qc(QLatin1Char(dataAt(dataPos)));
con's avatar
con committed
500
501
502
503
504
505
506
507
            if (!qc.isPrint())
                qc = 0xB7;
            x -= fontMetrics().width(qc);
            if (x <= 0)
                break;
        }
    }

hjk's avatar
hjk committed
508
    return qMin(m_size, qMin(m_numLines, topLine + line) * m_bytesPerLine) + column;
con's avatar
con committed
509
510
511
512
513
514
}

bool BinEditor::inTextArea(const QPoint &pos) const
{
    int xoffset = horizontalScrollBar()->value();
    int x = xoffset + pos.x() - m_margin - m_labelWidth;
hjk's avatar
hjk committed
515
    return (x > m_bytesPerLine * m_columnWidth + m_charWidth/2);
con's avatar
con committed
516
517
}

hjk's avatar
hjk committed
518
519
520
521
void BinEditor::updateLines()
{
    updateLines(m_cursorPosition, m_cursorPosition);
}
con's avatar
con committed
522
523
524
525

void BinEditor::updateLines(int fromPosition, int toPosition)
{
    int topLine = verticalScrollBar()->value();
hjk's avatar
hjk committed
526
527
    int firstLine = qMin(fromPosition, toPosition) / m_bytesPerLine;
    int lastLine = qMax(fromPosition, toPosition) / m_bytesPerLine;
con's avatar
con committed
528
529
530
531
532
533
    int y = (firstLine - topLine) * m_lineHeight;
    int h = (lastLine - firstLine + 1 ) * m_lineHeight;

    viewport()->update(0, y, viewport()->width(), h);
}

534
int BinEditor::dataIndexOf(const QByteArray &pattern, int from, bool caseSensitive) const
con's avatar
con committed
535
{
536
537
538
539
    int trailing = pattern.size();
    if (trailing > m_blockSize)
        return -1;

con's avatar
con committed
540
541
    QByteArray buffer;
    buffer.resize(m_blockSize + trailing);
542
543
544
545
    char *b = buffer.data();
    QByteArrayMatcher matcher(pattern);

    int block = from / m_blockSize;
ck's avatar
ck committed
546
547
548
    const int end =
        qMin<qint64>(static_cast<qint64>(from) + SearchStride, m_size);
    while (from < end) {
hjk's avatar
hjk committed
549
        if (!requestDataAt(block * m_blockSize))
550
551
552
553
554
555
556
557
558
559
560
561
562
563
            return -1;
        QByteArray data = blockData(block);
        ::memcpy(b, b + m_blockSize, trailing);
        ::memcpy(b + trailing, data.constData(), m_blockSize);

        if (!caseSensitive)
            ::lower(buffer);

        int pos = matcher.indexIn(buffer, from - (block * m_blockSize) + trailing);
        if (pos >= 0)
            return pos + block * m_blockSize - trailing;
        ++block;
        from = block * m_blockSize - trailing;
    }
ck's avatar
ck committed
564
    return end == m_size ? -1 : -2;
565
566
567
568
569
570
571
572
}

int BinEditor::dataLastIndexOf(const QByteArray &pattern, int from, bool caseSensitive) const
{
    int trailing = pattern.size();
    if (trailing > m_blockSize)
        return -1;

mae's avatar
mae committed
573
574
    QByteArray buffer;
    buffer.resize(m_blockSize + trailing);
575
576
577
    char *b = buffer.data();

    int block = from / m_blockSize;
ck's avatar
ck committed
578
579
    const int lowerBound = qMax(0, from - SearchStride);
    while (from > lowerBound) {
hjk's avatar
hjk committed
580
        if (!requestDataAt(block * m_blockSize))
581
582
583
584
585
586
587
588
589
590
591
592
593
594
            return -1;
        QByteArray data = blockData(block);
        ::memcpy(b + m_blockSize, b, trailing);
        ::memcpy(b, data.constData(), m_blockSize);

        if (!caseSensitive)
            ::lower(buffer);

        int pos = buffer.lastIndexOf(pattern, from - (block * m_blockSize));
        if (pos >= 0)
            return pos + block * m_blockSize;
        --block;
        from = block * m_blockSize + (m_blockSize-1) + trailing;
    }
ck's avatar
ck committed
595
    return lowerBound == 0 ? -1 : -2;
596
597
598
}


ck's avatar
ck committed
599
600
int BinEditor::find(const QByteArray &pattern_arg, int from,
                    QTextDocument::FindFlags findFlags)
601
602
603
604
605
606
607
608
609
610
611
{
    if (pattern_arg.isEmpty())
        return 0;

    QByteArray pattern = pattern_arg;

    bool caseSensitiveSearch = (findFlags & QTextDocument::FindCaseSensitively);

    if (!caseSensitiveSearch)
        ::lower(pattern);

con's avatar
con committed
612
    bool backwards = (findFlags & QTextDocument::FindBackward);
613
614
    int found = backwards ? dataLastIndexOf(pattern, from, caseSensitiveSearch)
                : dataIndexOf(pattern, from, caseSensitiveSearch);
ck's avatar
ck committed
615

con's avatar
con committed
616
    int foundHex = -1;
617
    QByteArray hexPattern = calculateHexPattern(pattern_arg);
con's avatar
con committed
618
    if (!hexPattern.isEmpty()) {
619
620
        foundHex = backwards ? dataLastIndexOf(hexPattern, from)
                   : dataIndexOf(hexPattern, from);
con's avatar
con committed
621
622
    }

ck's avatar
ck committed
623
624
    int pos = foundHex == -1 || (found >= 0 && (foundHex == -2 || found < foundHex))
              ? found : foundHex;
625
626
627
628

    if (pos >= m_size)
        pos = -1;

con's avatar
con committed
629
630
631
632
633
634
635
    if (pos >= 0) {
        setCursorPosition(pos);
        setCursorPosition(pos + (found == pos ? pattern.size() : hexPattern.size()), KeepAnchor);
    }
    return pos;
}

hjk's avatar
hjk committed
636
637
int BinEditor::findPattern(const QByteArray &data, const QByteArray &dataHex,
    int from, int offset, int *match)
con's avatar
con committed
638
639
640
{
    if (m_searchPattern.isEmpty())
        return -1;
hjk's avatar
hjk committed
641
642
643
644
    int normal = m_searchPattern.isEmpty()
        ? -1 : data.indexOf(m_searchPattern, from - offset);
    int hex = m_searchPatternHex.isEmpty()
        ? -1 : dataHex.indexOf(m_searchPatternHex, from - offset);
con's avatar
con committed
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665

    if (normal >= 0 && (hex < 0 || normal < hex)) {
        if (match)
            *match = m_searchPattern.length();
        return normal + offset;
    }
    if (hex >= 0) {
        if (match)
            *match = m_searchPatternHex.length();
        return hex + offset;
    }

    return -1;
}


void BinEditor::drawItems(QPainter *painter, int x, int y, const QString &itemString)
{
    if (m_isMonospacedFont) {
        painter->drawText(x, y, itemString);
    } else {
hjk's avatar
hjk committed
666
        for (int i = 0; i < m_bytesPerLine; ++i)
con's avatar
con committed
667
668
669
670
            painter->drawText(x + i*m_columnWidth, y, itemString.mid(i*3, 2));
    }
}

671
672
673
void BinEditor::drawChanges(QPainter *painter, int x, int y, const char *changes)
{
    const QBrush red(QColor(250, 150, 150));
hjk's avatar
hjk committed
674
    for (int i = 0; i < m_bytesPerLine; ++i) {
675
676
677
678
679
680
681
        if (changes[i]) {
            painter->fillRect(x + i*m_columnWidth, y - m_ascent,
                2*m_charWidth, m_lineHeight, red);
        }
    }
}

ck's avatar
ck committed
682
QString BinEditor::addressString(quint64 address)
con's avatar
con committed
683
684
685
{
    QChar *addressStringData = m_addressString.data();
    const char *hex = "0123456789abcdef";
ck's avatar
ck committed
686
687
688
689
690
691

    // Take colons into account.
    const int indices[16] = {
        0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18
    };

692
693
    for (int b = 0; b < m_addressBytes; ++b) {
        addressStringData[indices[2*m_addressBytes - 1 - b*2]] =
694
            QLatin1Char(hex[(address >> (8*b)) & 0xf]);
695
        addressStringData[indices[2*m_addressBytes - 2 - b*2]] =
696
            QLatin1Char(hex[(address >> (8*b + 4)) & 0xf]);
con's avatar
con committed
697
698
699
700
701
702
703
    }
    return m_addressString;
}

void BinEditor::paintEvent(QPaintEvent *e)
{
    QPainter painter(viewport());
hjk's avatar
hjk committed
704
705
706
707
708
709
    const int topLine = verticalScrollBar()->value();
    const int xoffset = horizontalScrollBar()->value();
    const int x1 = -xoffset + m_margin + m_labelWidth - m_charWidth/2;
    const int x2 = -xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth + m_charWidth/2;
    painter.drawLine(x1, 0, x1, viewport()->height());
    painter.drawLine(x2, 0, x2, viewport()->height());
con's avatar
con committed
710
711
712
713
714
715
716
717
718
719

    int viewport_height = viewport()->height();
    for (int i = 0; i < 8; ++i) {
        int bg_x = -xoffset +  m_margin + (2 * i + 1) * m_columnWidth + m_labelWidth;
        QRect r(bg_x - m_charWidth/2, 0, m_columnWidth, viewport_height);
        painter.fillRect(e->rect() & r, palette().alternateBase());
    }

    int matchLength = 0;

720
    QByteArray patternData, patternDataHex;
hjk's avatar
hjk committed
721
    int patternOffset = qMax(0, topLine*m_bytesPerLine - m_searchPattern.size());
722
    if (!m_searchPattern.isEmpty()) {
hjk's avatar
hjk committed
723
        patternData = dataMid(patternOffset, m_numVisibleLines * m_bytesPerLine + (topLine*m_bytesPerLine - patternOffset));
724
725
726
727
728
729
        patternDataHex = patternData;
        if (!m_caseSensitiveSearch)
            ::lower(patternData);
    }

    int foundPatternAt = findPattern(patternData, patternDataHex, patternOffset, patternOffset, &matchLength);
con's avatar
con committed
730

731
732
733
734
735
736
    int selStart, selEnd;
    if (m_cursorPosition >= m_anchorPosition) {
        selStart = m_anchorPosition;
        selEnd = m_cursorPosition;
    } else {
        selStart = m_cursorPosition;
Orgad Shaneh's avatar
Orgad Shaneh committed
737
        selEnd = m_anchorPosition;
738
    }
con's avatar
con committed
739

hjk's avatar
hjk committed
740
    QString itemString(m_bytesPerLine*3, QLatin1Char(' '));
con's avatar
con committed
741
    QChar *itemStringData = itemString.data();
hjk's avatar
hjk committed
742
    char changedString[160] = { false };
743
    QTC_ASSERT((size_t)m_bytesPerLine < sizeof(changedString), return);
con's avatar
con committed
744
745
746
    const char *hex = "0123456789abcdef";

    painter.setPen(palette().text().color());
hjk's avatar
hjk committed
747
    const QFontMetrics &fm = painter.fontMetrics();
con's avatar
con committed
748
749
750
751
752
    for (int i = 0; i <= m_numVisibleLines; ++i) {
        int line = topLine + i;
        if (line >= m_numLines)
            break;

hjk's avatar
hjk committed
753
        const quint64 lineAddress = m_baseAddr + uint(line) * m_bytesPerLine;
con's avatar
con committed
754
755
756
757
758
759
        int y = i * m_lineHeight + m_ascent;
        if (y - m_ascent > e->rect().bottom())
            break;
        if (y + m_descent < e->rect().top())
            continue;

ck's avatar
ck committed
760
        painter.drawText(-xoffset, i * m_lineHeight + m_ascent,
761
                         addressString(lineAddress));
762

con's avatar
con committed
763
        int cursor = -1;
hjk's avatar
hjk committed
764
765
766
        if (line * m_bytesPerLine <= m_cursorPosition
                && m_cursorPosition < line * m_bytesPerLine + m_bytesPerLine)
            cursor = m_cursorPosition - line * m_bytesPerLine;
767

hjk's avatar
hjk committed
768
769
        bool hasData = requestDataAt(line * m_bytesPerLine);
        bool hasOldData = requestOldDataAt(line * m_bytesPerLine);
770
        bool isOld = hasOldData && !hasData;
771
772
773

        QString printable;

774
        if (hasData || hasOldData) {
hjk's avatar
hjk committed
775
776
            for (int c = 0; c < m_bytesPerLine; ++c) {
                int pos = line * m_bytesPerLine + c;
777
778
                if (pos >= m_size)
                    break;
779
                QChar qc(QLatin1Char(dataAt(pos, isOld)));
780
781
782
783
784
                if (qc.unicode() >= 127 || !qc.isPrint())
                    qc = 0xB7;
                printable += qc;
            }
        } else {
hjk's avatar
hjk committed
785
            printable = QString(m_bytesPerLine, QLatin1Char(' '));
con's avatar
con committed
786
787
788
789
790
        }

        QRect selectionRect;
        QRect printableSelectionRect;

hjk's avatar
hjk committed
791
        bool isFullySelected = (selStart < selEnd && selStart <= line*m_bytesPerLine && (line+1)*m_bytesPerLine <= selEnd);
792
        bool somethingChanged = false;
con's avatar
con committed
793

794
        if (hasData || hasOldData) {
hjk's avatar
hjk committed
795
796
            for (int c = 0; c < m_bytesPerLine; ++c) {
                int pos = line * m_bytesPerLine + c;
797
                if (pos >= m_size) {
hjk's avatar
hjk committed
798
                    while (c < m_bytesPerLine) {
799
                        itemStringData[c*3] = itemStringData[c*3+1] = QLatin1Char(' ');
800
801
802
                        ++c;
                    }
                    break;
con's avatar
con committed
803
                }
804
                if (foundPatternAt >= 0 && pos >= foundPatternAt + matchLength)
805
                    foundPatternAt = findPattern(patternData, patternDataHex, foundPatternAt + matchLength, patternOffset, &matchLength);
con's avatar
con committed
806
807


808
                const uchar value = uchar(dataAt(pos, isOld));
809
810
                itemStringData[c*3] = QLatin1Char(hex[value >> 4]);
                itemStringData[c*3+1] = QLatin1Char(hex[value & 0xf]);
811
812
813
814
                if (hasOldData && !isOld && value != uchar(dataAt(pos, true))) {
                    changedString[c] = true;
                    somethingChanged = true;
                }
con's avatar
con committed
815

816
                int item_x = -xoffset +  m_margin + c * m_columnWidth + m_labelWidth;
con's avatar
con committed
817

818
819
820
821
822
823
824
825
826
827
828
829
                QColor color;
                foreach (const Markup &m, m_markup) {
                    if (m.covers(lineAddress + c)) {
                        color = m.color;
                        break;
                    }
                }
                if (foundPatternAt >= 0 && pos >= foundPatternAt && pos < foundPatternAt + matchLength)
                    color = QColor(0xffef0b);

                if (color.isValid()) {
                    painter.fillRect(item_x, y-m_ascent, m_columnWidth, m_lineHeight, color);
hjk's avatar
hjk committed
830
                    int printable_item_x = -xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth + m_charWidth
hjk's avatar
hjk committed
831
                                           + fm.width(printable.left(c));
832
                    painter.fillRect(printable_item_x, y-m_ascent,
hjk's avatar
hjk committed
833
                                     fm.width(printable.at(c)),
834
                                     m_lineHeight, color);
835
                }
con's avatar
con committed
836

Orgad Shaneh's avatar
Orgad Shaneh committed
837
                if (selStart < selEnd && !isFullySelected && pos >= selStart && pos <= selEnd) {
838
                    selectionRect |= QRect(item_x, y-m_ascent, m_columnWidth, m_lineHeight);
hjk's avatar
hjk committed
839
                    int printable_item_x = -xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth + m_charWidth
hjk's avatar
hjk committed
840
                                           + fm.width(printable.left(c));
841
                    printableSelectionRect |= QRect(printable_item_x, y-m_ascent,
hjk's avatar
hjk committed
842
                                                    fm.width(printable.at(c)),
843
844
                                                    m_lineHeight);
                }
con's avatar
con committed
845
846
847
848
            }
        }

        int x = -xoffset +  m_margin + m_labelWidth;
849
        bool cursorWanted = m_cursorPosition == m_anchorPosition;
con's avatar
con committed
850
851
852

        if (isFullySelected) {
            painter.save();
hjk's avatar
hjk committed
853
            painter.fillRect(x, y-m_ascent, m_bytesPerLine*m_columnWidth, m_lineHeight, palette().highlight());
con's avatar
con committed
854
855
856
857
            painter.setPen(palette().highlightedText().color());
            drawItems(&painter, x, y, itemString);
            painter.restore();
        } else {
858
859
            if (somethingChanged)
                drawChanges(&painter, x, y, changedString);
con's avatar
con committed
860
861
862
863
864
865
866
867
868
869
870
871
            drawItems(&painter, x, y, itemString);
            if (!selectionRect.isEmpty()) {
                painter.save();
                painter.fillRect(selectionRect, palette().highlight());
                painter.setPen(palette().highlightedText().color());
                painter.setClipRect(selectionRect);
                drawItems(&painter, x, y, itemString);
                painter.restore();
            }
        }


872
        if (cursor >= 0 && cursorWanted) {
hjk's avatar
hjk committed
873
            int w = fm.boundingRect(itemString.mid(cursor*3, 2)).width();
con's avatar
con committed
874
875
876
877
878
879
880
            QRect cursorRect(x + cursor * m_columnWidth, y - m_ascent, w + 1, m_lineHeight);
            painter.save();
            painter.setPen(Qt::red);
            painter.drawRect(cursorRect.adjusted(0, 0, 0, -1));
            painter.restore();
            if (m_hexCursor && m_cursorVisible) {
                if (m_lowNibble)
hjk's avatar
hjk committed
881
                    cursorRect.adjust(fm.width(itemString.left(1)), 0, 0, 0);
con's avatar
con committed
882
883
884
885
886
887
888
889
890
                painter.fillRect(cursorRect, Qt::red);
                painter.save();
                painter.setClipRect(cursorRect);
                painter.setPen(Qt::white);
                drawItems(&painter, x, y, itemString);
                painter.restore();
            }
        }

hjk's avatar
hjk committed
891
        int text_x = -xoffset + m_margin + m_labelWidth + m_bytesPerLine * m_columnWidth + m_charWidth;
con's avatar
con committed
892
893
894

        if (isFullySelected) {
                painter.save();
hjk's avatar
hjk committed
895
                painter.fillRect(text_x, y-m_ascent, fm.width(printable), m_lineHeight,
con's avatar
con committed
896
897
898
899
                                 palette().highlight());
                painter.setPen(palette().highlightedText().color());
                painter.drawText(text_x, y, printable);
                painter.restore();
900
        } else {
con's avatar
con committed
901
902
903
904
905
906
907
908
909
910
911
            painter.drawText(text_x, y, printable);
            if (!printableSelectionRect.isEmpty()) {
                painter.save();
                painter.fillRect(printableSelectionRect, palette().highlight());
                painter.setPen(palette().highlightedText().color());
                painter.setClipRect(printableSelectionRect);
                painter.drawText(text_x, y, printable);
                painter.restore();
            }
        }

912
        if (cursor >= 0 && !printable.isEmpty() && cursorWanted) {
hjk's avatar
hjk committed
913
            QRect cursorRect(text_x + fm.width(printable.left(cursor)),
con's avatar
con committed
914
                             y-m_ascent,
hjk's avatar
hjk committed
915
                             fm.width(printable.at(cursor)),
con's avatar
con committed
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
                             m_lineHeight);
            painter.save();
            if (m_hexCursor || !m_cursorVisible) {
                painter.setPen(Qt::red);
                painter.drawRect(cursorRect.adjusted(0, 0, 0, -1));
            } else {
                painter.setClipRect(cursorRect);
                painter.fillRect(cursorRect, Qt::red);
                painter.setPen(Qt::white);
                painter.drawText(text_x, y, printable);
            }
            painter.restore();
        }
    }
}


int BinEditor::cursorPosition() const
{
    return m_cursorPosition;
}

void BinEditor::setCursorPosition(int pos, MoveMode moveMode)
{
940
    pos = qMin(m_size-1, qMax(0, pos));
con's avatar
con committed
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
    int oldCursorPosition = m_cursorPosition;

    bool hasSelection = m_anchorPosition != m_cursorPosition;
    m_lowNibble = false;
    if (!hasSelection)
        updateLines();
    m_cursorPosition = pos;
    if (moveMode == MoveAnchor) {
        if (hasSelection)
            updateLines(m_anchorPosition, oldCursorPosition);
        m_anchorPosition = m_cursorPosition;
    }

    hasSelection = m_anchorPosition != m_cursorPosition;
    updateLines(hasSelection ? oldCursorPosition : m_cursorPosition, m_cursorPosition);
    ensureCursorVisible();
    emit cursorPositionChanged(m_cursorPosition);
}


void BinEditor::ensureCursorVisible()
{
    QRect cr = cursorRect();
    QRect vr = viewport()->rect();
    if (!vr.contains(cr)) {
        if (cr.top() < vr.top())
hjk's avatar
hjk committed
967
            verticalScrollBar()->setValue(m_cursorPosition / m_bytesPerLine);
con's avatar
con committed
968
        else if (cr.bottom() > vr.bottom())
hjk's avatar
hjk committed
969
            verticalScrollBar()->setValue(m_cursorPosition / m_bytesPerLine - m_numVisibleLines + 1);
con's avatar
con committed
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
    }
}

void BinEditor::mousePressEvent(QMouseEvent *e)
{
    if (e->button() != Qt::LeftButton)
        return;
    setCursorPosition(posAt(e->pos()));
    setBlinkingCursorEnabled(true);
    if (m_hexCursor == inTextArea(e->pos())) {
        m_hexCursor = !m_hexCursor;
        updateLines();
    }
}

void BinEditor::mouseMoveEvent(QMouseEvent *e)
{
    if (!(e->buttons() & Qt::LeftButton))
        return;
    setCursorPosition(posAt(e->pos()), KeepAnchor);
    if (m_hexCursor == inTextArea(e->pos())) {
        m_hexCursor = !m_hexCursor;
        updateLines();
    }
    QRect visible = viewport()->rect();
    if (visible.contains(e->pos()))
        m_autoScrollTimer.stop();
    else if (!m_autoScrollTimer.isActive())
        m_autoScrollTimer.start(100, this);
}

void BinEditor::mouseReleaseEvent(QMouseEvent *)
{
    if (m_autoScrollTimer.isActive()) {
        m_autoScrollTimer.stop();
        ensureCursorVisible();
    }
}

void BinEditor::selectAll()
{
    setCursorPosition(0);
1012
    setCursorPosition(m_size-1, KeepAnchor);
con's avatar
con committed
1013
1014
1015
1016
}

void BinEditor::clear()
{
hjk's avatar
hjk committed
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
    m_baseAddr = 0;
    m_data.clear();
    m_oldData.clear();
    m_modifiedData.clear();
    m_requests.clear();
    m_size = 0;
    m_addressBytes = 4;

    m_unmodifiedState = 0;
    m_undoStack.clear();
    m_redoStack.clear();

    init();
    m_cursorPosition = 0;
    verticalScrollBar()->setValue(0);

    emit cursorPositionChanged(m_cursorPosition);
    viewport()->update();
con's avatar
con committed
1035
1036
}

hjk's avatar
hjk committed
1037
1038
bool BinEditor::event(QEvent *e)
{
1039
1040
    switch (e->type()) {
    case QEvent::KeyPress:
con's avatar
con committed
1041
1042
1043
1044
1045
1046
1047
1048
        switch (static_cast<QKeyEvent*>(e)->key()) {
        case Qt::Key_Tab:
        case Qt::Key_Backtab:
            m_hexCursor = !m_hexCursor;
            setBlinkingCursorEnabled(true);
            ensureCursorVisible();
            e->accept();
            return true;
hjk's avatar
hjk committed
1049
1050
        case Qt::Key_Down: {
            const QScrollBar * const scrollBar = verticalScrollBar();
1051
1052
            const int maximum = scrollBar->maximum();
            if (maximum && scrollBar->value() >= maximum - 1) {
hjk's avatar
hjk committed
1053
1054
                emit newRangeRequested(editor(), baseAddress() + m_size);
                return true;
1055
1056
            }
            break;
hjk's avatar
hjk committed
1057
        }
con's avatar
con committed
1058
1059
        default:;
        }
1060
        break;
1061
1062
1063
1064
    case QEvent::ToolTip: {
        const QHelpEvent *helpEvent = static_cast<const QHelpEvent *>(e);
        const QString tt = toolTip(helpEvent);
        if (tt.isEmpty()) {
1065
            QToolTip::hideText();
1066
1067
1068
        } else {
            QToolTip::showText(helpEvent->globalPos(), tt, this);
        }
1069
1070
        e->accept();
        return true;
con's avatar
con committed
1071
    }
1072
1073
1074
    default:
        break;
    }
1075

con's avatar
con committed
1076
1077
1078
    return QAbstractScrollArea::event(e);
}

1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
QString BinEditor::toolTip(const QHelpEvent *helpEvent) const
{
    // Selection if mouse is in, else 1 byte at cursor
    int selStart = selectionStart();
    int selEnd = selectionEnd();
    int byteCount = selEnd - selStart;
    if (byteCount <= 0) {
        selStart = posAt(helpEvent->pos());
        selEnd = selStart + 1;
        byteCount = 1;
    }
    if (m_hexCursor == 0 || byteCount > 8)
        return QString();

    const QPoint &startPoint = offsetToPos(selStart);
    const QPoint &endPoint = offsetToPos(selEnd);
    const QPoint expandedEndPoint
            = QPoint(endPoint.x(), endPoint.y() + m_lineHeight);
    const QRect selRect(startPoint, expandedEndPoint);
    if (!selRect.contains(helpEvent->pos()))
        return QString();

    quint64 bigEndianValue, littleEndianValue;
    quint64 bigEndianValueOld, littleEndianValueOld;
    asIntegers(selStart, byteCount, bigEndianValue, littleEndianValue);
    asIntegers(selStart, byteCount, bigEndianValueOld, littleEndianValueOld, true);
    QString littleEndianSigned;
    QString bigEndianSigned;
    QString littleEndianSignedOld;
    QString bigEndianSignedOld;
    int intSize = 0;
    switch (byteCount) {
    case 8: case 7: case 6: case 5:
        littleEndianSigned = QString::number(static_cast<qint64>(littleEndianValue));
        bigEndianSigned = QString::number(static_cast<qint64>(bigEndianValue));
        littleEndianSignedOld = QString::number(static_cast<qint64>(littleEndianValueOld));
        bigEndianSignedOld = QString::number(static_cast<qint64>(bigEndianValueOld));
        intSize = 8;
        break;
    case 4: case 3:
        littleEndianSigned = QString::number(static_cast<qint32>(littleEndianValue));
        bigEndianSigned = QString::number(static_cast<qint32>(bigEndianValue));
        littleEndianSignedOld = QString::number(static_cast<qint32>(littleEndianValueOld));
        bigEndianSignedOld = QString::number(static_cast<qint32>(bigEndianValueOld));
        intSize = 4;
        break;
    case 2:
        littleEndianSigned = QString::number(static_cast<qint16>(littleEndianValue));
        bigEndianSigned = QString::number(static_cast<qint16>(bigEndianValue));
        littleEndianSignedOld = QString::number(static_cast<qint16>(littleEndianValueOld));
        bigEndianSignedOld = QString::number(static_cast<qint16>(bigEndianValueOld));
        intSize = 2;
        break;
    case 1:
        littleEndianSigned = QString::number(static_cast<qint8>(littleEndianValue));
        bigEndianSigned = QString::number(static_cast<qint8>(bigEndianValue));
        littleEndianSignedOld = QString::number(static_cast<qint8>(littleEndianValueOld));
        bigEndianSignedOld = QString::number(static_cast<qint8>(bigEndianValueOld));
        intSize = 1;
        break;
    }

    const quint64 address = m_baseAddr + selStart;
    const char tableRowStartC[] = "<tr><td>";
    const char tableRowEndC[] = "</td></tr>";
    const char numericTableRowSepC[] = "</td><td align=\"right\">";

    QString msg;
    QTextStream str(&msg);
    str << "<html><head/><body><p align=\"center\"><b>"
        << tr("Memory at 0x%1").arg(address, 0, 16) << "</b></p>";

    foreach (const Markup &m, m_markup) {
        if (m.covers(address) && !m.toolTip.isEmpty()) {
            str << "<p>" <<  m.toolTip << "</p><br>";
            break;
        }
    }
    const QString msgDecimalUnsigned = tr("Decimal&nbsp;unsigned&nbsp;value:");
    const QString msgDecimalSigned = tr("Decimal&nbsp;signed&nbsp;value:");
    const QString msgOldDecimalUnsigned = tr("Previous&nbsp;decimal&nbsp;unsigned&nbsp;value:");
    const QString msgOldDecimalSigned = tr("Previous&nbsp;decimal&nbsp;signed&nbsp;value:");

    // Table showing little vs. big endian integers for multi-byte
    if (intSize > 1) {
        str << "<table><tr><th>"
            << tr("%1-bit&nbsp;Integer&nbsp;Type").arg(8 * intSize) << "</th><th>"
            << tr("Little Endian") << "</th><th>" << tr("Big Endian") << "</th></tr>";
        str << tableRowStartC << msgDecimalUnsigned
            << numericTableRowSepC << littleEndianValue << numericTableRowSepC
            << bigEndianValue << tableRowEndC <<  tableRowStartC << msgDecimalSigned
            << numericTableRowSepC << littleEndianSigned << numericTableRowSepC
            << bigEndianSigned << tableRowEndC;
        if (bigEndianValue != bigEndianValueOld) {
            str << tableRowStartC << msgOldDecimalUnsigned
                << numericTableRowSepC << littleEndianValueOld << numericTableRowSepC
                << bigEndianValueOld << tableRowEndC << tableRowStartC
                << msgOldDecimalSigned << numericTableRowSepC << littleEndianSignedOld
                << numericTableRowSepC << bigEndianSignedOld << tableRowEndC;
        }
        str << "</table>";
    }

    switch (byteCount) {
    case 1:
        // 1 byte: As octal, decimal, etc.
        str << "<table>";
        str << tableRowStartC << msgDecimalUnsigned << numericTableRowSepC
            << littleEndianValue << tableRowEndC;
        if (littleEndianValue & 0x80) {
            str << tableRowStartC << msgDecimalSigned << numericTableRowSepC
                << littleEndianSigned << tableRowEndC;
        }
        str << tableRowStartC << tr("Binary&nbsp;value:") << numericTableRowSepC;
        str.setIntegerBase(2);
        str.setFieldWidth(8);
        str.setPadChar(QLatin1Char('0'));
        str << littleEndianValue;
        str.setFieldWidth(0);
        str << tableRowEndC << tableRowStartC
            << tr("Octal&nbsp;value:") << numericTableRowSepC;
        str.setIntegerBase(8);
        str.setFieldWidth(3);
        str << littleEndianValue << tableRowEndC;
        str.setIntegerBase(10);
        str.setFieldWidth(0);
        if (littleEndianValue != littleEndianValueOld) {
            str << tableRowStartC << msgOldDecimalUnsigned << numericTableRowSepC
                << littleEndianValueOld << tableRowEndC;
            if (littleEndianValueOld & 0x80) {
                str << tableRowStartC << msgOldDecimalSigned << numericTableRowSepC
                    << littleEndianSignedOld << tableRowEndC;
            }
            str << tableRowStartC << tr("Previous&nbsp;binary&nbsp;value:")
                << numericTableRowSepC;
            str.setIntegerBase(2);
            str.setFieldWidth(8);
            str << littleEndianValueOld;
            str.setFieldWidth(0);
            str << tableRowEndC << tableRowStartC << tr("Previous&nbsp;octal&nbsp;value:")
                << numericTableRowSepC;
            str.setIntegerBase(8);
            str.setFieldWidth(3);
            str << littleEndianValueOld << tableRowEndC;
        }
        str.setIntegerBase(10);
        str.setFieldWidth(0);
        str << "</table>";
        break;
    // Double value
    case sizeof(double): {
        str << "<br><table>";
        double doubleValue, doubleValueOld;
        asDouble(selStart, doubleValue, false);
        asDouble(selStart, doubleValueOld, true);
        str << tableRowStartC << tr("<i>double</i>&nbsp;value:") << numericTableRowSepC
            << doubleValue << tableRowEndC;
        if (doubleValue != doubleValueOld)
            str << tableRowStartC << tr("Previous <i>double</i>&nbsp;value:") << numericTableRowSepC
                << doubleValueOld << tableRowEndC;
        str << "</table>";
    }
    break;
    // Float value
    case sizeof(float): {
        str << "<br><table>";
        float floatValue, floatValueOld;
        asFloat(selStart, floatValue, false);
        asFloat(selStart, floatValueOld, true);
        str << tableRowStartC << tr("<i>float</i>&nbsp;value:") << numericTableRowSepC
            << floatValue << tableRowEndC;
        if (floatValue != floatValueOld)
            str << tableRowStartC << tr("Previous <i>float</i>&nbsp;value:") << numericTableRowSepC
                << floatValueOld << tableRowEndC;

        str << "</table>";
    }
    break;
    }
    str << "</body></html>";
    return msg;
}

con's avatar
con committed
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
void BinEditor::keyPressEvent(QKeyEvent *e)
{

    if (e == QKeySequence::SelectAll) {
            e->accept();
            selectAll();
            return;
    } else if (e == QKeySequence::Copy) {
        e->accept();
        copy();
        return;
    } else if (e == QKeySequence::Undo) {
        e->accept();
        undo();
        return;
    } else if (e == QKeySequence::Redo) {
        e->accept();
        redo();
        return;
    }


    MoveMode moveMode = e->modifiers() & Qt::ShiftModifier ? KeepAnchor : MoveAnchor;
    switch (e->key()) {
    case Qt::Key_Up:
hjk's avatar
hjk committed
1287
        setCursorPosition(m_cursorPosition - m_bytesPerLine, moveMode);
con's avatar
con committed
1288
1289
        break;
    case Qt::Key_Down:
hjk's avatar
hjk committed
1290
        setCursorPosition(m_cursorPosition + m_bytesPerLine, moveMode);
con's avatar
con committed
1291
1292
1293
1294
1295
1296
1297
1298
1299
        break;
    case Qt::Key_Right:
        setCursorPosition(m_cursorPosition + 1, moveMode);
        break;
    case Qt::Key_Left:
        setCursorPosition(m_cursorPosition - 1, moveMode);
        break;
    case Qt::Key_PageUp:
    case Qt::Key_PageDown: {
hjk's avatar
hjk committed
1300
        int line = qMax(0, m_cursorPosition / m_bytesPerLine - verticalScrollBar()->value());
con's avatar
con committed
1301
1302
        verticalScrollBar()->triggerAction(e->key() == Qt::Key_PageUp ?
                                           QScrollBar::SliderPageStepSub : QScrollBar::SliderPageStepAdd);
hjk's avatar
hjk committed
1303
        setCursorPosition((verticalScrollBar()->value() + line) * m_bytesPerLine + m_cursorPosition % m_bytesPerLine, moveMode);
con's avatar
con committed
1304
1305
1306
    } break;

    case Qt::Key_Home:
1307
        if (e->modifiers() & Qt::ControlModifier) {