diff --git a/app/node/graph.cpp b/app/node/graph.cpp index f9a68c500..6c71f5a75 100644 --- a/app/node/graph.cpp +++ b/app/node/graph.cpp @@ -83,6 +83,20 @@ void NodeGraph::childEvent(QChildEvent *event) emit NodeAdded(node); emit node->AddedToGraph(this); + // Emit input connections + for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { + if (nodes().contains(it->second)) { + emit InputConnected(it->second, it->first); + } + } + + // Emit output connections + for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) { + if (nodes().contains(it->second.node())) { + emit InputConnected(it->first, it->second); + } + } + } else if (event->type() == QEvent::ChildRemoved) { node_children_.removeOne(node); diff --git a/app/node/nodecopypaste.cpp b/app/node/nodecopypaste.cpp index 40f15b6fd..9b4aec472 100644 --- a/app/node/nodecopypaste.cpp +++ b/app/node/nodecopypaste.cpp @@ -24,7 +24,6 @@ #include "core.h" #include "node/factory.h" -#include "node/project/serializer/serializer.h" #include "widget/nodeview/nodeviewundo.h" #include "window/mainwindow/mainwindow.h" @@ -48,47 +47,45 @@ void NodeCopyPasteService::CopyNodesToClipboard(QVector nodes, void *use } } - ProjectSerializer::Save(nodes.first()->project(), &writer, nodes); + ProjectSerializer::SaveData data(nodes.first()->project(), QString(), nodes); - /*writer.writeStartElement(QStringLiteral("custom")); - CopyNodesToClipboardInternal(&writer, nodes, userdata); - writer.writeEndElement(); // custom*/ + CopyNodesToClipboardCallback(nodes, &data, userdata); + + ProjectSerializer::Save(&writer, data); Core::CopyStringToClipboard(copy_str); } -QVector NodeCopyPasteService::PasteNodesFromClipboard(Project *project, MultiUndoCommand* command, void *userdata) +void NodeCopyPasteService::PasteNodesFromClipboard(void *userdata) { - QVector pasted_nodes; - QString clipboard = Core::PasteStringFromClipboard(); if (clipboard.isEmpty()) { - return pasted_nodes; + return; } QXmlStreamReader reader(clipboard); Project temp; - ProjectSerializer::Load(&temp, &reader); + ProjectSerializer::Result res = ProjectSerializer::Load(&temp, &reader); + if (res.code() != ProjectSerializer::kSuccess) { + return; + } + + QVector pasted_nodes; foreach (Node *n, temp.nodes()) { if (!temp.default_nodes().contains(n)) { + // Move nodes out of Project n->setParent(nullptr); pasted_nodes.append(n); } } - return pasted_nodes; -} + if (pasted_nodes.isEmpty()) { + return; + } -void NodeCopyPasteService::CopyNodesToClipboardInternal(QXmlStreamWriter*, const QVector &, void*) -{ -} - -void NodeCopyPasteService::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*) -{ - Q_UNUSED(xml_node_data) - reader->skipCurrentElement(); + PasteNodesToClipboardCallback(pasted_nodes, res.GetLoadData(), userdata); } } diff --git a/app/node/nodecopypaste.h b/app/node/nodecopypaste.h index 067bef032..28c53bed9 100644 --- a/app/node/nodecopypaste.h +++ b/app/node/nodecopypaste.h @@ -25,7 +25,9 @@ #include #include "node/node.h" +#include "node/project/project.h" #include "node/project/sequence/sequence.h" +#include "node/project/serializer/serializer.h" namespace olive { @@ -37,11 +39,11 @@ public: protected: void CopyNodesToClipboard(QVector nodes, void* userdata = nullptr); - QVector PasteNodesFromClipboard(Project *project, MultiUndoCommand *command, void* userdata = nullptr); + void PasteNodesFromClipboard(void* userdata = nullptr); - virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void* userdata); + virtual void CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *data, void *userdata){} - virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata); + virtual void PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &load_data, void *userdata){} }; diff --git a/app/node/project/serializer/serializer.cpp b/app/node/project/serializer/serializer.cpp index 34f375567..446dced66 100644 --- a/app/node/project/serializer/serializer.cpp +++ b/app/node/project/serializer/serializer.cpp @@ -119,24 +119,30 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamRe } if (serializer) { - serializer->Load(project, reader, nullptr); - return kSuccess; + LoadData ld = serializer->Load(project, reader, nullptr); + Result r(kSuccess); + if (reader->hasError()) { + r = Result(kXmlError); + r.SetDetails(reader->errorString()); + } + r.SetLoadData(ld); + return r; } else { // Reached the end of the list with no serializer, assume too new return kProjectTooNew; } } -ProjectSerializer::Result ProjectSerializer::Save(Project *project, const QString &filename, const QVector &only) +ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data) { - QString temp_save = FileFunctions::GetSafeTemporaryFilename(filename); + QString temp_save = FileFunctions::GetSafeTemporaryFilename(data.GetFilename()); QFile project_file(temp_save); if (project_file.open(QFile::WriteOnly | QFile::Text)) { QXmlStreamWriter writer(&project_file); - Result inner_result = Save(project, &writer, filename, only); + Result inner_result = Save(&writer, data); project_file.close(); @@ -145,7 +151,7 @@ ProjectSerializer::Result ProjectSerializer::Save(Project *project, const QStrin } // Save was successful, we can now rewrite the original file - if (FileFunctions::RenameFileAllowOverwrite(temp_save, filename)) { + if (FileFunctions::RenameFileAllowOverwrite(temp_save, data.GetFilename())) { return kSuccess; } else { Result r(kOverwriteError); @@ -159,7 +165,7 @@ ProjectSerializer::Result ProjectSerializer::Save(Project *project, const QStrin } } -ProjectSerializer::Result ProjectSerializer::Save(Project *project, QXmlStreamWriter *writer, const QString &filename, const QVector &only) +ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, const SaveData &data) { writer->setAutoFormatting(true); @@ -175,13 +181,13 @@ ProjectSerializer::Result ProjectSerializer::Save(Project *project, QXmlStreamWr // Allows easy integer math for checking project versions. writer->writeTextElement(QStringLiteral("version"), QString::number(serializer->Version())); - if (!filename.isEmpty()) { - writer->writeTextElement("url", filename); + if (!data.GetFilename().isEmpty()) { + writer->writeTextElement("url", data.GetFilename()); } writer->writeStartElement(QStringLiteral("project")); - serializer->Save(project, writer, only, nullptr); + serializer->Save(writer, data, nullptr); writer->writeEndElement(); // project diff --git a/app/node/project/serializer/serializer.h b/app/node/project/serializer/serializer.h index ae7db078a..0417f440c 100644 --- a/app/node/project/serializer/serializer.h +++ b/app/node/project/serializer/serializer.h @@ -39,7 +39,7 @@ class ProjectSerializer public: ProjectSerializer() = default; - virtual ~ProjectSerializer(){}; + virtual ~ProjectSerializer(){} DISABLE_COPY_MOVE(ProjectSerializer) @@ -50,7 +50,18 @@ public: kUnknownVersion, kFileError, kXmlError, - kOverwriteError, + kOverwriteError + }; + + using SerializedProperties = QHash >; + + class LoadData + { + public: + LoadData() = default; + + SerializedProperties properties; + }; class Result @@ -63,26 +74,63 @@ public: bool operator==(const ResultCode &code) { return code_ == code; } bool operator!=(const ResultCode &code) { return code_ != code; } - const ResultCode &code() const - { - return code_; - } + const ResultCode &code() const { return code_; } - const QString &GetDetails() const - { - return details_; - } + const QString &GetDetails() const { return details_; } - void SetDetails(const QString &s) - { - details_ = s; - } + void SetDetails(const QString &s) { details_ = s; } + + const LoadData &GetLoadData() const { return load_data_; } + + void SetLoadData(const LoadData &p) { load_data_ = p; } private: ResultCode code_; QString details_; + LoadData load_data_; + + }; + + class SaveData + { + public: + SaveData(Project *project, const QString &filename, const QVector &only = QVector(), const SerializedProperties &p = SerializedProperties()) + { + project_ = project; + filename_ = filename; + only_serialize_nodes_ = only; + properties_ = p; + } + + Project *GetProject() const + { + return project_; + } + + const QString &GetFilename() const + { + return filename_; + } + + const QVector &GetOnlySerializeNodes() const { return only_serialize_nodes_; } + + void SetOnlySerializeNodes(const QVector &only) { only_serialize_nodes_ = only; } + + const SerializedProperties &GetProperties() const { return properties_; } + + void SetProperties(const SerializedProperties &p) { properties_ = p; } + + private: + Project *project_; + + QString filename_; + + QVector only_serialize_nodes_; + + SerializedProperties properties_; + }; static void Initialize(); @@ -92,17 +140,13 @@ public: static Result Load(Project *project, const QString &filename); static Result Load(Project *project, QXmlStreamReader *read_device); - static Result Save(Project *project, const QString &filename, const QVector &only = QVector()); - static Result Save(Project *project, QXmlStreamWriter *write_device, const QString &filename, const QVector &only = QVector()); - static Result Save(Project *project, QXmlStreamWriter *write_device, const QVector &only = QVector()) - { - return Save(project, write_device, QString(), only); - } + static Result Save(const SaveData &data); + static Result Save(QXmlStreamWriter *write_device, const SaveData &data); protected: - virtual void Load(Project *project, QXmlStreamReader *reader, void *reserved) const = 0; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const = 0; - virtual void Save(Project *project, QXmlStreamWriter *writer, const QVector &only, void *reserved) const {} + virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const {} virtual uint Version() const = 0; diff --git a/app/node/project/serializer/serializer190219.cpp b/app/node/project/serializer/serializer190219.cpp index 663af8e1a..6e81bc494 100644 --- a/app/node/project/serializer/serializer190219.cpp +++ b/app/node/project/serializer/serializer190219.cpp @@ -22,8 +22,9 @@ namespace olive { -void ProjectSerializer190219::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer::LoadData ProjectSerializer190219::Load(Project *project, QXmlStreamReader *reader, void *reserved) const { + return LoadData(); } } diff --git a/app/node/project/serializer/serializer190219.h b/app/node/project/serializer/serializer190219.h index d0e282581..159904c7c 100644 --- a/app/node/project/serializer/serializer190219.h +++ b/app/node/project/serializer/serializer190219.h @@ -31,7 +31,7 @@ public: ProjectSerializer190219() = default; protected: - virtual void Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; virtual uint Version() const override { diff --git a/app/node/project/serializer/serializer210528.cpp b/app/node/project/serializer/serializer210528.cpp index 4cd941863..4f86102d8 100644 --- a/app/node/project/serializer/serializer210528.cpp +++ b/app/node/project/serializer/serializer210528.cpp @@ -24,7 +24,7 @@ namespace olive { -void ProjectSerializer210528::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer210528::LoadData ProjectSerializer210528::Load(Project *project, QXmlStreamReader *reader, void *reserved) const { XMLNodeData xml_node_data; @@ -161,6 +161,8 @@ void ProjectSerializer210528::Load(Project *project, QXmlStreamReader *reader, v // Make connections PostConnect(xml_node_data); + + return LoadData(); } void ProjectSerializer210528::LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const diff --git a/app/node/project/serializer/serializer210528.h b/app/node/project/serializer/serializer210528.h index 4e1660481..7660ccb0f 100644 --- a/app/node/project/serializer/serializer210528.h +++ b/app/node/project/serializer/serializer210528.h @@ -31,7 +31,7 @@ public: ProjectSerializer210528() = default; protected: - virtual void Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; virtual uint Version() const override { diff --git a/app/node/project/serializer/serializer210907.cpp b/app/node/project/serializer/serializer210907.cpp index 2b2982ae4..72a3aa3f1 100644 --- a/app/node/project/serializer/serializer210907.cpp +++ b/app/node/project/serializer/serializer210907.cpp @@ -24,7 +24,7 @@ namespace olive { -void ProjectSerializer210907::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer210907::LoadData ProjectSerializer210907::Load(Project *project, QXmlStreamReader *reader, void *reserved) const { XMLNodeData xml_node_data; @@ -161,6 +161,8 @@ void ProjectSerializer210907::Load(Project *project, QXmlStreamReader *reader, v // Make connections PostConnect(xml_node_data); + + return LoadData(); } void ProjectSerializer210907::LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const diff --git a/app/node/project/serializer/serializer210907.h b/app/node/project/serializer/serializer210907.h index 6f4ff76fa..f3a3ca2f5 100644 --- a/app/node/project/serializer/serializer210907.h +++ b/app/node/project/serializer/serializer210907.h @@ -31,7 +31,7 @@ public: ProjectSerializer210907() = default; protected: - virtual void Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; virtual uint Version() const override { diff --git a/app/node/project/serializer/serializer211228.cpp b/app/node/project/serializer/serializer211228.cpp index 757bd4f12..1f9b9de4b 100644 --- a/app/node/project/serializer/serializer211228.cpp +++ b/app/node/project/serializer/serializer211228.cpp @@ -24,8 +24,10 @@ namespace olive { -void ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, void *reserved) const { + QMap > properties; + QMap > positions; XMLNodeData xml_node_data; while (XMLReadNextStartElement(reader)) { @@ -112,22 +114,15 @@ void ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, v } } - Node *context = xml_node_data.node_ptrs.value(context_ptr); - - if (!context) { - qWarning() << "Failed to find pointer for context"; - reader->skipCurrentElement(); - } else { + if (context_ptr) { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("node")) { quintptr node_ptr; Node::Position node_pos; if (LoadPosition(reader, &node_ptr, &node_pos)) { - Node *node = xml_node_data.node_ptrs.value(node_ptr); - - if (node) { - context->SetNodePositionInContext(node, node_pos); + if (node_ptr) { + positions[context_ptr].insert(node_ptr, node_pos); } else { qWarning() << "Failed to find pointer for node position"; reader->skipCurrentElement(); @@ -137,6 +132,9 @@ void ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, v reader->skipCurrentElement(); } } + } else { + qWarning() << "Attempted to load context with no pointer"; + reader->skipCurrentElement(); } } else { @@ -147,6 +145,34 @@ void ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, v } + + } else if (reader->name() == QStringLiteral("properties")) { + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + quintptr ptr = 0; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("ptr")) { + ptr = attr.value().toULongLong(); + + // Only attribute we're looking for right now + break; + } + } + + if (ptr) { + QMap properties_for_node; + while (XMLReadNextStartElement(reader)) { + properties_for_node.insert(reader->name().toString(), reader->readElementText()); + } + properties.insert(ptr, properties_for_node); + } + } else { + reader->skipCurrentElement(); + } + } + } else { // Skip this @@ -159,17 +185,44 @@ void ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, v } } + // Resolve positions + for (auto it=positions.cbegin(); it!=positions.cend(); it++) { + Node *ctx = xml_node_data.node_ptrs.value(it.key()); + if (ctx) { + for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) { + Node *n = xml_node_data.node_ptrs.value(jt.key()); + if (n) { + ctx->SetNodePositionInContext(n, jt.value()); + } + } + } + } + // Make connections PostConnect(xml_node_data); + + LoadData load_data; + + // Resolve serialized properties (if any) + for (auto it=properties.cbegin(); it!=properties.cend(); it++) { + Node *node = xml_node_data.node_ptrs.value(it.key()); + if (node) { + load_data.properties.insert(node, it.value()); + } + } + + return load_data; } -void ProjectSerializer211228::Save(Project *project, QXmlStreamWriter *writer, const QVector &only, void *reserved) const +void ProjectSerializer211228::Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const { - writer->writeTextElement(QStringLiteral("uuid"), project->GetUuid().toString()); + Project *project = data.GetProject(); + + writer->writeTextElement(QStringLiteral("uuid"), data.GetProject()->GetUuid().toString()); writer->writeStartElement(QStringLiteral("nodes")); - const QVector &using_node_list = (only.isEmpty()) ? project->nodes() : only; + const QVector &using_node_list = (data.GetOnlySerializeNodes().isEmpty()) ? project->nodes() : data.GetOnlySerializeNodes(); foreach (Node* node, using_node_list) { writer->writeStartElement(QStringLiteral("node")); @@ -202,7 +255,7 @@ void ProjectSerializer211228::Save(Project *project, QXmlStreamWriter *writer, c writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(context))); for (auto jt=map.cbegin(); jt!=map.cend(); jt++) { - if (only.isEmpty() || only.contains(jt.key())) { + if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) { writer->writeStartElement(QStringLiteral("node")); SavePosition(writer, jt.key(), jt.value()); writer->writeEndElement(); // node @@ -215,6 +268,22 @@ void ProjectSerializer211228::Save(Project *project, QXmlStreamWriter *writer, c writer->writeEndElement(); // positions + writer->writeStartElement(QStringLiteral("properties")); + + for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) { + writer->writeStartElement(QStringLiteral("node")); + + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(it.key()))); + + for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) { + writer->writeTextElement(jt.key(), jt.value()); + } + + writer->writeEndElement(); // node + } + + writer->writeEndElement(); // properties + // Save main window project layout project->GetLayoutInfo().toXml(writer); } @@ -229,7 +298,9 @@ void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, Q if (reader->name() == QStringLiteral("input")) { LoadInput(node, reader, xml_node_data); } else if (reader->name() == QStringLiteral("ptr")) { - xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), node); + quintptr ptr = reader->readElementText().toULongLong(); + xml_node_data.node_ptrs.insert(ptr, node); + qDebug() << "Inserting" << ptr << "as" << node; } else if (reader->name() == QStringLiteral("label")) { node->SetLabel(reader->readElementText()); } else if (reader->name() == QStringLiteral("uuid")) { @@ -245,9 +316,7 @@ void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, Q } } } else if (reader->name() == QStringLiteral("custom")) { - LoadNodeCustom(reader, node, xml_node_data); - } else if (reader->name() == QStringLiteral("connections")) { // Load connections while (XMLReadNextStartElement(reader)) { @@ -307,7 +376,7 @@ void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, Q void ProjectSerializer211228::SaveNode(Node *node, QXmlStreamWriter *writer) const { - writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast(this))); + writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast(node))); writer->writeTextElement(QStringLiteral("uuid"), node->GetUUID().toString()); writer->writeTextElement(QStringLiteral("label"), node->GetLabel()); diff --git a/app/node/project/serializer/serializer211228.h b/app/node/project/serializer/serializer211228.h index 0ad6762b9..5bd0b38b1 100644 --- a/app/node/project/serializer/serializer211228.h +++ b/app/node/project/serializer/serializer211228.h @@ -31,9 +31,9 @@ public: ProjectSerializer211228() = default; protected: - virtual void Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; - virtual void Save(Project *project, QXmlStreamWriter *writer, const QVector &only, void *reserved) const override; + virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const override; virtual uint Version() const override { diff --git a/app/task/project/save/save.cpp b/app/task/project/save/save.cpp index 222fc76d8..1f5742bcf 100644 --- a/app/task/project/save/save.cpp +++ b/app/task/project/save/save.cpp @@ -40,7 +40,9 @@ bool ProjectSaveTask::Run() { QString using_filename = override_filename_.isEmpty() ? project_->filename() : override_filename_; - ProjectSerializer::Result result = ProjectSerializer::Save(project_, using_filename); + ProjectSerializer::SaveData data(project_, using_filename); + + ProjectSerializer::Result result = ProjectSerializer::Save(data); bool success = false; diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index d01bc5e81..7c305b63e 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -205,12 +205,30 @@ void NodeView::CopySelected(bool cut) void NodeView::Paste() { - PasteNodesInternal(); + if (!contexts_.isEmpty()) { + PasteNodesFromClipboard(); + } } void NodeView::Duplicate() { - PasteNodesInternal(selected_nodes_); + if (!selected_nodes_.isEmpty()) { + Node::PositionMap map; + QVector new_nodes; + new_nodes.resize(selected_nodes_.size()); + + for (int i=0; icopy(); + Node::CopyInputs(og, copy, false); + map.insert(copy, GetAssumedPositionForSelectedNode(og)); + new_nodes[i] = copy; + } + + Node::CopyDependencyGraph(selected_nodes_, new_nodes, nullptr); + + PostPaste(new_nodes, map); + } } void NodeView::SetColorLabel(int index) @@ -903,9 +921,9 @@ bool NodeView::eventFilter(QObject *object, QEvent *event) return super::eventFilter(object, event); } -void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void *userdata) +void NodeView::CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *sdata, void *userdata) { - writer->writeStartElement(QStringLiteral("pos")); + ProjectSerializer::SerializedProperties properties; for (Node *n : nodes) { NodeViewItem *item = GetAssumedItemForSelectedNode(n); @@ -913,59 +931,31 @@ void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVec if (item) { Node::Position pos = item->GetNodePositionData(); - writer->writeStartElement(QStringLiteral("node")); - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(n))); - writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x())); - writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y())); - writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded)); - writer->writeEndElement(); // node + properties[n][QStringLiteral("x")] = QString::number(pos.position.x()); + properties[n][QStringLiteral("y")] = QString::number(pos.position.y()); + properties[n][QStringLiteral("expanded")] = QString::number(pos.expanded); } } - writer->writeEndElement(); // pos + sdata->SetProperties(properties); } -void NodeView::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void *userdata) +void NodeView::PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &ldata, void *userdata) { - Node::PositionMap *map = static_cast(userdata); + Node::PositionMap map; - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("pos")) { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("node")) { - Node *n = nullptr; - Node::Position pos; + for (auto it=ldata.properties.cbegin(); it!=ldata.properties.cend(); it++) { + Node::Position pos; - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("ptr")) { - n = xml_node_data.node_ptrs.value(attr.value().toULongLong()); - break; - } - } + const QMap &node_props = it.value(); + pos.position.setX(node_props.value(QStringLiteral("x")).toDouble()); + pos.position.setY(node_props.value(QStringLiteral("y")).toDouble()); + pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble(); - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("x")) { - pos.position.setX(reader->readElementText().toDouble()); - } else if (reader->name() == QStringLiteral("y")) { - pos.position.setY(reader->readElementText().toDouble()); - } else if (reader->name() == QStringLiteral("expanded")) { - pos.expanded = reader->readElementText().toInt(); - } else { - reader->skipCurrentElement(); - } - } - - if (n) { - map->insert(n, pos); - } - } else { - reader->skipCurrentElement(); - } - } - } else { - reader->skipCurrentElement(); - } + map.insert(it.key(), pos); } + + PostPaste(nodes, map); } void NodeView::changeEvent(QEvent *e) @@ -1265,75 +1255,6 @@ void NodeView::ItemAboutToBeDeleted(NodeViewItem *item) } } -void NodeView::PasteNodesInternal(const QVector &duplicate_nodes) -{ - // If no graph, do nothing - if (contexts_.isEmpty()) { - return; - } - - // If duplicating nodes, duplicate, otherwise paste - QVector new_nodes; - Node::PositionMap map; - if (duplicate_nodes.isEmpty()) { - new_nodes = PasteNodesFromClipboard(nullptr, nullptr, &map); - } else { - new_nodes.resize(selected_nodes_.size()); - - for (int i=0; icopy(); - Node::CopyInputs(og, copy, false); - map.insert(copy, GetAssumedPositionForSelectedNode(og)); - new_nodes[i] = copy; - } - - Node::CopyDependencyGraph(selected_nodes_, new_nodes, nullptr); - } - - // If no nodes were retrieved, do nothing - if (!new_nodes.isEmpty()) { - QVector new_attached; - - NodeViewItem *first_item = nullptr; - - for (int i=0; iSetFlowDirection(scene_.GetFlowDirection()); - new_item->SetNodePosition(map.value(node)); - scene_.addItem(new_item); - - if (!first_item) { - first_item = new_item; - } - } else { - new_item = nullptr; - } - - new_attached.append({new_item, node, QPointF(0, 0)}); - } - - // Correct positions - if (first_item) { - for (int i=0; ipos() - ai.item->pos(); - } - } - } - - SetAttachedItems(new_attached); - } -} - void NodeView::AddContext(Node *n) { NodeViewContext *ctx = scene_.AddContext(n); @@ -1449,6 +1370,48 @@ void NodeView::EndEdgeDrag(bool cancel) Core::instance()->undo_stack()->pushIfHasChildren(command); } +void NodeView::PostPaste(const QVector &new_nodes, const Node::PositionMap &map) +{ + QVector new_attached; + + NodeViewItem *first_item = nullptr; + + for (int i=0; iSetFlowDirection(scene_.GetFlowDirection()); + new_item->SetNodePosition(map.value(node)); + scene_.addItem(new_item); + + if (!first_item) { + first_item = new_item; + } + } else { + new_item = nullptr; + } + + new_attached.append({new_item, node, QPointF(0, 0)}); + } + + // Correct positions + if (first_item) { + for (int i=0; ipos() - ai.item->pos(); + } + } + } + + SetAttachedItems(new_attached); +} + void NodeView::SetAttachedItems(const QVector &items) { // Detach anything currently attached diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 510eb54db..b3d6d4038 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -127,8 +127,8 @@ protected: virtual bool eventFilter(QObject *object, QEvent *event) override; - virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void* userdata) override; - virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override; + virtual void CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *data, void* userdata) override; + virtual void PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &ldata, void *userdata) override; virtual void changeEvent(QEvent *e) override; @@ -155,8 +155,6 @@ private: void PositionNewEdge(const QPoint &pos); - void PasteNodesInternal(const QVector &duplicate_nodes = QVector()); - void AddContext(Node *n); void RemoveContext(Node *n); @@ -169,6 +167,8 @@ private: void EndEdgeDrag(bool cancel = false); + void PostPaste(const QVector &new_nodes, const Node::PositionMap &map); + NodeViewMiniMap *minimap_; struct AttachedItem { diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 30fd2babe..cb7cdd4ae 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -300,55 +300,64 @@ void TimelineWidget::DisconnectNodeEvent(ViewerOutput *n) } } -void TimelineWidget::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void* userdata) +void TimelineWidget::CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *sdata, void *userdata) { // Cache the earliest in point so all copied clips have a "relative" in point that can be pasted anywhere QVector& selected = *static_cast*>(userdata); rational earliest_in = RATIONAL_MAX; + ProjectSerializer::SerializedProperties properties; foreach (Block* block, selected) { earliest_in = qMin(earliest_in, block->in()); } foreach (Block* block, selected) { - writer->writeStartElement(QStringLiteral("block")); - - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(block))); - writer->writeAttribute(QStringLiteral("in"), (block->in() - earliest_in).toString()); - - Track* track = block->track(); - writer->writeAttribute(QStringLiteral("tracktype"), QString::number(track->type())); - writer->writeAttribute(QStringLiteral("trackindex"), QString::number(track->Index())); - - writer->writeEndElement(); + properties[block][QStringLiteral("in")] = (block->in() - earliest_in).toString(); + properties[block][QStringLiteral("track")] = block->track()->ToReference().ToString(); } + + sdata->SetProperties(properties); } -void TimelineWidget::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData& xml_node_data, void *userdata) +void TimelineWidget::PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &load_data, void *userdata) { - QVector& paste_data = *static_cast*>(userdata); + bool insert = *(bool*)userdata; - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("block")) { - BlockPasteData bpd; + MultiUndoCommand *command = new MultiUndoCommand(); - foreach (QXmlStreamAttribute attr, reader->attributes()) { - if (attr.name() == QStringLiteral("ptr")) { - bpd.block = static_cast(xml_node_data.node_ptrs.value(attr.value().toULongLong())); - } else if (attr.name() == QStringLiteral("in")) { - bpd.in = rational::fromString(attr.value().toString()); - } else if (attr.name() == QStringLiteral("tracktype")) { - bpd.track_type = static_cast(attr.value().toInt()); - } else if (attr.name() == QStringLiteral("trackindex")) { - bpd.track_index = attr.value().toInt(); - } - } + foreach (Node *n, nodes) { + command->add_child(new NodeAddCommand(GetConnectedNode()->project(), n)); + } - paste_data.append(bpd); + rational paste_start = GetTime(); - reader->skipCurrentElement(); + if (insert) { + rational paste_end = GetTime(); + + for (auto it=load_data.properties.cbegin(); it!=load_data.properties.cend(); it++) { + rational length = static_cast(it.key())->length(); + rational in = rational::fromString(it.value()[QStringLiteral("in")]); + + paste_end = qMax(paste_end, paste_start + in + length); + } + + if (paste_end != paste_start) { + InsertGapsAt(paste_start, paste_end - paste_start, command); } } + + for (auto it=load_data.properties.cbegin(); it!=load_data.properties.cend(); it++) { + Block *block = static_cast(it.key()); + rational in = rational::fromString(it.value()[QStringLiteral("in")]); + Track::Reference track = Track::Reference::FromString(it.value()[QStringLiteral("track")]); + + command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()), + track.index(), + block, + paste_start + in)); + } + + Core::instance()->undo_stack()->pushIfHasChildren(command); } void TimelineWidget::SelectAll() @@ -626,34 +635,7 @@ void TimelineWidget::Paste(bool insert) return; } - MultiUndoCommand* command = new MultiUndoCommand(); - - QVector paste_data; - QVector pasted = PasteNodesFromClipboard(GetConnectedNode()->project(), command, &paste_data); - - rational paste_start = GetTime(); - - if (insert) { - rational paste_end = GetTime(); - - foreach (const BlockPasteData& bpd, paste_data) { - paste_end = qMax(paste_end, paste_start + bpd.in + bpd.block->length()); - } - - if (paste_end != paste_start) { - InsertGapsAt(paste_start, paste_end - paste_start, command); - } - } - - foreach (const BlockPasteData& bpd, paste_data) { - qDebug() << "Placing" << bpd.block; - command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(bpd.track_type), - bpd.track_index, - bpd.block, - paste_start + bpd.in)); - } - - Core::instance()->undo_stack()->pushIfHasChildren(command); + PasteNodesFromClipboard(&insert); } void TimelineWidget::DeleteInToOut(bool ripple) diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index b942fcb44..6515b8369 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -272,15 +272,8 @@ protected: virtual void ConnectNodeEvent(ViewerOutput* n) override; virtual void DisconnectNodeEvent(ViewerOutput* n) override; - virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector &nodes, void* userdata) override; - virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override; - - struct BlockPasteData { - Block* block; - rational in; - Track::Type track_type; - int track_index; - }; + virtual void CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *data, void *userdata) override; + virtual void PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &load_data, void *userdata) override; private: QVector GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode);