Refactor emulator status reporting (and fix the statusbar doesn't update bug)
[lsnes.git] / src / platform / wxwidgets / settings-keyentry.cpp
blobc6ad66f7cd9f6792865712dc4d6ab4484f60de2c
1 #include "platform/wxwidgets/settings-common.hpp"
2 #include "platform/wxwidgets/settings-keyentry.hpp"
3 #include "core/keymapper.hpp"
5 namespace
7 int vert_padding = 40;
8 int horiz_padding = 60;
11 press_button_dialog::press_button_dialog(wxWindow* parent, const std::string& title, bool _axis)
12 : wxDialog(parent, wxID_ANY, towxstring(title))
14 axis = _axis;
15 wxStaticText* t;
16 wxBoxSizer* s2 = new wxBoxSizer(wxVERTICAL);
17 wxPanel* p = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, -1), wxWANTS_CHARS);
18 s2->Add(p, 1, wxGROW);
19 lastkbdkey = -1;
20 mouseflag = 0;
21 Centre();
22 wxFlexGridSizer* s = new wxFlexGridSizer(3, 3, 0, 0);
23 p->SetSizer(s);
24 SetSizer(s2);
25 s->Add(horiz_padding, vert_padding);
26 s->Add(0, 0);
27 s->Add(0, 0);
28 s->Add(0, 0);
29 s->Add(t = new wxStaticText(p, wxID_ANY, wxT("Press the key to assign"), wxDefaultPosition,
30 wxSize(-1, -1), wxWANTS_CHARS), 1, wxGROW);
31 s->Add(0, 0);
32 s->Add(0, 0);
33 s->Add(0, 0);
34 s->Add(horiz_padding, vert_padding);
35 p->SetFocus();
36 p->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(press_button_dialog::on_keyboard_down), NULL, this);
37 p->Connect(wxEVT_KEY_UP, wxKeyEventHandler(press_button_dialog::on_keyboard_up), NULL, this);
38 p->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
39 p->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
40 p->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
41 p->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
42 p->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
43 p->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
44 t->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(press_button_dialog::on_keyboard_down), NULL, this);
45 t->Connect(wxEVT_KEY_UP, wxKeyEventHandler(press_button_dialog::on_keyboard_up), NULL, this);
46 t->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
47 t->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
48 t->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
49 t->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
50 t->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
51 t->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(press_button_dialog::on_mouse), NULL, this);
52 settings_activate_keygrab([this](std::string key) { this->dismiss_with(key); });
53 s->SetSizeHints(this);
54 Fit();
57 bool press_button_dialog::handle_mousebtn(wxMouseEvent& e, bool(wxMouseEvent::*down)()const,
58 bool(wxMouseEvent::*up)()const, const std::string& k, int flag)
60 if((e.*down)())
61 mouseflag = flag;
62 if((e.*up)()) {
63 if(mouseflag == flag) {
64 dismiss_with(k);
65 return true;
66 } else
67 mouseflag = 0;
69 return false;
72 void press_button_dialog::on_mouse(wxMouseEvent& e)
74 handle_mousebtn(e, &wxMouseEvent::LeftDown, &wxMouseEvent::LeftUp, "mouse_left", 1);
75 handle_mousebtn(e, &wxMouseEvent::MiddleDown, &wxMouseEvent::MiddleUp, "mouse_center", 2);
76 handle_mousebtn(e, &wxMouseEvent::RightDown, &wxMouseEvent::RightUp, "mouse_right", 3);
79 void press_button_dialog::on_keyboard_down(wxKeyEvent& e)
81 lastkbdkey = e.GetKeyCode();
82 mouseflag = 0;
85 void press_button_dialog::on_keyboard_up(wxKeyEvent& e)
87 int kcode = e.GetKeyCode();
88 if(lastkbdkey == kcode) {
89 dismiss_with(map_keycode_to_key(kcode));
90 } else {
91 lastkbdkey = -1;
92 mouseflag = 0;
96 void press_button_dialog::dismiss_with(const std::string& _k)
98 std::string k = _k;
99 if(k == "")
100 return;
101 if(axis) {
102 //Check that k is a valid axis.
103 try {
104 //Remove the +/- postfix if any.
105 if(k.length() > 1) {
106 char lch = k[k.length() - 1];
107 if(lch == '+' || lch == '-')
108 k = k.substr(0, k.length() - 1);
110 keyboard::key& key = lsnes_kbd.lookup_key(k);
111 if(key.get_type() != keyboard::KBD_KEYTYPE_AXIS)
112 return;
113 } catch(...) {
114 return;
117 if(key == "") {
118 settings_deactivate_keygrab();
119 key = k;
120 EndModal(wxID_OK);
124 key_entry_dialog::key_entry_dialog(wxWindow* parent, const std::string& title, const std::string& spec,
125 bool clearable)
126 : wxDialog(parent, wxID_ANY, towxstring(title), wxDefaultPosition, wxSize(-1, -1))
128 wxString boxchoices[] = { wxT("Released"), wxT("Don't care"), wxT("Pressed") };
129 std::vector<wxString> classeslist;
130 wxString emptystring;
131 std::list<keyboard::modifier*> mods;
132 std::list<keyboard::key*> keys;
134 wtitle = title;
136 cleared = false;
137 std::set<std::string> x;
138 mods = lsnes_kbd.all_modifiers();
139 keys = lsnes_kbd.all_keys();
140 for(auto i : keys) {
141 std::string kclass = i->get_class();
142 if(!x.count(kclass))
143 classeslist.push_back(towxstring(kclass));
144 x.insert(kclass);
145 for(auto k2 : i->get_subkeys())
146 classes[kclass].insert(i->get_name() + k2);
149 Centre();
150 top_s = new wxFlexGridSizer(3, 1, 0, 0);
151 SetSizer(top_s);
153 t_s = new wxFlexGridSizer(2, 3, 0, 0);
154 wxFlexGridSizer* t2_s = new wxFlexGridSizer(mods.size(), 2, 0, 0);
155 for(auto i2 : mods) {
156 keyentry_mod_data m;
157 std::string i = i2->get_name();
158 t2_s->Add(new wxStaticText(this, wxID_ANY, towxstring(i)), 0, wxGROW);
159 t2_s->Add(m.pressed = new wxComboBox(this, wxID_ANY, boxchoices[1], wxDefaultPosition,
160 wxDefaultSize, 3, boxchoices, wxCB_READONLY), 1, wxGROW);
161 m.pressed->SetSelection(1);
162 modifiers[i] = m;
163 m.pressed->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
164 wxCommandEventHandler(key_entry_dialog::on_change_setting), NULL, this);
166 top_s->Add(t2_s);
167 t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Key")), 0, wxGROW);
168 t_s->Add(mainclass = new wxComboBox(this, wxID_ANY, classeslist[0], wxDefaultPosition, wxDefaultSize,
169 classeslist.size(), &classeslist[0], wxCB_READONLY), 0, wxGROW);
170 mainclass->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
171 wxCommandEventHandler(key_entry_dialog::on_classchange), NULL, this);
172 t_s->Add(mainkey = new wxComboBox(this, wxID_ANY, emptystring, wxDefaultPosition, wxDefaultSize,
173 1, &emptystring, wxCB_READONLY), 1, wxGROW);
174 mainkey->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
175 wxCommandEventHandler(key_entry_dialog::on_change_setting), NULL, this);
176 top_s->Add(t_s);
178 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
179 pbutton_s->Add(press = new wxButton(this, wxID_OK, wxT("Prompt key")), 0, wxGROW);
180 press->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
181 wxCommandEventHandler(key_entry_dialog::on_pressbutton), NULL, this);
182 if(clearable)
183 pbutton_s->Add(clear = new wxButton(this, wxID_OK, wxT("Clear")), 0, wxGROW);
184 pbutton_s->Add(ok = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
185 pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
186 pbutton_s->AddStretchSpacer();
187 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
188 wxCommandEventHandler(key_entry_dialog::on_ok), NULL, this);
189 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
190 wxCommandEventHandler(key_entry_dialog::on_cancel), NULL, this);
191 mainclass->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
192 wxCommandEventHandler(key_entry_dialog::on_classchange), NULL, this);
193 if(clearable)
194 clear->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
195 wxCommandEventHandler(key_entry_dialog::on_clear), NULL, this);
196 top_s->Add(pbutton_s, 0, wxGROW);
198 set_class(tostdstring(classeslist[0]));
200 t_s->SetSizeHints(this);
201 top_s->SetSizeHints(this);
202 Fit();
204 if(spec != "")
205 load_spec(spec);
208 #define TMPFLAG_UNMASKED 65
209 #define TMPFLAG_UNMASKED_LINK_CHILD 2
210 #define TMPFLAG_UNMASKED_LINK_PARENT 68
211 #define TMPFLAG_PRESSED 8
212 #define TMPFLAG_PRESSED_LINK_CHILD 16
213 #define TMPFLAG_PRESSED_LINK_PARENT 32
215 void key_entry_dialog::set_mask(const std::string& mod)
217 if(!modifiers.count(mod))
218 return;
219 if(modifiers[mod].pressed->GetSelection() == 1) {
220 wxCommandEvent e;
221 modifiers[mod].pressed->SetSelection(0);
222 on_change_setting(e);
226 void key_entry_dialog::set_mod(const std::string& mod)
228 if(!modifiers.count(mod))
229 return;
230 if(modifiers[mod].pressed->GetSelection() != 1) {
231 wxCommandEvent e;
232 modifiers[mod].pressed->SetSelection(2);
233 on_change_setting(e);
237 void key_entry_dialog::set_set(const std::string& mset,
238 void (key_entry_dialog::*fn)(const std::string& mod))
240 std::string rem = mset;
241 while(rem != "") {
242 size_t s = rem.find_first_of(",");
243 if(s >= rem.length()) {
244 (this->*fn)(rem);
245 break;
246 } else {
247 (this->*fn)(rem.substr(0, s));
248 rem = rem.substr(s + 1);
253 void key_entry_dialog::load_spec(const std::string& spec)
255 std::string _spec = spec;
256 size_t s1 = _spec.find_first_of("/");
257 size_t s2 = _spec.find_first_of("|");
258 if(s1 >= _spec.length() || s2 >= _spec.length())
259 return; //Bad.
260 std::string mod = _spec.substr(0, s1);
261 std::string mask = _spec.substr(s1 + 1, s2 - s1 - 1);
262 std::string key = _spec.substr(s2 + 1);
263 set_set(mask, &key_entry_dialog::set_mask);
264 set_set(mod, &key_entry_dialog::set_mod);
265 std::string _class;
266 for(auto i : classes)
267 if(i.second.count(key))
268 _class = i.first;
269 if(_class != "") {
270 set_class(_class);
271 mainclass->SetValue(towxstring(_class));
272 mainkey->SetValue(towxstring(key));
274 t_s->Layout();
275 top_s->Layout();
276 Fit();
279 void key_entry_dialog::on_change_setting(wxCommandEvent& e)
283 void key_entry_dialog::on_pressbutton(wxCommandEvent& e)
285 press_button_dialog* p = new press_button_dialog(this, wtitle, false);
286 p->ShowModal();
287 std::string key = p->getkey();
288 p->Destroy();
289 std::string _class;
290 for(auto i : classes)
291 if(i.second.count(key))
292 _class = i.first;
293 if(_class == "")
294 return;
295 set_class(_class);
296 mainclass->SetValue(towxstring(_class));
297 mainkey->SetValue(towxstring(key));
300 void key_entry_dialog::on_ok(wxCommandEvent& e)
302 EndModal(wxID_OK);
305 void key_entry_dialog::on_clear(wxCommandEvent& e)
307 cleared = true;
308 EndModal(wxID_OK);
311 void key_entry_dialog::on_cancel(wxCommandEvent& e)
313 EndModal(wxID_CANCEL);
316 void key_entry_dialog::set_class(const std::string& _class)
318 if(!mainkey)
319 return;
320 if(currentclass == _class)
321 return;
322 mainkey->Clear();
323 for(auto i : classes[_class])
324 mainkey->Append(towxstring(i));
325 currentclass = _class;
326 mainkey->SetSelection(0);
327 t_s->Layout();
328 top_s->Layout();
329 Fit();
332 void key_entry_dialog::on_classchange(wxCommandEvent& e)
334 set_class(tostdstring(mainclass->GetValue()));
337 std::string key_entry_dialog::getkey()
339 if(cleared)
340 return "";
341 std::string x;
342 bool f;
343 f = true;
344 for(auto i : modifiers) {
345 if(i.second.pressed->GetSelection() == 2) {
346 if(!f)
347 x = x + ",";
348 f = false;
349 x = x + i.first;
352 x = x + "/";
353 f = true;
354 for(auto i : modifiers) {
355 if(i.second.pressed->GetSelection() != 1) {
356 if(!f)
357 x = x + ",";
358 f = false;
359 x = x + i.first;
362 x = x + "|" + tostdstring(mainkey->GetValue());
363 return x;
366 std::string clean_keystring(const std::string& in)
368 regex_results tmp = regex("(.*)/(.*)\\|(.*)", in);
369 if(!tmp)
370 return in;
371 std::string mods = tmp[1];
372 std::string mask = tmp[2];
373 std::string key = tmp[3];
374 std::set<std::string> _mods, _mask;
375 std::string tmp2;
376 for(auto& tmp2 : token_iterator_foreach(mods, {","}))
377 _mods.insert(tmp2);
378 for(auto& tmp2 : token_iterator_foreach(mask, {","}))
379 _mask.insert(tmp2);
380 for(auto i : _mods)
381 if(!_mask.count(i))
382 return in;
383 std::string out;
384 for(auto i : _mask)
385 if(!_mods.count(i))
386 out = out + "!" + i + "+";
387 else
388 out = out + i + "+";
389 out = out + key;
390 return out;