Slot branches
This commit is contained in:
parent
4bb75bc30e
commit
91c765a0a7
19 changed files with 1170 additions and 163 deletions
|
@ -266,6 +266,7 @@ extern struct dispatch::source<bool> notify_sound_unmute;
|
|||
extern struct dispatch::source<bool> notify_mode_change;
|
||||
extern struct dispatch::source<> notify_core_change;
|
||||
extern struct dispatch::source<> notify_title_change;
|
||||
extern struct dispatch::source<> notify_branch_change;
|
||||
extern struct dispatch::source<bool> notify_core_changed;
|
||||
extern struct dispatch::source<> notify_new_core;
|
||||
extern struct dispatch::source<> notify_voice_stream_change;
|
||||
|
|
|
@ -31,7 +31,7 @@ void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc
|
|||
void do_load_beginning(bool reloading = false) throw(std::bad_alloc, std::runtime_error);
|
||||
void do_load_state(struct moviefile& _movie, int lmode);
|
||||
bool do_load_state(const std::string& filename, int lmode);
|
||||
std::string translate_name_mprefix(std::string original, int& binary, bool save);
|
||||
std::string translate_name_mprefix(std::string original, int& binary, int save);
|
||||
|
||||
extern std::string last_save;
|
||||
extern movie_logic movb;
|
||||
|
|
|
@ -9,6 +9,15 @@
|
|||
#include "core/controller.hpp"
|
||||
#include "core/rom.hpp"
|
||||
|
||||
//A branch.
|
||||
struct project_branch_info
|
||||
{
|
||||
//Parent branch ID.
|
||||
uint64_t pbid;
|
||||
//Name.
|
||||
std::string name;
|
||||
};
|
||||
|
||||
//Information about project.
|
||||
struct project_info
|
||||
{
|
||||
|
@ -32,6 +41,10 @@ struct project_info
|
|||
std::map<std::string, std::string> watches;
|
||||
//Macros.
|
||||
std::map<std::string, JSON::node> macros;
|
||||
//Branches.
|
||||
std::map<uint64_t, project_branch_info> branches;
|
||||
uint64_t active_branch;
|
||||
uint64_t next_branch;
|
||||
//Stub movie data.
|
||||
std::string gametype;
|
||||
std::map<std::string, std::string> settings;
|
||||
|
@ -46,6 +59,89 @@ struct project_info
|
|||
std::vector<char> anchor_savestate;
|
||||
int64_t movie_rtc_second;
|
||||
int64_t movie_rtc_subsecond;
|
||||
/**
|
||||
* Obtain parent of branch.
|
||||
*
|
||||
* Parameter bid: The branch ID.
|
||||
* Returns: The parent branch ID.
|
||||
* Throws std::runtime_error: Invalid branch ID.
|
||||
*
|
||||
* Note: bid 0 is root. Calling this on root always gives 0.
|
||||
*/
|
||||
uint64_t get_parent_branch(uint64_t bid);
|
||||
/**
|
||||
* Get current branch id.
|
||||
*
|
||||
* Returns: The branch id.
|
||||
*/
|
||||
uint64_t get_current_branch() { return active_branch; }
|
||||
/**
|
||||
* Set current branch.
|
||||
*
|
||||
* Parameter bid: The branch id.
|
||||
* Throws std::runtime_error: Invalid branch ID.
|
||||
*/
|
||||
void set_current_branch(uint64_t bid);
|
||||
/**
|
||||
* Get name of branch.
|
||||
*
|
||||
* Parameter bid: The branch id.
|
||||
* Throws std::runtime_error: Invalid branch ID.
|
||||
* Note: The name of ROOT branch is always empty string.
|
||||
*/
|
||||
const std::string& get_branch_name(uint64_t bid);
|
||||
/**
|
||||
* Set name of branch.
|
||||
*
|
||||
* Parameter bid: The branch id.
|
||||
* Parameter name: The new name
|
||||
* Throws std::runtime_error: Invalid branch ID.
|
||||
* Note: The name of ROOT branch can't be changed.
|
||||
*/
|
||||
void set_branch_name(uint64_t bid, const std::string& name);
|
||||
/**
|
||||
* Set parent branch of branch.
|
||||
*
|
||||
* Parameter bid: The branch id.
|
||||
* Parameter pbid: The new parent branch id.
|
||||
* Throws std::runtime_error: Invalid branch ID, or cyclic dependency.
|
||||
* Note: The parent of ROOT branch can't be set.
|
||||
*/
|
||||
void set_parent_branch(uint64_t bid, uint64_t pbid);
|
||||
/**
|
||||
* Enumerate child branches of specified branch.
|
||||
*
|
||||
* Parameter bid: The branch id.
|
||||
* Returns: The set of chilid branch IDs.
|
||||
* Throws std::runtime_error: Invalid branch ID.
|
||||
*/
|
||||
std::set<uint64_t> branch_children(uint64_t bid);
|
||||
/**
|
||||
* Create a new branch.
|
||||
*
|
||||
* Parameter pbid: Parent of the new branch.
|
||||
* Parameter name: Name of new branch.
|
||||
* Returns: Id of new branch.
|
||||
* Throws std::runtime_error: Invalid branch ID.
|
||||
*/
|
||||
uint64_t create_branch(uint64_t pbid, const std::string& name);
|
||||
/**
|
||||
* Delete a branch.
|
||||
*
|
||||
* Parameter bid: The branch id.
|
||||
* Throws std::runtime_error: Invalid branch ID or branch has children.
|
||||
*/
|
||||
void delete_branch(uint64_t bid);
|
||||
/**
|
||||
* Get name of current branch as string.
|
||||
*/
|
||||
std::string get_branch_string();
|
||||
/**
|
||||
* Flush the project to disk.
|
||||
*/
|
||||
void flush();
|
||||
private:
|
||||
void write(std::ostream& s);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -74,12 +170,6 @@ std::map<std::string, std::string> project_enumerate();
|
|||
* Returns: The project information.
|
||||
*/
|
||||
project_info& project_load(const std::string& id);
|
||||
/**
|
||||
* Flush any changes to project to disk.
|
||||
*
|
||||
* Parameter: The project to flush (NULL is no-op).
|
||||
*/
|
||||
void project_flush(project_info* p);
|
||||
/**
|
||||
* Get project movie path.
|
||||
*
|
||||
|
|
41
include/platform/wxwidgets/menu_branches.hpp
Normal file
41
include/platform/wxwidgets/menu_branches.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#ifndef _plat_wxwidgets__menu_branches__hpp__included__
|
||||
#define _plat_wxwidgets__menu_branches__hpp__included__
|
||||
|
||||
#include "core/dispatch.hpp"
|
||||
#include <wx/string.h>
|
||||
#include <wx/wx.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class branches_menu : public wxMenu
|
||||
{
|
||||
public:
|
||||
branches_menu(wxWindow* win, int wxid_low, int wxid_high);
|
||||
~branches_menu();
|
||||
void on_select(wxCommandEvent& e);
|
||||
void update();
|
||||
bool any_enabled();
|
||||
struct miteminfo
|
||||
{
|
||||
miteminfo(wxMenuItem* it, bool ismenu, wxMenu* p)
|
||||
: item(it), is_menu(ismenu), parent(p)
|
||||
{
|
||||
}
|
||||
wxMenuItem* item;
|
||||
bool is_menu;
|
||||
wxMenu* parent;
|
||||
};
|
||||
void set_disabler(std::function<void(bool enabled)> fn) { disabler_fn = fn; }
|
||||
private:
|
||||
struct dispatch::target<> branchchange;
|
||||
wxWindow* pwin;
|
||||
int wxid_range_low;
|
||||
int wxid_range_high;
|
||||
std::map<int, uint64_t> branch_ids;
|
||||
std::list<wxMenu*> menus;
|
||||
std::list<miteminfo> otheritems;
|
||||
std::function<void(bool enabled)> disabler_fn;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -16,12 +16,14 @@ public:
|
|||
void on_select(wxCommandEvent& e);
|
||||
void update();
|
||||
bool any_enabled();
|
||||
void set_disabler(std::function<void(bool enabled)> fn) { disabler_fn = fn; }
|
||||
private:
|
||||
struct dispatch::target<> corechange;
|
||||
wxWindow* pwin;
|
||||
int wxid_range_low;
|
||||
int wxid_range_high;
|
||||
std::vector<wxMenuItem*> items;
|
||||
std::function<void(bool enabled)> disabler_fn;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
void on_close(wxCloseEvent& e);
|
||||
void menu_start(wxString name);
|
||||
void menu_special(wxString name, wxMenu* menu);
|
||||
void menu_special_sub(wxString name, wxMenu* menu);
|
||||
wxMenuItem* menu_special_sub(wxString name, wxMenu* menu);
|
||||
void menu_entry(int id, wxString name);
|
||||
void menu_entry_check(int id, wxString name);
|
||||
void menu_start_sub(wxString name);
|
||||
|
|
76
manual.lyx
76
manual.lyx
|
@ -812,6 +812,62 @@ save-jukebox
|
|||
Do state save to jukebox.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Slot branches
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
list-branches
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
List all branches
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
create-branch <pid> <name>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Create a new branch, with <pid> as parent and <name> as name.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
rename-branch <id> <name>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Rename branch <id> to <name>.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
reparent-branch <id> <pid>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Set parent of branch <id> to <pid>.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
set-branch <id>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Set current branch to <id>.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
delete-branch <id>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Delete branch <id>.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Lua
|
||||
\end_layout
|
||||
|
@ -848,26 +904,6 @@ reset-lua
|
|||
Clear the Lua VM state and restore to factory defaults.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Memory watch
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
add-watch <name> <expression>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Adds new watch (or modifies old one).
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
remove-watch <name>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Remove a watch.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Sound
|
||||
\end_layout
|
||||
|
|
48
manual.txt
48
manual.txt
|
@ -387,34 +387,52 @@ Do load from jukebox (current mode).
|
|||
|
||||
Do state save to jukebox.
|
||||
|
||||
5.5 Lua
|
||||
5.5 Slot branches
|
||||
|
||||
5.5.1 evaluate-lua <luacode>
|
||||
5.5.1 list-branches
|
||||
|
||||
List all branches
|
||||
|
||||
5.5.2 create-branch <pid> <name>
|
||||
|
||||
Create a new branch, with <pid> as parent and <name> as name.
|
||||
|
||||
5.5.3 rename-branch <id> <name>
|
||||
|
||||
Rename branch <id> to <name>.
|
||||
|
||||
5.5.4 reparent-branch <id> <pid>
|
||||
|
||||
Set parent of branch <id> to <pid>.
|
||||
|
||||
5.5.5 set-branch <id>
|
||||
|
||||
Set current branch to <id>.
|
||||
|
||||
5.5.6 delete-branch <id>
|
||||
|
||||
Delete branch <id>.
|
||||
|
||||
|
||||
|
||||
5.6 Lua
|
||||
|
||||
5.6.1 evaluate-lua <luacode>
|
||||
|
||||
Run Lua code <luacode> using built-in Lua interpretter.
|
||||
|
||||
5.5.2 L <luacode>
|
||||
5.6.2 L <luacode>
|
||||
|
||||
Synonym for evaluate-lua.
|
||||
|
||||
5.5.3 run-lua <script>
|
||||
5.6.3 run-lua <script>
|
||||
|
||||
Run specified lua file using built-in Lua interpretter.
|
||||
|
||||
5.5.4 reset-lua
|
||||
5.6.4 reset-lua
|
||||
|
||||
Clear the Lua VM state and restore to factory defaults.
|
||||
|
||||
5.6 Memory watch
|
||||
|
||||
5.6.1 add-watch <name> <expression>
|
||||
|
||||
Adds new watch (or modifies old one).
|
||||
|
||||
5.6.2 remove-watch <name>
|
||||
|
||||
Remove a watch.
|
||||
|
||||
5.7 Sound
|
||||
|
||||
5.7.1 enable-sound <on/off>
|
||||
|
|
|
@ -230,7 +230,7 @@ void controller_state::erase_macro(const std::string& macro)
|
|||
project_info* p = project_get();
|
||||
if(p) {
|
||||
p->macros.erase(macro);
|
||||
project_flush(p);
|
||||
p->flush();
|
||||
}
|
||||
}
|
||||
load_macros(*this);
|
||||
|
@ -270,7 +270,7 @@ void controller_state::set_macro(const std::string& macro, const controller_macr
|
|||
project_info* p = project_get();
|
||||
if(p) {
|
||||
p->macros[macro] = all_macros[macro].serialize();
|
||||
project_flush(p);
|
||||
p->flush();
|
||||
}
|
||||
}
|
||||
load_macros(*this);
|
||||
|
|
|
@ -341,6 +341,7 @@ void dispatch_set_error_streams(std::ostream* stream)
|
|||
notify_core_changed.errors_to(stream);
|
||||
notify_multitrack_change.errors_to(stream);
|
||||
notify_title_change.errors_to(stream);
|
||||
notify_branch_change.errors_to(stream);
|
||||
}
|
||||
|
||||
struct dispatch::source<> notify_autohold_reconfigure("autohold_reconfigure");
|
||||
|
@ -361,3 +362,4 @@ struct dispatch::source<> notify_vu_change("vu_change");
|
|||
struct dispatch::source<> notify_subtitle_change("subtitle_change");
|
||||
struct dispatch::source<unsigned, unsigned, int> notify_multitrack_change("multitrack_change");
|
||||
struct dispatch::source<> notify_title_change("title_change");
|
||||
struct dispatch::source<> notify_branch_change("branch_change");
|
|
@ -281,13 +281,13 @@ namespace
|
|||
if(smode == SAVE_MOVIE) {
|
||||
//Just do this immediately.
|
||||
do_save_movie(filename, binary);
|
||||
flush_slotinfo(translate_name_mprefix(filename, tmp, false));
|
||||
flush_slotinfo(translate_name_mprefix(filename, tmp, -1));
|
||||
return;
|
||||
}
|
||||
if(location_special == SPECIAL_SAVEPOINT) {
|
||||
//We can save immediately here.
|
||||
do_save_state(filename, binary);
|
||||
flush_slotinfo(translate_name_mprefix(filename, tmp, false));
|
||||
flush_slotinfo(translate_name_mprefix(filename, tmp, -1));
|
||||
return;
|
||||
}
|
||||
queued_saves.insert(std::make_pair(filename, binary));
|
||||
|
@ -311,6 +311,7 @@ namespace
|
|||
|
||||
void update_movie_state()
|
||||
{
|
||||
auto p = project_get();
|
||||
bool readonly = false;
|
||||
static unsigned last_controllers = 0;
|
||||
{
|
||||
|
@ -352,13 +353,18 @@ void update_movie_state()
|
|||
}
|
||||
if(jukebox_size > 0) {
|
||||
int tmp = -1;
|
||||
std::string sfilen = translate_name_mprefix(save_jukebox_name(save_jukebox_pointer), tmp, false);
|
||||
std::string sfilen = translate_name_mprefix(save_jukebox_name(save_jukebox_pointer), tmp, -1);
|
||||
_status.set("!saveslot", (stringfmt() << (save_jukebox_pointer + 1)).str());
|
||||
_status.set("!saveslotinfo", get_slotinfo(sfilen));
|
||||
} else {
|
||||
_status.erase("!saveslot");
|
||||
_status.erase("!saveslotinfo");
|
||||
}
|
||||
if(p) {
|
||||
_status.set("!branch", p->get_branch_string());
|
||||
} else {
|
||||
_status.erase("!branch");
|
||||
}
|
||||
_status.set("!speed", (stringfmt() << (unsigned)(100 * get_realized_multiplier() + 0.5)).str());
|
||||
|
||||
if(!system_corrupt) {
|
||||
|
@ -1105,7 +1111,7 @@ nothing_to_do:
|
|||
for(auto i : queued_saves) {
|
||||
do_save_state(i.first, i.second);
|
||||
int tmp = -1;
|
||||
flush_slotinfo(translate_name_mprefix(i.first, tmp, false));
|
||||
flush_slotinfo(translate_name_mprefix(i.first, tmp, -1));
|
||||
}
|
||||
if(do_unsafe_rewind && !unsafe_rewind_obj) {
|
||||
uint64_t t = get_utime();
|
||||
|
|
|
@ -313,7 +313,7 @@ void lsnes_memorywatch_set::clear(const std::string& name)
|
|||
auto pr = project_get();
|
||||
if(pr) {
|
||||
pr->watches.erase(name);
|
||||
project_flush(pr);
|
||||
pr->flush();
|
||||
}
|
||||
redraw_framebuffer();
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ void lsnes_memorywatch_set::set(const std::string& name, lsnes_memorywatch_item&
|
|||
auto pr = project_get();
|
||||
if(pr) {
|
||||
pr->watches[name] = get_string(name);
|
||||
project_flush(pr);
|
||||
pr->flush();
|
||||
}
|
||||
redraw_framebuffer();
|
||||
update_movie_state();
|
||||
|
@ -399,7 +399,7 @@ void lsnes_memorywatch_set::set_multi(std::list<std::pair<std::string, lsnes_mem
|
|||
if(pr) {
|
||||
for(auto& i : list)
|
||||
pr->watches[i.first] = get_string(i.first);
|
||||
project_flush(pr);
|
||||
pr->flush();
|
||||
}
|
||||
redraw_framebuffer();
|
||||
update_movie_state();
|
||||
|
@ -427,7 +427,7 @@ void lsnes_memorywatch_set::clear_multi(const std::set<std::string>& names)
|
|||
if(pr) {
|
||||
for(auto& i : names)
|
||||
pr->watches.erase(i);
|
||||
project_flush(pr);
|
||||
pr->flush();
|
||||
}
|
||||
redraw_framebuffer();
|
||||
update_movie_state();
|
||||
|
|
|
@ -138,16 +138,28 @@ void set_mprefix_for_project(const std::string& pfx)
|
|||
set_mprefix(pfx);
|
||||
}
|
||||
|
||||
std::string translate_name_mprefix(std::string original, int& binary, bool save)
|
||||
std::string translate_name_mprefix(std::string original, int& binary, int save)
|
||||
{
|
||||
auto p = project_get();
|
||||
regex_results r = regex("\\$SLOT:(.*)", original);
|
||||
if(r) {
|
||||
if(binary < 0)
|
||||
binary = jukebox_dflt_binary ? 1 : 0;
|
||||
if(p)
|
||||
return p->directory + "/" + p->prefix + "-" + r[1] + ".lss";
|
||||
else {
|
||||
if(p) {
|
||||
uint64_t branch = p->get_current_branch();
|
||||
std::string branch_str;
|
||||
std::string filename;
|
||||
if(branch) branch_str = (stringfmt() << "--" << branch).str();
|
||||
filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
|
||||
while(save < 0 && branch) {
|
||||
if(zip::file_exists(filename))
|
||||
break;
|
||||
branch = p->get_parent_branch(branch);
|
||||
branch_str = branch ? ((stringfmt() << "--" << branch).str()) : "";
|
||||
filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
|
||||
}
|
||||
return filename;
|
||||
} else {
|
||||
std::string pprf = lsnes_vset["slotpath"].str() + "/";
|
||||
return pprf + get_mprefix() + r[1] + ".lsmv";
|
||||
}
|
||||
|
@ -195,7 +207,7 @@ void do_save_state(const std::string& filename, int binary) throw(std::bad_alloc
|
|||
messages << "Can't save movie without a ROM" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::string filename2 = translate_name_mprefix(filename, binary, true);
|
||||
std::string filename2 = translate_name_mprefix(filename, binary, 1);
|
||||
lua_callback_pre_save(filename2, true);
|
||||
try {
|
||||
uint64_t origtime = get_utime();
|
||||
|
@ -235,7 +247,7 @@ void do_save_state(const std::string& filename, int binary) throw(std::bad_alloc
|
|||
auto p = project_get();
|
||||
if(p) {
|
||||
p->last_save = last_save;
|
||||
project_flush(p);
|
||||
p->flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,7 +259,7 @@ void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc
|
|||
messages << "Can't save movie without a ROM" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::string filename2 = translate_name_mprefix(filename, binary, false);
|
||||
std::string filename2 = translate_name_mprefix(filename, binary, 0);
|
||||
lua_callback_pre_save(filename2, false);
|
||||
try {
|
||||
uint64_t origtime = get_utime();
|
||||
|
@ -276,7 +288,7 @@ void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc
|
|||
auto p = project_get();
|
||||
if(p) {
|
||||
p->last_save = last_save;
|
||||
project_flush(p);
|
||||
p->flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,7 +626,7 @@ void try_request_rom(const std::string& moviefile)
|
|||
bool do_load_state(const std::string& filename, int lmode)
|
||||
{
|
||||
int tmp = -1;
|
||||
std::string filename2 = translate_name_mprefix(filename, tmp, false);
|
||||
std::string filename2 = translate_name_mprefix(filename, tmp, -1);
|
||||
uint64_t origtime = get_utime();
|
||||
lua_callback_pre_load(filename2);
|
||||
struct moviefile mfile;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "core/settings.hpp"
|
||||
#include "core/window.hpp"
|
||||
#include "library/directory.hpp"
|
||||
#include "library/minmax.hpp"
|
||||
#include "library/string.hpp"
|
||||
#include <fstream>
|
||||
#include <dirent.h>
|
||||
|
@ -140,66 +141,6 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
void project_write(std::ostream& s, project_info& p)
|
||||
{
|
||||
s << p.name << std::endl;
|
||||
s << "rom=" << p.rom << std::endl;
|
||||
if(p.last_save != "")
|
||||
s << "last-save=" << p.last_save << std::endl;
|
||||
s << "directory=" << p.directory << std::endl;
|
||||
s << "prefix=" << p.prefix << std::endl;
|
||||
for(auto i : p.luascripts)
|
||||
s << "luascript=" << i << std::endl;
|
||||
s << "gametype=" << p.gametype << std::endl;
|
||||
s << "coreversion=" << p.coreversion << std::endl;
|
||||
if(p.gamename != "")
|
||||
s << "gamename=" << p.gamename << std::endl;
|
||||
s << "projectid=" << p.projectid << std::endl;
|
||||
s << "time=" << p.movie_rtc_second << ":" << p.movie_rtc_subsecond << std::endl;
|
||||
for(auto i : p.authors)
|
||||
s << "author=" << i.first << "|" << i.second << std::endl;
|
||||
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
|
||||
if(p.romimg_sha256[i] != "") {
|
||||
if(i)
|
||||
s << "slotsha" << static_cast<char>(96 + i) << "=" << p.romimg_sha256[i]
|
||||
<< std::endl;
|
||||
else
|
||||
s << "romsha=" << p.romimg_sha256[i] << std::endl;
|
||||
}
|
||||
if(p.romxml_sha256[i] != "") {
|
||||
if(i)
|
||||
s << "slotxml" << static_cast<char>(96 + i) << "=" << p.romxml_sha256[i]
|
||||
<< std::endl;
|
||||
else
|
||||
s << "romxml=" << p.romxml_sha256[i] << std::endl;
|
||||
}
|
||||
if(p.namehint[i] != "") {
|
||||
if(i)
|
||||
s << "slothint" << static_cast<char>(96 + i) << "=" << p.namehint[i]
|
||||
<< std::endl;
|
||||
else
|
||||
s << "romhint=" << p.namehint[i] << std::endl;
|
||||
}
|
||||
if(p.roms[i] != "") {
|
||||
if(i)
|
||||
s << "slotrom" << static_cast<char>(96 + i) << "=" << p.roms[i]
|
||||
<< std::endl;
|
||||
else
|
||||
s << "romrom=" << p.roms[i] << std::endl;
|
||||
}
|
||||
}
|
||||
for(auto i : p.settings)
|
||||
s << "setting." << i.first << "=" << i.second << std::endl;
|
||||
for(auto i : p.watches)
|
||||
s << "watch." << eq_escape(i.first) << "=" << i.second << std::endl;
|
||||
for(auto i : p.macros)
|
||||
s << "macro." + i.first << "=" << i.second.serialize() << std::endl;
|
||||
for(auto i : p.movie_sram)
|
||||
save_binary(s, "sram." + i.first, i.second);
|
||||
if(p.anchor_savestate.size())
|
||||
save_binary(s, "anchor", p.anchor_savestate);
|
||||
}
|
||||
|
||||
void fill_stub_movie(struct moviefile& m, struct project_info& p, struct core_type& coretype)
|
||||
{
|
||||
//Create a dummy movie.
|
||||
|
@ -255,6 +196,10 @@ project_info& project_load(const std::string& id)
|
|||
throw std::runtime_error("Can't open project file");
|
||||
project_info& pi = *new project_info();
|
||||
pi.id = id;
|
||||
pi.movie_rtc_second = 1000000000;
|
||||
pi.movie_rtc_subsecond = 0;
|
||||
pi.active_branch = 0;
|
||||
pi.next_branch = 0;
|
||||
//First line is always project name.
|
||||
std::getline(f, pi.name);
|
||||
if(!f || pi.name == "") {
|
||||
|
@ -323,47 +268,45 @@ project_info& project_load(const std::string& id)
|
|||
else if(r = regex("time=([0-9]+):([0-9]+)", tmp)) {
|
||||
pi.movie_rtc_second = parse_value<int64_t>(r[1]);
|
||||
pi.movie_rtc_subsecond = parse_value<int64_t>(r[2]);
|
||||
} else if(r = regex("branch([1-9][0-9]*)parent=([0-9]+)", tmp)) {
|
||||
uint64_t bid = parse_value<int64_t>(r[1]);
|
||||
uint64_t pbid = parse_value<int64_t>(r[2]);
|
||||
if(!pi.branches.count(bid))
|
||||
pi.branches[bid].name = "(Unnamed branch)";
|
||||
pi.branches[bid].pbid = pbid;
|
||||
} else if(r = regex("branch([1-9][0-9]*)name=(.*)", tmp)) {
|
||||
uint64_t bid = parse_value<int64_t>(r[1]);
|
||||
if(!pi.branches.count(bid))
|
||||
pi.branches[bid].pbid = 0;
|
||||
pi.branches[bid].name = r[2];
|
||||
} else if(r = regex("branchcurrent=([0-9]+)", tmp)) {
|
||||
pi.active_branch = parse_value<int64_t>(r[1]);
|
||||
} else if(r = regex("branchnext=([0-9]+)", tmp)) {
|
||||
pi.next_branch = parse_value<int64_t>(r[1]);
|
||||
}
|
||||
}
|
||||
for(auto& i : pi.branches) {
|
||||
uint64_t j = i.first;
|
||||
uint64_t m = j;
|
||||
while(j) {
|
||||
j = pi.branches[j].pbid;
|
||||
m = min(m, j);
|
||||
if(j == i.first) {
|
||||
//Cyclic dependency!
|
||||
messages << "Warning: Cyclic slot branch dependency, reparenting '" <<
|
||||
pi.branches[m].name << "' to be child of root..." << std::endl;
|
||||
pi.branches[j].pbid = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pi.active_branch && !pi.branches.count(pi.active_branch)) {
|
||||
messages << "Warning: Current slot branch does not exist, using root..." << std::endl;
|
||||
pi.active_branch = 0;
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
void project_flush(project_info* p)
|
||||
{
|
||||
if(!p)
|
||||
return;
|
||||
std::string file = get_config_path() + "/" + p->id + ".prj";
|
||||
std::string tmpfile = get_config_path() + "/" + p->id + ".prj.tmp";
|
||||
std::string bakfile = get_config_path() + "/" + p->id + ".prj.bak";
|
||||
std::ofstream f(tmpfile);
|
||||
if(!f)
|
||||
throw std::runtime_error("Can't write project file");
|
||||
project_write(f, *p);
|
||||
if(!f)
|
||||
throw std::runtime_error("Can't write project file");
|
||||
f.close();
|
||||
|
||||
std::ifstream f2(file);
|
||||
if(f2) {
|
||||
std::ofstream f3(bakfile);
|
||||
if(!f3)
|
||||
throw std::runtime_error("Can't backup project file");
|
||||
while(f2) {
|
||||
std::string tmp;
|
||||
std::getline(f2, tmp);
|
||||
f3 << tmp << std::endl;
|
||||
}
|
||||
f2.close();
|
||||
f3.close();
|
||||
}
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
|
||||
if(MoveFileEx(tmpfile.c_str(), file.c_str(), MOVEFILE_REPLACE_EXISTING) < 0)
|
||||
#else
|
||||
if(rename(tmpfile.c_str(), file.c_str()) < 0)
|
||||
#endif
|
||||
throw std::runtime_error("Can't replace project file");
|
||||
}
|
||||
|
||||
project_info* project_get()
|
||||
{
|
||||
return active_project;
|
||||
|
@ -376,6 +319,7 @@ bool project_set(project_info* p, bool current)
|
|||
voicesub_unload_collection();
|
||||
active_project = p;
|
||||
notify_core_change();
|
||||
notify_branch_change();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -445,6 +389,7 @@ skip_rom_movie:
|
|||
do_flush_slotinfo();
|
||||
update_movie_state();
|
||||
notify_core_change();
|
||||
notify_branch_change();
|
||||
}
|
||||
return switched;
|
||||
}
|
||||
|
@ -512,3 +457,351 @@ void project_copy_macros(project_info& p, controller_state& s)
|
|||
for(auto i : s.enumerate_macro())
|
||||
p.macros[i] = s.get_macro(i).serialize();
|
||||
}
|
||||
|
||||
uint64_t project_info::get_parent_branch(uint64_t bid)
|
||||
{
|
||||
if(!bid)
|
||||
return 0;
|
||||
if(!branches.count(bid))
|
||||
throw std::runtime_error("Invalid branch ID");
|
||||
return branches[bid].pbid;
|
||||
}
|
||||
|
||||
void project_info::set_current_branch(uint64_t bid)
|
||||
{
|
||||
if(bid && !branches.count(bid))
|
||||
throw std::runtime_error("Invalid branch ID");
|
||||
active_branch = bid;
|
||||
notify_branch_change();
|
||||
messages << "Set current slot branch to " << get_branch_string() << std::endl;
|
||||
}
|
||||
|
||||
const std::string& project_info::get_branch_name(uint64_t bid)
|
||||
{
|
||||
static std::string rootname = "(root)";
|
||||
if(!bid)
|
||||
return rootname;
|
||||
if(!branches.count(bid))
|
||||
throw std::runtime_error("Invalid branch ID");
|
||||
return branches[bid].name;
|
||||
}
|
||||
|
||||
void project_info::set_branch_name(uint64_t bid, const std::string& name)
|
||||
{
|
||||
if(!bid)
|
||||
throw std::runtime_error("Root branch name can't be set");
|
||||
if(!branches.count(bid))
|
||||
throw std::runtime_error("Invalid branch ID");
|
||||
branches[bid].name = name;
|
||||
notify_branch_change();
|
||||
}
|
||||
|
||||
void project_info::set_parent_branch(uint64_t bid, uint64_t pbid)
|
||||
{
|
||||
if(!bid)
|
||||
throw std::runtime_error("Root branch never has parent");
|
||||
if(!branches.count(bid))
|
||||
throw std::runtime_error("Invalid branch ID");
|
||||
if(pbid && !branches.count(pbid))
|
||||
throw std::runtime_error("Invalid parent branch ID");
|
||||
for(auto& i : branches) {
|
||||
uint64_t j = i.first;
|
||||
while(j) {
|
||||
j = (j == bid) ? pbid : branches[j].pbid;
|
||||
if(j == i.first)
|
||||
throw std::runtime_error("Reparenting would create a circular dependency");
|
||||
}
|
||||
}
|
||||
branches[bid].pbid = pbid;
|
||||
notify_branch_change();
|
||||
}
|
||||
|
||||
std::set<uint64_t> project_info::branch_children(uint64_t bid)
|
||||
{
|
||||
if(bid && !branches.count(bid))
|
||||
throw std::runtime_error("Invalid branch ID");
|
||||
std::set<uint64_t> r;
|
||||
for(auto& i : branches)
|
||||
if(i.second.pbid == bid)
|
||||
r.insert(i.first);
|
||||
return r;
|
||||
}
|
||||
|
||||
uint64_t project_info::create_branch(uint64_t pbid, const std::string& name)
|
||||
{
|
||||
if(pbid && !branches.count(pbid))
|
||||
throw std::runtime_error("Invalid parent branch ID");
|
||||
uint64_t assign_bid = next_branch;
|
||||
uint64_t last_bid = (branches.empty() ? 1 : branches.rbegin()->first + 1);
|
||||
assign_bid = max(assign_bid, last_bid);
|
||||
branches[assign_bid].name = name;
|
||||
branches[assign_bid].pbid = pbid;
|
||||
next_branch = assign_bid + 1;
|
||||
notify_branch_change();
|
||||
return assign_bid;
|
||||
}
|
||||
|
||||
void project_info::delete_branch(uint64_t bid)
|
||||
{
|
||||
if(!bid)
|
||||
throw std::runtime_error("Root branch can not be deleted");
|
||||
if(bid == active_branch)
|
||||
throw std::runtime_error("Current branch can't be deleted");
|
||||
if(!branches.count(bid))
|
||||
throw std::runtime_error("Invalid branch ID");
|
||||
for(auto& i : branches)
|
||||
if(i.second.pbid == bid)
|
||||
throw std::runtime_error("Can't delete branch with children");
|
||||
branches.erase(bid);
|
||||
notify_branch_change();
|
||||
}
|
||||
|
||||
std::string project_info::get_branch_string()
|
||||
{
|
||||
std::string r;
|
||||
uint64_t j = active_branch;
|
||||
if(!j)
|
||||
return "(root)";
|
||||
while(j) {
|
||||
if(r == "")
|
||||
r = get_branch_name(j);
|
||||
else
|
||||
r = get_branch_name(j) + "→" + r;
|
||||
j = get_parent_branch(j);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void project_info::flush()
|
||||
{
|
||||
std::string file = get_config_path() + "/" + id + ".prj";
|
||||
std::string tmpfile = get_config_path() + "/" + id + ".prj.tmp";
|
||||
std::string bakfile = get_config_path() + "/" + id + ".prj.bak";
|
||||
std::ofstream f(tmpfile);
|
||||
if(!f)
|
||||
throw std::runtime_error("Can't write project file");
|
||||
write(f);
|
||||
if(!f)
|
||||
throw std::runtime_error("Can't write project file");
|
||||
f.close();
|
||||
|
||||
std::ifstream f2(file);
|
||||
if(f2) {
|
||||
std::ofstream f3(bakfile);
|
||||
if(!f3)
|
||||
throw std::runtime_error("Can't backup project file");
|
||||
while(f2) {
|
||||
std::string tmp;
|
||||
std::getline(f2, tmp);
|
||||
f3 << tmp << std::endl;
|
||||
}
|
||||
f2.close();
|
||||
f3.close();
|
||||
}
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
|
||||
if(MoveFileEx(tmpfile.c_str(), file.c_str(), MOVEFILE_REPLACE_EXISTING) < 0)
|
||||
#else
|
||||
if(rename(tmpfile.c_str(), file.c_str()) < 0)
|
||||
#endif
|
||||
throw std::runtime_error("Can't replace project file");
|
||||
}
|
||||
|
||||
void project_info::write(std::ostream& s)
|
||||
{
|
||||
s << name << std::endl;
|
||||
s << "rom=" << rom << std::endl;
|
||||
if(last_save != "")
|
||||
s << "last-save=" << last_save << std::endl;
|
||||
s << "directory=" << directory << std::endl;
|
||||
s << "prefix=" << prefix << std::endl;
|
||||
for(auto i : luascripts)
|
||||
s << "luascript=" << i << std::endl;
|
||||
s << "gametype=" << gametype << std::endl;
|
||||
s << "coreversion=" << coreversion << std::endl;
|
||||
if(gamename != "")
|
||||
s << "gamename=" << gamename << std::endl;
|
||||
s << "projectid=" << projectid << std::endl;
|
||||
s << "time=" << movie_rtc_second << ":" << movie_rtc_subsecond << std::endl;
|
||||
for(auto i : authors)
|
||||
s << "author=" << i.first << "|" << i.second << std::endl;
|
||||
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
|
||||
if(romimg_sha256[i] != "") {
|
||||
if(i)
|
||||
s << "slotsha" << static_cast<char>(96 + i) << "=" << romimg_sha256[i] << std::endl;
|
||||
else
|
||||
s << "romsha=" << romimg_sha256[i] << std::endl;
|
||||
}
|
||||
if(romxml_sha256[i] != "") {
|
||||
if(i)
|
||||
s << "slotxml" << static_cast<char>(96 + i) << "=" << romxml_sha256[i] << std::endl;
|
||||
else
|
||||
s << "romxml=" << romxml_sha256[i] << std::endl;
|
||||
}
|
||||
if(namehint[i] != "") {
|
||||
if(i)
|
||||
s << "slothint" << static_cast<char>(96 + i) << "=" << namehint[i] << std::endl;
|
||||
else
|
||||
s << "romhint=" << namehint[i] << std::endl;
|
||||
}
|
||||
if(roms[i] != "") {
|
||||
if(i)
|
||||
s << "slotrom" << static_cast<char>(96 + i) << "=" << roms[i] << std::endl;
|
||||
else
|
||||
s << "romrom=" << roms[i] << std::endl;
|
||||
}
|
||||
}
|
||||
for(auto i : settings)
|
||||
s << "setting." << i.first << "=" << i.second << std::endl;
|
||||
for(auto i : watches)
|
||||
s << "watch." << eq_escape(i.first) << "=" << i.second << std::endl;
|
||||
for(auto i : macros)
|
||||
s << "macro." + i.first << "=" << i.second.serialize() << std::endl;
|
||||
for(auto i : movie_sram)
|
||||
save_binary(s, "sram." + i.first, i.second);
|
||||
if(anchor_savestate.size())
|
||||
save_binary(s, "anchor", anchor_savestate);
|
||||
for(auto& i : branches) {
|
||||
s << "branch" << i.first << "parent=" << i.second.pbid << std::endl;
|
||||
s << "branch" << i.first << "name=" << i.second.name << std::endl;
|
||||
}
|
||||
s << "branchcurrent=" << active_branch << std::endl;
|
||||
s << "branchnext=" << next_branch << std::endl;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void recursive_list_branch(uint64_t bid, std::set<unsigned>& dset, unsigned depth, bool last_of)
|
||||
{
|
||||
if(!active_project) {
|
||||
messages << "Not in project context." << std::endl;
|
||||
return;
|
||||
}
|
||||
std::set<uint64_t> children = active_project->branch_children(bid);
|
||||
std::string prefix;
|
||||
for(unsigned i = 0; i + 1 < depth; i++)
|
||||
prefix += (dset.count(i) ? "\u2502" : " ");
|
||||
prefix += (dset.count(depth - 1) ? (last_of ? "\u2514" : "\u251c") : " ");
|
||||
if(last_of) dset.erase(depth - 1);
|
||||
messages << prefix
|
||||
<< ((bid == active_project->get_current_branch()) ? "*" : "")
|
||||
<< bid << ":" << active_project->get_branch_name(bid) << std::endl;
|
||||
dset.insert(depth);
|
||||
size_t c = 0;
|
||||
for(auto i : children) {
|
||||
bool last = (++c == children.size());
|
||||
recursive_list_branch(i, dset, depth + 1, last);
|
||||
}
|
||||
dset.erase(depth);
|
||||
}
|
||||
|
||||
command::fnptr<> list_branches(lsnes_cmd, "list-branches", "List all slot branches",
|
||||
"Syntax: list-branches\nList all slot branches.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
std::set<unsigned> dset;
|
||||
recursive_list_branch(0, dset, 0, false);
|
||||
});
|
||||
|
||||
command::fnptr<const std::string&> create_branch(lsnes_cmd, "create-branch", "Create a new slot branch",
|
||||
"Syntax: create-branch <parentid> <name>\nCreate new branch named <name> under <parentid>.\n",
|
||||
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
|
||||
regex_results r = regex("([0-9]+)[ \t]+(.*)", args);
|
||||
if(!r) {
|
||||
messages << "Syntax: create-branch <parentid> <name>" << std::endl;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
uint64_t pbid = parse_value<uint64_t>(r[1]);
|
||||
if(!active_project)
|
||||
throw std::runtime_error("Not in project context");
|
||||
uint64_t bid = active_project->create_branch(pbid, r[2]);
|
||||
messages << "Created branch #" << bid << std::endl;
|
||||
active_project->flush();
|
||||
} catch(std::exception& e) {
|
||||
messages << "Can't create new branch: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
command::fnptr<const std::string&> delete_branch(lsnes_cmd, "delete-branch", "Delete a slot branch",
|
||||
"Syntax: delete-branch <id>\nDelete slot branch with id <id>.\n",
|
||||
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
|
||||
regex_results r = regex("([0-9]+)[ \t]*", args);
|
||||
if(!r) {
|
||||
messages << "Syntax: delete-branch <id>" << std::endl;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
uint64_t bid = parse_value<uint64_t>(r[1]);
|
||||
if(!active_project)
|
||||
throw std::runtime_error("Not in project context");
|
||||
active_project->delete_branch(bid);
|
||||
messages << "Deleted branch #" << bid << std::endl;
|
||||
active_project->flush();
|
||||
} catch(std::exception& e) {
|
||||
messages << "Can't delete branch: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
command::fnptr<const std::string&> set_branch(lsnes_cmd, "set-branch", "Set current slot branch",
|
||||
"Syntax: set-branch <id>\nSet current branch to <id>.\n",
|
||||
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
|
||||
regex_results r = regex("([0-9]+)[ \t]*", args);
|
||||
if(!r) {
|
||||
messages << "Syntax: set-branch <id>" << std::endl;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
uint64_t bid = parse_value<uint64_t>(r[1]);
|
||||
if(!active_project)
|
||||
throw std::runtime_error("Not in project context");
|
||||
active_project->set_current_branch(bid);
|
||||
messages << "Set current branch to #" << bid << std::endl;
|
||||
active_project->flush();
|
||||
update_movie_state();
|
||||
} catch(std::exception& e) {
|
||||
messages << "Can't set branch: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
command::fnptr<const std::string&> reparent_branch(lsnes_cmd, "reparent-branch", "Reparent a slot branch",
|
||||
"Syntax: reparent-branch <id> <newpid>\nReparent branch <id> to be child of <newpid>.\n",
|
||||
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
|
||||
regex_results r = regex("([0-9]+)[ \t]+([0-9]+)[ \t]*", args);
|
||||
if(!r) {
|
||||
messages << "Syntax: reparent-branch <id> <newpid>" << std::endl;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
uint64_t bid = parse_value<uint64_t>(r[1]);
|
||||
uint64_t pbid = parse_value<uint64_t>(r[2]);
|
||||
if(!active_project)
|
||||
throw std::runtime_error("Not in project context");
|
||||
active_project->set_parent_branch(bid, pbid);
|
||||
messages << "Reparented branch #" << bid << std::endl;
|
||||
active_project->flush();
|
||||
update_movie_state();
|
||||
} catch(std::exception& e) {
|
||||
messages << "Can't reparent branch: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
command::fnptr<const std::string&> rename_branch(lsnes_cmd, "rename-branch", "Rename a slot branch",
|
||||
"Syntax: rename-branch <id> <name>\nRename branch <id> to <name>.\n",
|
||||
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
|
||||
regex_results r = regex("([0-9]+)[ \t]+(.*)", args);
|
||||
if(!r) {
|
||||
messages << "Syntax: rename-branch <id> <name>" << std::endl;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
uint64_t bid = parse_value<uint64_t>(r[1]);
|
||||
if(!active_project)
|
||||
throw std::runtime_error("Not in project context");
|
||||
active_project->set_branch_name(bid, r[2]);
|
||||
messages << "Renamed branch #" << bid << std::endl;
|
||||
active_project->flush();
|
||||
update_movie_state();
|
||||
} catch(std::exception& e) {
|
||||
messages << "Can't rename branch: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
492
src/platform/wxwidgets/branchesmenu.cpp
Normal file
492
src/platform/wxwidgets/branchesmenu.cpp
Normal file
|
@ -0,0 +1,492 @@
|
|||
#include "platform/wxwidgets/settings-common.hpp"
|
||||
#include "platform/wxwidgets/settings-keyentry.hpp"
|
||||
#include "platform/wxwidgets/menu_branches.hpp"
|
||||
#include "platform/wxwidgets/platform.hpp"
|
||||
#include "platform/wxwidgets/loadsave.hpp"
|
||||
#include "core/debug.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/project.hpp"
|
||||
#include "core/moviedata.hpp"
|
||||
|
||||
void update_movie_state();
|
||||
|
||||
namespace
|
||||
{
|
||||
void fill_namemap(project_info& p, uint64_t id, std::map<uint64_t, std::string>& namemap,
|
||||
std::map<uint64_t, std::set<uint64_t>>& childmap)
|
||||
{
|
||||
namemap[id] = p.get_branch_name(id);
|
||||
auto s = p.branch_children(id);
|
||||
for(auto i : s)
|
||||
fill_namemap(p, i, namemap, childmap);
|
||||
childmap[id] = s;
|
||||
}
|
||||
|
||||
template<typename T> void runemufn_async(T fn)
|
||||
{
|
||||
platform::queue(functor_call_helper2<T>, new T(fn), false);
|
||||
}
|
||||
|
||||
//Tree of branches.
|
||||
class branches_tree : public wxTreeCtrl
|
||||
{
|
||||
public:
|
||||
branches_tree(wxWindow* parent, int id, bool _nosels)
|
||||
: wxTreeCtrl(parent, id), nosels(_nosels)
|
||||
{
|
||||
SetMinSize(wxSize(400, 300));
|
||||
branchchange.set(notify_branch_change, [this]() { runuifun([this]() { this->update(); }); });
|
||||
update();
|
||||
}
|
||||
struct selection
|
||||
{
|
||||
wxTreeItemId item;
|
||||
bool isroot;
|
||||
bool haschildren;
|
||||
bool iscurrent;
|
||||
uint64_t id;
|
||||
std::string name;
|
||||
};
|
||||
selection get_selection()
|
||||
{
|
||||
selection s;
|
||||
s.item = GetSelection();
|
||||
for(auto i : ids) {
|
||||
if(s.item.IsOk() && i.second == s.item) {
|
||||
s.isroot = (i.first == 0);
|
||||
s.haschildren = with_children.count(i.first);
|
||||
s.id = i.first;
|
||||
s.iscurrent = (i.first == current);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
s.isroot = false;
|
||||
s.haschildren = false;
|
||||
s.iscurrent = false;
|
||||
s.id = 0xFFFFFFFFFFFFFFFFULL;
|
||||
return s;
|
||||
}
|
||||
void update()
|
||||
{
|
||||
std::map<uint64_t, std::string> namemap;
|
||||
std::map<uint64_t, std::set<uint64_t>> childmap;
|
||||
uint64_t cur = 0;
|
||||
runemufn([&cur, &namemap, &childmap]() {
|
||||
auto p = project_get();
|
||||
if(!p) return;
|
||||
fill_namemap(*p, 0, namemap, childmap);
|
||||
cur = p->get_current_branch();
|
||||
});
|
||||
current = cur;
|
||||
selection cursel = get_selection();
|
||||
std::set<uint64_t> expanded;
|
||||
for(auto i : ids)
|
||||
if(IsExpanded(i.second))
|
||||
expanded.insert(i.first);
|
||||
DeleteAllItems();
|
||||
ids.clear();
|
||||
with_children.clear();
|
||||
if(namemap.empty()) return;
|
||||
//Create ROOT.
|
||||
names = namemap;
|
||||
ids[0] = AddRoot(towxstring(namemap[0] + ((!nosels && current == 0) ? " <selected>" : "")));
|
||||
build_tree(0, ids[0], childmap, namemap);
|
||||
for(auto i : expanded)
|
||||
if(ids.count(i))
|
||||
Expand(ids[i]);
|
||||
for(auto i : ids) {
|
||||
if(i.first == cursel.id) {
|
||||
SelectItem(i.second);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
std::string get_name(uint64_t id)
|
||||
{
|
||||
if(names.count(id))
|
||||
return names[id];
|
||||
return "";
|
||||
}
|
||||
void call_project_flush()
|
||||
{
|
||||
runemufn_async([] {
|
||||
auto p = project_get();
|
||||
if(p) p->flush();
|
||||
});
|
||||
}
|
||||
private:
|
||||
void build_tree(uint64_t id, wxTreeItemId parent, std::map<uint64_t, std::set<uint64_t>>& childmap,
|
||||
std::map<uint64_t, std::string>& namemap)
|
||||
{
|
||||
if(!childmap.count(id) || childmap[id].empty())
|
||||
return;
|
||||
for(auto i : childmap[id]) {
|
||||
ids[i] = AppendItem(ids[id], towxstring(namemap[i] + ((!nosels && current == i) ?
|
||||
" <selected>" : "")));
|
||||
build_tree(i, ids[i], childmap, namemap);
|
||||
}
|
||||
}
|
||||
uint64_t current;
|
||||
std::map<uint64_t, std::string> names;
|
||||
std::map<uint64_t, wxTreeItemId> ids;
|
||||
std::set<uint64_t> with_children;
|
||||
struct dispatch::target<> branchchange;
|
||||
bool nosels;
|
||||
};
|
||||
|
||||
class branch_select : public wxDialog
|
||||
{
|
||||
public:
|
||||
branch_select(wxWindow* parent)
|
||||
: wxDialog(parent, wxID_ANY, towxstring("lsnes: Select new parent branch"))
|
||||
{
|
||||
Centre();
|
||||
wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
|
||||
SetSizer(top_s);
|
||||
Center();
|
||||
|
||||
top_s->Add(branches = new branches_tree(this, wxID_ANY, true), 1, wxGROW);
|
||||
branches->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED,
|
||||
wxCommandEventHandler(branch_select::on_change), NULL, this);
|
||||
|
||||
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
|
||||
pbutton_s->AddStretchSpacer();
|
||||
pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
|
||||
pbutton_s->Add(cancelbutton = new wxButton(this, wxID_OK, wxT("Cancel")), 0, wxGROW);
|
||||
okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(branch_select::on_ok), NULL, this);
|
||||
cancelbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(branch_select::on_cancel), NULL, this);
|
||||
top_s->Add(pbutton_s, 0, wxGROW);
|
||||
|
||||
top_s->SetSizeHints(this);
|
||||
Fit();
|
||||
|
||||
wxCommandEvent e;
|
||||
on_change(e);
|
||||
}
|
||||
void on_ok(wxCommandEvent& e)
|
||||
{
|
||||
EndModal(wxID_OK);
|
||||
}
|
||||
void on_cancel(wxCommandEvent& e)
|
||||
{
|
||||
EndModal(wxID_CANCEL);
|
||||
}
|
||||
uint64_t get_selection()
|
||||
{
|
||||
return branches->get_selection().id;
|
||||
}
|
||||
void on_change(wxCommandEvent& e)
|
||||
{
|
||||
okbutton->Enable(get_selection() != 0xFFFFFFFFFFFFFFFFULL);
|
||||
}
|
||||
private:
|
||||
wxButton* okbutton;
|
||||
wxButton* cancelbutton;
|
||||
branches_tree* branches;
|
||||
};
|
||||
|
||||
class branch_config : public wxDialog
|
||||
{
|
||||
public:
|
||||
branch_config(wxWindow* parent)
|
||||
: wxDialog(parent, wxID_ANY, towxstring("lsnes: Edit slot branches"))
|
||||
{
|
||||
Centre();
|
||||
wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
|
||||
SetSizer(top_s);
|
||||
Center();
|
||||
|
||||
top_s->Add(branches = new branches_tree(this, wxID_ANY, false), 1, wxGROW);
|
||||
branches->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED,
|
||||
wxCommandEventHandler(branch_config::on_change), NULL, this);
|
||||
|
||||
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
|
||||
pbutton_s->Add(createbutton = new wxButton(this, wxID_OK, wxT("Create")), 0, wxGROW);
|
||||
pbutton_s->Add(selectbutton = new wxButton(this, wxID_OK, wxT("Select")), 0, wxGROW);
|
||||
pbutton_s->Add(renamebutton = new wxButton(this, wxID_OK, wxT("Rename")), 0, wxGROW);
|
||||
pbutton_s->Add(reparentbutton = new wxButton(this, wxID_OK, wxT("Reparent")), 0, wxGROW);
|
||||
pbutton_s->Add(deletebutton = new wxButton(this, wxID_OK, wxT("Delete")), 0, wxGROW);
|
||||
pbutton_s->AddStretchSpacer();
|
||||
pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("Close")), 0, wxGROW);
|
||||
createbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(branch_config::on_create), NULL, this);
|
||||
selectbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(branch_config::on_select), NULL, this);
|
||||
renamebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(branch_config::on_rename), NULL, this);
|
||||
reparentbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(branch_config::on_reparent), NULL, this);
|
||||
deletebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(branch_config::on_delete), NULL, this);
|
||||
okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
|
||||
wxCommandEventHandler(branch_config::on_close), NULL, this);
|
||||
top_s->Add(pbutton_s, 0, wxGROW);
|
||||
|
||||
top_s->SetSizeHints(this);
|
||||
Fit();
|
||||
wxCommandEvent e;
|
||||
on_change(e);
|
||||
}
|
||||
void on_create(wxCommandEvent& e)
|
||||
{
|
||||
uint64_t id = get_selected_id();
|
||||
if(id == 0xFFFFFFFFFFFFFFFFULL) return;
|
||||
std::string newname;
|
||||
try {
|
||||
newname = pick_text(this, "Enter new branch name", "Enter name for new branch:",
|
||||
newname, false);
|
||||
} catch(canceled_exception& e) {
|
||||
return;
|
||||
}
|
||||
runemufn([this, id, newname]() {
|
||||
try {
|
||||
auto p = project_get();
|
||||
if(p) p->create_branch(id, newname);
|
||||
} catch(std::exception& e) {
|
||||
std::string err = e.what();
|
||||
runuifun([this, err]() {
|
||||
show_message_ok(this, "Error creating branch",
|
||||
"Can't create branch: " + err, wxICON_EXCLAMATION);
|
||||
});
|
||||
}
|
||||
});
|
||||
branches->call_project_flush();
|
||||
}
|
||||
void on_select(wxCommandEvent& e)
|
||||
{
|
||||
uint64_t id = get_selected_id();
|
||||
if(id == 0xFFFFFFFFFFFFFFFFULL) return;
|
||||
runemufn([this, id]() {
|
||||
try {
|
||||
auto p = project_get();
|
||||
if(p) p->set_current_branch(id);
|
||||
} catch(std::exception& e) {
|
||||
std::string err = e.what();
|
||||
runuifun([this, err]() {
|
||||
show_message_ok(this, "Error setting branch",
|
||||
"Can't set branch: " + err, wxICON_EXCLAMATION);
|
||||
});
|
||||
}
|
||||
});
|
||||
branches->call_project_flush();
|
||||
update_movie_state();
|
||||
}
|
||||
void on_rename(wxCommandEvent& e)
|
||||
{
|
||||
uint64_t id = get_selected_id();
|
||||
if(id == 0xFFFFFFFFFFFFFFFFULL) return;
|
||||
std::string newname = branches->get_name(id);
|
||||
try {
|
||||
newname = pick_text(this, "Enter new branch name", "Rename this branch to:",
|
||||
newname, false);
|
||||
} catch(canceled_exception& e) {
|
||||
return;
|
||||
}
|
||||
runemufn([this, id, newname]() {
|
||||
try {
|
||||
auto p = project_get();
|
||||
if(p) p->set_branch_name(id, newname);
|
||||
} catch(std::exception& e) {
|
||||
std::string err = e.what();
|
||||
runuifun([this, err]() {
|
||||
show_message_ok(this, "Error renaming branch",
|
||||
"Can't rename branch: " + err, wxICON_EXCLAMATION);
|
||||
});
|
||||
}
|
||||
});
|
||||
branches->call_project_flush();
|
||||
update_movie_state();
|
||||
}
|
||||
void on_reparent(wxCommandEvent& e)
|
||||
{
|
||||
uint64_t id = get_selected_id();
|
||||
if(id == 0xFFFFFFFFFFFFFFFFULL) return;
|
||||
uint64_t pid;
|
||||
branch_select* bsel = new branch_select(this);
|
||||
int r = bsel->ShowModal();
|
||||
if(r != wxID_OK) {
|
||||
bsel->Destroy();
|
||||
return;
|
||||
}
|
||||
pid = bsel->get_selection();
|
||||
if(pid == 0xFFFFFFFFFFFFFFFFULL) return;
|
||||
bsel->Destroy();
|
||||
runemufn([this, id, pid]() {
|
||||
try {
|
||||
auto p = project_get();
|
||||
if(p) p->set_parent_branch(id, pid);
|
||||
} catch(std::exception& e) {
|
||||
std::string err = e.what();
|
||||
runuifun([this, err]() {
|
||||
show_message_ok(this, "Error reparenting branch",
|
||||
"Can't reparent branch: " + err, wxICON_EXCLAMATION);
|
||||
});
|
||||
}
|
||||
});
|
||||
branches->call_project_flush();
|
||||
update_movie_state();
|
||||
}
|
||||
void on_delete(wxCommandEvent& e)
|
||||
{
|
||||
uint64_t id = get_selected_id();
|
||||
if(id == 0xFFFFFFFFFFFFFFFFULL) return;
|
||||
runemufn([this, id]() {
|
||||
try {
|
||||
auto p = project_get();
|
||||
if(p) p->delete_branch(id);
|
||||
} catch(std::exception& e) {
|
||||
std::string err = e.what();
|
||||
runuifun([this, err]() {
|
||||
show_message_ok(this, "Error deleting branch",
|
||||
"Can't delete branch: " + err, wxICON_EXCLAMATION);
|
||||
});
|
||||
}
|
||||
});
|
||||
branches->call_project_flush();
|
||||
}
|
||||
void on_close(wxCommandEvent& e)
|
||||
{
|
||||
EndModal(wxID_OK);
|
||||
}
|
||||
void on_change(wxCommandEvent& e)
|
||||
{
|
||||
set_enabled(branches->get_selection());
|
||||
}
|
||||
private:
|
||||
uint64_t get_selected_id()
|
||||
{
|
||||
return branches->get_selection().id;
|
||||
}
|
||||
void set_enabled(branches_tree::selection id)
|
||||
{
|
||||
createbutton->Enable(id.item.IsOk());
|
||||
selectbutton->Enable(id.item.IsOk());
|
||||
renamebutton->Enable(id.item.IsOk() && !id.isroot);
|
||||
reparentbutton->Enable(id.item.IsOk() && !id.isroot);
|
||||
deletebutton->Enable(id.item.IsOk() && !id.isroot && !id.haschildren && !id.iscurrent);
|
||||
}
|
||||
wxButton* createbutton;
|
||||
wxButton* selectbutton;
|
||||
wxButton* renamebutton;
|
||||
wxButton* reparentbutton;
|
||||
wxButton* deletebutton;
|
||||
wxButton* okbutton;
|
||||
branches_tree* branches;
|
||||
};
|
||||
|
||||
void build_menus(wxMenu* root, uint64_t id, std::list<branches_menu::miteminfo>& otheritems,
|
||||
std::list<wxMenu*>& menus, std::map<uint64_t, std::string>& namemap,
|
||||
std::map<uint64_t, std::set<uint64_t>>& childmap, std::map<int, uint64_t>& branch_ids, int& nextid,
|
||||
uint64_t curbranch)
|
||||
{
|
||||
auto& children = childmap[id];
|
||||
int mid = nextid++;
|
||||
otheritems.push_back(branches_menu::miteminfo(root->AppendCheckItem(mid, towxstring(namemap[id])),
|
||||
false, root));
|
||||
branch_ids[mid] = id;
|
||||
root->FindItem(mid)->Check(id == curbranch);
|
||||
if(!children.empty())
|
||||
otheritems.push_back(branches_menu::miteminfo(root->AppendSeparator(), false, root));
|
||||
for(auto i : children) {
|
||||
bool has_children = !childmap[i].empty();
|
||||
if(!has_children) {
|
||||
//No children, just put the item there.
|
||||
int mid2 = nextid++;
|
||||
otheritems.push_back(branches_menu::miteminfo(root->AppendCheckItem(mid2,
|
||||
towxstring(namemap[i])), false, root));
|
||||
branch_ids[mid2] = i;
|
||||
root->FindItem(mid2)->Check(i == curbranch);
|
||||
} else {
|
||||
//Has children. Make a menu.
|
||||
wxMenu* m = new wxMenu();
|
||||
otheritems.push_back(branches_menu::miteminfo(root->AppendSubMenu(m,
|
||||
towxstring(namemap[i])), true, root));
|
||||
menus.push_back(m);
|
||||
build_menus(m, i, otheritems, menus, namemap, childmap, branch_ids, nextid,
|
||||
curbranch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
branches_menu::branches_menu(wxWindow* win, int wxid_low, int wxid_high)
|
||||
{
|
||||
pwin = win;
|
||||
wxid_range_low = wxid_low;
|
||||
wxid_range_high = wxid_high;
|
||||
win->Connect(wxid_low, wxid_high, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(branches_menu::on_select), NULL, this);
|
||||
branchchange.set(notify_branch_change, [this]() { runuifun([this]() { this->update(); }); });
|
||||
}
|
||||
|
||||
branches_menu::~branches_menu()
|
||||
{
|
||||
}
|
||||
|
||||
void branches_menu::on_select(wxCommandEvent& e)
|
||||
{
|
||||
int id = e.GetId();
|
||||
if(id < wxid_range_low || id > wxid_range_high) return;
|
||||
if(id == wxid_range_low) {
|
||||
//Configure.
|
||||
branch_config* bcfg = new branch_config(pwin);
|
||||
bcfg->ShowModal();
|
||||
bcfg->Destroy();
|
||||
return;
|
||||
}
|
||||
if(!branch_ids.count(id)) return;
|
||||
uint64_t bid = branch_ids[id];
|
||||
std::string err;
|
||||
runemufn_async([this, bid]() {
|
||||
auto p = project_get();
|
||||
try {
|
||||
if(p) p->set_current_branch(bid);
|
||||
} catch(std::exception& e) {
|
||||
std::string err = e.what();
|
||||
runuifun([this, err]() {
|
||||
show_message_ok(this->pwin, "Error changing branch", "Can't change branch: " +
|
||||
err, wxICON_EXCLAMATION);
|
||||
});
|
||||
}
|
||||
if(p) p->flush();
|
||||
update_movie_state();
|
||||
});
|
||||
}
|
||||
|
||||
void branches_menu::update()
|
||||
{
|
||||
std::map<uint64_t, std::string> namemap;
|
||||
std::map<uint64_t, std::set<uint64_t>> childmap;
|
||||
runemufn([&namemap, &childmap]() {
|
||||
auto p = project_get();
|
||||
if(!p) return;
|
||||
fill_namemap(*p, 0, namemap, childmap);
|
||||
});
|
||||
//First destroy everything that isn't a menu.
|
||||
for(auto i : otheritems)
|
||||
i.parent->Delete(i.item);
|
||||
//Then kill all menus.
|
||||
for(auto i : menus)
|
||||
delete i;
|
||||
otheritems.clear();
|
||||
menus.clear();
|
||||
branch_ids.clear();
|
||||
if(namemap.empty()) {
|
||||
if(disabler_fn) disabler_fn(false);
|
||||
return;
|
||||
}
|
||||
//Okay, cleared. Rebuild things.
|
||||
otheritems.push_back(miteminfo(Append(wxid_range_low, towxstring("Edit branches")), false, this));
|
||||
otheritems.push_back(miteminfo(AppendSeparator(), false, this));
|
||||
int ass_id = wxid_range_low + 1;
|
||||
build_menus(this, 0, otheritems, menus, namemap, childmap, branch_ids, ass_id,
|
||||
project_get()->get_current_branch());
|
||||
if(disabler_fn) disabler_fn(true);
|
||||
}
|
||||
|
||||
bool branches_menu::any_enabled()
|
||||
{
|
||||
return project_get();
|
||||
}
|
|
@ -227,7 +227,7 @@ void wxeditor_authors::on_ok(wxCommandEvent& e)
|
|||
proj->directory = dir;
|
||||
proj->name = prjname;
|
||||
proj->luascripts = luascriptlist;
|
||||
project_flush(proj);
|
||||
proj->flush();
|
||||
//For save status to immediately update.
|
||||
do_flush_slotinfo();
|
||||
update_movie_state();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "platform/wxwidgets/window-romload.hpp"
|
||||
#include "platform/wxwidgets/settings-common.hpp"
|
||||
#include "platform/wxwidgets/menu_tracelog.hpp"
|
||||
#include "platform/wxwidgets/menu_branches.hpp"
|
||||
|
||||
#include "core/audioapi.hpp"
|
||||
#include "core/command.hpp"
|
||||
|
@ -134,6 +135,8 @@ enum
|
|||
wxID_TRACELOG_FIRST,
|
||||
wxID_TRACELOG_LAST = wxID_TRACELOG_FIRST + 256,
|
||||
wxID_PLUGIN_MANAGER,
|
||||
wxID_BRANCH_FIRST,
|
||||
wxID_BRANCH_LAST = wxID_BRANCH_FIRST + 10240,
|
||||
};
|
||||
|
||||
|
||||
|
@ -815,9 +818,9 @@ void wxwin_mainwindow::menu_special(wxString name, wxMenu* menu)
|
|||
current_menu = NULL;
|
||||
}
|
||||
|
||||
void wxwin_mainwindow::menu_special_sub(wxString name, wxMenu* menu)
|
||||
wxMenuItem* wxwin_mainwindow::menu_special_sub(wxString name, wxMenu* menu)
|
||||
{
|
||||
current_menu->AppendSubMenu(menu, name);
|
||||
return current_menu->AppendSubMenu(menu, name);
|
||||
}
|
||||
|
||||
void wxwin_mainwindow::menu_entry(int id, wxString name)
|
||||
|
@ -1062,6 +1065,12 @@ wxwin_mainwindow::wxwin_mainwindow()
|
|||
recent_script_selected));
|
||||
menu_separator();
|
||||
menu_entry(wxID_CONFLICTRESOLUTION, wxT("Conflict resolution"));
|
||||
menu_separator();
|
||||
branches_menu* brlist;
|
||||
auto brlist_item = menu_special_sub(wxT("Branches"), brlist = new branches_menu(this, wxID_BRANCH_FIRST,
|
||||
wxID_BRANCH_LAST));
|
||||
brlist->set_disabler([brlist_item](bool enabled) { brlist_item->Enable(enabled); });
|
||||
brlist->update();
|
||||
menu_end_sub();
|
||||
menu_start_sub(wxT("Save"));
|
||||
menu_entry(wxID_SAVE_STATE, wxT("State..."));
|
||||
|
@ -1132,7 +1141,9 @@ wxwin_mainwindow::wxwin_mainwindow()
|
|||
menu_entry(wxID_MEMORY_SEARCH, wxT("Memory Search..."));
|
||||
menu_entry(wxID_HEXEDITOR, wxT("Memory editor..."));
|
||||
tracelog_menu* trlog;
|
||||
menu_special_sub(wxT("Trace log"), trlog = new tracelog_menu(this, wxID_TRACELOG_FIRST, wxID_TRACELOG_LAST));
|
||||
auto trlog_item = menu_special_sub(wxT("Trace log"), trlog = new tracelog_menu(this, wxID_TRACELOG_FIRST,
|
||||
wxID_TRACELOG_LAST));
|
||||
trlog->set_disabler([trlog_item](bool enabled) { trlog_item->Enable(enabled); });
|
||||
trlog->update();
|
||||
menu_separator();
|
||||
menu_entry(wxID_MOVIE_EDIT, wxT("Edit movie..."));
|
||||
|
@ -1270,6 +1281,8 @@ void wxwin_mainwindow::update_statusbar(const std::map<std::string, std::u32stri
|
|||
std::u32string macros = read_variable_map(vars, "!macros");
|
||||
if(macros.length())
|
||||
s << U" Macros: " << macros;
|
||||
if(vars.count("!branch"))
|
||||
s << U" Branch: " << read_variable_map(vars, "!branch");
|
||||
|
||||
statusbar->SetStatusText(towxstring(s.str()));
|
||||
} catch(std::exception& e) {
|
||||
|
|
|
@ -280,7 +280,7 @@ namespace
|
|||
}
|
||||
no_watch:
|
||||
project_info* pinfo2 = new project_info(pinfo);
|
||||
project_flush(pinfo2);
|
||||
pinfo2->flush();
|
||||
project_info* old_proj = project_get();
|
||||
project_set(pinfo2, true);
|
||||
if(old_proj)
|
||||
|
|
|
@ -51,6 +51,7 @@ void tracelog_menu::update()
|
|||
items[id]->Check(debug_tracelogging(id));
|
||||
id++;
|
||||
}
|
||||
if(disabler_fn) disabler_fn(!_items.empty());
|
||||
}
|
||||
|
||||
bool tracelog_menu::any_enabled()
|
||||
|
|
Loading…
Add table
Reference in a new issue