New settings framework
[lsnes.git] / src / platform / wxwidgets / settings.cpp
blobe8f923103691cf0c1f39669651d93dbafe59b3ec
1 #include "platform/wxwidgets/platform.hpp"
2 #include "core/command.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/mainloop.hpp"
5 #include "core/moviedata.hpp"
6 #include "core/settings.hpp"
7 #include "library/string.hpp"
9 #include <wx/wx.h>
10 #include <wx/notebook.h>
11 #include <wx/treectrl.h>
12 #include <wx/event.h>
13 #include <wx/control.h>
14 #include <wx/combobox.h>
15 #include <vector>
16 #include <string>
18 #include "library/string.hpp"
19 #include <boost/lexical_cast.hpp>
20 #include <sstream>
22 extern "C"
24 #ifndef UINT64_C
25 #define UINT64_C(val) val##ULL
26 #endif
27 #include <libswscale/swscale.h>
30 #define AMODE_DISABLED "Disabled"
31 #define AMODE_AXIS_PAIR "Axis"
32 #define AMODE_AXIS_PAIR_INVERSE "Axis (inverted)"
33 #define AMODE_PRESSURE_M0 "Pressure - to 0"
34 #define AMODE_PRESSURE_MP "Pressure - to +"
35 #define AMODE_PRESSURE_0M "Pressure 0 to -"
36 #define AMODE_PRESSURE_0P "Pressure 0 to +"
37 #define AMODE_PRESSURE_PM "Pressure + to -"
38 #define AMODE_PRESSURE_P0 "Pressure + to 0"
40 const char* scalealgo_choices[] = {"Fast Bilinear", "Bilinear", "Bicubic", "Experimential", "Point", "Area",
41 "Bicubic-Linear", "Gauss", "Sinc", "Lanczos", "Spline"};
43 namespace
45 class wxdialog_pressbutton;
46 volatile bool keygrab_active = false;
47 std::string pkey;
48 wxdialog_pressbutton* presser = NULL;
50 std::string get_title(int mode)
52 switch(mode) {
53 case 0:
54 return "lsnes: Configure emulator";
55 case 1:
56 return "lsnes: Configure hotkeys";
57 case 2:
58 return "lsnes: Configure controllers";
60 return "";
63 void report_grab_key(const std::string& name);
65 class keygrabber : public keyboard_event_listener
67 public:
68 keygrabber() { keygrab_active = false; }
69 void on_key_event(keyboard_modifier_set& mods, keyboard_key& key, keyboard_event& event)
71 if(!keygrab_active)
72 return;
73 uint32_t dev = event.get_change_mask();
74 auto subkeys = key.get_subkeys();
75 for(unsigned i = 0; i < 16 && i < subkeys.size(); i++) {
76 std::string pname = key.get_name() + subkeys[i];
77 if(((dev >> (2 * i)) & 3) == 3)
78 pkey = pname;
79 if(((dev >> (2 * i)) & 3) == 2) {
80 if(pkey == pname) {
81 keygrab_active = false;
82 std::string tmp = pkey;
83 runuifun([tmp]() { report_grab_key(tmp); });
84 } else
85 pkey = "";
89 } keygrabber;
91 std::string clean_keystring(const std::string& in)
93 regex_results tmp = regex("(.*)/(.*)\\|(.*)", in);
94 if(!tmp)
95 return in;
96 std::string mods = tmp[1];
97 std::string mask = tmp[2];
98 std::string key = tmp[3];
99 std::set<std::string> _mods, _mask;
100 std::string tmp2;
101 while(mods != "") {
102 extract_token(mods, tmp2, ",");
103 _mods.insert(tmp2);
105 while(mask != "") {
106 extract_token(mask, tmp2, ",");
107 _mask.insert(tmp2);
109 for(auto i : _mods)
110 if(!_mask.count(i))
111 return in;
112 std::string out;
113 for(auto i : _mask)
114 if(!_mods.count(i))
115 out = out + "!" + i + "+";
116 else
117 out = out + i + "+";
118 out = out + key;
119 return out;
123 * Extract token out of string.
125 * Parameter str: The original string and the rest of the string on return.
126 * Parameter tok: The extracted token will be written here.
127 * Parameter sep: The characters to split on (empty always extracts the rest).
128 * Parameter seq: If true, skip whole sequence of token ending characters.
129 * Returns: The character token splitting occured on (-1 if end of string, -2 if string is empty).
131 int extract_token(std::string& str, std::string& tok, const char* sep, bool seq = false) throw(std::bad_alloc);
133 class wxdialog_pressbutton : public wxDialog
135 public:
136 wxdialog_pressbutton(wxWindow* parent, const std::string& title, bool axis);
137 std::string getkey() { return key; }
138 void on_mouse(wxMouseEvent& e);
139 void on_keyboard_up(wxKeyEvent& e);
140 void on_keyboard_down(wxKeyEvent& e);
141 void dismiss_with(const std::string& k);
142 private:
143 bool handle_mousebtn(wxMouseEvent& e, bool(wxMouseEvent::*down)()const, bool(wxMouseEvent::*up)()const,
144 const std::string& k, int flag);
145 std::string key;
146 int mouseflag;
147 int lastkbdkey;
148 bool axis;
151 void report_grab_key(const std::string& name)
153 presser->dismiss_with(name);
156 int vert_padding = 40;
157 int horiz_padding = 60;
159 wxdialog_pressbutton::wxdialog_pressbutton(wxWindow* parent, const std::string& title, bool _axis)
160 : wxDialog(parent, wxID_ANY, towxstring(title))
162 axis = _axis;
163 wxStaticText* t;
164 wxBoxSizer* s2 = new wxBoxSizer(wxVERTICAL);
165 wxPanel* p = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, -1), wxWANTS_CHARS);
166 s2->Add(p, 1, wxGROW);
167 lastkbdkey = -1;
168 mouseflag = 0;
169 Centre();
170 wxFlexGridSizer* s = new wxFlexGridSizer(3, 3, 0, 0);
171 p->SetSizer(s);
172 SetSizer(s2);
173 s->Add(horiz_padding, vert_padding);
174 s->Add(0, 0);
175 s->Add(0, 0);
176 s->Add(0, 0);
177 s->Add(t = new wxStaticText(p, wxID_ANY, wxT("Press the key to assign"), wxDefaultPosition,
178 wxSize(-1, -1), wxWANTS_CHARS), 1, wxGROW);
179 s->Add(0, 0);
180 s->Add(0, 0);
181 s->Add(0, 0);
182 s->Add(horiz_padding, vert_padding);
183 p->SetFocus();
184 p->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(wxdialog_pressbutton::on_keyboard_down), NULL, this);
185 p->Connect(wxEVT_KEY_UP, wxKeyEventHandler(wxdialog_pressbutton::on_keyboard_up), NULL, this);
186 p->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
187 p->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
188 p->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
189 p->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
190 p->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
191 p->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
192 t->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(wxdialog_pressbutton::on_keyboard_down), NULL, this);
193 t->Connect(wxEVT_KEY_UP, wxKeyEventHandler(wxdialog_pressbutton::on_keyboard_up), NULL, this);
194 t->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
195 t->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
196 t->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
197 t->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
198 t->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
199 t->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxdialog_pressbutton::on_mouse), NULL, this);
200 presser = this;
201 keygrab_active = true;
202 s->SetSizeHints(this);
203 Fit();
206 bool wxdialog_pressbutton::handle_mousebtn(wxMouseEvent& e, bool(wxMouseEvent::*down)()const,
207 bool(wxMouseEvent::*up)()const, const std::string& k, int flag)
209 if((e.*down)())
210 mouseflag = flag;
211 if((e.*up)()) {
212 if(mouseflag == flag) {
213 dismiss_with(k);
214 return true;
215 } else
216 mouseflag = 0;
218 return false;
221 void wxdialog_pressbutton::on_mouse(wxMouseEvent& e)
223 handle_mousebtn(e, &wxMouseEvent::LeftDown, &wxMouseEvent::LeftUp, "mouse_left", 1);
224 handle_mousebtn(e, &wxMouseEvent::MiddleDown, &wxMouseEvent::MiddleUp, "mouse_center", 2);
225 handle_mousebtn(e, &wxMouseEvent::RightDown, &wxMouseEvent::RightUp, "mouse_right", 3);
228 void wxdialog_pressbutton::on_keyboard_down(wxKeyEvent& e)
230 lastkbdkey = e.GetKeyCode();
231 mouseflag = 0;
234 void wxdialog_pressbutton::on_keyboard_up(wxKeyEvent& e)
236 int kcode = e.GetKeyCode();
237 if(lastkbdkey == kcode) {
238 dismiss_with(map_keycode_to_key(kcode));
239 } else {
240 lastkbdkey = -1;
241 mouseflag = 0;
245 void wxdialog_pressbutton::dismiss_with(const std::string& _k)
247 std::string k = _k;
248 if(k == "")
249 return;
250 if(axis) {
251 //Check that k is a valid axis.
252 try {
253 //Remove the +/- postfix if any.
254 if(k.length() > 1) {
255 char lch = k[k.length() - 1];
256 if(lch == '+' || lch == '-')
257 k = k.substr(0, k.length() - 1);
259 keyboard_key& key = lsnes_kbd.lookup_key(k);
260 if(key.get_type() != KBD_KEYTYPE_AXIS)
261 return;
262 } catch(...) {
263 return;
266 if(key == "") {
267 keygrab_active = false;
268 key = k;
269 EndModal(wxID_OK);
273 struct keyentry_mod_data
275 wxComboBox* pressed;
276 unsigned tmpflags;
279 class wxdialog_keyentry : public wxDialog
281 public:
282 wxdialog_keyentry(wxWindow* parent, const std::string& title, const std::string& spec,
283 bool clearable);
284 void on_change_setting(wxCommandEvent& e);
285 void on_ok(wxCommandEvent& e);
286 void on_cancel(wxCommandEvent& e);
287 void on_clear(wxCommandEvent& e);
288 void on_pressbutton(wxCommandEvent& e);
289 void on_classchange(wxCommandEvent& e);
290 std::string getkey();
291 private:
292 void set_mask(const std::string& mod);
293 void set_mod(const std::string& mod);
294 void set_set(const std::string& mset,
295 void (wxdialog_keyentry::*fn)(const std::string& mod));
296 void load_spec(const std::string& spec);
297 void set_class(const std::string& _class);
298 std::map<std::string, keyentry_mod_data> modifiers;
299 std::map<std::string, std::set<std::string>> classes;
300 std::string wtitle;
301 std::string currentclass;
302 wxFlexGridSizer* top_s;
303 wxFlexGridSizer* t_s;
304 wxComboBox* mainclass;
305 wxComboBox* mainkey;
306 wxButton* press;
307 wxButton* ok;
308 wxButton* cancel;
309 wxButton* clear;
310 bool cleared;
313 wxdialog_keyentry::wxdialog_keyentry(wxWindow* parent, const std::string& title, const std::string& spec,
314 bool clearable)
315 : wxDialog(parent, wxID_ANY, towxstring(title), wxDefaultPosition, wxSize(-1, -1))
317 wxString boxchoices[] = { wxT("Released"), wxT("Don't care"), wxT("Pressed") };
318 std::vector<wxString> classeslist;
319 wxString emptystring;
320 std::list<keyboard_modifier*> mods;
321 std::list<keyboard_key*> keys;
323 wtitle = title;
325 cleared = false;
326 std::set<std::string> x;
327 mods = lsnes_kbd.all_modifiers();
328 keys = lsnes_kbd.all_keys();
329 for(auto i : keys) {
330 std::string kclass = i->get_class();
331 if(!x.count(kclass))
332 classeslist.push_back(towxstring(kclass));
333 x.insert(kclass);
334 for(auto k2 : i->get_subkeys())
335 classes[kclass].insert(i->get_name() + k2);
338 Centre();
339 top_s = new wxFlexGridSizer(3, 1, 0, 0);
340 SetSizer(top_s);
342 t_s = new wxFlexGridSizer(2, 3, 0, 0);
343 wxFlexGridSizer* t2_s = new wxFlexGridSizer(mods.size(), 2, 0, 0);
344 for(auto i2 : mods) {
345 keyentry_mod_data m;
346 std::string i = i2->get_name();
347 t2_s->Add(new wxStaticText(this, wxID_ANY, towxstring(i)), 0, wxGROW);
348 t2_s->Add(m.pressed = new wxComboBox(this, wxID_ANY, boxchoices[1], wxDefaultPosition,
349 wxDefaultSize, 3, boxchoices, wxCB_READONLY), 1, wxGROW);
350 m.pressed->SetSelection(1);
351 modifiers[i] = m;
352 m.pressed->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
353 wxCommandEventHandler(wxdialog_keyentry::on_change_setting), NULL, this);
355 top_s->Add(t2_s);
356 t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Key")), 0, wxGROW);
357 t_s->Add(mainclass = new wxComboBox(this, wxID_ANY, classeslist[0], wxDefaultPosition, wxDefaultSize,
358 classeslist.size(), &classeslist[0], wxCB_READONLY), 0, wxGROW);
359 mainclass->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
360 wxCommandEventHandler(wxdialog_keyentry::on_classchange), NULL, this);
361 t_s->Add(mainkey = new wxComboBox(this, wxID_ANY, emptystring, wxDefaultPosition, wxDefaultSize,
362 1, &emptystring, wxCB_READONLY), 1, wxGROW);
363 mainkey->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
364 wxCommandEventHandler(wxdialog_keyentry::on_change_setting), NULL, this);
365 top_s->Add(t_s);
367 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
368 pbutton_s->Add(press = new wxButton(this, wxID_OK, wxT("Prompt key")), 0, wxGROW);
369 press->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
370 wxCommandEventHandler(wxdialog_keyentry::on_pressbutton), NULL, this);
371 if(clearable)
372 pbutton_s->Add(clear = new wxButton(this, wxID_OK, wxT("Clear")), 0, wxGROW);
373 pbutton_s->Add(ok = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
374 pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
375 pbutton_s->AddStretchSpacer();
376 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
377 wxCommandEventHandler(wxdialog_keyentry::on_ok), NULL, this);
378 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
379 wxCommandEventHandler(wxdialog_keyentry::on_cancel), NULL, this);
380 mainclass->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
381 wxCommandEventHandler(wxdialog_keyentry::on_classchange), NULL, this);
382 if(clearable)
383 clear->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
384 wxCommandEventHandler(wxdialog_keyentry::on_clear), NULL, this);
385 top_s->Add(pbutton_s, 0, wxGROW);
387 set_class(tostdstring(classeslist[0]));
389 t_s->SetSizeHints(this);
390 top_s->SetSizeHints(this);
391 Fit();
393 if(spec != "")
394 load_spec(spec);
397 #define TMPFLAG_UNMASKED 65
398 #define TMPFLAG_UNMASKED_LINK_CHILD 2
399 #define TMPFLAG_UNMASKED_LINK_PARENT 68
400 #define TMPFLAG_PRESSED 8
401 #define TMPFLAG_PRESSED_LINK_CHILD 16
402 #define TMPFLAG_PRESSED_LINK_PARENT 32
404 void wxdialog_keyentry::set_mask(const std::string& mod)
406 if(!modifiers.count(mod))
407 return;
408 if(modifiers[mod].pressed->GetSelection() == 1) {
409 wxCommandEvent e;
410 modifiers[mod].pressed->SetSelection(0);
411 on_change_setting(e);
415 void wxdialog_keyentry::set_mod(const std::string& mod)
417 if(!modifiers.count(mod))
418 return;
419 if(modifiers[mod].pressed->GetSelection() != 1) {
420 wxCommandEvent e;
421 modifiers[mod].pressed->SetSelection(2);
422 on_change_setting(e);
426 void wxdialog_keyentry::set_set(const std::string& mset,
427 void (wxdialog_keyentry::*fn)(const std::string& mod))
429 std::string rem = mset;
430 while(rem != "") {
431 size_t s = rem.find_first_of(",");
432 if(s >= rem.length()) {
433 (this->*fn)(rem);
434 break;
435 } else {
436 (this->*fn)(rem.substr(0, s));
437 rem = rem.substr(s + 1);
442 void wxdialog_keyentry::load_spec(const std::string& spec)
444 std::string _spec = spec;
445 size_t s1 = _spec.find_first_of("/");
446 size_t s2 = _spec.find_first_of("|");
447 if(s1 >= _spec.length() || s2 >= _spec.length())
448 return; //Bad.
449 std::string mod = _spec.substr(0, s1);
450 std::string mask = _spec.substr(s1 + 1, s2 - s1 - 1);
451 std::string key = _spec.substr(s2 + 1);
452 set_set(mask, &wxdialog_keyentry::set_mask);
453 set_set(mod, &wxdialog_keyentry::set_mod);
454 std::string _class;
455 for(auto i : classes)
456 if(i.second.count(key))
457 _class = i.first;
458 if(_class != "") {
459 set_class(_class);
460 mainclass->SetValue(towxstring(_class));
461 mainkey->SetValue(towxstring(key));
463 t_s->Layout();
464 top_s->Layout();
465 Fit();
468 void wxdialog_keyentry::on_change_setting(wxCommandEvent& e)
472 void wxdialog_keyentry::on_pressbutton(wxCommandEvent& e)
474 wxdialog_pressbutton* p = new wxdialog_pressbutton(this, wtitle, false);
475 p->ShowModal();
476 std::string key = p->getkey();
477 p->Destroy();
478 std::string _class;
479 for(auto i : classes)
480 if(i.second.count(key))
481 _class = i.first;
482 if(_class == "")
483 return;
484 set_class(_class);
485 mainclass->SetValue(towxstring(_class));
486 mainkey->SetValue(towxstring(key));
489 void wxdialog_keyentry::on_ok(wxCommandEvent& e)
491 EndModal(wxID_OK);
494 void wxdialog_keyentry::on_clear(wxCommandEvent& e)
496 cleared = true;
497 EndModal(wxID_OK);
500 void wxdialog_keyentry::on_cancel(wxCommandEvent& e)
502 EndModal(wxID_CANCEL);
505 void wxdialog_keyentry::set_class(const std::string& _class)
507 if(!mainkey)
508 return;
509 if(currentclass == _class)
510 return;
511 mainkey->Clear();
512 for(auto i : classes[_class])
513 mainkey->Append(towxstring(i));
514 currentclass = _class;
515 mainkey->SetSelection(0);
516 t_s->Layout();
517 top_s->Layout();
518 Fit();
521 void wxdialog_keyentry::on_classchange(wxCommandEvent& e)
523 set_class(tostdstring(mainclass->GetValue()));
526 std::string wxdialog_keyentry::getkey()
528 if(cleared)
529 return "";
530 std::string x;
531 bool f;
532 f = true;
533 for(auto i : modifiers) {
534 if(i.second.pressed->GetSelection() == 2) {
535 if(!f)
536 x = x + ",";
537 f = false;
538 x = x + i.first;
541 x = x + "/";
542 f = true;
543 for(auto i : modifiers) {
544 if(i.second.pressed->GetSelection() != 1) {
545 if(!f)
546 x = x + ",";
547 f = false;
548 x = x + i.first;
551 x = x + "|" + tostdstring(mainkey->GetValue());
552 return x;
556 class wxeditor_esettings_joystick_aconfig : public wxDialog
558 public:
559 wxeditor_esettings_joystick_aconfig(wxWindow* parent, const std::string& _aname);
560 ~wxeditor_esettings_joystick_aconfig();
561 void on_ok(wxCommandEvent& e);
562 void on_cancel(wxCommandEvent& e);
563 private:
564 std::string aname;
565 wxComboBox* type;
566 wxTextCtrl* low;
567 wxTextCtrl* mid;
568 wxTextCtrl* hi;
569 wxTextCtrl* tol;
570 wxButton* okbutton;
571 wxButton* cancel;
574 wxeditor_esettings_joystick_aconfig::wxeditor_esettings_joystick_aconfig(wxWindow* parent, const std::string& _aname)
575 : wxDialog(parent, -1, towxstring("Configure axis " + _aname))
577 wxString choices[9];
578 int didx = 1;
579 choices[0] = wxT(AMODE_DISABLED);
580 choices[1] = wxT(AMODE_AXIS_PAIR);
581 choices[2] = wxT(AMODE_AXIS_PAIR_INVERSE);
582 choices[3] = wxT(AMODE_PRESSURE_M0);
583 choices[4] = wxT(AMODE_PRESSURE_MP);
584 choices[5] = wxT(AMODE_PRESSURE_0M);
585 choices[6] = wxT(AMODE_PRESSURE_0P);
586 choices[7] = wxT(AMODE_PRESSURE_PM);
587 choices[8] = wxT(AMODE_PRESSURE_P0);
589 aname = _aname;
591 keyboard_axis_calibration c;
592 keyboard_key* _k = lsnes_kbd.try_lookup_key(aname);
593 keyboard_key_axis* k = NULL;
594 if(_k)
595 k = _k->cast_axis();
596 if(k)
597 c = k->get_calibration();
599 if(c.mode == -1) didx = 0;
600 else if(c.mode == 1 && c.esign_a == -1 && c.esign_b == 1) didx = 1;
601 else if(c.mode == 1 && c.esign_a == 1 && c.esign_b == -1) didx = 2;
602 else if(c.mode == 0 && c.esign_a == -1 && c.esign_b == 0) didx = 3;
603 else if(c.mode == 0 && c.esign_a == -1 && c.esign_b == 1) didx = 4;
604 else if(c.mode == 0 && c.esign_a == 0 && c.esign_b == -1) didx = 5;
605 else if(c.mode == 0 && c.esign_a == 0 && c.esign_b == 1) didx = 6;
606 else if(c.mode == 0 && c.esign_a == 1 && c.esign_b == -1) didx = 7;
607 else if(c.mode == 0 && c.esign_a == 1 && c.esign_b == 0) didx = 8;
609 Centre();
610 wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
611 SetSizer(top_s);
613 wxFlexGridSizer* t_s = new wxFlexGridSizer(5, 2, 0, 0);
614 t_s->Add(new wxStaticText(this, -1, wxT("Type: ")), 0, wxGROW);
615 t_s->Add(type = new wxComboBox(this, wxID_ANY, choices[didx], wxDefaultPosition, wxDefaultSize,
616 9, choices, wxCB_READONLY), 1, wxGROW);
617 t_s->Add(new wxStaticText(this, -1, wxT("Low: ")), 0, wxGROW);
618 t_s->Add(low = new wxTextCtrl(this, -1, towxstring((stringfmt() << c.left).str()), wxDefaultPosition,
619 wxSize(100, -1)), 1, wxGROW);
620 t_s->Add(new wxStaticText(this, -1, wxT("Middle: ")), 0, wxGROW);
621 t_s->Add(mid = new wxTextCtrl(this, -1, towxstring((stringfmt() << c.center).str()),
622 wxDefaultPosition, wxSize(100, -1)), 1, wxGROW);
623 t_s->Add(new wxStaticText(this, -1, wxT("High: ")), 0, wxGROW);
624 t_s->Add(hi = new wxTextCtrl(this, -1, towxstring((stringfmt() << c.right).str()),
625 wxDefaultPosition, wxSize(100, -1)), 1, wxGROW);
626 t_s->Add(new wxStaticText(this, -1, wxT("Tolerance: ")), 0, wxGROW);
627 t_s->Add(tol = new wxTextCtrl(this, -1, towxstring((stringfmt() << c.nullwidth).str()),
628 wxDefaultPosition, wxSize(100, -1)), 1, wxGROW);
629 top_s->Add(t_s);
631 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
632 pbutton_s->AddStretchSpacer();
633 pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
634 pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
635 okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
636 wxCommandEventHandler(wxeditor_esettings_joystick_aconfig::on_ok), NULL, this);
637 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
638 wxCommandEventHandler(wxeditor_esettings_joystick_aconfig::on_cancel), NULL, this);
639 top_s->Add(pbutton_s, 0, wxGROW);
641 t_s->SetSizeHints(this);
642 top_s->SetSizeHints(this);
643 Fit();
646 wxeditor_esettings_joystick_aconfig::~wxeditor_esettings_joystick_aconfig()
650 void wxeditor_esettings_joystick_aconfig::on_ok(wxCommandEvent& e)
652 std::string _type = tostdstring(type->GetValue());
653 std::string _low = tostdstring(low->GetValue());
654 std::string _mid = tostdstring(mid->GetValue());
655 std::string _hi = tostdstring(hi->GetValue());
656 std::string _tol = tostdstring(tol->GetValue());
657 keyboard_key_axis* k = NULL;
658 keyboard_key* _k;
660 _k = lsnes_kbd.try_lookup_key(aname);
661 if(_k)
662 k = _k->cast_axis();
663 if(!k) {
664 //Axis gone away?
665 EndModal(wxID_OK);
666 return;
669 keyboard_axis_calibration c;
670 const char* bad_what = NULL;
671 try {
672 bad_what = "Bad axis type";
673 if(_type == AMODE_AXIS_PAIR) {c.mode = 1; c.esign_a = -1; c.esign_b = 1; }
674 else if(_type == AMODE_AXIS_PAIR_INVERSE) {c.mode = 1; c.esign_a = 1; c.esign_b = -1; }
675 else if(_type == AMODE_DISABLED) {c.mode = -1; c.esign_a = -1; c.esign_b = 1; }
676 else if(_type == AMODE_PRESSURE_0M) {c.mode = 0; c.esign_a = 0; c.esign_b = -1; }
677 else if(_type == AMODE_PRESSURE_0P) {c.mode = 0; c.esign_a = 0; c.esign_b = 1; }
678 else if(_type == AMODE_PRESSURE_M0) {c.mode = 0; c.esign_a = -1; c.esign_b = 0; }
679 else if(_type == AMODE_PRESSURE_MP) {c.mode = 0; c.esign_a = -1; c.esign_b = 1; }
680 else if(_type == AMODE_PRESSURE_P0) {c.mode = 0; c.esign_a = 1; c.esign_b = 0; }
681 else if(_type == AMODE_PRESSURE_PM) {c.mode = 0; c.esign_a = 1; c.esign_b = -1; }
682 else
683 throw 42;
684 bad_what = "Bad low calibration value (range is -32768 - 32767)";
685 c.left = boost::lexical_cast<int32_t>(_low);
686 bad_what = "Bad middle calibration value (range is -32768 - 32767)";
687 c.center = boost::lexical_cast<int32_t>(_mid);
688 bad_what = "Bad high calibration value (range is -32768 - 32767)";
689 c.right = boost::lexical_cast<int32_t>(_hi);
690 bad_what = "Bad tolerance (range is 0 - 1)";
691 c.nullwidth = boost::lexical_cast<double>(_tol);
692 if(c.nullwidth <= 0 || c.nullwidth >= 1)
693 throw 42;
694 } catch(...) {
695 wxMessageBox(towxstring(bad_what), _T("Error"), wxICON_EXCLAMATION | wxOK);
696 return;
698 k->set_calibration(c);
699 EndModal(wxID_OK);
702 void wxeditor_esettings_joystick_aconfig::on_cancel(wxCommandEvent& e)
704 EndModal(wxID_CANCEL);
707 class wxeditor_esettings_joystick : public wxPanel
709 public:
710 wxeditor_esettings_joystick(wxWindow* parent);
711 ~wxeditor_esettings_joystick();
712 void on_configure(wxCommandEvent& e);
713 private:
714 void refresh();
715 wxSizer* jgrid;
716 wxStaticText* no_joysticks;
717 std::map<std::string, wxButton*> buttons;
718 std::map<int, std::string> ids;
719 int last_id;
722 namespace
724 std::string formattype(const keyboard_axis_calibration& s)
726 if(s.mode == -1) return AMODE_DISABLED;
727 else if(s.mode == 1 && s.esign_b == 1) return AMODE_AXIS_PAIR;
728 else if(s.mode == 1 && s.esign_b == -1) return AMODE_AXIS_PAIR_INVERSE;
729 else if(s.mode == 0 && s.esign_a == 0 && s.esign_b == -1) return AMODE_PRESSURE_0M;
730 else if(s.mode == 0 && s.esign_a == 0 && s.esign_b == 1) return AMODE_PRESSURE_0P;
731 else if(s.mode == 0 && s.esign_a == -1 && s.esign_b == 0) return AMODE_PRESSURE_M0;
732 else if(s.mode == 0 && s.esign_a == -1 && s.esign_b == 1) return AMODE_PRESSURE_MP;
733 else if(s.mode == 0 && s.esign_a == 1 && s.esign_b == 0) return AMODE_PRESSURE_P0;
734 else if(s.mode == 0 && s.esign_a == 1 && s.esign_b == -1) return AMODE_PRESSURE_PM;
735 else return "Unknown";
738 std::string formatsettings(const std::string& name, const keyboard_axis_calibration& s)
740 return (stringfmt() << name << ": " << formattype(s) << " low:" << s.left << " mid:"
741 << s.center << " high:" << s.right << " tolerance:" << s.nullwidth).str();
744 std::string getalgo(int flags)
746 for(size_t i = 0; i < sizeof(scalealgo_choices) / sizeof(scalealgo_choices[0]); i++)
747 if(flags & (1 << i))
748 return scalealgo_choices[i];
749 return "unknown";
753 wxeditor_esettings_joystick::wxeditor_esettings_joystick(wxWindow* parent)
754 : wxPanel(parent, -1)
756 last_id = wxID_HIGHEST + 1;
757 no_joysticks = new wxStaticText(this, wxID_ANY, wxT("Sorry, no joysticks detected"));
758 no_joysticks->SetMinSize(wxSize(400, -1));
759 no_joysticks->Hide();
760 SetSizer(jgrid = new wxFlexGridSizer(0, 1, 0, 0));
761 refresh();
762 jgrid->SetSizeHints(this);
763 Fit();
766 wxeditor_esettings_joystick::~wxeditor_esettings_joystick()
770 void wxeditor_esettings_joystick::on_configure(wxCommandEvent& e)
772 if(!ids.count(e.GetId()))
773 return;
774 wxDialog* d = new wxeditor_esettings_joystick_aconfig(this, ids[e.GetId()]);
775 d->ShowModal();
776 d->Destroy();
777 refresh();
780 void wxeditor_esettings_joystick::refresh()
782 //Collect the new settings.
783 std::map<std::string, keyboard_axis_calibration> x;
784 auto axisset = lsnes_kbd.all_keys();
785 for(auto i : axisset) {
786 keyboard_key_axis* j = i->cast_axis();
787 if(!j)
788 continue;
789 x[i->get_name()] = j->get_calibration();
792 unsigned jcount = 0;
793 for(auto i : x) {
794 jcount++;
795 if(buttons.count(i.first)) {
796 //Okay, this already exists. Update.
797 buttons[i.first]->SetLabel(towxstring(formatsettings(i.first, i.second)));
798 if(!buttons[i.first]->IsShown()) {
799 jgrid->Add(buttons[i.first], 1, wxGROW);
800 buttons[i.first]->Show();
802 } else {
803 //New button.
804 ids[last_id] = i.first;
805 buttons[i.first] = new wxButton(this, last_id++, towxstring(formatsettings(i.first,
806 i.second)));
807 buttons[i.first]->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
808 wxCommandEventHandler(wxeditor_esettings_joystick::on_configure), NULL, this);
809 jgrid->Add(buttons[i.first], 1, wxGROW);
812 for(auto i : buttons) {
813 if(!x.count(i.first)) {
814 //Removed button.
815 i.second->Hide();
816 jgrid->Detach(i.second);
819 if(jcount > 0) {
820 jgrid->Detach(no_joysticks);
821 no_joysticks->Hide();
822 } else {
823 no_joysticks->Show();
824 jgrid->Add(no_joysticks);
826 jgrid->Layout();
827 this->Refresh();
828 Fit();
831 class wxeditor_esettings_settings : public wxPanel
833 public:
834 wxeditor_esettings_settings(wxWindow* parent);
835 ~wxeditor_esettings_settings();
836 void on_configure(wxCommandEvent& e);
837 wxCheckBox* hflip;
838 wxCheckBox* vflip;
839 wxCheckBox* rotate;
840 private:
841 void refresh();
842 wxFlexGridSizer* top_s;
843 wxStaticText* xscale;
844 wxStaticText* yscale;
845 wxStaticText* algo;
848 wxeditor_esettings_settings::wxeditor_esettings_settings(wxWindow* parent)
849 : wxPanel(parent, -1)
851 wxButton* tmp;
852 top_s = new wxFlexGridSizer(8, 3, 0, 0);
853 SetSizer(top_s);
854 top_s->Add(new wxStaticText(this, -1, wxT("X scale factor: ")), 0, wxGROW);
855 top_s->Add(xscale = new wxStaticText(this, -1, wxT("")), 1, wxGROW);
856 top_s->Add(tmp = new wxButton(this, wxID_HIGHEST + 6, wxT("Change...")), 0, wxGROW);
857 tmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_settings::on_configure),
858 NULL, this);
859 top_s->Add(new wxStaticText(this, -1, wxT("Y scale factor: ")), 0, wxGROW);
860 top_s->Add(yscale = new wxStaticText(this, -1, wxT("")), 1, wxGROW);
861 top_s->Add(tmp = new wxButton(this, wxID_HIGHEST + 7, wxT("Change...")), 0, wxGROW);
862 tmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_settings::on_configure),
863 NULL, this);
864 top_s->Add(new wxStaticText(this, -1, wxT("Scaling type: ")), 0, wxGROW);
865 top_s->Add(algo = new wxStaticText(this, -1, wxT("")), 1, wxGROW);
866 top_s->Add(tmp = new wxButton(this, wxID_HIGHEST + 8, wxT("Change...")), 0, wxGROW);
867 tmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_settings::on_configure),
868 NULL, this);
869 top_s->Add(new wxStaticText(this, -1, wxT("Hflip: ")), 0, wxGROW);
870 top_s->Add(hflip = new wxCheckBox(this, -1, wxT("")), 1, wxGROW);
871 top_s->Add(new wxStaticText(this, -1, wxT("")), 0, wxGROW);
872 top_s->Add(new wxStaticText(this, -1, wxT("Vflip: ")), 0, wxGROW);
873 top_s->Add(vflip = new wxCheckBox(this, -1, wxT("")), 1, wxGROW);
874 top_s->Add(new wxStaticText(this, -1, wxT("")), 0, wxGROW);
875 top_s->Add(new wxStaticText(this, -1, wxT("Rotate: ")), 0, wxGROW);
876 top_s->Add(rotate = new wxCheckBox(this, -1, wxT("")), 1, wxGROW);
877 top_s->Add(new wxStaticText(this, -1, wxT("")), 0, wxGROW);
879 refresh();
880 top_s->SetSizeHints(this);
881 Fit();
883 wxeditor_esettings_settings::~wxeditor_esettings_settings()
887 void wxeditor_esettings_settings::on_configure(wxCommandEvent& e)
889 std::vector<std::string> sa_choices;
890 std::string v;
891 int newflags = 1;
892 for(size_t i = 0; i < sizeof(scalealgo_choices) / sizeof(scalealgo_choices[0]); i++)
893 sa_choices.push_back(scalealgo_choices[i]);
894 std::string name;
895 if(e.GetId() <= wxID_HIGHEST || e.GetId() > wxID_HIGHEST + 10)
896 return;
897 std::string val;
898 try {
899 if(e.GetId() == wxID_HIGHEST + 6) {
900 val = (stringfmt() << horizontal_scale_factor).str();
901 val = pick_text(this, "Set X scaling factor", "Enter new horizontal scale factor (0.25-10):",
902 val);
903 } else if(e.GetId() == wxID_HIGHEST + 7) {
904 val = (stringfmt() << horizontal_scale_factor).str();
905 val = pick_text(this, "Set Y scaling factor", "Enter new vertical scale factor (0.25-10):",
906 val);
907 } else if(e.GetId() == wxID_HIGHEST + 8) {
908 val = pick_among(this, "Select algorithm", "Select scaling algorithm", sa_choices);
910 } catch(...) {
911 refresh();
912 return;
914 std::string err;
915 try {
916 if(e.GetId() == wxID_HIGHEST + 6) {
917 double x = parse_value<double>(val);
918 if(x < 0.25 || x > 10)
919 throw "Bad horizontal scaling factor (0.25-10)";
920 horizontal_scale_factor = x;
921 } else if(e.GetId() == wxID_HIGHEST + 7) {
922 double x = parse_value<double>(val);
923 if(x < 0.25 || x > 10)
924 throw "Bad vertical scaling factor (0.25-10)";
925 vertical_scale_factor = x;
926 } else if(e.GetId() == wxID_HIGHEST + 8) {
927 for(size_t i = 0; i < sizeof(scalealgo_choices) / sizeof(scalealgo_choices[0]); i++)
928 if(val == scalealgo_choices[i])
929 newflags = 1 << i;
930 scaling_flags = newflags;
932 } catch(std::exception& e) {
933 wxMessageBox(towxstring(std::string("Invalid value: ") + e.what()), wxT("Can't change value"),
934 wxICON_EXCLAMATION | wxOK);
936 refresh();
939 void wxeditor_esettings_settings::refresh()
941 xscale->SetLabel(towxstring((stringfmt() << horizontal_scale_factor).str()));
942 yscale->SetLabel(towxstring((stringfmt() << vertical_scale_factor).str()));
943 algo->SetLabel(towxstring(getalgo(scaling_flags)));
944 hflip->SetValue(hflip_enabled);
945 vflip->SetValue(vflip_enabled);
946 rotate->SetValue(rotate_enabled);
947 top_s->Layout();
948 Fit();
951 class wxeditor_esettings_aliases : public wxPanel
953 public:
954 wxeditor_esettings_aliases(wxWindow* parent);
955 ~wxeditor_esettings_aliases();
956 void on_add(wxCommandEvent& e);
957 void on_edit(wxCommandEvent& e);
958 void on_delete(wxCommandEvent& e);
959 void on_change(wxCommandEvent& e);
960 private:
961 std::map<int, std::string> numbers;
962 wxListBox* select;
963 wxButton* editbutton;
964 wxButton* deletebutton;
965 void refresh();
966 std::string selected();
969 wxeditor_esettings_aliases::wxeditor_esettings_aliases(wxWindow* parent)
970 : wxPanel(parent, -1)
972 wxButton* tmp;
974 wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
975 SetSizer(top_s);
977 top_s->Add(select = new wxListBox(this, wxID_ANY), 1, wxGROW);
978 select->Connect(wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler(wxeditor_esettings_aliases::on_change),
979 NULL, this);
981 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
982 pbutton_s->AddStretchSpacer();
983 pbutton_s->Add(tmp = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
984 tmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_aliases::on_add), NULL,
985 this);
986 pbutton_s->Add(editbutton = new wxButton(this, wxID_ANY, wxT("Edit")), 0, wxGROW);
987 editbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_aliases::on_edit),
988 NULL, this);
989 pbutton_s->Add(deletebutton = new wxButton(this, wxID_ANY, wxT("Delete")), 0, wxGROW);
990 deletebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
991 wxCommandEventHandler(wxeditor_esettings_aliases::on_delete), NULL, this);
992 top_s->Add(pbutton_s, 0, wxGROW);
994 refresh();
995 wxCommandEvent e;
996 on_change(e);
997 top_s->SetSizeHints(this);
998 Fit();
1001 wxeditor_esettings_aliases::~wxeditor_esettings_aliases()
1005 void wxeditor_esettings_aliases::on_change(wxCommandEvent& e)
1007 bool enable = (selected() != "");
1008 editbutton->Enable(enable);
1009 deletebutton->Enable(enable);
1012 void wxeditor_esettings_aliases::on_add(wxCommandEvent& e)
1014 try {
1015 std::string name = pick_text(this, "Enter alias name", "Enter name for the new alias:");
1016 if(!lsnes_cmd.valid_alias_name(name)) {
1017 show_message_ok(this, "Error", "Not a valid alias name: " + name, wxICON_EXCLAMATION);
1018 throw canceled_exception();
1020 std::string old_alias_value = lsnes_cmd.get_alias_for(name);
1021 std::string newcmd = pick_text(this, "Edit alias", "Enter new commands for '" + name + "':",
1022 old_alias_value, true);
1023 lsnes_cmd.set_alias_for(name, newcmd);
1024 } catch(...) {
1026 refresh();
1029 void wxeditor_esettings_aliases::on_edit(wxCommandEvent& e)
1031 std::string name = selected();
1032 if(name == "") {
1033 refresh();
1034 return;
1036 try {
1037 std::string old_alias_value = lsnes_cmd.get_alias_for(name);
1038 std::string newcmd = pick_text(this, "Edit alias", "Enter new commands for '" + name + "':",
1039 old_alias_value, true);
1040 lsnes_cmd.set_alias_for(name, newcmd);
1041 } catch(...) {
1043 refresh();
1046 void wxeditor_esettings_aliases::on_delete(wxCommandEvent& e)
1048 std::string name = selected();
1049 if(name == "") {
1050 refresh();
1051 return;
1053 lsnes_cmd.set_alias_for(name, "");
1054 refresh();
1057 void wxeditor_esettings_aliases::refresh()
1059 int n = select->GetSelection();
1060 std::set<std::string> bind;
1061 std::vector<wxString> choices;
1062 bind = lsnes_cmd.get_aliases();
1063 for(auto i : bind) {
1064 numbers[choices.size()] = i;
1065 choices.push_back(towxstring(i));
1067 select->Set(choices.size(), &choices[0]);
1068 if(n == wxNOT_FOUND && select->GetCount())
1069 select->SetSelection(0);
1070 else if(n >= (int)select->GetCount())
1071 select->SetSelection(select->GetCount() ? (select->GetCount() - 1) : wxNOT_FOUND);
1072 else
1073 select->SetSelection(n);
1074 wxCommandEvent e;
1075 on_change(e);
1076 select->Refresh();
1079 std::string wxeditor_esettings_aliases::selected()
1081 int x = select->GetSelection();
1082 if(numbers.count(x))
1083 return numbers[x];
1084 else
1085 return "";
1088 class wxeditor_esettings_hotkeys : public wxPanel
1090 public:
1091 wxeditor_esettings_hotkeys(wxWindow* parent);
1092 ~wxeditor_esettings_hotkeys();
1093 void on_primary(wxCommandEvent& e);
1094 void on_secondary(wxCommandEvent& e);
1095 void on_change(wxCommandEvent& e);
1096 void prepare_destroy();
1097 private:
1098 bool destruction_underway;
1099 wxListBox* category;
1100 wxListBox* control;
1101 wxButton* pri_button;
1102 wxButton* sec_button;
1103 std::map<int, std::string> categories;
1104 std::map<std::pair<int, int>, std::string> itemlabels;
1105 std::map<std::pair<int, int>, std::string> items;
1106 std::map<std::string, inverse_bind*> realitems;
1107 void change_category(int cat);
1108 void refresh();
1109 std::pair<std::string, std::string> splitkeyname(const std::string& kn);
1112 wxeditor_esettings_hotkeys::wxeditor_esettings_hotkeys(wxWindow* parent)
1113 : wxPanel(parent, -1)
1115 destruction_underway = false;
1116 wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1117 SetSizer(top_s);
1118 wxString empty[1];
1120 top_s->Add(category = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 1, empty), 1, wxGROW);
1121 top_s->Add(control = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 1, empty), 1, wxGROW);
1122 category->Connect(wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler(wxeditor_esettings_hotkeys::on_change),
1123 NULL, this);
1125 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
1126 pbutton_s->AddStretchSpacer();
1127 pbutton_s->Add(pri_button = new wxButton(this, wxID_ANY, wxT("Change primary")), 0, wxGROW);
1128 pri_button->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
1129 wxCommandEventHandler(wxeditor_esettings_hotkeys::on_primary), NULL, this);
1130 pbutton_s->Add(sec_button = new wxButton(this, wxID_ANY, wxT("Change secondary")), 0, wxGROW);
1131 sec_button->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
1132 wxCommandEventHandler(wxeditor_esettings_hotkeys::on_secondary), NULL, this);
1133 top_s->Add(pbutton_s, 0, wxGROW);
1135 refresh();
1136 top_s->SetSizeHints(this);
1137 Fit();
1140 void wxeditor_esettings_hotkeys::prepare_destroy()
1142 destruction_underway = true;
1145 std::pair<std::string, std::string> wxeditor_esettings_hotkeys::splitkeyname(const std::string& kn)
1147 std::string tmp = kn;
1148 size_t split = 0;
1149 for(size_t itr = 0; itr < tmp.length() - 2 && itr < tmp.length(); itr++) {
1150 unsigned char ch1 = tmp[itr];
1151 unsigned char ch2 = tmp[itr + 1];
1152 unsigned char ch3 = tmp[itr + 2];
1153 if(ch1 == 0xE2 && ch2 == 0x80 && ch3 == 0xA3)
1154 split = itr;
1156 if(split)
1157 return std::make_pair(tmp.substr(0, split), tmp.substr(split + 3));
1158 else
1159 return std::make_pair("(Uncategorized)", tmp);
1162 void wxeditor_esettings_hotkeys::on_change(wxCommandEvent& e)
1164 if(destruction_underway)
1165 return;
1166 int c = category->GetSelection();
1167 if(c == wxNOT_FOUND) {
1168 category->SetSelection(0);
1169 change_category(0);
1170 } else
1171 change_category(c);
1174 void wxeditor_esettings_hotkeys::change_category(int cat)
1176 if(destruction_underway)
1177 return;
1178 std::map<int, std::string> n;
1179 for(auto i : itemlabels)
1180 if(i.first.first == cat)
1181 n[i.first.second] = i.second;
1183 for(size_t i = 0; i < control->GetCount(); i++)
1184 if(n.count(i))
1185 control->SetString(i, towxstring(n[i]));
1186 else
1187 control->Delete(i--);
1188 for(auto i : n)
1189 if(i.first >= (int)control->GetCount())
1190 control->Append(towxstring(n[i.first]));
1191 if(control->GetSelection() == wxNOT_FOUND)
1192 control->SetSelection(0);
1195 wxeditor_esettings_hotkeys::~wxeditor_esettings_hotkeys()
1199 void wxeditor_esettings_hotkeys::on_primary(wxCommandEvent& e)
1201 if(destruction_underway)
1202 return;
1203 std::string name = items[std::make_pair(category->GetSelection(), control->GetSelection())];
1204 if(name == "") {
1205 refresh();
1206 return;
1208 try {
1209 inverse_bind* ik = realitems[name];
1210 if(!ik) {
1211 refresh();
1212 return;
1214 std::string key = ik->get(true);
1215 wxdialog_keyentry* d = new wxdialog_keyentry(this, "Specify key for " + name, key, true);
1216 if(d->ShowModal() == wxID_CANCEL) {
1217 d->Destroy();
1218 return;
1220 key = d->getkey();
1221 d->Destroy();
1222 if(key != "")
1223 ik->set(key, true);
1224 else
1225 ik->clear(true);
1227 } catch(...) {
1229 refresh();
1232 void wxeditor_esettings_hotkeys::on_secondary(wxCommandEvent& e)
1234 if(destruction_underway)
1235 return;
1236 std::string name = items[std::make_pair(category->GetSelection(), control->GetSelection())];
1237 if(name == "") {
1238 refresh();
1239 return;
1241 try {
1242 inverse_bind* ik = realitems[name];
1243 if(!ik) {
1244 refresh();
1245 return;
1247 std::string key = ik->get(false);
1248 wxdialog_keyentry* d = new wxdialog_keyentry(this, "Specify key for " + name, key, true);
1249 if(d->ShowModal() == wxID_CANCEL) {
1250 d->Destroy();
1251 return;
1253 key = d->getkey();
1254 d->Destroy();
1255 if(key != "")
1256 ik->set(key, false);
1257 else
1258 ik->clear(false);
1259 } catch(...) {
1261 refresh();
1264 void wxeditor_esettings_hotkeys::refresh()
1266 if(destruction_underway)
1267 return;
1268 std::map<inverse_bind*, std::pair<key_specifier, key_specifier>> data;
1269 std::map<std::string, int> cat_set;
1270 std::map<std::string, int> cat_assign;
1271 realitems.clear();
1272 auto x = lsnes_mapper.get_inverses();
1273 for(auto y : x) {
1274 realitems[y->getname()] = y;
1275 data[y] = std::make_pair(y->get(true), y->get(false));
1278 int cidx = 0;
1279 for(auto i : realitems) {
1280 std::pair<std::string, std::string> j = splitkeyname(i.first);
1281 if(!cat_set.count(j.first)) {
1282 categories[cidx] = j.first;
1283 cat_assign[j.first] = 0;
1284 cat_set[j.first] = cidx++;
1286 items[std::make_pair(cat_set[j.first], cat_assign[j.first])] = i.first;
1287 std::string text = j.second;
1288 if(!data[i.second].first)
1289 text = text + " (not set)";
1290 else if(!data[i.second].second)
1291 text = text + " (" + clean_keystring(data[i.second].first) + ")";
1292 else
1293 text = text + " (" + clean_keystring(data[i.second].first) + " or " +
1294 clean_keystring(data[i.second].second) + ")";
1295 itemlabels[std::make_pair(cat_set[j.first], cat_assign[j.first])] = text;
1296 cat_assign[j.first]++;
1299 for(size_t i = 0; i < category->GetCount(); i++)
1300 if(categories.count(i))
1301 category->SetString(i, towxstring(categories[i]));
1302 else
1303 category->Delete(i--);
1304 for(auto i : categories)
1305 if(i.first >= (int)category->GetCount())
1306 category->Append(towxstring(categories[i.first]));
1307 if(category->GetSelection() == wxNOT_FOUND)
1308 category->SetSelection(0);
1309 change_category(category->GetSelection());
1312 class wxeditor_esettings_controllers : public wxPanel
1314 public:
1315 wxeditor_esettings_controllers(wxWindow* parent);
1316 ~wxeditor_esettings_controllers();
1317 void on_setkey(wxCommandEvent& e);
1318 void on_clearkey(wxCommandEvent& e);
1319 void on_change(wxCommandEvent& e);
1320 void prepare_destroy();
1321 private:
1322 bool destruction_underway;
1323 wxListBox* category;
1324 wxListBox* control;
1325 wxButton* set_button;
1326 wxButton* clear_button;
1327 std::map<int, std::string> categories;
1328 std::map<std::pair<int, int>, std::string> itemlabels;
1329 std::map<std::pair<int, int>, std::string> items;
1330 std::map<std::string, controller_key*> realitems;
1331 void change_category(int cat);
1332 void refresh();
1333 std::pair<std::string, std::string> splitkeyname(const std::string& kn);
1336 wxeditor_esettings_controllers::wxeditor_esettings_controllers(wxWindow* parent)
1337 : wxPanel(parent, -1)
1339 destruction_underway = false;
1340 wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1341 SetSizer(top_s);
1342 wxString empty[1];
1344 top_s->Add(category = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 1, empty), 1, wxGROW);
1345 top_s->Add(control = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 1, empty), 1, wxGROW);
1346 category->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
1347 wxCommandEventHandler(wxeditor_esettings_controllers::on_change), NULL, this);
1349 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
1350 pbutton_s->AddStretchSpacer();
1351 pbutton_s->Add(set_button = new wxButton(this, wxID_ANY, wxT("Change")), 0, wxGROW);
1352 set_button->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
1353 wxCommandEventHandler(wxeditor_esettings_controllers::on_setkey), NULL, this);
1354 pbutton_s->Add(clear_button = new wxButton(this, wxID_ANY, wxT("Clear")), 0, wxGROW);
1355 clear_button->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
1356 wxCommandEventHandler(wxeditor_esettings_controllers::on_clearkey), NULL, this);
1357 top_s->Add(pbutton_s, 0, wxGROW);
1359 refresh();
1360 top_s->SetSizeHints(this);
1361 Fit();
1364 void wxeditor_esettings_controllers::prepare_destroy()
1366 destruction_underway = true;
1369 std::pair<std::string, std::string> wxeditor_esettings_controllers::splitkeyname(const std::string& kn)
1371 std::string tmp = kn;
1372 size_t split = 0;
1373 for(size_t itr = 0; itr < tmp.length() - 2 && itr < tmp.length(); itr++) {
1374 unsigned char ch1 = tmp[itr];
1375 unsigned char ch2 = tmp[itr + 1];
1376 unsigned char ch3 = tmp[itr + 2];
1377 if(ch1 == 0xE2 && ch2 == 0x80 && ch3 == 0xA3)
1378 split = itr;
1380 if(split)
1381 return std::make_pair(tmp.substr(0, split), tmp.substr(split + 3));
1382 else
1383 return std::make_pair("(Uncategorized)", tmp);
1386 void wxeditor_esettings_controllers::on_change(wxCommandEvent& e)
1388 if(destruction_underway)
1389 return;
1390 int c = category->GetSelection();
1391 if(c == wxNOT_FOUND) {
1392 category->SetSelection(0);
1393 change_category(0);
1394 } else
1395 change_category(c);
1398 void wxeditor_esettings_controllers::change_category(int cat)
1400 if(destruction_underway)
1401 return;
1402 std::map<int, std::string> n;
1403 for(auto i : itemlabels)
1404 if(i.first.first == cat)
1405 n[i.first.second] = i.second;
1407 for(size_t i = 0; i < control->GetCount(); i++)
1408 if(n.count(i))
1409 control->SetString(i, towxstring(n[i]));
1410 else
1411 control->Delete(i--);
1412 for(auto i : n)
1413 if(i.first >= (int)control->GetCount())
1414 control->Append(towxstring(n[i.first]));
1415 if(control->GetSelection() == wxNOT_FOUND && !control->IsEmpty())
1416 control->SetSelection(0);
1419 wxeditor_esettings_controllers::~wxeditor_esettings_controllers()
1423 void wxeditor_esettings_controllers::on_setkey(wxCommandEvent& e)
1425 if(destruction_underway)
1426 return;
1427 std::string name = items[std::make_pair(category->GetSelection(), control->GetSelection())];
1428 if(name == "") {
1429 refresh();
1430 return;
1432 try {
1433 controller_key* ik = realitems[name];
1434 if(!ik) {
1435 refresh();
1436 return;
1438 bool axis = ik->is_axis();
1439 std::string wtitle = (axis ? "Specify axis for " : "Specify key for ") + name;
1440 wxdialog_pressbutton* p = new wxdialog_pressbutton(this, wtitle, axis);
1441 p->ShowModal();
1442 std::string key = p->getkey();
1443 p->Destroy();
1444 ik->set(key);
1445 } catch(...) {
1447 refresh();
1450 void wxeditor_esettings_controllers::on_clearkey(wxCommandEvent& e)
1452 if(destruction_underway)
1453 return;
1454 std::string name = items[std::make_pair(category->GetSelection(), control->GetSelection())];
1455 if(name == "") {
1456 refresh();
1457 return;
1459 try {
1460 controller_key* ik = realitems[name];
1461 if(ik)
1462 ik->set(NULL, 0);
1463 } catch(...) {
1465 refresh();
1468 void wxeditor_esettings_controllers::refresh()
1470 if(destruction_underway)
1471 return;
1472 std::map<controller_key*, std::string> data;
1473 std::map<std::string, int> cat_set;
1474 std::map<std::string, int> cat_assign;
1475 realitems.clear();
1476 auto x = lsnes_mapper.get_controller_keys();
1477 for(auto y : x) {
1478 realitems[y->get_name()] = y;
1479 data[y] = y->get_string();
1482 int cidx = 0;
1483 for(auto i : realitems) {
1484 std::pair<std::string, std::string> j = splitkeyname(i.first);
1485 if(!cat_set.count(j.first)) {
1486 categories[cidx] = j.first;
1487 cat_assign[j.first] = 0;
1488 cat_set[j.first] = cidx++;
1490 items[std::make_pair(cat_set[j.first], cat_assign[j.first])] = i.first;
1491 std::string text = j.second;
1492 if(data[i.second] == "")
1493 text = text + " (not set)";
1494 else
1495 text = text + " (" + clean_keystring(data[i.second]) + ")";
1496 itemlabels[std::make_pair(cat_set[j.first], cat_assign[j.first])] = text;
1497 cat_assign[j.first]++;
1500 for(size_t i = 0; i < category->GetCount(); i++)
1501 if(categories.count(i))
1502 category->SetString(i, towxstring(categories[i]));
1503 else
1504 category->Delete(i--);
1505 for(auto i : categories)
1506 if(i.first >= (int)category->GetCount())
1507 category->Append(towxstring(categories[i.first]));
1508 if(category->GetSelection() == wxNOT_FOUND && !category->IsEmpty())
1509 category->SetSelection(0);
1510 change_category(category->GetSelection());
1513 class wxeditor_esettings_bindings : public wxPanel
1515 public:
1516 wxeditor_esettings_bindings(wxWindow* parent);
1517 ~wxeditor_esettings_bindings();
1518 void on_add(wxCommandEvent& e);
1519 void on_edit(wxCommandEvent& e);
1520 void on_delete(wxCommandEvent& e);
1521 void on_change(wxCommandEvent& e);
1522 void prepare_destroy();
1523 private:
1524 bool destruction_underway;
1525 std::map<int, std::string> numbers;
1526 wxListBox* select;
1527 void refresh();
1528 std::set<std::string> settings;
1529 std::map<std::string, std::string> values;
1530 std::map<int, std::string> selections;
1531 std::string selected();
1532 wxButton* editbutton;
1533 wxButton* deletebutton;
1534 wxListBox* _settings;
1537 wxeditor_esettings_bindings::wxeditor_esettings_bindings(wxWindow* parent)
1538 : wxPanel(parent, -1)
1540 destruction_underway = false;
1541 wxButton* tmp;
1543 wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1544 SetSizer(top_s);
1546 top_s->Add(select = new wxListBox(this, wxID_ANY), 1, wxGROW);
1547 select->Connect(wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler(wxeditor_esettings_bindings::on_change),
1548 NULL, this);
1550 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
1551 pbutton_s->AddStretchSpacer();
1552 pbutton_s->Add(tmp = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
1553 tmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_bindings::on_add), NULL,
1554 this);
1555 pbutton_s->Add(editbutton = new wxButton(this, wxID_ANY, wxT("Edit")), 0, wxGROW);
1556 editbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_bindings::on_edit),
1557 NULL, this);
1558 pbutton_s->Add(deletebutton = new wxButton(this, wxID_ANY, wxT("Delete")), 0, wxGROW);
1559 deletebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
1560 wxCommandEventHandler(wxeditor_esettings_bindings::on_delete), NULL, this);
1561 top_s->Add(pbutton_s, 0, wxGROW);
1563 refresh();
1564 wxCommandEvent e;
1565 on_change(e);
1566 top_s->SetSizeHints(this);
1567 Fit();
1570 wxeditor_esettings_bindings::~wxeditor_esettings_bindings()
1574 void wxeditor_esettings_bindings::prepare_destroy()
1576 destruction_underway = true;
1579 void wxeditor_esettings_bindings::on_change(wxCommandEvent& e)
1581 if(destruction_underway)
1582 return;
1583 bool enable = (selected() != "");
1584 editbutton->Enable(enable);
1585 deletebutton->Enable(enable);
1588 void wxeditor_esettings_bindings::on_add(wxCommandEvent& e)
1590 if(destruction_underway)
1591 return;
1592 try {
1593 std::string name;
1594 wxdialog_keyentry* d = new wxdialog_keyentry(this, "Specify new key", "", false);
1595 if(d->ShowModal() == wxID_CANCEL) {
1596 d->Destroy();
1597 throw 42;
1599 name = d->getkey();
1600 d->Destroy();
1602 std::string newcommand = pick_text(this, "New binding", "Enter command for binding:", "");
1603 try {
1604 lsnes_mapper.set(name, newcommand);
1605 } catch(std::exception& e) {
1606 wxMessageBox(wxT("Error"), towxstring(std::string("Can't bind key: ") + e.what()),
1607 wxICON_EXCLAMATION);
1609 } catch(...) {
1611 refresh();
1614 void wxeditor_esettings_bindings::on_edit(wxCommandEvent& e)
1616 if(destruction_underway)
1617 return;
1618 std::string name = selected();
1619 if(name == "") {
1620 refresh();
1621 return;
1623 try {
1624 std::string old_command_value = lsnes_mapper.get(name);
1625 std::string newcommand = pick_text(this, "Edit binding", "Enter new command for binding:",
1626 old_command_value);
1627 try {
1628 lsnes_mapper.set(name, newcommand);
1629 } catch(std::exception& e) {
1630 wxMessageBox(wxT("Error"), towxstring(std::string("Can't bind key: ") + e.what()),
1631 wxICON_EXCLAMATION);
1633 } catch(...) {
1635 refresh();
1638 void wxeditor_esettings_bindings::on_delete(wxCommandEvent& e)
1640 if(destruction_underway)
1641 return;
1642 std::string name = selected();
1643 if(name == "") {
1644 refresh();
1645 return;
1647 try { lsnes_mapper.set(name, ""); } catch(...) {}
1648 refresh();
1651 void wxeditor_esettings_bindings::refresh()
1653 if(destruction_underway)
1654 return;
1655 int n = select->GetSelection();
1656 std::map<std::string, std::string> bind;
1657 std::vector<wxString> choices;
1658 std::list<key_specifier> a = lsnes_mapper.get_bindings();
1659 for(auto i : a)
1660 bind[i] = lsnes_mapper.get(i);
1661 for(auto i : bind) {
1662 numbers[choices.size()] = i.first;
1663 choices.push_back(towxstring(clean_keystring(i.first) + " (" + i.second + ")"));
1665 select->Set(choices.size(), &choices[0]);
1666 if(n == wxNOT_FOUND && select->GetCount())
1667 select->SetSelection(0);
1668 else if(n >= (int)select->GetCount())
1669 select->SetSelection(select->GetCount() ? (int)(select->GetCount() - 1) : wxNOT_FOUND);
1670 else
1671 select->SetSelection(n);
1672 wxCommandEvent e;
1673 on_change(e);
1674 select->Refresh();
1677 std::string wxeditor_esettings_bindings::selected()
1679 if(destruction_underway)
1680 return "";
1681 int x = select->GetSelection();
1682 if(numbers.count(x))
1683 return numbers[x];
1684 else
1685 return "";
1688 class wxeditor_esettings_advanced : public wxPanel
1690 public:
1691 wxeditor_esettings_advanced(wxWindow* parent);
1692 ~wxeditor_esettings_advanced();
1693 void on_change(wxCommandEvent& e);
1694 void on_selchange(wxCommandEvent& e);
1695 void on_setting_change(const setting_var_base& val);
1696 void _refresh();
1697 void prepare_destroy();
1698 struct listener : public setting_var_listener
1700 listener(setting_var_group& group, wxeditor_esettings_advanced& _obj)
1701 : grp(group), obj(_obj)
1703 group.add_listener(*this);
1705 ~listener() throw()
1707 grp.remove_listener(*this);
1709 void on_setting_change(setting_var_group& grp, const setting_var_base& val)
1711 obj.on_setting_change(val);
1713 wxeditor_esettings_advanced& obj;
1714 setting_var_group& grp;
1716 private:
1717 listener _listener;
1718 bool destruction_underway;
1719 void refresh();
1720 std::set<std::string> settings;
1721 std::map<std::string, std::string> values;
1722 std::map<std::string, std::string> names;
1723 std::map<int, std::string> selections;
1724 std::string selected();
1725 wxButton* changebutton;
1726 wxListBox* _settings;
1729 wxeditor_esettings_advanced::wxeditor_esettings_advanced(wxWindow* parent)
1730 : wxPanel(parent, -1), _listener(lsnes_vset, *this)
1732 destruction_underway = false;
1734 wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1735 SetSizer(top_s);
1737 top_s->Add(_settings = new wxListBox(this, wxID_ANY), 1, wxGROW);
1738 _settings->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
1739 wxCommandEventHandler(wxeditor_esettings_advanced::on_selchange), NULL, this);
1741 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
1742 pbutton_s->AddStretchSpacer();
1743 pbutton_s->Add(changebutton = new wxButton(this, wxID_ANY, wxT("Change")), 0, wxGROW);
1744 changebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
1745 wxCommandEventHandler(wxeditor_esettings_advanced::on_change), NULL, this);
1746 top_s->Add(pbutton_s, 0, wxGROW);
1748 refresh();
1749 wxCommandEvent e;
1750 on_selchange(e);
1751 top_s->SetSizeHints(this);
1752 Fit();
1755 void wxeditor_esettings_advanced::prepare_destroy()
1757 destruction_underway = true;
1760 wxeditor_esettings_advanced::~wxeditor_esettings_advanced()
1764 void wxeditor_esettings_advanced::on_change(wxCommandEvent& e)
1766 if(destruction_underway)
1767 return;
1768 std::string name = selected();
1769 if(name == "")
1770 return;
1771 std::string value;
1772 std::string err;
1773 value = lsnes_vsetc.get(name);
1774 try {
1775 value = pick_text(this, "Set value to", "Set " + name + " to value:", value);
1776 } catch(...) {
1777 return;
1779 try {
1780 lsnes_vsetc.set(name, value);
1781 } catch(std::exception& e) {
1782 wxMessageBox(towxstring(e.what()), wxT("Error setting value"), wxICON_EXCLAMATION | wxOK);
1786 void wxeditor_esettings_advanced::on_selchange(wxCommandEvent& e)
1788 if(destruction_underway)
1789 return;
1790 std::string sel = selected();
1791 bool enable = (sel != "");
1792 changebutton->Enable(enable);
1795 void wxeditor_esettings_advanced::on_setting_change(const setting_var_base& val)
1797 if(destruction_underway)
1798 return;
1799 runuifun([this, &val]() {
1800 std::string setting = val.get_iname();
1801 std::string value = val.str();
1802 this->settings.insert(setting);
1803 this->values[setting] = value;
1804 this->_refresh();
1808 void wxeditor_esettings_advanced::refresh()
1810 if(destruction_underway)
1811 return;
1812 settings = lsnes_vsetc.get_keys();
1813 for(auto i : settings) {
1814 values[i] = lsnes_vsetc.get(i);
1815 names[i] = lsnes_vset[i].get_hname();
1817 _refresh();
1820 std::string wxeditor_esettings_advanced::selected()
1822 if(destruction_underway)
1823 return "";
1824 int x = _settings->GetSelection();
1825 if(selections.count(x))
1826 return selections[x];
1827 else
1828 return "";
1831 void wxeditor_esettings_advanced::_refresh()
1833 if(destruction_underway)
1834 return;
1835 std::vector<wxString> strings;
1836 std::multimap<std::string, std::string> sort;
1837 int k = 0;
1838 for(auto i : settings)
1839 sort.insert(std::make_pair(names[i], i));
1840 for(auto i : sort) {
1841 strings.push_back(towxstring(names[i.second] + " (Value: " + values[i.second] + ")"));
1842 selections[k++] = i.second;
1844 _settings->Set(strings.size(), &strings[0]);
1848 class wxeditor_esettings : public wxDialog
1850 public:
1851 wxeditor_esettings(wxWindow* parent, int mode);
1852 ~wxeditor_esettings();
1853 bool ShouldPreventAppExit() const;
1854 void on_close(wxCommandEvent& e);
1855 private:
1856 wxWindow* joystick_window;
1857 wxNotebook* tabset;
1858 wxButton* closebutton;
1859 wxeditor_esettings_hotkeys* hotkeytab;
1860 wxeditor_esettings_controllers* controllertab;
1861 wxeditor_esettings_bindings* bindtab;
1862 wxeditor_esettings_advanced* advtab;
1863 wxeditor_esettings_settings* settingstab;
1866 wxeditor_esettings::wxeditor_esettings(wxWindow* parent, int mode)
1867 : wxDialog(parent, wxID_ANY, towxstring(get_title(mode)), wxDefaultPosition, wxSize(-1, -1))
1869 //Grab keys to prevent the joystick driver from running who knows what commands.
1870 lsnes_kbd.set_exclusive(&keygrabber);
1872 Centre();
1873 wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1874 SetSizer(top_s);
1876 if(mode == 1) {
1877 hotkeytab = new wxeditor_esettings_hotkeys(this);
1878 controllertab = NULL;
1879 bindtab = NULL;
1880 advtab = NULL;
1881 settingstab = NULL;
1882 top_s->Add(hotkeytab);
1883 } else if(mode == 2) {
1884 hotkeytab = NULL;
1885 controllertab = new wxeditor_esettings_controllers(this);
1886 bindtab = NULL;
1887 advtab = NULL;
1888 settingstab = NULL;
1889 top_s->Add(controllertab);
1890 } else {
1891 tabset = new wxNotebook(this, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP);
1892 tabset->AddPage(new wxeditor_esettings_joystick(tabset), wxT("Joysticks"));
1893 tabset->AddPage(settingstab = new wxeditor_esettings_settings(tabset), wxT("Display"));
1894 tabset->AddPage(hotkeytab = new wxeditor_esettings_hotkeys(tabset), wxT("Hotkeys"));
1895 tabset->AddPage(controllertab = new wxeditor_esettings_controllers(tabset), wxT("Controllers"));
1896 tabset->AddPage(new wxeditor_esettings_aliases(tabset), wxT("Aliases"));
1897 tabset->AddPage(bindtab = new wxeditor_esettings_bindings(tabset), wxT("Bindings"));
1898 tabset->AddPage(advtab = new wxeditor_esettings_advanced(tabset), wxT("Advanced"));
1899 top_s->Add(tabset, 1, wxGROW);
1902 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
1903 pbutton_s->AddStretchSpacer();
1904 pbutton_s->Add(closebutton = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
1905 closebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
1906 wxCommandEventHandler(wxeditor_esettings::on_close), NULL, this);
1907 top_s->Add(pbutton_s, 0, wxGROW);
1909 top_s->SetSizeHints(this);
1910 Fit();
1913 wxeditor_esettings::~wxeditor_esettings()
1915 if(hotkeytab) hotkeytab->prepare_destroy();
1916 if(controllertab) controllertab->prepare_destroy();
1917 if(bindtab) bindtab->prepare_destroy();
1918 if(advtab) advtab->prepare_destroy();
1921 bool wxeditor_esettings::ShouldPreventAppExit() const
1923 return false;
1926 void wxeditor_esettings::on_close(wxCommandEvent& e)
1928 if(settingstab) {
1929 hflip_enabled = settingstab->hflip->GetValue();
1930 vflip_enabled = settingstab->vflip->GetValue();
1931 rotate_enabled = settingstab->rotate->GetValue();
1933 lsnes_kbd.set_exclusive(NULL);
1934 EndModal(wxID_OK);
1937 void wxsetingsdialog_display(wxWindow* parent, int mode)
1939 modal_pause_holder hld;
1940 wxDialog* editor;
1941 try {
1942 editor = new wxeditor_esettings(parent, mode);
1943 editor->ShowModal();
1944 } catch(...) {
1946 editor->Destroy();