Fix crash if text containing \n is printed at nonzero x
[lsnes.git] / src / platform / wxwidgets / editor-subtitles.cpp
blobc879491be67240e8267ce9c803cc01cc7ca54538
1 #include <wx/wx.h>
2 #include <wx/event.h>
3 #include <wx/control.h>
4 #include <wx/combobox.h>
5 #include <wx/radiobut.h>
7 #include "core/subtitles.hpp"
8 #include "core/instance.hpp"
9 #include "core/moviedata.hpp"
10 #include "core/dispatch.hpp"
11 #include "library/string.hpp"
13 #include "platform/wxwidgets/platform.hpp"
14 #include <cstdint>
17 namespace
19 struct subdata
21 uint64_t first;
22 uint64_t last;
23 std::string text;
27 class wxeditor_subtitles : public wxFrame
29 public:
30 wxeditor_subtitles(wxWindow* parent, emulator_instance& _inst);
31 ~wxeditor_subtitles() throw();
32 bool ShouldPreventAppExit() const;
33 void on_change(wxCommandEvent& e);
34 void on_add(wxCommandEvent& e);
35 void on_edit(wxCommandEvent& e);
36 void on_delete(wxCommandEvent& e);
37 void on_close(wxCommandEvent& e);
38 void on_wclose(wxCloseEvent& e);
39 void refresh();
40 private:
41 emulator_instance& inst;
42 bool closing;
43 wxListBox* subs;
44 wxTextCtrl* subtext;
45 wxButton* add;
46 wxButton* edit;
47 wxButton* _delete;
48 wxButton* close;
49 std::map<int, subdata> subtexts;
50 struct dispatch::target<> subchange;
53 namespace
56 class wxeditor_subtitles_subtitle : public wxDialog
58 public:
59 wxeditor_subtitles_subtitle(wxWindow* parent, subdata d);
60 void on_change(wxCommandEvent& e);
61 void on_cancel(wxCommandEvent& e);
62 void on_ok(wxCommandEvent& e);
63 subdata get_result();
64 private:
65 wxTextCtrl* first;
66 wxTextCtrl* last;
67 wxTextCtrl* text;
68 wxButton* ok;
69 wxButton* cancel;
72 wxeditor_subtitles_subtitle::wxeditor_subtitles_subtitle(wxWindow* parent, subdata d)
73 : wxDialog(parent, wxID_ANY, wxT("lsnes: Edit subtitle"), wxDefaultPosition, wxSize(-1, -1))
75 CHECK_UI_THREAD;
76 Centre();
77 wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
78 SetSizer(top_s);
80 wxFlexGridSizer* data_s = new wxFlexGridSizer(3, 2, 0, 0);
81 data_s->Add(new wxStaticText(this, wxID_ANY, wxT("First frame:")));
82 data_s->Add(first = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(200, -1)));
83 data_s->Add(new wxStaticText(this, wxID_ANY, wxT("Last frame:")));
84 data_s->Add(last = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(200, -1)));
85 data_s->Add(new wxStaticText(this, wxID_ANY, wxT("Text:")));
86 data_s->Add(text = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(400, -1)));
87 top_s->Add(data_s, 1, wxGROW);
89 first->Connect(wxEVT_COMMAND_TEXT_UPDATED,
90 wxCommandEventHandler(wxeditor_subtitles_subtitle::on_change), NULL, this);
91 last->Connect(wxEVT_COMMAND_TEXT_UPDATED,
92 wxCommandEventHandler(wxeditor_subtitles_subtitle::on_change), NULL, this);
93 text->Connect(wxEVT_COMMAND_TEXT_UPDATED,
94 wxCommandEventHandler(wxeditor_subtitles_subtitle::on_change), NULL, this);
96 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
97 pbutton_s->AddStretchSpacer();
98 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")), 0, wxGROW);
99 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")), 0, wxGROW);
100 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles_subtitle::on_ok),
101 NULL, this);
102 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
103 wxCommandEventHandler(wxeditor_subtitles_subtitle::on_cancel), NULL, this);
104 top_s->Add(pbutton_s, 0, wxGROW);
106 pbutton_s->SetSizeHints(this);
107 top_s->SetSizeHints(this);
109 first->SetValue(towxstring((stringfmt() << d.first).str()));
110 last->SetValue(towxstring((stringfmt() << d.last).str()));
111 text->SetValue(towxstring(d.text));
112 Fit();
115 void wxeditor_subtitles_subtitle::on_change(wxCommandEvent& e)
117 CHECK_UI_THREAD;
118 bool valid = true;
119 std::string _first = tostdstring(first->GetValue());
120 std::string _last = tostdstring(last->GetValue());
121 std::string _text = tostdstring(text->GetValue());
122 valid = valid && regex_match("[0-9]{1,19}", _first);
123 valid = valid && regex_match("[0-9]{1,19}", _last);
124 valid = valid && (_text != "");
125 ok->Enable(valid);
128 void wxeditor_subtitles_subtitle::on_cancel(wxCommandEvent& e)
130 CHECK_UI_THREAD;
131 EndModal(wxID_CANCEL);
134 void wxeditor_subtitles_subtitle::on_ok(wxCommandEvent& e)
136 CHECK_UI_THREAD;
137 EndModal(wxID_OK);
140 subdata wxeditor_subtitles_subtitle::get_result()
142 CHECK_UI_THREAD;
143 subdata d;
144 d.first = parse_value<uint64_t>(tostdstring(first->GetValue()));
145 d.last = parse_value<uint64_t>(tostdstring(last->GetValue()));
146 d.text = tostdstring(text->GetValue());
147 return d;
150 bool edit_subtext(wxWindow* w, struct subdata& d)
152 CHECK_UI_THREAD;
153 bool res = false;
154 wxeditor_subtitles_subtitle* editor = NULL;
155 try {
156 editor = new wxeditor_subtitles_subtitle(w, d);
157 int ret = editor->ShowModal();
158 if(ret == wxID_OK) {
159 d = editor->get_result();
160 res = true;
162 } catch(...) {
163 return false;
165 if(editor)
166 editor->Destroy();
167 return res;
172 wxeditor_subtitles::wxeditor_subtitles(wxWindow* parent, emulator_instance& _inst)
173 : wxFrame(NULL, wxID_ANY, wxT("lsnes: Edit subtitles"), wxDefaultPosition, wxSize(-1, -1)), inst(_inst)
175 CHECK_UI_THREAD;
176 closing = false;
177 Centre();
178 wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
179 SetSizer(top_s);
181 //TODO: Apppropriate controls.
182 top_s->Add(subs = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400), 0, NULL,
183 wxLB_SINGLE), 1, wxGROW);
184 subs->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
185 wxCommandEventHandler(wxeditor_subtitles::on_change), NULL, this);
187 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
188 pbutton_s->AddStretchSpacer();
189 pbutton_s->Add(add = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
190 pbutton_s->Add(edit = new wxButton(this, wxID_ANY, wxT("Edit")), 0, wxGROW);
191 pbutton_s->Add(_delete = new wxButton(this, wxID_ANY, wxT("Delete")), 0, wxGROW);
192 pbutton_s->Add(close = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
193 add->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_add), NULL, this);
194 edit->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_edit), NULL, this);
195 _delete->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_delete), NULL,
196 this);
197 close->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_subtitles::on_close), NULL, this);
198 top_s->Add(pbutton_s, 0, wxGROW);
200 pbutton_s->SetSizeHints(this);
201 top_s->SetSizeHints(this);
202 Fit();
203 subchange.set(inst.dispatch->subtitle_change, [this]() { runuifun([this]() -> void {
204 this->refresh(); }); });
205 refresh();
208 wxeditor_subtitles::~wxeditor_subtitles() throw()
212 bool wxeditor_subtitles::ShouldPreventAppExit() const
214 return false;
217 void wxeditor_subtitles::on_close(wxCommandEvent& e)
219 CHECK_UI_THREAD;
220 closing = true;
221 Destroy();
224 void wxeditor_subtitles::on_wclose(wxCloseEvent& e)
226 closing = true;
229 void wxeditor_subtitles::refresh()
231 CHECK_UI_THREAD;
232 if(closing)
233 return;
234 std::map<std::pair<uint64_t, uint64_t>, std::string> _subtitles;
235 inst.iqueue->run([&_subtitles]() -> void {
236 auto keys = CORE().subtitles->get_all();
237 for(auto i : keys)
238 _subtitles[i] = CORE().subtitles->get(i.first, i.second);
240 int sel = subs->GetSelection();
241 bool found = (subtexts.count(sel) != 0);
242 subdata matching = subtexts[sel];
243 subs->Clear();
244 unsigned num = 0;
245 subtexts.clear();
246 for(auto i : _subtitles) {
247 subdata newdata;
248 newdata.first = i.first.first;
249 newdata.last = i.first.second;
250 newdata.text = i.second;
251 subtexts[num++] = newdata;
252 std::string s = (stringfmt() << i.first.first << "-" << i.first.second << ": " << i.second).str();
253 subs->Append(towxstring(s));
255 for(size_t i = 0; i < subs->GetCount(); i++)
256 if(subtexts[i].first == matching.first && subtexts[i].last == matching.last)
257 subs->SetSelection(i);
258 if(subs->GetSelection() == wxNOT_FOUND && sel < (ssize_t)subs->GetCount())
259 subs->SetSelection(sel);
260 sel = subs->GetSelection();
261 found = (subtexts.count(sel) != 0);
262 edit->Enable(found);
263 _delete->Enable(found);
266 void wxeditor_subtitles::on_change(wxCommandEvent& e)
268 CHECK_UI_THREAD;
269 if(closing)
270 return;
271 int sel = subs->GetSelection();
272 bool found = (subtexts.count(sel) != 0);
273 edit->Enable(found);
274 _delete->Enable(found);
277 void wxeditor_subtitles::on_add(wxCommandEvent& e)
279 CHECK_UI_THREAD;
280 if(closing)
281 return;
282 subdata t;
283 t.first = 0;
284 t.last = 0;
285 t.text = "";
286 if(edit_subtext(this, t))
287 inst.subtitles->set(t.first, t.last, t.text);
290 void wxeditor_subtitles::on_edit(wxCommandEvent& e)
292 CHECK_UI_THREAD;
293 if(closing)
294 return;
295 int sel = subs->GetSelection();
296 if(!subtexts.count(sel))
297 return;
298 auto t = subtexts[sel];
299 auto old = t;
300 if(edit_subtext(this, t)) {
301 inst.subtitles->set(old.first, old.last, "");
302 inst.subtitles->set(t.first, t.last, t.text);
306 void wxeditor_subtitles::on_delete(wxCommandEvent& e)
308 CHECK_UI_THREAD;
309 if(closing)
310 return;
311 int sel = subs->GetSelection();
312 if(!subtexts.count(sel))
313 return;
314 auto t = subtexts[sel];
315 inst.subtitles->set(t.first, t.last, "");
318 void wxeditor_subtitles_display(wxWindow* parent, emulator_instance& inst)
320 CHECK_UI_THREAD;
321 wxFrame* editor;
322 if(!inst.mlogic) {
323 show_message_ok(parent, "No movie", "Can't edit subtitles of nonexistent movie", wxICON_EXCLAMATION);
324 return;
326 try {
327 editor = new wxeditor_subtitles(parent, inst);
328 editor->Show();
329 } catch(...) {