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 runemufn([&cur
, &namemap
, &childmap
]() {
70 auto p
= 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()
107 auto p
= project_get();
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 runemufn([this, id
, newname
]() {
239 auto p
= project_get();
240 if(p
) p
->create_branch(id
, newname
);
241 } catch(std::exception
& e
) {
242 std::string err
= e
.what();
243 runuifun([this, err
]() {
244 show_message_ok(this, "Error creating branch",
245 "Can't create branch: " + err
, wxICON_EXCLAMATION
);
249 branches
->call_project_flush();
251 void on_select(wxCommandEvent
& e
)
253 uint64_t id
= get_selected_id();
254 if(id
== 0xFFFFFFFFFFFFFFFFULL
) return;
255 runemufn([this, id
]() {
257 auto p
= project_get();
258 if(p
) p
->set_current_branch(id
);
259 } catch(std::exception
& e
) {
260 std::string err
= e
.what();
261 runuifun([this, err
]() {
262 show_message_ok(this, "Error setting branch",
263 "Can't set branch: " + err
, wxICON_EXCLAMATION
);
267 branches
->call_project_flush();
268 update_movie_state();
270 void on_rename(wxCommandEvent
& e
)
272 uint64_t id
= get_selected_id();
273 if(id
== 0xFFFFFFFFFFFFFFFFULL
) return;
274 std::string newname
= branches
->get_name(id
);
276 newname
= pick_text(this, "Enter new branch name", "Rename this branch to:",
278 } catch(canceled_exception
& e
) {
281 runemufn([this, id
, newname
]() {
283 auto p
= project_get();
284 if(p
) p
->set_branch_name(id
, newname
);
285 } catch(std::exception
& e
) {
286 std::string err
= e
.what();
287 runuifun([this, err
]() {
288 show_message_ok(this, "Error renaming branch",
289 "Can't rename branch: " + err
, wxICON_EXCLAMATION
);
293 branches
->call_project_flush();
294 update_movie_state();
296 void on_reparent(wxCommandEvent
& e
)
298 uint64_t id
= get_selected_id();
299 if(id
== 0xFFFFFFFFFFFFFFFFULL
) return;
301 branch_select
* bsel
= new branch_select(this);
302 int r
= bsel
->ShowModal();
307 pid
= bsel
->get_selection();
308 if(pid
== 0xFFFFFFFFFFFFFFFFULL
) return;
310 runemufn([this, id
, pid
]() {
312 auto p
= project_get();
313 if(p
) p
->set_parent_branch(id
, pid
);
314 } catch(std::exception
& e
) {
315 std::string err
= e
.what();
316 runuifun([this, err
]() {
317 show_message_ok(this, "Error reparenting branch",
318 "Can't reparent branch: " + err
, wxICON_EXCLAMATION
);
322 branches
->call_project_flush();
323 update_movie_state();
325 void on_delete(wxCommandEvent
& e
)
327 uint64_t id
= get_selected_id();
328 if(id
== 0xFFFFFFFFFFFFFFFFULL
) return;
329 runemufn([this, id
]() {
331 auto p
= project_get();
332 if(p
) p
->delete_branch(id
);
333 } catch(std::exception
& e
) {
334 std::string err
= e
.what();
335 runuifun([this, err
]() {
336 show_message_ok(this, "Error deleting branch",
337 "Can't delete branch: " + err
, wxICON_EXCLAMATION
);
341 branches
->call_project_flush();
343 void on_close(wxCommandEvent
& e
)
347 void on_change(wxCommandEvent
& e
)
349 set_enabled(branches
->get_selection());
352 uint64_t get_selected_id()
354 return branches
->get_selection().id
;
356 void set_enabled(branches_tree::selection id
)
358 createbutton
->Enable(id
.item
.IsOk());
359 selectbutton
->Enable(id
.item
.IsOk());
360 renamebutton
->Enable(id
.item
.IsOk() && !id
.isroot
);
361 reparentbutton
->Enable(id
.item
.IsOk() && !id
.isroot
);
362 deletebutton
->Enable(id
.item
.IsOk() && !id
.isroot
&& !id
.haschildren
&& !id
.iscurrent
);
364 wxButton
* createbutton
;
365 wxButton
* selectbutton
;
366 wxButton
* renamebutton
;
367 wxButton
* reparentbutton
;
368 wxButton
* deletebutton
;
370 branches_tree
* branches
;
373 void build_menus(wxMenu
* root
, uint64_t id
, std::list
<branches_menu::miteminfo
>& otheritems
,
374 std::list
<wxMenu
*>& menus
, std::map
<uint64_t, std::string
>& namemap
,
375 std::map
<uint64_t, std::set
<uint64_t>>& childmap
, std::map
<int, uint64_t>& branch_ids
, int& nextid
,
378 auto& children
= childmap
[id
];
380 otheritems
.push_back(branches_menu::miteminfo(root
->AppendCheckItem(mid
, towxstring(namemap
[id
])),
382 branch_ids
[mid
] = id
;
383 root
->FindItem(mid
)->Check(id
== curbranch
);
384 if(!children
.empty())
385 otheritems
.push_back(branches_menu::miteminfo(root
->AppendSeparator(), false, root
));
386 for(auto i
: children
) {
387 bool has_children
= !childmap
[i
].empty();
389 //No children, just put the item there.
391 otheritems
.push_back(branches_menu::miteminfo(root
->AppendCheckItem(mid2
,
392 towxstring(namemap
[i
])), false, root
));
393 branch_ids
[mid2
] = i
;
394 root
->FindItem(mid2
)->Check(i
== curbranch
);
396 //Has children. Make a menu.
397 wxMenu
* m
= new wxMenu();
398 otheritems
.push_back(branches_menu::miteminfo(root
->AppendSubMenu(m
,
399 towxstring(namemap
[i
])), true, root
));
401 build_menus(m
, i
, otheritems
, menus
, namemap
, childmap
, branch_ids
, nextid
,
408 branches_menu::branches_menu(wxWindow
* win
, int wxid_low
, int wxid_high
)
411 wxid_range_low
= wxid_low
;
412 wxid_range_high
= wxid_high
;
413 win
->Connect(wxid_low
, wxid_high
, wxEVT_COMMAND_MENU_SELECTED
,
414 wxCommandEventHandler(branches_menu::on_select
), NULL
, this);
415 branchchange
.set(notify_branch_change
, [this]() { runuifun([this]() { this->update(); }); });
418 branches_menu::~branches_menu()
422 void branches_menu::on_select(wxCommandEvent
& e
)
425 if(id
< wxid_range_low
|| id
> wxid_range_high
) return;
426 if(id
== wxid_range_low
) {
428 branch_config
* bcfg
= new branch_config(pwin
);
433 if(!branch_ids
.count(id
)) return;
434 uint64_t bid
= branch_ids
[id
];
436 runemufn_async([this, bid
]() {
437 auto p
= project_get();
439 if(p
) p
->set_current_branch(bid
);
440 } catch(std::exception
& e
) {
441 std::string err
= e
.what();
442 runuifun([this, err
]() {
443 show_message_ok(this->pwin
, "Error changing branch", "Can't change branch: " +
444 err
, wxICON_EXCLAMATION
);
448 update_movie_state();
452 void branches_menu::update()
454 std::map
<uint64_t, std::string
> namemap
;
455 std::map
<uint64_t, std::set
<uint64_t>> childmap
;
456 runemufn([&namemap
, &childmap
]() {
457 auto p
= project_get();
459 fill_namemap(*p
, 0, namemap
, childmap
);
461 //First destroy everything that isn't a menu.
462 for(auto i
: otheritems
)
463 i
.parent
->Delete(i
.item
);
464 //Then kill all menus.
470 if(namemap
.empty()) {
471 if(disabler_fn
) disabler_fn(false);
474 //Okay, cleared. Rebuild things.
475 otheritems
.push_back(miteminfo(Append(wxid_range_low
, towxstring("Edit branches")), false, this));
476 otheritems
.push_back(miteminfo(AppendSeparator(), false, this));
477 int ass_id
= wxid_range_low
+ 1;
478 build_menus(this, 0, otheritems
, menus
, namemap
, childmap
, branch_ids
, ass_id
,
479 project_get()->get_current_branch());
480 if(disabler_fn
) disabler_fn(true);
483 bool branches_menu::any_enabled()
485 return project_get();