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();
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
);
21 fill_namemap(p
, i
, namemap
, childmap
);
26 class branches_tree
: public wxTreeCtrl
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(); }); });
45 selection
get_selection()
48 s
.item
= GetSelection();
50 if(s
.item
.IsOk() && i
.second
== s
.item
) {
51 s
.isroot
= (i
.first
== 0);
52 s
.haschildren
= with_children
.count(i
.first
);
54 s
.iscurrent
= (i
.first
== current
);
59 s
.haschildren
= false;
61 s
.id
= 0xFFFFFFFFFFFFFFFFULL
;
66 std::map
<uint64_t, std::string
> namemap
;
67 std::map
<uint64_t, std::set
<uint64_t>> childmap
;
69 lsnes_instance
.iqueue
.run([&cur
, &namemap
, &childmap
]() {
70 auto p
= lsnes_instance
.project
.get();
72 fill_namemap(*p
, 0, namemap
, childmap
);
73 cur
= p
->get_current_branch();
76 selection cursel
= get_selection();
77 std::set
<uint64_t> expanded
;
79 if(IsExpanded(i
.second
))
80 expanded
.insert(i
.first
);
83 with_children
.clear();
84 if(namemap
.empty()) return;
87 ids
[0] = AddRoot(towxstring(namemap
[0] + ((!nosels
&& current
== 0) ? " <selected>" : "")));
88 build_tree(0, ids
[0], childmap
, namemap
);
89 for(auto i
: expanded
)
93 if(i
.first
== cursel
.id
) {
98 std::string
get_name(uint64_t id
)
104 void call_project_flush()
106 lsnes_instance
.iqueue
.run_async([] {
107 auto p
= CORE().project
.get();
109 }, [](std::exception
& e
) {});
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())
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
);
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
;
131 class branch_select
: public wxDialog
134 branch_select(wxWindow
* parent
)
135 : wxDialog(parent
, wxID_ANY
, towxstring("lsnes: Select new parent branch"))
138 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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);
162 void on_ok(wxCommandEvent
& e
)
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
);
180 wxButton
* cancelbutton
;
181 branches_tree
* branches
;
184 class branch_config
: public wxDialog
187 branch_config(wxWindow
* parent
)
188 : wxDialog(parent
, wxID_ANY
, towxstring("lsnes: Edit slot branches"))
191 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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);
226 void on_create(wxCommandEvent
& e
)
228 uint64_t id
= get_selected_id();
229 if(id
== 0xFFFFFFFFFFFFFFFFULL
) return;
232 newname
= pick_text(this, "Enter new branch name", "Enter name for new branch:",
234 } catch(canceled_exception
& e
) {
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
);
264 newname
= pick_text(this, "Enter new branch name", "Rename this branch to:",
266 } catch(canceled_exception
& e
) {
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;
283 branch_select
* bsel
= new branch_select(this);
284 int r
= bsel
->ShowModal();
289 pid
= bsel
->get_selection();
290 if(pid
== 0xFFFFFFFFFFFFFFFFULL
) return;
292 lsnes_instance
.iqueue
.run([this, id
, pid
]() {
293 run_show_error(this, "Error reparenting branch", "Can't reparent branch",
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
)
318 void on_change(wxCommandEvent
& e
)
320 set_enabled(branches
->get_selection());
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
;
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
,
349 auto& children
= childmap
[id
];
351 otheritems
.push_back(branches_menu::miteminfo(root
->AppendCheckItem(mid
, towxstring(namemap
[id
])),
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();
360 //No children, just put the item there.
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
);
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
));
372 build_menus(m
, i
, otheritems
, menus
, namemap
, childmap
, branch_ids
, nextid
,
379 branches_menu::branches_menu(wxWindow
* win
, int wxid_low
, int wxid_high
)
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
)
396 if(id
< wxid_range_low
|| id
> wxid_range_high
) return;
397 if(id
== wxid_range_low
) {
399 branch_config
* bcfg
= new branch_config(pwin
);
404 if(!branch_ids
.count(id
)) return;
405 uint64_t bid
= branch_ids
[id
];
407 lsnes_instance
.iqueue
.run_async([this, bid
]() {
408 auto p
= CORE().project
.get();
409 if(p
) p
->set_current_branch(bid
);
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();
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.
435 if(namemap
.empty()) {
436 if(disabler_fn
) disabler_fn(false);
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();