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