diff --git a/app/node/node.cpp b/app/node/node.cpp index b225d21ee..61a366ddb 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -1185,16 +1185,24 @@ bool Node::AreLinked(Node *a, Node *b) bool Node::Load(QXmlStreamReader *reader, SerializedData *data) { + uint version = 0; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("ptr")) { + quintptr ptr = attr.value().toULongLong(); + data->node_ptrs.insert(ptr, this); + } else if (attr.name() == QStringLiteral("version")) { + version = attr.value().toUInt(); + } + } + + Q_UNUSED(version) + while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("input")) { LoadInput(reader, data); - } else if (reader->name() == QStringLiteral("ptr")) { - quintptr ptr = reader->readElementText().toULongLong(); - data->node_ptrs.insert(ptr, this); } else if (reader->name() == QStringLiteral("label")) { this->SetLabel(reader->readElementText()); - } else if (reader->name() == QStringLiteral("uuid")) { - data->node_uuids.insert(this, QUuid::fromString(reader->readElementText())); } else if (reader->name() == QStringLiteral("color")) { this->SetOverrideColor(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("links")) { @@ -1278,7 +1286,7 @@ bool Node::Load(QXmlStreamReader *reader, SerializedData *data) if (!node_pos.load(reader)) { return false; } - data->positions[reinterpret_cast(this)].insert(node_ptr, node_pos); + data->positions[this].insert(node_ptr, node_pos); } else { return false; } @@ -1313,11 +1321,16 @@ bool Node::Load(QXmlStreamReader *reader, SerializedData *data) void Node::Save(QXmlStreamWriter *writer) const { writer->writeAttribute(QStringLiteral("version"), QString::number(1)); + writer->writeAttribute(QStringLiteral("id"), this->id()); + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(this))); - writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast(this))); + if (!this->GetLabel().isEmpty()) { + writer->writeTextElement(QStringLiteral("label"), this->GetLabel()); + } - writer->writeTextElement(QStringLiteral("label"), this->GetLabel()); - writer->writeTextElement(QStringLiteral("color"), QString::number(this->GetOverrideColor())); + if (this->GetOverrideColor() != -1) { + writer->writeTextElement(QStringLiteral("color"), QString::number(this->GetOverrideColor())); + } foreach (const QString& input, this->inputs()) { writer->writeStartElement(QStringLiteral("input")); @@ -1327,52 +1340,58 @@ void Node::Save(QXmlStreamWriter *writer) const writer->writeEndElement(); // input } - writer->writeStartElement(QStringLiteral("links")); - foreach (Node* link, this->links()) { - writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast(link))); + if (!this->links().empty()) { + writer->writeStartElement(QStringLiteral("links")); + foreach (Node* link, this->links()) { + writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast(link))); + } + writer->writeEndElement(); // links } - writer->writeEndElement(); // links - writer->writeStartElement(QStringLiteral("connections")); - for (auto it=this->input_connections().cbegin(); it!=this->input_connections().cend(); it++) { - writer->writeStartElement(QStringLiteral("connection")); + if (!this->input_connections().empty()) { + writer->writeStartElement(QStringLiteral("connections")); + for (auto it=this->input_connections().cbegin(); it!=this->input_connections().cend(); it++) { + writer->writeStartElement(QStringLiteral("connection")); - writer->writeAttribute(QStringLiteral("input"), it->first.input()); - writer->writeAttribute(QStringLiteral("element"), QString::number(it->first.element())); + writer->writeAttribute(QStringLiteral("input"), it->first.input()); + writer->writeAttribute(QStringLiteral("element"), QString::number(it->first.element())); - writer->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast(it->second))); + writer->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast(it->second))); - writer->writeEndElement(); // connection + writer->writeEndElement(); // connection + } + writer->writeEndElement(); // connections } - writer->writeEndElement(); // connections - writer->writeStartElement(QStringLiteral("hints")); - for (auto it=this->GetValueHints().cbegin(); it!=this->GetValueHints().cend(); it++) { - writer->writeStartElement(QStringLiteral("hint")); + if (!this->GetValueHints().empty()) { + writer->writeStartElement(QStringLiteral("hints")); + for (auto it=this->GetValueHints().cbegin(); it!=this->GetValueHints().cend(); it++) { + writer->writeStartElement(QStringLiteral("hint")); - writer->writeAttribute(QStringLiteral("input"), it.key().input); - writer->writeAttribute(QStringLiteral("element"), QString::number(it.key().element)); + writer->writeAttribute(QStringLiteral("input"), it.key().input); + writer->writeAttribute(QStringLiteral("element"), QString::number(it.key().element)); - it.value().save(writer); + it.value().save(writer); - writer->writeEndElement(); // hint + writer->writeEndElement(); // hint + } + writer->writeEndElement(); // hints } - writer->writeEndElement(); // hints - - writer->writeStartElement(QStringLiteral("context")); const Node::PositionMap &map = this->GetContextPositions(); if (!map.isEmpty()) { + writer->writeStartElement(QStringLiteral("context")); + for (auto jt=map.cbegin(); jt!=map.cend(); jt++) { writer->writeStartElement(QStringLiteral("node")); - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(this))); + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(jt.key()))); jt.value().save(writer); writer->writeEndElement(); // node } - } - writer->writeEndElement(); // context + writer->writeEndElement(); // context + } writer->writeStartElement(QStringLiteral("caches")); @@ -1399,8 +1418,7 @@ bool Node::LoadCustom(QXmlStreamReader *reader, SerializedData *data) void Node::PostLoadEvent(SerializedData *data) { // Resolve positions - quintptr this_ptr = data->node_ptrs.key(this); - const QMap &positions = data->positions.value(this_ptr); + const QMap &positions = data->positions.value(this); for (auto jt=positions.cbegin(); jt!=positions.cend(); jt++) { Node *n = data->node_ptrs.value(jt.key()); @@ -1479,27 +1497,31 @@ void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const { writer->writeAttribute(QStringLiteral("id"), id); - writer->writeStartElement(QStringLiteral("primary")); + if (!GetStandardValue(id, -1).isNull()) { + writer->writeStartElement(QStringLiteral("primary")); - SaveImmediate(writer, id, -1); + SaveImmediate(writer, id, -1); - writer->writeEndElement(); // primary - - writer->writeStartElement(QStringLiteral("subelements")); + writer->writeEndElement(); // primary + } int arr_sz = this->InputArraySize(id); - writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz)); + if (arr_sz > 0) { + writer->writeStartElement(QStringLiteral("subelements")); - for (int i=0; iwriteStartElement(QStringLiteral("element")); + writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz)); - SaveImmediate(writer, id, i); + for (int i=0; iwriteStartElement(QStringLiteral("element")); - writer->writeEndElement(); // element + SaveImmediate(writer, id, i); + + writer->writeEndElement(); // element + } + + writer->writeEndElement(); // subelements } - - writer->writeEndElement(); // subelements } bool Node::LoadImmediate(QXmlStreamReader *reader, const QString &input, int element, SerializedData *data) @@ -1545,8 +1567,11 @@ bool Node::LoadImmediate(QXmlStreamReader *reader, const QString &input, int ele reader->skipCurrentElement(); } } - } else if (reader->name() == QStringLiteral("keyframing") && this->IsInputKeyframable(input)) { - this->SetInputIsKeyframing(input, reader->readElementText().toInt(), element); + } else if (reader->name() == QStringLiteral("keyframing")) { + bool k = reader->readElementText().toInt(); + if (this->IsInputKeyframable(input)) { + this->SetInputIsKeyframing(input, k, element); + } } else if (reader->name() == QStringLiteral("keyframes")) { int track = 0; @@ -1583,12 +1608,16 @@ bool Node::LoadImmediate(QXmlStreamReader *reader, const QString &input, int ele reader->skipCurrentElement(); } } + + return true; } void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const { + bool is_keyframing = this->IsInputKeyframing(input, element); + if (this->IsInputKeyframable(input)) { - writer->writeTextElement(QStringLiteral("keyframing"), QString::number(this->IsInputKeyframing(input, element))); + writer->writeTextElement(QStringLiteral("keyframing"), QString::number(is_keyframing)); } NodeValue::Type data_type = this->GetInputDataType(input); @@ -1613,24 +1642,26 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int ele writer->writeEndElement(); // standard // Write keyframes - writer->writeStartElement(QStringLiteral("keyframes")); + if (is_keyframing) { + writer->writeStartElement(QStringLiteral("keyframes")); - for (const NodeKeyframeTrack& track : this->GetKeyframeTracks(input, element)) { - writer->writeStartElement(QStringLiteral("track")); + for (const NodeKeyframeTrack& track : this->GetKeyframeTracks(input, element)) { + writer->writeStartElement(QStringLiteral("track")); - for (NodeKeyframe* key : track) { - writer->writeStartElement(QStringLiteral("key")); + for (NodeKeyframe* key : track) { + writer->writeStartElement(QStringLiteral("key")); - key->save(writer, data_type); + key->save(writer, data_type); - writer->writeEndElement(); // key + writer->writeEndElement(); // key + } + + writer->writeEndElement(); // track } - writer->writeEndElement(); // track + writer->writeEndElement(); // keyframes } - writer->writeEndElement(); // keyframes - if (data_type == NodeValue::kColor) { // Save color management information writer->writeTextElement(QStringLiteral("csinput"), this->GetInputProperty(input, QStringLiteral("col_input")).toString()); @@ -2417,6 +2448,8 @@ bool Node::ValueHint::load(QXmlStreamReader *reader) reader->skipCurrentElement(); } } + + return true; } void Node::ValueHint::save(QXmlStreamWriter *writer) const diff --git a/app/node/node.h b/app/node/node.h index dce7598b5..e17a8c4c5 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -58,7 +58,7 @@ namespace olive { class Folder; class Project; -class SerializedData; +struct SerializedData; /** * @brief A single processing unit that can be connected with others to create intricate processing systems diff --git a/app/node/project.cpp b/app/node/project.cpp index dcd565ed6..5d5bf9db4 100644 --- a/app/node/project.cpp +++ b/app/node/project.cpp @@ -83,7 +83,7 @@ void Project::Clear() } } -void Project::Load(QXmlStreamReader *reader) +SerializedData Project::Load(QXmlStreamReader *reader) { SerializedData data; @@ -156,32 +156,12 @@ void Project::Load(QXmlStreamReader *reader) } } - for (auto it = this->nodes().cbegin(); it != this->nodes().cend(); it++){ - (*it)->PostLoadEvent(&data); - } - - foreach (const SerializedData::SerializedConnection& con, data.desired_connections) { - if (Node *out = data.node_ptrs.value(con.output_node)) { - Node::ConnectEdge(out, con.input); - } - } - - foreach (const SerializedData::BlockLink& l, data.block_links) { - Node *a = l.block; - Node *b = data.node_ptrs.value(l.link); - - Node::Link(a, b); - } - - // Re-enable caches and resolve tracks - for (Node *n : this->nodes()) { - n->SetCachesEnabled(true); - } + return data; } void Project::Save(QXmlStreamWriter *writer) const { - writer->writeAttribute(QStringLiteral("version"), QString::number(230220)); + writer->writeAttribute(QStringLiteral("version"), QString::number(1)); writer->writeTextElement(QStringLiteral("uuid"), this->GetUuid().toString()); @@ -198,8 +178,6 @@ void Project::Save(QXmlStreamWriter *writer) const writer->writeAttribute(QStringLiteral("settings"), QStringLiteral("1")); } - writer->writeAttribute(QStringLiteral("id"), node->id()); - node->Save(writer); writer->writeEndElement(); // node diff --git a/app/node/project.h b/app/node/project.h index fe9abd5f7..57863fe50 100644 --- a/app/node/project.h +++ b/app/node/project.h @@ -70,7 +70,7 @@ public: return default_nodes_; } - void Load(QXmlStreamReader *reader); + SerializedData Load(QXmlStreamReader *reader); void Save(QXmlStreamWriter *writer) const; int GetNumberOfContextsNodeIsIn(Node *node, bool except_itself = false) const; diff --git a/app/node/project/serializer/serializer.cpp b/app/node/project/serializer/serializer.cpp index f6a72b475..b08ba90b8 100644 --- a/app/node/project/serializer/serializer.cpp +++ b/app/node/project/serializer/serializer.cpp @@ -58,7 +58,7 @@ void ProjectSerializer::Destroy() instances_.clear(); } -ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QString &filename, const QString &type) +ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QString &filename, LoadType load_type) { QFile project_file(filename); @@ -76,7 +76,7 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QStrin reader.reset(new QXmlStreamReader(&project_file)); } - Result inner_result = Load(project, reader.get(), type); + Result inner_result = Load(project, reader.get(), load_type); project_file.close(); @@ -89,14 +89,14 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QStrin r.SetDetails(reader->errorString()); return r; } else { - return kSuccess; + return inner_result; } } else { return kFileError; } } -ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamReader *reader, const QString &type) +ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamReader *reader, LoadType load_type) { // Determine project version uint version = 0; @@ -106,24 +106,26 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamRe if (reader->name() == QStringLiteral("olive") || reader->name() == QStringLiteral("project")) { // 0.1 projects only - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("version")) { - version = reader->readElementText().toUInt(); - } else if (reader->name() == QStringLiteral("url")) { - project->SetSavedURL(reader->readElementText()); - - // HACK for 0.1 projects - if (version == 190219) { - res = LoadWithSerializerVersion(version, project, reader); - } - } else if (reader->name() == type) { - // Found our data - res = LoadWithSerializerVersion(version, project, reader); - } else { - reader->skipCurrentElement(); + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("version")) { // 230220+ projects + version = attr.value().toUInt(); } } + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("version")) { // projects <= 220403 + version = reader->readElementText().toUInt(); + } else if (reader->name() == QStringLiteral("url")) { + if (project) { + project->SetSavedURL(reader->readElementText()); + } else { + reader->skipCurrentElement(); + } + } else { + // Handle any other value with the serializer + res = LoadWithSerializerVersion(version, project, reader, load_type); + } + } } else { reader->skipCurrentElement(); } @@ -132,7 +134,7 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamRe return res; } -ProjectSerializer::Result ProjectSerializer::Paste(const QString &type) +ProjectSerializer::Result ProjectSerializer::Paste(LoadType load_type) { QString clipboard = Core::PasteStringFromClipboard(); if (clipboard.isEmpty()) { @@ -141,28 +143,10 @@ ProjectSerializer::Result ProjectSerializer::Paste(const QString &type) QXmlStreamReader reader(clipboard); - Project temp; - ProjectSerializer::Result res = ProjectSerializer::Load(&temp, &reader, type); - - if (res.code() != ProjectSerializer::kSuccess) { - return res; - } - - 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); - } - } - - res.SetLoadedNodes(pasted_nodes); - - return res; + return ProjectSerializer::Load(nullptr, &reader, load_type); } -ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QString &type, bool compress) +ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, bool compress) { QString temp_save = FileFunctions::GetSafeTemporaryFilename(data.GetFilename()); @@ -172,7 +156,7 @@ ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QS QByteArray b; QXmlStreamWriter writer(&b); - Result inner_result = Save(&writer, data, type); + Result inner_result = Save(&writer, data); if (writer.hasError()) { Result r(kXmlError); @@ -207,7 +191,7 @@ ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QS } } -ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, const SaveData &data, const QString &type) +ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, const SaveData &data) { writer->setAutoFormatting(true); @@ -221,18 +205,14 @@ ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, cons // Version is stored in YYMMDD from whenever the project format was last changed // Allows easy integer math for checking project versions. - writer->writeTextElement(QStringLiteral("version"), QString::number(serializer->Version())); + writer->writeAttribute(QStringLiteral("version"), QString::number(serializer->Version())); if (!data.GetFilename().isEmpty()) { writer->writeTextElement("url", data.GetFilename()); } - writer->writeStartElement(type); - serializer->Save(writer, data, nullptr); - writer->writeEndElement(); // [type] - writer->writeEndElement(); // olive writer->writeEndDocument(); @@ -244,12 +224,12 @@ ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, cons return kSuccess; } -ProjectSerializer::Result ProjectSerializer::Copy(const SaveData &data, const QString &type) +ProjectSerializer::Result ProjectSerializer::Copy(const SaveData &data) { QString copy_str; QXmlStreamWriter writer(©_str); - ProjectSerializer::Result res = ProjectSerializer::Save(&writer, data, type); + ProjectSerializer::Result res = ProjectSerializer::Save(&writer, data); if (res == kSuccess) { Core::CopyStringToClipboard(copy_str); @@ -269,7 +249,7 @@ bool ProjectSerializer::IsCancelled() const return false; } -ProjectSerializer::Result ProjectSerializer::LoadWithSerializerVersion(uint version, Project *project, QXmlStreamReader *reader) +ProjectSerializer::Result ProjectSerializer::LoadWithSerializerVersion(uint version, Project *project, QXmlStreamReader *reader, LoadType load_type) { // Failed to find version in file if (version == 0) { @@ -291,7 +271,7 @@ ProjectSerializer::Result ProjectSerializer::LoadWithSerializerVersion(uint vers } if (serializer) { - LoadData ld = serializer->Load(project, reader, nullptr); + LoadData ld = serializer->Load(project, reader, load_type, nullptr); Result r(kSuccess); if (reader->hasError()) { r = Result(kXmlError); diff --git a/app/node/project/serializer/serializer.h b/app/node/project/serializer/serializer.h index c44d8654a..6f3212b0f 100644 --- a/app/node/project/serializer/serializer.h +++ b/app/node/project/serializer/serializer.h @@ -38,6 +38,14 @@ namespace olive { class ProjectSerializer { public: + enum LoadType + { + kProject, + kOnlyNodes, + kOnlyMarkers, + kOnlyKeyframes + }; + ProjectSerializer() = default; virtual ~ProjectSerializer(){} @@ -69,6 +77,10 @@ public: SerializedKeyframes keyframes; + MainWindowLayoutInfo layout; + + QVector nodes; + }; class Result @@ -91,10 +103,6 @@ public: void SetLoadData(const LoadData &p) { load_data_ = p; } - const QVector &GetLoadedNodes() const { return loaded_nodes_; } - - void SetLoadedNodes(const QVector &n) { loaded_nodes_ = n; } - private: ResultCode code_; @@ -102,28 +110,28 @@ public: LoadData load_data_; - QVector loaded_nodes_; - }; class SaveData { public: - SaveData(Project *project = nullptr, const QString &filename = QString()) + SaveData(LoadType type, Project *project = nullptr, const QString &filename = QString()) { + type_ = type; project_ = project; filename_ = filename; } - Project *GetProject() const - { - return project_; - } + Project *GetProject() const { return project_; } + void SetProject(Project *p) { project_ = p; } - const QString &GetFilename() const - { - return filename_; - } + const QString &GetFilename() const { return filename_; } + void SetFilename(const QString &s) { filename_ = s; } + + LoadType type() const { return type_; } + + const MainWindowLayoutInfo &GetLayout() const { return layout_; } + void SetLayout(const MainWindowLayoutInfo &layout) { layout_ = layout; } const QVector &GetOnlySerializeNodes() const { return only_serialize_nodes_; } void SetOnlySerializeNodes(const QVector &only) { only_serialize_nodes_ = only; } @@ -139,10 +147,14 @@ public: void SetProperties(const SerializedProperties &p) { properties_ = p; } private: + LoadType type_; + Project *project_; QString filename_; + MainWindowLayoutInfo layout_; + QVector only_serialize_nodes_; SerializedProperties properties_; @@ -157,18 +169,18 @@ public: static void Destroy(); - static Result Load(Project *project, const QString &filename, const QString &type); - static Result Load(Project *project, QXmlStreamReader *read_device, const QString &type); - static Result Paste(const QString &type); + static Result Load(Project *project, const QString &filename, LoadType load_type); + static Result Load(Project *project, QXmlStreamReader *read_device, LoadType load_type); + static Result Paste(LoadType load_type); - static Result Save(const SaveData &data, const QString &type, bool compress); - static Result Save(QXmlStreamWriter *write_device, const SaveData &data, const QString &type); - static Result Copy(const SaveData &data, const QString &type); + static Result Save(const SaveData &data, bool compress); + static Result Save(QXmlStreamWriter *write_device, const SaveData &data); + static Result Copy(const SaveData &data); static bool CheckCompressedID(QFile *file); protected: - virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const = 0; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const = 0; virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const {} @@ -177,7 +189,7 @@ protected: bool IsCancelled() const; private: - static Result LoadWithSerializerVersion(uint version, Project *project, QXmlStreamReader *reader); + static Result LoadWithSerializerVersion(uint version, Project *project, QXmlStreamReader *reader, LoadType load_type); static QVector instances_; diff --git a/app/node/project/serializer/serializer190219.cpp b/app/node/project/serializer/serializer190219.cpp index 0da1a405c..298c30213 100644 --- a/app/node/project/serializer/serializer190219.cpp +++ b/app/node/project/serializer/serializer190219.cpp @@ -22,7 +22,7 @@ namespace olive { -ProjectSerializer::LoadData ProjectSerializer190219::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer::LoadData ProjectSerializer190219::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const { return LoadData(); } diff --git a/app/node/project/serializer/serializer190219.h b/app/node/project/serializer/serializer190219.h index f8f5a598c..9686d2369 100644 --- a/app/node/project/serializer/serializer190219.h +++ b/app/node/project/serializer/serializer190219.h @@ -31,7 +31,7 @@ public: ProjectSerializer190219() = default; protected: - virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, 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 3c6e7e585..baa5a0af7 100644 --- a/app/node/project/serializer/serializer210528.cpp +++ b/app/node/project/serializer/serializer210528.cpp @@ -26,7 +26,7 @@ namespace olive { -ProjectSerializer210528::LoadData ProjectSerializer210528::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer210528::LoadData ProjectSerializer210528::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const { XMLNodeData xml_node_data; diff --git a/app/node/project/serializer/serializer210528.h b/app/node/project/serializer/serializer210528.h index 537b89cf4..adcc83b2f 100644 --- a/app/node/project/serializer/serializer210528.h +++ b/app/node/project/serializer/serializer210528.h @@ -31,7 +31,7 @@ public: ProjectSerializer210528() = default; protected: - virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, 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 1676cc2ad..f1ee256e0 100644 --- a/app/node/project/serializer/serializer210907.cpp +++ b/app/node/project/serializer/serializer210907.cpp @@ -26,7 +26,7 @@ namespace olive { -ProjectSerializer210907::LoadData ProjectSerializer210907::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer210907::LoadData ProjectSerializer210907::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const { XMLNodeData xml_node_data; diff --git a/app/node/project/serializer/serializer210907.h b/app/node/project/serializer/serializer210907.h index c3e8033de..ca9865980 100644 --- a/app/node/project/serializer/serializer210907.h +++ b/app/node/project/serializer/serializer210907.h @@ -31,7 +31,7 @@ public: ProjectSerializer210907() = default; protected: - virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, 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 e86c6c9fe..4c6078cf1 100644 --- a/app/node/project/serializer/serializer211228.cpp +++ b/app/node/project/serializer/serializer211228.cpp @@ -26,7 +26,7 @@ namespace olive { -ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const { QMap > properties; QMap > positions; diff --git a/app/node/project/serializer/serializer211228.h b/app/node/project/serializer/serializer211228.h index bc424bc4c..ea028caed 100644 --- a/app/node/project/serializer/serializer211228.h +++ b/app/node/project/serializer/serializer211228.h @@ -31,7 +31,7 @@ public: ProjectSerializer211228() = default; protected: - virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override; virtual uint Version() const override { diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index 1e8feb45f..1121623cb 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -26,7 +26,7 @@ namespace olive { -ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const { QMap > properties; QMap > positions; @@ -34,253 +34,275 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project LoadData load_data; - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("uuid")) { + if ((load_type == kProject && reader->name() == QStringLiteral("project")) + || (load_type == kOnlyNodes && (reader->name() == QStringLiteral("nodes") || reader->name() == QStringLiteral("timeline"))) + || (load_type == kOnlyKeyframes && reader->name() == QStringLiteral("keyframes")) + || (load_type == kOnlyMarkers && reader->name() == QStringLiteral("markers"))) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("layout")) { - project->SetUuid(QUuid::fromString(reader->readElementText())); + // Since the main window's functions have to occur in the GUI thread (and we're likely + // loading in a secondary thread), we load all necessary data into a separate struct so we + // can continue loading and queue it with the main window so it can handle the data + // appropriately in its own thread. - } else if (reader->name() == QStringLiteral("nodes")) { + load_data.layout = MainWindowLayoutInfo::fromXml(reader, xml_node_data.node_ptrs); - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("node")) { - bool is_root = false; - bool is_cm = false; - bool is_settings = false; - QString id; + } else if (reader->name() == QStringLiteral("uuid")) { - { - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - id = attr.value().toString(); - } else if (attr.name() == QStringLiteral("root") && attr.value() == QStringLiteral("1")) { - is_root = true; - } else if (attr.name() == QStringLiteral("cm") && attr.value() == QStringLiteral("1")) { - is_cm = true; - } else if (attr.name() == QStringLiteral("settings") && attr.value() == QStringLiteral("1")) { - is_settings = true; - } - } - } - - if (id.isEmpty()) { - qWarning() << "Failed to load node with empty ID"; - reader->skipCurrentElement(); - } else { - Node* node; - - if (is_root) { - node = project->root(); - } else if (is_cm) { - node = project->color_manager(); - } else if (is_settings) { - node = project->settings(); - } else { - node = NodeFactory::CreateFromID(id); - } - - if (!node) { - qWarning() << "Failed to find node with ID" << id; - reader->skipCurrentElement(); - } else { - // Disable cache while node is being loaded (we'll re-enable it later) - node->SetCachesEnabled(false); - - LoadNode(node, xml_node_data, reader); - - node->setParent(project); - } - } + if (project) { + project->SetUuid(QUuid::fromString(reader->readElementText())); } else { reader->skipCurrentElement(); } - } - } else if (reader->name() == QStringLiteral("keyframes")) { + } else if (reader->name() == QStringLiteral("nodes")) { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("node")) { - QString node_id; - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - node_id = attr.value().toString(); - break; - } - } + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + bool is_root = false; + bool is_cm = false; + bool is_settings = false; + QString id; - Node *n = nullptr; - if (!node_id.isEmpty()) { - n = NodeFactory::CreateFromID(node_id); - } - - if (!n) { - reader->skipCurrentElement(); - } else { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("input")) { - QString input_id; - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - input_id = attr.value().toString(); - break; - } + { + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + id = attr.value().toString(); + } else if (attr.name() == QStringLiteral("root") && attr.value() == QStringLiteral("1")) { + is_root = true; + } else if (attr.name() == QStringLiteral("cm") && attr.value() == QStringLiteral("1")) { + is_cm = true; + } else if (attr.name() == QStringLiteral("settings") && attr.value() == QStringLiteral("1")) { + is_settings = true; } + } + } - if (input_id.isEmpty()) { - reader->skipCurrentElement(); + if (id.isEmpty()) { + qWarning() << "Failed to load node with empty ID"; + reader->skipCurrentElement(); + } else { + Node* node; + + if (is_root) { + node = project->root(); + } else if (is_cm) { + node = project->color_manager(); + } else if (is_settings) { + node = project->settings(); + } else { + node = NodeFactory::CreateFromID(id); + } + + if (!node) { + qWarning() << "Failed to find node with ID" << id; + reader->skipCurrentElement(); + } else { + // Disable cache while node is being loaded (we'll re-enable it later) + node->SetCachesEnabled(false); + + LoadNode(node, xml_node_data, reader); + + if (project) { + node->setParent(project); } else { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("element")) { - QString element_id; - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - element_id = attr.value().toString(); - break; - } - } + load_data.nodes.append(node); + } + } + } + } else { + reader->skipCurrentElement(); + } + } - if (element_id.isEmpty()) { - reader->skipCurrentElement(); - } else { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("track")) { - QString track_id; - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - track_id = attr.value().toString(); - break; - } - } + } else if (reader->name() == QStringLiteral("keyframes")) { - if (track_id.isEmpty()) { - reader->skipCurrentElement(); - } else { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("key")) { - NodeKeyframe *key = new NodeKeyframe(); - key->set_input(input_id); - key->set_element(element_id.toInt()); - key->set_track(track_id.toInt()); + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + QString node_id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + node_id = attr.value().toString(); + break; + } + } - LoadKeyframe(reader, key, n->GetInputDataType(input_id)); + Node *n = nullptr; + if (!node_id.isEmpty()) { + n = NodeFactory::CreateFromID(node_id); + } - load_data.keyframes[node_id].append(key); - } else { - reader->skipCurrentElement(); - } - } - } - } else { - reader->skipCurrentElement(); + if (!n) { + reader->skipCurrentElement(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("input")) { + QString input_id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + input_id = attr.value().toString(); + break; + } + } + + if (input_id.isEmpty()) { + reader->skipCurrentElement(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("element")) { + QString element_id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + element_id = attr.value().toString(); + break; } } + + if (element_id.isEmpty()) { + reader->skipCurrentElement(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("track")) { + QString track_id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + track_id = attr.value().toString(); + break; + } + } + + if (track_id.isEmpty()) { + reader->skipCurrentElement(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("key")) { + NodeKeyframe *key = new NodeKeyframe(); + key->set_input(input_id); + key->set_element(element_id.toInt()); + key->set_track(track_id.toInt()); + + LoadKeyframe(reader, key, n->GetInputDataType(input_id)); + + load_data.keyframes[node_id].append(key); + } else { + reader->skipCurrentElement(); + } + } + } + } else { + reader->skipCurrentElement(); + } + } + } + } else { + reader->skipCurrentElement(); } + } + } + } else { + reader->skipCurrentElement(); + } + } + } + + delete n; + } else { + reader->skipCurrentElement(); + } + } + + } else if (reader->name() == QStringLiteral("markers")) { + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("marker")) { + TimelineMarker *marker = new TimelineMarker(); + LoadMarker(reader, marker); + load_data.markers.push_back(marker); + } else { + reader->skipCurrentElement(); + } + } + + } else if (reader->name() == QStringLiteral("positions")) { + + while (XMLReadNextStartElement(reader)) { + + if (reader->name() == QStringLiteral("context")) { + + quintptr context_ptr = 0; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("ptr")) { + context_ptr = attr.value().toULongLong(); + break; + } + } + + 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)) { + if (node_ptr) { + positions[context_ptr].insert(node_ptr, node_pos); } else { + qWarning() << "Failed to find pointer for node position"; reader->skipCurrentElement(); } } + } else { + reader->skipCurrentElement(); } - } else { - reader->skipCurrentElement(); } + } else { + qWarning() << "Attempted to load context with no pointer"; + reader->skipCurrentElement(); } + + } else { + + reader->skipCurrentElement(); + } - delete n; - } else { - reader->skipCurrentElement(); } - } - } else if (reader->name() == QStringLiteral("markers")) { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("marker")) { - TimelineMarker *marker = new TimelineMarker(); - LoadMarker(reader, marker); - load_data.markers.push_back(marker); - } else { - reader->skipCurrentElement(); - } - } + } else if (reader->name() == QStringLiteral("properties")) { - } else if (reader->name() == QStringLiteral("positions")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + quintptr ptr = 0; - while (XMLReadNextStartElement(reader)) { + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("ptr")) { + ptr = attr.value().toULongLong(); - if (reader->name() == QStringLiteral("context")) { - - quintptr context_ptr = 0; - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("ptr")) { - context_ptr = attr.value().toULongLong(); - break; - } - } - - 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)) { - if (node_ptr) { - positions[context_ptr].insert(node_ptr, node_pos); - } else { - qWarning() << "Failed to find pointer for node position"; - reader->skipCurrentElement(); - } - } - } else { - reader->skipCurrentElement(); + // 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 { - qWarning() << "Attempted to load context with no pointer"; reader->skipCurrentElement(); } - - } else { - - reader->skipCurrentElement(); - } + } else { + + // Skip this + reader->skipCurrentElement(); + } - - - } 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 - reader->skipCurrentElement(); - } } @@ -309,7 +331,8 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project } // Re-enable caches and resolve tracks - for (Node *n : project->nodes()) { + const QVector &nodes = project ? project->nodes() : load_data.nodes; + for (Node *n : nodes) { n->SetCachesEnabled(true); if (Track *t = dynamic_cast(n)) { diff --git a/app/node/project/serializer/serializer220403.h b/app/node/project/serializer/serializer220403.h index 5b4b8f7aa..e457e629d 100644 --- a/app/node/project/serializer/serializer220403.h +++ b/app/node/project/serializer/serializer220403.h @@ -31,7 +31,7 @@ public: ProjectSerializer220403() = default; protected: - virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override; virtual uint Version() const override { diff --git a/app/node/project/serializer/serializer230220.cpp b/app/node/project/serializer/serializer230220.cpp index f20c5c81e..0991d9a60 100644 --- a/app/node/project/serializer/serializer230220.cpp +++ b/app/node/project/serializer/serializer230220.cpp @@ -23,21 +23,349 @@ #include "config/config.h" #include "node/factory.h" #include "node/group/group.h" +#include "node/serializeddata.h" namespace olive { -ProjectSerializer230220::LoadData ProjectSerializer230220::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +ProjectSerializer230220::LoadData ProjectSerializer230220::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const { + QMap > properties; + QMap > positions; LoadData load_data; + SerializedData project_data; - project->Load(reader); + switch (load_type) { + case kProject: + { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("project")) { + project_data = project->Load(reader); + } else if (reader->name() == QStringLiteral("layout")) { + load_data.layout = MainWindowLayoutInfo::fromXml(reader, project_data.node_ptrs); + } else { + reader->skipCurrentElement(); + } + } + + PostConnect(project->nodes(), &project_data); + break; + } + case kOnlyMarkers: + { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("marker")) { + TimelineMarker *marker = new TimelineMarker(); + marker->load(reader); + load_data.markers.push_back(marker); + } else { + reader->skipCurrentElement(); + } + } + break; + } + case kOnlyKeyframes: + { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + QString node_id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + node_id = attr.value().toString(); + break; + } + } + + Node *n = nullptr; + if (!node_id.isEmpty()) { + n = NodeFactory::CreateFromID(node_id); + } + + if (!n) { + reader->skipCurrentElement(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("input")) { + QString input_id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + input_id = attr.value().toString(); + break; + } + } + + if (input_id.isEmpty()) { + reader->skipCurrentElement(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("element")) { + QString element_id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + element_id = attr.value().toString(); + break; + } + } + + if (element_id.isEmpty()) { + reader->skipCurrentElement(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("track")) { + QString track_id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + track_id = attr.value().toString(); + break; + } + } + + if (track_id.isEmpty()) { + reader->skipCurrentElement(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("key")) { + NodeKeyframe *key = new NodeKeyframe(); + key->set_input(input_id); + key->set_element(element_id.toInt()); + key->set_track(track_id.toInt()); + + key->load(reader, n->GetInputDataType(input_id)); + + load_data.keyframes[node_id].append(key); + } else { + reader->skipCurrentElement(); + } + } + } + } else { + reader->skipCurrentElement(); + } + } + } + } else { + reader->skipCurrentElement(); + } + } + } + } else { + reader->skipCurrentElement(); + } + } + } + + delete n; + } else { + reader->skipCurrentElement(); + } + } + break; + } + case kOnlyNodes: + { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + QString id; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + id = attr.value().toString(); + } + } + + if (id.isEmpty()) { + qWarning() << "Failed to load node with empty ID"; + reader->skipCurrentElement(); + } else { + Node* node = NodeFactory::CreateFromID(id); + if (!node) { + qWarning() << "Failed to find node with ID" << id; + reader->skipCurrentElement(); + } else { + // Disable cache while node is being loaded (we'll re-enable it later) + node->SetCachesEnabled(false); + node->Load(reader, &project_data); + load_data.nodes.append(node); + } + } + } 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 { + reader->skipCurrentElement(); + } + } + + PostConnect(load_data.nodes, &project_data); + + // Resolve serialized properties (if any) + for (auto it=properties.cbegin(); it!=properties.cend(); it++) { + Node *node = project_data.node_ptrs.value(it.key()); + if (node) { + load_data.properties.insert(node, it.value()); + } + } + + break; + } + } return load_data; } void ProjectSerializer230220::Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const { + if (!data.GetOnlySerializeMarkers().empty()) { + writer->writeStartElement(QStringLiteral("markers")); + writer->writeAttribute(QStringLiteral("version"), QString::number(1)); + + for (auto it=data.GetOnlySerializeMarkers().cbegin(); it!=data.GetOnlySerializeMarkers().cend(); it++) { + TimelineMarker *marker = *it; + writer->writeStartElement(QStringLiteral("marker")); + marker->save(writer); + writer->writeEndElement(); // marker + } + + writer->writeEndElement(); // markers + } else if (!data.GetOnlySerializeKeyframes().empty()) { + writer->writeStartElement(QStringLiteral("keyframes")); + + writer->writeAttribute(QStringLiteral("version"), QString::number(1)); + + // Organize keyframes into node+input + QHash > > > > organized; + + for (auto it=data.GetOnlySerializeKeyframes().cbegin(); it!=data.GetOnlySerializeKeyframes().cend(); it++) { + NodeKeyframe *key = *it; + organized[key->parent()->id()][key->input()][key->element()][key->track()].append(key); + } + + for (auto it=organized.cbegin(); it!=organized.cend(); it++) { + writer->writeStartElement(QStringLiteral("node")); + + writer->writeAttribute(QStringLiteral("id"), it.key()); + + for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) { + writer->writeStartElement(QStringLiteral("input")); + + writer->writeAttribute(QStringLiteral("id"), jt.key()); + + for (auto kt=jt.value().cbegin(); kt!=jt.value().cend(); kt++) { + writer->writeStartElement(QStringLiteral("element")); + + writer->writeAttribute(QStringLiteral("id"), QString::number(kt.key())); + + for (auto lt=kt.value().cbegin(); lt!=kt.value().cend(); lt++) { + const QVector &keys = lt.value(); + + writer->writeStartElement(QStringLiteral("track")); + + writer->writeAttribute(QStringLiteral("id"), QString::number(lt.key())); + + for (NodeKeyframe *key : keys) { + writer->writeStartElement(QStringLiteral("key")); + key->save(writer, key->parent()->GetInputDataType(key->input())); + writer->writeEndElement(); // key + } + + writer->writeEndElement(); // track + } + + writer->writeEndElement(); // element + } + + writer->writeEndElement(); // input + } + + writer->writeEndElement(); // node; + } + + writer->writeEndElement(); // keyframes + } else if (!data.GetOnlySerializeNodes().empty()) { + writer->writeStartElement(QStringLiteral("nodes")); + + writer->writeAttribute(QStringLiteral("version"), QString::number(1)); + + for (Node *n : data.GetOnlySerializeNodes()) { + writer->writeStartElement(QStringLiteral("node")); + n->Save(writer); + writer->writeEndElement(); + } + + if (!data.GetProperties().empty()) { + 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 + } + + writer->writeEndElement(); // nodes + } else if (Project *project = data.GetProject()) { + writer->writeStartElement(QStringLiteral("project")); + project->Save(writer); + writer->writeEndElement(); + + writer->writeStartElement(QStringLiteral("layout")); + data.GetLayout().toXml(writer); + writer->writeEndElement(); + } else { + qCritical() << "ProjectSerializer provided nothing to save"; + } +} + +void ProjectSerializer230220::PostConnect(const QVector &nodes, SerializedData *project_data) const +{ + for (auto it = nodes.cbegin(); it != nodes.cend(); it++){ + Node *n = *it; + + n->PostLoadEvent(project_data); + + n->SetCachesEnabled(true); + } + + foreach (const SerializedData::SerializedConnection& con, project_data->desired_connections) { + if (Node *out = project_data->node_ptrs.value(con.output_node)) { + Node::ConnectEdge(out, con.input); + } + } + + foreach (const SerializedData::BlockLink& l, project_data->block_links) { + Node *a = l.block; + Node *b = project_data->node_ptrs.value(l.link); + + Node::Link(a, b); + } } } diff --git a/app/node/project/serializer/serializer230220.h b/app/node/project/serializer/serializer230220.h index a71b3b4b7..478830305 100644 --- a/app/node/project/serializer/serializer230220.h +++ b/app/node/project/serializer/serializer230220.h @@ -31,7 +31,7 @@ public: ProjectSerializer230220() = default; protected: - virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override; virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const override; @@ -40,6 +40,9 @@ protected: return 230220; } +private: + void PostConnect(const QVector &nodes, SerializedData *project_data) const; + }; } diff --git a/app/node/serializeddata.h b/app/node/serializeddata.h index 442e6baa9..d6bad8553 100644 --- a/app/node/serializeddata.h +++ b/app/node/serializeddata.h @@ -54,7 +54,7 @@ struct SerializedData { QHash custom_properties; }; - QMap > positions; + QMap > positions; QHash node_ptrs; QList desired_connections; QList block_links; diff --git a/app/task/project/load/load.cpp b/app/task/project/load/load.cpp index df1e34545..40ff89aea 100644 --- a/app/task/project/load/load.cpp +++ b/app/task/project/load/load.cpp @@ -37,7 +37,9 @@ bool ProjectLoadTask::Run() project_->set_filename(GetFilename()); - ProjectSerializer::Result result = ProjectSerializer::Load(project_, GetFilename(), QStringLiteral("project")); + ProjectSerializer::Result result = ProjectSerializer::Load(project_, GetFilename(), ProjectSerializer::kProject); + + layout_ = result.GetLoadData().layout; switch (result.code()) { case ProjectSerializer::kSuccess: diff --git a/app/task/project/load/loadbasetask.h b/app/task/project/load/loadbasetask.h index 7674764df..8d3640460 100644 --- a/app/task/project/load/loadbasetask.h +++ b/app/task/project/load/loadbasetask.h @@ -50,11 +50,11 @@ public: protected: Project* project_; + MainWindowLayoutInfo layout_; + private: QString filename_; - MainWindowLayoutInfo layout_; - }; } diff --git a/app/task/project/save/save.cpp b/app/task/project/save/save.cpp index 0283c02f7..4bf44f33e 100644 --- a/app/task/project/save/save.cpp +++ b/app/task/project/save/save.cpp @@ -41,9 +41,13 @@ bool ProjectSaveTask::Run() { QString using_filename = override_filename_.isEmpty() ? project_->filename() : override_filename_; - ProjectSerializer::SaveData data(project_, using_filename); + ProjectSerializer::SaveData data(ProjectSerializer::kProject); - ProjectSerializer::Result result = ProjectSerializer::Save(data, QStringLiteral("project"), use_compression_); + data.SetFilename(using_filename); + data.SetProject(project_); + data.SetLayout(layout_); + + ProjectSerializer::Result result = ProjectSerializer::Save(data, use_compression_); bool success = false; diff --git a/app/timeline/timelinemarker.cpp b/app/timeline/timelinemarker.cpp index 085eb3a66..e4ad0f582 100644 --- a/app/timeline/timelinemarker.cpp +++ b/app/timeline/timelinemarker.cpp @@ -164,6 +164,8 @@ bool TimelineMarker::load(QXmlStreamReader *reader) // This element has no inner text, so just skip it reader->skipCurrentElement(); + + return true; } void TimelineMarker::save(QXmlStreamWriter *writer) const diff --git a/app/timeline/timelineworkarea.cpp b/app/timeline/timelineworkarea.cpp index 9e04975be..ba2c09e79 100644 --- a/app/timeline/timelineworkarea.cpp +++ b/app/timeline/timelineworkarea.cpp @@ -66,6 +66,7 @@ bool TimelineWorkArea::load(QXmlStreamReader *reader) version = attr.value().toUInt(); } } + Q_UNUSED(version) while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("enabled")) { diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index 998e1664b..1705f689c 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -188,10 +188,10 @@ void KeyframeView::SelectionManagerDeselectEvent(void *obj) bool KeyframeView::CopySelected(bool cut) { if (!selection_manager_.GetSelectedObjects().empty()) { - ProjectSerializer::SaveData sdata; + ProjectSerializer::SaveData sdata(ProjectSerializer::kOnlyKeyframes); sdata.SetOnlySerializeKeyframes(selection_manager_.GetSelectedObjects()); - ProjectSerializer::Copy(sdata, QStringLiteral("keyframes")); + ProjectSerializer::Copy(sdata); if (cut) { DeleteSelected(); @@ -209,7 +209,7 @@ bool KeyframeView::Paste(std::function find_node_functi return false; } - ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("keyframes")); + ProjectSerializer::Result res = ProjectSerializer::Paste(ProjectSerializer::kOnlyKeyframes); if (res == ProjectSerializer::kSuccess) { const ProjectSerializer::SerializedKeyframes &keys = res.GetLoadData().keyframes; diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index c29006023..548308338 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -537,7 +537,7 @@ bool NodeParamView::CopySelected(bool cut) return false; } - ProjectSerializer::SaveData sdata(contexts_.first()->project()); + ProjectSerializer::SaveData sdata(ProjectSerializer::kOnlyNodes); ProjectSerializer::SerializedProperties properties; QVector nodes; @@ -558,7 +558,7 @@ bool NodeParamView::CopySelected(bool cut) sdata.SetOnlySerializeNodesAndResolveGroups(nodes); sdata.SetProperties(properties); - ProjectSerializer::Copy(sdata, QStringLiteral("nodes")); + ProjectSerializer::Copy(sdata); if (cut) { DeleteSelected(); @@ -580,15 +580,15 @@ bool NodeParamView::Paste() bool NodeParamView::Paste(QWidget *parent, std::function(const ProjectSerializer::Result &)> get_existing_map_function) { - ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("nodes")); - if (res.GetLoadedNodes().isEmpty()) { + ProjectSerializer::Result res = ProjectSerializer::Paste(ProjectSerializer::kOnlyNodes); + if (res.GetLoadData().nodes.isEmpty()) { return false; } // Determine if any nodes of this type are already in the editor QHash existing_nodes = get_existing_map_function(res); - QVector nodes_to_paste_as_new = res.GetLoadedNodes(); + QVector nodes_to_paste_as_new = res.GetLoadData().nodes; MultiUndoCommand *command = new MultiUndoCommand(); if (!existing_nodes.empty()) { @@ -835,7 +835,7 @@ QHash NodeParamView::GenerateExistingPasteMap(const ProjectSeria { QVector ignore_nodes; QHash existing_nodes; - for (Node *n : r.GetLoadedNodes()) { + for (Node *n : r.GetLoadData().nodes) { if (Node *existing = GetNodeWithIDAndIgnoreList(n->id(), ignore_nodes)) { existing_nodes.insert(existing, n); ignore_nodes.append(existing); diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 39b1ec791..5500f1bba 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -225,7 +225,7 @@ void NodeView::CopySelected(bool cut) QString copy_str; QXmlStreamWriter writer(©_str); - ProjectSerializer::SaveData sdata(selected_nodes_.first()->project()); + ProjectSerializer::SaveData sdata(ProjectSerializer::kOnlyNodes); sdata.SetOnlySerializeNodesAndResolveGroups(selected_nodes_); ProjectSerializer::SerializedProperties properties; @@ -244,7 +244,7 @@ void NodeView::CopySelected(bool cut) sdata.SetProperties(properties); - ProjectSerializer::Save(&writer, sdata, QStringLiteral("nodes")); + ProjectSerializer::Save(&writer, sdata); Core::CopyStringToClipboard(copy_str); @@ -259,8 +259,8 @@ void NodeView::Paste() return; } - ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("nodes")); - if (res.GetLoadedNodes().isEmpty()) { + ProjectSerializer::Result res = ProjectSerializer::Paste(ProjectSerializer::kOnlyNodes); + if (res.GetLoadData().nodes.isEmpty()) { return; } @@ -274,12 +274,10 @@ void NodeView::Paste() pos.position.setY(node_props.value(QStringLiteral("y")).toDouble()); pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble(); - qDebug() << it.key() << pos.position; - map.insert(it.key(), pos); } - PostPaste(res.GetLoadedNodes(), map); + PostPaste(res.GetLoadData().nodes, map); } void NodeView::Duplicate() diff --git a/app/widget/timebased/timebasedwidget.cpp b/app/widget/timebased/timebasedwidget.cpp index 20fcb3734..945bb34ea 100644 --- a/app/widget/timebased/timebasedwidget.cpp +++ b/app/widget/timebased/timebasedwidget.cpp @@ -911,7 +911,7 @@ void TimeBasedWidget::HideSnaps() bool TimeBasedWidget::CopySelected(bool cut) { - if (ruler()->hasFocus() && ruler()->CopySelected(cut)) { + if (ruler()->CopySelected(cut)) { return true; } @@ -920,7 +920,7 @@ bool TimeBasedWidget::CopySelected(bool cut) bool TimeBasedWidget::Paste() { - if (ruler()->hasFocus() && ruler()->PasteMarkers()) { + if (ruler()->PasteMarkers()) { return true; } diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 23bc574d7..0af5e74be 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -646,7 +646,7 @@ bool TimelineWidget::CopySelected(bool cut) } } - ProjectSerializer::SaveData sdata(selected_nodes.first()->project()); + ProjectSerializer::SaveData sdata(ProjectSerializer::kOnlyNodes); sdata.SetOnlySerializeNodesAndResolveGroups(selected_nodes); // Cache the earliest in point so all copied clips have a "relative" in point that can be pasted anywhere @@ -664,7 +664,7 @@ bool TimelineWidget::CopySelected(bool cut) sdata.SetProperties(properties); - ProjectSerializer::Copy(sdata, QStringLiteral("timeline")); + ProjectSerializer::Copy(sdata); if (cut) { DeleteSelected(); @@ -1958,14 +1958,14 @@ bool TimelineWidget::PasteInternal(bool insert) return false; } - ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("timeline")); - if (res.GetLoadedNodes().isEmpty()) { + ProjectSerializer::Result res = ProjectSerializer::Paste(ProjectSerializer::kOnlyNodes); + if (res.GetLoadData().nodes.isEmpty()) { return false; } MultiUndoCommand *command = new MultiUndoCommand(); - foreach (Node *n, res.GetLoadedNodes()) { + foreach (Node *n, res.GetLoadData().nodes) { command->add_child(new NodeAddCommand(GetConnectedNode()->project(), n)); } @@ -2013,7 +2013,7 @@ QHash TimelineWidget::GenerateExistingPasteMap(const ProjectSeri { QHash m; - for (Node *n : r.GetLoadedNodes()) { + for (Node *n : r.GetLoadData().nodes) { for (Block *b : qAsConst(this->selected_blocks_)) { for (auto it=b->GetContextPositions().cbegin(); it!=b->GetContextPositions().cend(); it++) { if (it.key()->id() == n->id() && !m.contains(it.key())) { diff --git a/app/widget/timeruler/seekablewidget.cpp b/app/widget/timeruler/seekablewidget.cpp index 6fb2a5765..4df431c97 100644 --- a/app/widget/timeruler/seekablewidget.cpp +++ b/app/widget/timeruler/seekablewidget.cpp @@ -123,10 +123,10 @@ void SeekableWidget::DeleteSelected() bool SeekableWidget::CopySelected(bool cut) { if (!selection_manager_.GetSelectedObjects().empty()) { - ProjectSerializer::SaveData sdata; + ProjectSerializer::SaveData sdata(ProjectSerializer::kOnlyMarkers); sdata.SetOnlySerializeMarkers(selection_manager_.GetSelectedObjects()); - ProjectSerializer::Copy(sdata, QStringLiteral("markers")); + ProjectSerializer::Copy(sdata); if (cut) { DeleteSelected(); @@ -140,7 +140,7 @@ bool SeekableWidget::CopySelected(bool cut) bool SeekableWidget::PasteMarkers() { - ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("markers")); + ProjectSerializer::Result res = ProjectSerializer::Paste(ProjectSerializer::kOnlyMarkers); if (res == ProjectSerializer::kSuccess) { const std::vector &markers = res.GetLoadData().markers; if (!markers.empty()) { diff --git a/app/window/mainwindow/mainwindowlayoutinfo.cpp b/app/window/mainwindow/mainwindowlayoutinfo.cpp index 4970f6433..c9e99de54 100644 --- a/app/window/mainwindow/mainwindowlayoutinfo.cpp +++ b/app/window/mainwindow/mainwindowlayoutinfo.cpp @@ -4,8 +4,6 @@ namespace olive { void MainWindowLayoutInfo::toXml(QXmlStreamWriter *writer) const { - writer->writeStartElement(QStringLiteral("layout")); - writer->writeAttribute(QStringLiteral("version"), QString::number(kVersion)); writer->writeStartElement(QStringLiteral("folders")); @@ -56,8 +54,6 @@ void MainWindowLayoutInfo::toXml(QXmlStreamWriter *writer) const writer->writeEndElement(); // data writer->writeTextElement(QStringLiteral("state"), QString(state_.toBase64())); - - writer->writeEndElement(); // layout } MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, const QHash &node_ptrs)