2 // controllertab.cpp: "Controller" tab on the config dialog
4 // Part of the Virtual Jaguar Project
5 // (C) 2011 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
8 // JLH = James Hammons <jlhamm@acm.org>
11 // --- ---------- ------------------------------------------------------------
12 // JLH 06/23/2011 Created this file
13 // JLH 07/20/2011 Fixed a bunch of stuff
16 #include "controllertab.h"
18 #include "controllerwidget.h"
21 #include "keygrabber.h"
25 ControllerTab::ControllerTab(QWidget * parent/*= 0*/): QWidget(parent),
26 label1(new QLabel(tr("Host Device:"))),
27 label2(new QLabel(tr("Map Name:"))),
28 label3(new QLabel(tr("Maps to:"))),
29 deviceList(new QComboBox(this)),
30 mapNameList(new QComboBox(this)),
31 mapToList(new QComboBox(this)),
32 addMapName(new QPushButton(tr("+"))),
33 deleteMapName(new QPushButton(tr("-"))),
34 redefineAll(new QPushButton(tr("Define All Inputs"))),
35 controllerWidget(new ControllerWidget(this))
37 QVBoxLayout * layout = new QVBoxLayout;
38 QHBoxLayout * top = new QHBoxLayout;
39 QVBoxLayout * left = new QVBoxLayout;
40 QVBoxLayout * right = new QVBoxLayout;
41 QHBoxLayout * middle = new QHBoxLayout;
42 top->addLayout(left, 0);
43 top->addLayout(right, 1);
44 layout->addLayout(top);
45 left->addWidget(label1, 0, Qt::AlignRight);
46 left->addWidget(label2, 0, Qt::AlignRight);
47 left->addWidget(label3, 0, Qt::AlignRight);
48 right->addWidget(deviceList);
50 right->addLayout(middle);
51 middle->addWidget(mapNameList, 1);
52 middle->addWidget(addMapName, 0);
53 middle->addWidget(deleteMapName, 0);
55 right->addWidget(mapToList);
56 layout->addWidget(controllerWidget);
57 layout->addWidget(redefineAll, 0, Qt::AlignHCenter);
59 // At least by doing this, it keeps the QComboBox from resizing itself too
60 // large and breaking the layout. :-P
61 setFixedWidth(sizeHint().width());
63 connect(redefineAll, SIGNAL(clicked()), this, SLOT(DefineAllKeys()));
64 connect(deviceList, SIGNAL(activaed(int)), this, SLOT(ChangeDevice(int)));
65 connect(mapNameList, SIGNAL(activated(int)), this, SLOT(ChangeMapName(int)));
66 connect(addMapName, SIGNAL(clicked()), this, SLOT(AddMapName()));
67 connect(deleteMapName, SIGNAL(clicked()), this, SLOT(DeleteMapName()));
68 connect(controllerWidget, SIGNAL(KeyDefined(int, uint32_t)), this, SLOT(UpdateProfileKeys(int, uint32_t)));
69 connect(mapToList, SIGNAL(activated(int)), this, SLOT(UpdateProfileConnections(int)));
71 // Set up the device combobox (Keyboard is the default, and always
73 deviceList->addItem(tr("Keyboard"), 0);
75 for(int i=0; i<Gamepad::numJoysticks; i++)
77 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
78 deviceList->addItem(Gamepad::GetJoystickName(i), deviceNum);
81 // Set up "Map To" combobox
82 mapToList->addItem(tr("None"), 0);
83 mapToList->addItem(tr("Controller #1"), CONTROLLER1);
84 mapToList->addItem(tr("Controller #2"), CONTROLLER2);
85 mapToList->addItem(tr("Either one that's free"), CONTROLLER1 | CONTROLLER2);
88 So now we come to implementation. When changing devices, could have a helper function
89 in profile.cpp that fills the mapNameList combobox with the appropriate names/profile
92 There needs to be some way of getting data from the ControllerWidget and the current
95 Gamepad will have to have some way of knowing which profile is mapped to which
96 Jaguar controllers and filtering out everything else.
98 Will have to have some intelligent handling of profiles when first run, to see first
99 what is connected and second, to assign profiles to Jaguar controllers. In this
100 case, keyboard is the lowest priority--if a controller is plugged in and assigned to
101 the same Jaguar controller as a keyboard, the controller is used. Not sure what to
102 do in the case of multiple controllers plugged in and assigned to the same Jaguar
105 Also, need a way to load/save profiles.
107 Meaning of checkboxes: None checked == profile not used.
108 1 checked == prefer connection to Jaguar controller X.
109 2 checked == no preference, use any available.
111 Single mapping cannot be deleted ("-" will be disabled). Can always add, up to the max
112 limit of profiles (MAX_PROFILES).
114 ------------------------------
116 Now the main window passes in/removes the last edited profile #. From here, when starting
117 up, we need to pull that number from the profile store and populate all our boxes.
119 -------------------------------
121 Need to do AutoConnectProfiles from here, and detect any conflicts
125 ControllerTab::~ControllerTab()
130 void ControllerTab::SetupLastUsedProfile(void)
132 int deviceNumIndex = deviceList->findData(profile[profileNum].device);
133 int mapNumIndex = mapNameList->findText(profile[profileNum].mapName);
135 if (deviceNumIndex == -1 || mapNumIndex == -1)
137 // We're doing the default, so set it up...
138 // mapToList->setCurrentIndex(mapToList->findData(profile[0].preferredController));
139 //printf("ControllerTab::SetupLastUsedProfile: [FAILED] profileNum=%i, controllerIndex=%i, preferredController=%i\n", profileNum, controllerIndex, profile[0].preferredController);
146 deviceList->setCurrentIndex(deviceNumIndex);
147 mapNameList->setCurrentIndex(mapNumIndex);
148 //no more: #warning "!!! bug in here where it doesn't save your preferred controller !!!"
150 int controllerIndex = mapToList->findData(profile[profileNum].preferredController);
151 mapToList->setCurrentIndex(controllerIndex);
152 //printf("ControllerTab::SetupLastUsedProfile: profileNum=%i, controllerIndex=%i, preferredController=%i\n", profileNum, controllerIndex, profile[profileNum].preferredController);
154 // We have to do this manually, since it's no longer done automagically...
155 ChangeDevice(deviceNumIndex);
156 ChangeMapName(mapNumIndex);
160 void ControllerTab::DefineAllKeys(void)
162 // char jagButtonName[21][10] = { "Up", "Down", "Left", "Right",
163 // "*", "7", "4", "1", "0", "8", "5", "2", "#", "9", "6", "3",
164 // "A", "B", "C", "Option", "Pause" };
165 int orderToDefine[21] = { 0, 1, 2, 3, 18, 17, 16, 20, 19, 7, 11, 15, 6, 10, 14, 5, 9, 13, 8, 4, 12 };
166 KeyGrabber keyGrab(this);
168 for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
170 keyGrab.SetKeyText(orderToDefine[i]);
172 int key = keyGrab.key;
174 if (key == Qt::Key_Escape)
177 // Otherwise, populate the appropriate spot in the settings & update screen...
178 controllerWidget->keys[orderToDefine[i]] = key;
179 controllerWidget->update();
180 profile[profileNum].map[orderToDefine[i]] = key;
185 void ControllerTab::UpdateProfileKeys(int mapPosition, uint32_t key)
187 profile[profileNum].map[mapPosition] = key;
191 void ControllerTab::UpdateProfileConnections(int selection)
193 // profile[profileNum].preferredController = (controller1->isChecked() ? CONTROLLER1 : 0) | (controller2->isChecked() ? CONTROLLER2 : 0);
194 profile[profileNum].preferredController = mapToList->itemData(selection).toInt();
195 //printf("Setting profile #%i 'Maps To' to %i...\n", profileNum, mapToList->itemData(selection).toInt());
199 void ControllerTab::ChangeDevice(int selection)
201 //printf("ControllerTab::ChangeDevice\n");
202 int deviceNum = deviceList->itemData(selection).toInt();
203 mapNameList->clear();
204 int numberOfMappings = FindMappingsForDevice(deviceNum, mapNameList);
205 deleteMapName->setDisabled(numberOfMappings == 1 ? true : false);
206 //printf("Found %i mappings for device #%u...\n", numberOfMappings, deviceNum);
210 void ControllerTab::ChangeMapName(int selection)
212 //printf("ControllerTab::ChangeMapName\n");
213 profileNum = mapNameList->itemData(selection).toInt();
214 //printf("You selected mapping: %s (profile #%u)\n", (mapNameList->itemText(selection)).toAscii().data(), profileNum);
216 for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
217 controllerWidget->keys[i] = profile[profileNum].map[i];
219 controllerWidget->update();
220 // controller1->setChecked(profile[profileNum].preferredController & CONTROLLER1);
221 // controller2->setChecked(profile[profileNum].preferredController & CONTROLLER2);
222 mapToList->setCurrentIndex(mapToList->findData(profile[profileNum].preferredController));
226 void ControllerTab::AddMapName(void)
228 int freeProfile = GetFreeProfile();
230 if (freeProfile == -1)
232 // Oh crap, we're out of room! Alert the media!
233 // (Really, tho, we should pop this up *before* asking for user input.
237 msg.setText(QString(tr("Can't create any more profiles!")));
238 msg.setIcon(QMessageBox::Warning);
241 QMessageBox::warning(this, tr("Houston, we have a problem..."), tr("Can't create any more profiles!"));
247 QString text = QInputDialog::getText(this, tr("Add Map Name"), tr("Map name:"), QLineEdit::Normal);
253 profileNum = freeProfile;
254 profile[profileNum].device = deviceList->itemData(deviceList->currentIndex()).toInt();
255 strncpy(profile[profileNum].mapName, text.toAscii().data(), 31);
256 profile[profileNum].mapName[31] = 0;
257 profile[profileNum].preferredController = CONTROLLER1;
259 for(int i=BUTTON_FIRST; i<BUTTON_LAST; i++)
260 profile[profileNum].map[i] = '*';
262 mapNameList->addItem(text, profileNum);
263 mapNameList->setCurrentIndex(mapNameList->count() - 1);
267 void ControllerTab::DeleteMapName(void)
269 //printf("Delete current mapping (TODO)...\n");
271 // hmm, don't need to check this... Because presumably, it's already been checked for.
272 // if (mapNameList->count() == 1) ;
274 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);
276 if (retVal == QMessageBox::No)
279 int index = mapNameList->currentIndex();
280 int profileToRemove = profileNum;
281 mapNameList->removeItem(index);
282 DeleteProfile(profileToRemove);
287 The profiles need the following:
289 - The name of the controller
290 - A unique human readable ID
291 - The key definitions for that controller (keyboard keys can be mixed in)
293 So there can be more than one profile for each unique controller; the
294 relationship is many-to-one. So basically, how it works it like this: SDL
295 reports all connected controllers. If there are none connected, the default
296 controller is the keyboard (which can have multiple profiles). The UI only
297 presents those profiles which are usuable with the controllers that are plugged
298 in, all else is ignored. The user can pick the profile for the controller and
299 configure the keys for it; the UI automagically saves everything.
301 How to handle the case of identical controllers being plugged in? How does the
302 UI know which is which? Each controller will have a mapping to a default
303 Jaguar controller (#1 or #2). Still doesn't prevent confusion though. Actually,
304 it can: The profile can have a field that maps it to a preferred Jaguar
305 controller, which can also be both (#1 AND #2--in this case we can set it to
306 zero which means no preference). If the UI detects two of the same controller
307 and each can be mapped to the same profile, it assigns them in order since it
308 doesn't matter, the profiles are identical.
310 The default profile is always available and is the keyboard (hey, we're PC
311 centric here). The default profile is usually #0.
313 Can there be more than one keyboard profile? Why not? You will need separate
314 ones for controller #1 and controller #2.
316 A profile might look like this:
318 Field 1: Nostomo N45 Analog
320 Field 3: Jaguar controller #1
321 Field 4: The button/stick mapping
323 Profile # would be implicit in the order that they are stored in the internal
326 When a new controller is plugged in with no profiles attached, it defaults to
327 a set keyboard layout which the user can change. So every new controller will
328 always have at least one profile.
331 The Gamepad class has the name of the controller (except for Keyboard)
332 The profile list is just a list
333 The controller name index + profile index makes a unique key
334 Probably the best way to deal with it is to stuff the name/profile indices
335 into the key definition structure.
337 #define CONTROLLER1 0x01
338 #define CONTROLLER2 0x02
342 int device; // Host device number
343 char mapName[32]; // Human readable map name
344 int preferredController; // CONTROLLER1 and/or CONTROLLER2
345 int map[21]; // Keys/buttons/axes
348 NOTE that device is an int, and the list is maintained elsewhere. It is
349 *not* the same as what you see in GetJoystickName(); the device names have
350 to be able to persist even when not available.
352 Where to store the master profile list? It has to be accessible to this class.
353 vjs.profile[x] would be good, but it's not really a concern for the Jaguar core.
354 So it shouldn't go there. There should be a separate global setting place for