diff --git a/aegilib/include/aegilib/actionlist.h b/aegilib/include/aegilib/actionlist.h index f0aa27e38..1922322fe 100644 --- a/aegilib/include/aegilib/actionlist.h +++ b/aegilib/include/aegilib/actionlist.h @@ -51,21 +51,22 @@ namespace Gorgonsub { private: String actionName; + String owner; Model &model; std::list actions; bool valid; + bool undoAble; - ActionList(); - ActionList(Model &model,const String actionName); + ActionList(Model &model,const String actionName,const String owner,bool undoAble); void Start(const String actionName); + void AddAction(const Action &action); public: ~ActionList(); - void AddAction(const Action &action); void Finish(); - void InsertLine(SectionEntryPtr line,int position=-1,const String section=L""); + void RemoveLine(int position,const String section); }; typedef shared_ptr ActionListPtr; diff --git a/aegilib/include/aegilib/controller.h b/aegilib/include/aegilib/controller.h index c955e94db..15cf423b4 100644 --- a/aegilib/include/aegilib/controller.h +++ b/aegilib/include/aegilib/controller.h @@ -52,11 +52,16 @@ namespace Gorgonsub { public: Controller (Model &model); - ActionListPtr CreateActionList(const String title); + ActionListPtr CreateActionList(const String title,const String owner=L"",bool undoAble=true); void LoadFile(const String filename,const String encoding=L""); void SaveFile(const String filename,const String encoding=L"UTF-8"); + bool CanUndo(const String owner=L"") const; + bool CanRedo(const String owner=L"") const; + void Undo(const String owner=L""); + void Redo(const String owner=L""); + SectionEntryDialoguePtr CreateDialogue(); SectionEntryStylePtr CreateStyle(); diff --git a/aegilib/include/aegilib/model.h b/aegilib/include/aegilib/model.h index acdce1b96..5a1517f43 100644 --- a/aegilib/include/aegilib/model.h +++ b/aegilib/include/aegilib/model.h @@ -36,6 +36,7 @@ #pragma once #include #include +#include #include #include "actionlist.h" #include "section.h" @@ -56,7 +57,7 @@ namespace Gorgonsub { friend class Controller; typedef std::list ViewList; - typedef std::list ActionStack; + typedef std::stack > ActionStack; typedef shared_ptr FormatPtr; private: @@ -66,15 +67,19 @@ namespace Gorgonsub { ViewList listeners; bool readOnly; FormatPtr format; - - void ProcessActionList(const ActionList &actionList,bool insertInStack); + + void ProcessActionList(const ActionList &actionList); + void DoActionList(const ActionListPtr list); void DoAction(const Action &action); + ActionListPtr CreateAntiActionList(const ActionListPtr &manipulator); + Action GetAntiAction(const Action &action); bool CanUndo(const String owner=L"") const; bool CanRedo(const String owner=L"") const; - bool Undo(const String owner=L""); - bool Redo(const String owner=L""); + void Undo(const String owner=L""); + void Redo(const String owner=L""); + void ActivateStack(ActionStack &from,ActionStack &to,const String &owner); void DispatchNotifications(const Notification ¬ification) const; diff --git a/aegilib/include/aegilib/section.h b/aegilib/include/aegilib/section.h index cfb41f6e7..ac2ad1d5d 100644 --- a/aegilib/include/aegilib/section.h +++ b/aegilib/include/aegilib/section.h @@ -69,7 +69,7 @@ namespace Gorgonsub { void AddEntry(SectionEntryPtr entry,int pos=-1); void RemoveEntryByIndex(size_t index); void RemoveEntry(SectionEntryPtr entry); - SectionEntryConstPtr GetEntry(size_t index) const; + SectionEntryPtr GetEntry(size_t index) const; size_t GetEntryCount() const; }; typedef shared_ptr
SectionPtr; diff --git a/aegilib/src/actionlist.cpp b/aegilib/src/actionlist.cpp index 7384b21ad..d2fa2303c 100644 --- a/aegilib/src/actionlist.cpp +++ b/aegilib/src/actionlist.cpp @@ -39,8 +39,8 @@ using namespace Gorgonsub; /////////////// // Constructor -ActionList::ActionList(Model &_model,String _actionName) -: model(_model) +ActionList::ActionList(Model &_model,String _actionName,const String _owner,bool _undoAble) +: model(_model), owner(_owner), undoAble(_undoAble) { Start(_actionName); } @@ -77,7 +77,7 @@ void ActionList::Start(const String name) void ActionList::Finish() { if (valid) { - model.ProcessActionList(*this,false); + model.ProcessActionList(*this); actions.clear(); valid = false; } @@ -91,3 +91,12 @@ void ActionList::InsertLine(SectionEntryPtr line,int position,const String secti Action action = Action(ACTION_INSERT,line,section,position); AddAction(action); } + + +///////////////////////////////// +// Create a "remove line" action +void ActionList::RemoveLine(int position,const String section) +{ + Action action = Action(ACTION_REMOVE,SectionEntryPtr(),section,position); + AddAction(action); +} diff --git a/aegilib/src/controller.cpp b/aegilib/src/controller.cpp index 10dc5c423..9ee9a747d 100644 --- a/aegilib/src/controller.cpp +++ b/aegilib/src/controller.cpp @@ -33,6 +33,7 @@ // Contact: mailto:amz@aegisub.net // +#include "controller.h" #include "Gorgonsub.h" using namespace Gorgonsub; @@ -47,9 +48,9 @@ Controller::Controller(Model &_model) ///////////////////////// // Create an action list -ActionListPtr Controller::CreateActionList(const String title) +ActionListPtr Controller::CreateActionList(const String title,const String owner,bool undoAble) { - return ActionListPtr (new ActionList(model,title)); + return ActionListPtr (new ActionList(model,title,owner,undoAble)); } @@ -91,3 +92,23 @@ SectionEntryStylePtr Controller::CreateStyle() { return GetFormat()->CreateStyle(); } + + +//////// +// Undo +bool Controller::CanUndo(const String owner) const +{ + return model.CanUndo(owner); +} +bool Controller::CanRedo(const String owner) const +{ + return model.CanRedo(owner); +} +void Controller::Undo(const String owner) +{ + model.Undo(owner); +} +void Controller::Redo(const String owner) +{ + model.Redo(owner); +} diff --git a/aegilib/src/model.cpp b/aegilib/src/model.cpp index fc700462d..9874c1edb 100644 --- a/aegilib/src/model.cpp +++ b/aegilib/src/model.cpp @@ -58,19 +58,28 @@ void Model::DispatchNotifications(const Notification ¬ification) const //////////////////////////// // Processes an action list -void Model::ProcessActionList(const ActionList &_actionList,bool insertInStack) +void Model::ProcessActionList(const ActionList &_actionList) { // Copy the list ActionListPtr actions = ActionListPtr(new ActionList(_actionList)); // Inserts the opposite into the undo stack - if (insertInStack) { - undoStack.push_back(CreateAntiActionList(actions)); - redoStack.clear(); + if (actions->undoAble) { + undoStack.push(CreateAntiActionList(actions)); + redoStack = ActionStack(); } - // Do actions - std::list::iterator cur; + // Execute list + DoActionList(actions); +} + + +////////////////////////// +// Execute an action list +void Model::DoActionList(const ActionListPtr actions) +{ + // Do each action + std::list::const_iterator cur; for (cur=actions->actions.begin();cur!=actions->actions.end();cur++) { DoAction(*cur); } @@ -85,7 +94,8 @@ void Model::ProcessActionList(const ActionList &_actionList,bool insertInStack) void Model::DoAction(const Action &action) { switch (action.GetType()) { - case ACTION_INSERT: { + // Insert a line + case ACTION_INSERT: { // Get the line SectionEntryPtr entry = static_pointer_cast(action.GetData()); @@ -96,6 +106,19 @@ void Model::DoAction(const Action &action) // Insert the line section->AddEntry(entry,action.GetLineNumber()); + return; + } + + // Delete a line + case ACTION_REMOVE: { + // Find the section to remote it from + String sectionName = action.GetSection(); + if (sectionName.IsEmpty()) throw Exception(Exception::TODO); // TODO + SectionPtr section = GetSection(sectionName); + + // Remove the line + section->RemoveEntryByIndex(action.GetLineNumber()); + return; } } } @@ -105,12 +128,52 @@ void Model::DoAction(const Action &action) // Create an anti-actionlist to undo the actions made by a actionlist ActionListPtr Model::CreateAntiActionList(const ActionListPtr &src) { - ActionListPtr dst(new ActionList(*this,src->actionName)); - // TODO + // Create list + ActionListPtr dst(new ActionList(*this,src->actionName,src->owner,false)); + + // Insert anti-actions + std::list::const_reverse_iterator cur; + for (cur=src->actions.rbegin();cur!=src->actions.rend();cur++) { + //std::list::const_iterator cur; + //for (cur=src->actions.begin();cur!=src->actions.end();cur++) { + dst->AddAction(GetAntiAction(*cur)); + } + + // Return return dst; } +/////////////////////////////////////////// +// Create the action opposite to the input +Action Model::GetAntiAction(const Action &action) +{ + switch (action.GetType()) { + // Create a remove + case ACTION_INSERT: { + // Find the section to insert it on + String section = action.GetSection(); + if (section.IsEmpty()) { + SectionEntryPtr entry = static_pointer_cast(action.GetData()); + section = entry->GetDefaultGroup(); + } + + return Action(ACTION_REMOVE,SectionEntryPtr(),section,action.GetLineNumber()); + } + + // Create an insert + case ACTION_REMOVE: { + int line = action.GetLineNumber(); + const String &sName = action.GetSection(); + SectionPtr section = GetSection(sName); + return Action(ACTION_INSERT,section->GetEntry(line),sName,line); + } + } + + throw Exception(Exception::Invalid_ActionList); +} + + ////////////////// // Load subtitles void Model::Load(wxInputStream &input,const FormatPtr _format,const String encoding) @@ -200,6 +263,54 @@ size_t Model::GetSectionCount() const void Model::Clear() { sections.clear(); - undoStack.clear(); - redoStack.clear(); + undoStack = ActionStack(); + redoStack = ActionStack(); +} + + +////////////////// +// Can undo/redo? +bool Model::CanUndo(const String owner) const +{ + (void) owner; + return undoStack.size() > 0; +} +bool Model::CanRedo(const String owner) const +{ + (void) owner; + return redoStack.size() > 0; +} + + +/////////////////// +// Perform an undo +void Model::Undo(const String owner) +{ + ActivateStack(undoStack,redoStack,owner); +} + + +////////////////// +// Perform a redo +void Model::Redo(const String owner) +{ + ActivateStack(redoStack,undoStack,owner); +} + + +///////////////////// +// Perform undo/redo +void Model::ActivateStack(ActionStack &from,ActionStack &to,const String &owner) +{ + // TODO: do something with this + (void) owner; + + // Create opposite + to.push(CreateAntiActionList(from.top())); + + // Process list + DoActionList(from.top()); + + // Pop original + from.pop(); } diff --git a/aegilib/src/section.cpp b/aegilib/src/section.cpp index 25993ed7f..8a3d39393 100644 --- a/aegilib/src/section.cpp +++ b/aegilib/src/section.cpp @@ -70,7 +70,7 @@ void Section::RemoveEntry(SectionEntryPtr entry) } } -SectionEntryConstPtr Section::GetEntry(size_t index) const +SectionEntryPtr Section::GetEntry(size_t index) const { return entries[index]; } diff --git a/aegilib/test/src/main.cpp b/aegilib/test/src/main.cpp index e2d72b6ed..d25742e6f 100644 --- a/aegilib/test/src/main.cpp +++ b/aegilib/test/src/main.cpp @@ -69,9 +69,30 @@ int main () { cout << "Processing actions... "; ActionListPtr actions = control.CreateActionList(L"Insert line"); actions->InsertLine(line,2); + actions->RemoveLine(3,L"Events"); actions->Finish(); cout << "Done.\n"; + // Save subtitles + cout << "Saving file... "; + control.SaveFile(L"subs_out_mid1.ass",L"UTF-8"); + cout << "Done.\n"; + + // Undo + cout << "Undoing... (can undo=" << (control.CanUndo()?"true":"false") << ") "; + control.Undo(); + cout << "Done.\n"; + + // Save subtitles + cout << "Saving file... "; + control.SaveFile(L"subs_out_mid2.ass",L"UTF-8"); + cout << "Done.\n"; + + // Redo + cout << "Undoing... (can redo=" << (control.CanRedo()?"true":"false") << ") "; + control.Redo(); + cout << "Done.\n"; + // Save subtitles cout << "Saving file... "; control.SaveFile(L"subs_out.ass",L"UTF-8");