forked from mia/Aegisub
Implemented a multi-line modification action. Still a bit hacky, but works well.
Originally committed to SVN as r2076.
This commit is contained in:
parent
18488241e8
commit
39938d213a
13 changed files with 196 additions and 24 deletions
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "gorgonstring.h"
|
#include "gorgonstring.h"
|
||||||
|
#include "selection.h"
|
||||||
|
|
||||||
namespace Gorgonsub {
|
namespace Gorgonsub {
|
||||||
// Prototypes
|
// Prototypes
|
||||||
|
@ -108,13 +109,12 @@ namespace Gorgonsub {
|
||||||
private:
|
private:
|
||||||
std::vector<shared_ptr<SectionEntry> > entries;
|
std::vector<shared_ptr<SectionEntry> > entries;
|
||||||
std::vector<shared_ptr<void> > deltas;
|
std::vector<shared_ptr<void> > deltas;
|
||||||
std::vector<int> lines;
|
Selection selection;
|
||||||
const String section;
|
const String section;
|
||||||
bool noTextFields;
|
bool noTextFields;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ActionModifyBatch(shared_ptr<SectionEntry> entry,int line,const String §ion,bool noTextFields);
|
ActionModifyBatch(std::vector<shared_ptr<SectionEntry> > entries,std::vector<shared_ptr<void> > deltas,Selection selection,const String §ion,bool noTextFields);
|
||||||
ActionModifyBatch(shared_ptr<void> delta,int line,const String §ion);
|
|
||||||
~ActionModifyBatch() {}
|
~ActionModifyBatch() {}
|
||||||
|
|
||||||
ActionPtr GetAntiAction(const Model &model) const;
|
ActionPtr GetAntiAction(const Model &model) const;
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace Gorgonsub {
|
||||||
void InsertLine(SectionEntryPtr line,int position=-1,const String section=L"");
|
void InsertLine(SectionEntryPtr line,int position=-1,const String section=L"");
|
||||||
void RemoveLine(int position,const String section);
|
void RemoveLine(int position,const String section);
|
||||||
SectionEntryPtr ModifyLine(int position,const String section);
|
SectionEntryPtr ModifyLine(int position,const String section);
|
||||||
SectionEntryPtr ModifyLines(Selection selection,const String section);
|
std::vector<SectionEntryPtr> ModifyLines(Selection selection,const String section);
|
||||||
};
|
};
|
||||||
typedef shared_ptr<ActionList> ActionListPtr;
|
typedef shared_ptr<ActionList> ActionListPtr;
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ namespace Gorgonsub {
|
||||||
Parse_Error,
|
Parse_Error,
|
||||||
Unsupported_Format_Feature,
|
Unsupported_Format_Feature,
|
||||||
Invalid_Token,
|
Invalid_Token,
|
||||||
|
Out_Of_Range,
|
||||||
TODO
|
TODO
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "gorgonstring.h"
|
#include "gorgonstring.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
namespace Gorgonsub {
|
namespace Gorgonsub {
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ namespace Gorgonsub {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Time() { ms = 0; }
|
Time() { ms = 0; }
|
||||||
Time(int ms) { (void)ms; }
|
Time(int _ms) { ms = _ms; }
|
||||||
|
|
||||||
void SetMS(int milliseconds) { ms = milliseconds; }
|
void SetMS(int milliseconds) { ms = milliseconds; }
|
||||||
int GetMS() const { return ms; }
|
int GetMS() const { return ms; }
|
||||||
|
@ -53,6 +54,8 @@ namespace Gorgonsub {
|
||||||
String GetString(int ms_precision,int h_precision) const;
|
String GetString(int ms_precision,int h_precision) const;
|
||||||
void Parse(const String &data);
|
void Parse(const String &data);
|
||||||
|
|
||||||
|
Time operator + (const int &par) const { return Max<int>(0,ms+par); }
|
||||||
|
Time operator - (const int &par) const { return Max<int>(0,ms-par); }
|
||||||
bool operator == (const Time &par) const { return ms == par.ms; }
|
bool operator == (const Time &par) const { return ms == par.ms; }
|
||||||
bool operator != (const Time &par) const { return ms != par.ms; }
|
bool operator != (const Time &par) const { return ms != par.ms; }
|
||||||
bool operator < (const Time &par) const { return ms < par.ms; }
|
bool operator < (const Time &par) const { return ms < par.ms; }
|
||||||
|
|
|
@ -58,6 +58,7 @@ namespace Gorgonsub {
|
||||||
private:
|
private:
|
||||||
std::vector<Range> ranges;
|
std::vector<Range> ranges;
|
||||||
size_t count;
|
size_t count;
|
||||||
|
void UpdateCount();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Selection();
|
Selection();
|
||||||
|
@ -68,11 +69,13 @@ namespace Gorgonsub {
|
||||||
void RemoveRange(const Range &range);
|
void RemoveRange(const Range &range);
|
||||||
void AddSelection (const Selection ¶m);
|
void AddSelection (const Selection ¶m);
|
||||||
void RemoveSelection (const Selection ¶m);
|
void RemoveSelection (const Selection ¶m);
|
||||||
|
void NormalizeRanges ();
|
||||||
|
|
||||||
size_t GetCount() const { return count; }
|
size_t GetCount() const { return count; }
|
||||||
size_t GetRanges() const { return ranges.size(); }
|
size_t GetRanges() const { return ranges.size(); }
|
||||||
size_t GetLine(size_t n) const;
|
size_t GetLine(size_t n) const;
|
||||||
size_t GetLineInRange(size_t n,size_t range) const { return ranges.at(range).GetLine(n); }
|
size_t GetLineInRange(size_t n,size_t range) const { return ranges.at(range).GetLine(n); }
|
||||||
|
size_t GetLinesInRange(size_t range) const { return ranges.at(range).GetSize(); }
|
||||||
bool IsContiguous() const { return GetRanges() <= 1; }
|
bool IsContiguous() const { return GetRanges() <= 1; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Gorgonsub.h"
|
#include "gorgonstring.h"
|
||||||
|
|
||||||
|
|
||||||
/////////////
|
/////////////
|
||||||
|
|
|
@ -142,7 +142,7 @@ ActionPtr ActionModify::GetAntiAction(const Model &model) const
|
||||||
|
|
||||||
// Store the whole original line
|
// Store the whole original line
|
||||||
else {
|
else {
|
||||||
return ActionPtr(new ActionModify(oldEntry,lineNumber,section));
|
return ActionPtr(new ActionModify(oldEntry,lineNumber,section,noTextFields));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ ActionPtr ActionModify::GetAntiAction(const Model &model) const
|
||||||
// Execute insertion
|
// Execute insertion
|
||||||
void ActionModify::Execute(Model &model)
|
void ActionModify::Execute(Model &model)
|
||||||
{
|
{
|
||||||
// Find the section to insert it on
|
// Find the section to modify
|
||||||
String sectionName = section;
|
String sectionName = section;
|
||||||
if (sectionName.IsEmpty()) sectionName = entry->GetDefaultGroup();
|
if (sectionName.IsEmpty()) sectionName = entry->GetDefaultGroup();
|
||||||
SectionPtr sect = GetSection(model,sectionName);
|
SectionPtr sect = GetSection(model,sectionName);
|
||||||
|
@ -163,3 +163,54 @@ void ActionModify::Execute(Model &model)
|
||||||
}
|
}
|
||||||
else sect->GetEntryRef(lineNumber) = entry;
|
else sect->GetEntryRef(lineNumber) = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////// Batch Modify line //////////////////////////
|
||||||
|
|
||||||
|
ActionModifyBatch::ActionModifyBatch(std::vector<shared_ptr<SectionEntry> > _entries, std::vector<shared_ptr<void> > _deltas, Selection _selection,const String &_section,bool _noTextFields)
|
||||||
|
: entries(_entries), deltas(_deltas), selection(_selection), section(_section), noTextFields(_noTextFields) {}
|
||||||
|
|
||||||
|
ActionPtr ActionModifyBatch::GetAntiAction(const Model &model) const
|
||||||
|
{
|
||||||
|
// Get section
|
||||||
|
SectionPtr sect = GetSection(model,section);
|
||||||
|
size_t len = selection.GetCount();
|
||||||
|
std::vector<VoidPtr> _deltas(len);
|
||||||
|
std::vector<SectionEntryPtr> oldEntries(len);
|
||||||
|
|
||||||
|
// For each line...
|
||||||
|
for (size_t i=0;i<len;i++) {
|
||||||
|
// Get old entry
|
||||||
|
SectionEntryPtr oldEntry = sect->GetEntry(selection.GetLine(i));
|
||||||
|
|
||||||
|
// Try to get a delta
|
||||||
|
DeltaCoderPtr deltaCoder = oldEntry->GetDeltaCoder();
|
||||||
|
if (deltaCoder) {
|
||||||
|
if (i < deltas.size() && deltas[i]) _deltas[i] = deltaCoder->EncodeReverseDelta(deltas[i],oldEntry);
|
||||||
|
_deltas[i] = deltaCoder->EncodeDelta(entries[i],oldEntry,!noTextFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the whole original line
|
||||||
|
else oldEntries[i] = oldEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionPtr(new ActionModifyBatch(oldEntries,_deltas,selection,section,noTextFields));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionModifyBatch::Execute(Model &model)
|
||||||
|
{
|
||||||
|
// Find the section to modify
|
||||||
|
size_t len = selection.GetCount();
|
||||||
|
String sectionName = section;
|
||||||
|
if (sectionName.IsEmpty()) sectionName = entries[0]->GetDefaultGroup();
|
||||||
|
SectionPtr sect = GetSection(model,sectionName);
|
||||||
|
|
||||||
|
// For each line...
|
||||||
|
for (size_t i=0;i<len;i++) {
|
||||||
|
if (i < deltas.size() && deltas[i]) {
|
||||||
|
SectionEntryPtr &ref = sect->GetEntryRef(selection.GetLine(i));
|
||||||
|
ref->GetDeltaCoder()->ApplyDelta(deltas[i],ref);
|
||||||
|
}
|
||||||
|
else sect->GetEntryRef(selection.GetLine(i)) = entries[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -130,3 +130,28 @@ SectionEntryPtr ActionList::ModifyLine(int position,const String section)
|
||||||
AddAction(action);
|
AddAction(action);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// Insert a "modify lines" batch action
|
||||||
|
std::vector<SectionEntryPtr> ActionList::ModifyLines(Selection selection,const String section)
|
||||||
|
{
|
||||||
|
// Get section
|
||||||
|
SectionPtr sect = model.GetSection(section);
|
||||||
|
|
||||||
|
// Generate entries
|
||||||
|
std::vector<SectionEntryPtr> entries(selection.GetCount());
|
||||||
|
size_t len = selection.GetRanges();
|
||||||
|
size_t n = 0;
|
||||||
|
for (size_t i=0;i<len;i++) {
|
||||||
|
size_t rLen = selection.GetLinesInRange(i);
|
||||||
|
for (size_t j=0;j<rLen;j++) {
|
||||||
|
entries[n++] = sect->GetEntry(selection.GetLineInRange(j,i))->Clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the action
|
||||||
|
ActionPtr action = ActionPtr (new ActionModifyBatch(entries,std::vector<VoidPtr>(),selection,section,false));
|
||||||
|
AddAction(action);
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ const char* Exception::GetMessageChar(int code)
|
||||||
case Parse_Error: return "Parse error.";
|
case Parse_Error: return "Parse error.";
|
||||||
case Unsupported_Format_Feature: return "This feature is not supported by this format.";
|
case Unsupported_Format_Feature: return "This feature is not supported by this format.";
|
||||||
case Invalid_Token: return "Invalid type for this token.";
|
case Invalid_Token: return "Invalid type for this token.";
|
||||||
|
case Out_Of_Range: return "Out of range.";
|
||||||
case TODO: return "TODO";
|
case TODO: return "TODO";
|
||||||
}
|
}
|
||||||
return "Invalid code.";
|
return "Invalid code.";
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace Gorgonsub {
|
||||||
// Basic features
|
// Basic features
|
||||||
String GetDefaultGroup() const { return L"Events"; }
|
String GetDefaultGroup() const { return L"Events"; }
|
||||||
SectionEntryPtr Clone() const { return SectionEntryPtr(new DialogueASS(*this)); }
|
SectionEntryPtr Clone() const { return SectionEntryPtr(new DialogueASS(*this)); }
|
||||||
DeltaCoderPtr GetDeltaCoder() const { return DeltaCoderPtr(new DialogueASSDeltaCoder()); }
|
//DeltaCoderPtr GetDeltaCoder() const { return DeltaCoderPtr(new DialogueASSDeltaCoder()); }
|
||||||
|
|
||||||
// Capabilities
|
// Capabilities
|
||||||
bool HasText() const { return true; }
|
bool HasText() const { return true; }
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
// Contact: mailto:amz@aegisub.net
|
// Contact: mailto:amz@aegisub.net
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "selection.h"
|
#include "gorgonsub.h"
|
||||||
using namespace Gorgonsub;
|
using namespace Gorgonsub;
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@ Selection::Selection()
|
||||||
// Adds a range
|
// Adds a range
|
||||||
void Selection::AddRange(const Range &range)
|
void Selection::AddRange(const Range &range)
|
||||||
{
|
{
|
||||||
// TODO
|
ranges.push_back(range);
|
||||||
(void) range;
|
UpdateCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,6 +60,53 @@ void Selection::RemoveRange(const Range &range)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
(void) range;
|
(void) range;
|
||||||
|
THROW_GORGON_EXCEPTION(Exception::TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Normalizes all ranges, that is, gets rid of overlaps and whatnot
|
||||||
|
void Selection::NormalizeRanges()
|
||||||
|
{
|
||||||
|
// Has anything to do?
|
||||||
|
if (ranges.size() == 0) return;
|
||||||
|
|
||||||
|
// Find largest value
|
||||||
|
size_t max = 0;
|
||||||
|
size_t len = ranges.size();
|
||||||
|
for (size_t i=0;i<len;i++) {
|
||||||
|
if (ranges[i].GetEnd() > max) max = ranges[i].GetEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a vector of that size
|
||||||
|
std::vector<bool> selected(max);
|
||||||
|
for (size_t i=0;i<len;i++) {
|
||||||
|
for (size_t j=ranges[i].GetStart();j<ranges[i].GetEnd();j++) {
|
||||||
|
selected[j] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear ranges and re-build them
|
||||||
|
ranges.clear();
|
||||||
|
size_t start = 0;
|
||||||
|
bool inside = false;
|
||||||
|
for (size_t i=0;i<max;i++) {
|
||||||
|
// Enter
|
||||||
|
if (!inside && selected[i]) {
|
||||||
|
start = i;
|
||||||
|
inside = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
else if (inside && !selected[i]) {
|
||||||
|
ranges.push_back(Range(start,i));
|
||||||
|
inside = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inside) ranges.push_back(Range(start,max));
|
||||||
|
|
||||||
|
// Update count
|
||||||
|
UpdateCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,8 +118,9 @@ size_t Selection::GetLine(size_t n) const
|
||||||
size_t cur = 0;
|
size_t cur = 0;
|
||||||
size_t len = ranges.size();
|
size_t len = ranges.size();
|
||||||
for (size_t i=0;i<len;i++) {
|
for (size_t i=0;i<len;i++) {
|
||||||
cur += ranges[i].GetSize();
|
size_t curLen = ranges[i].GetSize();
|
||||||
if (cur > n) return ranges[i].GetLine(n-ranges[i].GetStart());
|
if (cur+curLen > n) return ranges[i].GetLine(n-cur);
|
||||||
|
cur += curLen;
|
||||||
}
|
}
|
||||||
return ~0UL;
|
return ~0UL;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +134,18 @@ void Selection::AddSelection (const Selection ¶m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// Update count
|
||||||
|
void Selection::UpdateCount()
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
size_t len = ranges.size();
|
||||||
|
for (size_t i=0;i<len;i++) {
|
||||||
|
count += ranges[i].GetSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// Subtract another selection
|
// Subtract another selection
|
||||||
void Selection::RemoveSelection (const Selection ¶m)
|
void Selection::RemoveSelection (const Selection ¶m)
|
||||||
|
@ -93,3 +153,11 @@ void Selection::RemoveSelection (const Selection ¶m)
|
||||||
(void) param;
|
(void) param;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// Get line in range
|
||||||
|
size_t Range::GetLine(size_t n) const
|
||||||
|
{
|
||||||
|
if (start+n < end) return start+n;
|
||||||
|
else THROW_GORGON_EXCEPTION(Exception::Out_Of_Range);
|
||||||
|
}
|
||||||
|
|
|
@ -71,18 +71,36 @@ int main()
|
||||||
control.SaveFile(L"subs_out.ass",L"UTF-8");
|
control.SaveFile(L"subs_out.ass",L"UTF-8");
|
||||||
timer.Pause();
|
timer.Pause();
|
||||||
cout << "Done in " << timer.Time() << " ms.\n";
|
cout << "Done in " << timer.Time() << " ms.\n";
|
||||||
|
system("pause");
|
||||||
// Create line to be inserted
|
|
||||||
cout << "Creating data... ";
|
|
||||||
SectionEntryDialoguePtr line = control.CreateDialogue();
|
|
||||||
line->SetText(L"Hi, testing insertion of lines!");
|
|
||||||
cout << "Done.\n";
|
|
||||||
|
|
||||||
// Issue an action
|
// Issue an action
|
||||||
|
cout << "Executing action 100 times... ";
|
||||||
|
timer.Start();
|
||||||
|
for (size_t i=0;i<100;i++) {
|
||||||
ActionListPtr actions = control.CreateActionList(L"Test");
|
ActionListPtr actions = control.CreateActionList(L"Test");
|
||||||
SectionEntryDialoguePtr diag = dynamic_pointer_cast<SectionEntryDialogue> (actions->ModifyLine(10,L"Events"));
|
Selection selection;
|
||||||
diag->SetText(L"Hay guise sup");
|
selection.AddRange(Range(0,5000));
|
||||||
|
selection.AddRange(Range(4500,5500));
|
||||||
|
selection.AddRange(Range(9000,9100));
|
||||||
|
std::vector<SectionEntryPtr> entries = actions->ModifyLines(selection,L"Events");
|
||||||
|
size_t len = entries.size();
|
||||||
|
for (size_t i=0;i<len;i++) {
|
||||||
|
SectionEntryDialoguePtr diag = dynamic_pointer_cast<SectionEntryDialogue> (entries[i]);
|
||||||
|
diag->SetStartTime(diag->GetStartTime() - 55555);
|
||||||
|
diag->SetEndTime(diag->GetEndTime() + 5555);
|
||||||
|
}
|
||||||
actions->Finish();
|
actions->Finish();
|
||||||
|
}
|
||||||
|
timer.Pause();
|
||||||
|
cout << "Done in " << timer.Time() << " ms.\n";
|
||||||
|
system("pause");
|
||||||
|
|
||||||
|
// Rollback
|
||||||
|
cout << "Undoing 99 times... ";
|
||||||
|
for (size_t i=0;i<99;i++) {
|
||||||
|
control.Undo();
|
||||||
|
}
|
||||||
|
cout << "Done.\n";
|
||||||
|
|
||||||
// Undo
|
// Undo
|
||||||
cout << "Undoing and redoing 1000 times... ";
|
cout << "Undoing and redoing 1000 times... ";
|
||||||
|
|
|
@ -14,7 +14,9 @@ x = done
|
||||||
[x] Text file reader/writer
|
[x] Text file reader/writer
|
||||||
[ ] Automatic character set detection
|
[ ] Automatic character set detection
|
||||||
[ ] Conversion between subtitle formats
|
[ ] Conversion between subtitle formats
|
||||||
|
[ ] Override tag support
|
||||||
[:] ASS format support
|
[:] ASS format support
|
||||||
|
[ ] ASS override tags
|
||||||
[ ] Simple subtitles (srt, microdvd, encore, etc) format support
|
[ ] Simple subtitles (srt, microdvd, encore, etc) format support
|
||||||
[ ] Matroska-embedded subtitles support
|
[ ] Matroska-embedded subtitles support
|
||||||
[ ] DVD subtitles support (*)
|
[ ] DVD subtitles support (*)
|
||||||
|
|
Loading…
Reference in a new issue