If initsram/initstate points to LSS file, pull the matching member
[lsnes.git] / src / platform / wxwidgets / editor-multitrack.cpp
blob4da471b55c61868d31b4b8cdad26dfb665a01ddc
1 #include <wx/wx.h>
2 #include <wx/event.h>
3 #include <wx/control.h>
4 #include <wx/combobox.h>
5 #include <wx/statline.h>
6 #include <wx/spinctrl.h>
8 #include "core/controller.hpp"
9 #include "core/instance.hpp"
10 #include "core/moviedata.hpp"
11 #include "core/multitrack.hpp"
12 #include "core/dispatch.hpp"
13 #include "core/window.hpp"
15 #include "interface/controller.hpp"
16 #include "core/mainloop.hpp"
17 #include "platform/wxwidgets/platform.hpp"
18 #include "platform/wxwidgets/textrender.hpp"
19 #include "library/minmax.hpp"
20 #include "library/string.hpp"
21 #include "library/utf8.hpp"
23 #include <algorithm>
24 #include <cstring>
26 #define MTMODE_PRESERVE "Preserve"
27 #define MTMODE_OVERWRITE "Overwrite"
28 #define MTMODE_OR "OR"
29 #define MTMODE_XOR "XOR"
31 namespace
35 class wxeditor_multitrack : public wxDialog
37 public:
38 wxeditor_multitrack(wxWindow* parent, emulator_instance& _inst);
39 ~wxeditor_multitrack() throw();
40 bool ShouldPreventAppExit() const;
41 void on_wclose(wxCloseEvent& e);
42 void on_control(wxCommandEvent& e);
43 private:
44 struct dispatch::target<> ahreconfigure;
45 struct dispatch::target<bool> ahmodechange;
46 struct dispatch::target<unsigned, unsigned, int> ahmtchange;
47 struct controller_info
49 unsigned port;
50 unsigned controller;
51 wxStaticText* text;
52 wxComboBox* mode;
54 struct controller_info2
56 std::string name;
57 unsigned port;
58 unsigned controller;
60 emulator_instance& inst;
61 std::vector<controller_info> controllers;
62 void update_controls();
63 bool closing;
64 wxFlexGridSizer* vsizer;
65 const portctrl::type_set* typeset;
68 namespace
70 wxeditor_multitrack* multitrack_open;
73 wxeditor_multitrack::~wxeditor_multitrack() throw() {}
75 wxeditor_multitrack::wxeditor_multitrack(wxWindow* parent, emulator_instance& _inst)
76 : wxDialog(parent, wxID_ANY, wxT("lsnes: Multitrack recording"), wxDefaultPosition, wxSize(-1, -1)),
77 inst(_inst)
79 CHECK_UI_THREAD;
80 typeset = NULL;
81 closing = false;
82 Centre();
83 vsizer = new wxFlexGridSizer(0, 2, 0, 0);
84 SetSizer(vsizer);
85 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxeditor_multitrack::on_wclose));
86 update_controls();
87 vsizer->SetSizeHints(this);
88 Fit();
90 ahreconfigure.set(inst.dispatch->autohold_reconfigure, [this]() {
91 if(typeset && *typeset == CORE().controls->get_blank().porttypes())
92 return; //Don't reconfigure if no change.
93 CORE().mteditor->config_altered();
94 runuifun([this]() {
95 try {
96 this->update_controls();
97 } catch(std::runtime_error& e) {
98 //Close the window.
99 bool wasc = closing;
100 closing = true;
101 multitrack_open = NULL;
102 inst.iqueue->run([]() { CORE().mteditor->enable(false); });
103 if(!wasc)
104 Destroy();
108 ahmodechange.set(inst.dispatch->mode_change, [this](bool readonly) {
109 runuifun([this, readonly]() {
110 for(auto i : controllers)
111 i.mode->Enable(readonly);
114 ahmtchange.set(inst.dispatch->multitrack_change, [this](unsigned port, unsigned controller,
115 int state) {
116 runuifun([this, port, controller, state]() {
117 for(auto i : controllers) {
118 if(i.port == port && i.controller == controller) {
119 auto cb = i.mode;
120 if(state == multitrack_edit::MT_OR)
121 cb->SetStringSelection(towxstring(MTMODE_OR));
122 if(state == multitrack_edit::MT_OVERWRITE)
123 cb->SetStringSelection(towxstring(MTMODE_OVERWRITE));
124 if(state == multitrack_edit::MT_PRESERVE)
125 cb->SetStringSelection(towxstring(MTMODE_PRESERVE));
126 if(state == multitrack_edit::MT_XOR)
127 cb->SetStringSelection(towxstring(MTMODE_XOR));
134 void wxeditor_multitrack::on_control(wxCommandEvent& e)
136 CHECK_UI_THREAD;
137 int id = e.GetId();
138 if(id < wxID_HIGHEST + 1)
139 return;
140 size_t ctrl = id - wxID_HIGHEST - 1;
141 if(ctrl >= controllers.size())
142 return;
143 controller_info& ci = controllers[ctrl];
144 std::string mode = tostdstring(ci.mode->GetStringSelection());
145 inst.iqueue->run([ci, mode]() {
146 if(mode == MTMODE_PRESERVE)
147 CORE().mteditor->set(ci.port, ci.controller, multitrack_edit::MT_PRESERVE);
148 else if(mode == MTMODE_OVERWRITE)
149 CORE().mteditor->set(ci.port, ci.controller, multitrack_edit::MT_OVERWRITE);
150 else if(mode == MTMODE_OR)
151 CORE().mteditor->set(ci.port, ci.controller, multitrack_edit::MT_OR);
152 else if(mode == MTMODE_XOR)
153 CORE().mteditor->set(ci.port, ci.controller, multitrack_edit::MT_XOR);
157 void wxeditor_multitrack::update_controls()
159 CHECK_UI_THREAD;
160 bool readonly = inst.mlogic->get_movie().readonly_mode();
162 for(auto i : controllers) {
163 vsizer->Detach(i.text);
164 vsizer->Detach(i.mode);
165 i.text->Destroy();
166 i.mode->Destroy();
168 controllers.clear();
169 std::vector<controller_info2> info;
170 inst.iqueue->run([this, &info](){
171 std::map<std::string, unsigned> next_in_class;
172 portctrl::frame model = CORE().controls->get_blank();
173 const portctrl::type_set& pts = model.porttypes();
174 typeset = &pts;
175 unsigned cnum_g = 0;
176 for(unsigned i = 0;; i++) {
177 auto pcid = CORE().controls->lcid_to_pcid(i);
178 if(pcid.first < 0)
179 break;
180 const portctrl::type& pt = pts.port_type(pcid.first);
181 const portctrl::controller_set& pci = *(pt.controller_info);
182 if((ssize_t)pci.controllers.size() <= pcid.second)
183 continue;
184 const portctrl::controller& pc = pci.controllers[pcid.second];
185 //First check that this has non-hidden stuff.
186 bool has_buttons = false;
187 for(unsigned k = 0; k < pc.buttons.size(); k++) {
188 const portctrl::button& pcb = pc.buttons[k];
189 if(!pcb.shadow)
190 has_buttons = true;
192 if(!has_buttons)
193 continue;
194 //Okay, a valid controller.
195 if(!next_in_class.count(pc.cclass))
196 next_in_class[pc.cclass] = 1;
197 uint32_t cnum = next_in_class[pc.cclass]++;
198 controller_info2 _info;
199 _info.name = (stringfmt() << pc.cclass << "-" << cnum).str();
200 _info.port = pcid.first;
201 _info.controller = pcid.second;
202 info.push_back(_info);
203 cnum_g++;
206 if(info.empty())
207 throw std::runtime_error("No controllers");
208 unsigned index = 0;
209 for(auto i : info) {
210 struct controller_info _info;
211 _info.port = i.port;
212 _info.controller = i.controller;
213 _info.text = new wxStaticText(this, wxID_ANY, towxstring(i.name));
214 vsizer->Add(_info.text, 0, wxGROW);
215 std::vector<wxString> choices;
216 choices.push_back(towxstring(MTMODE_PRESERVE));
217 choices.push_back(towxstring(MTMODE_OVERWRITE));
218 choices.push_back(towxstring(MTMODE_OR));
219 choices.push_back(towxstring(MTMODE_XOR));
220 _info.mode = new wxComboBox(this, wxID_HIGHEST + 1 + index, choices[0], wxDefaultPosition,
221 wxDefaultSize, choices.size(), &choices[0], wxCB_READONLY);
222 _info.mode->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
223 wxCommandEventHandler(wxeditor_multitrack::on_control), NULL, this);
224 if(!readonly)
225 _info.mode->Enable(false);
226 vsizer->Add(_info.mode, 0, wxGROW);
227 controllers.push_back(_info);
228 index++;
230 vsizer->Layout();
231 Fit();
234 bool wxeditor_multitrack::ShouldPreventAppExit() const { return false; }
236 void wxeditor_multitrack::on_wclose(wxCloseEvent& e)
238 CHECK_UI_THREAD;
239 bool wasc = closing;
240 closing = true;
241 multitrack_open = NULL;
242 inst.iqueue->run([]() { CORE().mteditor->enable(false); });
243 if(!wasc)
244 Destroy();
247 void wxeditor_multitrack_display(wxWindow* parent, emulator_instance& inst)
249 CHECK_UI_THREAD;
250 if(multitrack_open)
251 return;
252 wxeditor_multitrack* v;
253 try {
254 v = new wxeditor_multitrack(parent, inst);
255 } catch(std::runtime_error& e) {
256 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION | wxOK, parent);
257 return;
259 v->Show();
260 multitrack_open = v;
261 inst.iqueue->run([]() { CORE().mteditor->enable(true); });