mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
BugFix: history manager undo and redo for delete and recreation of
source, delete multiple sources or clones, etc.
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -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_;
|
||||||
|
|||||||
12
Mixer.cpp
12
Mixer.cpp
@@ -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());
|
||||||
|
|||||||
2
Mixer.h
2
Mixer.h
@@ -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);
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user