BugFix: history manager undo and redo for delete and recreation of

source, delete multiple sources or clones, etc.
This commit is contained in:
brunoherbelin
2020-10-11 00:37:04 +02:00
parent 2c1eaff476
commit 650017c8f3
6 changed files with 94 additions and 24 deletions

View File

@@ -33,8 +33,6 @@ void Action::clear()
void Action::store(const std::string &label, uint64_t id) void Action::store(const std::string &label, uint64_t id)
{ {
// TODO use id of item stored
// ignore if locked or if no label is given // ignore if locked or if no label is given
if (locked_ || label.empty()) if (locked_ || label.empty())
return; return;
@@ -71,12 +69,13 @@ void Action::store(const std::string &label, uint64_t id)
// debug // debug
#ifdef ACTION_DEBUG #ifdef ACTION_DEBUG
Log::Info("Action stored %s '%s'", nodename.c_str(), label.c_str()); Log::Info("Action stored %s '%s'", nodename.c_str(), label.c_str());
// XMLSaveDoc(&xmlDoc_, "/home/bhbn/history.xml"); // XMLSaveDoc(&xmlDoc_, "/home/bhbn/history.xml");
#endif #endif
} }
void Action::undo() void Action::undo()
{ {
// not possible to go to 1 -1 = 0
if (step_ <= 1) if (step_ <= 1)
return; return;
@@ -87,11 +86,13 @@ void Action::undo()
uint64_t id = 0; uint64_t id = 0;
sessionNode->QueryUnsigned64Attribute("id", &id); sessionNode->QueryUnsigned64Attribute("id", &id);
// restore always changes step_ to step_ - 1
restore( step_ - 1, id); restore( step_ - 1, id);
} }
void Action::redo() void Action::redo()
{ {
// not possible to go to max_step_ + 1
if (step_ >= max_step_) if (step_ >= max_step_)
return; return;
@@ -101,14 +102,29 @@ void Action::redo()
uint64_t id = 0; uint64_t id = 0;
sessionNode->QueryUnsigned64Attribute("id", &id); sessionNode->QueryUnsigned64Attribute("id", &id);
// restore always changes step_ to step_ + 1
restore( step_ + 1, id); restore( step_ + 1, id);
} }
void Action::stepTo(uint target) void Action::stepTo(uint target)
{ {
// going to target step // get reasonable target
restore(target, 0); uint t = CLAMP(target, 1, max_step_);
// going backward
if ( t < step_ ) {
// go back one step at a time
while (t < step_)
undo();
}
// step forward
else if ( t > step_ ) {
// go forward one step at a time
while (t > step_)
redo();
}
// ignore t == step_
} }
@@ -135,22 +151,31 @@ void Action::restore(uint target, uint64_t id)
XMLElement *sessionNode = xmlDoc_.FirstChildElement( nodename.c_str() ); XMLElement *sessionNode = xmlDoc_.FirstChildElement( nodename.c_str() );
#ifdef ACTION_DEBUG #ifdef ACTION_DEBUG
Log::Info("Restore %s '%s'", nodename.c_str(), sessionNode->Attribute("label")); Log::Info("Restore %s '%s' ", nodename.c_str(), sessionNode->Attribute("label"));
#endif #endif
// sessionsources contains ids of all sources currently in the session // we operate on the current session
std::list<uint64_t> sessionsources = Mixer::manager().session()->getIdList(); Session *se = Mixer::manager().session();
if (se == nullptr)
return;
// sessionsources contains list of ids of all sources currently in the session
std::list<uint64_t> sessionsources = se->getIdList();
// for( auto it = sessionsources.begin(); it != sessionsources.end(); it++)
// Log::Info("sessionsources id %s", std::to_string(*it).c_str());
// load history status: // load history status:
// - if a source exists, its attributes are updated // - if a source exists, its attributes are updated, and that's all
// - if a source does not exists (in session), it is created in session // - if a source does not exists (in session), it is created in the session
SessionLoader loader( Mixer::manager().session() ); SessionLoader loader( se );
loader.load( sessionNode ); loader.load( sessionNode );
// loadersources contains ids of all sources generated by loader // loadersources contains list of ids of all sources generated by loader
std::list<uint64_t> loadersources = loader.getIdList(); std::list<uint64_t> loadersources = loader.getIdList();
// for( auto it = loadersources.begin(); it != loadersources.end(); it++)
// Log::Info("loadersources id %s", std::to_string(*it).c_str());
// remove intersect of both lists (sources were updated) // remove intersect of both lists (sources were updated by SessionLoader)
for( auto lsit = loadersources.begin(); lsit != loadersources.end(); ){ for( auto lsit = loadersources.begin(); lsit != loadersources.end(); ){
auto ssit = std::find(sessionsources.begin(), sessionsources.end(), (*lsit)); auto ssit = std::find(sessionsources.begin(), sessionsources.end(), (*lsit));
if ( ssit != sessionsources.end() ) { if ( ssit != sessionsources.end() ) {
@@ -163,13 +188,25 @@ void Action::restore(uint target, uint64_t id)
// remaining ids in list sessionsources : to remove // remaining ids in list sessionsources : to remove
while ( !sessionsources.empty() ){ while ( !sessionsources.empty() ){
Source *s = Mixer::manager().findSource( sessionsources.front() ); Source *s = Mixer::manager().findSource( sessionsources.front() );
Mixer::manager().detach( s ); if (s!=nullptr) {
Mixer::manager().session()->deleteSource( s ); #ifdef ACTION_DEBUG
Log::Info("Delete id %s", std::to_string(sessionsources.front() ).c_str());
#endif
// remove the source from the mixer
Mixer::manager().detach( s );
// delete source from session
se->deleteSource( s );
}
sessionsources.pop_front(); sessionsources.pop_front();
} }
// remaining ids in list loadersources : to add // remaining ids in list loadersources : to add
while ( !loadersources.empty() ){ while ( !loadersources.empty() ){
Log::Info("Recreate id %s", std::to_string(loadersources.front() ).c_str()); #ifdef ACTION_DEBUG
Log::Info("Recreate id %s to %s", std::to_string(id).c_str(), std::to_string(loadersources.front()).c_str());
#endif
// change the history to match the new id
replaceSourceId(id, loadersources.front());
// add the source to the mixer
Mixer::manager().attach( Mixer::manager().findSource( loadersources.front() ) ); Mixer::manager().attach( Mixer::manager().findSource( loadersources.front() ) );
loadersources.pop_front(); loadersources.pop_front();
} }
@@ -178,3 +215,32 @@ void Action::restore(uint target, uint64_t id)
locked_ = false; locked_ = false;
} }
void Action::replaceSourceId(uint64_t previousid, uint64_t newid)
{
// loop over every session history step
XMLElement* historyNode = xmlDoc_.FirstChildElement("H1");
for( ; historyNode ; historyNode = historyNode->NextSiblingElement())
{
// check if this history node references this id
uint64_t id_history_ = 0;
historyNode->QueryUnsigned64Attribute("id", &id_history_);
if ( id_history_ == previousid )
// change to new id
historyNode->SetAttribute("id", newid);
// loop over every source in session history
XMLElement* sourceNode = historyNode->FirstChildElement("Source");
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
{
// check if this source node has this id
uint64_t id_source_ = 0;
sourceNode->QueryUnsigned64Attribute("id", &id_source_);
if ( id_source_ == previousid )
// change to new id
sourceNode->SetAttribute("id", newid);
}
}
}

View File

@@ -22,7 +22,7 @@ public:
return _instance; return _instance;
} }
void store(const std::string &label, uint64_t id = -1); void store(const std::string &label, uint64_t id = 0);
void clear(); void clear();
void undo(); void undo();
@@ -37,6 +37,7 @@ public:
private: private:
void restore(uint target, uint64_t id); void restore(uint target, uint64_t id);
void replaceSourceId(uint64_t previousid, uint64_t newid);
tinyxml2::XMLDocument xmlDoc_; tinyxml2::XMLDocument xmlDoc_;
uint step_; uint step_;

View File

@@ -193,7 +193,7 @@ void Mixer::update()
if (failedFile != nullptr) { if (failedFile != nullptr) {
Settings::application.recentImport.remove( failedFile->path() ); Settings::application.recentImport.remove( failedFile->path() );
} }
deleteSource(failure); deleteSource(failure, false);
} }
// update views // update views
@@ -349,7 +349,7 @@ void Mixer::insertSource(Source *s, View::Mode m)
attach(s); attach(s);
// new state in history manager // new state in history manager
Action::manager().store(std::string("Insert ")+s->name()); Action::manager().store(s->name() + std::string(" inserted"), s->id());
// if requested to show the source in a given view // if requested to show the source in a given view
// (known to work for View::MIXING et TRANSITION: other views untested) // (known to work for View::MIXING et TRANSITION: other views untested)
@@ -366,12 +366,13 @@ void Mixer::insertSource(Source *s, View::Mode m)
} }
} }
void Mixer::deleteSource(Source *s) void Mixer::deleteSource(Source *s, bool withundo)
{ {
if ( s != nullptr ) if ( s != nullptr )
{ {
// keep name for log // keep name for log
std::string name = s->name(); std::string name = s->name();
uint64_t id = s->id();
// remove source Nodes from all views // remove source Nodes from all views
detach(s); detach(s);
@@ -379,8 +380,9 @@ void Mixer::deleteSource(Source *s)
// delete source // delete source
session_->deleteSource(s); session_->deleteSource(s);
// new state in history manager // store new state in history manager
Action::manager().store(std::string("Delete ")+name); if (withundo)
Action::manager().store(name + std::string(" deleted"), id);
// log // log
Log::Notify("Source %s deleted.", name.c_str()); Log::Notify("Source %s deleted.", name.c_str());

View File

@@ -45,7 +45,7 @@ public:
// operations on sources // operations on sources
void addSource (Source *s); void addSource (Source *s);
void deleteSource (Source *s); void deleteSource (Source *s, bool withundo=true);
void renameSource (Source *s, const std::string &newname); void renameSource (Source *s, const std::string &newname);
void attach (Source *s); void attach (Source *s);
void detach (Source *s); void detach (Source *s);

View File

@@ -219,7 +219,7 @@ void SessionVisitor::visit(ImageProcessingShader &n)
filter->SetAttribute("nbColors", n.nbColors); filter->SetAttribute("nbColors", n.nbColors);
filter->SetAttribute("invert", n.invert); filter->SetAttribute("invert", n.invert);
filter->SetAttribute("chromadelta", n.chromadelta); filter->SetAttribute("chromadelta", n.chromadelta);
filter->SetAttribute("filterid", n.filterid); filter->SetAttribute("filter", n.filterid);
xmlCurrent_->InsertEndChild(filter); xmlCurrent_->InsertEndChild(filter);
XMLElement *gamma = xmlDoc_->NewElement("gamma"); XMLElement *gamma = xmlDoc_->NewElement("gamma");

View File

@@ -34,6 +34,7 @@ Timeline& Timeline::operator = (const Timeline& b)
this->timing_ = b.timing_; this->timing_ = b.timing_;
this->step_ = b.step_; this->step_ = b.step_;
this->gaps_ = b.gaps_; this->gaps_ = b.gaps_;
this->gaps_array_need_update_ = b.gaps_array_need_update_;
memcpy( this->gapsArray_, b.gapsArray_, MAX_TIMELINE_ARRAY * sizeof(float)); memcpy( this->gapsArray_, b.gapsArray_, MAX_TIMELINE_ARRAY * sizeof(float));
memcpy( this->fadingArray_, b.fadingArray_, MAX_TIMELINE_ARRAY * sizeof(float)); memcpy( this->fadingArray_, b.fadingArray_, MAX_TIMELINE_ARRAY * sizeof(float));
} }