diff --git a/ActionManager.cpp b/ActionManager.cpp index 7bce473..339c744 100644 --- a/ActionManager.cpp +++ b/ActionManager.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "Log.h" #include "View.h" @@ -18,25 +19,45 @@ #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) { } -void Action::clear() +void Action::init(const std::string &xml) { // clean the history history_doc_.Clear(); history_step_ = 0; history_max_step_ = 0; + // start fresh + store("Session start"); // clean the snapshots snapshots_doc_.Clear(); snapshots_.clear(); - // start fresh - store("Session start"); + if ( !xml.empty() ) { + if ( XMLResultError( snapshots_doc_.Parse( xml.c_str() ) )) + Log::Info("Failed to load snapshots"); + else + { + const XMLElement* N = snapshots_doc_.RootElement(); + for( ; N ; N=N->NextSiblingElement()) { + + char c; + u_int64_t id = 0; + std::istringstream nodename( N->Name() ); + nodename >> c >> id; + snapshots_.push_back(id); + } + } + } + } void Action::store(const std::string &label) @@ -47,19 +68,17 @@ void Action::store(const std::string &label) // incremental naming of history nodes history_step_++; - std::string nodename = "H" + std::to_string(history_step_); // erase future for (uint e = history_step_; e <= history_max_step_; e++) { - std::string name = "H" + std::to_string(e); - XMLElement *node = history_doc_.FirstChildElement( name.c_str() ); + 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( nodename.c_str() ); + XMLElement *sessionNode = history_doc_.NewElement( HISTORY_NODE(history_step_) ); history_doc_.InsertEndChild(sessionNode); // label describes the action sessionNode->SetAttribute("label", label.c_str()); @@ -76,7 +95,7 @@ void Action::store(const std::string &label) // debug #ifdef ACTION_DEBUG - Log::Info("Action stored %s '%s'", nodename.c_str(), label.c_str()); + Log::Info("Action stored %d '%s'", history_step_, label.c_str()); // XMLSaveDoc(&xmlDoc_, "/home/bhbn/history.xml"); #endif } @@ -117,8 +136,7 @@ std::string Action::label(uint s) const std::string l = ""; if (s > 0 && s <= history_max_step_) { - std::string nodename = "H" + std::to_string(s); - const XMLElement *sessionNode = history_doc_.FirstChildElement( nodename.c_str() ); + const XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(s)); l = sessionNode->Attribute("label"); } return l; @@ -131,8 +149,7 @@ void Action::restore(uint target) // get history node of target step history_step_ = CLAMP(target, 1, history_max_step_); - std::string nodename = "H" + std::to_string(history_step_); - XMLElement *sessionNode = history_doc_.FirstChildElement( nodename.c_str() ); + XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(history_step_) ); if (sessionNode) { @@ -156,13 +173,15 @@ void Action::snapshot(const std::string &label) if (locked_ || label.empty()) return; + // create snapshot id u_int64_t id = GlmToolkit::uniqueId(); snapshots_.push_back(id); - // create history node - XMLElement *sessionNode = snapshots_doc_.NewElement( std::to_string(id).c_str() ); + // create snapshot node + XMLElement *sessionNode = snapshots_doc_.NewElement( SNAPSHOT_NODE(id) ); snapshots_doc_.InsertEndChild(sessionNode); - // label describes the action + + // label describes the snapshot sessionNode->SetAttribute("label", label.c_str()); // get session to operate on @@ -173,17 +192,29 @@ void Action::snapshot(const std::string &label) for (auto iter = se->begin(); iter != se->end(); iter++, sv.setRoot(sessionNode) ) (*iter)->accept(sv); + // TODO: copy action history instead? + // debug #ifdef ACTION_DEBUG - Log::Info("Snapshot stored %s '%s'", std::to_string(id).c_str(), label.c_str()); + Log::Info("Snapshot stored %d '%s'", id, label.c_str()); // XMLSaveDoc(&xmlDoc_, "/home/bhbn/history.xml"); #endif } -std::string Action::label(uint64_t s) const +const char *Action::snapshotsDescription() +{ + // get compact string + XMLPrinter xmlPrint; + snapshots_doc_.Print( &xmlPrint ); + + return xmlPrint.CStr(); +} + + +std::string Action::label(uint64_t snapshotid) const { std::string l = ""; - const XMLElement *sessionNode = snapshots_doc_.FirstChildElement( std::to_string(s).c_str() ); + const XMLElement *sessionNode = snapshots_doc_.FirstChildElement( SNAPSHOT_NODE(snapshotid) ); if (sessionNode) { l = sessionNode->Attribute("label"); @@ -194,7 +225,7 @@ std::string Action::label(uint64_t s) const void Action::setLabel (uint64_t snapshotid, const std::string &label) { // get history node of target - XMLElement *sessionNode = snapshots_doc_.FirstChildElement( std::to_string(snapshotid).c_str() ); + XMLElement *sessionNode = snapshots_doc_.FirstChildElement( SNAPSHOT_NODE(snapshotid) ); if (sessionNode) { sessionNode->SetAttribute("label", label.c_str()); @@ -204,7 +235,7 @@ void Action::setLabel (uint64_t snapshotid, const std::string &label) void Action::remove(uint64_t snapshotid) { // get history node of target - XMLElement *sessionNode = snapshots_doc_.FirstChildElement( std::to_string(snapshotid).c_str() ); + XMLElement *sessionNode = snapshots_doc_.FirstChildElement( SNAPSHOT_NODE(snapshotid) ); if (sessionNode) { snapshots_doc_.DeleteChild( sessionNode ); @@ -218,7 +249,7 @@ void Action::restore(uint64_t snapshotid) locked_ = true; // get history node of target - XMLElement *sessionNode = snapshots_doc_.FirstChildElement( std::to_string(snapshotid).c_str() ); + XMLElement *sessionNode = snapshots_doc_.FirstChildElement( SNAPSHOT_NODE(snapshotid) ); if (sessionNode) { // actually restore diff --git a/ActionManager.h b/ActionManager.h index 3ce2e78..12ee3e3 100644 --- a/ActionManager.h +++ b/ActionManager.h @@ -23,9 +23,10 @@ public: static Action _instance; return _instance; } + void init(const std::string &xml = ""); + // UNDO History void store(const std::string &label); - void clear(); void undo(); void redo(); void stepTo(uint target); @@ -34,23 +35,27 @@ public: inline uint max() const { return history_max_step_; } std::string label(uint s) const; + // Snapshots void snapshot(const std::string &label); inline std::list snapshots() const { return snapshots_; } - std::string label(uint64_t s) const; void restore(uint64_t snapshotid); void remove (uint64_t snapshotid); + + std::string label(uint64_t snapshotid) const; void setLabel (uint64_t snapshotid, const std::string &label); -private: + const char *snapshotsDescription(); - void restore(uint target); +// inline const tinyxml2::XMLElement *snapshotsRoot() const { return snapshots_doc_.RootElement(); } + +private: tinyxml2::XMLDocument history_doc_; uint history_step_; uint history_max_step_; std::atomic locked_; - + void restore(uint target); tinyxml2::XMLDocument snapshots_doc_; std::list snapshots_; diff --git a/GeometryView.cpp b/GeometryView.cpp index 49bf0af..82ab286 100644 --- a/GeometryView.cpp +++ b/GeometryView.cpp @@ -15,6 +15,7 @@ #include "Mixer.h" #include "defines.h" +#include "Source.h" #include "Settings.h" #include "PickingVisitor.h" #include "DrawVisitor.h" diff --git a/LayerView.cpp b/LayerView.cpp index 3e253fc..caf9d0f 100644 --- a/LayerView.cpp +++ b/LayerView.cpp @@ -14,6 +14,7 @@ #include "Mixer.h" #include "defines.h" +#include "Source.h" #include "Settings.h" #include "Decorations.h" #include "UserInterfaceManager.h" diff --git a/Mixer.cpp b/Mixer.cpp index a6d31cb..08cce02 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -1164,7 +1164,7 @@ void Mixer::swap() back_session_ = nullptr; // reset History manager - Action::manager().clear(); + Action::manager().init( session_->snapshots() ); // notification Log::Notify("Session %s loaded. %d source(s) created.", session_->filename().c_str(), session_->numSource()); diff --git a/MixingView.cpp b/MixingView.cpp index 44ed867..c7fea4f 100644 --- a/MixingView.cpp +++ b/MixingView.cpp @@ -14,6 +14,7 @@ #include "Mixer.h" #include "defines.h" +#include "Source.h" #include "Settings.h" #include "Decorations.h" #include "UserInterfaceManager.h" diff --git a/Session.cpp b/Session.cpp index 4dd9fdd..81a5157 100644 --- a/Session.cpp +++ b/Session.cpp @@ -1,6 +1,7 @@ #include #include "defines.h" +#include "Source.h" #include "Settings.h" #include "FrameBuffer.h" #include "Session.h" @@ -16,7 +17,7 @@ SessionNote::SessionNote(const std::string &t, bool l, int s): label(std::to_str { } -Session::Session() : failedSource_(nullptr), active_(true), fading_target_(0.f), filename_("") +Session::Session() : active_(true), filename_(""), failedSource_(nullptr), snapshots_(""), fading_target_(0.f) { config_[View::RENDERING] = new Group; config_[View::RENDERING]->scale_ = glm::vec3(0.f); diff --git a/Session.h b/Session.h index e82739c..996ce83 100644 --- a/Session.h +++ b/Session.h @@ -3,8 +3,8 @@ #include +#include "SourceList.h" #include "RenderView.h" -#include "Source.h" class FrameGrabber; class MixingGroup; @@ -21,6 +21,16 @@ struct SessionNote SessionNote(const std::string &t = "", bool l = false, int s = 0); }; +struct SessionSnapshot +{ + uint64_t id; + std::string label; + std::string xml; + + SessionSnapshot(const std::string &l, const std::string &desc); +}; + + class Session { public: @@ -110,13 +120,15 @@ public: std::list::iterator deleteMixingGroup (std::list::iterator g); // snapshots - + void setSnapshots (const std::string &xml) { snapshots_ = xml; } + std::string snapshots () const { return snapshots_; } // lock and unlock access (e.g. while saving) void lock (); void unlock (); protected: + bool active_; RenderView render_; std::string filename_; Source *failedSource_; @@ -125,7 +137,7 @@ protected: std::list notes_; std::list mixing_groups_; std::map config_; - bool active_; + std::string snapshots_; std::list grabbers_; float fading_target_; std::mutex access_; diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 830fdf9..f062559 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -100,6 +100,10 @@ void SessionCreator::load(const std::string& filename) for (auto group_it = groups.begin(); group_it != groups.end(); group_it++) session_->link( *group_it ); + // load snapshots + loadSnapshots( xmlDoc_.FirstChildElement("Snapshots") ); + + // load notes loadNotes( xmlDoc_.FirstChildElement("Notes") ); // all good @@ -119,6 +123,15 @@ void SessionCreator::loadConfig(XMLElement *viewsNode) } } +void SessionCreator::loadSnapshots(XMLElement *snapshotsNode) +{ + if (snapshotsNode != nullptr && session_ != nullptr) { + + std::string text = std::string ( snapshotsNode->GetText() ); + session_->setSnapshots( text ); + } +} + void SessionCreator::loadNotes(XMLElement *notesNode) { if (notesNode != nullptr && session_ != nullptr) { diff --git a/SessionCreator.h b/SessionCreator.h index a3200c5..df87fde 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -79,6 +79,7 @@ class SessionCreator : public SessionLoader { void loadConfig(tinyxml2::XMLElement *viewsNode); void loadNotes(tinyxml2::XMLElement *notesNode); + void loadSnapshots(tinyxml2::XMLElement *snapshotNode); public: SessionCreator(int recursion = 0); diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index b357ddd..26ad2e9 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -16,6 +16,7 @@ #include "MediaPlayer.h" #include "MixingGroup.h" #include "SystemToolkit.h" +#include "ActionManager.h" #include #include @@ -73,7 +74,17 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session) views->InsertEndChild(render); } - // 3. optional notes + // 3. snapshots + XMLElement *snapshots = xmlDoc.NewElement("Snapshots"); +// const XMLElement* N = Action::manager().snapshotsRoot(); +// for( ; N ; N=N->NextSiblingElement()) +// snapshots->InsertEndChild( N->DeepClone( &xmlDoc )); + + XMLText *text = xmlDoc.NewText( Action::manager().snapshotsDescription() ); + snapshots->InsertEndChild( text ); + xmlDoc.InsertEndChild(snapshots); + + // 4. optional notes XMLElement *notes = xmlDoc.NewElement("Notes"); xmlDoc.InsertEndChild(notes); for (auto nit = session->beginNotes(); nit != session->endNotes(); ++nit) { @@ -94,6 +105,7 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session) notes->InsertEndChild(note); } + // save file to disk return ( XMLSaveDoc(&xmlDoc, filename) ); } diff --git a/TextureView.cpp b/TextureView.cpp index 99d8b79..6822555 100644 --- a/TextureView.cpp +++ b/TextureView.cpp @@ -14,6 +14,7 @@ #include "defines.h" #include "Mixer.h" +#include "Source.h" #include "Settings.h" #include "Resource.h" #include "PickingVisitor.h" diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index a913c03..17ff742 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -257,6 +257,9 @@ void UserInterface::handleKeyboard() FrameGrabbing::manager().add(fg); } } + else if (ImGui::IsKeyPressed( GLFW_KEY_Y )) { + Action::manager().snapshot( SystemToolkit::date_time_string() ); + } else if (ImGui::IsKeyPressed( GLFW_KEY_Z )) { if (shift_modifier_active) Action::manager().redo(); @@ -286,6 +289,7 @@ void UserInterface::handleKeyboard() else if (ImGui::IsKeyPressed( GLFW_KEY_N ) && shift_modifier_active) { Mixer::manager().session()->addNote(); } + } // No CTRL modifier else { @@ -802,7 +806,7 @@ void UserInterface::showMenuEdit() if ( ImGui::MenuItem( ICON_FA_REDO " Redo", CTRL_MOD "Shift+Z") ) Action::manager().redo(); if ( ImGui::MenuItem( ICON_FA_STAR "+ Snapshot", CTRL_MOD "Y") ) - Action::manager().redo(); + Action::manager().snapshot( SystemToolkit::date_time_string() ); } void UserInterface::showMenuFile() @@ -3191,7 +3195,6 @@ void Navigator::RenderMainPannelVimix() // right buttons ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y )); if ( ImGuiToolkit::IconButton( ICON_FA_STAR "+")) { -// snapshots.push_back( SystemToolkit::date_time_string() ); Action::manager().snapshot( SystemToolkit::date_time_string() ); } // // active list element : delete snapshot button