Merge branch 'serializers'
This commit is contained in:
@@ -24,7 +24,6 @@
|
||||
#include "node/factory.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
//#include "widget/timelinewidget/undo/timelineundogeneral.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -44,60 +43,4 @@ bool XMLReadNextStartElement(QXmlStreamReader *reader)
|
||||
return false;
|
||||
}
|
||||
|
||||
void XMLNodeData::PostConnect(uint version, MultiUndoCommand *command) const
|
||||
{
|
||||
foreach (const XMLNodeData::SerializedConnection& con, desired_connections) {
|
||||
if (Node *out = node_ptrs.value(con.output_node)) {
|
||||
// Use output param as hint tag since we grandfathered those in
|
||||
Node::ValueHint hint(con.output_param);
|
||||
|
||||
if (command) {
|
||||
command->add_child(new NodeEdgeAddCommand(out, con.input));
|
||||
} else {
|
||||
Node::ConnectEdge(out, con.input);
|
||||
}
|
||||
|
||||
if (version < 210907) {
|
||||
/// Deprecated: backwards compatibility only
|
||||
if (command) {
|
||||
command->add_child(new NodeSetValueHintCommand(con.input, hint));
|
||||
} else {
|
||||
con.input.node()->SetValueHintForInput(con.input.input(), hint, con.input.element());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const XMLNodeData::BlockLink& l, block_links) {
|
||||
Node *a = l.block;
|
||||
Node *b = node_ptrs.value(l.link);
|
||||
if (command) {
|
||||
command->add_child(new NodeLinkCommand(a, b, true));
|
||||
} else {
|
||||
Node::Link(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const XMLNodeData::GroupLink &l, group_input_links) {
|
||||
if (Node *input_node = node_ptrs.value(l.input_node)) {
|
||||
NodeInput resolved(input_node, l.input_id, l.input_element);
|
||||
if (command) {
|
||||
command->add_child(new NodeGroupAddInputPassthrough(l.group, resolved));
|
||||
} else {
|
||||
l.group->AddInputPassthrough(resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=group_output_links.cbegin(); it!=group_output_links.cend(); it++) {
|
||||
if (Node *output_node = node_ptrs.value(it.value())) {
|
||||
if (command) {
|
||||
command->add_child(new NodeGroupSetOutputPassthrough(it.key(), output_node));
|
||||
} else {
|
||||
it.key()->SetOutputPassthrough(output_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-3
@@ -52,6 +52,7 @@
|
||||
#include "dialog/preferences/preferences.h"
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "panel/panelmanager.h"
|
||||
#include "panel/project/project.h"
|
||||
#include "panel/viewer/viewer.h"
|
||||
@@ -152,6 +153,9 @@ void Core::Start()
|
||||
// Initialize ConformManager
|
||||
ConformManager::CreateInstance();
|
||||
|
||||
// Initialize project serializers
|
||||
ProjectSerializer::Initialize();
|
||||
|
||||
//
|
||||
// Start application
|
||||
//
|
||||
@@ -207,6 +211,8 @@ void Core::Stop()
|
||||
}
|
||||
}
|
||||
|
||||
ProjectSerializer::Destroy();
|
||||
|
||||
ConformManager::DestroyInstance();
|
||||
|
||||
FrameManager::DestroyInstance();
|
||||
@@ -507,11 +513,10 @@ bool Core::AddOpenProjectFromTask(Task *task)
|
||||
|
||||
if (!load_task->IsCancelled()) {
|
||||
Project* project = load_task->GetLoadedProject();
|
||||
MainWindowLayoutInfo layout = load_task->GetLoadedLayout();
|
||||
|
||||
if (ValidateFootageInLoadedProject(project, load_task->GetFilenameProjectWasSavedAs())) {
|
||||
if (ValidateFootageInLoadedProject(project, project->GetSavedURL())) {
|
||||
AddOpenProject(project);
|
||||
main_window_->LoadLayout(layout);
|
||||
main_window_->LoadLayout(project->GetLayoutInfo());
|
||||
|
||||
return true;
|
||||
} else {
|
||||
@@ -770,6 +775,9 @@ void Core::SaveProjectInternal(Project* project, const QString& override_filenam
|
||||
// Create save manager
|
||||
Task* psm;
|
||||
|
||||
// Put layout into project
|
||||
project->SetLayoutInfo(main_window_->SaveLayout());
|
||||
|
||||
if (project->filename().endsWith(QStringLiteral(".otio"), Qt::CaseInsensitive)) {
|
||||
#ifdef USE_OTIO
|
||||
psm = new SaveOTIOTask(project);
|
||||
|
||||
@@ -83,6 +83,20 @@ void NodeGraph::childEvent(QChildEvent *event)
|
||||
emit NodeAdded(node);
|
||||
emit node->AddedToGraph(this);
|
||||
|
||||
// Emit input connections
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
if (nodes().contains(it->second)) {
|
||||
emit InputConnected(it->second, it->first);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit output connections
|
||||
for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) {
|
||||
if (nodes().contains(it->second.node())) {
|
||||
emit InputConnected(it->first, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (event->type() == QEvent::ChildRemoved) {
|
||||
|
||||
node_children_.removeOne(node);
|
||||
|
||||
@@ -129,61 +129,6 @@ QString NodeGroup::GetInputName(const QString &id) const
|
||||
return input_passthroughs_.value(id).name();
|
||||
}
|
||||
|
||||
bool NodeGroup::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled)
|
||||
{
|
||||
if (reader->name() == QStringLiteral("inputpassthroughs")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("inputpassthrough")) {
|
||||
XMLNodeData::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 {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
xml_node_data.group_input_links.append(link);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (reader->name() == QStringLiteral("outputpassthrough")) {
|
||||
xml_node_data.group_output_links.insert(this, reader->readElementText().toULongLong());
|
||||
return true;
|
||||
} else {
|
||||
return super::LoadCustom(reader, xml_node_data, version, cancelled);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGroup::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
super::SaveCustom(writer);
|
||||
|
||||
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
|
||||
|
||||
foreach (const NodeInput &ip, input_passthroughs_) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthrough"));
|
||||
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.node())));
|
||||
writer->writeTextElement(QStringLiteral("input"), ip.input());
|
||||
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.element()));
|
||||
writer->writeEndElement(); // input
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // inputpassthroughs
|
||||
|
||||
writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast<quintptr>(output_passthrough_)));
|
||||
}
|
||||
|
||||
void NodeGroupAddInputPassthrough::redo()
|
||||
{
|
||||
if (!group_->ContainsInputPassthrough(input_)) {
|
||||
|
||||
@@ -70,11 +70,6 @@ signals:
|
||||
|
||||
void OutputPassthroughChanged(NodeGroup *group, Node *output);
|
||||
|
||||
protected:
|
||||
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled) override;
|
||||
|
||||
virtual void SaveCustom(QXmlStreamWriter* writer) const override;
|
||||
|
||||
private:
|
||||
QHash<QString, NodeInput> input_passthroughs_;
|
||||
|
||||
|
||||
@@ -77,151 +77,6 @@ NodeGraph *Node::parent() const
|
||||
return static_cast<NodeGraph*>(QObject::parent());
|
||||
}
|
||||
|
||||
void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (cancelled && *cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reader->name() == QStringLiteral("input")) {
|
||||
LoadInput(reader, xml_node_data, cancelled);
|
||||
} else if (reader->name() == QStringLiteral("ptr")) {
|
||||
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this);
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
SetUUID(QUuid::fromString(reader->readElementText()));
|
||||
} else if (reader->name() == QStringLiteral("color")) {
|
||||
override_color_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("links")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("link")) {
|
||||
xml_node_data.block_links.append({this, reader->readElementText().toULongLong()});
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("custom")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (!LoadCustom(reader, xml_node_data, version, cancelled)) {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
QString output_param_id;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if ((version >= 210907 && reader->name() == QStringLiteral("output")) || reader->name() == QStringLiteral("node")) {
|
||||
output_node_id = reader->readElementText();
|
||||
} else if (version < 210907 && reader->name() == QStringLiteral("output")) {
|
||||
output_param_id = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
xml_node_data.desired_connections.append({NodeInput(this, param_id, ele), output_node_id.toULongLong(), output_param_id});
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
ValueHint vh;
|
||||
vh.Load(reader);
|
||||
|
||||
value_hints_.insert({input, element}, vh);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), uuid_.toString());
|
||||
writer->writeTextElement(QStringLiteral("label"), GetLabel());
|
||||
writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_));
|
||||
|
||||
foreach (const QString& input, input_ids_) {
|
||||
writer->writeStartElement(QStringLiteral("input"));
|
||||
|
||||
SaveInput(writer, input);
|
||||
|
||||
writer->writeEndElement(); // input
|
||||
}
|
||||
|
||||
writer->writeStartElement(QStringLiteral("links"));
|
||||
foreach (Node* link, links_) {
|
||||
writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast<quintptr>(link)));
|
||||
}
|
||||
writer->writeEndElement(); // links
|
||||
|
||||
writer->writeStartElement(QStringLiteral("connections"));
|
||||
for (auto it=input_connections().cbegin(); it!=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=value_hints_.cbegin(); it!=value_hints_.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();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("custom"));
|
||||
SaveCustom(writer);
|
||||
writer->writeEndElement(); // custom
|
||||
}
|
||||
|
||||
Project* Node::project() const
|
||||
{
|
||||
QObject *t = this->parent();
|
||||
@@ -393,90 +248,6 @@ QString Node::GetInputName(const QString &id) const
|
||||
}
|
||||
}
|
||||
|
||||
void Node::LoadInput(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled)
|
||||
{
|
||||
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 (!HasInputWithID(param_id)) {
|
||||
qWarning() << "Failed to load parameter that didn't exist:" << param_id;
|
||||
reader->skipCurrentElement();
|
||||
return;
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (cancelled && *cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reader->name() == QStringLiteral("primary")) {
|
||||
// Load primary immediate
|
||||
LoadImmediate(reader, param_id, -1, xml_node_data, cancelled);
|
||||
} else if (reader->name() == QStringLiteral("subelements")) {
|
||||
// Load subelements
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("count")) {
|
||||
InputArrayResize(param_id, attr.value().toInt());
|
||||
}
|
||||
}
|
||||
|
||||
int element_counter = 0;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("element")) {
|
||||
LoadImmediate(reader, param_id, element_counter, xml_node_data, cancelled);
|
||||
|
||||
element_counter++;
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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::IsInputHidden(const QString &input) const
|
||||
{
|
||||
return (GetInputFlags(input) & kInputFlagHidden);
|
||||
@@ -1477,15 +1248,6 @@ void Node::IgnoreHashingFrom(const QString &input_id)
|
||||
ignore_when_hashing_.append(input_id);
|
||||
}
|
||||
|
||||
bool Node::LoadCustom(QXmlStreamReader *, XMLNodeData &, uint, const QAtomicInt*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Node::SaveCustom(QXmlStreamWriter *) const
|
||||
{
|
||||
}
|
||||
|
||||
bool Node::HasGizmos() const
|
||||
{
|
||||
return false;
|
||||
@@ -1945,186 +1707,6 @@ void Node::ParameterValueChanged(const QString& input, int element, const TimeRa
|
||||
InvalidateCache(range, input, element);
|
||||
}
|
||||
|
||||
void Node::LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled)
|
||||
{
|
||||
Q_UNUSED(xml_node_data)
|
||||
|
||||
NodeValue::Type data_type = GetInputDataType(input);
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("standard")) {
|
||||
// Load standard value
|
||||
int val_index = 0;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (cancelled && *cancelled) {
|
||||
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, element);
|
||||
}
|
||||
}
|
||||
|
||||
SetSplitStandardValueOnTrack(input, val_index, value_on_track, element);
|
||||
|
||||
val_index++;
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("keyframing") && IsInputKeyframable(input)) {
|
||||
SetInputIsKeyframing(input, reader->readElementText().toInt(), element);
|
||||
} else if (reader->name() == QStringLiteral("keyframes")) {
|
||||
int track = 0;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (cancelled && *cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reader->name() == QStringLiteral("track")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (cancelled && *cancelled) {
|
||||
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 (cancelled && *cancelled) {
|
||||
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<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());
|
||||
}
|
||||
}
|
||||
|
||||
key_value = NodeValue::StringToValue(data_type, reader->readElementText(), true);
|
||||
|
||||
NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, key_input, this);
|
||||
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")) {
|
||||
SetInputProperty(input, QStringLiteral("col_input"), reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("csdisplay")) {
|
||||
SetInputProperty(input, QStringLiteral("col_display"), reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("csview")) {
|
||||
SetInputProperty(input, QStringLiteral("col_view"), reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("cslook")) {
|
||||
SetInputProperty(input, QStringLiteral("col_look"), reader->readElementText());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::SaveImmediate(QXmlStreamWriter *writer, const QString& input, int element) const
|
||||
{
|
||||
if (IsInputKeyframable(input)) {
|
||||
writer->writeTextElement(QStringLiteral("keyframing"), QString::number(IsInputKeyframing(input, element)));
|
||||
}
|
||||
|
||||
NodeValue::Type data_type = GetInputDataType(input);
|
||||
|
||||
// Write standard value
|
||||
writer->writeStartElement(QStringLiteral("standard"));
|
||||
|
||||
foreach (const QVariant& v, GetSplitStandardValue(input, element)) {
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
|
||||
if (data_type == NodeValue::kVideoParams) {
|
||||
v.value<VideoParams>().Save(writer);
|
||||
} else if (data_type == NodeValue::kAudioParams) {
|
||||
v.value<AudioParams>().Save(writer);
|
||||
} else {
|
||||
writer->writeCharacters(NodeValue::ValueToString(data_type, v, true));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // standard
|
||||
|
||||
// Write keyframes
|
||||
writer->writeStartElement(QStringLiteral("keyframes"));
|
||||
|
||||
foreach (const NodeKeyframeTrack& track, GetKeyframeTracks(input, element)) {
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
|
||||
foreach (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"), GetInputProperty(input, QStringLiteral("col_input")).toString());
|
||||
writer->writeTextElement(QStringLiteral("csdisplay"), GetInputProperty(input, QStringLiteral("col_display")).toString());
|
||||
writer->writeTextElement(QStringLiteral("csview"), GetInputProperty(input, QStringLiteral("col_view")).toString());
|
||||
writer->writeTextElement(QStringLiteral("cslook"), GetInputProperty(input, QStringLiteral("col_look")).toString());
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange Node::GetRangeAffectedByKeyframe(NodeKeyframe *key) const
|
||||
{
|
||||
const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key);
|
||||
@@ -2439,42 +2021,6 @@ void Node::ValueHint::Hash(QCryptographicHash &hash) const
|
||||
hash.addData(tag().toUtf8());
|
||||
}
|
||||
|
||||
void Node::ValueHint::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("types")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("type")) {
|
||||
type_.append(static_cast<NodeValue::Type>(reader->readElementText().toInt()));
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("index")) {
|
||||
index_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("tag")) {
|
||||
tag_ = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ValueHint::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("types"));
|
||||
|
||||
for (auto it=type_.cbegin(); it!=type_.cend(); it++) {
|
||||
writer->writeTextElement(QStringLiteral("type"), QString::number(*it));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // types
|
||||
|
||||
writer->writeTextElement(QStringLiteral("index"), QString::number(index_));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("tag"), tag_);
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::prepare()
|
||||
{
|
||||
move_recursively(node_, pos_.position - context_->GetNodePositionDataInContext(node_).position);
|
||||
|
||||
+5
-24
@@ -126,16 +126,6 @@ public:
|
||||
return flags_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear current node variables and replace them with
|
||||
*/
|
||||
void Load(QXmlStreamReader* reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled);
|
||||
|
||||
/**
|
||||
* @brief Save this node into a text/XML format
|
||||
*/
|
||||
void Save(QXmlStreamWriter* writer) const;
|
||||
|
||||
/**
|
||||
* @brief Return the name of the node
|
||||
*
|
||||
@@ -332,9 +322,6 @@ public:
|
||||
|
||||
virtual QString GetInputName(const QString& id) const;
|
||||
|
||||
void LoadInput(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled);
|
||||
void SaveInput(QXmlStreamWriter* writer, const QString& id) const;
|
||||
|
||||
bool IsInputHidden(const QString& input) const;
|
||||
bool IsInputConnectable(const QString& input) const;
|
||||
bool IsInputKeyframable(const QString& input) const;
|
||||
@@ -579,9 +566,6 @@ public:
|
||||
|
||||
void Hash(QCryptographicHash &hash) const;
|
||||
|
||||
void Load(QXmlStreamReader *reader);
|
||||
void Save(QXmlStreamWriter *writer) const;
|
||||
|
||||
const QVector<NodeValue::Type> &types() const { return type_; }
|
||||
const int &index() const { return index_; }
|
||||
const QString& tag() const { return tag_; }
|
||||
@@ -599,6 +583,11 @@ public:
|
||||
|
||||
static void Hash(const Node *node, const ValueHint &hint, QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params);
|
||||
|
||||
const QMap<InputElementPair, ValueHint> &GetValueHints() const
|
||||
{
|
||||
return value_hints_;
|
||||
}
|
||||
|
||||
ValueHint GetValueHintForInput(const QString &input, int element = -1) const
|
||||
{
|
||||
return value_hints_.value({input, element});
|
||||
@@ -1005,10 +994,6 @@ protected:
|
||||
return operation_stack_;
|
||||
}
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled);
|
||||
|
||||
virtual void SaveCustom(QXmlStreamWriter* writer) const;
|
||||
|
||||
enum GizmoScaleHandles {
|
||||
kGizmoScaleTopLeft,
|
||||
kGizmoScaleTopCenter,
|
||||
@@ -1265,10 +1250,6 @@ private:
|
||||
ParameterValueChanged(input.input(), input.element(), range);
|
||||
}
|
||||
|
||||
void LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled);
|
||||
|
||||
void SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const;
|
||||
|
||||
/**
|
||||
* @brief Intelligently determine how what time range is affected by a keyframe
|
||||
*/
|
||||
|
||||
+21
-186
@@ -34,24 +34,11 @@ void NodeCopyPasteService::CopyNodesToClipboard(QVector<Node *> nodes, void *use
|
||||
QString copy_str;
|
||||
|
||||
QXmlStreamWriter writer(©_str);
|
||||
writer.setAutoFormatting(true);
|
||||
|
||||
writer.writeStartDocument();
|
||||
writer.writeStartElement(QStringLiteral("olive"));
|
||||
|
||||
writer.writeTextElement(QStringLiteral("version"), QString::number(Project::kProjectVersion));
|
||||
|
||||
writer.writeStartElement(QStringLiteral("nodes"));
|
||||
// For any groups, add children
|
||||
for (int i=0; i<nodes.size(); i++) {
|
||||
Node *n = nodes.at(i);
|
||||
|
||||
writer.writeStartElement(QStringLiteral("node"));
|
||||
writer.writeAttribute(QStringLiteral("id"), n->id());
|
||||
n->Save(&writer);
|
||||
writer.writeEndElement(); // node
|
||||
|
||||
// If this is a group, add the child nodes too
|
||||
if (NodeGroup *g = dynamic_cast<NodeGroup*>(n)) {
|
||||
if (NodeGroup *g = dynamic_cast<NodeGroup*>(nodes.at(i))) {
|
||||
for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) {
|
||||
if (!nodes.contains(it.key())) {
|
||||
nodes.append(it.key());
|
||||
@@ -59,198 +46,46 @@ void NodeCopyPasteService::CopyNodesToClipboard(QVector<Node *> nodes, void *use
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.writeEndElement(); // nodes
|
||||
|
||||
writer.writeStartElement(QStringLiteral("contexts"));
|
||||
foreach (Node* n, nodes) {
|
||||
// Determine if this node is a context
|
||||
if (!n->GetContextPositions().isEmpty()) {
|
||||
writer.writeStartElement(QStringLiteral("context"));
|
||||
writer.writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(n)));
|
||||
ProjectSerializer::SaveData data(nodes.first()->project(), QString(), nodes);
|
||||
|
||||
const Node::PositionMap &map = n->GetContextPositions();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
writer.writeStartElement(QStringLiteral("node"));
|
||||
Project::SavePosition(&writer, it.key(), it.value());
|
||||
writer.writeEndElement(); // node
|
||||
}
|
||||
CopyNodesToClipboardCallback(nodes, &data, userdata);
|
||||
|
||||
writer.writeEndElement(); // context
|
||||
}
|
||||
}
|
||||
writer.writeEndElement(); // contexts
|
||||
|
||||
writer.writeStartElement(QStringLiteral("custom"));
|
||||
CopyNodesToClipboardInternal(&writer, nodes, userdata);
|
||||
writer.writeEndElement(); // custom
|
||||
|
||||
writer.writeEndElement(); // olive
|
||||
writer.writeEndDocument();
|
||||
ProjectSerializer::Save(&writer, data);
|
||||
|
||||
Core::CopyStringToClipboard(copy_str);
|
||||
}
|
||||
|
||||
QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand* command, void *userdata)
|
||||
void NodeCopyPasteService::PasteNodesFromClipboard(void *userdata)
|
||||
{
|
||||
QString clipboard = Core::PasteStringFromClipboard();
|
||||
|
||||
if (clipboard.isEmpty()) {
|
||||
return QVector<Node*>();
|
||||
return;
|
||||
}
|
||||
|
||||
QXmlStreamReader reader(clipboard);
|
||||
uint data_version = 0;
|
||||
|
||||
Project temp;
|
||||
ProjectSerializer::Result res = ProjectSerializer::Load(&temp, &reader);
|
||||
|
||||
if (res.code() != ProjectSerializer::kSuccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<Node*> pasted_nodes;
|
||||
XMLNodeData xml_node_data;
|
||||
QMap<quintptr, QMap<quintptr, Node::Position> > pasted_contexts;
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("olive")) {
|
||||
// Default to current version - this may not be desirable?
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("version")) {
|
||||
data_version = reader.readElementText().toUInt();
|
||||
} else if (reader.name() == QStringLiteral("nodes")) {
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("node")) {
|
||||
if (data_version == 0) {
|
||||
// Refuse to create any nodes if we didn't get a version string
|
||||
return QVector<Node*>();
|
||||
}
|
||||
|
||||
Node* node = nullptr;
|
||||
|
||||
XMLAttributeLoop((&reader), attr) {
|
||||
if (attr.name() == QStringLiteral("id")) {
|
||||
node = NodeFactory::CreateFromID(attr.value().toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node) {
|
||||
node->Load(&reader, xml_node_data, data_version, nullptr);
|
||||
|
||||
pasted_nodes.append(node);
|
||||
}
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader.name() == QStringLiteral("contexts")) {
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("context")) {
|
||||
// Get context ptr
|
||||
QMap<quintptr, Node::Position> map;
|
||||
quintptr context_ptr = 0;
|
||||
XMLAttributeLoop((&reader), attr) {
|
||||
if (attr.name() == QStringLiteral("ptr")) {
|
||||
context_ptr = attr.value().toULongLong();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find nodes section
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("node")) {
|
||||
quintptr node_ptr;
|
||||
Node::Position node_pos;
|
||||
|
||||
if (Project::LoadPosition(&reader, &node_ptr, &node_pos)) {
|
||||
map.insert(node_ptr, node_pos);
|
||||
}
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
// Insert
|
||||
if (!map.isEmpty()) {
|
||||
pasted_contexts.insert(context_ptr, map);
|
||||
}
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader.name() == QStringLiteral("custom")) {
|
||||
if (data_version == 0) {
|
||||
// Refuse to create any nodes if we didn't get a version string
|
||||
return QVector<Node*>();
|
||||
}
|
||||
|
||||
PasteNodesFromClipboardInternal(&reader, xml_node_data, userdata);
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
foreach (Node *n, temp.nodes()) {
|
||||
if (!temp.default_nodes().contains(n)) {
|
||||
// Move nodes out of Project
|
||||
n->setParent(nullptr);
|
||||
pasted_nodes.append(n);
|
||||
}
|
||||
}
|
||||
|
||||
if (pasted_nodes.isEmpty()) {
|
||||
// If we passed through the whole string and there were no nodes, it must not be data for us after all
|
||||
return QVector<Node*>();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have some nodes AND the XML data was malformed, the user should probably know
|
||||
if (reader.hasError()) {
|
||||
// Delete all nodes so this is a no-op
|
||||
foreach (Node* n, pasted_nodes) {
|
||||
delete n;
|
||||
}
|
||||
|
||||
// If this was NOT an internal error, we assume it's an XML error that the user needs to know about
|
||||
QMessageBox::critical(Core::instance()->main_window(),
|
||||
QCoreApplication::translate("NodeCopyPasteWidget", "Error pasting nodes"),
|
||||
QCoreApplication::translate("NodeCopyPasteWidget", "Failed to paste nodes: %1").arg(reader.errorString()),
|
||||
QMessageBox::Ok);
|
||||
|
||||
return QVector<Node*>();
|
||||
}
|
||||
|
||||
// Add all nodes to graph
|
||||
foreach (Node* n, pasted_nodes) {
|
||||
if (command) {
|
||||
command->add_child(new NodeAddCommand(graph, n));
|
||||
} else {
|
||||
n->setParent(graph);
|
||||
}
|
||||
}
|
||||
|
||||
// Process contexts
|
||||
for (auto it=pasted_contexts.cbegin(); it!=pasted_contexts.cend(); it++) {
|
||||
Node *context = xml_node_data.node_ptrs.value(it.key());
|
||||
if (context) {
|
||||
auto map = it.value();
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
Node *subnode = xml_node_data.node_ptrs.value(jt.key());
|
||||
if (subnode) {
|
||||
if (command) {
|
||||
command->add_child(new NodeSetPositionCommand(subnode, context, jt.value()));
|
||||
} else {
|
||||
context->SetNodePositionInContext(subnode, jt.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make connections
|
||||
xml_node_data.PostConnect(data_version, command);
|
||||
|
||||
return pasted_nodes;
|
||||
}
|
||||
|
||||
void NodeCopyPasteService::CopyNodesToClipboardInternal(QXmlStreamWriter*, const QVector<Node *> &, void*)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeCopyPasteService::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*)
|
||||
{
|
||||
Q_UNUSED(xml_node_data)
|
||||
reader->skipCurrentElement();
|
||||
PasteNodesToClipboardCallback(pasted_nodes, res.GetLoadData(), userdata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
#include <QUndoCommand>
|
||||
|
||||
#include "node/node.h"
|
||||
#include "node/project/project.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -37,11 +39,11 @@ public:
|
||||
protected:
|
||||
void CopyNodesToClipboard(QVector<Node *> nodes, void* userdata = nullptr);
|
||||
|
||||
QVector<Node*> PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand *command, void* userdata = nullptr);
|
||||
void PasteNodesFromClipboard(void* userdata = nullptr);
|
||||
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &nodes, void* userdata);
|
||||
virtual void CopyNodesToClipboardCallback(const QVector<Node*> &nodes, ProjectSerializer::SaveData *data, void *userdata){}
|
||||
|
||||
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata);
|
||||
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata){}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -137,23 +137,6 @@ void Track::SetTrackHeight(const double &height)
|
||||
emit TrackHeightChangedInPixels(GetTrackHeightInPixels());
|
||||
}
|
||||
|
||||
bool Track::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled)
|
||||
{
|
||||
if (reader->name() == QStringLiteral("height")) {
|
||||
SetTrackHeight(reader->readElementText().toDouble());
|
||||
return true;
|
||||
} else {
|
||||
return super::LoadCustom(reader, xml_node_data, version, cancelled);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
super::SaveCustom(writer);
|
||||
|
||||
writer->writeTextElement(QStringLiteral("height"), QString::number(GetTrackHeight()));
|
||||
}
|
||||
|
||||
void Track::InputConnectedEvent(const QString &input, int element, Node *output)
|
||||
{
|
||||
if (input == kBlockInput) {
|
||||
|
||||
@@ -455,10 +455,6 @@ signals:
|
||||
protected:
|
||||
virtual void Hash(QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params) const override;
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled) override;
|
||||
|
||||
virtual void SaveCustom(QXmlStreamWriter* writer) const override;
|
||||
|
||||
virtual void InputConnectedEvent(const QString& input, int element, Node *output) override;
|
||||
|
||||
virtual void InputDisconnectedEvent(const QString& input, int element, Node *output) override;
|
||||
|
||||
@@ -508,26 +508,6 @@ void ViewerOutput::set_parameters_from_footage(const QVector<ViewerOutput *> foo
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewerOutput::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled)
|
||||
{
|
||||
if (reader->name() == QStringLiteral("points")) {
|
||||
timeline_points_.Load(reader);
|
||||
return true;
|
||||
} else {
|
||||
return super::LoadCustom(reader, xml_node_data, version, cancelled);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOutput::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
super::SaveCustom(writer);
|
||||
|
||||
// Write TimelinePoints
|
||||
writer->writeStartElement(QStringLiteral("points"));
|
||||
timeline_points_.Save(writer);
|
||||
writer->writeEndElement(); // points
|
||||
}
|
||||
|
||||
int ViewerOutput::AddStream(Track::Type type, const QVariant& value)
|
||||
{
|
||||
QString id;
|
||||
|
||||
@@ -237,10 +237,6 @@ protected:
|
||||
|
||||
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled) override;
|
||||
|
||||
virtual void SaveCustom(QXmlStreamWriter *writer) const override;
|
||||
|
||||
int AddStream(Track::Type type, const QVariant &value);
|
||||
|
||||
private:
|
||||
|
||||
@@ -18,6 +18,7 @@ add_subdirectory(folder)
|
||||
add_subdirectory(footage)
|
||||
add_subdirectory(projectsettings)
|
||||
add_subdirectory(sequence)
|
||||
add_subdirectory(serializer)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
|
||||
@@ -72,23 +72,6 @@ void Footage::Retranslate()
|
||||
SetComboBoxStrings(kLoopModeInput, {tr("None"), tr("Loop"), tr("Clamp")});
|
||||
}
|
||||
|
||||
bool Footage::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled)
|
||||
{
|
||||
if (reader->name() == QStringLiteral("timestamp")) {
|
||||
set_timestamp(reader->readElementText().toLongLong());
|
||||
return true;
|
||||
} else {
|
||||
return super::LoadCustom(reader, xml_node_data, version, cancelled);
|
||||
}
|
||||
}
|
||||
|
||||
void Footage::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
super::SaveCustom(writer);
|
||||
|
||||
writer->writeTextElement(QStringLiteral("timestamp"), QString::number(timestamp_));
|
||||
}
|
||||
|
||||
void Footage::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
if (input == kFilenameInput) {
|
||||
|
||||
@@ -196,16 +196,6 @@ public:
|
||||
static const QString kLoopModeInput;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Load function
|
||||
*/
|
||||
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled) override;
|
||||
|
||||
/**
|
||||
* @brief Save function
|
||||
*/
|
||||
virtual void SaveCustom(QXmlStreamWriter *writer) const override;
|
||||
|
||||
virtual void InputValueChangedEvent(const QString &input, int element) override;
|
||||
|
||||
virtual rational VerifyLengthInternal(Track::Type type) const override;
|
||||
|
||||
@@ -32,12 +32,6 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
// This is the project version that Olive will save
|
||||
const uint Project::kProjectVersion = 211228;
|
||||
|
||||
// This is the minimum project version that Olive can load
|
||||
const uint Project::kProjectMinimumVersion = 210528;
|
||||
|
||||
Project::Project() :
|
||||
is_modified_(false),
|
||||
autorecovery_saved_(true)
|
||||
@@ -50,6 +44,7 @@ Project::Project() :
|
||||
root_->setParent(this);
|
||||
root_->SetLabel(tr("Root"));
|
||||
root_->SetCanBeDeleted(false);
|
||||
AddDefaultNode(root_);
|
||||
|
||||
// Adds a color manager "node" to this project so that it synchronizes
|
||||
color_manager_ = new ColorManager();
|
||||
@@ -67,194 +62,6 @@ Project::Project() :
|
||||
this, &Project::ColorManagerValueChanged);
|
||||
}
|
||||
|
||||
void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint version, const QAtomicInt* cancelled)
|
||||
{
|
||||
XMLNodeData xml_node_data;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("root")) {
|
||||
|
||||
root_->Load(reader, xml_node_data, version, cancelled);
|
||||
|
||||
} else 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.
|
||||
|
||||
*layout = MainWindowLayoutInfo::fromXml(reader, xml_node_data);
|
||||
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
|
||||
uuid_ = 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 = root_;
|
||||
} else if (is_cm) {
|
||||
node = color_manager_;
|
||||
} else if (is_settings) {
|
||||
node = settings_;
|
||||
} else {
|
||||
node = NodeFactory::CreateFromID(id);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
qWarning() << "Failed to find node with ID" << id;
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
node->Load(reader, xml_node_data, version, cancelled);
|
||||
node->setParent(this);
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
Node *context = xml_node_data.node_ptrs.value(context_ptr);
|
||||
|
||||
if (!context) {
|
||||
qWarning() << "Failed to find pointer for context";
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
quintptr node_ptr;
|
||||
Node::Position node_pos;
|
||||
|
||||
if (LoadPosition(reader, &node_ptr, &node_pos)) {
|
||||
Node *node = xml_node_data.node_ptrs.value(node_ptr);
|
||||
|
||||
if (node) {
|
||||
context->SetNodePositionInContext(node, node_pos);
|
||||
} else {
|
||||
qWarning() << "Failed to find pointer for node position";
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Skip this
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Make connections
|
||||
xml_node_data.PostConnect(version);
|
||||
}
|
||||
|
||||
void Project::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("uuid"), uuid_.toString());
|
||||
|
||||
writer->writeStartElement(QStringLiteral("nodes"));
|
||||
|
||||
foreach (Node* node, nodes()) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
|
||||
if (node == root_) {
|
||||
writer->writeAttribute(QStringLiteral("root"), QStringLiteral("1"));
|
||||
} else if (node == color_manager_) {
|
||||
writer->writeAttribute(QStringLiteral("cm"), QStringLiteral("1"));
|
||||
} else if (node == settings_) {
|
||||
writer->writeAttribute(QStringLiteral("settings"), QStringLiteral("1"));
|
||||
}
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), node->id());
|
||||
|
||||
node->Save(writer);
|
||||
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // nodes
|
||||
|
||||
writer->writeStartElement(QStringLiteral("positions"));
|
||||
|
||||
foreach (Node* context, nodes()) {
|
||||
const Node::PositionMap &map = context->GetContextPositions();
|
||||
|
||||
if (!map.isEmpty()) {
|
||||
writer->writeStartElement(QStringLiteral("context"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(context)));
|
||||
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
SavePosition(writer, jt.key(), jt.value());
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // context
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // positions
|
||||
|
||||
// Save main window project layout
|
||||
MainWindowLayoutInfo main_window_info = Core::instance()->main_window()->SaveLayout();
|
||||
main_window_info.toXml(writer);
|
||||
}
|
||||
|
||||
Folder *Project::root()
|
||||
{
|
||||
return root_;
|
||||
@@ -362,46 +169,6 @@ void Project::RegenerateUuid()
|
||||
uuid_ = QUuid::createUuid();
|
||||
}
|
||||
|
||||
bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos)
|
||||
{
|
||||
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 Project::SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos)
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(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 Project::ColorManagerValueChanged(const NodeInput &input, const TimeRange &range)
|
||||
{
|
||||
Q_UNUSED(input)
|
||||
|
||||
@@ -50,10 +50,6 @@ class Project : public NodeGraph
|
||||
public:
|
||||
Project();
|
||||
|
||||
void Load(QXmlStreamReader* reader, MainWindowLayoutInfo *layout, uint version, const QAtomicInt* cancelled);
|
||||
|
||||
void Save(QXmlStreamWriter* writer) const;
|
||||
|
||||
Folder* root();
|
||||
|
||||
QString name() const;
|
||||
@@ -81,13 +77,37 @@ public:
|
||||
return uuid_;
|
||||
}
|
||||
|
||||
void SetUuid(const QUuid &uuid)
|
||||
{
|
||||
uuid_ = uuid;
|
||||
}
|
||||
|
||||
void RegenerateUuid();
|
||||
|
||||
static bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos);
|
||||
static void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos);
|
||||
const MainWindowLayoutInfo &GetLayoutInfo() const
|
||||
{
|
||||
return layout_info_;
|
||||
}
|
||||
|
||||
static const uint kProjectVersion;
|
||||
static const uint kProjectMinimumVersion;
|
||||
void SetLayoutInfo(const MainWindowLayoutInfo &info)
|
||||
{
|
||||
layout_info_ = info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the filename the project was saved as, but not necessarily where it is now
|
||||
*
|
||||
* May help for resolving relative paths.
|
||||
*/
|
||||
const QString &GetSavedURL() const
|
||||
{
|
||||
return saved_url_;
|
||||
}
|
||||
|
||||
void SetSavedURL(const QString &url)
|
||||
{
|
||||
saved_url_ = url;
|
||||
}
|
||||
|
||||
signals:
|
||||
void NameChanged();
|
||||
@@ -101,6 +121,8 @@ private:
|
||||
|
||||
QString filename_;
|
||||
|
||||
QString saved_url_;
|
||||
|
||||
ColorManager* color_manager_;
|
||||
|
||||
ProjectSettingsNode* settings_;
|
||||
@@ -109,6 +131,8 @@ private:
|
||||
|
||||
bool autorecovery_saved_;
|
||||
|
||||
MainWindowLayoutInfo layout_info_;
|
||||
|
||||
private slots:
|
||||
void ColorManagerValueChanged(const NodeInput& input, const TimeRange& range);
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/project/serializer/serializer.cpp
|
||||
node/project/serializer/serializer.h
|
||||
node/project/serializer/serializer190219.cpp
|
||||
node/project/serializer/serializer190219.h
|
||||
node/project/serializer/serializer210528.cpp
|
||||
node/project/serializer/serializer210528.h
|
||||
node/project/serializer/serializer210907.cpp
|
||||
node/project/serializer/serializer210907.h
|
||||
node/project/serializer/serializer211228.cpp
|
||||
node/project/serializer/serializer211228.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,210 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "serializer.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "common/xmlutils.h"
|
||||
#include "core.h"
|
||||
#include "serializer190219.h"
|
||||
#include "serializer210528.h"
|
||||
#include "serializer210907.h"
|
||||
#include "serializer211228.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
QVector<ProjectSerializer*> ProjectSerializer::instances_;
|
||||
|
||||
void ProjectSerializer::Initialize()
|
||||
{
|
||||
// Make sure to order these from oldest to newest
|
||||
|
||||
// FIXME: Implement this - yes it's a 0.1 project loader
|
||||
//instances_.append(new ProjectSerializer190219);
|
||||
|
||||
instances_.append(new ProjectSerializer210528);
|
||||
instances_.append(new ProjectSerializer210907);
|
||||
instances_.append(new ProjectSerializer211228);
|
||||
}
|
||||
|
||||
void ProjectSerializer::Destroy()
|
||||
{
|
||||
qDeleteAll(instances_);
|
||||
instances_.clear();
|
||||
}
|
||||
|
||||
ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QString &filename)
|
||||
{
|
||||
QFile project_file(filename);
|
||||
|
||||
if (project_file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QXmlStreamReader reader(&project_file);
|
||||
|
||||
Result inner_result = Load(project, &reader);
|
||||
|
||||
project_file.close();
|
||||
|
||||
if (inner_result.code() != kSuccess) {
|
||||
return inner_result;
|
||||
}
|
||||
|
||||
if (reader.hasError()) {
|
||||
Result r(kXmlError);
|
||||
r.SetDetails(reader.errorString());
|
||||
return r;
|
||||
} else {
|
||||
return kSuccess;
|
||||
}
|
||||
} else {
|
||||
return kFileError;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamReader *reader)
|
||||
{
|
||||
// Determine project version
|
||||
uint version = 0;
|
||||
|
||||
while (!version && XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("olive") || reader->name() == QStringLiteral("project")) {
|
||||
while(!version && XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("version")) {
|
||||
version = reader->readElementText().toUInt();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to find version in file
|
||||
if (version == 0) {
|
||||
return kUnknownVersion;
|
||||
}
|
||||
|
||||
// We should now have the version, if we have a serializer for it, use it to load the project
|
||||
ProjectSerializer *serializer = nullptr;
|
||||
|
||||
foreach (ProjectSerializer *s, instances_) {
|
||||
if (version == s->Version()) {
|
||||
serializer = s;
|
||||
break;
|
||||
} else if (version < s->Version()) {
|
||||
// Assuming the instance list is in order, if the project version is less than any version
|
||||
// we find, we must not support it anymore
|
||||
return kProjectTooOld;
|
||||
}
|
||||
}
|
||||
|
||||
if (serializer) {
|
||||
LoadData ld = serializer->Load(project, reader, nullptr);
|
||||
Result r(kSuccess);
|
||||
if (reader->hasError()) {
|
||||
r = Result(kXmlError);
|
||||
r.SetDetails(reader->errorString());
|
||||
}
|
||||
r.SetLoadData(ld);
|
||||
return r;
|
||||
} else {
|
||||
// Reached the end of the list with no serializer, assume too new
|
||||
return kProjectTooNew;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data)
|
||||
{
|
||||
QString temp_save = FileFunctions::GetSafeTemporaryFilename(data.GetFilename());
|
||||
|
||||
QFile project_file(temp_save);
|
||||
|
||||
if (project_file.open(QFile::WriteOnly | QFile::Text)) {
|
||||
QXmlStreamWriter writer(&project_file);
|
||||
|
||||
Result inner_result = Save(&writer, data);
|
||||
|
||||
project_file.close();
|
||||
|
||||
if (inner_result != kSuccess) {
|
||||
return inner_result;
|
||||
}
|
||||
|
||||
// Save was successful, we can now rewrite the original file
|
||||
if (FileFunctions::RenameFileAllowOverwrite(temp_save, data.GetFilename())) {
|
||||
return kSuccess;
|
||||
} else {
|
||||
Result r(kOverwriteError);
|
||||
r.SetDetails(temp_save);
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
Result r(kFileError);
|
||||
r.SetDetails(temp_save);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, const SaveData &data)
|
||||
{
|
||||
writer->setAutoFormatting(true);
|
||||
|
||||
writer->writeStartDocument();
|
||||
|
||||
writer->writeStartElement("olive");
|
||||
|
||||
// By default, save as last serializer which, assuming the instances are ordered correctly,
|
||||
// will be the newest file format. But we may allow saving as older versions later on.
|
||||
ProjectSerializer *serializer = instances_.last();
|
||||
|
||||
// Version is stored in YYMMDD from whenever the project format was last changed
|
||||
// Allows easy integer math for checking project versions.
|
||||
writer->writeTextElement(QStringLiteral("version"), QString::number(serializer->Version()));
|
||||
|
||||
if (!data.GetFilename().isEmpty()) {
|
||||
writer->writeTextElement("url", data.GetFilename());
|
||||
}
|
||||
|
||||
writer->writeStartElement(QStringLiteral("project"));
|
||||
|
||||
serializer->Save(writer, data, nullptr);
|
||||
|
||||
writer->writeEndElement(); // project
|
||||
|
||||
writer->writeEndElement(); // olive
|
||||
|
||||
writer->writeEndDocument();
|
||||
|
||||
if (writer->hasError()) {
|
||||
return kXmlError;
|
||||
}
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
bool ProjectSerializer::IsCancelled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef PROJECTSERIALIZER_H
|
||||
#define PROJECTSERIALIZER_H
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
#include "common/define.h"
|
||||
#include "node/project/project.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief An abstract base class for serializing/deserializing project data
|
||||
*
|
||||
* The goal of this is to further abstract serialized project data from their
|
||||
* in-memory representations.
|
||||
*/
|
||||
class ProjectSerializer
|
||||
{
|
||||
public:
|
||||
ProjectSerializer() = default;
|
||||
|
||||
virtual ~ProjectSerializer(){}
|
||||
|
||||
DISABLE_COPY_MOVE(ProjectSerializer)
|
||||
|
||||
enum ResultCode {
|
||||
kSuccess,
|
||||
kProjectTooOld,
|
||||
kProjectTooNew,
|
||||
kUnknownVersion,
|
||||
kFileError,
|
||||
kXmlError,
|
||||
kOverwriteError
|
||||
};
|
||||
|
||||
using SerializedProperties = QHash<Node*, QMap<QString, QString> >;
|
||||
|
||||
class LoadData
|
||||
{
|
||||
public:
|
||||
LoadData() = default;
|
||||
|
||||
SerializedProperties properties;
|
||||
|
||||
};
|
||||
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
Result(const ResultCode &code) :
|
||||
code_(code)
|
||||
{}
|
||||
|
||||
bool operator==(const ResultCode &code) { return code_ == code; }
|
||||
bool operator!=(const ResultCode &code) { return code_ != code; }
|
||||
|
||||
const ResultCode &code() const { return code_; }
|
||||
|
||||
const QString &GetDetails() const { return details_; }
|
||||
|
||||
void SetDetails(const QString &s) { details_ = s; }
|
||||
|
||||
const LoadData &GetLoadData() const { return load_data_; }
|
||||
|
||||
void SetLoadData(const LoadData &p) { load_data_ = p; }
|
||||
|
||||
private:
|
||||
ResultCode code_;
|
||||
|
||||
QString details_;
|
||||
|
||||
LoadData load_data_;
|
||||
|
||||
};
|
||||
|
||||
class SaveData
|
||||
{
|
||||
public:
|
||||
SaveData(Project *project, const QString &filename, const QVector<Node*> &only = QVector<Node*>(), const SerializedProperties &p = SerializedProperties())
|
||||
{
|
||||
project_ = project;
|
||||
filename_ = filename;
|
||||
only_serialize_nodes_ = only;
|
||||
properties_ = p;
|
||||
}
|
||||
|
||||
Project *GetProject() const
|
||||
{
|
||||
return project_;
|
||||
}
|
||||
|
||||
const QString &GetFilename() const
|
||||
{
|
||||
return filename_;
|
||||
}
|
||||
|
||||
const QVector<Node*> &GetOnlySerializeNodes() const { return only_serialize_nodes_; }
|
||||
|
||||
void SetOnlySerializeNodes(const QVector<Node*> &only) { only_serialize_nodes_ = only; }
|
||||
|
||||
const SerializedProperties &GetProperties() const { return properties_; }
|
||||
|
||||
void SetProperties(const SerializedProperties &p) { properties_ = p; }
|
||||
|
||||
private:
|
||||
Project *project_;
|
||||
|
||||
QString filename_;
|
||||
|
||||
QVector<Node*> only_serialize_nodes_;
|
||||
|
||||
SerializedProperties properties_;
|
||||
|
||||
};
|
||||
|
||||
static void Initialize();
|
||||
|
||||
static void Destroy();
|
||||
|
||||
static Result Load(Project *project, const QString &filename);
|
||||
static Result Load(Project *project, QXmlStreamReader *read_device);
|
||||
|
||||
static Result Save(const SaveData &data);
|
||||
static Result Save(QXmlStreamWriter *write_device, const SaveData &data);
|
||||
|
||||
protected:
|
||||
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const = 0;
|
||||
|
||||
virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const {}
|
||||
|
||||
virtual uint Version() const = 0;
|
||||
|
||||
bool IsCancelled() const;
|
||||
|
||||
private:
|
||||
static QVector<ProjectSerializer*> instances_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PROJECTSERIALIZER_H
|
||||
@@ -0,0 +1,30 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "serializer190219.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
ProjectSerializer::LoadData ProjectSerializer190219::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
|
||||
{
|
||||
return LoadData();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef PROJECTSERIALIZER190219_H
|
||||
#define PROJECTSERIALIZER190219_H
|
||||
|
||||
#include "serializer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ProjectSerializer190219 : public ProjectSerializer
|
||||
{
|
||||
public:
|
||||
ProjectSerializer190219() = default;
|
||||
|
||||
protected:
|
||||
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
|
||||
|
||||
virtual uint Version() const override
|
||||
{
|
||||
return 190219;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SERIALIZER190219_H
|
||||
@@ -0,0 +1,649 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "serializer210528.h"
|
||||
|
||||
#include "node/factory.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
ProjectSerializer210528::LoadData ProjectSerializer210528::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
|
||||
{
|
||||
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));
|
||||
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
Node *context = xml_node_data.node_ptrs.value(context_ptr);
|
||||
|
||||
if (!context) {
|
||||
qWarning() << "Failed to find pointer for context";
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
quintptr node_ptr;
|
||||
Node::Position node_pos;
|
||||
|
||||
if (LoadPosition(reader, &node_ptr, &node_pos)) {
|
||||
Node *node = xml_node_data.node_ptrs.value(node_ptr);
|
||||
|
||||
if (node) {
|
||||
context->SetNodePositionInContext(node, node_pos);
|
||||
} else {
|
||||
qWarning() << "Failed to find pointer for node position";
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Skip this
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
// Make connections
|
||||
PostConnect(xml_node_data);
|
||||
|
||||
return LoadData();
|
||||
}
|
||||
|
||||
void ProjectSerializer210528::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")) {
|
||||
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), node);
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
node->SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
node->SetUUID(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;
|
||||
QString output_param_id;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
output_node_id = reader->readElementText();
|
||||
} else if (reader->name() == QStringLiteral("output")) {
|
||||
output_param_id = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
xml_node_data.desired_connections.append({NodeInput(node, param_id, ele), output_node_id.toULongLong(), output_param_id});
|
||||
} 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 ProjectSerializer210528::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const
|
||||
{
|
||||
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 ProjectSerializer210528::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, element);
|
||||
}
|
||||
}
|
||||
|
||||
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<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());
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectSerializer210528::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 ProjectSerializer210528::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)) {
|
||||
// Use output param as hint tag since we grandfathered those in
|
||||
Node::ValueHint hint(con.output_param);
|
||||
|
||||
Node::ConnectEdge(out, con.input);
|
||||
|
||||
con.input.node()->SetValueHintForInput(con.input.input(), hint, con.input.element());
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 ProjectSerializer210528::LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const
|
||||
{
|
||||
// Viewer-based nodes
|
||||
if (ViewerOutput *viewer = dynamic_cast<ViewerOutput*>(node)) {
|
||||
|
||||
Footage *footage = dynamic_cast<Footage*>(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<Track*>(node)) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("height")) {
|
||||
track->SetTrackHeight(reader->readElementText().toDouble());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (NodeGroup *group = dynamic_cast<NodeGroup*>(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 {
|
||||
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 ProjectSerializer210528::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 ProjectSerializer210528::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 ProjectSerializer210528::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 ProjectSerializer210528::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const
|
||||
{
|
||||
QVector<NodeValue::Type> types;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("types")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("type")) {
|
||||
types.append(static_cast<NodeValue::Type>(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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SERIALIZER210528_H
|
||||
#define SERIALIZER210528_H
|
||||
|
||||
#include "serializer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ProjectSerializer210528 : public ProjectSerializer
|
||||
{
|
||||
public:
|
||||
ProjectSerializer210528() = default;
|
||||
|
||||
protected:
|
||||
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
|
||||
|
||||
virtual uint Version() const override
|
||||
{
|
||||
return 210528;
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const;
|
||||
|
||||
void LoadInput(Node *node, QXmlStreamReader* reader, XMLNodeData &xml_node_data) const;
|
||||
|
||||
void LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData& xml_node_data) const;
|
||||
|
||||
bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const;
|
||||
|
||||
void PostConnect(const XMLNodeData &xml_node_data) const;
|
||||
|
||||
void LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const;
|
||||
|
||||
void LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const;
|
||||
|
||||
void LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const;
|
||||
|
||||
void LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const;
|
||||
|
||||
void LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SERIALIZER211228_H
|
||||
@@ -0,0 +1,641 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "serializer210907.h"
|
||||
|
||||
#include "node/factory.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
ProjectSerializer210907::LoadData ProjectSerializer210907::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
|
||||
{
|
||||
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));
|
||||
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
Node *context = xml_node_data.node_ptrs.value(context_ptr);
|
||||
|
||||
if (!context) {
|
||||
qWarning() << "Failed to find pointer for context";
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
quintptr node_ptr;
|
||||
Node::Position node_pos;
|
||||
|
||||
if (LoadPosition(reader, &node_ptr, &node_pos)) {
|
||||
Node *node = xml_node_data.node_ptrs.value(node_ptr);
|
||||
|
||||
if (node) {
|
||||
context->SetNodePositionInContext(node, node_pos);
|
||||
} else {
|
||||
qWarning() << "Failed to find pointer for node position";
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Skip this
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
// Make connections
|
||||
PostConnect(xml_node_data);
|
||||
|
||||
return LoadData();
|
||||
}
|
||||
|
||||
void ProjectSerializer210907::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")) {
|
||||
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), node);
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
node->SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
node->SetUUID(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(), QString()});
|
||||
} 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 ProjectSerializer210907::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const
|
||||
{
|
||||
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 ProjectSerializer210907::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, element);
|
||||
}
|
||||
}
|
||||
|
||||
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<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());
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectSerializer210907::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 ProjectSerializer210907::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);
|
||||
}
|
||||
}
|
||||
|
||||
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 ProjectSerializer210907::LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const
|
||||
{
|
||||
// Viewer-based nodes
|
||||
if (ViewerOutput *viewer = dynamic_cast<ViewerOutput*>(node)) {
|
||||
|
||||
Footage *footage = dynamic_cast<Footage*>(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<Track*>(node)) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("height")) {
|
||||
track->SetTrackHeight(reader->readElementText().toDouble());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (NodeGroup *group = dynamic_cast<NodeGroup*>(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 {
|
||||
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 ProjectSerializer210907::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 ProjectSerializer210907::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 ProjectSerializer210907::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 ProjectSerializer210907::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const
|
||||
{
|
||||
QVector<NodeValue::Type> types;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("types")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("type")) {
|
||||
types.append(static_cast<NodeValue::Type>(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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SERIALIZER210907_H
|
||||
#define SERIALIZER210907_H
|
||||
|
||||
#include "serializer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ProjectSerializer210907 : public ProjectSerializer
|
||||
{
|
||||
public:
|
||||
ProjectSerializer210907() = default;
|
||||
|
||||
protected:
|
||||
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
|
||||
|
||||
virtual uint Version() const override
|
||||
{
|
||||
return 210907;
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const;
|
||||
|
||||
void LoadInput(Node *node, QXmlStreamReader* reader, XMLNodeData &xml_node_data) const;
|
||||
|
||||
void LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData& xml_node_data) const;
|
||||
|
||||
bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const;
|
||||
|
||||
void PostConnect(const XMLNodeData &xml_node_data) const;
|
||||
|
||||
void LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const;
|
||||
|
||||
void LoadTimelinePoints(QXmlStreamReader *reader, TimelinePoints *points) const;
|
||||
|
||||
void LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const;
|
||||
|
||||
void LoadMarkerList(QXmlStreamReader *reader, TimelineMarkerList *markers) const;
|
||||
|
||||
void LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SERIALIZER211228_H
|
||||
@@ -0,0 +1,995 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "serializer211228.h"
|
||||
|
||||
#include "node/factory.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
|
||||
{
|
||||
QMap<quintptr, QMap<QString, QString> > properties;
|
||||
QMap<quintptr, QMap<quintptr, Node::Position> > positions;
|
||||
XMLNodeData xml_node_data;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
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));
|
||||
|
||||
} 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<QString, QString> properties_for_node;
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
properties_for_node.insert(reader->name().toString(), reader->readElementText());
|
||||
}
|
||||
properties.insert(ptr, properties_for_node);
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Skip this
|
||||
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 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<Node*> &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<quintptr>(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<quintptr>(it.key())));
|
||||
|
||||
for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) {
|
||||
writer->writeTextElement(jt.key(), jt.value());
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // properties
|
||||
|
||||
// Save main window project layout
|
||||
project->GetLayoutInfo().toXml(writer);
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::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);
|
||||
qDebug() << "Inserting" << ptr << "as" << node;
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
node->SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
node->SetUUID(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(), QString()});
|
||||
} 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 ProjectSerializer211228::SaveNode(Node *node, QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(node)));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), node->GetUUID().toString());
|
||||
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<quintptr>(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<quintptr>(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;
|
||||
|
||||
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 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; i<arr_sz; i++) {
|
||||
writer->writeStartElement(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)
|
||||
|
||||
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, element);
|
||||
}
|
||||
}
|
||||
|
||||
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<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());
|
||||
}
|
||||
}
|
||||
|
||||
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 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<VideoParams>().Save(writer);
|
||||
} else if (data_type == NodeValue::kAudioParams) {
|
||||
v.value<AudioParams>().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;
|
||||
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 ProjectSerializer211228::SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 ProjectSerializer211228::LoadNodeCustom(QXmlStreamReader *reader, Node *node, XMLNodeData &xml_node_data) const
|
||||
{
|
||||
// Viewer-based nodes
|
||||
if (ViewerOutput *viewer = dynamic_cast<ViewerOutput*>(node)) {
|
||||
|
||||
Footage *footage = dynamic_cast<Footage*>(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<Track*>(node)) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("height")) {
|
||||
track->SetTrackHeight(reader->readElementText().toDouble());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (NodeGroup *group = dynamic_cast<NodeGroup*>(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 {
|
||||
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 ProjectSerializer211228::SaveNodeCustom(QXmlStreamWriter *writer, Node *node) const
|
||||
{
|
||||
if (ViewerOutput *viewer = dynamic_cast<ViewerOutput*>(node)) {
|
||||
// Write TimelinePoints
|
||||
writer->writeStartElement(QStringLiteral("points"));
|
||||
SaveTimelinePoints(writer, viewer->GetTimelinePoints());
|
||||
writer->writeEndElement(); // points
|
||||
|
||||
if (Footage *footage = dynamic_cast<Footage*>(node)) {
|
||||
writer->writeTextElement(QStringLiteral("timestamp"), QString::number(footage->timestamp()));
|
||||
}
|
||||
} else if (Track *track = dynamic_cast<Track*>(node)) {
|
||||
writer->writeTextElement(QStringLiteral("height"), QString::number(track->GetTrackHeight()));
|
||||
} else if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
|
||||
|
||||
foreach (const NodeInput &ip, group->GetInputPassthroughs()) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthrough"));
|
||||
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.node())));
|
||||
writer->writeTextElement(QStringLiteral("input"), ip.input());
|
||||
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.element()));
|
||||
writer->writeEndElement(); // input
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // inputpassthroughs
|
||||
|
||||
writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast<quintptr>(group->GetOutputPassthrough())));
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer211228::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 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();
|
||||
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 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)) {
|
||||
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 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<NodeValue::Type> types;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("types")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("type")) {
|
||||
types.append(static_cast<NodeValue::Type>(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 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SERIALIZER211228_H
|
||||
#define SERIALIZER211228_H
|
||||
|
||||
#include "serializer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ProjectSerializer211228 : public ProjectSerializer
|
||||
{
|
||||
public:
|
||||
ProjectSerializer211228() = 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 211228;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SERIALIZER211228_H
|
||||
@@ -21,11 +21,8 @@
|
||||
#include "load.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "common/xmlutils.h"
|
||||
#include "core.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -36,78 +33,42 @@ ProjectLoadTask::ProjectLoadTask(const QString &filename) :
|
||||
|
||||
bool ProjectLoadTask::Run()
|
||||
{
|
||||
QFile project_file(GetFilename());
|
||||
project_ = new Project();
|
||||
|
||||
if (project_file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QXmlStreamReader reader(&project_file);
|
||||
project_->set_filename(GetFilename());
|
||||
|
||||
uint project_version = 0;
|
||||
ProjectSerializer::Result result = ProjectSerializer::Load(project_, GetFilename());
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("olive")) {
|
||||
while(XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("version")) {
|
||||
bool ok;
|
||||
|
||||
project_version = reader.readElementText().toUInt(&ok);
|
||||
|
||||
if (!ok) {
|
||||
SetError(tr("Failed to parse project version."));
|
||||
return false;
|
||||
} else if (project_version > Project::kProjectVersion) {
|
||||
// Project is newer than we support
|
||||
SetError(tr("This project is from a newer version of Olive and cannot be opened in this version."));
|
||||
return false;
|
||||
} else if (project_version < Project::kProjectMinimumVersion) { // Change this if we drop support for a project version
|
||||
// Project is older than we support
|
||||
SetError(tr("This project is from a version of Olive that is no longer supported in this version."));
|
||||
return false;
|
||||
}
|
||||
} else if (reader.name() == QStringLiteral("url")) {
|
||||
project_saved_url_ = reader.readElementText();
|
||||
} else if (reader.name() == QStringLiteral("project")) {
|
||||
if (project_version == 0) {
|
||||
// Failed to find project version, throw error before we create project
|
||||
SetError(tr("Failed to find project version."));
|
||||
return false;
|
||||
}
|
||||
|
||||
project_ = new Project();
|
||||
|
||||
project_->set_filename(GetFilename());
|
||||
|
||||
project_->Load(&reader, &layout_info_, project_version, &IsCancelled());
|
||||
|
||||
// Ensure project is in main thread
|
||||
project_->moveToThread(qApp->thread());
|
||||
break;
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader.name() == QStringLiteral("project")) {
|
||||
// 0.1 projects use "project" as the root instead of Olive. We don't currently support
|
||||
// these projects
|
||||
SetError(tr("This project is from a version of Olive that is no longer supported in this version."));
|
||||
return false;
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
project_file.close();
|
||||
|
||||
emit ProgressChanged(1);
|
||||
|
||||
if (reader.hasError()) {
|
||||
SetError(reader.errorString());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
} else {
|
||||
switch (result.code()) {
|
||||
case ProjectSerializer::kSuccess:
|
||||
break;
|
||||
case ProjectSerializer::kProjectTooOld:
|
||||
SetError(tr("This project is from a version of Olive that is no longer supported in this version."));
|
||||
break;
|
||||
case ProjectSerializer::kProjectTooNew:
|
||||
SetError(tr("This project is from a newer version of Olive and cannot be opened in this version."));
|
||||
break;
|
||||
case ProjectSerializer::kUnknownVersion:
|
||||
SetError(tr("Failed to determine project version."));
|
||||
break;
|
||||
case ProjectSerializer::kFileError:
|
||||
SetError(tr("Failed to read file \"%1\" for reading.").arg(GetFilename()));
|
||||
break;
|
||||
case ProjectSerializer::kXmlError:
|
||||
SetError(tr("Failed to read XML document. File may be corrupt. Error was: %1").arg(result.GetDetails()));
|
||||
break;
|
||||
|
||||
// Errors that should never be thrown by a load
|
||||
case ProjectSerializer::kOverwriteError:
|
||||
SetError(tr("Unknown error."));
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == ProjectSerializer::kSuccess) {
|
||||
project_->moveToThread(qApp->thread());
|
||||
return true;
|
||||
} else {
|
||||
delete project_;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,21 +37,6 @@ public:
|
||||
return project_;
|
||||
}
|
||||
|
||||
MainWindowLayoutInfo GetLoadedLayout() const
|
||||
{
|
||||
return layout_info_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the filename the project was saved as, but not necessarily where it is now
|
||||
*
|
||||
* May help for resolving relative paths.
|
||||
*/
|
||||
const QString& GetFilenameProjectWasSavedAs() const
|
||||
{
|
||||
return project_saved_url_;
|
||||
}
|
||||
|
||||
const QString& GetFilename() const
|
||||
{
|
||||
return filename_;
|
||||
@@ -60,10 +45,6 @@ public:
|
||||
protected:
|
||||
Project* project_;
|
||||
|
||||
MainWindowLayoutInfo layout_info_;
|
||||
|
||||
QString project_saved_url_;
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
#include "core.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -39,54 +40,37 @@ bool ProjectSaveTask::Run()
|
||||
{
|
||||
QString using_filename = override_filename_.isEmpty() ? project_->filename() : override_filename_;
|
||||
|
||||
// File to temporarily save to (ensures we can't half-write the user's main file and crash)
|
||||
QString temp_save = FileFunctions::GetSafeTemporaryFilename(using_filename);
|
||||
ProjectSerializer::SaveData data(project_, using_filename);
|
||||
|
||||
QFile project_file(temp_save);
|
||||
ProjectSerializer::Result result = ProjectSerializer::Save(data);
|
||||
|
||||
if (project_file.open(QFile::WriteOnly | QFile::Text)) {
|
||||
QXmlStreamWriter writer(&project_file);
|
||||
writer.setAutoFormatting(true);
|
||||
bool success = false;
|
||||
|
||||
writer.writeStartDocument();
|
||||
switch (result.code()) {
|
||||
case ProjectSerializer::kSuccess:
|
||||
success = true;
|
||||
break;
|
||||
case ProjectSerializer::kXmlError:
|
||||
SetError(tr("Failed to write XML data."));
|
||||
break;
|
||||
case ProjectSerializer::kFileError:
|
||||
SetError(tr("Failed to open file \"%1\" for writing.").arg(result.GetDetails()));
|
||||
break;
|
||||
case ProjectSerializer::kOverwriteError:
|
||||
SetError(tr("Failed to overwrite \"%1\". Project has been saved as \"%2\" instead.")
|
||||
.arg(using_filename, result.GetDetails()));
|
||||
success = true;
|
||||
break;
|
||||
|
||||
writer.writeStartElement("olive");
|
||||
|
||||
// Version is stored in YYMMDD from whenever the project format was last changed
|
||||
// Allows easy integer math for checking project versions.
|
||||
writer.writeTextElement(QStringLiteral("version"), QString::number(Project::kProjectVersion));
|
||||
|
||||
writer.writeTextElement("url", using_filename);
|
||||
|
||||
writer.writeStartElement(QStringLiteral("project"));
|
||||
|
||||
project_->Save(&writer);
|
||||
|
||||
writer.writeEndElement(); // project
|
||||
|
||||
writer.writeEndElement(); // olive
|
||||
|
||||
writer.writeEndDocument();
|
||||
|
||||
project_file.close();
|
||||
|
||||
if (writer.hasError()) {
|
||||
SetError(tr("Failed to write XML data"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save was successful, we can now rewrite the original file
|
||||
if (FileFunctions::RenameFileAllowOverwrite(temp_save, using_filename)) {
|
||||
return true;
|
||||
} else {
|
||||
SetError(tr("Failed to overwrite \"%1\". Project has been saved as \"%2\" instead.")
|
||||
.arg(using_filename, temp_save));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
SetError(tr("Failed to open temporary file \"%1\" for writing.").arg(temp_save));
|
||||
return false;
|
||||
// Errors that should never be thrown by a save
|
||||
case ProjectSerializer::kProjectTooNew:
|
||||
case ProjectSerializer::kProjectTooOld:
|
||||
case ProjectSerializer::kUnknownVersion:
|
||||
SetError(tr("Unknown error."));
|
||||
break;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,20 +53,6 @@ void TimelineMarker::set_name(const QString &name)
|
||||
emit NameChanged(name_);
|
||||
}
|
||||
|
||||
void TimelineMarkerList::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
foreach (TimelineMarker* marker, markers_) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
TimelineMarkerList::~TimelineMarkerList()
|
||||
{
|
||||
qDeleteAll(markers_);
|
||||
@@ -99,28 +85,4 @@ const QList<TimelineMarker*> &TimelineMarkerList::list() const
|
||||
return markers_;
|
||||
}
|
||||
|
||||
void TimelineMarkerList::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
AddMarker(TimeRange(in, out), name);
|
||||
}
|
||||
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -67,9 +67,6 @@ public:
|
||||
|
||||
const QList<TimelineMarker *> &list() const;
|
||||
|
||||
void Load(QXmlStreamReader* reader);
|
||||
void Save(QXmlStreamWriter* writer) const;
|
||||
|
||||
signals:
|
||||
void MarkerAdded(TimelineMarker* marker);
|
||||
|
||||
|
||||
@@ -39,30 +39,6 @@ const TimelineWorkArea *TimelinePoints::workarea() const
|
||||
return &workarea_;
|
||||
}
|
||||
|
||||
void TimelinePoints::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("markers")) {
|
||||
markers_.Load(reader);
|
||||
} else if (reader->name() == QStringLiteral("workarea")) {
|
||||
workarea_.Load(reader);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelinePoints::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("workarea"));
|
||||
workarea_.Save(writer);
|
||||
writer->writeEndElement(); // workarea
|
||||
|
||||
writer->writeStartElement(QStringLiteral("markers"));
|
||||
markers_.Save(writer);
|
||||
writer->writeEndElement(); // markers
|
||||
}
|
||||
|
||||
TimelineWorkArea *TimelinePoints::workarea()
|
||||
{
|
||||
return &workarea_;
|
||||
|
||||
@@ -40,9 +40,6 @@ public:
|
||||
TimelineWorkArea* workarea();
|
||||
const TimelineWorkArea* workarea() const;
|
||||
|
||||
void Load(QXmlStreamReader* reader);
|
||||
void Save(QXmlStreamWriter* writer) const;
|
||||
|
||||
private:
|
||||
TimelineMarkerList markers_;
|
||||
|
||||
|
||||
@@ -55,37 +55,6 @@ void TimelineWorkArea::set_range(const TimeRange &range)
|
||||
emit RangeChanged(workarea_range_);
|
||||
}
|
||||
|
||||
void TimelineWorkArea::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
rational range_in = workarea_range_.in();
|
||||
rational range_out = workarea_range_.out();
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("enabled")) {
|
||||
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_) {
|
||||
set_range(loaded_workarea);
|
||||
}
|
||||
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
|
||||
void TimelineWorkArea::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("enabled"), QString::number(workarea_enabled_));
|
||||
writer->writeAttribute(QStringLiteral("in"), workarea_range_.in().toString());
|
||||
writer->writeAttribute(QStringLiteral("out"), workarea_range_.out().toString());
|
||||
}
|
||||
|
||||
const rational &TimelineWorkArea::in() const
|
||||
{
|
||||
return workarea_range_.in();
|
||||
|
||||
@@ -44,9 +44,6 @@ public:
|
||||
const TimeRange& range() const;
|
||||
void set_range(const TimeRange& range);
|
||||
|
||||
void Load(QXmlStreamReader* reader);
|
||||
void Save(QXmlStreamWriter* writer) const;
|
||||
|
||||
static const rational kResetIn;
|
||||
static const rational kResetOut;
|
||||
|
||||
|
||||
@@ -205,12 +205,30 @@ void NodeView::CopySelected(bool cut)
|
||||
|
||||
void NodeView::Paste()
|
||||
{
|
||||
PasteNodesInternal();
|
||||
if (!contexts_.isEmpty()) {
|
||||
PasteNodesFromClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::Duplicate()
|
||||
{
|
||||
PasteNodesInternal(selected_nodes_);
|
||||
if (!selected_nodes_.isEmpty()) {
|
||||
Node::PositionMap map;
|
||||
QVector<Node*> new_nodes;
|
||||
new_nodes.resize(selected_nodes_.size());
|
||||
|
||||
for (int i=0; i<new_nodes.size(); i++) {
|
||||
Node *og = selected_nodes_.at(i);
|
||||
Node *copy = og->copy();
|
||||
Node::CopyInputs(og, copy, false);
|
||||
map.insert(copy, GetAssumedPositionForSelectedNode(og));
|
||||
new_nodes[i] = copy;
|
||||
}
|
||||
|
||||
Node::CopyDependencyGraph(selected_nodes_, new_nodes, nullptr);
|
||||
|
||||
PostPaste(new_nodes, map);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::SetColorLabel(int index)
|
||||
@@ -903,9 +921,9 @@ bool NodeView::eventFilter(QObject *object, QEvent *event)
|
||||
return super::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node *> &nodes, void *userdata)
|
||||
void NodeView::CopyNodesToClipboardCallback(const QVector<Node*> &nodes, ProjectSerializer::SaveData *sdata, void *userdata)
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("pos"));
|
||||
ProjectSerializer::SerializedProperties properties;
|
||||
|
||||
for (Node *n : nodes) {
|
||||
NodeViewItem *item = GetAssumedItemForSelectedNode(n);
|
||||
@@ -913,59 +931,31 @@ void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVec
|
||||
if (item) {
|
||||
Node::Position pos = item->GetNodePositionData();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(n)));
|
||||
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x()));
|
||||
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y()));
|
||||
writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded));
|
||||
writer->writeEndElement(); // node
|
||||
properties[n][QStringLiteral("x")] = QString::number(pos.position.x());
|
||||
properties[n][QStringLiteral("y")] = QString::number(pos.position.y());
|
||||
properties[n][QStringLiteral("expanded")] = QString::number(pos.expanded);
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // pos
|
||||
sdata->SetProperties(properties);
|
||||
}
|
||||
|
||||
void NodeView::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void *userdata)
|
||||
void NodeView::PasteNodesToClipboardCallback(const QVector<Node *> &nodes, const ProjectSerializer::LoadData &ldata, void *userdata)
|
||||
{
|
||||
Node::PositionMap *map = static_cast<Node::PositionMap *>(userdata);
|
||||
Node::PositionMap map;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("pos")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
Node *n = nullptr;
|
||||
Node::Position pos;
|
||||
for (auto it=ldata.properties.cbegin(); it!=ldata.properties.cend(); it++) {
|
||||
Node::Position pos;
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("ptr")) {
|
||||
n = xml_node_data.node_ptrs.value(attr.value().toULongLong());
|
||||
break;
|
||||
}
|
||||
}
|
||||
const QMap<QString, QString> &node_props = it.value();
|
||||
pos.position.setX(node_props.value(QStringLiteral("x")).toDouble());
|
||||
pos.position.setY(node_props.value(QStringLiteral("y")).toDouble());
|
||||
pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble();
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("x")) {
|
||||
pos.position.setX(reader->readElementText().toDouble());
|
||||
} else if (reader->name() == QStringLiteral("y")) {
|
||||
pos.position.setY(reader->readElementText().toDouble());
|
||||
} else if (reader->name() == QStringLiteral("expanded")) {
|
||||
pos.expanded = reader->readElementText().toInt();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
if (n) {
|
||||
map->insert(n, pos);
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
map.insert(it.key(), pos);
|
||||
}
|
||||
|
||||
PostPaste(nodes, map);
|
||||
}
|
||||
|
||||
void NodeView::changeEvent(QEvent *e)
|
||||
@@ -1265,75 +1255,6 @@ void NodeView::ItemAboutToBeDeleted(NodeViewItem *item)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
|
||||
{
|
||||
// If no graph, do nothing
|
||||
if (contexts_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If duplicating nodes, duplicate, otherwise paste
|
||||
QVector<Node*> new_nodes;
|
||||
Node::PositionMap map;
|
||||
if (duplicate_nodes.isEmpty()) {
|
||||
new_nodes = PasteNodesFromClipboard(nullptr, nullptr, &map);
|
||||
} else {
|
||||
new_nodes.resize(selected_nodes_.size());
|
||||
|
||||
for (int i=0; i<new_nodes.size(); i++) {
|
||||
Node *og = selected_nodes_.at(i);
|
||||
Node *copy = og->copy();
|
||||
Node::CopyInputs(og, copy, false);
|
||||
map.insert(copy, GetAssumedPositionForSelectedNode(og));
|
||||
new_nodes[i] = copy;
|
||||
}
|
||||
|
||||
Node::CopyDependencyGraph(selected_nodes_, new_nodes, nullptr);
|
||||
}
|
||||
|
||||
// If no nodes were retrieved, do nothing
|
||||
if (!new_nodes.isEmpty()) {
|
||||
QVector<AttachedItem> new_attached;
|
||||
|
||||
NodeViewItem *first_item = nullptr;
|
||||
|
||||
for (int i=0; i<new_nodes.size(); i++) {
|
||||
Node *node = new_nodes.at(i);
|
||||
|
||||
// Determine if item had a position, if not don't create an item for it
|
||||
NodeViewItem *new_item;
|
||||
|
||||
if (map.contains(node)) {
|
||||
new_item = new NodeViewItem(node, nullptr);
|
||||
new_item->SetFlowDirection(scene_.GetFlowDirection());
|
||||
new_item->SetNodePosition(map.value(node));
|
||||
scene_.addItem(new_item);
|
||||
|
||||
if (!first_item) {
|
||||
first_item = new_item;
|
||||
}
|
||||
} else {
|
||||
new_item = nullptr;
|
||||
}
|
||||
|
||||
new_attached.append({new_item, node, QPointF(0, 0)});
|
||||
}
|
||||
|
||||
// Correct positions
|
||||
if (first_item) {
|
||||
for (int i=0; i<new_attached.size(); i++) {
|
||||
AttachedItem &ai = new_attached[i];
|
||||
|
||||
if (ai.item) {
|
||||
ai.original_pos = first_item->pos() - ai.item->pos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetAttachedItems(new_attached);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::AddContext(Node *n)
|
||||
{
|
||||
NodeViewContext *ctx = scene_.AddContext(n);
|
||||
@@ -1449,6 +1370,48 @@ void NodeView::EndEdgeDrag(bool cancel)
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void NodeView::PostPaste(const QVector<Node *> &new_nodes, const Node::PositionMap &map)
|
||||
{
|
||||
QVector<AttachedItem> new_attached;
|
||||
|
||||
NodeViewItem *first_item = nullptr;
|
||||
|
||||
for (int i=0; i<new_nodes.size(); i++) {
|
||||
Node *node = new_nodes.at(i);
|
||||
|
||||
// Determine if item had a position, if not don't create an item for it
|
||||
NodeViewItem *new_item;
|
||||
|
||||
if (map.contains(node)) {
|
||||
new_item = new NodeViewItem(node, nullptr);
|
||||
new_item->SetFlowDirection(scene_.GetFlowDirection());
|
||||
new_item->SetNodePosition(map.value(node));
|
||||
scene_.addItem(new_item);
|
||||
|
||||
if (!first_item) {
|
||||
first_item = new_item;
|
||||
}
|
||||
} else {
|
||||
new_item = nullptr;
|
||||
}
|
||||
|
||||
new_attached.append({new_item, node, QPointF(0, 0)});
|
||||
}
|
||||
|
||||
// Correct positions
|
||||
if (first_item) {
|
||||
for (int i=0; i<new_attached.size(); i++) {
|
||||
AttachedItem &ai = new_attached[i];
|
||||
|
||||
if (ai.item) {
|
||||
ai.original_pos = first_item->pos() - ai.item->pos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetAttachedItems(new_attached);
|
||||
}
|
||||
|
||||
void NodeView::SetAttachedItems(const QVector<AttachedItem> &items)
|
||||
{
|
||||
// Detach anything currently attached
|
||||
|
||||
@@ -127,8 +127,8 @@ protected:
|
||||
|
||||
virtual bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &nodes, void* userdata) override;
|
||||
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override;
|
||||
virtual void CopyNodesToClipboardCallback(const QVector<Node *> &nodes, ProjectSerializer::SaveData *data, void* userdata) override;
|
||||
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &ldata, void *userdata) override;
|
||||
|
||||
virtual void changeEvent(QEvent *e) override;
|
||||
|
||||
@@ -155,8 +155,6 @@ private:
|
||||
|
||||
void PositionNewEdge(const QPoint &pos);
|
||||
|
||||
void PasteNodesInternal(const QVector<Node*> &duplicate_nodes = QVector<Node *>());
|
||||
|
||||
void AddContext(Node *n);
|
||||
|
||||
void RemoveContext(Node *n);
|
||||
@@ -169,6 +167,8 @@ private:
|
||||
|
||||
void EndEdgeDrag(bool cancel = false);
|
||||
|
||||
void PostPaste(const QVector<Node*> &new_nodes, const Node::PositionMap &map);
|
||||
|
||||
NodeViewMiniMap *minimap_;
|
||||
|
||||
struct AttachedItem {
|
||||
|
||||
@@ -300,55 +300,64 @@ void TimelineWidget::DisconnectNodeEvent(ViewerOutput *n)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node *> &nodes, void* userdata)
|
||||
void TimelineWidget::CopyNodesToClipboardCallback(const QVector<Node *> &nodes, ProjectSerializer::SaveData *sdata, void *userdata)
|
||||
{
|
||||
// Cache the earliest in point so all copied clips have a "relative" in point that can be pasted anywhere
|
||||
QVector<Block*>& selected = *static_cast<QVector<Block*>*>(userdata);
|
||||
rational earliest_in = RATIONAL_MAX;
|
||||
ProjectSerializer::SerializedProperties properties;
|
||||
|
||||
foreach (Block* block, selected) {
|
||||
earliest_in = qMin(earliest_in, block->in());
|
||||
}
|
||||
|
||||
foreach (Block* block, selected) {
|
||||
writer->writeStartElement(QStringLiteral("block"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(block)));
|
||||
writer->writeAttribute(QStringLiteral("in"), (block->in() - earliest_in).toString());
|
||||
|
||||
Track* track = block->track();
|
||||
writer->writeAttribute(QStringLiteral("tracktype"), QString::number(track->type()));
|
||||
writer->writeAttribute(QStringLiteral("trackindex"), QString::number(track->Index()));
|
||||
|
||||
writer->writeEndElement();
|
||||
properties[block][QStringLiteral("in")] = (block->in() - earliest_in).toString();
|
||||
properties[block][QStringLiteral("track")] = block->track()->ToReference().ToString();
|
||||
}
|
||||
|
||||
sdata->SetProperties(properties);
|
||||
}
|
||||
|
||||
void TimelineWidget::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData& xml_node_data, void *userdata)
|
||||
void TimelineWidget::PasteNodesToClipboardCallback(const QVector<Node *> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata)
|
||||
{
|
||||
QVector<BlockPasteData>& paste_data = *static_cast<QVector<BlockPasteData>*>(userdata);
|
||||
bool insert = *(bool*)userdata;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("block")) {
|
||||
BlockPasteData bpd;
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
foreach (QXmlStreamAttribute attr, reader->attributes()) {
|
||||
if (attr.name() == QStringLiteral("ptr")) {
|
||||
bpd.block = static_cast<Block*>(xml_node_data.node_ptrs.value(attr.value().toULongLong()));
|
||||
} else if (attr.name() == QStringLiteral("in")) {
|
||||
bpd.in = rational::fromString(attr.value().toString());
|
||||
} else if (attr.name() == QStringLiteral("tracktype")) {
|
||||
bpd.track_type = static_cast<Track::Type>(attr.value().toInt());
|
||||
} else if (attr.name() == QStringLiteral("trackindex")) {
|
||||
bpd.track_index = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
foreach (Node *n, nodes) {
|
||||
command->add_child(new NodeAddCommand(GetConnectedNode()->project(), n));
|
||||
}
|
||||
|
||||
paste_data.append(bpd);
|
||||
rational paste_start = GetTime();
|
||||
|
||||
reader->skipCurrentElement();
|
||||
if (insert) {
|
||||
rational paste_end = GetTime();
|
||||
|
||||
for (auto it=load_data.properties.cbegin(); it!=load_data.properties.cend(); it++) {
|
||||
rational length = static_cast<Block*>(it.key())->length();
|
||||
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
|
||||
|
||||
paste_end = qMax(paste_end, paste_start + in + length);
|
||||
}
|
||||
|
||||
if (paste_end != paste_start) {
|
||||
InsertGapsAt(paste_start, paste_end - paste_start, command);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=load_data.properties.cbegin(); it!=load_data.properties.cend(); it++) {
|
||||
Block *block = static_cast<Block*>(it.key());
|
||||
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
|
||||
Track::Reference track = Track::Reference::FromString(it.value()[QStringLiteral("track")]);
|
||||
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()),
|
||||
track.index(),
|
||||
block,
|
||||
paste_start + in));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void TimelineWidget::SelectAll()
|
||||
@@ -626,34 +635,7 @@ void TimelineWidget::Paste(bool insert)
|
||||
return;
|
||||
}
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
QVector<BlockPasteData> paste_data;
|
||||
QVector<Node*> pasted = PasteNodesFromClipboard(GetConnectedNode()->parent(), command, &paste_data);
|
||||
|
||||
rational paste_start = GetTime();
|
||||
|
||||
if (insert) {
|
||||
rational paste_end = GetTime();
|
||||
|
||||
foreach (const BlockPasteData& bpd, paste_data) {
|
||||
paste_end = qMax(paste_end, paste_start + bpd.in + bpd.block->length());
|
||||
}
|
||||
|
||||
if (paste_end != paste_start) {
|
||||
InsertGapsAt(paste_start, paste_end - paste_start, command);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const BlockPasteData& bpd, paste_data) {
|
||||
qDebug() << "Placing" << bpd.block;
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(bpd.track_type),
|
||||
bpd.track_index,
|
||||
bpd.block,
|
||||
paste_start + bpd.in));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
PasteNodesFromClipboard(&insert);
|
||||
}
|
||||
|
||||
void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
|
||||
@@ -272,15 +272,8 @@ protected:
|
||||
virtual void ConnectNodeEvent(ViewerOutput* n) override;
|
||||
virtual void DisconnectNodeEvent(ViewerOutput* n) override;
|
||||
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVector<Node*> &nodes, void* userdata) override;
|
||||
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override;
|
||||
|
||||
struct BlockPasteData {
|
||||
Block* block;
|
||||
rational in;
|
||||
Track::Type track_type;
|
||||
int track_index;
|
||||
};
|
||||
virtual void CopyNodesToClipboardCallback(const QVector<Node*> &nodes, ProjectSerializer::SaveData *data, void *userdata) override;
|
||||
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata) override;
|
||||
|
||||
private:
|
||||
QVector<Timeline::EditToInfo> GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode);
|
||||
|
||||
Reference in New Issue
Block a user