]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/controllertab.cpp
Fixed updated joystick handling, first major stab at gamepad profiles.
[virtualjaguar] / src / gui / controllertab.cpp
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 //
15
16 #include "controllertab.h"
17
18 #include "controllerwidget.h"
19 #include "gamepad.h"
20 #include "joystick.h"
21 #include "keygrabber.h"
22 #include "profile.h"
23
24
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))
37 {
38 //      mapNameList->setEditable(true);
39
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);
53
54         right->addLayout(middle);
55         middle->addWidget(mapNameList, 1);
56         middle->addWidget(addMapName, 0);
57         middle->addWidget(deleteMapName, 0);
58 //      right->addWidget(mapNameList);
59
60         right->addWidget(controller1);
61         right->addWidget(controller2);
62         layout->addWidget(controllerWidget);
63         layout->addWidget(redefineAll, 0, Qt::AlignHCenter);
64         setLayout(layout);
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());
68
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()));
77
78         // Set up the device combobox (Keyboard is the default, and always
79         // present)
80         deviceList->addItem(tr("Keyboard"), 0);
81         // Set up map name combobox (Default is default, and always present)
82 //      mapNameList->addItem(tr("Default"));
83
84         for(int i=0; i<Gamepad::numJoysticks; i++)
85         {
86                 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
87                 deviceList->addItem(Gamepad::GetJoystickName(i), deviceNum);
88         }
89 }
90 /*
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
93 numbers.
94
95 There needs to be some way of getting data from the ControllerWidget and the current
96 profile.
97
98 Gamepad will have to have some way of knowing which profile is mapped to which
99 Jaguar controllers and filtering out everything else.
100
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
106 controller.
107
108 Also, need a way to load/save profiles.
109
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.
113
114 Single mapping cannot be deleted ("-" will be disabled). Can always add, up to the max
115 limit of profiles (MAX_PROFILES).
116
117 ------------------------------
118
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.
121 */
122
123
124 ControllerTab::~ControllerTab()
125 {
126 }
127
128
129 void ControllerTab::SetupLastUsedProfile(void)
130 {
131         int deviceNum = deviceList->findData(profile[profileNum].device);
132         int mapNum = mapNameList->findText(profile[profileNum].mapName);
133
134         if (deviceNum == -1 || mapNum == -1)
135                 return;
136
137         ChangeDevice(deviceNum);
138         ChangeMapName(mapNum);
139 }
140
141
142 void 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 screen...
160                 controllerWidget->keys[orderToDefine[i]] = key;
161                 controllerWidget->update();
162                 profile[profileNum].map[orderToDefine[i]] = key;
163         }
164 }
165
166
167 void ControllerTab::UpdateProfileKeys(int mapPosition, uint32_t key)
168 {
169         profile[profileNum].map[mapPosition] = key;
170 }
171
172
173 void ControllerTab::UpdateProfileConnections(void)
174 {
175         profile[profileNum].preferredController = (controller1->isChecked() ? CONTROLLER1 : 0) | (controller2->isChecked() ? CONTROLLER2 : 0);
176 }
177
178
179 void ControllerTab::ChangeDevice(int selection)
180 {
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);
186 }
187
188
189 void ControllerTab::ChangeMapName(int selection)
190 {
191         profileNum = mapNameList->itemData(selection).toInt();
192 //printf("You selected mapping: %s (profile #%u)\n", (mapNameList->itemText(selection)).toAscii().data(), profileNum);
193
194         for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
195                 controllerWidget->keys[i] = profile[profileNum].map[i];
196
197         controllerWidget->update();
198         controller1->setChecked(profile[profileNum].preferredController & CONTROLLER1);
199         controller2->setChecked(profile[profileNum].preferredController & CONTROLLER2);
200 }
201
202
203 void ControllerTab::AddMapName(void)
204 {
205 printf("Add new mapping (TODO)...\n");
206 }
207
208
209 void ControllerTab::DeleteMapName(void)
210 {
211 printf("Delete current mapping (TODO)...\n");
212 }
213
214
215 /*
216 The profiles need the following:
217
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)
221
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.
229
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.
238
239 The default profile is always available and is the keyboard (hey, we're PC
240 centric here). The default profile is usually #0.
241
242 Can there be more than one keyboard profile? Why not? You will need separate
243 ones for controller #1 and controller #2.
244
245 A profile might look like this:
246
247 Field 1: Nostomo N45 Analog
248 Field 2: Dad's #1
249 Field 3: Jaguar controller #1
250 Field 4: The button/stick mapping
251
252 Profile # would be implicit in the order that they are stored in the internal
253 data structure.
254
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.
258
259 Data structures:
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.
265
266 #define CONTROLLER1 0x01
267 #define CONTROLLER2 0x02
268
269 struct Profile
270 {
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
275 };
276
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.
280
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
284 GUI stuff...
285 */
286