#include #include #include "Log.h" #include "View.h" #include "Mixer.h" #include "MixingGroup.h" #include "tinyxml2Toolkit.h" //#include "SessionSource.h" #include "ImageProcessingShader.h" #include "SessionVisitor.h" #include "SessionCreator.h" #include "Settings.h" #include "GlmToolkit.h" #include "Interpolator.h" #include "ActionManager.h" #ifndef NDEBUG #define ACTION_DEBUG #endif #define HISTORY_NODE(i) std::to_string(i).insert(0, "H").c_str() #define SNAPSHOT_NODE(i) std::to_string(i).insert(0, "S").c_str() using namespace tinyxml2; Action::Action(): history_step_(0), history_max_step_(0), locked_(false), snapshot_id_(0), snapshot_node_(nullptr), interpolator_(nullptr), interpolator_node_(nullptr) { } void Action::init() { // clean the history history_doc_.Clear(); history_step_ = 0; history_max_step_ = 0; // reset snapshot snapshot_id_ = 0; snapshot_node_ = nullptr; store("Session start"); } void Action::store(const std::string &label) { // ignore if locked or if no label is given if (locked_ || label.empty()) return; // incremental naming of history nodes history_step_++; // erase future for (uint e = history_step_; e <= history_max_step_; e++) { XMLElement *node = history_doc_.FirstChildElement( HISTORY_NODE(e) ); if ( node ) history_doc_.DeleteChild(node); } history_max_step_ = history_step_; // create history node XMLElement *sessionNode = history_doc_.NewElement( HISTORY_NODE(history_step_) ); history_doc_.InsertEndChild(sessionNode); // label describes the action sessionNode->SetAttribute("label", label.c_str()); // view indicates the view when this action occured sessionNode->SetAttribute("view", (int) Mixer::manager().view()->mode()); // get session to operate on Session *se = Mixer::manager().session(); // save all sources using source visitor SessionVisitor sv(&history_doc_, sessionNode); for (auto iter = se->begin(); iter != se->end(); ++iter, sv.setRoot(sessionNode) ) (*iter)->accept(sv); // debug #ifdef ACTION_DEBUG Log::Info("Action stored %d '%s'", history_step_, label.c_str()); // XMLSaveDoc(&xmlDoc_, "/home/bhbn/history.xml"); #endif } void Action::undo() { // not possible to go to 1 -1 = 0 if (history_step_ <= 1) return; // restore always changes step_ to step_ - 1 restore( history_step_ - 1); } void Action::redo() { // not possible to go to max_step_ + 1 if (history_step_ >= history_max_step_) return; // restore always changes step_ to step_ + 1 restore( history_step_ + 1); } void Action::stepTo(uint target) { // get reasonable target uint t = CLAMP(target, 1, history_max_step_); // ignore t == step_ if (t != history_step_) restore(t); } std::string Action::label(uint s) const { std::string l = ""; if (s > 0 && s <= history_max_step_) { const XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(s)); l = sessionNode->Attribute("label"); } return l; } void Action::restore(uint target) { // lock locked_ = true; // get history node of target step history_step_ = CLAMP(target, 1, history_max_step_); XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(history_step_) ); if (sessionNode) { // ask view to refresh, and switch to action view if user prefers int view = Settings::application.current_view ; if (Settings::application.action_history_follow_view) sessionNode->QueryIntAttribute("view", &view); Mixer::manager().setView( (View::Mode) view); // actually restore Mixer::manager().restore(sessionNode); } // free locked_ = false; } void Action::snapshot(const std::string &label) { // ignore if locked or if no label is given if (locked_ || label.empty()) return; // get session to operate on Session *se = Mixer::manager().session(); // create snapshot id u_int64_t id = GlmToolkit::uniqueId(); se->snapshots()->keys_.push_back(id); // create snapshot node XMLElement *sessionNode = se->snapshots()->xmlDoc_->NewElement( SNAPSHOT_NODE(id) ); se->snapshots()->xmlDoc_->InsertEndChild(sessionNode); // label describes the snapshot sessionNode->SetAttribute("label", label.c_str()); // save all sources using source visitor SessionVisitor sv(se->snapshots()->xmlDoc_, sessionNode); for (auto iter = se->begin(); iter != se->end(); ++iter, sv.setRoot(sessionNode) ) (*iter)->accept(sv); // TODO: copy current action history instead? // debug #ifdef ACTION_DEBUG Log::Info("Snapshot stored %d '%s'", id, label.c_str()); #endif } void Action::open(uint64_t snapshotid) { if ( snapshot_id_ != snapshotid ) { // get snapshot node of target in current session Session *se = Mixer::manager().session(); snapshot_node_ = se->snapshots()->xmlDoc_->FirstChildElement( SNAPSHOT_NODE(snapshotid) ); if (snapshot_node_) snapshot_id_ = snapshotid; else snapshot_id_ = 0; interpolator_node_ = nullptr; } } void Action::replace(uint64_t snapshotid) { // ignore if locked or if no label is given if (locked_) return; if (snapshotid > 0) open(snapshotid); if (snapshot_node_) { // remember label std::string label = snapshot_node_->Attribute("label"); // remove previous node Session *se = Mixer::manager().session(); se->snapshots()->xmlDoc_->DeleteChild( snapshot_node_ ); // create snapshot node snapshot_node_ = se->snapshots()->xmlDoc_->NewElement( SNAPSHOT_NODE(snapshot_id_) ); se->snapshots()->xmlDoc_->InsertEndChild(snapshot_node_); // save all sources using source visitor SessionVisitor sv(se->snapshots()->xmlDoc_, snapshot_node_); for (auto iter = se->begin(); iter != se->end(); ++iter, sv.setRoot(snapshot_node_) ) (*iter)->accept(sv); // restore label snapshot_node_->SetAttribute("label", label.c_str()); // debug #ifdef ACTION_DEBUG Log::Info("Snapshot replaced %d '%s'", snapshot_id_, label.c_str()); #endif } else snapshot_id_ = 0; } std::list Action::snapshots() const { Session *se = Mixer::manager().session(); return se->snapshots()->keys_; } std::string Action::label(uint64_t snapshotid) const { std::string label = ""; // get snapshot node of target in current session Session *se = Mixer::manager().session(); const XMLElement *snap = se->snapshots()->xmlDoc_->FirstChildElement( SNAPSHOT_NODE(snapshotid) ); if (snap) label = snap->Attribute("label"); return label; } void Action::setLabel (uint64_t snapshotid, const std::string &label) { open(snapshotid); if (snapshot_node_) snapshot_node_->SetAttribute("label", label.c_str()); } void Action::remove(uint64_t snapshotid) { if (snapshotid > 0) open(snapshotid); if (snapshot_node_) { // remove Session *se = Mixer::manager().session(); se->snapshots()->xmlDoc_->DeleteChild( snapshot_node_ ); se->snapshots()->keys_.remove( snapshot_id_ ); } snapshot_node_ = nullptr; snapshot_id_ = 0; } void Action::restore(uint64_t snapshotid) { // lock locked_ = true; if (snapshotid > 0) open(snapshotid); if (snapshot_node_) // actually restore Mixer::manager().restore(snapshot_node_); // free locked_ = false; store("Snapshot " + label(snapshot_id_)); } float Action::interpolation() { float ret = 0.f; if ( interpolator_node_ == snapshot_node_ && interpolator_) ret = interpolator_->current(); return ret; } void Action::interpolate(float val, uint64_t snapshotid) { if (snapshotid > 0) open(snapshotid); if (snapshot_node_) { if ( interpolator_node_ != snapshot_node_ ) { // change interpolator if (interpolator_) delete interpolator_; // create new interpolator interpolator_ = new Interpolator; // current session Session *se = Mixer::manager().session(); XMLElement* N = snapshot_node_->FirstChildElement("Source"); for( ; N ; N = N->NextSiblingElement()) { // check if a source with the given id exists in the session uint64_t id_xml_ = 0; N->QueryUnsigned64Attribute("id", &id_xml_); SourceList::iterator sit = se->find(id_xml_); // a source with this id exists if ( sit != se->end() ) { // read target in the snapshot xml SourceCore target; SessionLoader::XMLToSourcecore(N, target); // add an interpolator for this source interpolator_->add(*sit, target); } } // operate interpolation on opened snapshot interpolator_node_ = snapshot_node_; } if (interpolator_) { // Log::Info("Action::interpolate %f", val); interpolator_->apply( val ); } } }