If initsram/initstate points to LSS file, pull the matching member
[lsnes.git] / src / platform / wxwidgets / settings-keyentry.cpp
blobd08aca682d15f11685cbc373f81b55fb023f401f
1 #include "platform/wxwidgets/settings-common.hpp"
2 #include "platform/wxwidgets/settings-keyentry.hpp"
3 #include "core/instance.hpp"
4 #include "core/keymapper.hpp"
6 namespace
8 int vert_padding = 40;
9 int horiz_padding = 60;
12 press_button_dialog::press_button_dialog(wxWindow* parent, emulator_instance& _inst, const std::string& title,
13 bool _axis)
14 : wxDialog(parent, wxID_ANY, towxstring(title)), inst(_inst)
16 CHECK_UI_THREAD;
17 axis = _axis;
18 wxStaticText* t;
19 wxBoxSizer* s2 = new wxBoxSizer(wxVERTICAL);
20 wxPanel* p = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, -1), wxWANTS_CHARS);
21 s2->Add(p, 1, wxGROW);
22 lastkbdkey = -1;
23 mouseflag = 0;
24 Centre();
25 wxFlexGridSizer* s = new wxFlexGridSizer(3, 3, 0, 0);
26 p->SetSizer(s);
27 SetSizer(s2);
28 s->Add(horiz_padding, vert_padding);
29 s->Add(0, 0);
30 s->Add(0, 0);
31 s->Add(0, 0);
32 s->Add(t = new wxStaticText(p, wxID_ANY, wxT("Press the key to assign"), wxDefaultPosition,
33 wxSize(-1, -1), wxWANTS_CHARS), 1, wxGROW);
34 s->Add(0, 0);
35 s->Add(0, 0);
36 s->Add(0, 0);
37 s->Add(horiz_padding, vert_padding);
38 p->SetFocus();
39 p->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(press_button_dialog::on_keyboard_down), NULL, this);
40 p->Connect(wxEVT_KEY_UP, wxKeyEventHandler(press_button_dialog::on_keyboard_up), NULL, this);
41 p->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
42 p->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
43 p->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
44 p->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
45 p->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
46 p->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
47 t->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(press_button_dialog::on_keyboard_down), NULL, this);
48 t->Connect(wxEVT_KEY_UP, wxKeyEventHandler(press_button_dialog::on_keyboard_up), NULL, this);
49 t->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
50 t->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
51 t->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
52 t->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
53 t->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
54 t->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
55 settings_activate_keygrab(inst, [this](std::string key) { this->dismiss_with(key); });
56 s->SetSizeHints(this);
57 Fit();
60 bool press_button_dialog::handle_mousebtn(wxMouseEvent& e, bool(wxMouseEvent::*down)()const,
61 bool(wxMouseEvent::*up)()const, const std::string& k, int flag)
63 CHECK_UI_THREAD;
64 if((e.*down)())
65 mouseflag = flag;
66 if((e.*up)()) {
67 if(mouseflag == flag) {
68 dismiss_with(k);
69 return true;
70 } else
71 mouseflag = 0;
73 return false;
76 void press_button_dialog::on_mouse(wxMouseEvent& e)
78 handle_mousebtn(e, &wxMouseEvent::LeftDown, &wxMouseEvent::LeftUp, "mouse_left", 1);
79 handle_mousebtn(e, &wxMouseEvent::MiddleDown, &wxMouseEvent::MiddleUp, "mouse_center", 2);
80 handle_mousebtn(e, &wxMouseEvent::RightDown, &wxMouseEvent::RightUp, "mouse_right", 3);
83 void press_button_dialog::on_keyboard_down(wxKeyEvent& e)
85 CHECK_UI_THREAD;
86 lastkbdkey = e.GetKeyCode();
87 mouseflag = 0;
90 void press_button_dialog::on_keyboard_up(wxKeyEvent& e)
92 CHECK_UI_THREAD;
93 int kcode = e.GetKeyCode();
94 if(lastkbdkey == kcode) {
95 dismiss_with(map_keycode_to_key(kcode));
96 } else {
97 lastkbdkey = -1;
98 mouseflag = 0;
102 void press_button_dialog::dismiss_with(const std::string& _k)
104 CHECK_UI_THREAD;
105 std::string k = _k;
106 if(k == "")
107 return;
108 if(axis) {
109 //Check that k is a valid axis.
110 try {
111 //Remove the +/- postfix if any.
112 if(k.length() > 1) {
113 char lch = k[k.length() - 1];
114 if(lch == '+' || lch == '-')
115 k = k.substr(0, k.length() - 1);
117 keyboard::key& key = inst.keyboard->lookup_key(k);
118 if(key.get_type() != keyboard::KBD_KEYTYPE_AXIS)
119 return;
120 } catch(...) {
121 return;
124 if(key == "") {
125 settings_deactivate_keygrab(inst);
126 key = k;
127 EndModal(wxID_OK);
131 key_entry_dialog::key_entry_dialog(wxWindow* parent, emulator_instance& _inst, const std::string& title,
132 const std::string& spec, bool clearable)
133 : wxDialog(parent, wxID_ANY, towxstring(title), wxDefaultPosition, wxSize(-1, -1)), inst(_inst)
135 CHECK_UI_THREAD;
136 wxString boxchoices[] = { wxT("Released"), wxT("Don't care"), wxT("Pressed") };
137 std::vector<wxString> classeslist;
138 wxString emptystring;
139 std::list<keyboard::modifier*> mods;
140 std::list<keyboard::key*> keys;
142 wtitle = title;
144 cleared = false;
145 std::set<std::string> x;
146 mods = inst.keyboard->all_modifiers();
147 keys = inst.keyboard->all_keys();
148 for(auto i : keys) {
149 std::string kclass = i->get_class();
150 if(!x.count(kclass))
151 classeslist.push_back(towxstring(kclass));
152 x.insert(kclass);
153 for(auto k2 : i->get_subkeys())
154 classes[kclass].insert(i->get_name() + k2);
157 Centre();
158 top_s = new wxFlexGridSizer(3, 1, 0, 0);
159 SetSizer(top_s);
161 t_s = new wxFlexGridSizer(2, 3, 0, 0);
162 wxFlexGridSizer* t2_s = new wxFlexGridSizer(mods.size(), 2, 0, 0);
163 for(auto i2 : mods) {
164 keyentry_mod_data m;
165 std::string i = i2->get_name();
166 t2_s->Add(new wxStaticText(this, wxID_ANY, towxstring(i)), 0, wxGROW);
167 t2_s->Add(m.pressed = new wxComboBox(this, wxID_ANY, boxchoices[1], wxDefaultPosition,
168 wxDefaultSize, 3, boxchoices, wxCB_READONLY), 1, wxGROW);
169 m.pressed->SetSelection(1);
170 modifiers[i] = m;
171 m.pressed->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
172 wxCommandEventHandler(key_entry_dialog::on_change_setting), NULL, this);
174 top_s->Add(t2_s);
175 t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Key")), 0, wxGROW);
176 t_s->Add(mainclass = new wxComboBox(this, wxID_ANY, classeslist[0], wxDefaultPosition, wxDefaultSize,
177 classeslist.size(), &classeslist[0], wxCB_READONLY), 0, wxGROW);
178 mainclass->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
179 wxCommandEventHandler(key_entry_dialog::on_classchange), NULL, this);
180 t_s->Add(mainkey = new wxComboBox(this, wxID_ANY, emptystring, wxDefaultPosition, wxDefaultSize,
181 1, &emptystring, wxCB_READONLY), 1, wxGROW);
182 mainkey->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
183 wxCommandEventHandler(key_entry_dialog::on_change_setting), NULL, this);
184 top_s->Add(t_s);
186 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
187 pbutton_s->Add(press = new wxButton(this, wxID_OK, wxT("Prompt key")), 0, wxGROW);
188 press->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
189 wxCommandEventHandler(key_entry_dialog::on_pressbutton), NULL, this);
190 if(clearable)
191 pbutton_s->Add(clear = new wxButton(this, wxID_OK, wxT("Clear")), 0, wxGROW);
192 pbutton_s->Add(ok = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
193 pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
194 pbutton_s->AddStretchSpacer();
195 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
196 wxCommandEventHandler(key_entry_dialog::on_ok), NULL, this);
197 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
198 wxCommandEventHandler(key_entry_dialog::on_cancel), NULL, this);
199 mainclass->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
200 wxCommandEventHandler(key_entry_dialog::on_classchange), NULL, this);
201 if(clearable)
202 clear->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
203 wxCommandEventHandler(key_entry_dialog::on_clear), NULL, this);
204 top_s->Add(pbutton_s, 0, wxGROW);
206 if(classeslist.empty())
207 throw std::runtime_error("No keys");
209 set_class(tostdstring(classeslist[0]));
211 t_s->SetSizeHints(this);
212 top_s->SetSizeHints(this);
213 Fit();
215 if(spec != "")
216 load_spec(spec);
219 #define TMPFLAG_UNMASKED 65
220 #define TMPFLAG_UNMASKED_LINK_CHILD 2
221 #define TMPFLAG_UNMASKED_LINK_PARENT 68
222 #define TMPFLAG_PRESSED 8
223 #define TMPFLAG_PRESSED_LINK_CHILD 16
224 #define TMPFLAG_PRESSED_LINK_PARENT 32
226 void key_entry_dialog::set_mask(const std::string& mod)
228 CHECK_UI_THREAD;
229 if(!modifiers.count(mod))
230 return;
231 if(modifiers[mod].pressed->GetSelection() == 1) {
232 wxCommandEvent e;
233 modifiers[mod].pressed->SetSelection(0);
234 on_change_setting(e);
238 void key_entry_dialog::set_mod(const std::string& mod)
240 CHECK_UI_THREAD;
241 if(!modifiers.count(mod))
242 return;
243 if(modifiers[mod].pressed->GetSelection() != 1) {
244 wxCommandEvent e;
245 modifiers[mod].pressed->SetSelection(2);
246 on_change_setting(e);
250 void key_entry_dialog::set_set(const std::string& mset,
251 void (key_entry_dialog::*fn)(const std::string& mod))
253 CHECK_UI_THREAD;
254 std::string rem = mset;
255 while(rem != "") {
256 size_t s = rem.find_first_of(",");
257 if(s >= rem.length()) {
258 (this->*fn)(rem);
259 break;
260 } else {
261 (this->*fn)(rem.substr(0, s));
262 rem = rem.substr(s + 1);
267 void key_entry_dialog::load_spec(const std::string& spec)
269 CHECK_UI_THREAD;
270 std::string _spec = spec;
271 size_t s1 = _spec.find_first_of("/");
272 size_t s2 = _spec.find_first_of("|");
273 if(s1 >= _spec.length() || s2 >= _spec.length())
274 return; //Bad.
275 std::string mod = _spec.substr(0, s1);
276 std::string mask = _spec.substr(s1 + 1, s2 - s1 - 1);
277 std::string key = _spec.substr(s2 + 1);
278 set_set(mask, &key_entry_dialog::set_mask);
279 set_set(mod, &key_entry_dialog::set_mod);
280 std::string _class;
281 for(auto i : classes)
282 if(i.second.count(key))
283 _class = i.first;
284 if(_class != "") {
285 set_class(_class);
286 mainclass->SetValue(towxstring(_class));
287 mainkey->SetValue(towxstring(key));
289 t_s->Layout();
290 top_s->Layout();
291 Fit();
294 void key_entry_dialog::on_change_setting(wxCommandEvent& e)
298 void key_entry_dialog::on_pressbutton(wxCommandEvent& e)
300 CHECK_UI_THREAD;
301 press_button_dialog* p = new press_button_dialog(this, inst, wtitle, false);
302 p->ShowModal();
303 std::string key = p->getkey();
304 p->Destroy();
305 std::string _class;
306 for(auto i : classes)
307 if(i.second.count(key))
308 _class = i.first;
309 if(_class == "")
310 return;
311 set_class(_class);
312 mainclass->SetValue(towxstring(_class));
313 mainkey->SetValue(towxstring(key));
316 void key_entry_dialog::on_ok(wxCommandEvent& e)
318 CHECK_UI_THREAD;
319 EndModal(wxID_OK);
322 void key_entry_dialog::on_clear(wxCommandEvent& e)
324 CHECK_UI_THREAD;
325 cleared = true;
326 EndModal(wxID_OK);
329 void key_entry_dialog::on_cancel(wxCommandEvent& e)
331 CHECK_UI_THREAD;
332 EndModal(wxID_CANCEL);
335 void key_entry_dialog::set_class(const std::string& _class)
337 CHECK_UI_THREAD;
338 if(!mainkey)
339 return;
340 if(currentclass == _class)
341 return;
342 mainkey->Clear();
343 for(auto i : classes[_class])
344 mainkey->Append(towxstring(i));
345 currentclass = _class;
346 mainkey->SetSelection(0);
347 t_s->Layout();
348 top_s->Layout();
349 Fit();
352 void key_entry_dialog::on_classchange(wxCommandEvent& e)
354 CHECK_UI_THREAD;
355 set_class(tostdstring(mainclass->GetValue()));
358 std::string key_entry_dialog::getkey()
360 CHECK_UI_THREAD;
361 if(cleared)
362 return "";
363 std::string x;
364 bool f;
365 f = true;
366 for(auto i : modifiers) {
367 if(i.second.pressed->GetSelection() == 2) {
368 if(!f)
369 x = x + ",";
370 f = false;
371 x = x + i.first;
374 x = x + "/";
375 f = true;
376 for(auto i : modifiers) {
377 if(i.second.pressed->GetSelection() != 1) {
378 if(!f)
379 x = x + ",";
380 f = false;
381 x = x + i.first;
384 x = x + "|" + tostdstring(mainkey->GetValue());
385 return x;
388 std::string clean_keystring(const std::string& in)
390 regex_results tmp = regex("(.*)/(.*)\\|(.*)", in);
391 if(!tmp)
392 return in;
393 std::string mods = tmp[1];
394 std::string mask = tmp[2];
395 std::string key = tmp[3];
396 std::set<std::string> _mods, _mask;
397 std::string tmp2;
398 for(auto& tmp2 : token_iterator<char>::foreach(mods, {","}))
399 _mods.insert(tmp2);
400 for(auto& tmp2 : token_iterator<char>::foreach(mask, {","}))
401 _mask.insert(tmp2);
402 for(auto i : _mods)
403 if(!_mask.count(i))
404 return in;
405 std::string out;
406 for(auto i : _mask)
407 if(!_mods.count(i))
408 out = out + "!" + i + "+";
409 else
410 out = out + i + "+";
411 out = out + key;
412 return out;