forked from mia/Aegisub
Use boost::flyweight to intern the wxString members of AssDialogue
100 no-op non-amend commits on a subtitle file with 6689 dialogue lines, with the undo limit set to 100: Without flyweight: No video open: Initial memory usage: 30.6 MB Final memory usage: 498.0 MB Elapsed time: 6.3 seconds Video open, using libass: Initial memory usage: 54.3 MB Final memory usage: 653.3 MB Elapsed time: 23.7 seconds With flyweight: No video open: Initial memory usage: 26.0 MB Final memory usage: 104.5 MB Elapsed time: 3.0 seconds Video open, using libass: Initial memory usage: 46.7 MB Final memory usage: 251.8 MB Elapsed time: 13.0 seconds No video open: Memory usage: -79% Time: -52% Video open: Memory usage: -61.5% Time: -45% 100 no-op amend commits on a line in the middle of a subtitle file with 6689 dialogue lines, with video open: Without flyweight: Initial memory usage: 48.2 MB Final memory usage: 182.3 MB Elapsed time: 22.3 seconds With flyweight: Initial memory usage: 39.8 MB Final memory usage: 165.8 MB Elapsed time: 13.8 seconds Note: The large jump in memory usage here is due to that the benchmark is blocking the main thread, so at the end there are ~100 video frames waiting to be displayed.
This commit is contained in:
parent
c7c270cf12
commit
d5aae26d83
26 changed files with 144 additions and 133 deletions
|
@ -69,6 +69,7 @@
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <boost/flyweight.hpp>
|
||||||
#include <boost/ptr_container/ptr_vector.hpp>
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||||||
#include <boost/range/adaptor/filtered.hpp>
|
#include <boost/range/adaptor/filtered.hpp>
|
||||||
#include <boost/range/adaptor/indirected.hpp>
|
#include <boost/range/adaptor/indirected.hpp>
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/join.hpp>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
@ -47,6 +48,10 @@
|
||||||
|
|
||||||
#include <libaegisub/of_type_adaptor.h>
|
#include <libaegisub/of_type_adaptor.h>
|
||||||
|
|
||||||
|
std::size_t hash_value(wxString const& s) {
|
||||||
|
return wxStringHash()(s);
|
||||||
|
}
|
||||||
|
|
||||||
AssDialogue::AssDialogue()
|
AssDialogue::AssDialogue()
|
||||||
: AssEntry(wxString())
|
: AssEntry(wxString())
|
||||||
, Comment(false)
|
, Comment(false)
|
||||||
|
@ -128,15 +133,11 @@ bool AssDialogue::Parse(wxString const& rawData) {
|
||||||
|
|
||||||
// Get style
|
// Get style
|
||||||
if (!tkn.HasMoreTokens()) return false;
|
if (!tkn.HasMoreTokens()) return false;
|
||||||
Style = tkn.GetNextToken();
|
Style = tkn.GetNextToken().Trim(true).Trim(false);
|
||||||
Style.Trim(true);
|
|
||||||
Style.Trim(false);
|
|
||||||
|
|
||||||
// Get actor
|
// Get actor
|
||||||
if (!tkn.HasMoreTokens()) return false;
|
if (!tkn.HasMoreTokens()) return false;
|
||||||
Actor = tkn.GetNextToken();
|
Actor = tkn.GetNextToken().Trim(true).Trim(false);
|
||||||
Actor.Trim(true);
|
|
||||||
Actor.Trim(false);
|
|
||||||
|
|
||||||
// Get margins
|
// Get margins
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
@ -145,9 +146,7 @@ bool AssDialogue::Parse(wxString const& rawData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tkn.HasMoreTokens()) return false;
|
if (!tkn.HasMoreTokens()) return false;
|
||||||
Effect = tkn.GetNextToken();
|
Effect = tkn.GetNextToken().Trim(true).Trim(false);
|
||||||
Effect.Trim(true);
|
|
||||||
Effect.Trim(false);
|
|
||||||
|
|
||||||
// Get text
|
// Get text
|
||||||
Text = rawData.Mid(pos + tkn.GetPosition());
|
Text = rawData.Mid(pos + tkn.GetPosition());
|
||||||
|
@ -172,7 +171,7 @@ wxString AssDialogue::GetData(bool ssa) const {
|
||||||
s, a,
|
s, a,
|
||||||
Margin[0], Margin[1], Margin[2],
|
Margin[0], Margin[1], Margin[2],
|
||||||
e,
|
e,
|
||||||
Text);
|
Text.get());
|
||||||
|
|
||||||
// Make sure that final has no line breaks
|
// Make sure that final has no line breaks
|
||||||
str.Replace("\n", "");
|
str.Replace("\n", "");
|
||||||
|
@ -193,17 +192,17 @@ std::auto_ptr<boost::ptr_vector<AssDialogueBlock>> AssDialogue::ParseTags() cons
|
||||||
boost::ptr_vector<AssDialogueBlock> Blocks;
|
boost::ptr_vector<AssDialogueBlock> Blocks;
|
||||||
|
|
||||||
// Empty line, make an empty block
|
// Empty line, make an empty block
|
||||||
if (Text.empty()) {
|
if (Text.get().empty()) {
|
||||||
Blocks.push_back(new AssDialogueBlockPlain);
|
Blocks.push_back(new AssDialogueBlockPlain);
|
||||||
return Blocks.release();
|
return Blocks.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
int drawingLevel = 0;
|
int drawingLevel = 0;
|
||||||
|
|
||||||
for (size_t len = Text.size(), cur = 0; cur < len; ) {
|
for (size_t len = Text.get().size(), cur = 0; cur < len; ) {
|
||||||
// Overrides block
|
// Overrides block
|
||||||
if (Text[cur] == '{') {
|
if (Text.get()[cur] == '{') {
|
||||||
size_t end = Text.find('}', cur);
|
size_t end = Text.get().find('}', cur);
|
||||||
|
|
||||||
// VSFilter requires that override blocks be closed, while libass
|
// VSFilter requires that override blocks be closed, while libass
|
||||||
// does not. We match VSFilter here.
|
// does not. We match VSFilter here.
|
||||||
|
@ -212,7 +211,7 @@ std::auto_ptr<boost::ptr_vector<AssDialogueBlock>> AssDialogue::ParseTags() cons
|
||||||
|
|
||||||
++cur;
|
++cur;
|
||||||
// Get contents of block
|
// Get contents of block
|
||||||
wxString work = Text.substr(cur, end - cur);
|
wxString work = Text.get().substr(cur, end - cur);
|
||||||
cur = end + 1;
|
cur = end + 1;
|
||||||
|
|
||||||
if (work.size() && work.find('\\') == wxString::npos) {
|
if (work.size() && work.find('\\') == wxString::npos) {
|
||||||
|
@ -240,13 +239,13 @@ std::auto_ptr<boost::ptr_vector<AssDialogueBlock>> AssDialogue::ParseTags() cons
|
||||||
// Plain-text/drawing block
|
// Plain-text/drawing block
|
||||||
plain:
|
plain:
|
||||||
wxString work;
|
wxString work;
|
||||||
size_t end = Text.find('{', cur + 1);
|
size_t end = Text.get().find('{', cur + 1);
|
||||||
if (end == wxString::npos) {
|
if (end == wxString::npos) {
|
||||||
work = Text.substr(cur);
|
work = Text.get().substr(cur);
|
||||||
cur = len;
|
cur = len;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
work = Text.substr(cur, end - cur);
|
work = Text.get().substr(cur, end - cur);
|
||||||
cur = end;
|
cur = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,12 +264,12 @@ void AssDialogue::StripTags() {
|
||||||
|
|
||||||
void AssDialogue::StripTag(wxString const& tag_name) {
|
void AssDialogue::StripTag(wxString const& tag_name) {
|
||||||
boost::ptr_vector<AssDialogueBlock> blocks(ParseTags());
|
boost::ptr_vector<AssDialogueBlock> blocks(ParseTags());
|
||||||
Text.clear();
|
wxString new_text;
|
||||||
|
|
||||||
// Look for blocks
|
// Look for blocks
|
||||||
for (auto& block : blocks) {
|
for (auto& block : blocks) {
|
||||||
if (block.GetType() != BLOCK_OVERRIDE) {
|
if (block.GetType() != BLOCK_OVERRIDE) {
|
||||||
Text += block.GetText();
|
new_text += block.GetText();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,15 +281,16 @@ void AssDialogue::StripTag(wxString const& tag_name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!temp.empty())
|
if (!temp.empty())
|
||||||
Text += "{" + temp + "}";
|
new_text += "{" + temp + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text = new_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static wxString get_text(AssDialogueBlock &d) { return d.GetText(); }
|
||||||
void AssDialogue::UpdateText(boost::ptr_vector<AssDialogueBlock>& blocks) {
|
void AssDialogue::UpdateText(boost::ptr_vector<AssDialogueBlock>& blocks) {
|
||||||
if (blocks.empty()) return;
|
if (blocks.empty()) return;
|
||||||
Text.clear();
|
Text = join(blocks | boost::adaptors::transformed(get_text), wxS(""));
|
||||||
for (auto& block : blocks)
|
|
||||||
Text += block.GetText();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssDialogue::SetMarginString(wxString const& origvalue, int which) {
|
void AssDialogue::SetMarginString(wxString const& origvalue, int which) {
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
#include <libaegisub/exception.h>
|
#include <libaegisub/exception.h>
|
||||||
|
|
||||||
|
#include <boost/flyweight.hpp>
|
||||||
#include <boost/ptr_container/ptr_vector.hpp>
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -50,6 +51,8 @@ enum AssBlockType {
|
||||||
class AssOverrideParameter;
|
class AssOverrideParameter;
|
||||||
class AssOverrideTag;
|
class AssOverrideTag;
|
||||||
|
|
||||||
|
std::size_t hash_value(wxString const& s);
|
||||||
|
|
||||||
/// @class AssDialogueBlock
|
/// @class AssDialogueBlock
|
||||||
/// @brief AssDialogue Blocks
|
/// @brief AssDialogue Blocks
|
||||||
///
|
///
|
||||||
|
@ -131,13 +134,13 @@ public:
|
||||||
/// Ending time
|
/// Ending time
|
||||||
AssTime End;
|
AssTime End;
|
||||||
/// Style name
|
/// Style name
|
||||||
wxString Style;
|
boost::flyweight<wxString> Style;
|
||||||
/// Actor name
|
/// Actor name
|
||||||
wxString Actor;
|
boost::flyweight<wxString> Actor;
|
||||||
/// Effect name
|
/// Effect name
|
||||||
wxString Effect;
|
boost::flyweight<wxString> Effect;
|
||||||
/// Raw text data
|
/// Raw text data
|
||||||
wxString Text;
|
boost::flyweight<wxString> Text;
|
||||||
|
|
||||||
AssEntryGroup Group() const override { return ENTRY_DIALOGUE; }
|
AssEntryGroup Group() const override { return ENTRY_DIALOGUE; }
|
||||||
|
|
||||||
|
|
|
@ -607,15 +607,15 @@ void BaseGrid::GetRowStrings(int row, AssDialogue *line, bool *paint_columns, wx
|
||||||
|
|
||||||
// Hidden overrides
|
// Hidden overrides
|
||||||
if (replace) {
|
if (replace) {
|
||||||
strings[10].reserve(line->Text.size());
|
strings[10].reserve(line->Text.get().size());
|
||||||
size_t start = 0, pos;
|
size_t start = 0, pos;
|
||||||
while ((pos = line->Text.find('{', start)) != wxString::npos) {
|
while ((pos = line->Text.get().find('{', start)) != wxString::npos) {
|
||||||
strings[10] += line->Text.Mid(start, pos - start);
|
strings[10] += line->Text.get().Mid(start, pos - start);
|
||||||
strings[10] += rep_char;
|
strings[10] += rep_char;
|
||||||
start = line->Text.find('}', pos);
|
start = line->Text.get().find('}', pos);
|
||||||
if (start != wxString::npos) ++start;
|
if (start != wxString::npos) ++start;
|
||||||
}
|
}
|
||||||
strings[10] += line->Text.Mid(start);
|
strings[10] += line->Text.get().Mid(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show overrides
|
// Show overrides
|
||||||
|
|
|
@ -207,7 +207,7 @@ void set_tag(AssDialogue *line, boost::ptr_vector<AssDialogueBlock> &blocks, wxS
|
||||||
// Cursor is in a comment block, so try the previous block instead
|
// Cursor is in a comment block, so try the previous block instead
|
||||||
if (plain->GetText().StartsWith("{")) {
|
if (plain->GetText().StartsWith("{")) {
|
||||||
--blockn;
|
--blockn;
|
||||||
start = line->Text.rfind('{', start);
|
start = line->Text.get().rfind('{', start);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
@ -227,7 +227,7 @@ void set_tag(AssDialogue *line, boost::ptr_vector<AssDialogueBlock> &blocks, wxS
|
||||||
wxString insert = tag + value;
|
wxString insert = tag + value;
|
||||||
int shift = insert.size();
|
int shift = insert.size();
|
||||||
if (plain || blockn < 0) {
|
if (plain || blockn < 0) {
|
||||||
line->Text = line->Text.Left(start) + "{" + insert + "}" + line->Text.Mid(start);
|
line->Text = line->Text.get().Left(start) + "{" + insert + "}" + line->Text.get().Mid(start);
|
||||||
shift += 2;
|
shift += 2;
|
||||||
blocks = line->ParseTags();
|
blocks = line->ParseTags();
|
||||||
}
|
}
|
||||||
|
@ -681,11 +681,11 @@ static void combine_lines(agi::Context *c, void (*combiner)(AssDialogue *, AssDi
|
||||||
}
|
}
|
||||||
|
|
||||||
static void combine_karaoke(AssDialogue *first, AssDialogue *second) {
|
static void combine_karaoke(AssDialogue *first, AssDialogue *second) {
|
||||||
first->Text += wxString::Format("{\\k%d}%s", (second->Start - first->End) / 10, second->Text);
|
first->Text = wxString::Format("%s{\\k%d}%s", first->Text.get(), (second->Start - first->End) / 10, second->Text.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void combine_concat(AssDialogue *first, AssDialogue *second) {
|
static void combine_concat(AssDialogue *first, AssDialogue *second) {
|
||||||
first->Text += " " + second->Text;
|
first->Text = first->Text + " " + second->Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void combine_drop(AssDialogue *, AssDialogue *) { }
|
static void combine_drop(AssDialogue *, AssDialogue *) { }
|
||||||
|
|
|
@ -202,7 +202,7 @@ namespace {
|
||||||
|
|
||||||
void resample_line(resample_state *state, AssEntry &line) {
|
void resample_line(resample_state *state, AssEntry &line) {
|
||||||
AssDialogue *diag = dynamic_cast<AssDialogue*>(&line);
|
AssDialogue *diag = dynamic_cast<AssDialogue*>(&line);
|
||||||
if (diag && !(diag->Comment && (diag->Effect.StartsWith("template") || diag->Effect.StartsWith("code")))) {
|
if (diag && !(diag->Comment && (diag->Effect.get().StartsWith("template") || diag->Effect.get().StartsWith("code")))) {
|
||||||
boost::ptr_vector<AssDialogueBlock> blocks(diag->ParseTags());
|
boost::ptr_vector<AssDialogueBlock> blocks(diag->ParseTags());
|
||||||
|
|
||||||
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
|
for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
|
||||||
|
|
|
@ -243,6 +243,13 @@ void SearchReplaceEngine::FindNext() {
|
||||||
ReplaceNext(false);
|
ReplaceNext(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boost::flyweight<wxString> *get_text(AssDialogue *cur, int field) {
|
||||||
|
if (field == 0) return &cur->Text;
|
||||||
|
else if (field == 1) return &cur->Style;
|
||||||
|
else if (field == 2) return &cur->Actor;
|
||||||
|
else if (field == 3) return &cur->Effect;
|
||||||
|
else throw wxString("Invalid field");
|
||||||
|
}
|
||||||
|
|
||||||
void SearchReplaceEngine::ReplaceNext(bool DoReplace) {
|
void SearchReplaceEngine::ReplaceNext(bool DoReplace) {
|
||||||
if (!CanContinue) {
|
if (!CanContinue) {
|
||||||
|
@ -267,7 +274,6 @@ void SearchReplaceEngine::ReplaceNext(bool DoReplace) {
|
||||||
int start = curLine;
|
int start = curLine;
|
||||||
int nrows = context->subsGrid->GetRows();
|
int nrows = context->subsGrid->GetRows();
|
||||||
bool found = false;
|
bool found = false;
|
||||||
wxString *Text = nullptr;
|
|
||||||
size_t tempPos;
|
size_t tempPos;
|
||||||
int regFlags = wxRE_ADVANCED;
|
int regFlags = wxRE_ADVANCED;
|
||||||
if (!matchCase) {
|
if (!matchCase) {
|
||||||
|
@ -276,8 +282,9 @@ void SearchReplaceEngine::ReplaceNext(bool DoReplace) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for it
|
// Search for it
|
||||||
|
boost::flyweight<wxString> *Text = nullptr;
|
||||||
while (!found) {
|
while (!found) {
|
||||||
Text = GetText(context->subsGrid->GetDialogue(curLine), field);
|
Text = get_text(context->subsGrid->GetDialogue(curLine), field);
|
||||||
if (DoReplace && LastWasFind)
|
if (DoReplace && LastWasFind)
|
||||||
tempPos = pos;
|
tempPos = pos;
|
||||||
else
|
else
|
||||||
|
@ -287,7 +294,7 @@ void SearchReplaceEngine::ReplaceNext(bool DoReplace) {
|
||||||
if (isReg) {
|
if (isReg) {
|
||||||
wxRegEx regex (LookFor,regFlags);
|
wxRegEx regex (LookFor,regFlags);
|
||||||
if (regex.IsValid()) {
|
if (regex.IsValid()) {
|
||||||
if (regex.Matches(Text->Mid(tempPos))) {
|
if (regex.Matches(Text->get().Mid(tempPos))) {
|
||||||
size_t match_start;
|
size_t match_start;
|
||||||
regex.GetMatch(&match_start,&matchLen,0);
|
regex.GetMatch(&match_start,&matchLen,0);
|
||||||
pos = match_start + tempPos;
|
pos = match_start + tempPos;
|
||||||
|
@ -298,7 +305,7 @@ void SearchReplaceEngine::ReplaceNext(bool DoReplace) {
|
||||||
|
|
||||||
// Normal
|
// Normal
|
||||||
else {
|
else {
|
||||||
wxString src = Text->Mid(tempPos);
|
wxString src = Text->get().Mid(tempPos);
|
||||||
if (!matchCase) src.MakeLower();
|
if (!matchCase) src.MakeLower();
|
||||||
int textPos = src.Find(LookFor);
|
int textPos = src.Find(LookFor);
|
||||||
if (textPos != -1) {
|
if (textPos != -1) {
|
||||||
|
@ -325,16 +332,16 @@ void SearchReplaceEngine::ReplaceNext(bool DoReplace) {
|
||||||
if (DoReplace) {
|
if (DoReplace) {
|
||||||
// Replace with regular expressions
|
// Replace with regular expressions
|
||||||
if (isReg) {
|
if (isReg) {
|
||||||
wxString toReplace = Text->Mid(pos,matchLen);
|
wxString toReplace = Text->get().Mid(pos,matchLen);
|
||||||
wxRegEx regex(LookFor,regFlags);
|
wxRegEx regex(LookFor,regFlags);
|
||||||
regex.ReplaceFirst(&toReplace,ReplaceWith);
|
regex.ReplaceFirst(&toReplace,ReplaceWith);
|
||||||
*Text = Text->Left(pos) + toReplace + Text->Mid(pos+matchLen);
|
*Text = Text->get().Left(pos) + toReplace + Text->get().Mid(pos+matchLen);
|
||||||
replaceLen = toReplace.Length();
|
replaceLen = toReplace.Length();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal replace
|
// Normal replace
|
||||||
else {
|
else {
|
||||||
*Text = Text->Left(pos) + ReplaceWith + Text->Mid(pos+matchLen);
|
*Text = Text->get().Left(pos) + ReplaceWith + Text->get().Mid(pos+matchLen);
|
||||||
replaceLen = ReplaceWith.Length();
|
replaceLen = ReplaceWith.Length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +394,7 @@ void SearchReplaceEngine::ReplaceAll() {
|
||||||
if (inSel && hasSelection && !sel.count(diag))
|
if (inSel && hasSelection && !sel.count(diag))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
wxString *Text = GetText(diag, field);
|
boost::flyweight<wxString> *Text = get_text(diag, field);
|
||||||
|
|
||||||
// Regular expressions
|
// Regular expressions
|
||||||
if (isReg) {
|
if (isReg) {
|
||||||
|
@ -398,7 +405,9 @@ void SearchReplaceEngine::ReplaceAll() {
|
||||||
// A zero length match (such as '$') will always be replaced
|
// A zero length match (such as '$') will always be replaced
|
||||||
// maxMatches times, which is almost certainly not what the user
|
// maxMatches times, which is almost certainly not what the user
|
||||||
// wanted, so limit it to one replacement in that situation
|
// wanted, so limit it to one replacement in that situation
|
||||||
count += reg.Replace(Text, ReplaceWith, len > 0 ? 1000 : 1);
|
wxString repl(*Text);
|
||||||
|
count += reg.Replace(&repl, ReplaceWith, len > 0 ? 1000 : 1);
|
||||||
|
*Text = repl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Normal replace
|
// Normal replace
|
||||||
|
@ -424,8 +433,10 @@ void SearchReplaceEngine::ReplaceAll() {
|
||||||
*Text = Left + Right;
|
*Text = Left + Right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(Text->Contains(LookFor)) {
|
else if(Text->get().Contains(LookFor)) {
|
||||||
count += Text->Replace(LookFor, ReplaceWith);
|
wxString repl(*Text);
|
||||||
|
count += repl.Replace(LookFor, ReplaceWith);
|
||||||
|
*Text = repl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -475,12 +486,4 @@ void SearchReplaceEngine::OpenDialog (bool replace) {
|
||||||
hasReplace = replace;
|
hasReplace = replace;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString *SearchReplaceEngine::GetText(AssDialogue *cur, int field) {
|
|
||||||
if (field == 0) return &cur->Text;
|
|
||||||
else if (field == 1) return &cur->Style;
|
|
||||||
else if (field == 2) return &cur->Actor;
|
|
||||||
else if (field == 3) return &cur->Effect;
|
|
||||||
else throw wxString("Invalid field");
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchReplaceEngine Search;
|
SearchReplaceEngine Search;
|
||||||
|
|
|
@ -59,8 +59,6 @@ class SearchReplaceEngine {
|
||||||
wxString LookFor;
|
wxString LookFor;
|
||||||
wxString ReplaceWith;
|
wxString ReplaceWith;
|
||||||
|
|
||||||
wxString *GetText(AssDialogue *cur, int field);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
agi::Context *context;
|
agi::Context *context;
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ enum {
|
||||||
|
|
||||||
DEFINE_SIMPLE_EXCEPTION(BadRegex, agi::InvalidInputException, "bad_regex")
|
DEFINE_SIMPLE_EXCEPTION(BadRegex, agi::InvalidInputException, "bad_regex")
|
||||||
|
|
||||||
static wxString AssDialogue::* get_field(int field_n) {
|
static boost::flyweight<wxString> AssDialogue::* get_field(int field_n) {
|
||||||
switch(field_n) {
|
switch(field_n) {
|
||||||
case FIELD_TEXT: return &AssDialogue::Text; break;
|
case FIELD_TEXT: return &AssDialogue::Text; break;
|
||||||
case FIELD_STYLE: return &AssDialogue::Style; break;
|
case FIELD_STYLE: return &AssDialogue::Style; break;
|
||||||
|
@ -109,7 +109,7 @@ static std::set<AssDialogue*> process(wxString match_text, bool match_case, int
|
||||||
match_case = false;
|
match_case = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString AssDialogue::*field = get_field(field_n);
|
boost::flyweight<wxString> AssDialogue::*field = get_field(field_n);
|
||||||
std::function<bool (wxString)> pred = get_predicate(mode, &re, match_case, match_text);
|
std::function<bool (wxString)> pred = get_predicate(mode, &re, match_case, match_text);
|
||||||
|
|
||||||
std::set<AssDialogue*> matches;
|
std::set<AssDialogue*> matches;
|
||||||
|
|
|
@ -281,8 +281,10 @@ void DialogSpellChecker::Replace() {
|
||||||
AssDialogue *active_line = context->selectionController->GetActiveLine();
|
AssDialogue *active_line = context->selectionController->GetActiveLine();
|
||||||
|
|
||||||
// Only replace if the user hasn't changed the selection to something else
|
// Only replace if the user hasn't changed the selection to something else
|
||||||
if (active_line->Text.Mid(word_start, word_len) == orig_word->GetValue()) {
|
if (active_line->Text.get().Mid(word_start, word_len) == orig_word->GetValue()) {
|
||||||
active_line->Text.replace(word_start, word_len, replace_word->GetValue());
|
wxString text = active_line->Text;
|
||||||
|
text.replace(word_start, word_len, replace_word->GetValue());
|
||||||
|
active_line->Text = text;
|
||||||
context->ass->Commit(_("spell check replace"), AssFile::COMMIT_DIAG_TEXT);
|
context->ass->Commit(_("spell check replace"), AssFile::COMMIT_DIAG_TEXT);
|
||||||
context->textSelectionController->SetInsertionPoint(word_start + replace_word->GetValue().size());
|
context->textSelectionController->SetInsertionPoint(word_start + replace_word->GetValue().size());
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,7 +159,7 @@ void DialogStyling::OnActiveLineChanged(AssDialogue *new_line) {
|
||||||
|
|
||||||
current_line_text->SetValue(active_line->Text);
|
current_line_text->SetValue(active_line->Text);
|
||||||
style_name->SetValue(active_line->Style);
|
style_name->SetValue(active_line->Style);
|
||||||
style_name->SetSelection(0, active_line->Style.size());
|
style_name->SetSelection(0, active_line->Style.get().size());
|
||||||
style_name->SetFocus();
|
style_name->SetFocus();
|
||||||
|
|
||||||
style_list->SetStringSelection(active_line->Style);
|
style_list->SetStringSelection(active_line->Style);
|
||||||
|
|
|
@ -55,7 +55,7 @@ void AssFixStylesFilter::ProcessSubs(AssFile *subs, wxWindow *) {
|
||||||
styles.Sort();
|
styles.Sort();
|
||||||
|
|
||||||
for (auto diag : subs->Line | agi::of_type<AssDialogue>()) {
|
for (auto diag : subs->Line | agi::of_type<AssDialogue>()) {
|
||||||
if (!std::binary_search(styles.begin(), styles.end(), diag->Style.Lower()))
|
if (!std::binary_search(styles.begin(), styles.end(), diag->Style.get().Lower()))
|
||||||
diag->Style = "Default";
|
diag->Style = "Default";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index) {
|
||||||
wxString name = tag->Name;
|
wxString name = tag->Name;
|
||||||
|
|
||||||
if (name == "\\r") {
|
if (name == "\\r") {
|
||||||
style = styles[tag->Params[0]->Get(line->Style)];
|
style = styles[tag->Params[0]->Get<wxString>(line->Style)];
|
||||||
overriden = false;
|
overriden = false;
|
||||||
}
|
}
|
||||||
else if (name == "\\b") {
|
else if (name == "\\b") {
|
||||||
|
|
|
@ -306,7 +306,7 @@ void SubsEditBox::OnCommit(int type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsEditBox::PopulateList(wxComboBox *combo, wxString AssDialogue::*field) {
|
void SubsEditBox::PopulateList(wxComboBox *combo, boost::flyweight<wxString> AssDialogue::*field) {
|
||||||
wxEventBlocker blocker(this);
|
wxEventBlocker blocker(this);
|
||||||
|
|
||||||
std::set<wxString> values;
|
std::set<wxString> values;
|
||||||
|
@ -397,7 +397,7 @@ void SubsEditBox::SetSelectedRows(T AssDialogue::*field, T value, wxString const
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsEditBox::CommitText(wxString const& desc) {
|
void SubsEditBox::CommitText(wxString const& desc) {
|
||||||
SetSelectedRows(&AssDialogue::Text, TextEdit->GetText(), desc, AssFile::COMMIT_DIAG_TEXT, true);
|
SetSelectedRows(&AssDialogue::Text, boost::flyweight<wxString>(TextEdit->GetText()), desc, AssFile::COMMIT_DIAG_TEXT, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsEditBox::CommitTimes(TimeField field) {
|
void SubsEditBox::CommitTimes(TimeField field) {
|
||||||
|
@ -487,12 +487,12 @@ void SubsEditBox::SetControlsState(bool state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsEditBox::OnStyleChange(wxCommandEvent &) {
|
void SubsEditBox::OnStyleChange(wxCommandEvent &) {
|
||||||
SetSelectedRows(&AssDialogue::Style, StyleBox->GetValue(), _("style change"), AssFile::COMMIT_DIAG_META);
|
SetSelectedRows(&AssDialogue::Style, boost::flyweight<wxString>(StyleBox->GetValue()), _("style change"), AssFile::COMMIT_DIAG_META);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubsEditBox::OnActorChange(wxCommandEvent &evt) {
|
void SubsEditBox::OnActorChange(wxCommandEvent &evt) {
|
||||||
bool amend = evt.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED;
|
bool amend = evt.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED;
|
||||||
SetSelectedRows(&AssDialogue::Actor, ActorBox->GetValue(), _("actor change"), AssFile::COMMIT_DIAG_META, amend);
|
SetSelectedRows(&AssDialogue::Actor, boost::flyweight<wxString>(ActorBox->GetValue()), _("actor change"), AssFile::COMMIT_DIAG_META, amend);
|
||||||
PopulateList(ActorBox, &AssDialogue::Actor);
|
PopulateList(ActorBox, &AssDialogue::Actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +502,7 @@ void SubsEditBox::OnLayerEnter(wxCommandEvent &) {
|
||||||
|
|
||||||
void SubsEditBox::OnEffectChange(wxCommandEvent &evt) {
|
void SubsEditBox::OnEffectChange(wxCommandEvent &evt) {
|
||||||
bool amend = evt.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED;
|
bool amend = evt.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED;
|
||||||
SetSelectedRows(&AssDialogue::Effect, Effect->GetValue(), _("effect change"), AssFile::COMMIT_DIAG_META, amend);
|
SetSelectedRows(&AssDialogue::Effect, boost::flyweight<wxString>(Effect->GetValue()), _("effect change"), AssFile::COMMIT_DIAG_META, amend);
|
||||||
PopulateList(Effect, &AssDialogue::Effect);
|
PopulateList(Effect, &AssDialogue::Effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <boost/container/map.hpp>
|
#include <boost/container/map.hpp>
|
||||||
|
#include <boost/flyweight/flyweight_fwd.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <wx/panel.h>
|
#include <wx/panel.h>
|
||||||
|
@ -178,7 +179,7 @@ class SubsEditBox : public wxPanel {
|
||||||
void OnCommit(int type);
|
void OnCommit(int type);
|
||||||
|
|
||||||
/// Regenerate a dropdown list with the unique values of a dialogue field
|
/// Regenerate a dropdown list with the unique values of a dialogue field
|
||||||
void PopulateList(wxComboBox *combo, wxString AssDialogue::*field);
|
void PopulateList(wxComboBox *combo, boost::flyweight<wxString> AssDialogue::*field);
|
||||||
|
|
||||||
/// @brief Enable or disable frame timing mode
|
/// @brief Enable or disable frame timing mode
|
||||||
void UpdateFrameTiming(agi::vfr::Framerate const& fps);
|
void UpdateFrameTiming(agi::vfr::Framerate const& fps);
|
||||||
|
|
|
@ -212,7 +212,7 @@ void SubsTextEditCtrl::UpdateStyle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
AssDialogue *diag = context ? context->selectionController->GetActiveLine() : 0;
|
AssDialogue *diag = context ? context->selectionController->GetActiveLine() : 0;
|
||||||
bool template_line = diag && diag->Comment && diag->Effect.Lower().StartsWith("template");
|
bool template_line = diag && diag->Comment && diag->Effect.get().Lower().StartsWith("template");
|
||||||
|
|
||||||
tokenized_line = agi::ass::TokenizeDialogueBody(line_text, template_line);
|
tokenized_line = agi::ass::TokenizeDialogueBody(line_text, template_line);
|
||||||
agi::ass::SplitWords(line_text, tokenized_line);
|
agi::ass::SplitWords(line_text, tokenized_line);
|
||||||
|
|
|
@ -57,11 +57,11 @@ SubtitlesGrid::SubtitlesGrid(wxWindow *parent, agi::Context *context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static void trim_text(AssDialogue *diag) {
|
static void trim_text(wxString *text) {
|
||||||
static wxRegEx start("^( |\t|\\\\[nNh])+");
|
static wxRegEx start("^( |\t|\\\\[nNh])+");
|
||||||
static wxRegEx end("( |\t|\\\\[nNh])+$");
|
static wxRegEx end("( |\t|\\\\[nNh])+$");
|
||||||
start.ReplaceFirst(&diag->Text, "");
|
start.ReplaceFirst(text, "");
|
||||||
end.ReplaceFirst(&diag->Text, "");
|
end.ReplaceFirst(text, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void expand_times(AssDialogue *src, AssDialogue *dst) {
|
static void expand_times(AssDialogue *src, AssDialogue *dst) {
|
||||||
|
@ -69,6 +69,25 @@ static void expand_times(AssDialogue *src, AssDialogue *dst) {
|
||||||
dst->End = std::max(dst->End, src->End);
|
dst->End = std::max(dst->End, src->End);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool check_lines(AssDialogue *d1, AssDialogue *d2, bool (wxString::*pred)(wxString const&, wxString *) const) {
|
||||||
|
wxString rest;
|
||||||
|
if ((d1->Text.get().*pred)(d2->Text.get(), &rest)) {
|
||||||
|
trim_text(&rest);
|
||||||
|
d1->Text = rest;
|
||||||
|
expand_times(d1, d2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_start(AssDialogue *d1, AssDialogue *d2) {
|
||||||
|
return check_lines(d1, d2, &wxString::StartsWith);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_end(AssDialogue *d1, AssDialogue *d2) {
|
||||||
|
return check_lines(d1, d2, &wxString::EndsWith);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Recombine
|
/// @brief Recombine
|
||||||
void SubtitlesGrid::RecombineLines() {
|
void SubtitlesGrid::RecombineLines() {
|
||||||
Selection selectedSet = GetSelectedSet();
|
Selection selectedSet = GetSelectedSet();
|
||||||
|
@ -77,14 +96,17 @@ void SubtitlesGrid::RecombineLines() {
|
||||||
AssDialogue *activeLine = GetActiveLine();
|
AssDialogue *activeLine = GetActiveLine();
|
||||||
|
|
||||||
std::vector<AssDialogue*> sel(selectedSet.begin(), selectedSet.end());
|
std::vector<AssDialogue*> sel(selectedSet.begin(), selectedSet.end());
|
||||||
for_each(sel.begin(), sel.end(), trim_text);
|
|
||||||
sort(sel.begin(), sel.end(), &AssFile::CompStart);
|
sort(sel.begin(), sel.end(), &AssFile::CompStart);
|
||||||
|
for (auto &diag : sel) {
|
||||||
|
wxString text = diag->Text;
|
||||||
|
trim_text(&text);
|
||||||
|
diag->Text = text;
|
||||||
|
}
|
||||||
|
|
||||||
typedef std::vector<AssDialogue*>::iterator diag_iter;
|
auto end = sel.end() - 1;
|
||||||
diag_iter end = sel.end() - 1;
|
for (auto cur = sel.begin(); cur != end; ++cur) {
|
||||||
for (diag_iter cur = sel.begin(); cur != end; ++cur) {
|
|
||||||
AssDialogue *d1 = *cur;
|
AssDialogue *d1 = *cur;
|
||||||
diag_iter d2 = cur + 1;
|
auto d2 = cur + 1;
|
||||||
|
|
||||||
// 1, 1+2 (or 2+1), 2 gets turned into 1, 2, 2 so kill the duplicate
|
// 1, 1+2 (or 2+1), 2 gets turned into 1, 2, 2 so kill the duplicate
|
||||||
if (d1->Text == (*d2)->Text) {
|
if (d1->Text == (*d2)->Text) {
|
||||||
|
@ -94,44 +116,33 @@ void SubtitlesGrid::RecombineLines() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1, 1+2, 1 turns into 1, 2, [empty]
|
// 1, 1+2, 1 turns into 1, 2, [empty]
|
||||||
if (d1->Text.empty()) {
|
if (d1->Text.get().empty()) {
|
||||||
delete d1;
|
delete d1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If d2 is the last line in the selection it'll never hit the above test
|
// If d2 is the last line in the selection it'll never hit the above test
|
||||||
if (d2 == end && (*d2)->Text.empty()) {
|
if (d2 == end && (*d2)->Text.get().empty()) {
|
||||||
delete *d2;
|
delete *d2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1, 1+2
|
// 1, 1+2
|
||||||
while (d2 <= end && (*d2)->Text.StartsWith(d1->Text, &(*d2)->Text)) {
|
while (d2 <= end && check_start(*d2, d1))
|
||||||
expand_times(*d2, d1);
|
|
||||||
trim_text(*d2);
|
|
||||||
++d2;
|
++d2;
|
||||||
}
|
|
||||||
|
|
||||||
// 1, 2+1
|
// 1, 2+1
|
||||||
while (d2 <= end && (*d2)->Text.EndsWith(d1->Text, &(*d2)->Text)) {
|
while (d2 <= end && check_end(*d2, d1))
|
||||||
expand_times(*d2, d1);
|
|
||||||
trim_text(*d2);
|
|
||||||
++d2;
|
++d2;
|
||||||
}
|
|
||||||
|
|
||||||
// 1+2, 2
|
// 1+2, 2
|
||||||
while (d2 <= end && d1->Text.EndsWith((*d2)->Text, &d1->Text)) {
|
while (d2 <= end && check_end(d1, *d2))
|
||||||
expand_times(d1, *d2);
|
|
||||||
trim_text(d1);
|
|
||||||
++d2;
|
++d2;
|
||||||
}
|
|
||||||
|
|
||||||
// 2+1, 2
|
// 2+1, 2
|
||||||
while (d2 <= end && d1->Text.StartsWith((*d2)->Text, &d1->Text)) {
|
while (d2 <= end && check_start(d1, *d2))
|
||||||
expand_times(d1, *d2);
|
|
||||||
trim_text(d1);
|
|
||||||
++d2;
|
++d2;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Remove now non-existent lines from the selection
|
// Remove now non-existent lines from the selection
|
||||||
Selection lines;
|
Selection lines;
|
||||||
|
|
|
@ -170,19 +170,21 @@ void SubtitleFormat::StripTags(AssFile &file) {
|
||||||
|
|
||||||
void SubtitleFormat::ConvertNewlines(AssFile &file, wxString const& newline, bool mergeLineBreaks) {
|
void SubtitleFormat::ConvertNewlines(AssFile &file, wxString const& newline, bool mergeLineBreaks) {
|
||||||
for (auto current : file.Line | agi::of_type<AssDialogue>()) {
|
for (auto current : file.Line | agi::of_type<AssDialogue>()) {
|
||||||
current->Text.Replace("\\h", " ");
|
wxString repl(current->Text);
|
||||||
current->Text.Replace("\\n", newline);
|
repl.Replace("\\h", " ");
|
||||||
current->Text.Replace("\\N", newline);
|
repl.Replace("\\n", newline);
|
||||||
|
repl.Replace("\\N", newline);
|
||||||
if (mergeLineBreaks) {
|
if (mergeLineBreaks) {
|
||||||
while (current->Text.Replace(newline+newline, newline));
|
while (repl.Replace(newline+newline, newline));
|
||||||
}
|
}
|
||||||
|
current->Text = repl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubtitleFormat::StripComments(AssFile &file) {
|
void SubtitleFormat::StripComments(AssFile &file) {
|
||||||
file.Line.remove_and_dispose_if([](AssEntry const& e) {
|
file.Line.remove_and_dispose_if([](AssEntry const& e) {
|
||||||
const AssDialogue *diag = dynamic_cast<const AssDialogue*>(&e);
|
const AssDialogue *diag = dynamic_cast<const AssDialogue*>(&e);
|
||||||
return diag && (diag->Comment || !diag->Text);
|
return diag && (diag->Comment || !diag->Text.get());
|
||||||
}, [](AssEntry *e) { delete e; });
|
}, [](AssEntry *e) { delete e; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -416,7 +416,7 @@ namespace
|
||||||
else if (!imline.CheckLineLengths(export_settings.max_line_length))
|
else if (!imline.CheckLineLengths(export_settings.max_line_length))
|
||||||
{
|
{
|
||||||
if (export_settings.line_wrapping_mode == EbuExportSettings::AbortOverLength)
|
if (export_settings.line_wrapping_mode == EbuExportSettings::AbortOverLength)
|
||||||
throw Ebu3264SubtitleFormat::ConversionFailed(STD_STR(wxString::Format(_("Line over maximum length: %s"), line->Text.c_str())), 0);
|
throw Ebu3264SubtitleFormat::ConversionFailed(STD_STR(wxString::Format(_("Line over maximum length: %s"), line->Text.get())), 0);
|
||||||
else // skip over-long lines
|
else // skip over-long lines
|
||||||
subs_list.pop_back();
|
subs_list.pop_back();
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,6 @@ void EncoreSubtitleFormat::WriteFile(const AssFile *src, wxString const& filenam
|
||||||
StripTags(copy);
|
StripTags(copy);
|
||||||
ConvertNewlines(copy, "\r\n");
|
ConvertNewlines(copy, "\r\n");
|
||||||
|
|
||||||
|
|
||||||
// Encode wants ; for NTSC and : for PAL
|
// Encode wants ; for NTSC and : for PAL
|
||||||
// The manual suggests no other frame rates are supported
|
// The manual suggests no other frame rates are supported
|
||||||
char sep = fps.NeedsDropFrames() ? ';' : ':';
|
char sep = fps.NeedsDropFrames() ? ';' : ':';
|
||||||
|
@ -79,8 +78,6 @@ void EncoreSubtitleFormat::WriteFile(const AssFile *src, wxString const& filenam
|
||||||
// Write lines
|
// Write lines
|
||||||
int i = 0;
|
int i = 0;
|
||||||
TextFileWriter file(filename, "UTF-8");
|
TextFileWriter file(filename, "UTF-8");
|
||||||
for (auto current : copy.Line | agi::of_type<AssDialogue>()) {
|
for (auto current : copy.Line | agi::of_type<AssDialogue>())
|
||||||
++i;
|
file.WriteLineToFile(wxString::Format("%i %s %s %s", ++i, ft.ToSMPTE(current->Start), ft.ToSMPTE(current->End), current->Text.get()));
|
||||||
file.WriteLineToFile(wxString::Format("%i %s %s %s", i, ft.ToSMPTE(current->Start), ft.ToSMPTE(current->End), current->Text));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,6 @@ void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, wxString const& filen
|
||||||
int start = fps.FrameAtTime(current->Start, agi::vfr::START);
|
int start = fps.FrameAtTime(current->Start, agi::vfr::START);
|
||||||
int end = fps.FrameAtTime(current->End, agi::vfr::END);
|
int end = fps.FrameAtTime(current->End, agi::vfr::END);
|
||||||
|
|
||||||
file.WriteLineToFile(wxString::Format("{%i}{%i}%s", start, end, current->Text));
|
file.WriteLineToFile(wxString::Format("{%i}{%i}%s", start, end, current->Text.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,6 +402,7 @@ void SRTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxSt
|
||||||
int line_num = 0;
|
int line_num = 0;
|
||||||
int linebreak_debt = 0;
|
int linebreak_debt = 0;
|
||||||
AssDialogue *line = 0;
|
AssDialogue *line = 0;
|
||||||
|
wxString text;
|
||||||
while (file.HasMoreLines()) {
|
while (file.HasMoreLines()) {
|
||||||
wxString text_line = file.ReadLineFromFile();
|
wxString text_line = file.ReadLineFromFile();
|
||||||
line_num++;
|
line_num++;
|
||||||
|
@ -426,8 +427,8 @@ void SRTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxSt
|
||||||
found_timestamps:
|
found_timestamps:
|
||||||
if (line) {
|
if (line) {
|
||||||
// finalize active line
|
// finalize active line
|
||||||
line->Text = tag_parser.ToAss(line->Text);
|
line->Text = tag_parser.ToAss(text);
|
||||||
line = 0;
|
text.clear();
|
||||||
}
|
}
|
||||||
// create new subtitle
|
// create new subtitle
|
||||||
line = new AssDialogue;
|
line = new AssDialogue;
|
||||||
|
@ -446,7 +447,7 @@ found_timestamps:
|
||||||
linebreak_debt = 0;
|
linebreak_debt = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
line->Text.Append(text_line);
|
text.Append(text_line);
|
||||||
state = STATE_REST_OF_BODY;
|
state = STATE_REST_OF_BODY;
|
||||||
break;
|
break;
|
||||||
case STATE_REST_OF_BODY:
|
case STATE_REST_OF_BODY:
|
||||||
|
@ -458,7 +459,7 @@ found_timestamps:
|
||||||
linebreak_debt = 1;
|
linebreak_debt = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
line->Text.Append("\\N").Append(text_line);
|
text.Append("\\N").Append(text_line);
|
||||||
break;
|
break;
|
||||||
case STATE_LAST_WAS_BLANK:
|
case STATE_LAST_WAS_BLANK:
|
||||||
++linebreak_debt;
|
++linebreak_debt;
|
||||||
|
@ -475,20 +476,19 @@ found_timestamps:
|
||||||
// assume it's a continuation of the subtitle text
|
// assume it's a continuation of the subtitle text
|
||||||
// resolve our line break debt and append the line text
|
// resolve our line break debt and append the line text
|
||||||
while (linebreak_debt-- > 0)
|
while (linebreak_debt-- > 0)
|
||||||
line->Text.Append("\\N");
|
text.Append("\\N");
|
||||||
line->Text.Append(text_line);
|
text.Append(text_line);
|
||||||
state = STATE_REST_OF_BODY;
|
state = STATE_REST_OF_BODY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == 1 || state == 2) {
|
if (state == 1 || state == 2)
|
||||||
throw SRTParseError("Parsing SRT: Incomplete file", 0);
|
throw SRTParseError("Parsing SRT: Incomplete file", 0);
|
||||||
}
|
|
||||||
|
|
||||||
if (line)
|
if (line)
|
||||||
// an unfinalized line
|
// an unfinalized line
|
||||||
line->Text = tag_parser.ToAss(line->Text);
|
line->Text = tag_parser.ToAss(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SRTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
|
void SRTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const {
|
||||||
|
|
|
@ -71,6 +71,8 @@ void TranStationSubtitleFormat::WriteFile(const AssFile *src, wxString const& fi
|
||||||
StripComments(copy);
|
StripComments(copy);
|
||||||
RecombineOverlaps(copy);
|
RecombineOverlaps(copy);
|
||||||
MergeIdentical(copy);
|
MergeIdentical(copy);
|
||||||
|
StripTags(copy);
|
||||||
|
ConvertNewlines(copy, "\r\n");
|
||||||
|
|
||||||
SmpteFormatter ft(fps);
|
SmpteFormatter ft(fps);
|
||||||
TextFileWriter file(filename, encoding);
|
TextFileWriter file(filename, encoding);
|
||||||
|
@ -106,7 +108,7 @@ wxString TranStationSubtitleFormat::ConvertLine(AssFile *file, AssDialogue *curr
|
||||||
|
|
||||||
// Hack: If an italics-tag (\i1) appears anywhere in the line,
|
// Hack: If an italics-tag (\i1) appears anywhere in the line,
|
||||||
// make it all italics
|
// make it all italics
|
||||||
if (current->Text.Find("\\i1") != wxNOT_FOUND) type = "I";
|
if (current->Text.get().Find("\\i1") != wxNOT_FOUND) type = "I";
|
||||||
|
|
||||||
// Write header
|
// Write header
|
||||||
AssTime end = current->End;
|
AssTime end = current->End;
|
||||||
|
@ -118,14 +120,5 @@ wxString TranStationSubtitleFormat::ConvertLine(AssFile *file, AssDialogue *curr
|
||||||
end = fps.TimeAtFrame(fps.FrameAtTime(end, agi::vfr::END) - 1, agi::vfr::END);
|
end = fps.TimeAtFrame(fps.FrameAtTime(end, agi::vfr::END) - 1, agi::vfr::END);
|
||||||
|
|
||||||
wxString header = wxString::Format("SUB[%i%s%s ", valign, halign, type) + ft.ToSMPTE(current->Start) + ">" + ft.ToSMPTE(end) + "]\r\n";
|
wxString header = wxString::Format("SUB[%i%s%s ", valign, halign, type) + ft.ToSMPTE(current->Start) + ">" + ft.ToSMPTE(end) + "]\r\n";
|
||||||
|
|
||||||
// Process text
|
|
||||||
wxString lineEnd = "\r\n";
|
|
||||||
current->StripTags();
|
|
||||||
current->Text.Replace("\\h", " ", true);
|
|
||||||
current->Text.Replace("\\n", lineEnd, true);
|
|
||||||
current->Text.Replace("\\N", lineEnd, true);
|
|
||||||
while (current->Text.Replace(lineEnd + lineEnd, lineEnd, true));
|
|
||||||
|
|
||||||
return header + current->Text;
|
return header + current->Text;
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ void TXTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename,
|
||||||
for (auto dia : src->Line | agi::of_type<AssDialogue>()) {
|
for (auto dia : src->Line | agi::of_type<AssDialogue>()) {
|
||||||
if (!dia->Comment) {
|
if (!dia->Comment) {
|
||||||
num_dialogue_lines++;
|
num_dialogue_lines++;
|
||||||
if (!dia->Actor.empty())
|
if (!dia->Actor.get().empty())
|
||||||
num_actor_names++;
|
num_actor_names++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ void VisualToolClip::CommitHold() {
|
||||||
for (auto line : c->selectionController->GetSelectedSet()) {
|
for (auto line : c->selectionController->GetSelectedSet()) {
|
||||||
// This check is technically not correct as it could be outside of an
|
// This check is technically not correct as it could be outside of an
|
||||||
// override block... but that's rather unlikely
|
// override block... but that's rather unlikely
|
||||||
bool has_iclip = line->Text.find("\\iclip") != wxString::npos;
|
bool has_iclip = line->Text.get().find("\\iclip") != wxString::npos;
|
||||||
SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value);
|
SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,7 @@ void VisualToolVectorClip::Save() {
|
||||||
for (auto line : c->selectionController->GetSelectedSet()) {
|
for (auto line : c->selectionController->GetSelectedSet()) {
|
||||||
// This check is technically not correct as it could be outside of an
|
// This check is technically not correct as it could be outside of an
|
||||||
// override block... but that's rather unlikely
|
// override block... but that's rather unlikely
|
||||||
bool has_iclip = line->Text.find("\\iclip") != wxString::npos;
|
bool has_iclip = line->Text.get().find("\\iclip") != wxString::npos;
|
||||||
SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value);
|
SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue