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