Live subtitle editing

This commit is contained in:
Ilari Liusvaara 2013-01-24 04:16:38 +02:00
parent ab204556da
commit 5c3e48348b
5 changed files with 293 additions and 84 deletions

View file

@ -423,6 +423,17 @@ public:
* Call on_voice_stream_change on all objects.
*/
static void do_voice_stream_change() throw();
/**
* Notify about changes to subtitles.
*
* Default implementation does nothing.
*/
virtual void on_subtitle_change();
/**
* Call on_subtitle_change on all objects.
*/
static void do_subtitle_change() throw();
protected:
/**
* Call to indicate this target is interested in sound sample data.

View file

@ -554,3 +554,19 @@ void information_dispatch::do_voice_stream_change() throw()
END_EH_BLOCK(i, "on_voice_stream_change");
}
}
void information_dispatch::on_subtitle_change()
{
//Do nothing.
}
void information_dispatch::do_subtitle_change() throw()
{
if(in_global_ctors())
return;
for(auto& i : dispatch()) {
START_EH_BLOCK
i->on_subtitle_change();
END_EH_BLOCK(i, "on_subtitle_change");
}
}

View file

@ -1,4 +1,6 @@
#include "core/command.hpp"
#include "core/dispatch.hpp"
#include "core/framebuffer.hpp"
#include "core/moviedata.hpp"
#include "core/subtitles.hpp"
#include "core/window.hpp"
@ -109,6 +111,8 @@ namespace
our_movie.subtitles.erase(key);
else
our_movie.subtitles[key] = s_unescape(text);
information_dispatch::do_subtitle_change();
redraw_framebuffer();
});
function_ptr_command<> list_subtitle("list-subtitle", "List the subtitles",
@ -229,4 +233,6 @@ void set_subtitle_for(uint64_t f, uint64_t l, const std::string& x)
our_movie.subtitles.erase(key);
else
our_movie.subtitles[key] = s_unescape(x);
information_dispatch::do_subtitle_change();
redraw_framebuffer();
}

View file

@ -639,8 +639,6 @@ void platform::queue(const std::string& c) throw(std::bad_alloc)
void platform::queue(void (*f)(void* arg), void* arg, bool sync) throw(std::bad_alloc)
{
if(sync && queue_synchronous_fn_warning)
std::cerr << "WARNING: Synchronous queue in callback to UI, this may deadlock!" << std::endl;
init_threading();
mutex::holder h(*queue_lock);
++next_function;

View file

@ -10,57 +10,204 @@
#include <wx/radiobut.h>
#include "library/string.hpp"
#include "core/dispatch.hpp"
#include "core/emucore.hpp"
#include "core/subtitles.hpp"
class wxeditor_subtitles : public wxDialog
namespace
{
struct subdata
{
uint64_t first;
uint64_t last;
std::string text;
};
}
class wxeditor_subtitles : public wxFrame
{
public:
wxeditor_subtitles(wxWindow* parent);
~wxeditor_subtitles() throw();
bool ShouldPreventAppExit() const;
void on_subtitles_change(wxCommandEvent& e);
void on_ok(wxCommandEvent& e);
void on_cancel(wxCommandEvent& e);
void on_change(wxCommandEvent& e);
void on_add(wxCommandEvent& e);
void on_edit(wxCommandEvent& e);
void on_delete(wxCommandEvent& e);
void on_close(wxCommandEvent& e);
void on_wclose(wxCloseEvent& e);
void refresh();
private:
wxTextCtrl* subs;
wxButton* ok;
wxButton* cancel;
struct refresh_listener : public information_dispatch
{
refresh_listener(wxeditor_subtitles* v)
: information_dispatch("subtitle-editor-change-listener")
{
obj = v;
}
void on_subtitle_change()
{
wxeditor_subtitles* _obj = obj;
runuifun([_obj]() -> void { _obj->refresh(); });
}
wxeditor_subtitles* obj;
};
bool closing;
wxListBox* subs;
wxTextCtrl* subtext;
wxButton* add;
wxButton* edit;
wxButton* _delete;
wxButton* close;
std::map<int, subdata> subtexts;
refresh_listener* rlistener;
};
wxeditor_subtitles::wxeditor_subtitles(wxWindow* parent)
: wxDialog(parent, wxID_ANY, wxT("lsnes: Edit subtitles"), wxDefaultPosition, wxSize(-1, -1))
namespace
{
class wxeditor_subtitles_subtitle : public wxDialog
{
public:
wxeditor_subtitles_subtitle(wxWindow* parent, subdata d);
void on_change(wxCommandEvent& e);
void on_cancel(wxCommandEvent& e);
void on_ok(wxCommandEvent& e);
subdata get_result();
private:
wxTextCtrl* first;
wxTextCtrl* last;
wxTextCtrl* text;
wxButton* ok;
wxButton* cancel;
};
wxeditor_subtitles_subtitle::wxeditor_subtitles_subtitle(wxWindow* parent, subdata d)
: wxDialog(parent, wxID_ANY, wxT("lsnes: Edit subtitle"), wxDefaultPosition, wxSize(-1, -1))
{
Centre();
wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
SetSizer(top_s);
top_s->Add(subs = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(400, 300),
wxTE_MULTILINE), 1, wxGROW);
subs->Connect(wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler(wxeditor_subtitles::on_subtitles_change), NULL, this);
wxFlexGridSizer* data_s = new wxFlexGridSizer(3, 2, 0, 0);
data_s->Add(new wxStaticText(this, wxID_ANY, wxT("First frame:")));
data_s->Add(first = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(200, -1)));
data_s->Add(new wxStaticText(this, wxID_ANY, wxT("Last frame:")));
data_s->Add(last = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(200, -1)));
data_s->Add(new wxStaticText(this, wxID_ANY, wxT("Text:")));
data_s->Add(text = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(400, -1)));
top_s->Add(data_s, 1, wxGROW);
first->Connect(wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler(wxeditor_subtitles_subtitle::on_change), NULL, this);
last->Connect(wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler(wxeditor_subtitles_subtitle::on_change), NULL, this);
text->Connect(wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler(wxeditor_subtitles_subtitle::on_change), NULL, this);
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->AddStretchSpacer();
pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")), 0, wxGROW);
pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")), 0, wxGROW);
ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_ok), NULL, this);
cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_cancel), NULL,
ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles_subtitle::on_ok),
NULL, this);
cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxeditor_subtitles_subtitle::on_cancel), NULL, this);
top_s->Add(pbutton_s, 0, wxGROW);
pbutton_s->SetSizeHints(this);
top_s->SetSizeHints(this);
first->SetValue(towxstring((stringfmt() << d.first).str()));
last->SetValue(towxstring((stringfmt() << d.last).str()));
text->SetValue(towxstring(d.text));
Fit();
}
void wxeditor_subtitles_subtitle::on_change(wxCommandEvent& e)
{
bool valid = true;
std::string _first = tostdstring(first->GetValue());
std::string _last = tostdstring(last->GetValue());
std::string _text = tostdstring(text->GetValue());
valid = valid && regex_match("[0-9]{1,19}", _first);
valid = valid && regex_match("[0-9]{1,19}", _last);
valid = valid && (_text != "");
ok->Enable(valid);
}
void wxeditor_subtitles_subtitle::on_cancel(wxCommandEvent& e)
{
EndModal(wxID_CANCEL);
}
void wxeditor_subtitles_subtitle::on_ok(wxCommandEvent& e)
{
EndModal(wxID_OK);
}
subdata wxeditor_subtitles_subtitle::get_result()
{
subdata d;
d.first = parse_value<uint64_t>(tostdstring(first->GetValue()));
d.last = parse_value<uint64_t>(tostdstring(last->GetValue()));
d.text = tostdstring(text->GetValue());
return d;
}
bool edit_subtext(wxWindow* w, struct subdata& d)
{
wxeditor_subtitles_subtitle* editor = NULL;
try {
editor = new wxeditor_subtitles_subtitle(w, d);
int ret = editor->ShowModal();
if(ret == wxID_OK)
d = editor->get_result();
} catch(...) {
}
if(editor)
editor->Destroy();
}
}
wxeditor_subtitles::wxeditor_subtitles(wxWindow* parent)
: wxFrame(NULL, wxID_ANY, wxT("lsnes: Edit subtitles"), wxDefaultPosition, wxSize(-1, -1))
{
closing = false;
Centre();
wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
SetSizer(top_s);
//TODO: Apppropriate controls.
top_s->Add(subs = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400), 0, NULL,
wxLB_SINGLE), 1, wxGROW);
subs->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
wxCommandEventHandler(wxeditor_subtitles::on_change), NULL, this);
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->AddStretchSpacer();
pbutton_s->Add(add = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
pbutton_s->Add(edit = new wxButton(this, wxID_ANY, wxT("Edit")), 0, wxGROW);
pbutton_s->Add(_delete = new wxButton(this, wxID_ANY, wxT("Delete")), 0, wxGROW);
pbutton_s->Add(close = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
add->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_add), NULL, this);
edit->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_edit), NULL, this);
_delete->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_delete), NULL,
this);
close->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_close), NULL, this);
top_s->Add(pbutton_s, 0, wxGROW);
pbutton_s->SetSizeHints(this);
top_s->SetSizeHints(this);
Fit();
rlistener = new refresh_listener(this);
refresh();
}
std::string txt = "";
runemufn([&txt]() {
for(auto i : get_subtitles()) {
std::ostringstream line;
line << i.first << " " << i.second << " " << get_subtitle_for(i.first, i.second) << std::endl;
txt = txt + line.str();
}
});
subs->SetValue(towxstring(txt));
wxeditor_subtitles::~wxeditor_subtitles() throw()
{
delete rlistener;
}
bool wxeditor_subtitles::ShouldPreventAppExit() const
@ -68,76 +215,107 @@ bool wxeditor_subtitles::ShouldPreventAppExit() const
return false;
}
void wxeditor_subtitles::on_subtitles_change(wxCommandEvent& e)
void wxeditor_subtitles::on_close(wxCommandEvent& e)
{
std::string txt = tostdstring(subs->GetValue());
std::string line;
while(txt != "") {
extract_token(txt, line, "\n");
istrip_CR(line);
if(line == "")
continue;
auto r = regex("([0-9]+)[ \t]+([0-9]+)[ \t]+(.+)", line);
if(!r) {
ok->Disable();
return;
}
try {
parse_value<uint64_t>(r[1]);
parse_value<uint64_t>(r[2]);
} catch(...) {
ok->Disable();
return;
}
}
ok->Enable();
closing = true;
Destroy();
}
void wxeditor_subtitles::on_ok(wxCommandEvent& e)
void wxeditor_subtitles::on_wclose(wxCloseEvent& e)
{
std::map<std::pair<uint64_t, uint64_t>, std::string> data;
runemufn([&data]() {
for(auto i : get_subtitles())
data[std::make_pair(i.first, i.second)] = "";
});
std::string txt = tostdstring(subs->GetValue());
std::string line;
while(txt != "") {
extract_token(txt, line, "\n");
istrip_CR(line);
if(line == "")
continue;
auto r = regex("([0-9]+)[ \t]+([0-9]+)[ \t]+(.+)", line);
if(!r)
return;
try {
uint64_t f = parse_value<uint64_t>(r[1]);
uint64_t l = parse_value<uint64_t>(r[2]);
data[std::make_pair(f, l)] = r[3];
} catch(...) {
return;
}
}
runemufn([&data]() {
for(auto i : data)
set_subtitle_for(i.first.first, i.first.second, i.second);
});
EndModal(wxID_OK);
closing = true;
}
void wxeditor_subtitles::on_cancel(wxCommandEvent& e)
void wxeditor_subtitles::refresh()
{
EndModal(wxID_CANCEL);
if(closing)
return;
std::map<std::pair<uint64_t, uint64_t>, std::string> _subtitles;
runemufn([&_subtitles]() -> void {
auto keys = get_subtitles();
for(auto i : keys)
_subtitles[i] = get_subtitle_for(i.first, i.second);
});
int sel = subs->GetSelection();
bool found = (subtexts.count(sel) != 0);
subdata matching = subtexts[sel];
subs->Clear();
unsigned num = 0;
subtexts.clear();
for(auto i : _subtitles) {
subdata newdata;
newdata.first = i.first.first;
newdata.last = i.first.second;
newdata.text = i.second;
subtexts[num++] = newdata;
std::string s = (stringfmt() << i.first.first << "-" << i.first.second << ": " << i.second).str();
subs->Append(towxstring(s));
}
for(int i = 0; i < subs->GetCount(); i++)
if(subtexts[i].first == matching.first && subtexts[i].last == matching.last)
subs->SetSelection(i);
if(subs->GetSelection() == wxNOT_FOUND && sel < subs->GetCount())
subs->SetSelection(sel);
sel = subs->GetSelection();
found = (subtexts.count(sel) != 0);
edit->Enable(found);
_delete->Enable(found);
}
void wxeditor_subtitles::on_change(wxCommandEvent& e)
{
if(closing)
return;
int sel = subs->GetSelection();
bool found = (subtexts.count(sel) != 0);
edit->Enable(found);
_delete->Enable(found);
}
void wxeditor_subtitles::on_add(wxCommandEvent& e)
{
if(closing)
return;
subdata t;
t.first = 0;
t.last = 0;
t.text = "";
if(edit_subtext(this, t))
set_subtitle_for(t.first, t.last, t.text);
}
void wxeditor_subtitles::on_edit(wxCommandEvent& e)
{
if(closing)
return;
int sel = subs->GetSelection();
if(!subtexts.count(sel))
return;
auto t = subtexts[sel];
auto old = t;
if(edit_subtext(this, t)) {
set_subtitle_for(old.first, old.last, "");
set_subtitle_for(t.first, t.last, t.text);
}
}
void wxeditor_subtitles::on_delete(wxCommandEvent& e)
{
if(closing)
return;
int sel = subs->GetSelection();
if(!subtexts.count(sel))
return;
auto t = subtexts[sel];
set_subtitle_for(t.first, t.last, "");
}
void wxeditor_subtitles_display(wxWindow* parent)
{
modal_pause_holder hld;
wxDialog* editor;
wxFrame* editor;
try {
editor = new wxeditor_subtitles(parent);
editor->ShowModal();
editor->Show();
} catch(...) {
}
editor->Destroy();
}