moved serialization

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