Actually call on_reset callback
[lsnes.git] / src / platform / wxwidgets / editor-macro.cpp
bloba08a2e2e7f9056b4fae6c5bc17042e8ff105ad0a
1 #include <wx/wx.h>
2 #include <wx/event.h>
3 #include <wx/control.h>
4 #include <wx/combobox.h>
6 #include "core/controller.hpp"
7 #include "core/command.hpp"
8 #include "core/dispatch.hpp"
9 #include "core/instance.hpp"
10 #include "core/mainloop.hpp"
11 #include "core/moviedata.hpp"
12 #include "core/project.hpp"
13 #include "core/ui-services.hpp"
14 #include "library/zip.hpp"
15 #include "library/minmax.hpp"
16 #include "library/json.hpp"
18 #include "platform/wxwidgets/platform.hpp"
19 #include "platform/wxwidgets/loadsave.hpp"
21 namespace
23 std::map<unsigned, const portctrl::controller*> get_controller_set(emulator_instance& inst)
25 std::map<unsigned, const portctrl::controller*> r;
26 const portctrl::type_set& s = inst.controls->get_blank().porttypes();
27 for(unsigned i = 0; i < s.number_of_controllers(); i++) {
28 auto g = s.lcid_to_pcid(i);
29 if(g.first < 0)
30 continue;
31 portctrl::controller_set* pcs = s.port_type(g.first).controller_info;
32 if(g.second >= pcs->controllers.size())
33 continue;
34 r[i] = &pcs->controllers[g.second];
36 return r;
39 std::string summarize_controller(unsigned lcid, const JSON::node& c)
41 std::ostringstream s;
42 bool first = true;
43 uint64_t acnt = 0;
44 s << "#" << (lcid + 1) << " [";
45 for(auto i : c) {
46 if(i.type() == JSON::number)
47 acnt = max(acnt, i.as_uint() + 1);
48 if(i.type() != JSON::string)
49 continue;
50 if(!first)
51 s << ", ";
52 first = false;
53 s << i.as_string8();
55 if(acnt) {
56 if(!first)
57 s << ", ";
58 if(acnt == 1)
59 s << "1 analog";
60 else
61 s << acnt << " analogs";
63 s << "]";
64 return s.str();
67 class wxeditor_macro_1 : public wxDialog
69 public:
70 wxeditor_macro_1(wxWindow* parent, emulator_instance& _inst, const std::string& title,
71 const portctrl::macro& m);
72 void on_ok(wxCommandEvent& e);
73 void on_cancel(wxCommandEvent& e);
74 void on_macro_edit(wxCommandEvent& e);
75 portctrl::macro get_macro() { return curmacro; }
76 private:
77 emulator_instance& inst;
78 std::vector<wxCheckBox*> enabled;
79 std::vector<wxTextCtrl*> macros;
80 wxButton* okbutton;
81 wxButton* cancelbutton;
82 wxRadioButton* rb_overwrite;
83 wxRadioButton* rb_or;
84 wxRadioButton* rb_xor;
85 portctrl::macro curmacro;
86 bool constructing;
89 wxeditor_macro_1::wxeditor_macro_1(wxWindow* parent, emulator_instance& _inst, const std::string& title,
90 const portctrl::macro& m)
91 : wxDialog(parent, wxID_ANY, towxstring(title), wxDefaultPosition, wxSize(-1, -1)), inst(_inst)
93 CHECK_UI_THREAD;
94 constructing = true;
95 curmacro = m;
96 Centre();
97 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
98 SetSizer(top_s);
100 size_t ctrlsize = 0;
101 for(auto i : curmacro.macros)
102 ctrlsize = max(ctrlsize, (size_t)(i.first + 1));
103 enabled.resize(ctrlsize);
104 macros.resize(ctrlsize);
105 for(auto i : curmacro.macros) {
106 top_s->Add(new wxStaticText(this, wxID_ANY, towxstring(summarize_controller(i.first,
107 i.second._descriptor))), 0, wxGROW);
108 wxBoxSizer* tmp = new wxBoxSizer(wxHORIZONTAL);
109 tmp->Add(enabled[i.first] = new wxCheckBox(this, wxID_ANY, wxT("Enabled")), 0, wxGROW);
110 enabled[i.first]->SetValue(curmacro.macros.count(i.first) &&
111 curmacro.macros[i.first].enabled);
112 tmp->Add(macros[i.first] = new wxTextCtrl(this, wxID_ANY,
113 towxstring(curmacro.macros.count(i.first) ? curmacro.macros[i.first].orig : ""),
114 wxDefaultPosition, wxSize(400, -1)), 0, wxGROW);
115 macros[i.first]->Connect(wxEVT_COMMAND_TEXT_UPDATED,
116 wxCommandEventHandler(wxeditor_macro_1::on_macro_edit), NULL, this);
117 top_s->Add(tmp, 1, wxGROW);
120 wxBoxSizer* tmp2 = new wxBoxSizer(wxHORIZONTAL);
121 rb_overwrite = new wxRadioButton(this, wxID_HIGHEST + 1, wxT("Overwrite"), wxDefaultPosition,
122 wxDefaultSize, wxRB_GROUP);
123 rb_or = new wxRadioButton(this, wxID_HIGHEST + 1, wxT("OR"));
124 rb_xor = new wxRadioButton(this, wxID_HIGHEST + 1, wxT("XOR"));
125 tmp2->Add(rb_overwrite, 0, wxGROW);
126 tmp2->Add(rb_or, 0, wxGROW);
127 tmp2->Add(rb_xor, 0, wxGROW);
128 top_s->Add(tmp2, 0, wxGROW);
130 switch(curmacro.amode) {
131 case portctrl::macro_data::AM_OVERWRITE: rb_overwrite->SetValue(true); break;
132 case portctrl::macro_data::AM_OR: rb_or->SetValue(true); break;
133 case portctrl::macro_data::AM_XOR: rb_xor->SetValue(true); break;
136 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
137 pbutton_s->AddStretchSpacer();
138 pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
139 pbutton_s->Add(cancelbutton = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
140 okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
141 wxCommandEventHandler(wxeditor_macro_1::on_ok), NULL, this);
142 cancelbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
143 wxCommandEventHandler(wxeditor_macro_1::on_cancel), NULL, this);
144 top_s->Add(pbutton_s, 0, wxGROW);
146 top_s->SetSizeHints(this);
147 Fit();
149 constructing = false;
150 Show();
153 void wxeditor_macro_1::on_ok(wxCommandEvent& e)
155 CHECK_UI_THREAD;
156 portctrl::macro m;
157 if(rb_overwrite->GetValue()) m.amode = portctrl::macro_data::AM_OVERWRITE;
158 if(rb_or->GetValue()) m.amode = portctrl::macro_data::AM_OR;
159 if(rb_xor->GetValue()) m.amode = portctrl::macro_data::AM_XOR;
160 try {
161 for(auto i : curmacro.macros) {
162 if(!macros[i.first])
163 continue;
164 m.macros[i.first] = portctrl::macro_data(tostdstring(macros[i.first]->GetValue()),
165 curmacro.macros[i.first].get_descriptor(), i.first);
166 m.macros[i.first].enabled = enabled[i.first]->GetValue();
168 } catch(std::exception& e) {
169 wxMessageBox(towxstring(e.what()), _T("Error parsing macro"), wxICON_EXCLAMATION | wxOK,
170 this);
171 return;
173 curmacro = m;
174 EndModal(wxID_OK);
177 void wxeditor_macro_1::on_cancel(wxCommandEvent& e)
179 CHECK_UI_THREAD;
180 EndModal(wxID_CANCEL);
183 void wxeditor_macro_1::on_macro_edit(wxCommandEvent& e)
185 CHECK_UI_THREAD;
186 if(constructing)
187 return;
188 bool ret = true;
189 auto c = get_controller_set(inst);
190 for(auto i : curmacro.macros) {
191 if(!portctrl::macro_data::syntax_check(tostdstring(macros[i.first]->GetValue()).c_str(),
192 curmacro.macros[i.first].get_descriptor()))
193 ret = false;
195 okbutton->Enable(ret);
199 class wxeditor_macro : public wxDialog
201 public:
202 wxeditor_macro(wxWindow* parent, emulator_instance& _inst);
203 bool ShouldPreventAppExit() const;
204 void on_close(wxCommandEvent& e);
205 void on_change(wxCommandEvent& e);
206 void on_add(wxCommandEvent& e);
207 void on_edit(wxCommandEvent& e);
208 void on_rename(wxCommandEvent& e);
209 void on_delete(wxCommandEvent& e);
210 void on_load(wxCommandEvent& e);
211 void on_save(wxCommandEvent& e);
212 private:
213 bool do_edit(const std::string& mname, portctrl::macro& m);
214 void update();
215 emulator_instance& inst;
216 wxButton* closebutton;
217 wxButton* addbutton;
218 wxButton* editbutton;
219 wxButton* renamebutton;
220 wxButton* deletebutton;
221 wxButton* savebutton;
222 wxButton* loadbutton;
223 wxListBox* macros;
224 std::vector<std::string> macronames;
227 bool wxeditor_macro::ShouldPreventAppExit() const
229 return false;
232 wxeditor_macro::wxeditor_macro(wxWindow* parent, emulator_instance& _inst)
233 : wxDialog(parent, wxID_ANY, wxT("lsnes: Edit macros"), wxDefaultPosition, wxSize(-1, -1)), inst(_inst)
235 CHECK_UI_THREAD;
236 Centre();
237 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
238 SetSizer(top_s);
240 top_s->Add(macros = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400), 0, NULL,
241 wxLB_SINGLE), 1, wxGROW);
242 macros->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
243 wxCommandEventHandler(wxeditor_macro::on_change), NULL, this);
245 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
246 pbutton_s->Add(addbutton = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
247 pbutton_s->Add(editbutton = new wxButton(this, wxID_ANY, wxT("Edit")), 0, wxGROW);
248 pbutton_s->Add(renamebutton = new wxButton(this, wxID_ANY, wxT("Rename")), 0, wxGROW);
249 pbutton_s->Add(deletebutton = new wxButton(this, wxID_ANY, wxT("Delete")), 0, wxGROW);
250 pbutton_s->Add(savebutton = new wxButton(this, wxID_ANY, wxT("Save")), 0, wxGROW);
251 pbutton_s->Add(loadbutton = new wxButton(this, wxID_ANY, wxT("Load")), 0, wxGROW);
252 pbutton_s->AddStretchSpacer();
253 pbutton_s->Add(closebutton = new wxButton(this, wxID_OK, wxT("Close")), 0, wxGROW);
254 addbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
255 wxCommandEventHandler(wxeditor_macro::on_add), NULL, this);
256 editbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
257 wxCommandEventHandler(wxeditor_macro::on_edit), NULL, this);
258 renamebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
259 wxCommandEventHandler(wxeditor_macro::on_rename), NULL, this);
260 deletebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
261 wxCommandEventHandler(wxeditor_macro::on_delete), NULL, this);
262 savebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
263 wxCommandEventHandler(wxeditor_macro::on_save), NULL, this);
264 loadbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
265 wxCommandEventHandler(wxeditor_macro::on_load), NULL, this);
266 closebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
267 wxCommandEventHandler(wxeditor_macro::on_close), NULL, this);
268 top_s->Add(pbutton_s, 0, wxGROW);
270 top_s->SetSizeHints(this);
271 Fit();
273 update();
274 Show();
277 void wxeditor_macro::update()
279 CHECK_UI_THREAD;
280 std::set<std::string> macro_list = inst.controls->enumerate_macro();
281 std::string current;
282 int sel = macros->GetSelection();
283 int selpos = -1;
284 int idx = 0;
285 if(sel != wxNOT_FOUND)
286 current = macronames[sel];
287 macros->Clear();
288 macronames.clear();
289 for(auto i : macro_list) {
290 macronames.push_back(i);
291 macros->Append(towxstring(i));
292 if(i == current)
293 selpos = idx;
294 idx++;
296 if(!macro_list.empty()) {
297 if(selpos >= 0)
298 macros->SetSelection(selpos);
299 if(sel < idx)
300 macros->SetSelection(sel);
301 else
302 macros->SetSelection(idx - 1);
304 wxCommandEvent e;
305 on_change(e);
308 void wxeditor_macro::on_close(wxCommandEvent& e)
310 CHECK_UI_THREAD;
311 EndModal(wxID_OK);
314 void wxeditor_macro::on_change(wxCommandEvent& e)
316 CHECK_UI_THREAD;
317 int sel = macros->GetSelection();
318 editbutton->Enable(sel != wxNOT_FOUND);
319 deletebutton->Enable(sel != wxNOT_FOUND);
320 savebutton->Enable(sel != wxNOT_FOUND);
323 void wxeditor_macro::on_delete(wxCommandEvent& e)
325 CHECK_UI_THREAD;
326 int sel = macros->GetSelection();
327 if(sel == wxNOT_FOUND)
328 return;
329 std::string macro = macronames[sel];
330 inst.controls->erase_macro(macro);
331 update();
334 void wxeditor_macro::on_add(wxCommandEvent& e)
336 CHECK_UI_THREAD;
337 try {
338 std::string mname = pick_text(this, "Name new macro", "Enter name for the new macro:", "");
339 if(mname == "")
340 return;
341 portctrl::macro _macro;
342 _macro.amode = portctrl::macro_data::AM_XOR;
343 auto c = get_controller_set(inst);
344 for(auto i : c) {
345 _macro.macros[i.first] = portctrl::macro_data("",
346 portctrl::macro_data::make_descriptor(*i.second), i.first);
347 _macro.macros[i.first].enabled = false;
349 if(do_edit("", _macro))
350 inst.controls->set_macro(mname, _macro);
351 } catch(canceled_exception& e) {
352 } catch(std::exception& e) {
353 wxMessageBox(towxstring(e.what()), _T("Error creating macro"), wxICON_EXCLAMATION | wxOK, this);
355 update();
358 void wxeditor_macro::on_edit(wxCommandEvent& e)
360 CHECK_UI_THREAD;
361 int sel = macros->GetSelection();
362 if(sel == wxNOT_FOUND)
363 return;
364 std::string macro = macronames[sel];
365 portctrl::macro _macro;
366 try {
367 _macro = inst.controls->get_macro(macro);
368 } catch(...) {
369 return;
371 if(do_edit(macro, _macro))
372 inst.controls->set_macro(macro, _macro);
375 void wxeditor_macro::on_rename(wxCommandEvent& e)
377 CHECK_UI_THREAD;
378 int sel = macros->GetSelection();
379 if(sel == wxNOT_FOUND)
380 return;
381 std::string macro = macronames[sel];
382 try {
383 std::string mname = pick_text(this, "Rename macro", "Enter new name for the macro:", "");
384 if(mname == "")
385 return;
386 inst.controls->rename_macro(macro, mname);
387 } catch(canceled_exception& e) {
388 } catch(std::exception& e) {
389 wxMessageBox(towxstring(e.what()), _T("Error renaming macro"), wxICON_EXCLAMATION | wxOK, this);
391 update();
394 void wxeditor_macro::on_load(wxCommandEvent& e)
396 CHECK_UI_THREAD;
397 try {
398 std::string mname = pick_text(this, "Name new macro", "Enter name for the new macro:", "");
399 if(mname == "")
400 return;
401 std::string file = choose_file_load(this, "Load macro from", UI_get_project_otherpath(inst),
402 filetype_macro);
403 std::vector<char> contents = zip::readrel(file, "");
404 portctrl::macro m(JSON::node(std::string(contents.begin(), contents.end())));
405 inst.controls->set_macro(mname, m);
406 } catch(canceled_exception& e) {
407 } catch(std::exception& e) {
408 wxMessageBox(towxstring(e.what()), _T("Error loading macro"), wxICON_EXCLAMATION | wxOK, this);
410 update();
413 void wxeditor_macro::on_save(wxCommandEvent& e)
415 CHECK_UI_THREAD;
416 int sel = macros->GetSelection();
417 if(sel == wxNOT_FOUND)
418 return;
419 std::string macro = macronames[sel];
420 portctrl::macro* _macro;
421 try {
422 _macro = &inst.controls->get_macro(macro);
423 } catch(...) {
424 return;
426 std::string mdata = _macro->serialize().serialize();
427 //Okay, have the macro data, now prompt for file and save.
428 try {
429 std::string tfile = choose_file_save(this, "Save macro to", UI_get_project_otherpath(inst),
430 filetype_macro);
431 std::ofstream f(tfile);
432 f << mdata;
433 if(!f)
434 wxMessageBox(towxstring("Error saving macro"), _T("Error"), wxICON_EXCLAMATION | wxOK, this);
435 } catch(canceled_exception& e) {
439 bool wxeditor_macro::do_edit(const std::string& mname, portctrl::macro& m)
441 CHECK_UI_THREAD;
442 wxeditor_macro_1* editor;
443 bool ret;
445 try {
446 std::string title;
447 if(mname != "")
448 title = "Editing macro " + mname;
449 else
450 title = "Create a new macro";
451 editor = new wxeditor_macro_1(this, inst, title, m);
452 ret = (editor->ShowModal() == wxID_OK);
453 if(ret)
454 m = editor->get_macro();
455 } catch(...) {
456 return false;
458 editor->Destroy();
459 return ret;
462 void wxeditor_macro_display(wxWindow* parent, emulator_instance& inst)
464 CHECK_UI_THREAD;
465 modal_pause_holder hld;
466 wxDialog* editor;
467 try {
468 editor = new wxeditor_macro(parent, inst);
469 editor->ShowModal();
470 } catch(...) {
471 return;
473 editor->Destroy();