Implementation of Copy, Cut & Paste of sources. Keeps description (xml)

of source in clipboard for pasting in another session or in the same
(then it clones existing sources).
This commit is contained in:
brunoherbelin
2020-10-11 23:41:24 +02:00
parent bd922f5bcc
commit c674fa0897
10 changed files with 238 additions and 35 deletions

View File

@@ -18,7 +18,7 @@ using namespace tinyxml2;
#include "Log.h"
#include "View.h"
#include "SystemToolkit.h"
//#include "GarbageVisitor.h"
#include "SessionCreator.h"
#include "SessionVisitor.h"
#include "SessionSource.h"
#include "MediaSource.h"
@@ -243,7 +243,7 @@ Source * Mixer::createSourceFile(const std::string &path)
Settings::application.recentImport.path = SystemToolkit::path_filename(path);
// propose a new name based on uri
renameSource(s, SystemToolkit::base_filename(path));
s->setName(SystemToolkit::base_filename(path));
}
else {
@@ -260,7 +260,7 @@ Source * Mixer::createSourceRender()
RenderSource *s = new RenderSource(session_);
// propose a new name based on session name
renameSource(s, SystemToolkit::base_filename(session_->filename()));
s->setName(SystemToolkit::base_filename(session_->filename()));
return s;
}
@@ -273,7 +273,7 @@ Source * Mixer::createSourceStream(const std::string &gstreamerpipeline)
// propose a new name based on pattern name
std::string name = gstreamerpipeline.substr(0, gstreamerpipeline.find(" "));
renameSource(s, name);
s->setName(name);
return s;
}
@@ -287,7 +287,7 @@ Source * Mixer::createSourcePattern(uint pattern, glm::ivec2 res)
// propose a new name based on pattern name
std::string name = Pattern::pattern_types[pattern];
name = name.substr(0, name.find(" "));
renameSource(s, name);
s->setName(name);
return s;
}
@@ -299,7 +299,7 @@ Source * Mixer::createSourceDevice(const std::string &namedevice)
// propose a new name based on pattern name
std::string name = namedevice.substr(0, namedevice.find(" "));
renameSource(s, name);
s->setName(name);
return s;
}
@@ -332,14 +332,18 @@ Source * Mixer::createSourceClone(const std::string &namesource)
void Mixer::addSource(Source *s)
{
if (s != nullptr)
if (s != nullptr) {
candidate_sources_.push_back(s);
}
}
void Mixer::insertSource(Source *s, View::Mode m)
{
if ( s != nullptr )
{
// avoid duplicate name
renameSource(s, s->name());
// Add source to Session (ignored if source already in)
SourceList::iterator sit = session_->addSource(s);
@@ -884,3 +888,27 @@ void Mixer::set(Session *s)
// swap current with given session
sessionSwapRequested_ = true;
}
void Mixer::paste(const std::string& clipboard)
{
if (clipboard.empty())
return;
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLError eResult = xmlDoc.Parse(clipboard.c_str());
if ( XMLResultError(eResult))
return;
tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
if ( root == nullptr )
return;
SessionLoader loader( session_ );
XMLElement* sourceNode = root->FirstChildElement("Source");
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
{
addSource(loader.cloneOrCreateSource(sourceNode));
}
}

View File

@@ -90,6 +90,9 @@ public:
void close ();
void open (const std::string& filename);
// create sources if clipboard contains well-formed xml text
void paste (const std::string& clipboard);
protected:
Session *session_;

View File

@@ -1,5 +1,9 @@
#include <algorithm>
#include "defines.h"
#include "SessionVisitor.h"
#include <tinyxml2.h>
#include "Selection.h"
Selection::Selection()
@@ -136,4 +140,29 @@ SourceList::iterator Selection::end()
return selection_.end();
}
std::string Selection::xml()
{
std::string x = "";
if (!selection_.empty()) {
// create xml doc and root node
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
selectionNode->SetAttribute("size", selection_.size());
xmlDoc.InsertEndChild(selectionNode);
// fill doc
SessionVisitor sv(&xmlDoc, selectionNode);
for (auto iter = selection_.begin(); iter != selection_.end(); iter++, sv.setRoot(selectionNode) )
(*iter)->accept(sv);
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
}
return x;
}

View File

@@ -26,6 +26,8 @@ public:
bool empty();
uint size ();
std::string xml();
protected:
SourceList::iterator find (Source *s);
SourceList selection_;

View File

@@ -101,6 +101,51 @@ SessionLoader::SessionLoader(Session *session): Visitor(), session_(session)
}
//Source *SessionLoader::createSource(XMLElement *sourceNode)
//{
// // source to load
// Source *load_source = nullptr;
// // check if a source with the given id exists in the session
// uint64_t id__ = 0;
// sourceNode->QueryUnsigned64Attribute("id", &id__);
// SourceList::iterator sit = session_->find(id__);
// // no source with this id exists
// if ( sit == session_->end() ) {
// // create a new source depending on type
// const char *pType = sourceNode->Attribute("type");
// if (!pType)
// continue;
// if ( std::string(pType) == "MediaSource") {
// load_source = new MediaSource;
// }
// else if ( std::string(pType) == "SessionSource") {
// load_source = new SessionSource;
// }
// else if ( std::string(pType) == "RenderSource") {
// load_source = new RenderSource(session_);
// }
// else if ( std::string(pType) == "PatternSource") {
// load_source = new PatternSource;
// }
// else if ( std::string(pType) == "DeviceSource") {
// load_source = new DeviceSource;
// }
// // skip failed (including clones)
// if (!load_source)
// continue;
// // add source to session
// session_->addSource(load_source);
// }
// // get reference to the existing source
// else
// load_source = *sit;
// return load_source;
//}
void SessionLoader::load(XMLElement *sessionNode)
{
@@ -205,6 +250,71 @@ void SessionLoader::load(XMLElement *sessionNode)
}
Source *SessionLoader::cloneOrCreateSource(tinyxml2::XMLElement *sourceNode)
{
xmlCurrent_ = sourceNode;
// source to load
Source *load_source = nullptr;
bool is_clone = false;
// check if a source with the given id exists in the session
uint64_t id__ = 0;
xmlCurrent_->QueryUnsigned64Attribute("id", &id__);
SourceList::iterator sit = session_->find(id__);
// no source with this id exists
if ( sit == session_->end() ) {
// create a new source depending on type
const char *pType = xmlCurrent_->Attribute("type");
if (pType) {
if ( std::string(pType) == "MediaSource") {
load_source = new MediaSource;
}
else if ( std::string(pType) == "SessionSource") {
load_source = new SessionSource;
}
else if ( std::string(pType) == "RenderSource") {
load_source = new RenderSource(session_);
}
else if ( std::string(pType) == "PatternSource") {
load_source = new PatternSource;
}
else if ( std::string(pType) == "DeviceSource") {
load_source = new DeviceSource;
}
else if ( std::string(pType) == "CloneSource") {
// clone from given origin
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
if (originNode) {
std::string sourcename = std::string ( originNode->GetText() );
SourceList::iterator origin = session_->find(sourcename);
// found the orign source
if (origin != session_->end())
load_source = (*origin)->clone();
}
}
}
}
// clone existing source
else {
load_source = (*sit)->clone();
is_clone = true;
}
// apply config to source
if (load_source) {
load_source->accept(*this);
// reset mixing (force to place in mixing scene)
load_source->group(View::MIXING)->translation_ = glm::vec3(DEFAULT_MIXING_TRANSLATION, 0.f);
// increment depth for clones (avoid supperposition)
if (is_clone)
load_source->group(View::LAYER)->translation_.z += 0.2f;
}
return load_source;
}
void SessionLoader::XMLToNode(tinyxml2::XMLElement *xml, Node &n)
{

View File

@@ -12,33 +12,36 @@ class Session;
class SessionLoader : public Visitor {
public:
SessionLoader(Session *session);
inline Session *session() const { return session_; }
void load(tinyxml2::XMLElement *sessionNode);
inline std::list<uint64_t> getIdList() const { return sources_id_; }
// Elements of Scene
void visit(Node& n) override;
Source *cloneOrCreateSource(tinyxml2::XMLElement *sourceNode);
void visit(Scene& n) override {}
void visit(Group& n) override {}
void visit(Switch& n) override {}
void visit(Primitive& n) override {}
void visit(Surface& n) override {}
void visit(ImageSurface& n) override {}
void visit(MediaSurface& n) override {}
void visit(FrameBufferSurface& n) override {}
void visit(LineStrip& n) override {}
void visit(LineSquare&) override {}
void visit(LineCircle& n) override {}
void visit(Mesh& n) override {}
// Elements of Scene
void visit (Node& n) override;
void visit (Scene&) override {}
void visit (Group&) override {}
void visit (Switch&) override {}
void visit (Primitive&) override {}
void visit (Surface&) override {}
void visit (ImageSurface&) override {}
void visit (MediaSurface&) override {}
void visit (FrameBufferSurface&) override {}
void visit (LineStrip&) override {}
void visit (LineSquare&) override {}
void visit (LineCircle&) override {}
void visit (Mesh&) override {}
// Elements with attributes
void visit(MediaPlayer& n) override;
void visit(Shader& n) override;
void visit(ImageShader& n) override;
void visit(ImageProcessingShader& n) override;
void visit (MediaPlayer& n) override;
void visit (Shader& n) override;
void visit (ImageShader& n) override;
void visit (ImageProcessingShader& n) override;
// Sources
void visit (Source& s) override;

View File

@@ -34,7 +34,7 @@ Source::Source() : initialized_(false), active_(true), need_update_(true)
groups_[View::MIXING] = new Group;
groups_[View::MIXING]->visible_ = false;
groups_[View::MIXING]->scale_ = glm::vec3(0.15f, 0.15f, 1.f);
groups_[View::MIXING]->translation_ = glm::vec3(-1.f, 1.f, 0.f);
groups_[View::MIXING]->translation_ = glm::vec3(DEFAULT_MIXING_TRANSLATION, 0.f);
frames_[View::MIXING] = new Switch;
Frame *frame = new Frame(Frame::ROUND, Frame::THIN, Frame::DROP);
@@ -102,6 +102,7 @@ Source::Source() : initialized_(false), active_(true), need_update_(true)
// default layer nodes
groups_[View::LAYER] = new Group;
groups_[View::LAYER]->visible_ = false;
groups_[View::LAYER]->translation_.z = -1.f;
frames_[View::LAYER] = new Switch;
frame = new Frame(Frame::ROUND, Frame::THIN, Frame::PERSPECTIVE);

View File

@@ -8,6 +8,8 @@
#include "View.h"
#define DEFAULT_MIXING_TRANSLATION -1.f, 1.f
class ImageShader;
class ImageProcessingShader;
class FrameBuffer;

View File

@@ -282,8 +282,15 @@ void UserInterface::handleKeyboard()
Settings::application.widget.media_player = !Settings::application.widget.media_player;
}
else if (ImGui::IsKeyPressed( GLFW_KEY_A )) {
// select all
Mixer::manager().view()->selectAll();
if (shift_modifier_active)
{
// clear selection
Mixer::manager().unsetCurrentSource();
Mixer::selection().clear();
}
else
// select all
Mixer::manager().view()->selectAll();
}
else if (ImGui::IsKeyPressed( GLFW_KEY_R )) {
// toggle recording
@@ -299,6 +306,23 @@ void UserInterface::handleKeyboard()
else
Action::manager().undo();
}
else if (ImGui::IsKeyPressed( GLFW_KEY_C )) {
std::string clipboard = Mixer::selection().xml();
if (!clipboard.empty())
ImGui::SetClipboardText(clipboard.c_str());
}
else if (ImGui::IsKeyPressed( GLFW_KEY_X )) {
std::string clipboard = Mixer::selection().xml();
if (!clipboard.empty()) {
ImGui::SetClipboardText(clipboard.c_str());
Mixer::manager().deleteSelection();
}
}
else if (ImGui::IsKeyPressed( GLFW_KEY_V )) {
auto clipboard = ImGui::GetClipboardText();
if (clipboard != nullptr && strlen(clipboard) > 0)
Mixer::manager().paste(clipboard);
}
}
// No CTRL modifier
else {
@@ -348,9 +372,9 @@ void UserInterface::handleKeyboard()
// confirmation for leaving vimix: prevent un-wanted Ctrl+Q, but make it easy to confirm
if (ImGui::BeginPopup("confirm_quit_popup"))
{
ImGui::Text(" Leave vimix? ");
ImGui::Text(" Leave vimix? [Q to confirm]");
// Clic Quit or press Q to confirm exit
if (ImGui::Button( ICON_FA_POWER_OFF " Quit ") ||
if (ImGui::Button( ICON_FA_POWER_OFF " Quit ", ImVec2(250,0)) ||
( !confirm_quit_popup && ImGui::IsKeyPressed( GLFW_KEY_Q )) )
Rendering::manager().close();
ImGui::EndPopup();

View File

@@ -528,7 +528,7 @@ void MixingView::setAlpha(Source *s)
// move the layer node of the source
Group *sourceNode = s->group(mode_);
glm::vec2 mix_pos = glm::vec2(sourceNode->translation_);
glm::vec2 mix_pos = glm::vec2(DEFAULT_MIXING_TRANSLATION);
for(NodeSet::iterator it = scene.ws()->begin(); it != scene.ws()->end(); it++) {
@@ -1307,9 +1307,12 @@ float LayerView::setDepth(Source *s, float d)
if (!s)
return -1.f;
float depth = d;
// move the layer node of the source
Group *sourceNode = s->group(mode_);
// negative depth given; find the front most depth
float depth = d < 0.f ? sourceNode->translation_.z : d;
// negative or no depth given; find the front most depth
if ( depth < 0.f ) {
Node *front = scene.ws()->front();
if (front)
@@ -1318,8 +1321,6 @@ float LayerView::setDepth(Source *s, float d)
depth = 0.5f;
}
// move the layer node of the source
Group *sourceNode = s->group(mode_);
// move on x
sourceNode->translation_.x = CLAMP( -depth, -(SCENE_DEPTH - 2.f), 0.f);
// discretized translation with ALT