From d7b3c523f4fc54f092ae704e2ac64da1bcf7b6c7 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 4 Apr 2022 00:03:57 -0700 Subject: [PATCH] groups: improved serialization Introduced new project version with extra customization for groups --- app/node/group/group.cpp | 29 +- app/node/group/group.h | 6 +- app/node/node.cpp | 33 +- app/node/node.h | 8 +- app/node/param.cpp | 9 + app/node/param.h | 49 +- app/node/project/serializer/CMakeLists.txt | 2 + app/node/project/serializer/serializer.cpp | 2 + .../project/serializer/serializer211228.cpp | 304 ----- .../project/serializer/serializer211228.h | 20 - .../project/serializer/serializer220403.cpp | 1071 +++++++++++++++++ .../project/serializer/serializer220403.h | 119 ++ app/node/value.cpp | 67 ++ app/node/value.h | 10 +- app/render/opengl/openglrenderer.cpp | 1 + .../nodeparamviewwidgetbridge.cpp | 3 + app/widget/nodeview/nodeview.cpp | 2 +- 17 files changed, 1392 insertions(+), 343 deletions(-) create mode 100644 app/node/project/serializer/serializer220403.cpp create mode 100644 app/node/project/serializer/serializer220403.h diff --git a/app/node/group/group.cpp b/app/node/group/group.cpp index 97ef05677..20b43caaa 100644 --- a/app/node/group/group.cpp +++ b/app/node/group/group.cpp @@ -58,7 +58,7 @@ void NodeGroup::Retranslate() } } -QString NodeGroup::AddInputPassthrough(const NodeInput &input, const InputFlags &flags) +QString NodeGroup::AddInputPassthrough(const NodeInput &input, const InputFlags &flags, const QString &force_id) { Q_ASSERT(ContextContainsNode(input.node())); @@ -70,11 +70,26 @@ QString NodeGroup::AddInputPassthrough(const NodeInput &input, const InputFlags } // Add input - QString id = input.input(); - int i = 2; - while (HasInputWithID(id)) { - id = QStringLiteral("%1_%2").arg(input.name(), QString::number(i)); - i++; + QString id; + if (force_id.isEmpty()) { + id = input.input(); + int i = 2; + while (HasInputWithID(id)) { + id = QStringLiteral("%1_%2").arg(input.name(), QString::number(i)); + i++; + } + } else { + id = force_id; + + bool already_exists = false; + for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) { + if (it->first == id) { + already_exists = true; + break; + } + } + + Q_ASSERT(!already_exists); } AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags() | flags); @@ -153,7 +168,7 @@ bool NodeGroup::GetInner(NodeInput *input) void NodeGroupAddInputPassthrough::redo() { if (!group_->ContainsInputPassthrough(input_)) { - group_->AddInputPassthrough(input_); + group_->AddInputPassthrough(input_, InputFlags(), force_id_); actually_added_ = true; } else { actually_added_ = false; diff --git a/app/node/group/group.h b/app/node/group/group.h index b87339d30..d9407dd3a 100644 --- a/app/node/group/group.h +++ b/app/node/group/group.h @@ -41,7 +41,7 @@ public: virtual void Retranslate() override; - QString AddInputPassthrough(const NodeInput &input, const InputFlags &flags = InputFlags()); + QString AddInputPassthrough(const NodeInput &input, const InputFlags &flags = InputFlags(), const QString &force_id = QString()); void RemoveInputPassthrough(const NodeInput &input); @@ -103,7 +103,7 @@ private: class NodeGroupAddInputPassthrough : public UndoCommand { public: - NodeGroupAddInputPassthrough(NodeGroup *group, const NodeInput &input) : + NodeGroupAddInputPassthrough(NodeGroup *group, const NodeInput &input, const QString &force_id = QString()) : group_(group), input_(input), actually_added_(false) @@ -124,6 +124,8 @@ private: NodeInput input_; + QString force_id_; + bool actually_added_; }; diff --git a/app/node/node.cpp b/app/node/node.cpp index 74468036e..daf12f663 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -554,6 +554,37 @@ QVariant Node::GetSplitDefaultValueOnTrack(const QString &input, int track) cons } } +void Node::SetDefaultValue(const QString &input, const QVariant &val) +{ + NodeValue::Type type = GetInputDataType(input); + + SetSplitDefaultValue(input, NodeValue::split_normal_value_into_track_values(type, val)); +} + +void Node::SetSplitDefaultValue(const QString &input, const SplitValue &val) +{ + Input* i = GetInternalInputData(input); + + if (i) { + i->default_value = val; + } else { + ReportInvalidInput("set default value of", input); + } +} + +void Node::SetSplitDefaultValueOnTrack(const QString &input, const QVariant &val, int track) +{ + Input* i = GetInternalInputData(input); + + if (i) { + if (track < i->default_value.size()) { + i->default_value[track] = val; + } + } else { + ReportInvalidInput("set default value on track of", input); + } +} + const QVector &Node::GetKeyframeTracks(const QString &input, int element) const { return GetImmediate(input, element)->keyframe_tracks(); @@ -1019,7 +1050,7 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap& cre // This node should have been created by the context loop above NodeInput input = it->second; input.set_node(created.value(input.node())); - command->add_child(new NodeGroupAddInputPassthrough(dst_group, input)); + command->add_child(new NodeGroupAddInputPassthrough(dst_group, input, it->first)); } command->add_child(new NodeGroupSetOutputPassthrough(dst_group, created.value(src_group->GetOutputPassthrough()))); diff --git a/app/node/node.h b/app/node/node.h index f0c27c55a..1b5414d2b 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -321,6 +321,8 @@ public: virtual QString GetInputName(const QString& id) const; + void SetInputName(const QString& id, const QString& name); + bool IsInputHidden(const QString& input) const; bool IsInputConnectable(const QString& input) const; bool IsInputKeyframable(const QString& input) const; @@ -404,6 +406,10 @@ public: SplitValue GetSplitDefaultValue(const QString& input) const; QVariant GetSplitDefaultValueOnTrack(const QString& input, int track) const; + void SetDefaultValue(const QString& input, const QVariant &val); + void SetSplitDefaultValue(const QString& input, const SplitValue &val); + void SetSplitDefaultValueOnTrack(const QString& input, const QVariant &val, int track); + const QVector& GetKeyframeTracks(const QString& input, int element) const; const QVector& GetKeyframeTracks(const NodeInput& input) const { @@ -974,8 +980,6 @@ protected: void RemoveInput(const QString& id); - void SetInputName(const QString& id, const QString& name); - void SetComboBoxStrings(const QString& id, const QStringList& strings) { SetInputProperty(id, QStringLiteral("combo_str"), strings); diff --git a/app/node/param.cpp b/app/node/param.cpp index d7676c615..07a5c6510 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -123,6 +123,15 @@ QVariant NodeInput::GetProperty(const QString &key) const } } +QHash NodeInput::GetProperties() const +{ + if (IsValid()) { + return node_->GetInputProperties(input_); + } else { + return QHash(); + } +} + QVariant NodeInput::GetValueAtTime(const rational &time) const { if (IsValid()) { diff --git a/app/node/param.h b/app/node/param.h index e06ba0a9b..da2e12320 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -52,11 +52,6 @@ public: f_ = flags; } - bool operator&(const InputFlag& f) const - { - return f_ & f; - } - InputFlags operator|(const InputFlags &f) const { InputFlags i = *this; @@ -70,6 +65,49 @@ public: return *this; } + InputFlags operator&(const InputFlags &f) const + { + InputFlags i = *this; + i &= f; + return i; + } + + InputFlags &operator&=(const InputFlags &f) + { + f_ &= f.f_; + return *this; + } + + InputFlags operator&(const InputFlag &f) const + { + InputFlags i = *this; + i &= f; + return i; + } + + InputFlags &operator&=(const InputFlag &f) + { + f_ &= f; + return *this; + } + + InputFlags operator~() const + { + InputFlags i = *this; + i.f_ = ~i.f_; + return i; + } + + inline operator bool() const + { + return f_; + } + + inline const uint64_t &value() const + { + return f_; + } + private: uint64_t f_; @@ -188,6 +226,7 @@ public: QStringList GetComboBoxStrings() const; QVariant GetProperty(const QString& key) const; + QHash GetProperties() const; QVariant GetValueAtTime(const rational& time) const; diff --git a/app/node/project/serializer/CMakeLists.txt b/app/node/project/serializer/CMakeLists.txt index e0328450e..44201649c 100644 --- a/app/node/project/serializer/CMakeLists.txt +++ b/app/node/project/serializer/CMakeLists.txt @@ -27,5 +27,7 @@ set(OLIVE_SOURCES node/project/serializer/serializer210907.h node/project/serializer/serializer211228.cpp node/project/serializer/serializer211228.h + node/project/serializer/serializer220403.cpp + node/project/serializer/serializer220403.h PARENT_SCOPE ) diff --git a/app/node/project/serializer/serializer.cpp b/app/node/project/serializer/serializer.cpp index 446dced66..91164db0e 100644 --- a/app/node/project/serializer/serializer.cpp +++ b/app/node/project/serializer/serializer.cpp @@ -30,6 +30,7 @@ #include "serializer210528.h" #include "serializer210907.h" #include "serializer211228.h" +#include "serializer220403.h" namespace olive { @@ -45,6 +46,7 @@ void ProjectSerializer::Initialize() instances_.append(new ProjectSerializer210528); instances_.append(new ProjectSerializer210907); instances_.append(new ProjectSerializer211228); + instances_.append(new ProjectSerializer220403); } void ProjectSerializer::Destroy() diff --git a/app/node/project/serializer/serializer211228.cpp b/app/node/project/serializer/serializer211228.cpp index e41c33360..fb7c507d3 100644 --- a/app/node/project/serializer/serializer211228.cpp +++ b/app/node/project/serializer/serializer211228.cpp @@ -214,80 +214,6 @@ ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project return load_data; } -void ProjectSerializer211228::Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const -{ - Project *project = data.GetProject(); - - writer->writeTextElement(QStringLiteral("uuid"), data.GetProject()->GetUuid().toString()); - - writer->writeStartElement(QStringLiteral("nodes")); - - const QVector &using_node_list = (data.GetOnlySerializeNodes().isEmpty()) ? project->nodes() : data.GetOnlySerializeNodes(); - - foreach (Node* node, using_node_list) { - writer->writeStartElement(QStringLiteral("node")); - - if (node == project->root()) { - writer->writeAttribute(QStringLiteral("root"), QStringLiteral("1")); - } else if (node == project->color_manager()) { - writer->writeAttribute(QStringLiteral("cm"), QStringLiteral("1")); - } else if (node == project->settings()) { - writer->writeAttribute(QStringLiteral("settings"), QStringLiteral("1")); - } - - writer->writeAttribute(QStringLiteral("id"), node->id()); - - SaveNode(node, writer); - - writer->writeEndElement(); // node - } - - writer->writeEndElement(); // nodes - - writer->writeStartElement(QStringLiteral("positions")); - - foreach (Node* context, using_node_list) { - const Node::PositionMap &map = context->GetContextPositions(); - - if (!map.isEmpty()) { - writer->writeStartElement(QStringLiteral("context")); - - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(context))); - - for (auto jt=map.cbegin(); jt!=map.cend(); jt++) { - if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) { - writer->writeStartElement(QStringLiteral("node")); - SavePosition(writer, jt.key(), jt.value()); - writer->writeEndElement(); // node - } - } - - writer->writeEndElement(); // context - } - } - - writer->writeEndElement(); // positions - - writer->writeStartElement(QStringLiteral("properties")); - - for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) { - writer->writeStartElement(QStringLiteral("node")); - - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(it.key()))); - - for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) { - writer->writeTextElement(jt.key(), jt.value()); - } - - writer->writeEndElement(); // node - } - - writer->writeEndElement(); // properties - - // Save main window project layout - project->GetLayoutInfo().toXml(writer); -} - void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const { while (XMLReadNextStartElement(reader)) { @@ -373,60 +299,6 @@ 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(node))); - - writer->writeTextElement(QStringLiteral("label"), node->GetLabel()); - writer->writeTextElement(QStringLiteral("color"), QString::number(node->GetOverrideColor())); - - foreach (const QString& input, node->inputs()) { - writer->writeStartElement(QStringLiteral("input")); - - SaveInput(node, writer, input); - - writer->writeEndElement(); // input - } - - writer->writeStartElement(QStringLiteral("links")); - foreach (Node* link, node->links()) { - writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast(link))); - } - writer->writeEndElement(); // links - - writer->writeStartElement(QStringLiteral("connections")); - for (auto it=node->input_connections().cbegin(); it!=node->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->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast(it->second))); - - writer->writeEndElement(); // connection - } - writer->writeEndElement(); // connections - - writer->writeStartElement(QStringLiteral("hints")); - for (auto it=node->GetValueHints().cbegin(); it!=node->GetValueHints().cend(); it++) { - writer->writeStartElement(QStringLiteral("hint")); - - writer->writeAttribute(QStringLiteral("input"), it.key().input); - writer->writeAttribute(QStringLiteral("element"), QString::number(it.key().element)); - - SaveValueHint(&it.value(), writer); - - writer->writeEndElement(); // hint - } - writer->writeEndElement(); - - writer->writeStartElement(QStringLiteral("custom")); - - SaveNodeCustom(writer, node); - - writer->writeEndElement(); // custom -} - void ProjectSerializer211228::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const { QString param_id; @@ -484,33 +356,6 @@ void ProjectSerializer211228::LoadInput(Node *node, QXmlStreamReader *reader, XM } } -void ProjectSerializer211228::SaveInput(Node *node, QXmlStreamWriter *writer, const QString &id) const -{ - writer->writeAttribute(QStringLiteral("id"), id); - - writer->writeStartElement(QStringLiteral("primary")); - - SaveImmediate(writer, node, id, -1); - - writer->writeEndElement(); // primary - - writer->writeStartElement(QStringLiteral("subelements")); - - int arr_sz = node->InputArraySize(id); - - writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz)); - - for (int i=0; iwriteStartElement(QStringLiteral("element")); - - SaveImmediate(writer, node, id, i); - - writer->writeEndElement(); // element - } - - writer->writeEndElement(); // subelements -} - void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData &xml_node_data) const { Q_UNUSED(xml_node_data) @@ -628,69 +473,6 @@ void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node } } -void ProjectSerializer211228::SaveImmediate(QXmlStreamWriter *writer, Node *node, const QString& input, int element) const -{ - if (node->IsInputKeyframable(input)) { - writer->writeTextElement(QStringLiteral("keyframing"), QString::number(node->IsInputKeyframing(input, element))); - } - - NodeValue::Type data_type = node->GetInputDataType(input); - - // Write standard value - writer->writeStartElement(QStringLiteral("standard")); - - foreach (const QVariant& v, node->GetSplitStandardValue(input, element)) { - writer->writeStartElement(QStringLiteral("track")); - - if (data_type == NodeValue::kVideoParams) { - v.value().Save(writer); - } else if (data_type == NodeValue::kAudioParams) { - v.value().Save(writer); - } else { - writer->writeCharacters(NodeValue::ValueToString(data_type, v, true)); - } - - writer->writeEndElement(); // track - } - - writer->writeEndElement(); // standard - - // Write keyframes - writer->writeStartElement(QStringLiteral("keyframes")); - - for (const NodeKeyframeTrack& track : node->GetKeyframeTracks(input, element)) { - writer->writeStartElement(QStringLiteral("track")); - - for (NodeKeyframe* key : track) { - writer->writeStartElement(QStringLiteral("key")); - - writer->writeAttribute(QStringLiteral("input"), key->input()); - writer->writeAttribute(QStringLiteral("time"), key->time().toString()); - writer->writeAttribute(QStringLiteral("type"), QString::number(key->type())); - writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(key->bezier_control_in().x())); - writer->writeAttribute(QStringLiteral("inhandley"), QString::number(key->bezier_control_in().y())); - writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(key->bezier_control_out().x())); - writer->writeAttribute(QStringLiteral("outhandley"), QString::number(key->bezier_control_out().y())); - - writer->writeCharacters(NodeValue::ValueToString(data_type, key->value(), true)); - - writer->writeEndElement(); // key - } - - writer->writeEndElement(); // track - } - - writer->writeEndElement(); // keyframes - - if (data_type == NodeValue::kColor) { - // Save color management information - writer->writeTextElement(QStringLiteral("csinput"), node->GetInputProperty(input, QStringLiteral("col_input")).toString()); - writer->writeTextElement(QStringLiteral("csdisplay"), node->GetInputProperty(input, QStringLiteral("col_display")).toString()); - writer->writeTextElement(QStringLiteral("csview"), node->GetInputProperty(input, QStringLiteral("col_view")).toString()); - writer->writeTextElement(QStringLiteral("cslook"), node->GetInputProperty(input, QStringLiteral("col_look")).toString()); - } -} - bool ProjectSerializer211228::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const { bool got_node_ptr = false; @@ -722,15 +504,6 @@ bool ProjectSerializer211228::LoadPosition(QXmlStreamReader *reader, quintptr *n return got_node_ptr && got_pos_x && got_pos_y; } -void ProjectSerializer211228::SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const -{ - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(node))); - - 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)); -} - void ProjectSerializer211228::PostConnect(const XMLNodeData &xml_node_data) const { foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { @@ -827,36 +600,6 @@ void ProjectSerializer211228::LoadNodeCustom(QXmlStreamReader *reader, Node *nod } } -void ProjectSerializer211228::SaveNodeCustom(QXmlStreamWriter *writer, Node *node) const -{ - if (ViewerOutput *viewer = dynamic_cast(node)) { - // Write TimelinePoints - writer->writeStartElement(QStringLiteral("points")); - SaveTimelinePoints(writer, viewer->GetTimelinePoints()); - writer->writeEndElement(); // points - - if (Footage *footage = dynamic_cast(node)) { - writer->writeTextElement(QStringLiteral("timestamp"), QString::number(footage->timestamp())); - } - } else if (Track *track = dynamic_cast(node)) { - writer->writeTextElement(QStringLiteral("height"), QString::number(track->GetTrackHeight())); - } else if (NodeGroup *group = dynamic_cast(node)) { - writer->writeStartElement(QStringLiteral("inputpassthroughs")); - - foreach (const NodeGroup::InputPassthrough &ip, group->GetInputPassthroughs()) { - writer->writeStartElement(QStringLiteral("inputpassthrough")); - writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast(ip.second.node()))); - writer->writeTextElement(QStringLiteral("input"), ip.second.input()); - writer->writeTextElement(QStringLiteral("element"), QString::number(ip.second.element())); - writer->writeEndElement(); // input - } - - writer->writeEndElement(); // inputpassthroughs - - writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast(group->GetOutputPassthrough()))); - } -} - void ProjectSerializer211228::LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const { while (XMLReadNextStartElement(reader)) { @@ -870,17 +613,6 @@ void ProjectSerializer211228::LoadTimelinePoints(QXmlStreamReader *reader, Timel } } -void ProjectSerializer211228::SaveTimelinePoints(QXmlStreamWriter *writer, TimelinePoints *points) const -{ - writer->writeStartElement(QStringLiteral("workarea")); - SaveWorkArea(writer, points->workarea()); - writer->writeEndElement(); // workarea - - writer->writeStartElement(QStringLiteral("markers")); - SaveMarkerList(writer, points->markers()); - writer->writeEndElement(); // markers -} - void ProjectSerializer211228::LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const { rational range_in = workarea->in(); @@ -905,13 +637,6 @@ void ProjectSerializer211228::LoadWorkArea(QXmlStreamReader *reader, TimelineWor reader->skipCurrentElement(); } -void ProjectSerializer211228::SaveWorkArea(QXmlStreamWriter *writer, TimelineWorkArea *workarea) const -{ - writer->writeAttribute(QStringLiteral("enabled"), QString::number(workarea->enabled())); - writer->writeAttribute(QStringLiteral("in"), workarea->in().toString()); - writer->writeAttribute(QStringLiteral("out"), workarea->out().toString()); -} - void ProjectSerializer211228::LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const { while (XMLReadNextStartElement(reader)) { @@ -936,20 +661,6 @@ void ProjectSerializer211228::LoadMarkerList(QXmlStreamReader *reader, TimelineM } } -void ProjectSerializer211228::SaveMarkerList(QXmlStreamWriter *writer, TimelineMarkerList *markers) const -{ - foreach (TimelineMarker* marker, markers->list()) { - writer->writeStartElement(QStringLiteral("marker")); - - writer->writeAttribute(QStringLiteral("name"), marker->name()); - - writer->writeAttribute(QStringLiteral("in"), marker->time().in().toString()); - writer->writeAttribute(QStringLiteral("out"), marker->time().out().toString()); - - writer->writeEndElement(); // marker - } -} - void ProjectSerializer211228::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const { QVector types; @@ -975,19 +686,4 @@ void ProjectSerializer211228::LoadValueHint(Node::ValueHint *hint, QXmlStreamRea hint->set_type(types); } -void ProjectSerializer211228::SaveValueHint(const Node::ValueHint *hint, QXmlStreamWriter *writer) const -{ - writer->writeStartElement(QStringLiteral("types")); - - for (auto it=hint->types().cbegin(); it!=hint->types().cend(); it++) { - writer->writeTextElement(QStringLiteral("type"), QString::number(*it)); - } - - writer->writeEndElement(); // types - - writer->writeTextElement(QStringLiteral("index"), QString::number(hint->index())); - - writer->writeTextElement(QStringLiteral("tag"), hint->tag()); -} - } diff --git a/app/node/project/serializer/serializer211228.h b/app/node/project/serializer/serializer211228.h index a13e0641b..20982c07a 100644 --- a/app/node/project/serializer/serializer211228.h +++ b/app/node/project/serializer/serializer211228.h @@ -33,8 +33,6 @@ public: protected: virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; - virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const override; - virtual uint Version() const override { return 211228; @@ -70,42 +68,24 @@ private: void LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const; - void SaveNode(Node *node, QXmlStreamWriter *writer) const; - void LoadInput(Node *node, QXmlStreamReader* reader, XMLNodeData &xml_node_data) const; - void SaveInput(Node *node, QXmlStreamWriter* writer, const QString& id) const; - void LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData& xml_node_data) const; - void SaveImmediate(QXmlStreamWriter *writer, Node *node, const QString &input, int element) const; - bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const; - void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const; - void PostConnect(const XMLNodeData &xml_node_data) const; void LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const; - void SaveNodeCustom(QXmlStreamWriter *writer, Node *node) const; - void LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const; - void SaveTimelinePoints(QXmlStreamWriter *writer, TimelinePoints *points) const; - void LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const; - void SaveWorkArea(QXmlStreamWriter *writer, TimelineWorkArea *workarea) const; - void LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const; - void SaveMarkerList(QXmlStreamWriter *writer, TimelineMarkerList *markers) const; - void LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const; - void SaveValueHint(const Node::ValueHint *hint, QXmlStreamWriter *writer) const; - }; } diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp new file mode 100644 index 000000000..e8e9187f2 --- /dev/null +++ b/app/node/project/serializer/serializer220403.cpp @@ -0,0 +1,1071 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "serializer220403.h" + +#include "node/factory.h" + +namespace olive { + +ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project, QXmlStreamReader *reader, void *reserved) const +{ + QMap > properties; + QMap > positions; + XMLNodeData xml_node_data; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("url")) { + project->SetSavedURL(reader->readElementText()); + } else if (reader->name() == QStringLiteral("project")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("layout")) { + + // 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. + + project->SetLayoutInfo(MainWindowLayoutInfo::fromXml(reader, xml_node_data.node_ptrs)); + + } else if (reader->name() == QStringLiteral("uuid")) { + + project->SetUuid(QUuid::fromString(reader->readElementText())); + + } else if (reader->name() == QStringLiteral("nodes")) { + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + bool is_root = false; + bool is_cm = false; + bool is_settings = false; + QString id; + + { + 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 { + LoadNode(node, xml_node_data, reader); + node->setParent(project); + } + } + } 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 { + qWarning() << "Attempted to load context with no pointer"; + reader->skipCurrentElement(); + } + + } else { + + 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(); + + } + } + } else { + reader->skipCurrentElement(); + } + } + + // 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 ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const +{ + Project *project = data.GetProject(); + + writer->writeTextElement(QStringLiteral("uuid"), data.GetProject()->GetUuid().toString()); + + writer->writeStartElement(QStringLiteral("nodes")); + + const QVector &using_node_list = (data.GetOnlySerializeNodes().isEmpty()) ? project->nodes() : data.GetOnlySerializeNodes(); + + foreach (Node* node, using_node_list) { + writer->writeStartElement(QStringLiteral("node")); + + if (node == project->root()) { + writer->writeAttribute(QStringLiteral("root"), QStringLiteral("1")); + } else if (node == project->color_manager()) { + writer->writeAttribute(QStringLiteral("cm"), QStringLiteral("1")); + } else if (node == project->settings()) { + writer->writeAttribute(QStringLiteral("settings"), QStringLiteral("1")); + } + + writer->writeAttribute(QStringLiteral("id"), node->id()); + + SaveNode(node, writer); + + writer->writeEndElement(); // node + } + + writer->writeEndElement(); // nodes + + writer->writeStartElement(QStringLiteral("positions")); + + foreach (Node* context, using_node_list) { + const Node::PositionMap &map = context->GetContextPositions(); + + if (!map.isEmpty()) { + writer->writeStartElement(QStringLiteral("context")); + + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(context))); + + for (auto jt=map.cbegin(); jt!=map.cend(); jt++) { + if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) { + writer->writeStartElement(QStringLiteral("node")); + SavePosition(writer, jt.key(), jt.value()); + writer->writeEndElement(); // node + } + } + + writer->writeEndElement(); // context + } + } + + writer->writeEndElement(); // positions + + writer->writeStartElement(QStringLiteral("properties")); + + for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) { + writer->writeStartElement(QStringLiteral("node")); + + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(it.key()))); + + for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) { + writer->writeTextElement(jt.key(), jt.value()); + } + + writer->writeEndElement(); // node + } + + writer->writeEndElement(); // properties + + // Save main window project layout + project->GetLayoutInfo().toXml(writer); +} + +void ProjectSerializer220403::LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const +{ + while (XMLReadNextStartElement(reader)) { + if (IsCancelled()) { + return; + } + + if (reader->name() == QStringLiteral("input")) { + LoadInput(node, reader, xml_node_data); + } else if (reader->name() == QStringLiteral("ptr")) { + quintptr ptr = reader->readElementText().toULongLong(); + xml_node_data.node_ptrs.insert(ptr, node); + } else if (reader->name() == QStringLiteral("label")) { + node->SetLabel(reader->readElementText()); + } else if (reader->name() == QStringLiteral("uuid")) { + xml_node_data.node_uuids.insert(node, QUuid::fromString(reader->readElementText())); + } else if (reader->name() == QStringLiteral("color")) { + node->SetOverrideColor(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("links")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("link")) { + xml_node_data.block_links.append({node, reader->readElementText().toULongLong()}); + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("custom")) { + LoadNodeCustom(reader, node, xml_node_data); + } else if (reader->name() == QStringLiteral("connections")) { + // Load connections + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("connection")) { + QString param_id; + int ele = -1; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("element")) { + ele = attr.value().toInt(); + } else if (attr.name() == QStringLiteral("input")) { + param_id = attr.value().toString(); + } + } + + QString output_node_id; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("output")) { + output_node_id = reader->readElementText(); + } else { + reader->skipCurrentElement(); + } + } + + xml_node_data.desired_connections.append({NodeInput(node, param_id, ele), output_node_id.toULongLong()}); + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("hints")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("hint")) { + QString input; + int element = -1; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("input")) { + input = attr.value().toString(); + } else if (attr.name() == QStringLiteral("element")) { + element = attr.value().toInt(); + } + } + + Node::ValueHint vh; + LoadValueHint(&vh, reader); + node->SetValueHintForInput(input, vh, element); + } else { + reader->skipCurrentElement(); + } + } + } else { + reader->skipCurrentElement(); + } + } +} + +void ProjectSerializer220403::SaveNode(Node *node, QXmlStreamWriter *writer) const +{ + writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast(node))); + + writer->writeTextElement(QStringLiteral("label"), node->GetLabel()); + writer->writeTextElement(QStringLiteral("color"), QString::number(node->GetOverrideColor())); + + foreach (const QString& input, node->inputs()) { + writer->writeStartElement(QStringLiteral("input")); + + SaveInput(node, writer, input); + + writer->writeEndElement(); // input + } + + writer->writeStartElement(QStringLiteral("links")); + foreach (Node* link, node->links()) { + writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast(link))); + } + writer->writeEndElement(); // links + + writer->writeStartElement(QStringLiteral("connections")); + for (auto it=node->input_connections().cbegin(); it!=node->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->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast(it->second))); + + writer->writeEndElement(); // connection + } + writer->writeEndElement(); // connections + + writer->writeStartElement(QStringLiteral("hints")); + for (auto it=node->GetValueHints().cbegin(); it!=node->GetValueHints().cend(); it++) { + writer->writeStartElement(QStringLiteral("hint")); + + writer->writeAttribute(QStringLiteral("input"), it.key().input); + writer->writeAttribute(QStringLiteral("element"), QString::number(it.key().element)); + + SaveValueHint(&it.value(), writer); + + writer->writeEndElement(); // hint + } + writer->writeEndElement(); + + writer->writeStartElement(QStringLiteral("custom")); + + SaveNodeCustom(writer, node); + + writer->writeEndElement(); // custom +} + +void ProjectSerializer220403::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const +{ + if (dynamic_cast(node)) { + // Ignore input of group + reader->skipCurrentElement(); + return; + } + + QString param_id; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + param_id = attr.value().toString(); + + break; + } + } + + if (param_id.isEmpty()) { + qWarning() << "Failed to load parameter with missing ID"; + reader->skipCurrentElement(); + return; + } + + if (!node->HasInputWithID(param_id)) { + qWarning() << "Failed to load parameter that didn't exist:" << param_id; + reader->skipCurrentElement(); + return; + } + + while (XMLReadNextStartElement(reader)) { + if (IsCancelled()) { + return; + } + + if (reader->name() == QStringLiteral("primary")) { + // Load primary immediate + LoadImmediate(reader, node, param_id, -1, xml_node_data); + } else if (reader->name() == QStringLiteral("subelements")) { + // Load subelements + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("count")) { + node->InputArrayResize(param_id, attr.value().toInt()); + } + } + + int element_counter = 0; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("element")) { + LoadImmediate(reader, node, param_id, element_counter, xml_node_data); + + element_counter++; + } else { + reader->skipCurrentElement(); + } + } + } else { + reader->skipCurrentElement(); + } + } +} + +void ProjectSerializer220403::SaveInput(Node *node, QXmlStreamWriter *writer, const QString &id) const +{ + writer->writeAttribute(QStringLiteral("id"), id); + + writer->writeStartElement(QStringLiteral("primary")); + + SaveImmediate(writer, node, id, -1); + + writer->writeEndElement(); // primary + + writer->writeStartElement(QStringLiteral("subelements")); + + int arr_sz = node->InputArraySize(id); + + writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz)); + + for (int i=0; iwriteStartElement(QStringLiteral("element")); + + SaveImmediate(writer, node, id, i); + + writer->writeEndElement(); // element + } + + writer->writeEndElement(); // subelements +} + +void ProjectSerializer220403::LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData &xml_node_data) const +{ + Q_UNUSED(xml_node_data) + + NodeValue::Type data_type = node->GetInputDataType(input); + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("standard")) { + // Load standard value + int val_index = 0; + + while (XMLReadNextStartElement(reader)) { + if (IsCancelled()) { + return; + } + + if (reader->name() == QStringLiteral("track")) { + QVariant value_on_track; + + if (data_type == NodeValue::kVideoParams) { + VideoParams vp; + vp.Load(reader); + value_on_track = QVariant::fromValue(vp); + } else if (data_type == NodeValue::kAudioParams) { + AudioParams ap; + ap.Load(reader); + value_on_track = QVariant::fromValue(ap); + } else { + QString value_text = reader->readElementText(); + + if (!value_text.isEmpty()) { + value_on_track = NodeValue::StringToValue(data_type, value_text, true); + } + } + + node->SetSplitStandardValueOnTrack(input, val_index, value_on_track, element); + + val_index++; + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("keyframing") && node->IsInputKeyframable(input)) { + node->SetInputIsKeyframing(input, reader->readElementText().toInt(), element); + } else if (reader->name() == QStringLiteral("keyframes")) { + int track = 0; + + while (XMLReadNextStartElement(reader)) { + if (IsCancelled()) { + return; + } + + if (reader->name() == QStringLiteral("track")) { + while (XMLReadNextStartElement(reader)) { + if (IsCancelled()) { + return; + } + + if (reader->name() == QStringLiteral("key")) { + QString key_input; + rational key_time; + NodeKeyframe::Type key_type = NodeKeyframe::kLinear; + QVariant key_value; + QPointF key_in_handle; + QPointF key_out_handle; + + XMLAttributeLoop(reader, attr) { + if (IsCancelled()) { + return; + } + + if (attr.name() == QStringLiteral("input")) { + key_input = attr.value().toString(); + } else if (attr.name() == QStringLiteral("time")) { + key_time = rational::fromString(attr.value().toString()); + } else if (attr.name() == QStringLiteral("type")) { + key_type = static_cast(attr.value().toInt()); + } else if (attr.name() == QStringLiteral("inhandlex")) { + key_in_handle.setX(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("inhandley")) { + key_in_handle.setY(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("outhandlex")) { + key_out_handle.setX(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("outhandley")) { + key_out_handle.setY(attr.value().toDouble()); + } + } + + key_value = NodeValue::StringToValue(data_type, reader->readElementText(), true); + + NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, key_input, node); + key->set_bezier_control_in(key_in_handle); + key->set_bezier_control_out(key_out_handle); + } else { + reader->skipCurrentElement(); + } + } + + track++; + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("csinput")) { + node->SetInputProperty(input, QStringLiteral("col_input"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("csdisplay")) { + node->SetInputProperty(input, QStringLiteral("col_display"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("csview")) { + node->SetInputProperty(input, QStringLiteral("col_view"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("cslook")) { + node->SetInputProperty(input, QStringLiteral("col_look"), reader->readElementText()); + } else { + reader->skipCurrentElement(); + } + } +} + +void ProjectSerializer220403::SaveImmediate(QXmlStreamWriter *writer, Node *node, const QString& input, int element) const +{ + if (node->IsInputKeyframable(input)) { + writer->writeTextElement(QStringLiteral("keyframing"), QString::number(node->IsInputKeyframing(input, element))); + } + + NodeValue::Type data_type = node->GetInputDataType(input); + + // Write standard value + writer->writeStartElement(QStringLiteral("standard")); + + foreach (const QVariant& v, node->GetSplitStandardValue(input, element)) { + writer->writeStartElement(QStringLiteral("track")); + + if (data_type == NodeValue::kVideoParams) { + v.value().Save(writer); + } else if (data_type == NodeValue::kAudioParams) { + v.value().Save(writer); + } else { + writer->writeCharacters(NodeValue::ValueToString(data_type, v, true)); + } + + writer->writeEndElement(); // track + } + + writer->writeEndElement(); // standard + + // Write keyframes + writer->writeStartElement(QStringLiteral("keyframes")); + + for (const NodeKeyframeTrack& track : node->GetKeyframeTracks(input, element)) { + writer->writeStartElement(QStringLiteral("track")); + + for (NodeKeyframe* key : track) { + writer->writeStartElement(QStringLiteral("key")); + + writer->writeAttribute(QStringLiteral("input"), key->input()); + writer->writeAttribute(QStringLiteral("time"), key->time().toString()); + writer->writeAttribute(QStringLiteral("type"), QString::number(key->type())); + writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(key->bezier_control_in().x())); + writer->writeAttribute(QStringLiteral("inhandley"), QString::number(key->bezier_control_in().y())); + writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(key->bezier_control_out().x())); + writer->writeAttribute(QStringLiteral("outhandley"), QString::number(key->bezier_control_out().y())); + + writer->writeCharacters(NodeValue::ValueToString(data_type, key->value(), true)); + + writer->writeEndElement(); // key + } + + writer->writeEndElement(); // track + } + + writer->writeEndElement(); // keyframes + + if (data_type == NodeValue::kColor) { + // Save color management information + writer->writeTextElement(QStringLiteral("csinput"), node->GetInputProperty(input, QStringLiteral("col_input")).toString()); + writer->writeTextElement(QStringLiteral("csdisplay"), node->GetInputProperty(input, QStringLiteral("col_display")).toString()); + writer->writeTextElement(QStringLiteral("csview"), node->GetInputProperty(input, QStringLiteral("col_view")).toString()); + writer->writeTextElement(QStringLiteral("cslook"), node->GetInputProperty(input, QStringLiteral("col_look")).toString()); + } +} + +bool ProjectSerializer220403::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const +{ + bool got_node_ptr = false; + bool got_pos_x = false; + bool got_pos_y = false; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("ptr")) { + *node_ptr = attr.value().toULongLong(); + got_node_ptr = true; + break; + } + } + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("x")) { + pos->position.setX(reader->readElementText().toDouble()); + got_pos_x = true; + } else if (reader->name() == QStringLiteral("y")) { + pos->position.setY(reader->readElementText().toDouble()); + got_pos_y = true; + } else if (reader->name() == QStringLiteral("expanded")) { + pos->expanded = reader->readElementText().toInt(); + } else { + reader->skipCurrentElement(); + } + } + + return got_node_ptr && got_pos_x && got_pos_y; +} + +void ProjectSerializer220403::SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const +{ + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(node))); + + 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)); +} + +void ProjectSerializer220403::PostConnect(const XMLNodeData &xml_node_data) const +{ + foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { + if (Node *out = xml_node_data.node_ptrs.value(con.output_node)) { + Node::ConnectEdge(out, con.input); + } + } + + foreach (const XMLNodeData::BlockLink& l, xml_node_data.block_links) { + Node *a = l.block; + Node *b = xml_node_data.node_ptrs.value(l.link); + + Node::Link(a, b); + } + + foreach (const XMLNodeData::GroupLink &l, xml_node_data.group_input_links) { + if (Node *input_node = xml_node_data.node_ptrs.value(l.input_node)) { + NodeInput resolved(input_node, l.input_id, l.input_element); + + l.group->AddInputPassthrough(resolved, l.custom_flags, l.passthrough_id); + + if (!l.custom_name.isEmpty()) { + l.group->SetInputName(l.passthrough_id, l.custom_name); + } + + l.group->SetInputDataType(l.passthrough_id, l.data_type); + + l.group->SetDefaultValue(l.passthrough_id, l.default_val); + + for (auto it=l.custom_properties.cbegin(); it!=l.custom_properties.cend(); it++) { + l.group->SetInputProperty(l.passthrough_id, it.key(), it.value()); + } + } + } + + for (auto it=xml_node_data.group_output_links.cbegin(); it!=xml_node_data.group_output_links.cend(); it++) { + if (Node *output_node = xml_node_data.node_ptrs.value(it.value())) { + it.key()->SetOutputPassthrough(output_node); + } + } +} + +void ProjectSerializer220403::LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const +{ + // Viewer-based nodes + if (ViewerOutput *viewer = dynamic_cast(node)) { + + Footage *footage = dynamic_cast(node); + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("points")) { + LoadTimelinePoints(reader, viewer->GetTimelinePoints()); + } else if (reader->name() == QStringLiteral("timestamp") && footage) { + footage->set_timestamp(reader->readElementText().toLongLong()); + } else { + reader->skipCurrentElement(); + } + } + + } else if (Track *track = dynamic_cast(node)) { + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("height")) { + track->SetTrackHeight(reader->readElementText().toDouble()); + } else { + reader->skipCurrentElement(); + } + } + + } else if (NodeGroup *group = dynamic_cast(node)) { + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("inputpassthroughs")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("inputpassthrough")) { + XMLNodeData::GroupLink link; + + link.group = group; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + link.input_node = reader->readElementText().toULongLong(); + } else if (reader->name() == QStringLiteral("input")) { + link.input_id = reader->readElementText(); + } else if (reader->name() == QStringLiteral("element")) { + link.input_element = reader->readElementText().toInt(); + } else if (reader->name() == QStringLiteral("id")) { + link.passthrough_id = reader->readElementText(); + } else if (reader->name() == QStringLiteral("name")) { + link.custom_name = reader->readElementText(); + } else if (reader->name() == QStringLiteral("flags")) { + link.custom_flags = InputFlags(reader->readElementText().toULongLong()); + } else if (reader->name() == QStringLiteral("type")) { + link.data_type = NodeValue::GetDataTypeFromName(reader->readElementText()); + } else if (reader->name() == QStringLiteral("default")) { + link.default_val = NodeValue::StringToValue(link.data_type, reader->readElementText(), false); + } else if (reader->name() == QStringLiteral("properties")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("property")) { + QString key; + QString value; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("key")) { + key = reader->readElementText(); + } else if (reader->name() == QStringLiteral("value")) { + value = reader->readElementText(); + } else { + reader->skipCurrentElement(); + } + } + + if (!key.isEmpty()) { + link.custom_properties.insert(key, value); + } + } else { + reader->skipCurrentElement(); + } + } + } else { + reader->skipCurrentElement(); + } + } + + xml_node_data.group_input_links.append(link); + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("outputpassthrough")) { + xml_node_data.group_output_links.insert(group, reader->readElementText().toULongLong()); + } else { + reader->skipCurrentElement(); + } + } + + } else { + reader->skipCurrentElement(); + } +} + +void ProjectSerializer220403::SaveNodeCustom(QXmlStreamWriter *writer, Node *node) const +{ + if (ViewerOutput *viewer = dynamic_cast(node)) { + // Write TimelinePoints + writer->writeStartElement(QStringLiteral("points")); + SaveTimelinePoints(writer, viewer->GetTimelinePoints()); + writer->writeEndElement(); // points + + if (Footage *footage = dynamic_cast(node)) { + writer->writeTextElement(QStringLiteral("timestamp"), QString::number(footage->timestamp())); + } + } else if (Track *track = dynamic_cast(node)) { + writer->writeTextElement(QStringLiteral("height"), QString::number(track->GetTrackHeight())); + } else if (NodeGroup *group = dynamic_cast(node)) { + writer->writeStartElement(QStringLiteral("inputpassthroughs")); + + foreach (const NodeGroup::InputPassthrough &ip, group->GetInputPassthroughs()) { + writer->writeStartElement(QStringLiteral("inputpassthrough")); + + // Reference to inner input + writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast(ip.second.node()))); + writer->writeTextElement(QStringLiteral("input"), ip.second.input()); + writer->writeTextElement(QStringLiteral("element"), QString::number(ip.second.element())); + + // ID of passthrough + writer->writeTextElement(QStringLiteral("id"), ip.first); + + // Passthrough-specific details + NodeInput group_input(group, ip.first, ip.second.element()); + writer->writeTextElement(QStringLiteral("name"), group->Node::GetInputName(ip.first)); + + writer->writeTextElement(QStringLiteral("flags"), QString::number((group_input.GetFlags() & ~ip.second.GetFlags()).value())); + + NodeValue::Type data_type = group_input.GetDataType(); + writer->writeTextElement(QStringLiteral("type"), NodeValue::GetDataTypeName(data_type)); + + writer->writeTextElement(QStringLiteral("default"), NodeValue::ValueToString(data_type, group_input.GetDefaultValue(), false)); + + writer->writeStartElement(QStringLiteral("properties")); + auto p = group_input.GetProperties(); + for (auto it=p.cbegin(); it!=p.cend(); it++) { + writer->writeStartElement(QStringLiteral("property")); + writer->writeTextElement(QStringLiteral("key"), it.key()); + writer->writeTextElement(QStringLiteral("value"), it.value().toString()); + writer->writeEndElement(); // property + } + writer->writeEndElement(); // properties + + writer->writeEndElement(); // input + } + + writer->writeEndElement(); // inputpassthroughs + + writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast(group->GetOutputPassthrough()))); + } +} + +void ProjectSerializer220403::LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const +{ + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("markers")) { + LoadMarkerList(reader, points->markers()); + } else if (reader->name() == QStringLiteral("workarea")) { + LoadWorkArea(reader, points->workarea()); + } else { + reader->skipCurrentElement(); + } + } +} + +void ProjectSerializer220403::SaveTimelinePoints(QXmlStreamWriter *writer, TimelinePoints *points) const +{ + writer->writeStartElement(QStringLiteral("workarea")); + SaveWorkArea(writer, points->workarea()); + writer->writeEndElement(); // workarea + + writer->writeStartElement(QStringLiteral("markers")); + SaveMarkerList(writer, points->markers()); + writer->writeEndElement(); // markers +} + +void ProjectSerializer220403::LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const +{ + rational range_in = workarea->in(); + rational range_out = workarea->out(); + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("enabled")) { + workarea->set_enabled(attr.value() != QStringLiteral("0")); + } else if (attr.name() == QStringLiteral("in")) { + range_in = rational::fromString(attr.value().toString()); + } else if (attr.name() == QStringLiteral("out")) { + range_out = rational::fromString(attr.value().toString()); + } + } + + TimeRange loaded_workarea(range_in, range_out); + + if (loaded_workarea != workarea->range()) { + workarea->set_range(loaded_workarea); + } + + reader->skipCurrentElement(); +} + +void ProjectSerializer220403::SaveWorkArea(QXmlStreamWriter *writer, TimelineWorkArea *workarea) const +{ + writer->writeAttribute(QStringLiteral("enabled"), QString::number(workarea->enabled())); + writer->writeAttribute(QStringLiteral("in"), workarea->in().toString()); + writer->writeAttribute(QStringLiteral("out"), workarea->out().toString()); +} + +void ProjectSerializer220403::LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const +{ + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("marker")) { + QString name; + rational in, out; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("name")) { + name = attr.value().toString(); + } else if (attr.name() == QStringLiteral("in")) { + in = rational::fromString(attr.value().toString()); + } else if (attr.name() == QStringLiteral("out")) { + out = rational::fromString(attr.value().toString()); + } + } + + markers->AddMarker(TimeRange(in, out), name); + } + + reader->skipCurrentElement(); + } +} + +void ProjectSerializer220403::SaveMarkerList(QXmlStreamWriter *writer, TimelineMarkerList *markers) const +{ + foreach (TimelineMarker* marker, markers->list()) { + writer->writeStartElement(QStringLiteral("marker")); + + writer->writeAttribute(QStringLiteral("name"), marker->name()); + + writer->writeAttribute(QStringLiteral("in"), marker->time().in().toString()); + writer->writeAttribute(QStringLiteral("out"), marker->time().out().toString()); + + writer->writeEndElement(); // marker + } +} + +void ProjectSerializer220403::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const +{ + QVector types; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("types")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("type")) { + types.append(static_cast(reader->readElementText().toInt())); + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("index")) { + hint->set_index(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("tag")) { + hint->set_tag(reader->readElementText()); + } else { + reader->skipCurrentElement(); + } + } + + hint->set_type(types); +} + +void ProjectSerializer220403::SaveValueHint(const Node::ValueHint *hint, QXmlStreamWriter *writer) const +{ + writer->writeStartElement(QStringLiteral("types")); + + for (auto it=hint->types().cbegin(); it!=hint->types().cend(); it++) { + writer->writeTextElement(QStringLiteral("type"), QString::number(*it)); + } + + writer->writeEndElement(); // types + + writer->writeTextElement(QStringLiteral("index"), QString::number(hint->index())); + + writer->writeTextElement(QStringLiteral("tag"), hint->tag()); +} + +} diff --git a/app/node/project/serializer/serializer220403.h b/app/node/project/serializer/serializer220403.h new file mode 100644 index 000000000..f6a1b939f --- /dev/null +++ b/app/node/project/serializer/serializer220403.h @@ -0,0 +1,119 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef SERIALIZER220403_H +#define SERIALIZER220403_H + +#include "serializer.h" + +namespace olive { + +class ProjectSerializer220403 : public ProjectSerializer +{ +public: + ProjectSerializer220403() = default; + +protected: + virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override; + + virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const override; + + virtual uint Version() const override + { + return 220403; + } + +private: + struct XMLNodeData { + struct SerializedConnection { + NodeInput input; + quintptr output_node; + }; + + struct BlockLink { + Node* block; + quintptr link; + }; + + struct GroupLink { + NodeGroup *group; + QString passthrough_id; + quintptr input_node; + QString input_id; + int input_element; + QString custom_name; + InputFlags custom_flags; + NodeValue::Type data_type; + QVariant default_val; + QHash custom_properties; + }; + + QHash node_ptrs; + QList desired_connections; + QList block_links; + QVector group_input_links; + QHash group_output_links; + QHash node_uuids; + + }; + + void LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const; + + void SaveNode(Node *node, QXmlStreamWriter *writer) const; + + void LoadInput(Node *node, QXmlStreamReader* reader, XMLNodeData &xml_node_data) const; + + void SaveInput(Node *node, QXmlStreamWriter* writer, const QString& id) const; + + void LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData& xml_node_data) const; + + void SaveImmediate(QXmlStreamWriter *writer, Node *node, const QString &input, int element) const; + + bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const; + + void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const; + + void PostConnect(const XMLNodeData &xml_node_data) const; + + void LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const; + + void SaveNodeCustom(QXmlStreamWriter *writer, Node *node) const; + + void LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const; + + void SaveTimelinePoints(QXmlStreamWriter *writer, TimelinePoints *points) const; + + void LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const; + + void SaveWorkArea(QXmlStreamWriter *writer, TimelineWorkArea *workarea) const; + + void LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const; + + void SaveMarkerList(QXmlStreamWriter *writer, TimelineMarkerList *markers) const; + + void LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const; + + void SaveValueHint(const Node::ValueHint *hint, QXmlStreamWriter *writer) const; + +}; + +} + +#endif // SERIALIZER220403_H diff --git a/app/node/value.cpp b/app/node/value.cpp index 3cc567a92..d3fa7373b 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -141,6 +141,7 @@ QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value) case kShaderJob: case kSampleJob: case kGenerateJob: + case kDataTypeCount: break; } @@ -357,12 +358,78 @@ QString NodeValue::GetPrettyDataTypeName(Type type) case kShaderJob: case kSampleJob: case kGenerateJob: + case kDataTypeCount: break; } return QCoreApplication::translate("NodeValue", "Unknown"); } +QString NodeValue::GetDataTypeName(Type type) +{ + switch (type) { + case kNone: + return QStringLiteral("none"); + case kInt: + return QStringLiteral("int"); + case kCombo: + return QStringLiteral("combo"); + case kFloat: + return QStringLiteral("float"); + case kRational: + return QStringLiteral("rational"); + case kBoolean: + return QStringLiteral("bool"); + case kColor: + return QStringLiteral("color"); + case kMatrix: + return QStringLiteral("matrix"); + case kText: + return QStringLiteral("text"); + case kFont: + return QStringLiteral("font"); + case kFile: + return QStringLiteral("file"); + case kTexture: + return QStringLiteral("texture"); + case kSamples: + return QStringLiteral("samples"); + case kVec2: + return QStringLiteral("vec2"); + case kVec3: + return QStringLiteral("vec3"); + case kVec4: + return QStringLiteral("vec4"); + case kBezier: + return QStringLiteral("bezier"); + case kVideoParams: + return QStringLiteral("vparam"); + case kAudioParams: + return QStringLiteral("aparam"); + case kFootageJob: + case kShaderJob: + case kSampleJob: + case kGenerateJob: + case kDataTypeCount: + break; + } + + return QString(); +} + +NodeValue::Type NodeValue::GetDataTypeFromName(const QString &n) +{ + // Slow but easy to maintain + for (int i=0; i(i); + if (GetDataTypeName(t) == n) { + return t; + } + } + + return NodeValue::kNone; +} + NodeValue NodeValueTable::GetWithMeta(const QVector &type, const QString &tag) const { int value_index = GetValueIndex(type, tag); diff --git a/app/node/value.h b/app/node/value.h index 518517b78..12fb91a33 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -206,7 +206,12 @@ public: * take place. This value will usually be taken from a table and a kSamples value will be * pushed to take its place. */ - kGenerateJob + kGenerateJob, + + /** + * End of list + */ + kDataTypeCount }; static const QVector kNumber; @@ -261,6 +266,9 @@ public: static QString GetPrettyDataTypeName(Type type); + static QString GetDataTypeName(Type type); + static NodeValue::Type GetDataTypeFromName(const QString &n); + static QString ValueToString(Type data_type, const QVariant& value, bool value_is_a_key_track); static QVariant StringToValue(Type data_type, const QString &string, bool value_is_a_key_track); diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index 6464580cf..810b022e6 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -523,6 +523,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video case NodeValue::kFootageJob: case NodeValue::kBezier: case NodeValue::kNone: + case NodeValue::kDataTypeCount: break; } } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index feca63029..29c35657b 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -92,6 +92,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeValue::kGenerateJob: case NodeValue::kVideoParams: case NodeValue::kAudioParams: + case NodeValue::kDataTypeCount: break; case NodeValue::kInt: { @@ -245,6 +246,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() case NodeValue::kGenerateJob: case NodeValue::kVideoParams: case NodeValue::kAudioParams: + case NodeValue::kDataTypeCount: break; case NodeValue::kInt: { @@ -421,6 +423,7 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() case NodeValue::kGenerateJob: case NodeValue::kVideoParams: case NodeValue::kAudioParams: + case NodeValue::kDataTypeCount: break; case NodeValue::kInt: { diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 92b8f0d03..2c1ffb3e5 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -261,7 +261,7 @@ void NodeView::Duplicate() for (auto it=src_group->GetInputPassthroughs().cbegin(); it!=src_group->GetInputPassthroughs().cend(); it++) { NodeInput input = it->second; input.set_node(new_nodes.at(selected.indexOf(input.node()))); - dst_group->AddInputPassthrough(input); + dst_group->AddInputPassthrough(input, InputFlags(), it->first); } dst_group->SetOutputPassthrough(new_nodes.at(selected.indexOf(src_group->GetOutputPassthrough())));