Fix crash if selected settings window does not open
[lsnes.git] / src / platform / wxwidgets / settings-common.cpp
blob48617fde2b06cf5a812ac7a33e23cfc8a21d7026
1 #include <functional>
2 #include "platform/wxwidgets/settings-common.hpp"
3 #include "core/instance.hpp"
4 #include "core/instance-map.hpp"
5 #include "core/keymapper.hpp"
7 namespace
9 std::map<std::string, settings_tab_factory*>& _factories()
11 static std::map<std::string, settings_tab_factory*> x;
12 return x;
15 class wxeditor_esettings2;
16 instance_map<wxeditor_esettings2> sdialogs;
20 settings_tab_factory::settings_tab_factory(const std::string& tabname,
21 std::function<settings_tab*(wxWindow* parent, emulator_instance& inst)> create_fn)
22 : _tabname(tabname), _create_fn(create_fn)
24 _factories()[_tabname] = this;
27 settings_tab_factory::~settings_tab_factory()
29 _factories().erase(_tabname);
32 std::list<settings_tab_factory*> settings_tab_factory::factories()
34 std::list<settings_tab_factory*> f;
35 for(auto i : _factories())
36 f.push_back(i.second);
37 return f;
40 settings_menu::settings_menu(wxWindow* win, emulator_instance& _inst, int id)
41 : inst(_inst)
43 CHECK_UI_THREAD;
44 parent = win;
45 items[id] = NULL;
46 Append(id, towxstring("All as tabs..."));
47 win->Connect(id++, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(settings_menu::on_selected), NULL,
48 this);
49 AppendSeparator();
50 for(auto i : settings_tab_factory::factories()) {
51 items[id] = i;
52 Append(id, towxstring(i->get_name() + "..."));
53 win->Connect(id++, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(settings_menu::on_selected),
54 NULL, this);
58 void settings_menu::on_selected(wxCommandEvent& e)
60 CHECK_UI_THREAD;
61 int id = e.GetId();
62 if(!items.count(id))
63 return;
64 display_settings_dialog(parent, inst, items[id]);
67 namespace
69 volatile bool keygrab_active = false;
70 std::string pkey;
71 std::function<void(std::string key)> keygrab_callback;
73 class keygrabber : public keyboard::event_listener
75 public:
76 keygrabber()
78 keygrab_active = false;
80 ~keygrabber() throw()
83 void on_key_event(keyboard::modifier_set& mods, keyboard::key& key, keyboard::event& event)
85 if(!keygrab_active)
86 return;
87 uint32_t dev = event.get_change_mask();
88 auto subkeys = key.get_subkeys();
89 for(unsigned i = 0; i < 16 && i < subkeys.size(); i++) {
90 std::string pname = key.get_name() + subkeys[i];
91 if(((dev >> (2 * i)) & 3) == 3)
92 pkey = pname;
93 if(((dev >> (2 * i)) & 3) == 2) {
94 if(pkey == pname) {
95 keygrab_active = false;
96 std::string tmp = pkey;
97 runuifun([this, tmp]() { this->keygrab_callback(tmp); });
98 } else
99 pkey = "";
103 volatile bool keygrab_active;
104 std::string pkey;
105 std::function<void(std::string key)> keygrab_callback;
108 class wxeditor_esettings2 : public wxDialog
110 public:
111 wxeditor_esettings2(emulator_instance& _inst, wxWindow* parent, settings_tab_factory* singletab);
112 ~wxeditor_esettings2();
113 bool ShouldPreventAppExit() const;
114 void on_close(wxCommandEvent& e);
115 void on_notify();
116 private:
117 emulator_instance& inst;
118 wxNotebook* tabset;
119 wxButton* closebutton;
120 std::list<settings_tab*> tabs;
121 std::string get_title(settings_tab_factory* singletab);
122 public:
123 keygrabber keygrab;
126 std::string wxeditor_esettings2::get_title(settings_tab_factory* singletab)
128 if(!singletab)
129 return "lsnes: Configuration";
130 else
131 return "lsnes: Configuration: " + singletab->get_name();
134 wxeditor_esettings2::wxeditor_esettings2(emulator_instance& _inst, wxWindow* parent,
135 settings_tab_factory* singletab)
136 : wxDialog(parent, wxID_ANY, towxstring(get_title(singletab)), wxDefaultPosition, wxSize(-1, -1),
137 wxCAPTION | wxSYSTEM_MENU | wxCLOSE_BOX | wxRESIZE_BORDER), inst(_inst)
139 CHECK_UI_THREAD;
140 //Grab keys to prevent the joystick driver from running who knows what commands.
141 inst.keyboard->set_exclusive(&keygrab);
143 Centre();
144 wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
145 SetSizer(top_s);
147 if(singletab) {
148 //If this throws, let it throw through.
149 settings_tab* t;
150 try {
151 t = singletab->create(this, inst);
152 } catch(...) {
153 //Release the key grab, so we do not crash.
154 inst.keyboard->set_exclusive(NULL);
155 throw;
157 top_s->Add(t, 1, wxGROW);
158 t->set_notify([this]() { this->on_notify(); });
159 tabs.push_back(t);
160 } else {
161 int created = 0;
162 tabset = new wxNotebook(this, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP);
163 for(auto i : settings_tab_factory::factories()) {
164 settings_tab* t;
165 try {
166 t = i->create(tabset, inst);
167 } catch(...) {
168 continue;
170 tabset->AddPage(t, towxstring(i->get_name()));
171 t->set_notify([this]() { this->on_notify(); });
172 tabs.push_back(t);
173 created++;
175 top_s->Add(tabset, 1, wxGROW);
176 if(!created) {
177 //Release the key grab, so we do not crash.
178 inst.keyboard->set_exclusive(NULL);
179 throw std::runtime_error("Nothing to configure here, move along");
183 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
184 pbutton_s->AddStretchSpacer();
185 pbutton_s->Add(closebutton = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
186 closebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
187 wxCommandEventHandler(wxeditor_esettings2::on_close), NULL, this);
188 top_s->Add(pbutton_s, 0, wxGROW);
190 top_s->SetSizeHints(this);
191 Fit();
194 wxeditor_esettings2::~wxeditor_esettings2()
196 CHECK_UI_THREAD;
197 for(auto i : tabs)
198 i->notify_close();
199 inst.keyboard->set_exclusive(NULL);
200 sdialogs.remove(inst);
203 bool wxeditor_esettings2::ShouldPreventAppExit() const
205 return false;
208 void wxeditor_esettings2::on_close(wxCommandEvent& e)
210 CHECK_UI_THREAD;
211 for(auto i : tabs)
212 i->on_close();
213 EndModal(wxID_OK);
216 void wxeditor_esettings2::on_notify()
218 CHECK_UI_THREAD;
219 for(auto i : tabs)
220 i->on_notify();
224 void display_settings_dialog(wxWindow* parent, emulator_instance& inst, settings_tab_factory* singletab)
226 CHECK_UI_THREAD;
227 modal_pause_holder hld;
228 wxDialog* editor;
229 try {
230 try {
231 editor = sdialogs.create(inst, parent, singletab);
232 } catch(std::exception& e) {
233 std::string title = "Configure";
234 if(singletab)
235 title = "Configure " + singletab->get_name();
236 show_message_ok(parent, title, e.what(), wxICON_EXCLAMATION);
237 return;
239 editor->ShowModal();
240 } catch(...) {
241 return;
243 editor->Destroy();
244 do_save_configuration();
247 void settings_activate_keygrab(emulator_instance& inst, std::function<void(std::string key)> callback)
249 auto s = sdialogs.lookup(inst);
250 if(!s) return;
251 s->keygrab.keygrab_callback = callback;
252 s->keygrab.keygrab_active = true;
255 void settings_deactivate_keygrab(emulator_instance& inst)
257 auto s = sdialogs.lookup(inst);
258 if(!s) return;
259 s->keygrab.keygrab_active = false;