mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
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:
42
Mixer.cpp
42
Mixer.cpp
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
3
Mixer.h
3
Mixer.h
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public:
|
||||
bool empty();
|
||||
uint size ();
|
||||
|
||||
std::string xml();
|
||||
|
||||
protected:
|
||||
SourceList::iterator find (Source *s);
|
||||
SourceList selection_;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
2
Source.h
2
Source.h
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "View.h"
|
||||
|
||||
#define DEFAULT_MIXING_TRANSLATION -1.f, 1.f
|
||||
|
||||
class ImageShader;
|
||||
class ImageProcessingShader;
|
||||
class FrameBuffer;
|
||||
|
||||
@@ -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();
|
||||
|
||||
11
View.cpp
11
View.cpp
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user