src/platform/qt/LoadSaveState.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 "LoadSaveState.h"
7
8#include "GameController.h"
9#include "GamepadAxisEvent.h"
10#include "GamepadButtonEvent.h"
11#include "VFileDevice.h"
12
13#include <QKeyEvent>
14#include <QPainter>
15
16extern "C" {
17#include "gba/serialize.h"
18#include "gba/video.h"
19}
20
21using namespace QGBA;
22
23LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent)
24 : QWidget(parent)
25 , m_controller(controller)
26 , m_currentFocus(0)
27{
28 m_ui.setupUi(this);
29
30 m_slots[0] = m_ui.state1;
31 m_slots[1] = m_ui.state2;
32 m_slots[2] = m_ui.state3;
33 m_slots[3] = m_ui.state4;
34 m_slots[4] = m_ui.state5;
35 m_slots[5] = m_ui.state6;
36 m_slots[6] = m_ui.state7;
37 m_slots[7] = m_ui.state8;
38 m_slots[8] = m_ui.state9;
39
40 int i;
41 for (i = 0; i < NUM_SLOTS; ++i) {
42 loadState(i + 1);
43 m_slots[i]->installEventFilter(this);
44 connect(m_slots[i], &QAbstractButton::clicked, this, [this, i]() { triggerState(i + 1); });
45 }
46
47 QAction* escape = new QAction(this);
48 escape->connect(escape, SIGNAL(triggered()), this, SLOT(close()));
49 escape->setShortcut(QKeySequence("Esc"));
50 escape->setShortcutContext(Qt::WidgetWithChildrenShortcut);
51 addAction(escape);
52}
53
54void LoadSaveState::setMode(LoadSave mode) {
55 m_mode = mode;
56 QString text = mode == LoadSave::LOAD ? tr("Load State") : tr("Save State");
57 setWindowTitle(text);
58 m_ui.lsLabel->setText(text);
59}
60
61bool LoadSaveState::eventFilter(QObject* object, QEvent* event) {
62 if (event->type() == QEvent::KeyPress) {
63 int column = m_currentFocus % 3;
64 int row = m_currentFocus - column;
65 switch (static_cast<QKeyEvent*>(event)->key()) {
66 case Qt::Key_Up:
67 row += 6;
68 break;
69 case Qt::Key_Down:
70 row += 3;
71 break;
72 case Qt::Key_Left:
73 column += 2;
74 break;
75 case Qt::Key_Right:
76 column += 1;
77 break;
78 case Qt::Key_1:
79 case Qt::Key_2:
80 case Qt::Key_3:
81 case Qt::Key_4:
82 case Qt::Key_5:
83 case Qt::Key_6:
84 case Qt::Key_7:
85 case Qt::Key_8:
86 case Qt::Key_9:
87 triggerState(static_cast<QKeyEvent*>(event)->key() - Qt::Key_1 + 1);
88 break;
89 case Qt::Key_Enter:
90 case Qt::Key_Return:
91 triggerState(m_currentFocus + 1);
92 break;
93 default:
94 return false;
95 }
96 column %= 3;
97 row %= 9;
98 m_currentFocus = column + row;
99 m_slots[m_currentFocus]->setFocus();
100 return true;
101 }
102 if (event->type() == QEvent::Enter) {
103 int i;
104 for (i = 0; i < 9; ++i) {
105 if (m_slots[i] == object) {
106 m_currentFocus = i;
107 m_slots[m_currentFocus]->setFocus();
108 return true;
109 }
110 }
111 }
112 if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadAxisEvent::Type()) {
113 int column = m_currentFocus % 3;
114 int row = m_currentFocus - column;
115 GBAKey key = GBA_KEY_NONE;
116 if (event->type() == GamepadButtonEvent::Down()) {
117 key = static_cast<GamepadButtonEvent*>(event)->gbaKey();
118 } else if (event->type() == GamepadAxisEvent::Type()) {
119 GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
120 if (gae->isNew()) {
121 key = gae->gbaKey();
122 } else {
123 return false;
124 }
125 }
126 switch (key) {
127 case GBA_KEY_UP:
128 row += 6;
129 break;
130 case GBA_KEY_DOWN:
131 row += 3;
132 break;
133 case GBA_KEY_LEFT:
134 column += 2;
135 break;
136 case GBA_KEY_RIGHT:
137 column += 1;
138 break;
139 case GBA_KEY_B:
140 event->accept();
141 close();
142 return true;
143 case GBA_KEY_A:
144 case GBA_KEY_START:
145 event->accept();
146 triggerState(m_currentFocus + 1);
147 return true;
148 default:
149 return false;
150 }
151 column %= 3;
152 row %= 9;
153 m_currentFocus = column + row;
154 m_slots[m_currentFocus]->setFocus();
155 event->accept();
156 return true;
157 }
158 return false;
159}
160
161void LoadSaveState::loadState(int slot) {
162 GBAThread* thread = m_controller->thread();
163 VFile* vf = GBAGetState(thread->gba, thread->stateDir, slot, false);
164 if (!vf) {
165 m_slots[slot - 1]->setText(tr("Empty"));
166 return;
167 }
168 VFileDevice vdev(vf);
169 QImage stateImage;
170 stateImage.load(&vdev, "PNG");
171 if (!stateImage.isNull()) {
172 QPixmap statePixmap;
173 statePixmap.convertFromImage(stateImage);
174 m_slots[slot - 1]->setIcon(statePixmap);
175 m_slots[slot - 1]->setText(QString());
176 } else {
177 m_slots[slot - 1]->setText(tr("Slot %1").arg(slot));
178 }
179}
180
181void LoadSaveState::triggerState(int slot) {
182 if (m_mode == LoadSave::SAVE) {
183 m_controller->saveState(slot);
184 } else {
185 m_controller->loadState(slot);
186 }
187 close();
188}
189
190void LoadSaveState::closeEvent(QCloseEvent* event) {
191 emit closed();
192 QWidget::closeEvent(event);
193}
194
195void LoadSaveState::showEvent(QShowEvent* event) {
196 m_slots[m_currentFocus]->setFocus();
197 QWidget::showEvent(event);
198}
199
200void LoadSaveState::paintEvent(QPaintEvent*) {
201 QPainter painter(this);
202 QRect full(QPoint(), size());
203 painter.drawPixmap(full, m_currentImage);
204 painter.fillRect(full, QColor(0, 0, 0, 128));
205}