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