src/platform/qt/MemoryModel.cpp (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "MemoryModel.h"
7
8#include "GBAApp.h"
9#include "CoreController.h"
10#include "LogController.h"
11#include "VFileDevice.h"
12
13#include <QAction>
14#include <QApplication>
15#include <QClipboard>
16#include <QFontMetrics>
17#include <QPainter>
18#include <QScrollBar>
19#include <QSlider>
20#include <QWheelEvent>
21
22#include <mgba/core/core.h>
23#include <mgba-util/vfs.h>
24
25using namespace QGBA;
26
27MemoryModel::MemoryModel(QWidget* parent)
28 : QAbstractScrollArea(parent)
29{
30 m_font.setFamily("Source Code Pro");
31 m_font.setStyleHint(QFont::Monospace);
32#ifdef Q_OS_MAC
33 m_font.setPointSize(12);
34#else
35 m_font.setPointSize(10);
36#endif
37 QFontMetrics metrics(m_font);
38 m_cellHeight = metrics.height();
39 m_letterWidth = metrics.averageCharWidth();
40
41 setFocusPolicy(Qt::StrongFocus);
42 setContextMenuPolicy(Qt::ActionsContextMenu);
43
44 QAction* copy = new QAction(tr("Copy selection"), this);
45 copy->setShortcut(QKeySequence::Copy);
46 connect(copy, &QAction::triggered, this, &MemoryModel::copy);
47 addAction(copy);
48
49 QAction* save = new QAction(tr("Save selection"), this);
50 save->setShortcut(QKeySequence::Save);
51 connect(save, &QAction::triggered, this, &MemoryModel::save);
52 addAction(save);
53
54 QAction* paste = new QAction(tr("Paste"), this);
55 paste->setShortcut(QKeySequence::Paste);
56 connect(paste, &QAction::triggered, this, &MemoryModel::paste);
57 addAction(paste);
58
59 QAction* load = new QAction(tr("Load"), this);
60 load->setShortcut(QKeySequence::Open);
61 connect(load, &QAction::triggered, this, &MemoryModel::load);
62 addAction(load);
63
64 static QString arg("%0");
65 for (int i = 0; i < 256; ++i) {
66 QStaticText str(arg.arg(i, 2, 16, QChar('0')).toUpper());
67 str.prepare(QTransform(), m_font);
68 m_staticNumbers.append(str);
69 }
70
71 for (int i = 0; i < 256; ++i) {
72 QChar c(i);
73 if (!c.isPrint()) {
74 c = '.';
75 }
76 QStaticText str = QStaticText(QString(c));
77 str.prepare(QTransform(), m_font);
78 m_staticLatin1.append(str);
79 }
80
81 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
82 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
83 m_margins = QMargins(metrics.width("0FFFFFF0 ") + 3, m_cellHeight + 1, metrics.width(" AAAAAAAAAAAAAAAA") + 3, 0);
84 m_cellSize = QSizeF((viewport()->size().width() - (m_margins.left() + m_margins.right())) / 16.0, m_cellHeight);
85
86 connect(verticalScrollBar(), &QSlider::sliderMoved, [this](int position) {
87 m_top = position;
88 update();
89 });
90
91 setRegion(0, 0x10000000, tr("All"));
92}
93
94void MemoryModel::setController(std::shared_ptr<CoreController> controller) {
95 m_core = controller->thread()->core;
96}
97
98void MemoryModel::setRegion(uint32_t base, uint32_t size, const QString& name, int segment) {
99 m_top = 0;
100 m_base = base;
101 m_size = size;
102 m_regionName = QStaticText(name);
103 m_regionName.prepare(QTransform(), m_font);
104 m_currentBank = segment;
105 verticalScrollBar()->setRange(0, (size >> 4) + 1 - viewport()->size().height() / m_cellHeight);
106 verticalScrollBar()->setValue(0);
107 viewport()->update();
108}
109
110void MemoryModel::setSegment(int segment) {
111 m_currentBank = segment;
112 viewport()->update();
113}
114
115void MemoryModel::setAlignment(int width) {
116 if (width != 1 && width != 2 && width != 4) {
117 return;
118 }
119 m_align = width;
120 m_buffer = 0;
121 m_bufferedNybbles = 0;
122 viewport()->update();
123}
124
125void MemoryModel::loadTBLFromPath(const QString& path) {
126 VFile* vf = VFileDevice::open(path, O_RDONLY);
127 if (!vf) {
128 return;
129 }
130 m_codec = std::unique_ptr<TextCodec, TextCodecFree>(new TextCodec);
131 TextCodecLoadTBL(m_codec.get(), vf, true);
132 vf->close(vf);
133}
134
135void MemoryModel::loadTBL() {
136 QString filename = GBAApp::app()->getOpenFileName(this, tr("Load TBL"));
137 if (filename.isNull()) {
138 return;
139 }
140 loadTBLFromPath(filename);
141}
142
143void MemoryModel::jumpToAddress(const QString& hex) {
144 bool ok = false;
145 uint32_t i = hex.toInt(&ok, 16);
146 if (ok) {
147 jumpToAddress(i);
148 }
149}
150
151void MemoryModel::jumpToAddress(uint32_t address) {
152 if (address >= 0x10000000) {
153 return;
154 }
155 if (address < m_base || address >= m_base + m_size) {
156 setRegion(0, 0x10000000, tr("All"));
157 }
158 m_top = (address - m_base) / 16;
159 boundsCheck();
160 verticalScrollBar()->setValue(m_top);
161 m_buffer = 0;
162 m_bufferedNybbles = 0;
163}
164
165void MemoryModel::copy() {
166 QClipboard* clipboard = QApplication::clipboard();
167 if (!clipboard) {
168 return;
169 }
170 QByteArray bytestring(serialize());
171 QString string;
172 string.reserve(bytestring.size() * 2);
173 static QString arg("%0");
174 static QChar c0('0');
175 for (uchar c : bytestring) {
176 string.append(arg.arg(c, 2, 16, c0).toUpper());
177 }
178 clipboard->setText(string);
179}
180
181void MemoryModel::paste() {
182 QClipboard* clipboard = QApplication::clipboard();
183 if (!clipboard) {
184 return;
185 }
186 QString string = clipboard->text();
187 if (string.isEmpty()) {
188 return;
189 }
190 QByteArray bytestring(QByteArray::fromHex(string.toLocal8Bit()));
191 deserialize(bytestring);
192 viewport()->update();
193}
194
195void MemoryModel::save() {
196 QString filename = GBAApp::app()->getSaveFileName(this, tr("Save selected memory"));
197 if (filename.isNull()) {
198 return;
199 }
200 QFile outfile(filename);
201 if (!outfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
202 LOG(QT, WARN) << tr("Failed to open output file: %1").arg(filename);
203 return;
204 }
205 QByteArray out(serialize());
206 outfile.write(out);
207}
208
209void MemoryModel::load() {
210 QString filename = GBAApp::app()->getOpenFileName(this, tr("Load memory"));
211 if (filename.isNull()) {
212 return;
213 }
214 QFile infile(filename);
215 if (!infile.open(QIODevice::ReadOnly)) {
216 LOG(QT, WARN) << tr("Failed to open input file: %1").arg(filename);
217 return;
218 }
219 QByteArray bytestring(infile.readAll());
220 deserialize(bytestring);
221 viewport()->update();
222}
223
224QByteArray MemoryModel::serialize() {
225 QByteArray bytes;
226 bytes.reserve(m_selection.second - m_selection.first);
227 switch (m_align) {
228 case 1:
229 for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
230 char datum = m_core->rawRead8(m_core, i, m_currentBank);
231 bytes.append(datum);
232 }
233 break;
234 case 2:
235 for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
236 quint16 datum = m_core->rawRead16(m_core, i, m_currentBank);
237 char leDatum[2];
238 STORE_16LE(datum, 0, (uint16_t*) leDatum);
239 bytes.append(leDatum, 2);
240 }
241 break;
242 case 4:
243 for (uint32_t i = m_selection.first; i < m_selection.second; i += m_align) {
244 quint32 datum = m_core->rawRead32(m_core, i, m_currentBank);
245 char leDatum[4];
246 STORE_32LE(datum, 0, (uint16_t*) leDatum);
247 bytes.append(leDatum, 4);
248 }
249 break;
250 }
251 return bytes;
252}
253
254void MemoryModel::deserialize(const QByteArray& bytes) {
255 uint32_t addr = m_selection.first;
256 switch (m_align) {
257 case 1:
258 for (int i = 0; i < bytes.size(); i += m_align, addr += m_align) {
259 uint8_t datum = bytes[i];
260 m_core->rawWrite8(m_core, addr, m_currentBank, datum);
261 }
262 break;
263 case 2:
264 for (int i = 0; i < bytes.size(); i += m_align, addr += m_align) {
265 char leDatum[2]{ bytes[i], bytes[i + 1] };
266 uint16_t datum;
267 LOAD_16LE(datum, 0, leDatum);
268 m_core->rawWrite16(m_core, addr, m_currentBank, datum);
269 }
270 break;
271 case 4:
272 for (int i = 0; i < bytes.size(); i += m_align, addr += m_align) {
273 char leDatum[4]{ bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3] };
274 uint32_t datum;
275 LOAD_32LE(datum, 0, leDatum);
276 m_core->rawWrite32(m_core, addr, m_currentBank, datum);
277 }
278 break;
279 }
280}
281
282QString MemoryModel::decodeText(const QByteArray& bytes) {
283 QString text;
284 if (m_codec) {
285 QByteArray array;
286 TextCodecIterator iter;
287 TextCodecStartDecode(m_codec.get(), &iter);
288 uint8_t lineBuffer[128];
289 for (quint8 byte : bytes) {
290 ssize_t size = TextCodecAdvance(&iter, byte, lineBuffer, sizeof(lineBuffer));
291 if (size > (ssize_t) sizeof(lineBuffer)) {
292 size = sizeof(lineBuffer);
293 }
294 for (ssize_t i = 0; i < size; ++i) {
295 array.append(lineBuffer[i]);
296 }
297 }
298 ssize_t size = TextCodecFinish(&iter, lineBuffer, sizeof(lineBuffer));
299 if (size > (ssize_t) sizeof(lineBuffer)) {
300 size = sizeof(lineBuffer);
301 }
302 for (ssize_t i = 0; i < size; ++i) {
303 array.append(lineBuffer[i]);
304 }
305 text = QString::fromUtf8(array);
306 } else {
307 for (uint8_t c : bytes) {
308 text.append((uchar) c);
309 }
310 }
311 return text;
312}
313
314void MemoryModel::resizeEvent(QResizeEvent*) {
315 m_cellSize = QSizeF((viewport()->size().width() - (m_margins.left() + m_margins.right())) / 16.0, m_cellHeight);
316 verticalScrollBar()->setRange(0, (m_size >> 4) + 1 - viewport()->size().height() / m_cellHeight);
317 boundsCheck();
318}
319
320void MemoryModel::paintEvent(QPaintEvent* event) {
321 QPainter painter(viewport());
322 QPalette palette;
323 painter.setFont(m_font);
324 painter.setPen(palette.color(QPalette::WindowText));
325 static QChar c0('0');
326 static QString arg("%0");
327 static QString arg2("%0:%1");
328 QSizeF letterSize = QSizeF(m_letterWidth, m_cellHeight);
329 painter.drawStaticText(QPointF((m_margins.left() - m_regionName.size().width() - 1) / 2.0, 0), m_regionName);
330 painter.drawText(
331 QRect(QPoint(viewport()->size().width() - m_margins.right(), 0), QSize(m_margins.right(), m_margins.top())),
332 Qt::AlignHCenter, m_codec ? tr("TBL") : tr("ISO-8859-1"));
333 for (int x = 0; x < 16; ++x) {
334 painter.drawText(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), 0), m_cellSize), Qt::AlignHCenter,
335 QString::number(x, 16).toUpper());
336 }
337 int height = (viewport()->size().height() - m_cellHeight) / m_cellHeight;
338 for (int y = 0; y < height; ++y) {
339 int yp = m_cellHeight * y + m_margins.top();
340 if ((y + m_top) * 16 >= m_size) {
341 break;
342 }
343 QString data;
344 if (m_currentBank >= 0) {
345 data = arg2.arg(m_currentBank, 2, 16, c0).arg((y + m_top) * 16 + m_base, 4, 16, c0).toUpper();
346 } else {
347 data = arg.arg((y + m_top) * 16 + m_base, 8, 16, c0).toUpper();
348 }
349 painter.drawText(QRectF(QPointF(0, yp), QSizeF(m_margins.left(), m_cellHeight)), Qt::AlignHCenter, data);
350 switch (m_align) {
351 case 2:
352 for (int x = 0; x < 16; x += 2) {
353 uint32_t address = (y + m_top) * 16 + x + m_base;
354 if (address >= m_base + m_size) {
355 break;
356 }
357 if (isInSelection(address)) {
358 painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp),
359 QSizeF(m_cellSize.width() * 2, m_cellSize.height())),
360 palette.highlight());
361 painter.setPen(palette.color(QPalette::HighlightedText));
362 if (isEditing(address)) {
363 drawEditingText(
364 painter,
365 QPointF(m_cellSize.width() * (x + 1.0) - 2 * m_letterWidth + m_margins.left(), yp));
366 continue;
367 }
368 } else {
369 painter.setPen(palette.color(QPalette::WindowText));
370 }
371 uint16_t b = m_core->rawRead16(m_core, address, m_currentBank);
372 painter.drawStaticText(
373 QPointF(m_cellSize.width() * (x + 1.0) - 2 * m_letterWidth + m_margins.left(), yp),
374 m_staticNumbers[(b >> 8) & 0xFF]);
375 painter.drawStaticText(QPointF(m_cellSize.width() * (x + 1.0) + m_margins.left(), yp),
376 m_staticNumbers[b & 0xFF]);
377 }
378 break;
379 case 4:
380 for (int x = 0; x < 16; x += 4) {
381 uint32_t address = (y + m_top) * 16 + x + m_base;
382 if (address >= m_base + m_size) {
383 break;
384 }
385 if (isInSelection(address)) {
386 painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp),
387 QSizeF(m_cellSize.width() * 4, m_cellSize.height())),
388 palette.highlight());
389 painter.setPen(palette.color(QPalette::HighlightedText));
390 if (isEditing(address)) {
391 drawEditingText(
392 painter,
393 QPointF(m_cellSize.width() * (x + 2.0) - 4 * m_letterWidth + m_margins.left(), yp));
394 continue;
395 }
396 } else {
397 painter.setPen(palette.color(QPalette::WindowText));
398 }
399 uint32_t b = m_core->rawRead32(m_core, address, m_currentBank);
400 painter.drawStaticText(
401 QPointF(m_cellSize.width() * (x + 2.0) - 4 * m_letterWidth + m_margins.left(), yp),
402 m_staticNumbers[(b >> 24) & 0xFF]);
403 painter.drawStaticText(
404 QPointF(m_cellSize.width() * (x + 2.0) - 2 * m_letterWidth + m_margins.left(), yp),
405 m_staticNumbers[(b >> 16) & 0xFF]);
406 painter.drawStaticText(QPointF(m_cellSize.width() * (x + 2.0) + m_margins.left(), yp),
407 m_staticNumbers[(b >> 8) & 0xFF]);
408 painter.drawStaticText(
409 QPointF(m_cellSize.width() * (x + 2.0) + 2 * m_letterWidth + m_margins.left(), yp),
410 m_staticNumbers[b & 0xFF]);
411 }
412 break;
413 case 1:
414 default:
415 for (int x = 0; x < 16; ++x) {
416 uint32_t address = (y + m_top) * 16 + x + m_base;
417 if (address >= m_base + m_size) {
418 break;
419 }
420 if (isInSelection(address)) {
421 painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp), m_cellSize),
422 palette.highlight());
423 painter.setPen(palette.color(QPalette::HighlightedText));
424 if (isEditing(address)) {
425 drawEditingText(painter,
426 QPointF(m_cellSize.width() * (x + 0.5) - m_letterWidth + m_margins.left(), yp));
427 continue;
428 }
429 } else {
430 painter.setPen(palette.color(QPalette::WindowText));
431 }
432 uint8_t b = m_core->rawRead8(m_core, address, m_currentBank);
433 painter.drawStaticText(QPointF(m_cellSize.width() * (x + 0.5) - m_letterWidth + m_margins.left(), yp),
434 m_staticNumbers[b]);
435 }
436 break;
437 }
438 painter.setPen(palette.color(QPalette::WindowText));
439 for (int x = 0; x < 16; x += m_align) {
440 QByteArray array;
441 uint32_t b;
442 switch (m_align) {
443 case 1:
444 b = m_core->rawRead8(m_core, (y + m_top) * 16 + x + m_base, m_currentBank);
445 array.append((char) b);
446 break;
447 case 2:
448 b = m_core->rawRead16(m_core, (y + m_top) * 16 + x + m_base, m_currentBank);
449 array.append((char) b);
450 array.append((char) (b >> 8));
451 break;
452 case 4:
453 b = m_core->rawRead32(m_core, (y + m_top) * 16 + x + m_base, m_currentBank);
454 array.append((char) b);
455 array.append((char) (b >> 8));
456 array.append((char) (b >> 16));
457 array.append((char) (b >> 24));
458 break;
459 }
460 QString unfilteredText = decodeText(array);
461 QString text;
462 if (unfilteredText.isEmpty()) {
463 text.fill('.', m_align);
464 } else {
465 for (QChar c : unfilteredText) {
466 if (!c.isPrint()) {
467 text.append(QChar('.'));
468 } else {
469 text.append(c);
470 }
471 }
472 }
473 for (int i = 0; i < text.size() && i < m_align; ++i) {
474 const QChar c = text.at(i);
475 const QPointF location(viewport()->size().width() - (16 - x - i) * m_margins.right() / 17.0 - m_letterWidth * 0.5, yp);
476 if (c < 256) {
477 painter.drawStaticText(location, m_staticLatin1[c.cell()]);
478 } else {
479 painter.drawText(location, c);
480 }
481 }
482 }
483 }
484 painter.drawLine(m_margins.left(), 0, m_margins.left(), viewport()->size().height());
485 painter.drawLine(viewport()->size().width() - m_margins.right(), 0, viewport()->size().width() - m_margins.right(),
486 viewport()->size().height());
487 painter.drawLine(0, m_margins.top(), viewport()->size().width(), m_margins.top());
488}
489
490void MemoryModel::wheelEvent(QWheelEvent* event) {
491 m_top -= event->angleDelta().y() / 8;
492 boundsCheck();
493 event->accept();
494 verticalScrollBar()->setValue(m_top);
495 update();
496}
497
498void MemoryModel::mousePressEvent(QMouseEvent* event) {
499 if (event->x() < m_margins.left() || event->y() < m_margins.top() ||
500 event->x() > size().width() - m_margins.right()) {
501 m_selection = qMakePair(0, 0);
502 return;
503 }
504
505 QPoint position(event->pos() - QPoint(m_margins.left(), m_margins.top()));
506 uint32_t address = int(position.x() / m_cellSize.width()) +
507 (int(position.y() / m_cellSize.height()) + m_top) * 16 + m_base;
508 if (event->button() == Qt::RightButton && isInSelection(address)) {
509 return;
510 }
511 if (event->modifiers() & Qt::ShiftModifier) {
512 if ((address & ~(m_align - 1)) < m_selectionAnchor) {
513 m_selection = qMakePair(address & ~(m_align - 1), m_selectionAnchor + m_align);
514 } else {
515 m_selection = qMakePair(m_selectionAnchor, (address & ~(m_align - 1)) + m_align);
516 }
517 } else {
518 m_selectionAnchor = address & ~(m_align - 1);
519 m_selection = qMakePair(m_selectionAnchor, m_selectionAnchor + m_align);
520 }
521 m_buffer = 0;
522 m_bufferedNybbles = 0;
523 emit selectionChanged(m_selection.first, m_selection.second);
524 viewport()->update();
525}
526
527void MemoryModel::mouseMoveEvent(QMouseEvent* event) {
528 if (event->x() < m_margins.left() || event->y() < m_margins.top() ||
529 event->x() > size().width() - m_margins.right()) {
530 return;
531 }
532
533 QPoint position(event->pos() - QPoint(m_margins.left(), m_margins.top()));
534 uint32_t address = int(position.x() / m_cellSize.width()) +
535 (int(position.y() / m_cellSize.height()) + m_top) * 16 + m_base;
536 if ((address & ~(m_align - 1)) < m_selectionAnchor) {
537 m_selection = qMakePair(address & ~(m_align - 1), m_selectionAnchor + m_align);
538 } else {
539 m_selection = qMakePair(m_selectionAnchor, (address & ~(m_align - 1)) + m_align);
540 }
541 m_buffer = 0;
542 m_bufferedNybbles = 0;
543 emit selectionChanged(m_selection.first, m_selection.second);
544 viewport()->update();
545}
546
547void MemoryModel::keyPressEvent(QKeyEvent* event) {
548 if (m_selection.first >= m_selection.second) {
549 return;
550 }
551 int key = event->key();
552 uint8_t nybble = 0;
553 switch (key) {
554 case Qt::Key_0:
555 case Qt::Key_1:
556 case Qt::Key_2:
557 case Qt::Key_3:
558 case Qt::Key_4:
559 case Qt::Key_5:
560 case Qt::Key_6:
561 case Qt::Key_7:
562 case Qt::Key_8:
563 case Qt::Key_9:
564 nybble = key - Qt::Key_0;
565 break;
566 case Qt::Key_A:
567 case Qt::Key_B:
568 case Qt::Key_C:
569 case Qt::Key_D:
570 case Qt::Key_E:
571 case Qt::Key_F:
572 nybble = key - Qt::Key_A + 10;
573 break;
574 case Qt::Key_Left:
575 adjustCursor(-m_align, event->modifiers() & Qt::ShiftModifier);
576 return;
577 case Qt::Key_Right:
578 adjustCursor(m_align, event->modifiers() & Qt::ShiftModifier);
579 return;
580 case Qt::Key_Up:
581 adjustCursor(-16, event->modifiers() & Qt::ShiftModifier);
582 return;
583 case Qt::Key_Down:
584 adjustCursor(16, event->modifiers() & Qt::ShiftModifier);
585 return;
586 default:
587 return;
588 }
589 m_buffer <<= 4;
590 m_buffer |= nybble;
591 ++m_bufferedNybbles;
592 if (m_bufferedNybbles == m_align * 2) {
593 switch (m_align) {
594 case 1:
595 m_core->rawWrite8(m_core, m_selection.first, m_currentBank, m_buffer);
596 break;
597 case 2:
598 m_core->rawWrite16(m_core, m_selection.first, m_currentBank, m_buffer);
599 break;
600 case 4:
601 m_core->rawWrite32(m_core, m_selection.first, m_currentBank, m_buffer);
602 break;
603 }
604 m_bufferedNybbles = 0;
605 m_buffer = 0;
606 m_selection.first += m_align;
607 if (m_selection.second <= m_selection.first) {
608 m_selection.second = m_selection.first + m_align;
609 }
610 emit selectionChanged(m_selection.first, m_selection.second);
611 }
612 viewport()->update();
613}
614
615void MemoryModel::boundsCheck() {
616 if (m_top < 0) {
617 m_top = 0;
618 } else if (m_top > (m_size >> 4) + 1 - viewport()->size().height() / m_cellHeight) {
619 m_top = (m_size >> 4) + 1 - viewport()->size().height() / m_cellHeight;
620 }
621}
622
623bool MemoryModel::isInSelection(uint32_t address) {
624 if (m_selection.first == m_selection.second) {
625 return false;
626 }
627 if (m_selection.second <= (address | (m_align - 1))) {
628 return false;
629 }
630 if (m_selection.first <= (address & ~(m_align - 1))) {
631 return true;
632 }
633 return false;
634}
635
636bool MemoryModel::isEditing(uint32_t address) {
637 return m_bufferedNybbles && m_selection.first == (address & ~(m_align - 1));
638}
639
640void MemoryModel::drawEditingText(QPainter& painter, const QPointF& origin) {
641 QPointF o(origin);
642 for (int nybbles = m_bufferedNybbles; nybbles > 0; nybbles -= 2) {
643 if (nybbles > 1) {
644 uint8_t b = m_buffer >> ((nybbles - 2) * 4);
645 painter.drawStaticText(o, m_staticNumbers[b]);
646 } else {
647 int b = m_buffer & 0xF;
648 if (b < 10) {
649 painter.drawStaticText(o, m_staticLatin1[b + '0']);
650 } else {
651 painter.drawStaticText(o, m_staticLatin1[b - 10 + 'A']);
652 }
653 }
654 o += QPointF(m_letterWidth * 2, 0);
655 }
656}
657
658void MemoryModel::adjustCursor(int adjust, bool shift) {
659 if (m_selection.first >= m_selection.second) {
660 return;
661 }
662 int cursorPosition = m_top;
663 if (shift) {
664 if (m_selectionAnchor == m_selection.first) {
665 if (adjust < 0 && m_base - adjust > m_selection.second) {
666 adjust = m_base - m_selection.second + m_align;
667 } else if (adjust > 0 && m_selection.second + adjust >= m_base + m_size) {
668 adjust = m_base + m_size - m_selection.second;
669 }
670 adjust += m_selection.second;
671 if (adjust <= m_selection.first) {
672 m_selection.second = m_selection.first + m_align;
673 m_selection.first = adjust - m_align;
674 cursorPosition = m_selection.first;
675 } else {
676 m_selection.second = adjust;
677 cursorPosition = m_selection.second - m_align;
678 }
679 } else {
680 if (adjust < 0 && m_base - adjust > m_selection.first) {
681 adjust = m_base - m_selection.first;
682 } else if (adjust > 0 && m_selection.first + adjust >= m_base + m_size) {
683 adjust = m_base + m_size - m_selection.first - m_align;
684 }
685 adjust += m_selection.first;
686 if (adjust >= m_selection.second) {
687 m_selection.first = m_selection.second - m_align;
688 m_selection.second = adjust + m_align;
689 cursorPosition = adjust;
690 } else {
691 m_selection.first = adjust;
692 cursorPosition = m_selection.first;
693 }
694 }
695 cursorPosition = (cursorPosition - m_base) / 16;
696 } else {
697 if (m_selectionAnchor == m_selection.first) {
698 m_selectionAnchor = m_selection.second - m_align;
699 } else {
700 m_selectionAnchor = m_selection.first;
701 }
702 if (adjust < 0 && m_base - adjust > m_selectionAnchor) {
703 m_selectionAnchor = m_base;
704 } else if (adjust > 0 && m_selectionAnchor + adjust >= m_base + m_size) {
705 m_selectionAnchor = m_base + m_size - m_align;
706 } else {
707 m_selectionAnchor += adjust;
708 }
709 m_selection.first = m_selectionAnchor;
710 m_selection.second = m_selection.first + m_align;
711 cursorPosition = (m_selectionAnchor - m_base) / 16;
712 }
713 if (cursorPosition < m_top) {
714 m_top = cursorPosition;
715 } else if (cursorPosition >= m_top + viewport()->size().height() / m_cellHeight - 1) {
716 m_top = cursorPosition - viewport()->size().height() / m_cellHeight + 2;
717 }
718 emit selectionChanged(m_selection.first, m_selection.second);
719 viewport()->update();
720}
721
722void MemoryModel::TextCodecFree::operator()(TextCodec* codec) {
723 TextCodecDeinit(codec);
724 delete(codec);
725}