refactored copy/paste code for new serializers
Vastly improves code quality and reusability
This commit is contained in:
@@ -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);
|
||||
|
||||
+17
-20
@@ -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<Node *> 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<Node *> NodeCopyPasteService::PasteNodesFromClipboard(Project *project, MultiUndoCommand* command, void *userdata)
|
||||
void NodeCopyPasteService::PasteNodesFromClipboard(void *userdata)
|
||||
{
|
||||
QVector<Node*> 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<Node*> 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<Node *> &, void*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeCopyPasteService::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*)
|
||||
{
|
||||
Q_UNUSED(xml_node_data)
|
||||
reader->skipCurrentElement();
|
||||
PasteNodesToClipboardCallback(pasted_nodes, res.GetLoadData(), userdata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
#include <QUndoCommand>
|
||||
|
||||
#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<Node *> nodes, void* userdata = nullptr);
|
||||
|
||||
QVector<Node*> PasteNodesFromClipboard(Project *project, MultiUndoCommand *command, void* userdata = nullptr);
|
||||
void PasteNodesFromClipboard(void* userdata = nullptr);
|
||||
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &nodes, void* userdata);
|
||||
virtual void CopyNodesToClipboardCallback(const QVector<Node*> &nodes, ProjectSerializer::SaveData *data, void *userdata){}
|
||||
|
||||
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata);
|
||||
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata){}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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<Node *> &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<Node *> &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
|
||||
|
||||
|
||||
@@ -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<Node*, QMap<QString, QString> >;
|
||||
|
||||
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<Node*> &only = QVector<Node*>(), 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<Node*> &GetOnlySerializeNodes() const { return only_serialize_nodes_; }
|
||||
|
||||
void SetOnlySerializeNodes(const QVector<Node*> &only) { only_serialize_nodes_ = only; }
|
||||
|
||||
const SerializedProperties &GetProperties() const { return properties_; }
|
||||
|
||||
void SetProperties(const SerializedProperties &p) { properties_ = p; }
|
||||
|
||||
private:
|
||||
Project *project_;
|
||||
|
||||
QString filename_;
|
||||
|
||||
QVector<Node*> 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<Node*> &only = QVector<Node*>());
|
||||
static Result Save(Project *project, QXmlStreamWriter *write_device, const QString &filename, const QVector<Node*> &only = QVector<Node*>());
|
||||
static Result Save(Project *project, QXmlStreamWriter *write_device, const QVector<Node*> &only = QVector<Node*>())
|
||||
{
|
||||
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<Node*> &only, void *reserved) const {}
|
||||
virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const {}
|
||||
|
||||
virtual uint Version() const = 0;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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<quintptr, QMap<QString, QString> > properties;
|
||||
QMap<quintptr, QMap<quintptr, Node::Position> > 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<QString, QString> 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<Node *> &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<Node*> &using_node_list = (only.isEmpty()) ? project->nodes() : only;
|
||||
const QVector<Node*> &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<quintptr>(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<quintptr>(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<quintptr>(this)));
|
||||
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(node)));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), node->GetUUID().toString());
|
||||
writer->writeTextElement(QStringLiteral("label"), node->GetLabel());
|
||||
|
||||
@@ -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<Node*> &only, void *reserved) const override;
|
||||
virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const override;
|
||||
|
||||
virtual uint Version() const override
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<Node*> new_nodes;
|
||||
new_nodes.resize(selected_nodes_.size());
|
||||
|
||||
for (int i=0; i<new_nodes.size(); i++) {
|
||||
Node *og = selected_nodes_.at(i);
|
||||
Node *copy = og->copy();
|
||||
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<Node *> &nodes, void *userdata)
|
||||
void NodeView::CopyNodesToClipboardCallback(const QVector<Node*> &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<quintptr>(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<Node *> &nodes, const ProjectSerializer::LoadData &ldata, void *userdata)
|
||||
{
|
||||
Node::PositionMap *map = static_cast<Node::PositionMap *>(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<QString, QString> &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<Node *> &duplicate_nodes)
|
||||
{
|
||||
// If no graph, do nothing
|
||||
if (contexts_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If duplicating nodes, duplicate, otherwise paste
|
||||
QVector<Node*> 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; i<new_nodes.size(); i++) {
|
||||
Node *og = selected_nodes_.at(i);
|
||||
Node *copy = og->copy();
|
||||
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<AttachedItem> new_attached;
|
||||
|
||||
NodeViewItem *first_item = nullptr;
|
||||
|
||||
for (int i=0; i<new_nodes.size(); i++) {
|
||||
Node *node = new_nodes.at(i);
|
||||
|
||||
// Determine if item had a position, if not don't create an item for it
|
||||
NodeViewItem *new_item;
|
||||
|
||||
if (map.contains(node)) {
|
||||
new_item = new NodeViewItem(node, nullptr);
|
||||
new_item->SetFlowDirection(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; i<new_attached.size(); i++) {
|
||||
AttachedItem &ai = new_attached[i];
|
||||
|
||||
if (ai.item) {
|
||||
ai.original_pos = first_item->pos() - 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<Node *> &new_nodes, const Node::PositionMap &map)
|
||||
{
|
||||
QVector<AttachedItem> new_attached;
|
||||
|
||||
NodeViewItem *first_item = nullptr;
|
||||
|
||||
for (int i=0; i<new_nodes.size(); i++) {
|
||||
Node *node = new_nodes.at(i);
|
||||
|
||||
// Determine if item had a position, if not don't create an item for it
|
||||
NodeViewItem *new_item;
|
||||
|
||||
if (map.contains(node)) {
|
||||
new_item = new NodeViewItem(node, nullptr);
|
||||
new_item->SetFlowDirection(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; i<new_attached.size(); i++) {
|
||||
AttachedItem &ai = new_attached[i];
|
||||
|
||||
if (ai.item) {
|
||||
ai.original_pos = first_item->pos() - ai.item->pos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetAttachedItems(new_attached);
|
||||
}
|
||||
|
||||
void NodeView::SetAttachedItems(const QVector<AttachedItem> &items)
|
||||
{
|
||||
// Detach anything currently attached
|
||||
|
||||
@@ -127,8 +127,8 @@ protected:
|
||||
|
||||
virtual bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &nodes, void* userdata) override;
|
||||
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override;
|
||||
virtual void CopyNodesToClipboardCallback(const QVector<Node *> &nodes, ProjectSerializer::SaveData *data, void* userdata) override;
|
||||
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &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<Node*> &duplicate_nodes = QVector<Node *>());
|
||||
|
||||
void AddContext(Node *n);
|
||||
|
||||
void RemoveContext(Node *n);
|
||||
@@ -169,6 +167,8 @@ private:
|
||||
|
||||
void EndEdgeDrag(bool cancel = false);
|
||||
|
||||
void PostPaste(const QVector<Node*> &new_nodes, const Node::PositionMap &map);
|
||||
|
||||
NodeViewMiniMap *minimap_;
|
||||
|
||||
struct AttachedItem {
|
||||
|
||||
@@ -300,55 +300,64 @@ void TimelineWidget::DisconnectNodeEvent(ViewerOutput *n)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node *> &nodes, void* userdata)
|
||||
void TimelineWidget::CopyNodesToClipboardCallback(const QVector<Node *> &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<Block*>& selected = *static_cast<QVector<Block*>*>(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<quintptr>(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<Node *> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata)
|
||||
{
|
||||
QVector<BlockPasteData>& paste_data = *static_cast<QVector<BlockPasteData>*>(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<Block*>(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<Track::Type>(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<Block*>(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<Block*>(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<BlockPasteData> paste_data;
|
||||
QVector<Node*> 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)
|
||||
|
||||
@@ -272,15 +272,8 @@ protected:
|
||||
virtual void ConnectNodeEvent(ViewerOutput* n) override;
|
||||
virtual void DisconnectNodeEvent(ViewerOutput* n) override;
|
||||
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &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<Node*> &nodes, ProjectSerializer::SaveData *data, void *userdata) override;
|
||||
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata) override;
|
||||
|
||||
private:
|
||||
QVector<Timeline::EditToInfo> GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode);
|
||||
|
||||
Reference in New Issue
Block a user