Make various instance stuff to take references to other instance objs
[lsnes.git] / src / platform / wxwidgets / editor-autohold.cpp
blob49a4f9b2963a53bda8be34a6d289bac41ad47877
1 #include "core/controller.hpp"
2 #include "core/movie.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/window.hpp"
7 #include "interface/controller.hpp"
8 #include "core/mainloop.hpp"
9 #include "platform/wxwidgets/platform.hpp"
10 #include "platform/wxwidgets/textrender.hpp"
11 #include "library/minmax.hpp"
12 #include "library/string.hpp"
13 #include "library/utf8.hpp"
15 #include <algorithm>
16 #include <cstring>
17 #include <wx/wx.h>
18 #include <wx/event.h>
19 #include <wx/control.h>
20 #include <wx/combobox.h>
21 #include <wx/statline.h>
23 class wxeditor_autohold : public wxDialog
25 public:
26 wxeditor_autohold(wxWindow* parent);
27 ~wxeditor_autohold() throw();
28 bool ShouldPreventAppExit() const;
29 void on_wclose(wxCloseEvent& e);
30 void on_checkbox(wxCommandEvent& e);
31 private:
32 struct dispatch::target<unsigned, unsigned, unsigned, bool> ahupdate;
33 struct dispatch::target<unsigned, unsigned, unsigned, unsigned, unsigned> afupdate;
34 struct dispatch::target<> ahreconfigure;
36 struct control_triple
38 unsigned port;
39 unsigned controller;
40 unsigned index;
41 int afid;
42 wxStaticText* label; //Used only by UI version.
43 wxCheckBox* check; //Used only by UI version.
44 wxCheckBox* afcheck; //Used only by UI version.
45 bool status; //Used only by internal version.
46 bool afstatus; //Used only by internal version.
47 unsigned logical; //Logical controller. Internal only.
48 std::string name; //Name. Internal only.
50 struct controller_double
52 wxPanel* panel;
53 wxStaticBox* box;
54 wxStaticText* label;
55 wxSizer* rtop;
56 wxSizer* top;
57 wxSizer* grid;
59 std::map<int, control_triple> autoholds;
60 std::vector<controller_double> panels;
61 void update_controls();
62 bool closing;
63 wxBoxSizer* hsizer;
66 namespace
68 wxeditor_autohold* autohold_open;
71 wxeditor_autohold::~wxeditor_autohold() throw() {}
73 wxeditor_autohold::wxeditor_autohold(wxWindow* parent)
74 : wxDialog(parent, wxID_ANY, wxT("lsnes: Autohold/Autofire"), wxDefaultPosition, wxSize(-1, -1))
76 closing = false;
77 Centre();
78 hsizer = new wxBoxSizer(wxHORIZONTAL);
79 SetSizer(hsizer);
80 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxeditor_autohold::on_wclose));
81 update_controls();
82 hsizer->SetSizeHints(this);
83 Fit();
85 ahupdate.set(notify_autohold_update, [this](unsigned port, unsigned controller, unsigned ctrlnum,
86 bool newstate) {
87 runuifun([this, port, controller, ctrlnum, newstate]() {
88 for(auto i : this->autoholds) {
89 if(i.second.port != port) continue;
90 if(i.second.controller != controller) continue;
91 if(i.second.index != ctrlnum) continue;
92 i.second.check->SetValue(newstate);
94 });
95 });
96 afupdate.set(notify_autofire_update, [this](unsigned port, unsigned controller, unsigned ctrlnum,
97 unsigned duty, unsigned cyclelen) {
98 runuifun([this, port, controller, ctrlnum, duty]() {
99 for(auto i : this->autoholds) {
100 if(i.second.port != port) continue;
101 if(i.second.controller != controller) continue;
102 if(i.second.index != ctrlnum) continue;
103 i.second.afcheck->SetValue(duty != 0);
107 ahreconfigure.set(notify_autohold_reconfigure, [this]() {
108 runuifun([this]() {
109 try {
110 this->update_controls();
111 } catch(std::runtime_error& e) {
112 //Close the window.
113 bool wasc = closing;
114 closing = true;
115 autohold_open = NULL;
116 if(!wasc)
117 Destroy();
123 void wxeditor_autohold::on_checkbox(wxCommandEvent& e)
125 int id = e.GetId();
126 if(!autoholds.count(id))
127 return;
128 auto t = autoholds[id];
129 bool isaf = (t.afid == id);
130 bool newstate = isaf ? t.afcheck->IsChecked() : t.check->IsChecked();
131 bool state = false;
132 lsnes_instance.iqueue.run([t, newstate, &state, isaf]() {
133 if(isaf) {
134 auto _state = CORE().controls.autofire2(t.port, t.controller, t.index);
135 state = (_state.first != 0);
136 if(lua_callback_do_button(t.port, t.controller, t.index, newstate ? "autofire 1 2" :
137 "autofire"))
138 return;
139 CORE().controls.autofire2(t.port, t.controller, t.index, newstate ? 1 : 0, newstate ? 2 : 1);
140 state = newstate;
141 } else {
142 state = CORE().controls.autohold2(t.port, t.controller, t.index);
143 if(lua_callback_do_button(t.port, t.controller, t.index, newstate ? "hold" : "unhold"))
144 return;
145 CORE().controls.autohold2(t.port, t.controller, t.index, newstate);
146 state = newstate;
149 if(isaf)
150 t.afcheck->SetValue(state);
151 else
152 t.check->SetValue(state);
155 void wxeditor_autohold::update_controls()
157 for(auto i : autoholds) {
158 if(i.first != i.second.afid)
159 i.second.label->Destroy();
160 if(i.first != i.second.afid)
161 i.second.check->Destroy();
162 if(i.first == i.second.afid)
163 i.second.afcheck->Destroy();
165 for(auto i : panels) {
166 hsizer->Detach(i.panel);
167 i.panel->Destroy();
169 autoholds.clear();
170 panels.clear();
171 std::vector<control_triple> _autoholds;
172 std::vector<std::string> _controller_labels;
173 lsnes_instance.iqueue.run([&_autoholds, &_controller_labels](){
174 std::map<std::string, unsigned> next_in_class;
175 controller_frame model = CORE().controls.get_blank();
176 const port_type_set& pts = model.porttypes();
177 unsigned cnum_g = 0;
178 for(unsigned i = 0;; i++) {
179 auto pcid = CORE().controls.lcid_to_pcid(i);
180 if(pcid.first < 0)
181 break;
182 const port_type& pt = pts.port_type(pcid.first);
183 const port_controller_set& pci = *(pt.controller_info);
184 if((ssize_t)pci.controllers.size() <= pcid.second)
185 continue;
186 const port_controller& pc = pci.controllers[pcid.second];
187 //First check that this has non-hidden buttons.
188 bool has_buttons = false;
189 for(unsigned k = 0; k < pc.buttons.size(); k++) {
190 const port_controller_button& pcb = pc.buttons[k];
191 if(pcb.type == port_controller_button::TYPE_BUTTON && !pcb.shadow)
192 has_buttons = true;
194 if(!has_buttons)
195 continue;
196 //Okay, a valid controller.
197 if(!next_in_class.count(pc.cclass))
198 next_in_class[pc.cclass] = 1;
199 uint32_t cnum = next_in_class[pc.cclass]++;
200 _controller_labels.push_back((stringfmt() << pc.cclass << "-" << cnum).str());
201 for(unsigned k = 0; k < pc.buttons.size(); k++) {
202 const port_controller_button& pcb = pc.buttons[k];
203 if(pcb.type != port_controller_button::TYPE_BUTTON || pcb.shadow)
204 continue;
205 struct control_triple t;
206 t.port = pcid.first;
207 t.controller = pcid.second;
208 t.index = k;
209 t.status = CORE().controls.autohold2(pcid.first, pcid.second, k);
210 auto h = CORE().controls.autofire2(pcid.first, pcid.second, k);
211 t.afstatus = (h.first > 0);
212 t.logical = cnum_g;
213 t.name = pcb.name;
214 _autoholds.push_back(t);
216 cnum_g++;
219 int next_id = wxID_HIGHEST + 1;
220 unsigned last_logical = 0xFFFFFFFFUL;
221 wxSizer* current = NULL;
222 wxPanel* current_p = NULL;
223 wxSizer* current_t = NULL;
224 for(auto i : _autoholds) {
225 if(i.logical != last_logical) {
226 //New controller starts.
227 if(current_t) {
228 hsizer->Add(current_p);
229 current_t->SetSizeHints(current_p);
230 current_t->Fit(current_p);
232 controller_double d;
233 current_p = d.panel = new wxPanel(this, wxID_ANY);
234 current_t = d.rtop = new wxBoxSizer(wxVERTICAL);
235 d.panel->SetSizer(d.rtop);
236 d.box = new wxStaticBox(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
237 d.top = new wxStaticBoxSizer(d.box, wxVERTICAL);
238 #ifdef __WXMAC__
239 d.label = new wxStaticText(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
240 d.top->Add(d.label);
241 #endif
242 current = d.grid = new wxFlexGridSizer(0, 3, 0, 0);
243 d.top->Add(d.grid);
244 d.rtop->Add(d.top);
245 panels.push_back(d);
246 last_logical = i.logical;
248 wxStaticText* label = new wxStaticText(current_p, wxID_ANY, towxstring(i.name));
249 wxCheckBox* check = new wxCheckBox(current_p, next_id, wxT("Hold"));
250 wxCheckBox* afcheck = new wxCheckBox(current_p, next_id + 1, wxT("Rapid"));
251 struct control_triple t;
252 t.port = i.port;
253 t.controller = i.controller;
254 t.index = i.index;
255 t.label = label;
256 t.check = check;
257 t.afcheck = afcheck;
258 t.afid = next_id + 1;
259 check->SetValue(i.status);
260 check->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(wxeditor_autohold::on_checkbox),
261 NULL, this);
262 afcheck->SetValue(i.afstatus);
263 afcheck->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
264 wxCommandEventHandler(wxeditor_autohold::on_checkbox), NULL, this);
265 current->Add(label);
266 current->Add(check);
267 current->Add(afcheck);
268 autoholds[next_id++] = t;
269 autoholds[next_id++] = t;
271 if(current_t) {
272 hsizer->Add(current_p);
273 current_t->SetSizeHints(current_p);
274 current_t->Fit(current_p);
276 if(_autoholds.empty()) {
277 throw std::runtime_error("No controlers");
279 hsizer->SetSizeHints(this);
280 hsizer->Layout();
281 hsizer->Fit(this);
282 Fit();
285 bool wxeditor_autohold::ShouldPreventAppExit() const { return false; }
287 void wxeditor_autohold::on_wclose(wxCloseEvent& e)
289 bool wasc = closing;
290 closing = true;
291 autohold_open = NULL;
292 if(!wasc)
293 Destroy();
296 void wxeditor_autohold_display(wxWindow* parent)
298 if(autohold_open)
299 return;
300 wxeditor_autohold* v;
301 try {
302 v = new wxeditor_autohold(parent);
303 } catch(std::runtime_error& e) {
304 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION | wxOK, parent);
305 return;
307 v->Show();
308 autohold_open = v;