moved serialization
This commit is contained in:
@@ -25,12 +25,13 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
bool XMLReadNextStartElement(QXmlStreamReader *reader)
|
||||
bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom)
|
||||
{
|
||||
QXmlStreamReader::TokenType token;
|
||||
|
||||
while ((token = reader->readNext()) != QXmlStreamReader::Invalid
|
||||
&& token != QXmlStreamReader::EndDocument) {
|
||||
&& token != QXmlStreamReader::EndDocument
|
||||
&& (!cancel_atom || !cancel_atom->IsCancelled())) {
|
||||
if (reader->isEndElement()) {
|
||||
return false;
|
||||
} else if (reader->isStartElement()) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "node/param.h"
|
||||
#include "render/cancelatom.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -45,7 +46,7 @@ class NodeGroup;
|
||||
*
|
||||
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
|
||||
*/
|
||||
bool XMLReadNextStartElement(QXmlStreamReader* reader);
|
||||
bool XMLReadNextStartElement(QXmlStreamReader* reader, CancelAtom *cancel_atom = nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -492,7 +492,7 @@ bool Core::AddOpenProjectFromTask(Task *task, bool add_to_recents)
|
||||
|
||||
if (ValidateFootageInLoadedProject(project, project->GetSavedURL())) {
|
||||
AddOpenProject(project, add_to_recents);
|
||||
main_window_->LoadLayout(project->GetLayoutInfo());
|
||||
main_window_->LoadLayout(load_task->GetLoadedLayout());
|
||||
|
||||
return true;
|
||||
} else {
|
||||
@@ -801,9 +801,6 @@ void Core::SaveProjectInternal(const QString& override_filename)
|
||||
// Create save manager
|
||||
Task* psm;
|
||||
|
||||
// Put layout into project
|
||||
open_project_->SetLayoutInfo(main_window_->SaveLayout());
|
||||
|
||||
if (open_project_->filename().endsWith(QStringLiteral(".otio"), Qt::CaseInsensitive)) {
|
||||
#ifdef USE_OTIO
|
||||
psm = new SaveOTIOTask(open_project_);
|
||||
@@ -817,6 +814,7 @@ void Core::SaveProjectInternal(const QString& override_filename)
|
||||
} else {
|
||||
bool use_compression = !open_project_->filename().endsWith(QStringLiteral(".ovexml"), Qt::CaseInsensitive);
|
||||
psm = new ProjectSaveTask(open_project_, use_compression);
|
||||
static_cast<ProjectSaveTask*>(psm)->SetLayout(main_window_->SaveLayout());
|
||||
|
||||
if (!override_filename.isEmpty()) {
|
||||
// Set override filename if provided
|
||||
|
||||
+148
-1
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "group.h"
|
||||
|
||||
#include "node/project.h"
|
||||
#include "node/serializeddata.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -61,6 +61,153 @@ void NodeGroup::Retranslate()
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeGroup::LoadCustom(QXmlStreamReader *reader, SerializedData *data)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("inputpassthroughs")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("inputpassthrough")) {
|
||||
SerializedData::GroupLink link;
|
||||
|
||||
link.group = this;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
data->group_input_links.append(link);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("outputpassthrough")) {
|
||||
data->group_output_links.insert(this, reader->readElementText().toULongLong());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NodeGroup::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
|
||||
|
||||
foreach (const NodeGroup::InputPassthrough &ip, this->GetInputPassthroughs()) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthrough"));
|
||||
|
||||
// Reference to inner input
|
||||
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(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
|
||||
const QString &input = ip.first;
|
||||
writer->writeTextElement(QStringLiteral("name"), this->Node::GetInputName(input));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("flags"), QString::number((GetInputFlags(input) & ~ip.second.GetFlags()).value()));
|
||||
|
||||
NodeValue::Type data_type = GetInputDataType(input);
|
||||
writer->writeTextElement(QStringLiteral("type"), NodeValue::GetDataTypeName(data_type));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("default"), NodeValue::ValueToString(data_type, GetDefaultValue(input), false));
|
||||
|
||||
writer->writeStartElement(QStringLiteral("properties"));
|
||||
auto p = GetInputProperties(input);
|
||||
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<quintptr>(this->GetOutputPassthrough())));
|
||||
}
|
||||
|
||||
void NodeGroup::PostLoadEvent(SerializedData *data)
|
||||
{
|
||||
super::PostLoadEvent(data);
|
||||
|
||||
foreach (const SerializedData::GroupLink &l, data->group_input_links) {
|
||||
if (Node *input_node = data->node_ptrs.value(l.input_node)) {
|
||||
NodeInput resolved(input_node, l.input_id, l.input_element);
|
||||
|
||||
l.group->AddInputPassthrough(resolved, l.passthrough_id);
|
||||
|
||||
l.group->SetInputFlag(l.passthrough_id, InputFlag(l.custom_flags.value()));
|
||||
|
||||
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=data->group_output_links.cbegin(); it!=data->group_output_links.cend(); it++) {
|
||||
if (Node *output_node = data->node_ptrs.value(it.value())) {
|
||||
it.key()->SetOutputPassthrough(output_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString NodeGroup::AddInputPassthrough(const NodeInput &input, const QString &force_id)
|
||||
{
|
||||
Q_ASSERT(ContextContainsNode(input.node()));
|
||||
|
||||
@@ -40,6 +40,10 @@ public:
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader *reader, SerializedData *data) override;
|
||||
virtual void SaveCustom(QXmlStreamWriter *writer) const override;
|
||||
virtual void PostLoadEvent(SerializedData *data) override;
|
||||
|
||||
QString AddInputPassthrough(const NodeInput &input, const QString &force_id = QString());
|
||||
|
||||
void RemoveInputPassthrough(const NodeInput &input);
|
||||
|
||||
@@ -210,4 +210,49 @@ bool NodeKeyframe::has_sibling_at_time(const rational &t) const
|
||||
return k && k != this;
|
||||
}
|
||||
|
||||
bool NodeKeyframe::load(QXmlStreamReader *reader, NodeValue::Type data_type)
|
||||
{
|
||||
QString key_input;
|
||||
QPointF key_in_handle;
|
||||
QPointF key_out_handle;
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("input")) {
|
||||
key_input = attr.value().toString();
|
||||
} else if (attr.name() == QStringLiteral("time")) {
|
||||
this->set_time(rational::fromString(attr.value().toString().toStdString()));
|
||||
} else if (attr.name() == QStringLiteral("type")) {
|
||||
this->set_type_no_bezier_adj(static_cast<NodeKeyframe::Type>(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());
|
||||
}
|
||||
}
|
||||
|
||||
this->set_value(NodeValue::StringToValue(data_type, reader->readElementText(), true));
|
||||
|
||||
this->set_bezier_control_in(key_in_handle);
|
||||
this->set_bezier_control_out(key_out_handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NodeKeyframe::save(QXmlStreamWriter *writer, NodeValue::Type data_type) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("input"), this->input());
|
||||
writer->writeAttribute(QStringLiteral("time"), QString::fromStdString(this->time().toString()));
|
||||
writer->writeAttribute(QStringLiteral("type"), QString::number(this->type()));
|
||||
writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(this->bezier_control_in().x()));
|
||||
writer->writeAttribute(QStringLiteral("inhandley"), QString::number(this->bezier_control_in().y()));
|
||||
writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(this->bezier_control_out().x()));
|
||||
writer->writeAttribute(QStringLiteral("outhandley"), QString::number(this->bezier_control_out().y()));
|
||||
|
||||
writer->writeCharacters(NodeValue::ValueToString(data_type, this->value(), true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -165,6 +165,9 @@ public:
|
||||
|
||||
bool has_sibling_at_time(const rational &t) const;
|
||||
|
||||
bool load(QXmlStreamReader *reader, NodeValue::Type data_type);
|
||||
void save(QXmlStreamWriter *writer, NodeValue::Type data_type) const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when this keyframe's time is changed
|
||||
|
||||
@@ -29,8 +29,10 @@
|
||||
#include "core.h"
|
||||
#include "config/config.h"
|
||||
#include "node/group/group.h"
|
||||
#include "node/project/serializer/typeserializer.h"
|
||||
#include "nodeundo.h"
|
||||
#include "project.h"
|
||||
#include "serializeddata.h"
|
||||
#include "ui/colorcoding.h"
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
@@ -1181,6 +1183,463 @@ bool Node::AreLinked(Node *a, Node *b)
|
||||
return a->links_.contains(b);
|
||||
}
|
||||
|
||||
bool Node::Load(QXmlStreamReader *reader, SerializedData *data)
|
||||
{
|
||||
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")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("link")) {
|
||||
data->block_links.append({this, reader->readElementText().toULongLong()});
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("custom")) {
|
||||
if (!LoadCustom(reader, data)) {
|
||||
return false;
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
data->desired_connections.append({NodeInput(this, 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;
|
||||
if (!vh.load(reader)) {
|
||||
return false;
|
||||
}
|
||||
this->SetValueHintForInput(input, vh, element);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("context")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
quintptr node_ptr = 0;
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("ptr")) {
|
||||
node_ptr = attr.value().toULongLong();
|
||||
}
|
||||
}
|
||||
|
||||
if (node_ptr) {
|
||||
Node::Position node_pos;
|
||||
if (!node_pos.load(reader)) {
|
||||
return false;
|
||||
}
|
||||
data->positions[reinterpret_cast<quintptr>(this)].insert(node_ptr, node_pos);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("caches")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("audio")) {
|
||||
this->audio_playback_cache()->SetUuid(QUuid::fromString(reader->readElementText()));
|
||||
} else if (reader->name() == QStringLiteral("video")) {
|
||||
this->video_frame_cache()->SetUuid(QUuid::fromString(reader->readElementText()));
|
||||
} else if (reader->name() == QStringLiteral("thumb")) {
|
||||
this->thumbnail_cache()->SetUuid(QUuid::fromString(reader->readElementText()));
|
||||
} else if (reader->name() == QStringLiteral("waveform")) {
|
||||
this->waveform_cache()->SetUuid(QUuid::fromString(reader->readElementText()));
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
this->LoadFinishedEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Node::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("version"), QString::number(1));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("label"), this->GetLabel());
|
||||
writer->writeTextElement(QStringLiteral("color"), QString::number(this->GetOverrideColor()));
|
||||
|
||||
foreach (const QString& input, this->inputs()) {
|
||||
writer->writeStartElement(QStringLiteral("input"));
|
||||
|
||||
SaveInput(writer, input);
|
||||
|
||||
writer->writeEndElement(); // input
|
||||
}
|
||||
|
||||
writer->writeStartElement(QStringLiteral("links"));
|
||||
foreach (Node* link, this->links()) {
|
||||
writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast<quintptr>(link)));
|
||||
}
|
||||
writer->writeEndElement(); // links
|
||||
|
||||
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->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast<quintptr>(it->second)));
|
||||
|
||||
writer->writeEndElement(); // connection
|
||||
}
|
||||
writer->writeEndElement(); // connections
|
||||
|
||||
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));
|
||||
|
||||
it.value().save(writer);
|
||||
|
||||
writer->writeEndElement(); // hint
|
||||
}
|
||||
writer->writeEndElement(); // hints
|
||||
|
||||
writer->writeStartElement(QStringLiteral("context"));
|
||||
|
||||
const Node::PositionMap &map = this->GetContextPositions();
|
||||
|
||||
if (!map.isEmpty()) {
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
|
||||
jt.value().save(writer);
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // context
|
||||
|
||||
writer->writeStartElement(QStringLiteral("caches"));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("audio"), this->audio_playback_cache()->GetUuid().toString());
|
||||
writer->writeTextElement(QStringLiteral("video"), this->video_frame_cache()->GetUuid().toString());
|
||||
writer->writeTextElement(QStringLiteral("thumb"), this->thumbnail_cache()->GetUuid().toString());
|
||||
writer->writeTextElement(QStringLiteral("waveform"), this->waveform_cache()->GetUuid().toString());
|
||||
|
||||
writer->writeEndElement(); // caches
|
||||
|
||||
writer->writeStartElement(QStringLiteral("custom"));
|
||||
|
||||
SaveCustom(writer);
|
||||
|
||||
writer->writeEndElement(); // custom
|
||||
}
|
||||
|
||||
bool Node::LoadCustom(QXmlStreamReader *reader, SerializedData *data)
|
||||
{
|
||||
reader->skipCurrentElement();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Node::PostLoadEvent(SerializedData *data)
|
||||
{
|
||||
// Resolve positions
|
||||
quintptr this_ptr = data->node_ptrs.key(this);
|
||||
const QMap<quintptr, Node::Position> &positions = data->positions.value(this_ptr);
|
||||
|
||||
for (auto jt=positions.cbegin(); jt!=positions.cend(); jt++) {
|
||||
Node *n = data->node_ptrs.value(jt.key());
|
||||
if (n) {
|
||||
this->SetNodePositionInContext(n, jt.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Node::LoadInput(QXmlStreamReader *reader, SerializedData *data)
|
||||
{
|
||||
if (dynamic_cast<NodeGroup*>(this)) {
|
||||
// Ignore input of group
|
||||
reader->skipCurrentElement();
|
||||
return true;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
if (!this->HasInputWithID(param_id)) {
|
||||
qWarning() << "Failed to load parameter that didn't exist:" << param_id;
|
||||
reader->skipCurrentElement();
|
||||
return false;
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("primary")) {
|
||||
// Load primary immediate
|
||||
if (!LoadImmediate(reader, param_id, -1, data)) {
|
||||
return false;
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("subelements")) {
|
||||
// Load subelements
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("count")) {
|
||||
this->InputArrayResize(param_id, attr.value().toInt());
|
||||
}
|
||||
}
|
||||
|
||||
int element_counter = 0;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("element")) {
|
||||
if (!LoadImmediate(reader, param_id, element_counter, data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
element_counter++;
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("id"), id);
|
||||
|
||||
writer->writeStartElement(QStringLiteral("primary"));
|
||||
|
||||
SaveImmediate(writer, id, -1);
|
||||
|
||||
writer->writeEndElement(); // primary
|
||||
|
||||
writer->writeStartElement(QStringLiteral("subelements"));
|
||||
|
||||
int arr_sz = this->InputArraySize(id);
|
||||
|
||||
writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz));
|
||||
|
||||
for (int i=0; i<arr_sz; i++) {
|
||||
writer->writeStartElement(QStringLiteral("element"));
|
||||
|
||||
SaveImmediate(writer, id, i);
|
||||
|
||||
writer->writeEndElement(); // element
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // subelements
|
||||
}
|
||||
|
||||
bool Node::LoadImmediate(QXmlStreamReader *reader, const QString &input, int element, SerializedData *data)
|
||||
{
|
||||
NodeValue::Type data_type = this->GetInputDataType(input);
|
||||
|
||||
// HACK: SubtitleParams contain the actual subtitle data, so loading/replacing it will overwrite
|
||||
// the valid subtitles. We hack around it by simply skipping loading subtitles, we'll see
|
||||
// if this ends up being an issue in the future.
|
||||
if (data_type == NodeValue::kSubtitleParams) {
|
||||
reader->skipCurrentElement();
|
||||
return true;
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("standard")) {
|
||||
// Load standard value
|
||||
int val_index = 0;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
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 = TypeSerializer::LoadAudioParams(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);
|
||||
}
|
||||
}
|
||||
|
||||
this->SetSplitStandardValueOnTrack(input, val_index, value_on_track, element);
|
||||
|
||||
val_index++;
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("keyframing") && this->IsInputKeyframable(input)) {
|
||||
this->SetInputIsKeyframing(input, reader->readElementText().toInt(), element);
|
||||
} else if (reader->name() == QStringLiteral("keyframes")) {
|
||||
int track = 0;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("track")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("key")) {
|
||||
NodeKeyframe* key = new NodeKeyframe();
|
||||
key->set_input(input);
|
||||
key->set_element(element);
|
||||
key->set_track(track);
|
||||
|
||||
key->load(reader, data_type);
|
||||
key->setParent(this);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
track++;
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("csinput")) {
|
||||
this->SetInputProperty(input, QStringLiteral("col_input"), reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("csdisplay")) {
|
||||
this->SetInputProperty(input, QStringLiteral("col_display"), reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("csview")) {
|
||||
this->SetInputProperty(input, QStringLiteral("col_view"), reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("cslook")) {
|
||||
this->SetInputProperty(input, QStringLiteral("col_look"), reader->readElementText());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const
|
||||
{
|
||||
if (this->IsInputKeyframable(input)) {
|
||||
writer->writeTextElement(QStringLiteral("keyframing"), QString::number(this->IsInputKeyframing(input, element)));
|
||||
}
|
||||
|
||||
NodeValue::Type data_type = this->GetInputDataType(input);
|
||||
|
||||
// Write standard value
|
||||
writer->writeStartElement(QStringLiteral("standard"));
|
||||
|
||||
foreach (const QVariant& v, this->GetSplitStandardValue(input, element)) {
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
|
||||
if (data_type == NodeValue::kVideoParams) {
|
||||
v.value<VideoParams>().Save(writer);
|
||||
} else if (data_type == NodeValue::kAudioParams) {
|
||||
TypeSerializer::SaveAudioParams(writer, v.value<AudioParams>());
|
||||
} 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 : this->GetKeyframeTracks(input, element)) {
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
|
||||
for (NodeKeyframe* key : track) {
|
||||
writer->writeStartElement(QStringLiteral("key"));
|
||||
|
||||
key->save(writer, data_type);
|
||||
|
||||
writer->writeEndElement(); // key
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // keyframes
|
||||
|
||||
if (data_type == NodeValue::kColor) {
|
||||
// Save color management information
|
||||
writer->writeTextElement(QStringLiteral("csinput"), this->GetInputProperty(input, QStringLiteral("col_input")).toString());
|
||||
writer->writeTextElement(QStringLiteral("csdisplay"), this->GetInputProperty(input, QStringLiteral("col_display")).toString());
|
||||
writer->writeTextElement(QStringLiteral("csview"), this->GetInputProperty(input, QStringLiteral("col_view")).toString());
|
||||
writer->writeTextElement(QStringLiteral("cslook"), this->GetInputProperty(input, QStringLiteral("col_look")).toString());
|
||||
}
|
||||
}
|
||||
|
||||
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, InputFlags flags, int index)
|
||||
{
|
||||
if (id.isEmpty()) {
|
||||
@@ -1930,4 +2389,80 @@ std::list<NodeInput> Node::FindPath(Node *from, Node *to, int path_index)
|
||||
return v;
|
||||
}
|
||||
|
||||
bool Node::ValueHint::load(QXmlStreamReader *reader)
|
||||
{
|
||||
uint version = 0;
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
version = attr.value().toUInt();
|
||||
}
|
||||
|
||||
Q_UNUSED(version)
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("types")) {
|
||||
QVector<NodeValue::Type> types;
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("type")) {
|
||||
types.append(static_cast<NodeValue::Type>(reader->readElementText().toInt()));
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
this->set_type(types);
|
||||
} else if (reader->name() == QStringLiteral("index")) {
|
||||
this->set_index(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("tag")) {
|
||||
this->set_tag(reader->readElementText());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ValueHint::save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("version"), QString::number(1));
|
||||
|
||||
writer->writeStartElement(QStringLiteral("types"));
|
||||
|
||||
for (auto it=this->types().cbegin(); it!=this->types().cend(); it++) {
|
||||
writer->writeTextElement(QStringLiteral("type"), QString::number(*it));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // types
|
||||
|
||||
writer->writeTextElement(QStringLiteral("index"), QString::number(this->index()));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("tag"), this->tag());
|
||||
}
|
||||
|
||||
bool Node::Position::load(QXmlStreamReader *reader)
|
||||
{
|
||||
bool got_pos_x = false;
|
||||
bool got_pos_y = false;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("x")) {
|
||||
this->position.setX(reader->readElementText().toDouble());
|
||||
got_pos_x = true;
|
||||
} else if (reader->name() == QStringLiteral("y")) {
|
||||
this->position.setY(reader->readElementText().toDouble());
|
||||
got_pos_y = true;
|
||||
} else if (reader->name() == QStringLiteral("expanded")) {
|
||||
this->expanded = reader->readElementText().toInt();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return got_pos_x && got_pos_y;
|
||||
}
|
||||
|
||||
void Node::Position::save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("x"), QString::number(this->position.x()));
|
||||
writer->writeTextElement(QStringLiteral("y"), QString::number(this->position.y()));
|
||||
writer->writeTextElement(QStringLiteral("expanded"), QString::number(this->expanded));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-1
@@ -56,8 +56,9 @@ namespace olive {
|
||||
#define NODE_COPY_FUNCTION(x) \
|
||||
virtual Node *copy() const override {return new x();}
|
||||
|
||||
class Project;
|
||||
class Folder;
|
||||
class Project;
|
||||
class SerializedData;
|
||||
|
||||
/**
|
||||
* @brief A single processing unit that can be connected with others to create intricate processing systems
|
||||
@@ -288,6 +289,9 @@ public:
|
||||
expanded = e;
|
||||
}
|
||||
|
||||
bool load(QXmlStreamReader *reader);
|
||||
void save(QXmlStreamWriter *writer) const;
|
||||
|
||||
QPointF position;
|
||||
bool expanded;
|
||||
|
||||
@@ -682,6 +686,9 @@ public:
|
||||
void set_index(const int &index) { index_ = index; }
|
||||
void set_tag(const QString &tag) { tag_ = tag; }
|
||||
|
||||
bool load(QXmlStreamReader *reader);
|
||||
void save(QXmlStreamWriter *writer) const;
|
||||
|
||||
private:
|
||||
QVector<NodeValue::Type> type_;
|
||||
int index_;
|
||||
@@ -941,6 +948,19 @@ public:
|
||||
static bool Unlink(Node* a, Node* b);
|
||||
static bool AreLinked(Node* a, Node* b);
|
||||
|
||||
bool Load(QXmlStreamReader *reader, SerializedData *data);
|
||||
void Save(QXmlStreamWriter *writer) const;
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader *reader, SerializedData *data);
|
||||
virtual void SaveCustom(QXmlStreamWriter *writer) const {}
|
||||
virtual void PostLoadEvent(SerializedData *data);
|
||||
|
||||
bool LoadInput(QXmlStreamReader *reader, SerializedData *data);
|
||||
void SaveInput(QXmlStreamWriter *writer, const QString &id) const;
|
||||
|
||||
bool LoadImmediate(QXmlStreamReader *reader, const QString &input, int element, SerializedData *data);
|
||||
void SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const;
|
||||
|
||||
void SetFolder(Folder* folder)
|
||||
{
|
||||
folder_ = folder;
|
||||
|
||||
@@ -195,6 +195,24 @@ void Track::SetTrackHeight(const double &height)
|
||||
emit TrackHeightChanged(track_height_);
|
||||
}
|
||||
|
||||
bool Track::LoadCustom(QXmlStreamReader *reader, SerializedData *data)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("height")) {
|
||||
this->SetTrackHeight(reader->readElementText().toDouble());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Track::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("height"), QString::number(this->GetTrackHeight()));
|
||||
}
|
||||
|
||||
void Track::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
@@ -102,6 +102,9 @@ public:
|
||||
SetTrackHeight(PixelHeightToInternalHeight(h));
|
||||
}
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader *reader, SerializedData *data) override;
|
||||
virtual void SaveCustom(QXmlStreamWriter *writer) const override;
|
||||
|
||||
static int InternalHeightToPixelHeight(double h)
|
||||
{
|
||||
return qRound(h * QFontMetrics(QFont()).height());
|
||||
|
||||
@@ -430,6 +430,36 @@ void ViewerOutput::Value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewerOutput::LoadCustom(QXmlStreamReader *reader, SerializedData *data)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("markers")) {
|
||||
if (!this->GetMarkers()->load(reader)) {
|
||||
return false;
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("workarea")) {
|
||||
if (!this->GetWorkArea()->load(reader)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViewerOutput::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("workarea"));
|
||||
this->GetWorkArea()->save(writer);
|
||||
writer->writeEndElement(); // workarea
|
||||
|
||||
writer->writeStartElement(QStringLiteral("markers"));
|
||||
this->GetMarkers()->save(writer);
|
||||
writer->writeEndElement(); // markers
|
||||
}
|
||||
|
||||
void ViewerOutput::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
if (element == 0) {
|
||||
|
||||
@@ -191,6 +191,9 @@ public:
|
||||
const EncodingParams &GetLastUsedEncodingParams() const { return last_used_encoding_params_; }
|
||||
void SetLastUsedEncodingParams(const EncodingParams &p) { last_used_encoding_params_ = p; }
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader *reader, SerializedData *data) override;
|
||||
virtual void SaveCustom(QXmlStreamWriter *writer) const override;
|
||||
|
||||
static const QString kVideoParamsInput;
|
||||
static const QString kAudioParamsInput;
|
||||
static const QString kSubtitleParamsInput;
|
||||
|
||||
+126
-5
@@ -28,6 +28,7 @@
|
||||
#include "core.h"
|
||||
#include "dialog/progress/progress.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/serializeddata.h"
|
||||
#include "render/diskmanager.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
@@ -82,6 +83,131 @@ void Project::Clear()
|
||||
}
|
||||
}
|
||||
|
||||
void Project::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
SerializedData data;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("uuid")) {
|
||||
|
||||
this->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 = this->root();
|
||||
} else if (is_cm) {
|
||||
node = this->color_manager();
|
||||
} else if (is_settings) {
|
||||
node = this->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);
|
||||
|
||||
node->Load(reader, &data);
|
||||
|
||||
node->setParent(this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Skip this
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void Project::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("version"), QString::number(230220));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), this->GetUuid().toString());
|
||||
|
||||
writer->writeStartElement(QStringLiteral("nodes"));
|
||||
|
||||
foreach (Node* node, this->nodes()) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
|
||||
if (node == this->root()) {
|
||||
writer->writeAttribute(QStringLiteral("root"), QStringLiteral("1"));
|
||||
} else if (node == this->color_manager()) {
|
||||
writer->writeAttribute(QStringLiteral("cm"), QStringLiteral("1"));
|
||||
} else if (node == this->settings()) {
|
||||
writer->writeAttribute(QStringLiteral("settings"), QStringLiteral("1"));
|
||||
}
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), node->id());
|
||||
|
||||
node->Save(writer);
|
||||
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // nodes
|
||||
}
|
||||
|
||||
int Project::GetNumberOfContextsNodeIsIn(Node *node, bool except_itself) const
|
||||
{
|
||||
int count = 0;
|
||||
@@ -162,11 +288,6 @@ void Project::childEvent(QChildEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
Folder *Project::root()
|
||||
{
|
||||
return root_;
|
||||
}
|
||||
|
||||
QString Project::name() const
|
||||
{
|
||||
if (filename_.isEmpty()) {
|
||||
|
||||
+6
-16
@@ -70,9 +70,10 @@ public:
|
||||
return default_nodes_;
|
||||
}
|
||||
|
||||
int GetNumberOfContextsNodeIsIn(Node *node, bool except_itself = false) const;
|
||||
void Load(QXmlStreamReader *reader);
|
||||
void Save(QXmlStreamWriter *writer) const;
|
||||
|
||||
Folder* root();
|
||||
int GetNumberOfContextsNodeIsIn(Node *node, bool except_itself = false) const;
|
||||
|
||||
QString name() const;
|
||||
|
||||
@@ -80,8 +81,9 @@ public:
|
||||
QString pretty_filename() const;
|
||||
void set_filename(const QString& s);
|
||||
|
||||
ColorManager* color_manager() { return color_manager_; }
|
||||
ProjectSettingsNode* settings() { return settings_; }
|
||||
Folder* root() const { return root_; }
|
||||
ColorManager* color_manager() const { return color_manager_; }
|
||||
ProjectSettingsNode* settings() const { return settings_; }
|
||||
|
||||
bool is_modified() const { return is_modified_; }
|
||||
void set_modified(bool e);
|
||||
@@ -106,16 +108,6 @@ public:
|
||||
|
||||
void RegenerateUuid();
|
||||
|
||||
const MainWindowLayoutInfo &GetLayoutInfo() const
|
||||
{
|
||||
return layout_info_;
|
||||
}
|
||||
|
||||
void SetLayoutInfo(const MainWindowLayoutInfo &info)
|
||||
{
|
||||
layout_info_ = info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the filename the project was saved as, but not necessarily where it is now
|
||||
*
|
||||
@@ -195,8 +187,6 @@ private:
|
||||
|
||||
bool autorecovery_saved_;
|
||||
|
||||
MainWindowLayoutInfo layout_info_;
|
||||
|
||||
QVector<Node*> node_children_;
|
||||
|
||||
QVector<Node*> default_nodes_;
|
||||
|
||||
@@ -404,6 +404,34 @@ QVariant Footage::data(const DataType &d) const
|
||||
return super::data(d);
|
||||
}
|
||||
|
||||
bool Footage::LoadCustom(QXmlStreamReader *reader, SerializedData *data)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("timestamp")) {
|
||||
this->set_timestamp(reader->readElementText().toLongLong());
|
||||
} else if (reader->name() == QStringLiteral("viewer")) {
|
||||
if (!ViewerOutput::LoadCustom(reader, data)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Footage::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("timestamp"), QString::number(this->timestamp()));
|
||||
|
||||
writer->writeStartElement(QStringLiteral("viewer"));
|
||||
|
||||
ViewerOutput::SaveCustom(writer);
|
||||
|
||||
writer->writeEndElement(); // viewer
|
||||
}
|
||||
|
||||
void Footage::UpdateTooltip()
|
||||
{
|
||||
if (valid_) {
|
||||
|
||||
@@ -173,6 +173,9 @@ public:
|
||||
|
||||
virtual int GetTotalStreamCount() const override { return total_stream_count_; }
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader *reader, SerializedData *data) override;
|
||||
virtual void SaveCustom(QXmlStreamWriter *writer) const override;
|
||||
|
||||
static const QString kFilenameInput;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -31,16 +31,7 @@ ProjectSerializer210528::LoadData ProjectSerializer210528::Load(Project *project
|
||||
XMLNodeData xml_node_data;
|
||||
|
||||
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")) {
|
||||
if (reader->name() == QStringLiteral("uuid")) {
|
||||
|
||||
project->SetUuid(QUuid::fromString(reader->readElementText()));
|
||||
|
||||
|
||||
@@ -31,16 +31,7 @@ ProjectSerializer210907::LoadData ProjectSerializer210907::Load(Project *project
|
||||
XMLNodeData xml_node_data;
|
||||
|
||||
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")) {
|
||||
if (reader->name() == QStringLiteral("uuid")) {
|
||||
|
||||
project->SetUuid(QUuid::fromString(reader->readElementText()));
|
||||
|
||||
|
||||
@@ -33,16 +33,7 @@ ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project
|
||||
XMLNodeData xml_node_data;
|
||||
|
||||
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")) {
|
||||
if (reader->name() == QStringLiteral("uuid")) {
|
||||
|
||||
project->SetUuid(QUuid::fromString(reader->readElementText()));
|
||||
|
||||
|
||||
@@ -35,16 +35,7 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project
|
||||
LoadData load_data;
|
||||
|
||||
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")) {
|
||||
if (reader->name() == QStringLiteral("uuid")) {
|
||||
|
||||
project->SetUuid(QUuid::fromString(reader->readElementText()));
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,86 +40,6 @@ protected:
|
||||
return 230220;
|
||||
}
|
||||
|
||||
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<QString, QVariant> custom_properties;
|
||||
};
|
||||
|
||||
QHash<quintptr, Node*> node_ptrs;
|
||||
QList<SerializedConnection> desired_connections;
|
||||
QList<BlockLink> block_links;
|
||||
QVector<GroupLink> group_input_links;
|
||||
QHash<NodeGroup*, quintptr> group_output_links;
|
||||
QHash<Node*, QUuid> 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;
|
||||
|
||||
void LoadKeyframe(QXmlStreamReader *reader, NodeKeyframe *key, NodeValue::Type data_type) const;
|
||||
|
||||
void SaveKeyframe(QXmlStreamWriter *writer, NodeKeyframe *key, NodeValue::Type data_type) 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, ViewerOutput *viewer) const;
|
||||
|
||||
void SaveTimelinePoints(QXmlStreamWriter *writer, ViewerOutput *viewer) const;
|
||||
|
||||
void LoadMarker(QXmlStreamReader *reader, TimelineMarker *marker) const;
|
||||
|
||||
void SaveMarker(QXmlStreamWriter *writer, TimelineMarker *marker) 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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "serializeddata.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SERIALIZEDDATA_H
|
||||
#define SERIALIZEDDATA_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QVariant>
|
||||
|
||||
#include "node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeGroup;
|
||||
|
||||
struct SerializedData {
|
||||
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<QString, QVariant> custom_properties;
|
||||
};
|
||||
|
||||
QMap<quintptr, QMap<quintptr, Node::Position> > positions;
|
||||
QHash<quintptr, Node*> node_ptrs;
|
||||
QList<SerializedConnection> desired_connections;
|
||||
QList<BlockLink> block_links;
|
||||
QVector<GroupLink> group_input_links;
|
||||
QHash<NodeGroup*, quintptr> group_output_links;
|
||||
QHash<Node*, QUuid> node_uuids;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SERIALIZEDDATA_H
|
||||
@@ -42,12 +42,19 @@ public:
|
||||
return filename_;
|
||||
}
|
||||
|
||||
const MainWindowLayoutInfo &GetLoadedLayout() const
|
||||
{
|
||||
return layout_;
|
||||
}
|
||||
|
||||
protected:
|
||||
Project* project_;
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
MainWindowLayoutInfo layout_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ public:
|
||||
override_filename_ = filename;
|
||||
}
|
||||
|
||||
void SetLayout(const MainWindowLayoutInfo &layout)
|
||||
{
|
||||
layout_ = layout;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool Run() override;
|
||||
|
||||
@@ -52,6 +57,8 @@ private:
|
||||
|
||||
bool use_compression_;
|
||||
|
||||
MainWindowLayoutInfo layout_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -144,6 +144,65 @@ QRect TimelineMarker::Draw(QPainter *p, const QPoint &pt, int max_right, double
|
||||
}
|
||||
}
|
||||
|
||||
bool TimelineMarker::load(QXmlStreamReader *reader)
|
||||
{
|
||||
rational in, out;
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("name")) {
|
||||
this->set_name(attr.value().toString());
|
||||
} else if (attr.name() == QStringLiteral("in")) {
|
||||
in = rational::fromString(attr.value().toString().toStdString());
|
||||
} else if (attr.name() == QStringLiteral("out")) {
|
||||
out = rational::fromString(attr.value().toString().toStdString());
|
||||
} else if (attr.name() == QStringLiteral("color")) {
|
||||
this->set_color(attr.value().toInt());
|
||||
}
|
||||
}
|
||||
|
||||
this->set_time(TimeRange(in, out));
|
||||
|
||||
// This element has no inner text, so just skip it
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
|
||||
void TimelineMarker::save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("name"), this->name());
|
||||
writer->writeAttribute(QStringLiteral("in"), QString::fromStdString(this->time().in().toString()));
|
||||
writer->writeAttribute(QStringLiteral("out"), QString::fromStdString(this->time().out().toString()));
|
||||
writer->writeAttribute(QStringLiteral("color"), QString::number(this->color()));
|
||||
}
|
||||
|
||||
bool TimelineMarkerList::load(QXmlStreamReader *reader)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("marker")) {
|
||||
TimelineMarker *marker = new TimelineMarker(this);
|
||||
if (!marker->load(reader)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimelineMarkerList::save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
for (auto it=this->cbegin(); it!=this->cend(); it++) {
|
||||
TimelineMarker* marker = *it;
|
||||
|
||||
writer->writeStartElement(QStringLiteral("marker"));
|
||||
|
||||
marker->save(writer);
|
||||
|
||||
writer->writeEndElement(); // marker
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineMarkerList::childEvent(QChildEvent *e)
|
||||
{
|
||||
QObject::childEvent(e);
|
||||
|
||||
@@ -55,6 +55,9 @@ public:
|
||||
static int GetMarkerHeight(const QFontMetrics &fm);
|
||||
QRect Draw(QPainter *p, const QPoint &pt, int max_right, double scale, bool selected);
|
||||
|
||||
bool load(QXmlStreamReader *reader);
|
||||
void save(QXmlStreamWriter *writer) const;
|
||||
|
||||
signals:
|
||||
void TimeChanged(const TimeRange& time);
|
||||
|
||||
@@ -89,6 +92,9 @@ public:
|
||||
inline TimelineMarker *front() const { return markers_.front(); }
|
||||
inline size_t size() const { return markers_.size(); }
|
||||
|
||||
bool load(QXmlStreamReader *reader);
|
||||
void save(QXmlStreamWriter *writer) const;
|
||||
|
||||
TimelineMarker *GetMarkerAtTime(const rational &t) const
|
||||
{
|
||||
for (auto it=markers_.cbegin(); it!=markers_.cend(); it++) {
|
||||
|
||||
@@ -55,6 +55,48 @@ void TimelineWorkArea::set_range(const TimeRange &range)
|
||||
emit RangeChanged(workarea_range_);
|
||||
}
|
||||
|
||||
bool TimelineWorkArea::load(QXmlStreamReader *reader)
|
||||
{
|
||||
rational range_in = this->in();
|
||||
rational range_out = this->out();
|
||||
|
||||
uint version = 0;
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("version")) {
|
||||
version = attr.value().toUInt();
|
||||
}
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("enabled")) {
|
||||
this->set_enabled(reader->readElementText() != QStringLiteral("0"));
|
||||
} else if (reader->name() == QStringLiteral("in")) {
|
||||
range_in = rational::fromString(reader->readElementText().toStdString());
|
||||
} else if (reader->name() == QStringLiteral("out")) {
|
||||
range_out = rational::fromString(reader->readElementText().toStdString());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange loaded_workarea(range_in, range_out);
|
||||
|
||||
if (loaded_workarea != this->range()) {
|
||||
this->set_range(loaded_workarea);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimelineWorkArea::save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("version"), QString::number(1));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("enabled"), QString::number(this->enabled()));
|
||||
writer->writeTextElement(QStringLiteral("in"), QString::fromStdString(this->in().toString()));
|
||||
writer->writeTextElement(QStringLiteral("out"), QString::fromStdString(this->out().toString()));
|
||||
}
|
||||
|
||||
const rational &TimelineWorkArea::in() const
|
||||
{
|
||||
return workarea_range_.in();
|
||||
|
||||
@@ -45,6 +45,9 @@ public:
|
||||
const TimeRange& range() const;
|
||||
void set_range(const TimeRange& range);
|
||||
|
||||
bool load(QXmlStreamReader *reader);
|
||||
void save(QXmlStreamWriter *writer) const;
|
||||
|
||||
static const rational kResetIn;
|
||||
static const rational kResetOut;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user