Added screenshot feature
[clinton/Virtual-Jaguar-Rx.git] / src / gui / controllertab.cpp
CommitLineData
cf76e892
JPM
1//
2// controllertab.cpp: "Controller" tab on the config dialog
3//
4// Part of the Virtual Jaguar Project
5// (C) 2011 Underground Software
6// See the README and GPLv3 files for licensing and warranty information
7//
8// JLH = James Hammons <jlhamm@acm.org>
9//
10// WHO WHEN WHAT
11// --- ---------- ------------------------------------------------------------
12// JLH 06/23/2011 Created this file
13// JLH 07/20/2011 Fixed a bunch of stuff
14// JLH 10/02/2014 Fixed even more stuff, related to the profile system
15//
16
17/*
18To really fix this shit, we have to straighten out some stuff. So here goes:
19
20We have a database of profiles consisting of a device list (devices that have
21been seen already) and map list (consisting of a key into the device list, a
22human readable name, a preferred slot #, and a key/button mapping). This is a
23list that can hold up to 64 different profiles.
24
25We have a a list of attached gamepads in Gamepad::. There can be 8 at most
26attached any one time.
27
28There are two game port slots that a controller can be hooked into.
29
30So, what we need to do when configuring and/or using this system is this.
31
32 - Populate the device combobox with the list of attached devices from the
33 profile database.
34 - Populate the map to combobox with the profiles associated with that profile
35 device number.
36 - Save stuff when the user changes stuff (this happens already)
37*/
38
39#include "controllertab.h"
40
41#include "controllerwidget.h"
42#include "gamepad.h"
43#include "joystick.h"
44#include "keygrabber.h"
45#include "profile.h"
46
47
48ControllerTab::ControllerTab(QWidget * parent/*= 0*/): QWidget(parent),
49 label1(new QLabel(tr("Host Device:"))),
50 label2(new QLabel(tr("Map Name:"))),
51 label3(new QLabel(tr("Maps to:"))),
52 deviceList(new QComboBox(this)),
53 mapNameList(new QComboBox(this)),
54 mapToList(new QComboBox(this)),
55 addMapName(new QPushButton(tr("+"))),
56 deleteMapName(new QPushButton(tr("-"))),
57 redefineAll(new QPushButton(tr("Define All Inputs"))),
58 controllerWidget(new ControllerWidget(this))
59{
60 QVBoxLayout * layout = new QVBoxLayout;
61 QHBoxLayout * top = new QHBoxLayout;
62 QVBoxLayout * left = new QVBoxLayout;
63 QVBoxLayout * right = new QVBoxLayout;
64 QHBoxLayout * middle = new QHBoxLayout;
65 top->addLayout(left, 0);
66 top->addLayout(right, 1);
67 layout->addLayout(top);
68 left->addWidget(label1, 0, Qt::AlignRight);
69 left->addWidget(label2, 0, Qt::AlignRight);
70 left->addWidget(label3, 0, Qt::AlignRight);
71 right->addWidget(deviceList);
72
73 right->addLayout(middle);
74 middle->addWidget(mapNameList, 1);
75 middle->addWidget(addMapName, 0);
76 middle->addWidget(deleteMapName, 0);
77
78 right->addWidget(mapToList);
79 layout->addWidget(controllerWidget);
80 layout->addWidget(redefineAll, 0, Qt::AlignHCenter);
81 setLayout(layout);
82 // At least by doing this, it keeps the QComboBox from resizing itself too
83 // large and breaking the layout. :-P
84 setFixedWidth(sizeHint().width());
85
86 connect(redefineAll, SIGNAL(clicked()), this, SLOT(DefineAllKeys()));
87 connect(deviceList, SIGNAL(activated(int)), this, SLOT(ChangeDevice(int)));
88 connect(mapNameList, SIGNAL(activated(int)), this, SLOT(ChangeMapName(int)));
89 connect(addMapName, SIGNAL(clicked()), this, SLOT(AddMapName()));
90 connect(deleteMapName, SIGNAL(clicked()), this, SLOT(DeleteMapName()));
91 connect(controllerWidget, SIGNAL(KeyDefined(int, uint32_t)), this, SLOT(UpdateProfileKeys(int, uint32_t)));
92 connect(mapToList, SIGNAL(activated(int)), this, SLOT(UpdateProfileConnections(int)));
93
94 // Set up the device combobox (Keyboard is the default, and always
95 // present)
96 deviceList->addItem(tr("Keyboard"), 0);
97
98 for(int i=0; i<Gamepad::numJoysticks; i++)
99 {
100 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
101 deviceList->addItem(Gamepad::GetJoystickName(i), deviceNum);
102 }
103
104 // Set up "Map To" combobox
105 mapToList->addItem(tr("None"), 0);
106 mapToList->addItem(tr("Controller #1"), CONTROLLER1);
107 mapToList->addItem(tr("Controller #2"), CONTROLLER2);
108 mapToList->addItem(tr("Either one that's free"), CONTROLLER1 | CONTROLLER2);
109}
110
111
112ControllerTab::~ControllerTab()
113{
114}
115
116
117void ControllerTab::SetupLastUsedProfile(void)
118{
119 int deviceNumIndex = deviceList->findData(profile[profileNum].device);
120 int mapNumIndex = mapNameList->findText(profile[profileNum].mapName);
121
122 if (deviceNumIndex == -1 || mapNumIndex == -1)
123 {
124 // We're doing the default, so set it up...
125 deviceNumIndex = 0;
126 mapNumIndex = 0;
127 profileNum = 0;
128 }
129
130 deviceList->setCurrentIndex(deviceNumIndex);
131 mapNameList->setCurrentIndex(mapNumIndex);
132
133 int controllerIndex = mapToList->findData(profile[profileNum].preferredSlot);
134 mapToList->setCurrentIndex(controllerIndex);
135
136 // We have to do this manually, since it's no longer done automagically...
137 ChangeDevice(deviceNumIndex);
138 ChangeMapName(mapNumIndex);
139}
140
141
142void ControllerTab::DefineAllKeys(void)
143{
144// char jagButtonName[21][10] = { "Up", "Down", "Left", "Right",
145// "*", "7", "4", "1", "0", "8", "5", "2", "#", "9", "6", "3",
146// "A", "B", "C", "Option", "Pause" };
147 int orderToDefine[21] = { 0, 1, 2, 3, 18, 17, 16, 20, 19, 7, 11, 15, 6, 10, 14, 5, 9, 13, 8, 4, 12 };
148 KeyGrabber keyGrab(this);
149
150 for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
151 {
152 keyGrab.SetKeyText(orderToDefine[i]);
153 keyGrab.exec();
154 int key = keyGrab.key;
155
156 if (key == Qt::Key_Escape)
157 break;
158
159 // Otherwise, populate the appropriate spot in the settings & update
160 // the screen...
161 controllerWidget->keys[orderToDefine[i]] = key;
162 controllerWidget->update();
163 profile[profileNum].map[orderToDefine[i]] = key;
164 }
165}
166
167
168void ControllerTab::UpdateProfileKeys(int mapPosition, uint32_t key)
169{
170 profile[profileNum].map[mapPosition] = key;
171}
172
173
174void ControllerTab::UpdateProfileConnections(int selection)
175{
176 profile[profileNum].preferredSlot = mapToList->itemData(selection).toInt();
177}
178
179
180void ControllerTab::ChangeDevice(int selection)
181{
182 int deviceNum = deviceList->itemData(selection).toInt();
183 mapNameList->clear();
184 int numberOfMappings = FindMappingsForDevice(deviceNum, mapNameList);
185 // Make sure to disable the "-" button is there's only one mapping for this
186 // device...
187 deleteMapName->setDisabled(numberOfMappings == 1 ? true : false);
188 // Set up new profile #...
189 ChangeMapName(0);
190}
191
192
193void ControllerTab::ChangeMapName(int selection)
194{
195 profileNum = mapNameList->itemData(selection).toInt();
196
197 for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
198 controllerWidget->keys[i] = profile[profileNum].map[i];
199
200 controllerWidget->update();
201 mapToList->setCurrentIndex(mapToList->findData(profile[profileNum].preferredSlot));
202}
203
204
205void ControllerTab::AddMapName(void)
206{
207 int freeProfile = GetFreeProfile();
208
209 if (freeProfile == -1)
210 {
211 // Oh crap, we're out of room! Alert the media!
212 QMessageBox::warning(this, tr("Houston, we have a problem..."), tr("Can't create any more profiles!"));
213
214 return;
215 }
216
217 QString text = QInputDialog::getText(this, tr("Add Map Name"), tr("Map name:"), QLineEdit::Normal);
218
219 if (text.isEmpty())
220 return;
221
222 // Add mapping...
223 profileNum = freeProfile;
224 profile[profileNum].device = deviceList->itemData(deviceList->currentIndex()).toInt();
225 strncpy(profile[profileNum].mapName, text.toUtf8().data(), 31);
226 profile[profileNum].mapName[31] = 0;
227 profile[profileNum].preferredSlot = CONTROLLER1;
228
229 for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
230 profile[profileNum].map[i] = '*';
231
232 mapNameList->addItem(text, profileNum);
233#if 0
234 mapNameList->setCurrentIndex(mapNameList->count() - 1);
235#else
236 int selection = mapNameList->count() - 1;
237 mapNameList->setCurrentIndex(selection);
238 ChangeMapName(selection);
239 // We just added a new mapping, so enable the delete button!
240 deleteMapName->setDisabled(false);
241#endif
242}
243
244
245void ControllerTab::DeleteMapName(void)
246{
247 QString msg = QString("Map name: %1\n\nAre you sure you want to remove this mapping?").arg(profile[profileNum].mapName);
248
249// QMessageBox::StandardButton retVal = QMessageBox::question(this, tr("Remove Mapping"), tr("Are you sure you want to remove this mapping?"), QMessageBox::No | QMessageBox::Yes, QMessageBox::No);
250 QMessageBox::StandardButton retVal = QMessageBox::question(this, tr("Remove Mapping"), msg, QMessageBox::No | QMessageBox::Yes, QMessageBox::No);
251
252 if (retVal == QMessageBox::No)
253 return;
254
255 int index = mapNameList->currentIndex();
256 int profileToRemove = profileNum;
257 mapNameList->removeItem(index);
258 DeleteProfile(profileToRemove);
259 // We need to reload the profile that we move to after deleting the current
260 // one...
261 ChangeMapName(mapNameList->currentIndex());
262 // If we get down to one profile left for the device, we need to make sure
263 // that the user can't delete it!
264 deleteMapName->setDisabled(mapNameList->count() == 1 ? true : false);
265}
266