If initsram/initstate points to LSS file, pull the matching member
[lsnes.git] / src / platform / wxwidgets / editor-action.cpp
blobcfc460460ff37ac54ca6df3205ebe49418cac39f
1 #include <wx/wx.h>
2 #include <wx/event.h>
3 #include <wx/control.h>
4 #include <wx/combobox.h>
5 #include <wx/spinctrl.h>
7 #include "core/command.hpp"
8 #include "core/dispatch.hpp"
9 #include "core/mainloop.hpp"
10 #include "core/moviedata.hpp"
11 #include "core/project.hpp"
12 #include "library/zip.hpp"
13 #include "library/json.hpp"
14 #include <stdexcept>
16 #include "platform/wxwidgets/platform.hpp"
19 class wxeditor_action : public wxDialog
21 public:
22 wxeditor_action(wxWindow* parent, const std::string& label, const std::list<interface_action_param>& _params);
23 std::vector<interface_action_paramval> get_results() { return results; }
24 void on_ok(wxCommandEvent& e);
25 void on_cancel(wxCommandEvent& e);
26 void on_change(wxCommandEvent& e);
27 private:
28 wxButton* ok;
29 wxButton* cancel;
30 std::list<interface_action_param> params;
31 std::list<wxWindow*> controls;
32 std::vector<interface_action_paramval> results;
35 wxeditor_action::wxeditor_action(wxWindow* parent, const std::string& label,
36 const std::list<interface_action_param>& _params)
37 : wxDialog(parent, wxID_ANY, towxstring("lsnes: Action " + label), wxDefaultPosition, wxSize(-1, -1))
40 CHECK_UI_THREAD;
41 params = _params;
42 Centre();
43 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
44 SetSizer(top_s);
46 for(auto i : params) {
47 regex_results r;
48 if(r = regex("string(:(.*))?", i.model)) {
49 wxBoxSizer* tmp1 = new wxBoxSizer(wxHORIZONTAL);
50 tmp1->Add(new wxStaticText(this, wxID_ANY, towxstring(i.name)), 0, wxGROW);
51 wxTextCtrl* tmp2 = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition,
52 wxSize(200, -1));
53 controls.push_back(tmp2);
54 tmp1->Add(tmp2, 1, wxGROW);
55 tmp2->Connect(wxEVT_COMMAND_TEXT_UPDATED,
56 wxCommandEventHandler(wxeditor_action::on_change), NULL, this);
57 top_s->Add(tmp1, 0, wxGROW);
58 } else if(r = regex("int:(-?[0-9]+),(-?[0-9]+)", i.model)) {
59 int64_t low, high;
60 try {
61 low = parse_value<int64_t>(r[1]);
62 high = parse_value<int64_t>(r[2]);
63 } catch(...) {
64 show_message_ok(this, "Internal error", (stringfmt() << "Unknown limits in '"
65 << i.model << "'.").str(), wxICON_EXCLAMATION);
66 return;
68 wxBoxSizer* tmp1 = new wxBoxSizer(wxHORIZONTAL);
69 tmp1->Add(new wxStaticText(this, wxID_ANY, towxstring(i.name)), 0, wxGROW);
70 wxSpinCtrl* tmp2 = new wxSpinCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
71 wxSP_ARROW_KEYS, low, high, low);
72 controls.push_back(tmp2);
73 tmp1->Add(tmp2, 1, wxGROW);
74 tmp2->Connect(wxEVT_COMMAND_TEXT_UPDATED,
75 wxCommandEventHandler(wxeditor_action::on_change), NULL, this);
76 top_s->Add(tmp1, 0, wxGROW);
77 } else if(r = regex("enum:(.*)", i.model)) {
78 std::vector<wxString> choices;
79 try {
80 JSON::node e(r[1]);
81 for(auto i : e) {
82 if(i.type() == JSON::string)
83 choices.push_back(towxstring(i.as_string8()));
84 else if(i.type() == JSON::array)
85 choices.push_back(towxstring(i.index(1).as_string8()));
86 else
87 throw std::runtime_error("Choice not array nor string");
89 } catch(std::exception& e) {
90 show_message_ok(this, "Internal error", (stringfmt() << "JSON parse error parsing "
91 << "model: " << e.what()).str(), wxICON_EXCLAMATION);
92 return;
94 wxBoxSizer* tmp1 = new wxBoxSizer(wxHORIZONTAL);
95 tmp1->Add(new wxStaticText(this, wxID_ANY, towxstring(i.name)), 0, wxGROW);
96 wxComboBox* tmp2 = new wxComboBox(this, wxID_ANY, choices[0], wxDefaultPosition,
97 wxDefaultSize, choices.size(), &choices[0], wxCB_READONLY);
98 controls.push_back(tmp2);
99 tmp1->Add(tmp2, 1, wxGROW);
100 tmp2->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
101 wxCommandEventHandler(wxeditor_action::on_change), NULL, this);
102 top_s->Add(tmp1, 0, wxGROW);
103 } else if(regex_match("bool", i.model)) {
104 wxCheckBox* tmp2 = new wxCheckBox(this, wxID_ANY, towxstring(i.name));
105 controls.push_back(tmp2);
106 top_s->Add(tmp2, 0, wxGROW);
107 } else if(regex_match("toggle", i.model)) {
108 //Nothing for toggles.
109 } else {
110 show_message_ok(this, "Internal error", (stringfmt() << "Unknown parameter model in '"
111 << i.model << "'.").str(), wxICON_EXCLAMATION);
112 return;
116 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
117 pbutton_s->AddStretchSpacer();
118 pbutton_s->Add(ok = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
119 pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
120 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
121 wxCommandEventHandler(wxeditor_action::on_ok), NULL, this);
122 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
123 wxCommandEventHandler(wxeditor_action::on_cancel), NULL, this);
124 top_s->Add(pbutton_s, 0, wxGROW);
126 top_s->SetSizeHints(this);
127 wxCommandEvent d;
128 on_change(d);
131 void wxeditor_action::on_ok(wxCommandEvent& e)
133 CHECK_UI_THREAD;
134 std::list<interface_action_param>::iterator i;
135 std::list<wxWindow*>::iterator j;
136 for(i = params.begin(), j = controls.begin(); i != params.end() && j != controls.end(); i++, j++) {
137 regex_results r;
138 interface_action_paramval pv;
139 if(r = regex("string(:(.*))?", i->model)) {
140 std::string p;
141 try {
142 p = tostdstring(reinterpret_cast<wxTextCtrl*>(*j)->GetValue());
143 if(r[2] != "" && !regex_match(r[2], p)) {
144 show_message_ok(this, "Error in parameters",
145 (stringfmt() << "String (" << i->name << ") does not satisfy "
146 << "constraints.").str(), wxICON_EXCLAMATION);
147 return;
149 } catch(...) {
150 show_message_ok(this, "Internal error", (stringfmt() << "Bad constraint in '"
151 << i->model << "'.").str(), wxICON_EXCLAMATION);
152 return;
154 pv.s = p;
155 } else if(r = regex("int:([0-9]+),([0-9]+)", i->model)) {
156 int64_t low, high, v;
157 try {
158 low = parse_value<int64_t>(r[1]);
159 high = parse_value<int64_t>(r[2]);
160 } catch(...) {
161 show_message_ok(this, "Internal error", (stringfmt() << "Unknown limits in '"
162 << i->model << "'.").str(), wxICON_EXCLAMATION);
163 return;
165 v = reinterpret_cast<wxSpinCtrl*>(*j)->GetValue();
166 if(v < low || v > high) {
167 show_message_ok(this, "Error in parameters",
168 (stringfmt() << "Integer (" << i->name << ") out of range.").str(),
169 wxICON_EXCLAMATION);
170 return;
172 pv.i = v;
173 } else if(r = regex("enum:(.*)", i->model)) {
174 int v = reinterpret_cast<wxComboBox*>(*j)->GetSelection();
175 if(v == wxNOT_FOUND) {
176 show_message_ok(this, "Error in parameters",
177 (stringfmt() << "No selection for '" << i->name << "'.").str(),
178 wxICON_EXCLAMATION);
179 return;
181 pv.i = v;
182 } else if(regex_match("bool", i->model)) {
183 pv.b = reinterpret_cast<wxCheckBox*>(*j)->GetValue();
184 } else if(regex_match("toggle", i->model)) {
185 //Empty.
186 } else {
187 show_message_ok(this, "Internal error", (stringfmt() << "Unknown parameter model in '"
188 << i->model << "'.").str(), wxICON_EXCLAMATION);
189 return;
191 results.push_back(pv);
193 EndModal(wxID_OK);
196 void wxeditor_action::on_cancel(wxCommandEvent& e)
198 CHECK_UI_THREAD;
199 EndModal(wxID_CANCEL);
202 void wxeditor_action::on_change(wxCommandEvent& e)
204 CHECK_UI_THREAD;
205 std::list<interface_action_param>::iterator i;
206 std::list<wxWindow*>::iterator j;
207 for(i = params.begin(), j = controls.begin(); i != params.end() && j != controls.end(); i++, j++) {
208 regex_results r;
209 if(r = regex("string(:(.*))?", i->model)) {
210 try {
211 std::string p = tostdstring(reinterpret_cast<wxTextCtrl*>(*j)->GetValue());
212 if(r[2] != "" && !regex_match(r[2], p)) {
213 goto bad;
215 } catch(...) {
216 goto bad;
218 } else if(r = regex("int:([0-9]+),([0-9]+)", i->model)) {
219 int64_t low, high, v;
220 try {
221 low = parse_value<int64_t>(r[1]);
222 high = parse_value<int64_t>(r[2]);
223 } catch(...) {
224 goto bad;
226 v = reinterpret_cast<wxSpinCtrl*>(*j)->GetValue();
227 if(v < low || v > high)
228 goto bad;
229 } else if(r = regex("enum:(.*)", i->model)) {
230 if(reinterpret_cast<wxComboBox*>(*j)->GetSelection() == wxNOT_FOUND)
231 goto bad;
232 } else if(regex_match("bool", i->model)) {
233 } else if(regex_match("toggle", i->model)) {
234 } else {
235 goto bad;
238 ok->Enable();
239 return;
240 bad:
241 ok->Disable();
245 std::vector<interface_action_paramval> prompt_action_params(wxWindow* parent, const std::string& label,
246 const std::list<interface_action_param>& params)
248 CHECK_UI_THREAD;
249 //Empty special case.
250 if(params.empty())
251 return std::vector<interface_action_paramval>();
252 //Another special case.
253 if(params.size() == 1 && params.begin()->model == std::string("toggle")) {
254 std::vector<interface_action_paramval> x;
255 x.push_back(interface_action_paramval());
256 return x;
258 modal_pause_holder hld;
259 try {
260 wxeditor_action* f = new wxeditor_action(parent, label, params);
261 int r = f->ShowModal();
262 if(r == wxID_CANCEL) {
263 f->Destroy();
264 throw canceled_exception();
266 auto p = f->get_results();
267 f->Destroy();
268 return p;
269 } catch(canceled_exception& e) {
270 throw;
271 } catch(...) {
272 throw canceled_exception();