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