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"
10 #include <wx/notebook.h>
11 #include <wx/treectrl.h>
13 #include <wx/control.h>
14 #include <wx/combobox.h>
18 #include "library/string.hpp"
19 #include <boost/lexical_cast.hpp>
25 #define UINT64_C(val) val##ULL
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"};
45 class wxdialog_pressbutton
;
46 volatile bool keygrab_active
= false;
48 wxdialog_pressbutton
* presser
= NULL
;
50 std::string
get_title(int mode
)
54 return "lsnes: Configure emulator";
56 return "lsnes: Configure hotkeys";
58 return "lsnes: Configure controllers";
63 void report_grab_key(const std::string
& name
);
65 class keygrabber
: public keyboard_event_listener
68 keygrabber() { keygrab_active
= false; }
69 void on_key_event(keyboard_modifier_set
& mods
, keyboard_key
& key
, keyboard_event
& event
)
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)
79 if(((dev
>> (2 * i
)) & 3) == 2) {
81 keygrab_active
= false;
82 std::string tmp
= pkey
;
83 runuifun([tmp
]() { report_grab_key(tmp
); });
91 std::string
clean_keystring(const std::string
& in
)
93 regex_results tmp
= regex("(.*)/(.*)\\|(.*)", 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
;
102 extract_token(mods
, tmp2
, ",");
106 extract_token(mask
, tmp2
, ",");
115 out
= out
+ "!" + i
+ "+";
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
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
);
143 bool handle_mousebtn(wxMouseEvent
& e
, bool(wxMouseEvent::*down
)()const, bool(wxMouseEvent::*up
)()const,
144 const std::string
& k
, int flag
);
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
))
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
);
170 wxFlexGridSizer
* s
= new wxFlexGridSizer(3, 3, 0, 0);
173 s
->Add(horiz_padding
, vert_padding
);
177 s
->Add(t
= new wxStaticText(p
, wxID_ANY
, wxT("Press the key to assign"), wxDefaultPosition
,
178 wxSize(-1, -1), wxWANTS_CHARS
), 1, wxGROW
);
182 s
->Add(horiz_padding
, vert_padding
);
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);
201 keygrab_active
= true;
202 s
->SetSizeHints(this);
206 bool wxdialog_pressbutton::handle_mousebtn(wxMouseEvent
& e
, bool(wxMouseEvent::*down
)()const,
207 bool(wxMouseEvent::*up
)()const, const std::string
& k
, int flag
)
212 if(mouseflag
== flag
) {
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();
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
));
245 void wxdialog_pressbutton::dismiss_with(const std::string
& _k
)
251 //Check that k is a valid axis.
253 //Remove the +/- postfix if any.
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
)
267 keygrab_active
= false;
273 struct keyentry_mod_data
279 class wxdialog_keyentry
: public wxDialog
282 wxdialog_keyentry(wxWindow
* parent
, const std::string
& title
, const std::string
& spec
,
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();
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
;
301 std::string currentclass
;
302 wxFlexGridSizer
* top_s
;
303 wxFlexGridSizer
* t_s
;
304 wxComboBox
* mainclass
;
313 wxdialog_keyentry::wxdialog_keyentry(wxWindow
* parent
, const std::string
& title
, const std::string
& spec
,
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
;
326 std::set
<std::string
> x
;
327 mods
= lsnes_kbd
.all_modifiers();
328 keys
= lsnes_kbd
.all_keys();
330 std::string kclass
= i
->get_class();
332 classeslist
.push_back(towxstring(kclass
));
334 for(auto k2
: i
->get_subkeys())
335 classes
[kclass
].insert(i
->get_name() + k2
);
339 top_s
= new wxFlexGridSizer(3, 1, 0, 0);
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
) {
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);
352 m
.pressed
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
353 wxCommandEventHandler(wxdialog_keyentry::on_change_setting
), NULL
, this);
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);
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);
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);
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);
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
))
408 if(modifiers
[mod
].pressed
->GetSelection() == 1) {
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
))
419 if(modifiers
[mod
].pressed
->GetSelection() != 1) {
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
;
431 size_t s
= rem
.find_first_of(",");
432 if(s
>= rem
.length()) {
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())
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
);
455 for(auto i
: classes
)
456 if(i
.second
.count(key
))
460 mainclass
->SetValue(towxstring(_class
));
461 mainkey
->SetValue(towxstring(key
));
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);
476 std::string key
= p
->getkey();
479 for(auto i
: classes
)
480 if(i
.second
.count(key
))
485 mainclass
->SetValue(towxstring(_class
));
486 mainkey
->SetValue(towxstring(key
));
489 void wxdialog_keyentry::on_ok(wxCommandEvent
& e
)
494 void wxdialog_keyentry::on_clear(wxCommandEvent
& e
)
500 void wxdialog_keyentry::on_cancel(wxCommandEvent
& e
)
502 EndModal(wxID_CANCEL
);
505 void wxdialog_keyentry::set_class(const std::string
& _class
)
509 if(currentclass
== _class
)
512 for(auto i
: classes
[_class
])
513 mainkey
->Append(towxstring(i
));
514 currentclass
= _class
;
515 mainkey
->SetSelection(0);
521 void wxdialog_keyentry::on_classchange(wxCommandEvent
& e
)
523 set_class(tostdstring(mainclass
->GetValue()));
526 std::string
wxdialog_keyentry::getkey()
533 for(auto i
: modifiers
) {
534 if(i
.second
.pressed
->GetSelection() == 2) {
543 for(auto i
: modifiers
) {
544 if(i
.second
.pressed
->GetSelection() != 1) {
551 x
= x
+ "|" + tostdstring(mainkey
->GetValue());
556 class wxeditor_esettings_joystick_aconfig
: public wxDialog
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
);
574 wxeditor_esettings_joystick_aconfig::wxeditor_esettings_joystick_aconfig(wxWindow
* parent
, const std::string
& _aname
)
575 : wxDialog(parent
, -1, towxstring("Configure axis " + _aname
))
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
);
591 keyboard_axis_calibration c
;
592 keyboard_key
* _k
= lsnes_kbd
.try_lookup_key(aname
);
593 keyboard_key_axis
* k
= NULL
;
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;
610 wxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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
);
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);
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
;
660 _k
= lsnes_kbd
.try_lookup_key(aname
);
669 keyboard_axis_calibration c
;
670 const char* bad_what
= NULL
;
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; }
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)
695 wxMessageBox(towxstring(bad_what
), _T("Error"), wxICON_EXCLAMATION
| wxOK
);
698 k
->set_calibration(c
);
702 void wxeditor_esettings_joystick_aconfig::on_cancel(wxCommandEvent
& e
)
704 EndModal(wxID_CANCEL
);
707 class wxeditor_esettings_joystick
: public wxPanel
710 wxeditor_esettings_joystick(wxWindow
* parent
);
711 ~wxeditor_esettings_joystick();
712 void on_configure(wxCommandEvent
& e
);
716 wxStaticText
* no_joysticks
;
717 std::map
<std::string
, wxButton
*> buttons
;
718 std::map
<int, std::string
> ids
;
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
++)
748 return scalealgo_choices
[i
];
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));
762 jgrid
->SetSizeHints(this);
766 wxeditor_esettings_joystick::~wxeditor_esettings_joystick()
770 void wxeditor_esettings_joystick::on_configure(wxCommandEvent
& e
)
772 if(!ids
.count(e
.GetId()))
774 wxDialog
* d
= new wxeditor_esettings_joystick_aconfig(this, ids
[e
.GetId()]);
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();
789 x
[i
->get_name()] = j
->get_calibration();
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();
804 ids
[last_id
] = i
.first
;
805 buttons
[i
.first
] = new wxButton(this, last_id
++, towxstring(formatsettings(i
.first
,
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
)) {
816 jgrid
->Detach(i
.second
);
820 jgrid
->Detach(no_joysticks
);
821 no_joysticks
->Hide();
823 no_joysticks
->Show();
824 jgrid
->Add(no_joysticks
);
831 class wxeditor_esettings_settings
: public wxPanel
834 wxeditor_esettings_settings(wxWindow
* parent
);
835 ~wxeditor_esettings_settings();
836 void on_configure(wxCommandEvent
& e
);
842 wxFlexGridSizer
* top_s
;
843 wxStaticText
* xscale
;
844 wxStaticText
* yscale
;
848 wxeditor_esettings_settings::wxeditor_esettings_settings(wxWindow
* parent
)
849 : wxPanel(parent
, -1)
852 top_s
= new wxFlexGridSizer(8, 3, 0, 0);
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
),
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
),
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
),
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
);
880 top_s
->SetSizeHints(this);
883 wxeditor_esettings_settings::~wxeditor_esettings_settings()
887 void wxeditor_esettings_settings::on_configure(wxCommandEvent
& e
)
889 std::vector
<std::string
> sa_choices
;
892 for(size_t i
= 0; i
< sizeof(scalealgo_choices
) / sizeof(scalealgo_choices
[0]); i
++)
893 sa_choices
.push_back(scalealgo_choices
[i
]);
895 if(e
.GetId() <= wxID_HIGHEST
|| e
.GetId() > wxID_HIGHEST
+ 10)
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):",
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):",
907 } else if(e
.GetId() == wxID_HIGHEST
+ 8) {
908 val
= pick_among(this, "Select algorithm", "Select scaling algorithm", sa_choices
);
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
])
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
);
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
);
951 class wxeditor_esettings_aliases
: public wxPanel
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
);
961 std::map
<int, std::string
> numbers
;
963 wxButton
* editbutton
;
964 wxButton
* deletebutton
;
966 std::string
selected();
969 wxeditor_esettings_aliases::wxeditor_esettings_aliases(wxWindow
* parent
)
970 : wxPanel(parent
, -1)
974 wxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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
),
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
,
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
),
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
);
997 top_s
->SetSizeHints(this);
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
)
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
);
1029 void wxeditor_esettings_aliases::on_edit(wxCommandEvent
& e
)
1031 std::string name
= selected();
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
);
1046 void wxeditor_esettings_aliases::on_delete(wxCommandEvent
& e
)
1048 std::string name
= selected();
1053 lsnes_cmd
.set_alias_for(name
, "");
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
);
1073 select
->SetSelection(n
);
1079 std::string
wxeditor_esettings_aliases::selected()
1081 int x
= select
->GetSelection();
1082 if(numbers
.count(x
))
1088 class wxeditor_esettings_hotkeys
: public wxPanel
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();
1098 bool destruction_underway
;
1099 wxListBox
* category
;
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
);
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
);
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
),
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
);
1136 top_s
->SetSizeHints(this);
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
;
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)
1157 return std::make_pair(tmp
.substr(0, split
), tmp
.substr(split
+ 3));
1159 return std::make_pair("(Uncategorized)", tmp
);
1162 void wxeditor_esettings_hotkeys::on_change(wxCommandEvent
& e
)
1164 if(destruction_underway
)
1166 int c
= category
->GetSelection();
1167 if(c
== wxNOT_FOUND
) {
1168 category
->SetSelection(0);
1174 void wxeditor_esettings_hotkeys::change_category(int cat
)
1176 if(destruction_underway
)
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
++)
1185 control
->SetString(i
, towxstring(n
[i
]));
1187 control
->Delete(i
--);
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
)
1203 std::string name
= items
[std::make_pair(category
->GetSelection(), control
->GetSelection())];
1209 inverse_bind
* ik
= realitems
[name
];
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
) {
1232 void wxeditor_esettings_hotkeys::on_secondary(wxCommandEvent
& e
)
1234 if(destruction_underway
)
1236 std::string name
= items
[std::make_pair(category
->GetSelection(), control
->GetSelection())];
1242 inverse_bind
* ik
= realitems
[name
];
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
) {
1256 ik
->set(key
, false);
1264 void wxeditor_esettings_hotkeys::refresh()
1266 if(destruction_underway
)
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
;
1272 auto x
= lsnes_mapper
.get_inverses();
1274 realitems
[y
->getname()] = y
;
1275 data
[y
] = std::make_pair(y
->get(true), y
->get(false));
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
) + ")";
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
]));
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
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();
1322 bool destruction_underway
;
1323 wxListBox
* category
;
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
);
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
);
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
);
1360 top_s
->SetSizeHints(this);
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
;
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)
1381 return std::make_pair(tmp
.substr(0, split
), tmp
.substr(split
+ 3));
1383 return std::make_pair("(Uncategorized)", tmp
);
1386 void wxeditor_esettings_controllers::on_change(wxCommandEvent
& e
)
1388 if(destruction_underway
)
1390 int c
= category
->GetSelection();
1391 if(c
== wxNOT_FOUND
) {
1392 category
->SetSelection(0);
1398 void wxeditor_esettings_controllers::change_category(int cat
)
1400 if(destruction_underway
)
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
++)
1409 control
->SetString(i
, towxstring(n
[i
]));
1411 control
->Delete(i
--);
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
)
1427 std::string name
= items
[std::make_pair(category
->GetSelection(), control
->GetSelection())];
1433 controller_key
* ik
= realitems
[name
];
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
);
1442 std::string key
= p
->getkey();
1450 void wxeditor_esettings_controllers::on_clearkey(wxCommandEvent
& e
)
1452 if(destruction_underway
)
1454 std::string name
= items
[std::make_pair(category
->GetSelection(), control
->GetSelection())];
1460 controller_key
* ik
= realitems
[name
];
1468 void wxeditor_esettings_controllers::refresh()
1470 if(destruction_underway
)
1472 std::map
<controller_key
*, std::string
> data
;
1473 std::map
<std::string
, int> cat_set
;
1474 std::map
<std::string
, int> cat_assign
;
1476 auto x
= lsnes_mapper
.get_controller_keys();
1478 realitems
[y
->get_name()] = y
;
1479 data
[y
] = y
->get_string();
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)";
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
]));
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
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();
1524 bool destruction_underway
;
1525 std::map
<int, std::string
> numbers
;
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;
1543 wxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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
),
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
,
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
),
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
);
1566 top_s
->SetSizeHints(this);
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
)
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
)
1594 wxdialog_keyentry
* d
= new wxdialog_keyentry(this, "Specify new key", "", false);
1595 if(d
->ShowModal() == wxID_CANCEL
) {
1602 std::string newcommand
= pick_text(this, "New binding", "Enter command for binding:", "");
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
);
1614 void wxeditor_esettings_bindings::on_edit(wxCommandEvent
& e
)
1616 if(destruction_underway
)
1618 std::string name
= selected();
1624 std::string old_command_value
= lsnes_mapper
.get(name
);
1625 std::string newcommand
= pick_text(this, "Edit binding", "Enter new command for binding:",
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
);
1638 void wxeditor_esettings_bindings::on_delete(wxCommandEvent
& e
)
1640 if(destruction_underway
)
1642 std::string name
= selected();
1647 try { lsnes_mapper
.set(name
, ""); } catch(...) {}
1651 void wxeditor_esettings_bindings::refresh()
1653 if(destruction_underway
)
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();
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
);
1671 select
->SetSelection(n
);
1677 std::string
wxeditor_esettings_bindings::selected()
1679 if(destruction_underway
)
1681 int x
= select
->GetSelection();
1682 if(numbers
.count(x
))
1688 class wxeditor_esettings_advanced
: public wxPanel
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
);
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);
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
;
1718 bool destruction_underway
;
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
);
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
);
1751 top_s
->SetSizeHints(this);
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
)
1768 std::string name
= selected();
1773 value
= lsnes_vsetc
.get(name
);
1775 value
= pick_text(this, "Set value to", "Set " + name
+ " to value:", value
);
1780 std::string errorstr
;
1781 runemufn([&error
, &errorstr
, name
, value
]() {
1783 lsnes_vsetc
.set(name
, value
);
1784 } catch(std::exception
& e
) {
1786 errorstr
= e
.what();
1790 wxMessageBox(towxstring(errorstr
), wxT("Error setting value"), wxICON_EXCLAMATION
| wxOK
);
1793 void wxeditor_esettings_advanced::on_selchange(wxCommandEvent
& e
)
1795 if(destruction_underway
)
1797 std::string sel
= selected();
1798 bool enable
= (sel
!= "");
1799 changebutton
->Enable(enable
);
1802 void wxeditor_esettings_advanced::on_setting_change(const setting_var_base
& val
)
1804 if(destruction_underway
)
1806 runuifun([this, &val
]() {
1807 std::string setting
= val
.get_iname();
1808 std::string value
= val
.str();
1809 this->settings
.insert(setting
);
1810 this->values
[setting
] = value
;
1815 void wxeditor_esettings_advanced::refresh()
1817 if(destruction_underway
)
1819 settings
= lsnes_vsetc
.get_keys();
1820 for(auto i
: settings
) {
1821 values
[i
] = lsnes_vsetc
.get(i
);
1822 names
[i
] = lsnes_vset
[i
].get_hname();
1827 std::string
wxeditor_esettings_advanced::selected()
1829 if(destruction_underway
)
1831 int x
= _settings
->GetSelection();
1832 if(selections
.count(x
))
1833 return selections
[x
];
1838 void wxeditor_esettings_advanced::_refresh()
1840 if(destruction_underway
)
1842 std::vector
<wxString
> strings
;
1843 std::multimap
<std::string
, std::string
> sort
;
1845 for(auto i
: settings
)
1846 sort
.insert(std::make_pair(names
[i
], i
));
1847 for(auto i
: sort
) {
1848 strings
.push_back(towxstring(names
[i
.second
] + " (Value: " + values
[i
.second
] + ")"));
1849 selections
[k
++] = i
.second
;
1851 _settings
->Set(strings
.size(), &strings
[0]);
1855 class wxeditor_esettings
: public wxDialog
1858 wxeditor_esettings(wxWindow
* parent
, int mode
);
1859 ~wxeditor_esettings();
1860 bool ShouldPreventAppExit() const;
1861 void on_close(wxCommandEvent
& e
);
1863 wxWindow
* joystick_window
;
1865 wxButton
* closebutton
;
1866 wxeditor_esettings_hotkeys
* hotkeytab
;
1867 wxeditor_esettings_controllers
* controllertab
;
1868 wxeditor_esettings_bindings
* bindtab
;
1869 wxeditor_esettings_advanced
* advtab
;
1870 wxeditor_esettings_settings
* settingstab
;
1873 wxeditor_esettings::wxeditor_esettings(wxWindow
* parent
, int mode
)
1874 : wxDialog(parent
, wxID_ANY
, towxstring(get_title(mode
)), wxDefaultPosition
, wxSize(-1, -1))
1876 //Grab keys to prevent the joystick driver from running who knows what commands.
1877 lsnes_kbd
.set_exclusive(&keygrabber
);
1880 wxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
1884 hotkeytab
= new wxeditor_esettings_hotkeys(this);
1885 controllertab
= NULL
;
1889 top_s
->Add(hotkeytab
);
1890 } else if(mode
== 2) {
1892 controllertab
= new wxeditor_esettings_controllers(this);
1896 top_s
->Add(controllertab
);
1898 tabset
= new wxNotebook(this, -1, wxDefaultPosition
, wxDefaultSize
, wxNB_TOP
);
1899 tabset
->AddPage(new wxeditor_esettings_joystick(tabset
), wxT("Joysticks"));
1900 tabset
->AddPage(settingstab
= new wxeditor_esettings_settings(tabset
), wxT("Display"));
1901 tabset
->AddPage(hotkeytab
= new wxeditor_esettings_hotkeys(tabset
), wxT("Hotkeys"));
1902 tabset
->AddPage(controllertab
= new wxeditor_esettings_controllers(tabset
), wxT("Controllers"));
1903 tabset
->AddPage(new wxeditor_esettings_aliases(tabset
), wxT("Aliases"));
1904 tabset
->AddPage(bindtab
= new wxeditor_esettings_bindings(tabset
), wxT("Bindings"));
1905 tabset
->AddPage(advtab
= new wxeditor_esettings_advanced(tabset
), wxT("Advanced"));
1906 top_s
->Add(tabset
, 1, wxGROW
);
1909 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
1910 pbutton_s
->AddStretchSpacer();
1911 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_ANY
, wxT("Close")), 0, wxGROW
);
1912 closebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
1913 wxCommandEventHandler(wxeditor_esettings::on_close
), NULL
, this);
1914 top_s
->Add(pbutton_s
, 0, wxGROW
);
1916 top_s
->SetSizeHints(this);
1920 wxeditor_esettings::~wxeditor_esettings()
1922 if(hotkeytab
) hotkeytab
->prepare_destroy();
1923 if(controllertab
) controllertab
->prepare_destroy();
1924 if(bindtab
) bindtab
->prepare_destroy();
1925 if(advtab
) advtab
->prepare_destroy();
1926 lsnes_kbd
.set_exclusive(NULL
);
1929 bool wxeditor_esettings::ShouldPreventAppExit() const
1934 void wxeditor_esettings::on_close(wxCommandEvent
& e
)
1937 hflip_enabled
= settingstab
->hflip
->GetValue();
1938 vflip_enabled
= settingstab
->vflip
->GetValue();
1939 rotate_enabled
= settingstab
->rotate
->GetValue();
1944 void wxsetingsdialog_display(wxWindow
* parent
, int mode
)
1946 modal_pause_holder hld
;
1949 editor
= new wxeditor_esettings(parent
, mode
);
1950 editor
->ShowModal();