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
|
||||
#include "gorgonstring.h"
|
||||
#include "selection.h"
|
||||
|
||||
namespace Gorgonsub {
|
||||
// Prototypes
|
||||
|
@ -108,13 +109,12 @@ namespace Gorgonsub {
|
|||
private:
|
||||
std::vector<shared_ptr<SectionEntry> > entries;
|
||||
std::vector<shared_ptr<void> > deltas;
|
||||
std::vector<int> lines;
|
||||
Selection selection;
|
||||
const String section;
|
||||
bool noTextFields;
|
||||
|
||||
public:
|
||||
ActionModifyBatch(shared_ptr<SectionEntry> entry,int line,const String §ion,bool noTextFields);
|
||||
ActionModifyBatch(shared_ptr<void> delta,int line,const String §ion);
|
||||
ActionModifyBatch(std::vector<shared_ptr<SectionEntry> > entries,std::vector<shared_ptr<void> > deltas,Selection selection,const String §ion,bool noTextFields);
|
||||
~ActionModifyBatch() {}
|
||||
|
||||
ActionPtr GetAntiAction(const Model &model) const;
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace Gorgonsub {
|
|||
void InsertLine(SectionEntryPtr line,int position=-1,const String section=L"");
|
||||
void RemoveLine(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;
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace Gorgonsub {
|
|||
Parse_Error,
|
||||
Unsupported_Format_Feature,
|
||||
Invalid_Token,
|
||||
Out_Of_Range,
|
||||
TODO
|
||||
};
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "gorgonstring.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace Gorgonsub {
|
||||
|
||||
|
@ -45,7 +46,7 @@ namespace Gorgonsub {
|
|||
|
||||
public:
|
||||
Time() { ms = 0; }
|
||||
Time(int ms) { (void)ms; }
|
||||
Time(int _ms) { ms = _ms; }
|
||||
|
||||
void SetMS(int milliseconds) { ms = milliseconds; }
|
||||
int GetMS() const { return ms; }
|
||||
|
@ -53,6 +54,8 @@ namespace Gorgonsub {
|
|||
String GetString(int ms_precision,int h_precision) const;
|
||||
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; }
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace Gorgonsub {
|
|||
private:
|
||||
std::vector<Range> ranges;
|
||||
size_t count;
|
||||
void UpdateCount();
|
||||
|
||||
public:
|
||||
Selection();
|
||||
|
@ -68,11 +69,13 @@ namespace Gorgonsub {
|
|||
void RemoveRange(const Range &range);
|
||||
void AddSelection (const Selection ¶m);
|
||||
void RemoveSelection (const Selection ¶m);
|
||||
void NormalizeRanges ();
|
||||
|
||||
size_t GetCount() const { return count; }
|
||||
size_t GetRanges() const { return ranges.size(); }
|
||||
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 GetLinesInRange(size_t range) const { return ranges.at(range).GetSize(); }
|
||||
bool IsContiguous() const { return GetRanges() <= 1; }
|
||||
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
|
||||
#pragma once
|
||||
#include "Gorgonsub.h"
|
||||
#include "gorgonstring.h"
|
||||
|
||||
|
||||
/////////////
|
||||
|
|
|
@ -142,7 +142,7 @@ ActionPtr ActionModify::GetAntiAction(const Model &model) const
|
|||
|
||||
// Store the whole original line
|
||||
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
|
||||
void ActionModify::Execute(Model &model)
|
||||
{
|
||||
// Find the section to insert it on
|
||||
// Find the section to modify
|
||||
String sectionName = section;
|
||||
if (sectionName.IsEmpty()) sectionName = entry->GetDefaultGroup();
|
||||
SectionPtr sect = GetSection(model,sectionName);
|
||||
|
@ -163,3 +163,54 @@ void ActionModify::Execute(Model &model)
|
|||
}
|
||||
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);
|
||||
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 Unsupported_Format_Feature: return "This feature is not supported by this format.";
|
||||
case Invalid_Token: return "Invalid type for this token.";
|
||||
case Out_Of_Range: return "Out of range.";
|
||||
case TODO: return "TODO";
|
||||
}
|
||||
return "Invalid code.";
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace Gorgonsub {
|
|||
// Basic features
|
||||
String GetDefaultGroup() const { return L"Events"; }
|
||||
SectionEntryPtr Clone() const { return SectionEntryPtr(new DialogueASS(*this)); }
|
||||
DeltaCoderPtr GetDeltaCoder() const { return DeltaCoderPtr(new DialogueASSDeltaCoder()); }
|
||||
//DeltaCoderPtr GetDeltaCoder() const { return DeltaCoderPtr(new DialogueASSDeltaCoder()); }
|
||||
|
||||
// Capabilities
|
||||
bool HasText() const { return true; }
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
// Contact: mailto:amz@aegisub.net
|
||||
//
|
||||
|
||||
#include "selection.h"
|
||||
#include "gorgonsub.h"
|
||||
using namespace Gorgonsub;
|
||||
|
||||
|
||||
|
@ -49,8 +49,8 @@ Selection::Selection()
|
|||
// Adds a range
|
||||
void Selection::AddRange(const Range &range)
|
||||
{
|
||||
// TODO
|
||||
(void) range;
|
||||
ranges.push_back(range);
|
||||
UpdateCount();
|
||||
}
|
||||
|
||||
|
||||
|
@ -60,6 +60,53 @@ void Selection::RemoveRange(const Range &range)
|
|||
{
|
||||
// TODO
|
||||
(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 len = ranges.size();
|
||||
for (size_t i=0;i<len;i++) {
|
||||
cur += ranges[i].GetSize();
|
||||
if (cur > n) return ranges[i].GetLine(n-ranges[i].GetStart());
|
||||
size_t curLen = ranges[i].GetSize();
|
||||
if (cur+curLen > n) return ranges[i].GetLine(n-cur);
|
||||
cur += curLen;
|
||||
}
|
||||
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
|
||||
void Selection::RemoveSelection (const Selection ¶m)
|
||||
|
@ -93,3 +153,11 @@ void Selection::RemoveSelection (const Selection ¶m)
|
|||
(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");
|
||||
timer.Pause();
|
||||
cout << "Done in " << timer.Time() << " ms.\n";
|
||||
|
||||
// Create line to be inserted
|
||||
cout << "Creating data... ";
|
||||
SectionEntryDialoguePtr line = control.CreateDialogue();
|
||||
line->SetText(L"Hi, testing insertion of lines!");
|
||||
cout << "Done.\n";
|
||||
system("pause");
|
||||
|
||||
// Issue an action
|
||||
ActionListPtr actions = control.CreateActionList(L"Test");
|
||||
SectionEntryDialoguePtr diag = dynamic_pointer_cast<SectionEntryDialogue> (actions->ModifyLine(10,L"Events"));
|
||||
diag->SetText(L"Hay guise sup");
|
||||
actions->Finish();
|
||||
cout << "Executing action 100 times... ";
|
||||
timer.Start();
|
||||
for (size_t i=0;i<100;i++) {
|
||||
ActionListPtr actions = control.CreateActionList(L"Test");
|
||||
Selection selection;
|
||||
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();
|
||||
}
|
||||
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
|
||||
cout << "Undoing and redoing 1000 times... ";
|
||||
|
|
|
@ -14,7 +14,9 @@ x = done
|
|||
[x] Text file reader/writer
|
||||
[ ] Automatic character set detection
|
||||
[ ] Conversion between subtitle formats
|
||||
[ ] Override tag support
|
||||
[:] ASS format support
|
||||
[ ] ASS override tags
|
||||
[ ] Simple subtitles (srt, microdvd, encore, etc) format support
|
||||
[ ] Matroska-embedded subtitles support
|
||||
[ ] DVD subtitles support (*)
|
||||
|
|
Loading…
Reference in a new issue