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 controller1(new QCheckBox(tr("Jaguar Controller #1"))),
32 controller2(new QCheckBox(tr("Jaguar Controller #2"))),
33 addMapName(new QPushButton(tr("+"))),
34 deleteMapName(new QPushButton(tr("-"))),
35 redefineAll(new QPushButton(tr("Define All Inputs"))),
36 controllerWidget(new ControllerWidget(this))
38 // mapNameList->setEditable(true);
40 QVBoxLayout * layout = new QVBoxLayout;
41 QHBoxLayout * top = new QHBoxLayout;
42 QVBoxLayout * left = new QVBoxLayout;
43 QVBoxLayout * right = new QVBoxLayout;
44 QHBoxLayout * middle = new QHBoxLayout;
45 top->addLayout(left, 0);
46 top->addLayout(right, 1);
47 layout->addLayout(top);
48 left->addWidget(label1, 0, Qt::AlignRight);
49 left->addWidget(label2, 0, Qt::AlignRight);
50 left->addWidget(label3, 0, Qt::AlignRight);
51 left->addWidget(new QLabel);
52 right->addWidget(deviceList);
54 right->addLayout(middle);
55 middle->addWidget(mapNameList, 1);
56 middle->addWidget(addMapName, 0);
57 middle->addWidget(deleteMapName, 0);
58 // right->addWidget(mapNameList);
60 right->addWidget(controller1);
61 right->addWidget(controller2);
62 layout->addWidget(controllerWidget);
63 layout->addWidget(redefineAll, 0, Qt::AlignHCenter);
65 // At least by doing this, it keeps the QComboBox from resizing itself too
66 // large and breaking the layout. :-P
67 setFixedWidth(sizeHint().width());
69 connect(redefineAll, SIGNAL(clicked()), this, SLOT(DefineAllKeys()));
70 connect(deviceList, SIGNAL(currentIndexChanged(int)), this, SLOT(ChangeDevice(int)));
71 connect(mapNameList, SIGNAL(currentIndexChanged(int)), this, SLOT(ChangeMapName(int)));
72 connect(addMapName, SIGNAL(clicked()), this, SLOT(AddMapName()));
73 connect(deleteMapName, SIGNAL(clicked()), this, SLOT(DeleteMapName()));
74 connect(controllerWidget, SIGNAL(KeyDefined(int, uint32_t)), this, SLOT(UpdateProfileKeys(int, uint32_t)));
75 connect(controller1, SIGNAL(clicked()), this, SLOT(UpdateProfileConnections()));
76 connect(controller2, SIGNAL(clicked()), this, SLOT(UpdateProfileConnections()));
78 // Set up the device combobox (Keyboard is the default, and always
80 deviceList->addItem(tr("Keyboard"), 0);
81 // Set up map name combobox (Default is default, and always present)
82 // mapNameList->addItem(tr("Default"));
84 for(int i=0; i<Gamepad::numJoysticks; i++)
86 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
87 deviceList->addItem(Gamepad::GetJoystickName(i), deviceNum);
91 So now we come to implementation. When changing devices, could have a helper function
92 in profile.cpp that fills the mapNameList combobox with the appropriate names/profile
95 There needs to be some way of getting data from the ControllerWidget and the current
98 Gamepad will have to have some way of knowing which profile is mapped to which
99 Jaguar controllers and filtering out everything else.
101 Will have to have some intelligent handling of profiles when first run, to see first
102 what is connected and second, to assign profiles to Jaguar controllers. In this
103 case, keyboard is the lowest priority--if a controller is plugged in and assigned to
104 the same Jaguar controller as a keyboard, the controller is used. Not sure what to
105 do in the case of multiple controllers plugged in and assigned to the same Jaguar
108 Also, need a way to load/save profiles.
110 Meaning of checkboxes: None checked == profile not used.
111 1 checked == prefer connection to Jaguar controller X.
112 2 checked == no preference, use any available.
114 Single mapping cannot be deleted ("-" will be disabled). Can always add, up to the max
115 limit of profiles (MAX_PROFILES).
117 ------------------------------
119 Now the main window passes in/removes the last edited profile #. From here, when starting
120 up, we need to pull that number from the profile store and populate all our boxes.
124 ControllerTab::~ControllerTab()
129 void ControllerTab::SetupLastUsedProfile(void)
131 int deviceNum = deviceList->findData(profile[profileNum].device);
132 int mapNum = mapNameList->findText(profile[profileNum].mapName);
134 if (deviceNum == -1 || mapNum == -1)
137 ChangeDevice(deviceNum);
138 ChangeMapName(mapNum);
142 void ControllerTab::DefineAllKeys(void)
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);
150 for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
152 keyGrab.SetKeyText(orderToDefine[i]);
154 int key = keyGrab.key;
156 if (key == Qt::Key_Escape)
159 // Otherwise, populate the appropriate spot in the settings & update screen...
160 controllerWidget->keys[orderToDefine[i]] = key;
161 controllerWidget->update();
162 profile[profileNum].map[orderToDefine[i]] = key;
167 void ControllerTab::UpdateProfileKeys(int mapPosition, uint32_t key)
169 profile[profileNum].map[mapPosition] = key;
173 void ControllerTab::UpdateProfileConnections(void)
175 profile[profileNum].preferredController = (controller1->isChecked() ? CONTROLLER1 : 0) | (controller2->isChecked() ? CONTROLLER2 : 0);
179 void ControllerTab::ChangeDevice(int selection)
181 int deviceNum = deviceList->itemData(selection).toInt();
182 mapNameList->clear();
183 int numberOfMappings = FindMappingsForDevice(deviceNum, mapNameList);
184 deleteMapName->setDisabled(numberOfMappings == 1 ? true : false);
185 //printf("Found %i mappings for device #%u...\n", numberOfMappings, deviceNum);
189 void ControllerTab::ChangeMapName(int selection)
191 profileNum = mapNameList->itemData(selection).toInt();
192 //printf("You selected mapping: %s (profile #%u)\n", (mapNameList->itemText(selection)).toAscii().data(), profileNum);
194 for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
195 controllerWidget->keys[i] = profile[profileNum].map[i];
197 controllerWidget->update();
198 controller1->setChecked(profile[profileNum].preferredController & CONTROLLER1);
199 controller2->setChecked(profile[profileNum].preferredController & CONTROLLER2);
203 void ControllerTab::AddMapName(void)
205 printf("Add new mapping (TODO)...\n");
209 void ControllerTab::DeleteMapName(void)
211 printf("Delete current mapping (TODO)...\n");
216 The profiles need the following:
218 - The name of the controller
219 - A unique human readable ID
220 - The key definitions for that controller (keyboard keys can be mixed in)
222 So there can be more than one profile for each unique controller; the
223 relationship is many-to-one. So basically, how it works it like this: SDL
224 reports all connected controllers. If there are none connected, the default
225 controller is the keyboard (which can have multiple profiles). The UI only
226 presents those profiles which are usuable with the controllers that are plugged
227 in, all else is ignored. The user can pick the profile for the controller and
228 configure the keys for it; the UI automagically saves everything.
230 How to handle the case of identical controllers being plugged in? How does the
231 UI know which is which? Each controller will have a mapping to a default
232 Jaguar controller (#1 or #2). Still doesn't prevent confusion though. Actually,
233 it can: The profile can have a field that maps it to a preferred Jaguar
234 controller, which can also be both (#1 AND #2--in this case we can set it to
235 zero which means no preference). If the UI detects two of the same controller
236 and each can be mapped to the same profile, it assigns them in order since it
237 doesn't matter, the profiles are identical.
239 The default profile is always available and is the keyboard (hey, we're PC
240 centric here). The default profile is usually #0.
242 Can there be more than one keyboard profile? Why not? You will need separate
243 ones for controller #1 and controller #2.
245 A profile might look like this:
247 Field 1: Nostomo N45 Analog
249 Field 3: Jaguar controller #1
250 Field 4: The button/stick mapping
252 Profile # would be implicit in the order that they are stored in the internal
255 When a new controller is plugged in with no profiles attached, it defaults to
256 a set keyboard layout which the user can change. So every new controller will
257 always have at least one profile.
260 The Gamepad class has the name of the controller (except for Keyboard)
261 The profile list is just a list
262 The controller name index + profile index makes a unique key
263 Probably the best way to deal with it is to stuff the name/profile indices
264 into the key definition structure.
266 #define CONTROLLER1 0x01
267 #define CONTROLLER2 0x02
271 int device; // Host device number
272 char mapName[32]; // Human readable map name
273 int preferredController; // CONTROLLER1 and/or CONTROLLER2
274 int map[21]; // Keys/buttons/axes
277 NOTE that device is an int, and the list is maintained elsewhere. It is
278 *not* the same as what you see in GetJoystickName(); the device names have
279 to be able to persist even when not available.
281 Where to store the master profile list? It has to be accessible to this class.
282 vjs.profile[x] would be good, but it's not really a concern for the Jaguar core.
283 So it shouldn't go there. There should be a separate global setting place for