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