Make various instance stuff to take references to other instance objs
[lsnes.git] / src / platform / wxwidgets / branchesmenu.cpp
bloba8d4a1a39f0671fefdf7ae167fcb5f2f9a0398e6
1 #include "platform/wxwidgets/settings-common.hpp"
2 #include "platform/wxwidgets/settings-keyentry.hpp"
3 #include "platform/wxwidgets/menu_branches.hpp"
4 #include "platform/wxwidgets/platform.hpp"
5 #include "platform/wxwidgets/loadsave.hpp"
6 #include "core/debug.hpp"
7 #include "core/dispatch.hpp"
8 #include "core/project.hpp"
9 #include "core/moviedata.hpp"
11 void update_movie_state();
13 namespace
15 void fill_namemap(project_info& p, uint64_t id, std::map<uint64_t, std::string>& namemap,
16 std::map<uint64_t, std::set<uint64_t>>& childmap)
18 namemap[id] = p.get_branch_name(id);
19 auto s = p.branch_children(id);
20 for(auto i : s)
21 fill_namemap(p, i, namemap, childmap);
22 childmap[id] = s;
25 //Tree of branches.
26 class branches_tree : public wxTreeCtrl
28 public:
29 branches_tree(wxWindow* parent, int id, bool _nosels)
30 : wxTreeCtrl(parent, id), nosels(_nosels)
32 SetMinSize(wxSize(400, 300));
33 branchchange.set(notify_branch_change, [this]() { runuifun([this]() { this->update(); }); });
34 update();
36 struct selection
38 wxTreeItemId item;
39 bool isroot;
40 bool haschildren;
41 bool iscurrent;
42 uint64_t id;
43 std::string name;
45 selection get_selection()
47 selection s;
48 s.item = GetSelection();
49 for(auto i : ids) {
50 if(s.item.IsOk() && i.second == s.item) {
51 s.isroot = (i.first == 0);
52 s.haschildren = with_children.count(i.first);
53 s.id = i.first;
54 s.iscurrent = (i.first == current);
55 return s;
58 s.isroot = false;
59 s.haschildren = false;
60 s.iscurrent = false;
61 s.id = 0xFFFFFFFFFFFFFFFFULL;
62 return s;
64 void update()
66 std::map<uint64_t, std::string> namemap;
67 std::map<uint64_t, std::set<uint64_t>> childmap;
68 uint64_t cur = 0;
69 lsnes_instance.iqueue.run([&cur, &namemap, &childmap]() {
70 auto p = lsnes_instance.project.get();
71 if(!p) return;
72 fill_namemap(*p, 0, namemap, childmap);
73 cur = p->get_current_branch();
74 });
75 current = cur;
76 selection cursel = get_selection();
77 std::set<uint64_t> expanded;
78 for(auto i : ids)
79 if(IsExpanded(i.second))
80 expanded.insert(i.first);
81 DeleteAllItems();
82 ids.clear();
83 with_children.clear();
84 if(namemap.empty()) return;
85 //Create ROOT.
86 names = namemap;
87 ids[0] = AddRoot(towxstring(namemap[0] + ((!nosels && current == 0) ? " <selected>" : "")));
88 build_tree(0, ids[0], childmap, namemap);
89 for(auto i : expanded)
90 if(ids.count(i))
91 Expand(ids[i]);
92 for(auto i : ids) {
93 if(i.first == cursel.id) {
94 SelectItem(i.second);
98 std::string get_name(uint64_t id)
100 if(names.count(id))
101 return names[id];
102 return "";
104 void call_project_flush()
106 lsnes_instance.iqueue.run_async([] {
107 auto p = CORE().project.get();
108 if(p) p->flush();
109 }, [](std::exception& e) {});
111 private:
112 void build_tree(uint64_t id, wxTreeItemId parent, std::map<uint64_t, std::set<uint64_t>>& childmap,
113 std::map<uint64_t, std::string>& namemap)
115 if(!childmap.count(id) || childmap[id].empty())
116 return;
117 for(auto i : childmap[id]) {
118 ids[i] = AppendItem(ids[id], towxstring(namemap[i] + ((!nosels && current == i) ?
119 " <selected>" : "")));
120 build_tree(i, ids[i], childmap, namemap);
123 uint64_t current;
124 std::map<uint64_t, std::string> names;
125 std::map<uint64_t, wxTreeItemId> ids;
126 std::set<uint64_t> with_children;
127 struct dispatch::target<> branchchange;
128 bool nosels;
131 class branch_select : public wxDialog
133 public:
134 branch_select(wxWindow* parent)
135 : wxDialog(parent, wxID_ANY, towxstring("lsnes: Select new parent branch"))
137 Centre();
138 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
139 SetSizer(top_s);
140 Center();
142 top_s->Add(branches = new branches_tree(this, wxID_ANY, true), 1, wxGROW);
143 branches->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED,
144 wxCommandEventHandler(branch_select::on_change), NULL, this);
146 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
147 pbutton_s->AddStretchSpacer();
148 pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
149 pbutton_s->Add(cancelbutton = new wxButton(this, wxID_OK, wxT("Cancel")), 0, wxGROW);
150 okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
151 wxCommandEventHandler(branch_select::on_ok), NULL, this);
152 cancelbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
153 wxCommandEventHandler(branch_select::on_cancel), NULL, this);
154 top_s->Add(pbutton_s, 0, wxGROW);
156 top_s->SetSizeHints(this);
157 Fit();
159 wxCommandEvent e;
160 on_change(e);
162 void on_ok(wxCommandEvent& e)
164 EndModal(wxID_OK);
166 void on_cancel(wxCommandEvent& e)
168 EndModal(wxID_CANCEL);
170 uint64_t get_selection()
172 return branches->get_selection().id;
174 void on_change(wxCommandEvent& e)
176 okbutton->Enable(get_selection() != 0xFFFFFFFFFFFFFFFFULL);
178 private:
179 wxButton* okbutton;
180 wxButton* cancelbutton;
181 branches_tree* branches;
184 class branch_config : public wxDialog
186 public:
187 branch_config(wxWindow* parent)
188 : wxDialog(parent, wxID_ANY, towxstring("lsnes: Edit slot branches"))
190 Centre();
191 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
192 SetSizer(top_s);
193 Center();
195 top_s->Add(branches = new branches_tree(this, wxID_ANY, false), 1, wxGROW);
196 branches->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED,
197 wxCommandEventHandler(branch_config::on_change), NULL, this);
199 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
200 pbutton_s->Add(createbutton = new wxButton(this, wxID_OK, wxT("Create")), 0, wxGROW);
201 pbutton_s->Add(selectbutton = new wxButton(this, wxID_OK, wxT("Select")), 0, wxGROW);
202 pbutton_s->Add(renamebutton = new wxButton(this, wxID_OK, wxT("Rename")), 0, wxGROW);
203 pbutton_s->Add(reparentbutton = new wxButton(this, wxID_OK, wxT("Reparent")), 0, wxGROW);
204 pbutton_s->Add(deletebutton = new wxButton(this, wxID_OK, wxT("Delete")), 0, wxGROW);
205 pbutton_s->AddStretchSpacer();
206 pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("Close")), 0, wxGROW);
207 createbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
208 wxCommandEventHandler(branch_config::on_create), NULL, this);
209 selectbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
210 wxCommandEventHandler(branch_config::on_select), NULL, this);
211 renamebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
212 wxCommandEventHandler(branch_config::on_rename), NULL, this);
213 reparentbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
214 wxCommandEventHandler(branch_config::on_reparent), NULL, this);
215 deletebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
216 wxCommandEventHandler(branch_config::on_delete), NULL, this);
217 okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
218 wxCommandEventHandler(branch_config::on_close), NULL, this);
219 top_s->Add(pbutton_s, 0, wxGROW);
221 top_s->SetSizeHints(this);
222 Fit();
223 wxCommandEvent e;
224 on_change(e);
226 void on_create(wxCommandEvent& e)
228 uint64_t id = get_selected_id();
229 if(id == 0xFFFFFFFFFFFFFFFFULL) return;
230 std::string newname;
231 try {
232 newname = pick_text(this, "Enter new branch name", "Enter name for new branch:",
233 newname, false);
234 } catch(canceled_exception& e) {
235 return;
237 lsnes_instance.iqueue.run([this, id, newname]() {
238 run_show_error(this, "Error creating branch", "Can't create branch", [id, newname]() {
239 auto p = CORE().project.get();
240 if(p) p->create_branch(id, newname);
243 branches->call_project_flush();
245 void on_select(wxCommandEvent& e)
247 uint64_t id = get_selected_id();
248 if(id == 0xFFFFFFFFFFFFFFFFULL) return;
249 lsnes_instance.iqueue.run([this, id]() {
250 run_show_error(this, "Error setting branch", "Can't set branch", [id]() {
251 auto p = CORE().project.get();
252 if(p) p->set_current_branch(id);
255 branches->call_project_flush();
256 update_movie_state();
258 void on_rename(wxCommandEvent& e)
260 uint64_t id = get_selected_id();
261 if(id == 0xFFFFFFFFFFFFFFFFULL) return;
262 std::string newname = branches->get_name(id);
263 try {
264 newname = pick_text(this, "Enter new branch name", "Rename this branch to:",
265 newname, false);
266 } catch(canceled_exception& e) {
267 return;
269 lsnes_instance.iqueue.run([this, id, newname]() {
270 run_show_error(this, "Error renaming branch", "Can't rename branch", [id, newname]() {
271 auto p = CORE().project.get();
272 if(p) p->set_branch_name(id, newname);
275 branches->call_project_flush();
276 update_movie_state();
278 void on_reparent(wxCommandEvent& e)
280 uint64_t id = get_selected_id();
281 if(id == 0xFFFFFFFFFFFFFFFFULL) return;
282 uint64_t pid;
283 branch_select* bsel = new branch_select(this);
284 int r = bsel->ShowModal();
285 if(r != wxID_OK) {
286 bsel->Destroy();
287 return;
289 pid = bsel->get_selection();
290 if(pid == 0xFFFFFFFFFFFFFFFFULL) return;
291 bsel->Destroy();
292 lsnes_instance.iqueue.run([this, id, pid]() {
293 run_show_error(this, "Error reparenting branch", "Can't reparent branch",
294 [id, pid]() {
295 auto p = CORE().project.get();
296 if(p) p->set_parent_branch(id, pid);
299 branches->call_project_flush();
300 update_movie_state();
302 void on_delete(wxCommandEvent& e)
304 uint64_t id = get_selected_id();
305 if(id == 0xFFFFFFFFFFFFFFFFULL) return;
306 lsnes_instance.iqueue.run([this, id]() {
307 run_show_error(this, "Error deleting branch", "Can't delete branch", [id]() {
308 auto p = CORE().project.get();
309 if(p) p->delete_branch(id);
312 branches->call_project_flush();
314 void on_close(wxCommandEvent& e)
316 EndModal(wxID_OK);
318 void on_change(wxCommandEvent& e)
320 set_enabled(branches->get_selection());
322 private:
323 uint64_t get_selected_id()
325 return branches->get_selection().id;
327 void set_enabled(branches_tree::selection id)
329 createbutton->Enable(id.item.IsOk());
330 selectbutton->Enable(id.item.IsOk());
331 renamebutton->Enable(id.item.IsOk() && !id.isroot);
332 reparentbutton->Enable(id.item.IsOk() && !id.isroot);
333 deletebutton->Enable(id.item.IsOk() && !id.isroot && !id.haschildren && !id.iscurrent);
335 wxButton* createbutton;
336 wxButton* selectbutton;
337 wxButton* renamebutton;
338 wxButton* reparentbutton;
339 wxButton* deletebutton;
340 wxButton* okbutton;
341 branches_tree* branches;
344 void build_menus(wxMenu* root, uint64_t id, std::list<branches_menu::miteminfo>& otheritems,
345 std::list<wxMenu*>& menus, std::map<uint64_t, std::string>& namemap,
346 std::map<uint64_t, std::set<uint64_t>>& childmap, std::map<int, uint64_t>& branch_ids, int& nextid,
347 uint64_t curbranch)
349 auto& children = childmap[id];
350 int mid = nextid++;
351 otheritems.push_back(branches_menu::miteminfo(root->AppendCheckItem(mid, towxstring(namemap[id])),
352 false, root));
353 branch_ids[mid] = id;
354 root->FindItem(mid)->Check(id == curbranch);
355 if(!children.empty())
356 otheritems.push_back(branches_menu::miteminfo(root->AppendSeparator(), false, root));
357 for(auto i : children) {
358 bool has_children = !childmap[i].empty();
359 if(!has_children) {
360 //No children, just put the item there.
361 int mid2 = nextid++;
362 otheritems.push_back(branches_menu::miteminfo(root->AppendCheckItem(mid2,
363 towxstring(namemap[i])), false, root));
364 branch_ids[mid2] = i;
365 root->FindItem(mid2)->Check(i == curbranch);
366 } else {
367 //Has children. Make a menu.
368 wxMenu* m = new wxMenu();
369 otheritems.push_back(branches_menu::miteminfo(root->AppendSubMenu(m,
370 towxstring(namemap[i])), true, root));
371 menus.push_back(m);
372 build_menus(m, i, otheritems, menus, namemap, childmap, branch_ids, nextid,
373 curbranch);
379 branches_menu::branches_menu(wxWindow* win, int wxid_low, int wxid_high)
381 pwin = win;
382 wxid_range_low = wxid_low;
383 wxid_range_high = wxid_high;
384 win->Connect(wxid_low, wxid_high, wxEVT_COMMAND_MENU_SELECTED,
385 wxCommandEventHandler(branches_menu::on_select), NULL, this);
386 branchchange.set(notify_branch_change, [this]() { runuifun([this]() { this->update(); }); });
389 branches_menu::~branches_menu()
393 void branches_menu::on_select(wxCommandEvent& e)
395 int id = e.GetId();
396 if(id < wxid_range_low || id > wxid_range_high) return;
397 if(id == wxid_range_low) {
398 //Configure.
399 branch_config* bcfg = new branch_config(pwin);
400 bcfg->ShowModal();
401 bcfg->Destroy();
402 return;
404 if(!branch_ids.count(id)) return;
405 uint64_t bid = branch_ids[id];
406 std::string err;
407 lsnes_instance.iqueue.run_async([this, bid]() {
408 auto p = CORE().project.get();
409 if(p) p->set_current_branch(bid);
410 if(p) p->flush();
411 update_movie_state();
412 }, [this](std::exception& e) {
413 show_exception(this->pwin, "Error changing branch", "Can't change branch", e);
417 void branches_menu::update()
419 std::map<uint64_t, std::string> namemap;
420 std::map<uint64_t, std::set<uint64_t>> childmap;
421 lsnes_instance.iqueue.run([&namemap, &childmap]() {
422 auto p = CORE().project.get();
423 if(!p) return;
424 fill_namemap(*p, 0, namemap, childmap);
426 //First destroy everything that isn't a menu.
427 for(auto i : otheritems)
428 i.parent->Delete(i.item);
429 //Then kill all menus.
430 for(auto i : menus)
431 delete i;
432 otheritems.clear();
433 menus.clear();
434 branch_ids.clear();
435 if(namemap.empty()) {
436 if(disabler_fn) disabler_fn(false);
437 return;
439 //Okay, cleared. Rebuild things.
440 otheritems.push_back(miteminfo(Append(wxid_range_low, towxstring("Edit branches")), false, this));
441 otheritems.push_back(miteminfo(AppendSeparator(), false, this));
442 int ass_id = wxid_range_low + 1;
443 build_menus(this, 0, otheritems, menus, namemap, childmap, branch_ids, ass_id,
444 lsnes_instance.project.get()->get_current_branch());
445 if(disabler_fn) disabler_fn(true);
448 bool branches_menu::any_enabled()
450 return lsnes_instance.project.get();