From f7396de1218b5ab634d920941db5331bfb021509 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 1 May 2022 22:33:58 -0700 Subject: [PATCH] markers: reimplemented copy/paste --- .../keyframeproperties/keyframeproperties.cpp | 18 +- .../keyframeproperties/keyframeproperties.h | 4 +- .../markerpropertiesdialog.cpp | 16 +- .../markerproperties/markerpropertiesdialog.h | 4 +- app/node/CMakeLists.txt | 2 - app/node/nodecopypaste.cpp | 91 --------- app/node/nodecopypaste.h | 57 ------ app/node/project/serializer/serializer.cpp | 61 ++++++ app/node/project/serializer/serializer.h | 25 ++- .../project/serializer/serializer220403.cpp | 193 +++++++++++------- .../project/serializer/serializer220403.h | 4 + app/task/project/load/load.cpp | 3 + app/task/project/save/save.cpp | 1 + app/timeline/timelinemarker.cpp | 6 + app/timeline/timelinemarker.h | 1 + app/widget/curvewidget/curveview.cpp | 12 +- app/widget/curvewidget/curvewidget.cpp | 14 +- app/widget/keyframeview/keyframeview.cpp | 10 +- app/widget/keyframeview/keyframeview.h | 2 +- app/widget/nodeview/nodeview.cpp | 93 +++++---- app/widget/nodeview/nodeview.h | 6 +- .../timebased/timebasedviewselectionmanager.h | 34 +-- app/widget/timelinewidget/timelinewidget.cpp | 135 ++++++------ app/widget/timelinewidget/timelinewidget.h | 6 +- app/widget/timeruler/seekablewidget.cpp | 71 +++++-- app/widget/timeruler/seekablewidget.h | 7 +- 26 files changed, 452 insertions(+), 424 deletions(-) delete mode 100644 app/node/nodecopypaste.cpp delete mode 100644 app/node/nodecopypaste.h diff --git a/app/dialog/keyframeproperties/keyframeproperties.cpp b/app/dialog/keyframeproperties/keyframeproperties.cpp index 9637c486f..65fbcd791 100644 --- a/app/dialog/keyframeproperties/keyframeproperties.cpp +++ b/app/dialog/keyframeproperties/keyframeproperties.cpp @@ -30,7 +30,7 @@ namespace olive { -KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector &keys, const rational &timebase, QWidget *parent) : +KeyframePropertiesDialog::KeyframePropertiesDialog(const std::vector &keys, const rational &timebase, QWidget *parent) : QDialog(parent), keys_(keys), timebase_(timebase) @@ -91,7 +91,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector bool all_same_bezier_out_x = true; bool all_same_bezier_out_y = true; - for (int i=0;i 0) { NodeKeyframe* prev_key = keys_.at(i-1); NodeKeyframe* this_key = keys_.at(i); @@ -126,7 +126,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector // Determine if any keyframes are on the same track (in which case we can't set the time) if (can_set_time) { - for (int j=0;jtrack() == keys_.at(i)->track()) { can_set_time = false; @@ -147,7 +147,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector } if (all_same_time) { - time_slider_->SetValue(keys_.first()->time()); + time_slider_->SetValue(keys_.front()->time()); } else { time_slider_->SetTristate(); } @@ -169,7 +169,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector if (all_same_type) { // If all keyframes are the same type, set it here for (int i=0;icount();i++) { - if (type_select_->itemData(i).toInt() == keys_.first()->type()) { + if (type_select_->itemData(i).toInt() == keys_.front()->type()) { type_select_->setCurrentIndex(i); // Ensure UI updates for this index @@ -179,10 +179,10 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector } } - SetUpBezierSlider(bezier_in_x_slider_, all_same_bezier_in_x, keys_.first()->bezier_control_in().x()); - SetUpBezierSlider(bezier_in_y_slider_, all_same_bezier_in_y, keys_.first()->bezier_control_in().y()); - SetUpBezierSlider(bezier_out_x_slider_, all_same_bezier_out_x, keys_.first()->bezier_control_out().x()); - SetUpBezierSlider(bezier_out_y_slider_, all_same_bezier_out_y, keys_.first()->bezier_control_out().y()); + SetUpBezierSlider(bezier_in_x_slider_, all_same_bezier_in_x, keys_.front()->bezier_control_in().x()); + SetUpBezierSlider(bezier_in_y_slider_, all_same_bezier_in_y, keys_.front()->bezier_control_in().y()); + SetUpBezierSlider(bezier_out_x_slider_, all_same_bezier_out_x, keys_.front()->bezier_control_out().x()); + SetUpBezierSlider(bezier_out_y_slider_, all_same_bezier_out_y, keys_.front()->bezier_control_out().y()); row++; diff --git a/app/dialog/keyframeproperties/keyframeproperties.h b/app/dialog/keyframeproperties/keyframeproperties.h index 835845943..418c09321 100644 --- a/app/dialog/keyframeproperties/keyframeproperties.h +++ b/app/dialog/keyframeproperties/keyframeproperties.h @@ -35,7 +35,7 @@ class KeyframePropertiesDialog : public QDialog { Q_OBJECT public: - KeyframePropertiesDialog(const QVector& keys, const rational& timebase, QWidget* parent = nullptr); + KeyframePropertiesDialog(const std::vector& keys, const rational& timebase, QWidget* parent = nullptr); public slots: virtual void accept() override; @@ -43,7 +43,7 @@ public slots: private: void SetUpBezierSlider(FloatSlider *slider, bool all_same, double value); - const QVector& keys_; + const std::vector& keys_; rational timebase_; diff --git a/app/dialog/markerproperties/markerpropertiesdialog.cpp b/app/dialog/markerproperties/markerpropertiesdialog.cpp index 5473af3ab..007625f8f 100644 --- a/app/dialog/markerproperties/markerpropertiesdialog.cpp +++ b/app/dialog/markerproperties/markerpropertiesdialog.cpp @@ -32,7 +32,7 @@ namespace olive { #define super QDialog -MarkerPropertiesDialog::MarkerPropertiesDialog(const QVector &markers, const rational &timebase, QWidget *parent) : +MarkerPropertiesDialog::MarkerPropertiesDialog(const std::vector &markers, const rational &timebase, QWidget *parent) : super(parent), markers_(markers) { @@ -60,10 +60,10 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(const QVector & } if (markers.size() == 1) { - in_slider_->SetValue(markers.first()->time_range().in()); + in_slider_->SetValue(markers.front()->time_range().in()); in_slider_->SetDisplayType(RationalSlider::kTime); in_slider_->SetTimebase(timebase); - out_slider_->SetValue(markers.first()->time_range().out()); + out_slider_->SetValue(markers.front()->time_range().out()); out_slider_->SetDisplayType(RationalSlider::kTime); out_slider_->SetTimebase(timebase); } else { @@ -83,8 +83,8 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(const QVector & color_menu_ = new ColorCodingComboBox(); layout->addWidget(color_menu_, row, 1); - color_menu_->SetColor(markers.first()->color()); - for (int i=1; iSetColor(markers.front()->color()); + for (size_t i=1; icolor() != color_menu_->GetSelectedColor()) { color_menu_->SetColor(-1); break; @@ -102,8 +102,8 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(const QVector & layout->addWidget(label_edit_, row, 1); // Determine what the startup label text should be - label_edit_->setText(markers.first()->name()); - for (int i=1; isetText(markers.front()->name()); + for (size_t i=1; iname() != label_edit_->text()) { label_edit_->clear(); label_edit_->setPlaceholderText(tr("(multiple)")); @@ -141,7 +141,7 @@ void MarkerPropertiesDialog::accept() } if (markers_.size() == 1) { - command->add_child(new MarkerChangeTimeCommand(markers_.first(), TimeRange(in_slider_->GetValue(), out_slider_->GetValue()))); + command->add_child(new MarkerChangeTimeCommand(markers_.front(), TimeRange(in_slider_->GetValue(), out_slider_->GetValue()))); } Core::instance()->undo_stack()->pushIfHasChildren(command); diff --git a/app/dialog/markerproperties/markerpropertiesdialog.h b/app/dialog/markerproperties/markerpropertiesdialog.h index 218339af1..a358dc9a7 100644 --- a/app/dialog/markerproperties/markerpropertiesdialog.h +++ b/app/dialog/markerproperties/markerpropertiesdialog.h @@ -55,13 +55,13 @@ class MarkerPropertiesDialog : public QDialog { Q_OBJECT public: - MarkerPropertiesDialog(const QVector &markers, const rational &timebase, QWidget *parent = nullptr); + MarkerPropertiesDialog(const std::vector &markers, const rational &timebase, QWidget *parent = nullptr); public slots: virtual void accept() override; private: - QVector markers_; + std::vector markers_; LineEditWithFocusSignal *label_edit_; diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index cd510b021..3b66b8129 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -48,8 +48,6 @@ set(OLIVE_SOURCES node/keyframe.h node/node.cpp node/node.h - node/nodecopypaste.cpp - node/nodecopypaste.h node/param.cpp node/param.h node/splitvalue.h diff --git a/app/node/nodecopypaste.cpp b/app/node/nodecopypaste.cpp deleted file mode 100644 index 7741f9940..000000000 --- a/app/node/nodecopypaste.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "nodecopypaste.h" - -#include - -#include "core.h" -#include "node/factory.h" -#include "widget/nodeview/nodeviewundo.h" -#include "window/mainwindow/mainwindow.h" - -namespace olive { - -void NodeCopyPasteService::CopyNodesToClipboard(QVector nodes, void *userdata) -{ - QString copy_str; - - QXmlStreamWriter writer(©_str); - - // For any groups, add children - for (int i=0; i(nodes.at(i))) { - for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) { - if (!nodes.contains(it.key())) { - nodes.append(it.key()); - } - } - } - } - - ProjectSerializer::SaveData data(nodes.first()->project(), QString(), nodes); - - CopyNodesToClipboardCallback(nodes, &data, userdata); - - ProjectSerializer::Save(&writer, data, type_); - - Core::CopyStringToClipboard(copy_str); -} - -void NodeCopyPasteService::PasteNodesFromClipboard(void *userdata) -{ - QString clipboard = Core::PasteStringFromClipboard(); - if (clipboard.isEmpty()) { - return; - } - - QXmlStreamReader reader(clipboard); - - Project temp; - ProjectSerializer::Result res = ProjectSerializer::Load(&temp, &reader, type_); - - if (res.code() != ProjectSerializer::kSuccess) { - return; - } - - QVector pasted_nodes; - foreach (Node *n, temp.nodes()) { - if (!temp.default_nodes().contains(n)) { - // Move nodes out of Project - n->setParent(nullptr); - pasted_nodes.append(n); - } - } - - if (pasted_nodes.isEmpty()) { - return; - } - - PasteNodesToClipboardCallback(pasted_nodes, res.GetLoadData(), userdata); -} - -} diff --git a/app/node/nodecopypaste.h b/app/node/nodecopypaste.h deleted file mode 100644 index eaf3c2b86..000000000 --- a/app/node/nodecopypaste.h +++ /dev/null @@ -1,57 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef NODECOPYPASTEWIDGET_H -#define NODECOPYPASTEWIDGET_H - -#include -#include - -#include "node/node.h" -#include "node/project/project.h" -#include "node/project/sequence/sequence.h" -#include "node/project/serializer/serializer.h" - -namespace olive { - -class NodeCopyPasteService -{ -public: - NodeCopyPasteService(const QString &type) : - type_(type) - {} - -protected: - void CopyNodesToClipboard(QVector nodes, void* userdata = nullptr); - - void PasteNodesFromClipboard(void* userdata = nullptr); - - virtual void CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *data, void *userdata){} - - virtual void PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &load_data, void *userdata){} - -private: - QString type_; - -}; - -} - -#endif // NODECOPYPASTEWIDGET_H diff --git a/app/node/project/serializer/serializer.cpp b/app/node/project/serializer/serializer.cpp index 5f3a3507e..67bd879f7 100644 --- a/app/node/project/serializer/serializer.cpp +++ b/app/node/project/serializer/serializer.cpp @@ -118,6 +118,36 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamRe return res; } +ProjectSerializer::Result ProjectSerializer::Paste(const QString &type) +{ + QString clipboard = Core::PasteStringFromClipboard(); + if (clipboard.isEmpty()) { + return kNoData; + } + + QXmlStreamReader reader(clipboard); + + Project temp; + ProjectSerializer::Result res = ProjectSerializer::Load(&temp, &reader, type); + + if (res.code() != ProjectSerializer::kSuccess) { + return res; + } + + QVector pasted_nodes; + foreach (Node *n, temp.nodes()) { + if (!temp.default_nodes().contains(n)) { + // Move nodes out of Project + n->setParent(nullptr); + pasted_nodes.append(n); + } + } + + res.SetLoadedNodes(pasted_nodes); + + return res; +} + ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QString &type) { QString temp_save = FileFunctions::GetSafeTemporaryFilename(data.GetFilename()); @@ -187,6 +217,20 @@ ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, cons return kSuccess; } +ProjectSerializer::Result ProjectSerializer::Copy(const SaveData &data, const QString &type) +{ + QString copy_str; + QXmlStreamWriter writer(©_str); + + ProjectSerializer::Result res = ProjectSerializer::Save(&writer, data, type); + + if (res == kSuccess) { + Core::CopyStringToClipboard(copy_str); + } + + return res; +} + bool ProjectSerializer::IsCancelled() const { return false; @@ -228,4 +272,21 @@ ProjectSerializer::Result ProjectSerializer::LoadWithSerializerVersion(uint vers } } +void ProjectSerializer::SaveData::SetOnlySerializeNodesAndResolveGroups(QVector nodes) +{ + // For any groups, add children + for (int i=0; i(nodes.at(i))) { + for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) { + if (!nodes.contains(it.key())) { + nodes.append(it.key()); + } + } + } + } + + SetOnlySerializeNodes(nodes); +} + } diff --git a/app/node/project/serializer/serializer.h b/app/node/project/serializer/serializer.h index 79968ea43..cd6b99082 100644 --- a/app/node/project/serializer/serializer.h +++ b/app/node/project/serializer/serializer.h @@ -50,7 +50,8 @@ public: kUnknownVersion, kFileError, kXmlError, - kOverwriteError + kOverwriteError, + kNoData }; using SerializedProperties = QHash >; @@ -62,6 +63,8 @@ public: SerializedProperties properties; + std::vector markers; + }; class Result @@ -84,6 +87,10 @@ public: void SetLoadData(const LoadData &p) { load_data_ = p; } + const QVector &GetLoadedNodes() const { return loaded_nodes_; } + + void SetLoadedNodes(const QVector &n) { loaded_nodes_ = n; } + private: ResultCode code_; @@ -91,17 +98,17 @@ public: LoadData load_data_; + QVector loaded_nodes_; + }; class SaveData { public: - SaveData(Project *project, const QString &filename, const QVector &only = QVector(), const SerializedProperties &p = SerializedProperties()) + SaveData(Project *project, const QString &filename = QString()) { project_ = project; filename_ = filename; - only_serialize_nodes_ = only; - properties_ = p; } Project *GetProject() const @@ -115,11 +122,13 @@ public: } const QVector &GetOnlySerializeNodes() const { return only_serialize_nodes_; } - void SetOnlySerializeNodes(const QVector &only) { only_serialize_nodes_ = only; } + void SetOnlySerializeNodesAndResolveGroups(QVector only); + + const std::vector &GetOnlySerializeMarkers() const { return only_serialize_markers_; } + void SetOnlySerializeMarkers(const std::vector &only) { only_serialize_markers_ = only; } const SerializedProperties &GetProperties() const { return properties_; } - void SetProperties(const SerializedProperties &p) { properties_ = p; } private: @@ -131,6 +140,8 @@ public: SerializedProperties properties_; + std::vector only_serialize_markers_; + }; static void Initialize(); @@ -139,9 +150,11 @@ public: static Result Load(Project *project, const QString &filename, const QString &type); static Result Load(Project *project, QXmlStreamReader *read_device, const QString &type); + static Result Paste(const QString &type); static Result Save(const SaveData &data, const QString &type); static Result Save(QXmlStreamWriter *write_device, const SaveData &data, const QString &type); + static Result Copy(const SaveData &data, const QString &type); protected: virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const = 0; diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index aa80921dd..c64e420bf 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -31,6 +31,8 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project QMap > positions; XMLNodeData xml_node_data; + LoadData load_data; + while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("layout")) { @@ -97,6 +99,18 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project } } + } else if (reader->name() == QStringLiteral("markers")) { + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("marker")) { + TimelineMarker *marker = new TimelineMarker(); + LoadMarker(reader, marker); + load_data.markers.push_back(marker); + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("positions")) { while (XMLReadNextStartElement(reader)) { @@ -194,8 +208,6 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project // 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()); @@ -211,74 +223,94 @@ void ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &dat { Project *project = data.GetProject(); - writer->writeTextElement(QStringLiteral("uuid"), data.GetProject()->GetUuid().toString()); + if (!data.GetOnlySerializeMarkers().empty()) { - writer->writeStartElement(QStringLiteral("nodes")); + writer->writeStartElement(QStringLiteral("markers")); - const QVector &using_node_list = (data.GetOnlySerializeNodes().isEmpty()) ? project->nodes() : data.GetOnlySerializeNodes(); + for (auto it=data.GetOnlySerializeMarkers().cbegin(); it!=data.GetOnlySerializeMarkers().cend(); it++) { + TimelineMarker *marker = *it; - foreach (Node* node, using_node_list) { - writer->writeStartElement(QStringLiteral("node")); + writer->writeStartElement(QStringLiteral("marker")); - 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")); + SaveMarker(writer, marker); + + writer->writeEndElement(); // marker } - writer->writeAttribute(QStringLiteral("id"), node->id()); + writer->writeEndElement(); // markers - SaveNode(node, writer); + } else { - writer->writeEndElement(); // node - } + writer->writeTextElement(QStringLiteral("uuid"), data.GetProject()->GetUuid().toString()); - writer->writeEndElement(); // nodes + writer->writeStartElement(QStringLiteral("nodes")); - writer->writeStartElement(QStringLiteral("positions")); + const QVector &using_node_list = (data.GetOnlySerializeNodes().isEmpty()) ? project->nodes() : data.GetOnlySerializeNodes(); - foreach (Node* context, using_node_list) { - const Node::PositionMap &map = context->GetContextPositions(); + foreach (Node* node, using_node_list) { + writer->writeStartElement(QStringLiteral("node")); - if (!map.isEmpty()) { - writer->writeStartElement(QStringLiteral("context")); - - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(context))); - - for (auto jt=map.cbegin(); jt!=map.cend(); jt++) { - if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) { - writer->writeStartElement(QStringLiteral("node")); - SavePosition(writer, jt.key(), jt.value()); - writer->writeEndElement(); // node - } + 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->writeEndElement(); // context - } - } + writer->writeAttribute(QStringLiteral("id"), node->id()); - writer->writeEndElement(); // positions + SaveNode(node, writer); - writer->writeStartElement(QStringLiteral("properties")); - - for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) { - writer->writeStartElement(QStringLiteral("node")); - - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(it.key()))); - - for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) { - writer->writeTextElement(jt.key(), jt.value()); + writer->writeEndElement(); // node } - writer->writeEndElement(); // node + writer->writeEndElement(); // nodes + + writer->writeStartElement(QStringLiteral("positions")); + + foreach (Node* context, using_node_list) { + const Node::PositionMap &map = context->GetContextPositions(); + + if (!map.isEmpty()) { + writer->writeStartElement(QStringLiteral("context")); + + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(context))); + + for (auto jt=map.cbegin(); jt!=map.cend(); jt++) { + if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) { + writer->writeStartElement(QStringLiteral("node")); + SavePosition(writer, jt.key(), jt.value()); + writer->writeEndElement(); // node + } + } + + writer->writeEndElement(); // context + } + } + + writer->writeEndElement(); // positions + + writer->writeStartElement(QStringLiteral("properties")); + + for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) { + writer->writeStartElement(QStringLiteral("node")); + + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(it.key()))); + + for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) { + writer->writeTextElement(jt.key(), jt.value()); + } + + writer->writeEndElement(); // node + } + + writer->writeEndElement(); // properties + + // Save main window project layout + project->GetLayoutInfo().toXml(writer); + } - - writer->writeEndElement(); // properties - - // Save main window project layout - project->GetLayoutInfo().toXml(writer); } void ProjectSerializer220403::LoadNode(Node *node, XMLNodeData &xml_node_data, QXmlStreamReader *reader) const @@ -954,6 +986,36 @@ void ProjectSerializer220403::SaveTimelinePoints(QXmlStreamWriter *writer, Timel writer->writeEndElement(); // markers } +void ProjectSerializer220403::LoadMarker(QXmlStreamReader *reader, TimelineMarker *marker) const +{ + rational in, out; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("name")) { + marker->set_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()); + } else if (attr.name() == QStringLiteral("color")) { + marker->set_color(attr.value().toInt()); + } + } + + marker->set_time(TimeRange(in, out)); + + // This element has no inner text, so just skip it + reader->skipCurrentElement(); +} + +void ProjectSerializer220403::SaveMarker(QXmlStreamWriter *writer, TimelineMarker *marker) const +{ + writer->writeAttribute(QStringLiteral("name"), marker->name()); + writer->writeAttribute(QStringLiteral("in"), marker->time_range().in().toString()); + writer->writeAttribute(QStringLiteral("out"), marker->time_range().out().toString()); + writer->writeAttribute(QStringLiteral("color"), QString::number(marker->color())); +} + void ProjectSerializer220403::LoadWorkArea(QXmlStreamReader *reader, TimelineWorkArea *workarea) const { rational range_in = workarea->in(); @@ -989,26 +1051,11 @@ void ProjectSerializer220403::LoadMarkerList(QXmlStreamReader *reader, TimelineM { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("marker")) { - QString name; - rational in, out; - int color = Config::Current()[QStringLiteral("MarkerColor")].toInt(); - - 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()); - } else if (attr.name() == QStringLiteral("color")) { - color = attr.value().toInt(); - } - } - - new TimelineMarker(color, TimeRange(in, out), name, markers); + TimelineMarker *marker = new TimelineMarker(markers); + LoadMarker(reader, marker); + } else { + reader->skipCurrentElement(); } - - reader->skipCurrentElement(); } } @@ -1019,11 +1066,7 @@ void ProjectSerializer220403::SaveMarkerList(QXmlStreamWriter *writer, TimelineM writer->writeStartElement(QStringLiteral("marker")); - writer->writeAttribute(QStringLiteral("name"), marker->name()); - - writer->writeAttribute(QStringLiteral("in"), marker->time_range().in().toString()); - writer->writeAttribute(QStringLiteral("out"), marker->time_range().out().toString()); - writer->writeAttribute(QStringLiteral("color"), QString::number(marker->color())); + SaveMarker(writer, marker); writer->writeEndElement(); // marker } diff --git a/app/node/project/serializer/serializer220403.h b/app/node/project/serializer/serializer220403.h index f6a1b939f..16de30679 100644 --- a/app/node/project/serializer/serializer220403.h +++ b/app/node/project/serializer/serializer220403.h @@ -100,6 +100,10 @@ private: void SaveTimelinePoints(QXmlStreamWriter *writer, TimelinePoints *points) 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; diff --git a/app/task/project/load/load.cpp b/app/task/project/load/load.cpp index f1878dec9..17497723c 100644 --- a/app/task/project/load/load.cpp +++ b/app/task/project/load/load.cpp @@ -57,6 +57,9 @@ bool ProjectLoadTask::Run() case ProjectSerializer::kXmlError: SetError(tr("Failed to read XML document. File may be corrupt. Error was: %1").arg(result.GetDetails())); break; + case ProjectSerializer::kNoData: + SetError(tr("Failed to find any data to parse.")); + break; // Errors that should never be thrown by a load case ProjectSerializer::kOverwriteError: diff --git a/app/task/project/save/save.cpp b/app/task/project/save/save.cpp index 3d4c5f6e5..24c95b665 100644 --- a/app/task/project/save/save.cpp +++ b/app/task/project/save/save.cpp @@ -66,6 +66,7 @@ bool ProjectSaveTask::Run() case ProjectSerializer::kProjectTooNew: case ProjectSerializer::kProjectTooOld: case ProjectSerializer::kUnknownVersion: + case ProjectSerializer::kNoData: SetError(tr("Unknown error.")); break; } diff --git a/app/timeline/timelinemarker.cpp b/app/timeline/timelinemarker.cpp index bb40b7790..32eb98976 100644 --- a/app/timeline/timelinemarker.cpp +++ b/app/timeline/timelinemarker.cpp @@ -28,6 +28,12 @@ namespace olive { +TimelineMarker::TimelineMarker(QObject *parent) : + color_(Config::Current()[QStringLiteral("MarkerColor")].toInt()) +{ + setParent(parent); +} + TimelineMarker::TimelineMarker(int color, const TimeRange &time, const QString &name, QObject *parent) : time_(time), name_(name), diff --git a/app/timeline/timelinemarker.h b/app/timeline/timelinemarker.h index d55af84bf..1389da26e 100644 --- a/app/timeline/timelinemarker.h +++ b/app/timeline/timelinemarker.h @@ -35,6 +35,7 @@ class TimelineMarker : public QObject { Q_OBJECT public: + TimelineMarker(QObject* parent = nullptr); TimelineMarker(int color, const TimeRange& time, const QString& name = QString(), QObject* parent = nullptr); const rational &time() const { return time_.in(); } diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index e756d7376..49748e5d2 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -406,7 +406,7 @@ void CurveView::FirstChanceMouseRelease(QMouseEvent *event) void CurveView::KeyframeDragStart(QMouseEvent *event) { drag_keyframe_values_.resize(GetSelectedKeyframes().size()); - for (int i=0; ivalue(); } @@ -418,7 +418,7 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) { if (event->modifiers() & Qt::ShiftModifier) { // Lock to X axis only and set original values on all keys - for (int i=0; iset_value(drag_keyframe_values_.at(i)); } @@ -429,7 +429,7 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) double scaled_diff = (mapToScene(event->pos()).y() - drag_start_.y()) / GetYScale(); // Validate movement - ensure no keyframe goes above its max point or below its min point - for (int i=0; iset_value(FloatSlider::TransformDisplayToValue(FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).toDouble(), display) - scaled_diff, display)); } - NodeKeyframe *tip_item = GetSelectedKeyframes().first(); + NodeKeyframe *tip_item = GetSelectedKeyframes().front(); bool ok; double num_value = tip_item->value().toDouble(&ok); @@ -472,7 +472,7 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) void CurveView::KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command) { - for (int i=0; ivalue().toDouble(), drag_keyframe_values_.at(i).toDouble())) { command->add_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i))); diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index 4d56896ae..e75d6746b 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -251,16 +251,16 @@ void CurveWidget::ConnectInputInternal(Node *node, const QString &input, int ele void CurveWidget::SelectionChanged() { - const QVector &selected = view_->GetSelectedKeyframes(); + const std::vector &selected = view_->GetSelectedKeyframes(); SetKeyframeButtonChecked(false); - SetKeyframeButtonEnabled(!selected.isEmpty()); + SetKeyframeButtonEnabled(!selected.empty()); - if (!selected.isEmpty()) { + if (!selected.empty()) { bool all_same_type = true; - NodeKeyframe::Type type = selected.first()->type(); + NodeKeyframe::Type type = selected.front()->type(); - for (int i=1;i &selected = view_->GetSelectedKeyframes(); - if (selected.isEmpty()) { + const std::vector &selected = view_->GetSelectedKeyframes(); + if (selected.empty()) { return; } diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index 0d5241e3c..16d845352 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -462,11 +462,11 @@ void KeyframeView::ShowContextMenu() QAction* bezier_key_action = nullptr; QAction* hold_key_action = nullptr; - if (!GetSelectedKeyframes().isEmpty()) { + if (!GetSelectedKeyframes().empty()) { bool all_keys_are_same_type = true; - NodeKeyframe::Type type = GetSelectedKeyframes().first()->type(); + NodeKeyframe::Type type = GetSelectedKeyframes().front()->type(); - for (int i=1;i &GetSelectedKeyframes() const + const std::vector &GetSelectedKeyframes() const { return selection_manager_.GetSelectedObjects(); } diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index c5bcb355a..07251995d 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -32,6 +32,7 @@ #include "node/distort/transform/transformdistortnode.h" #include "node/factory.h" #include "node/group/group.h" +#include "node/project/serializer/serializer.h" #include "node/traverser.h" #include "ui/icons/icons.h" #include "widget/menu/menushared.h" @@ -45,7 +46,6 @@ const double NodeView::kMinimumScale = 0.1; NodeView::NodeView(QWidget *parent) : HandMovableView(parent), - NodeCopyPasteService(QStringLiteral("nodes")), drop_edge_(nullptr), create_edge_(nullptr), create_edge_output_item_(nullptr), @@ -204,7 +204,31 @@ void NodeView::CopySelected(bool cut) return; } - CopyNodesToClipboard(selected_nodes_); + QString copy_str; + QXmlStreamWriter writer(©_str); + + ProjectSerializer::SaveData sdata(selected_nodes_.first()->project()); + sdata.SetOnlySerializeNodesAndResolveGroups(selected_nodes_); + + ProjectSerializer::SerializedProperties properties; + + for (Node *n : selected_nodes_) { + NodeViewItem *item = GetAssumedItemForSelectedNode(n); + + if (item) { + Node::Position pos = item->GetNodePositionData(); + + 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); + } + } + + sdata.SetProperties(properties); + + ProjectSerializer::Save(&writer, sdata, QStringLiteral("nodes")); + + Core::CopyStringToClipboard(copy_str); if (cut) { DeleteSelected(); @@ -213,9 +237,31 @@ void NodeView::CopySelected(bool cut) void NodeView::Paste() { - if (!contexts_.isEmpty()) { - PasteNodesFromClipboard(); + if (contexts_.isEmpty()) { + return; } + + ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("nodes")); + if (res.GetLoadedNodes().isEmpty()) { + return; + } + + Node::PositionMap map; + + for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) { + Node::Position pos; + + const QMap &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(); + + qDebug() << it.key() << pos.position; + + map.insert(it.key(), pos); + } + + PostPaste(res.GetLoadedNodes(), map); } void NodeView::Duplicate() @@ -1007,43 +1053,6 @@ bool NodeView::eventFilter(QObject *object, QEvent *event) return super::eventFilter(object, event); } -void NodeView::CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *sdata, void *userdata) -{ - ProjectSerializer::SerializedProperties properties; - - for (Node *n : nodes) { - NodeViewItem *item = GetAssumedItemForSelectedNode(n); - - if (item) { - Node::Position pos = item->GetNodePositionData(); - - 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); - } - } - - sdata->SetProperties(properties); -} - -void NodeView::PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &ldata, void *userdata) -{ - Node::PositionMap map; - - for (auto it=ldata.properties.cbegin(); it!=ldata.properties.cend(); it++) { - Node::Position pos; - - const QMap &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(); - - map.insert(it.key(), pos); - } - - PostPaste(nodes, map); -} - void NodeView::changeEvent(QEvent *e) { // Add translation code @@ -1548,7 +1557,7 @@ void NodeView::PostPaste(const QVector &new_nodes, const Node::PositionM AttachedItem &ai = new_attached[i]; if (ai.item) { - ai.original_pos = first_item->pos() - ai.item->pos(); + ai.original_pos = ai.item->pos() - first_item->pos(); } } } diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 3c62818db..26894511e 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -26,7 +26,6 @@ #include "core.h" #include "node/graph.h" -#include "node/nodecopypaste.h" #include "nodeviewedge.h" #include "nodeviewcontext.h" #include "nodeviewminimap.h" @@ -42,7 +41,7 @@ namespace olive { * This widget takes a NodeGraph object and constructs a QGraphicsScene representing its data, viewing and allowing * the user to make modifications to it. */ -class NodeView : public HandMovableView, public NodeCopyPasteService +class NodeView : public HandMovableView { Q_OBJECT public: @@ -139,9 +138,6 @@ protected: virtual bool eventFilter(QObject *object, QEvent *event) override; - virtual void CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *data, void* userdata) override; - virtual void PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &ldata, void *userdata) override; - virtual void changeEvent(QEvent *e) override; private: diff --git a/app/widget/timebased/timebasedviewselectionmanager.h b/app/widget/timebased/timebasedviewselectionmanager.h index cc214e8dd..d13d4754e 100644 --- a/app/widget/timebased/timebasedviewselectionmanager.h +++ b/app/widget/timebased/timebasedviewselectionmanager.h @@ -48,7 +48,7 @@ public: void DeclareDrawnObject(T *object, const QRectF &pos) { - drawn_objects_.append({object, pos}); + drawn_objects_.push_back({object, pos}); } bool Select(T *key) @@ -56,7 +56,7 @@ public: Q_ASSERT(key); if (!IsSelected(key)) { - selected_.append(key); + selected_.push_back(key); return true; } @@ -67,7 +67,13 @@ public: { Q_ASSERT(key); - return selected_.removeOne(key); + auto it = std::find(selected_.cbegin(), selected_.cend(), key); + if (it == selected_.cend()) { + return false; + } else { + selected_.erase(it); + return true; + } } void ClearSelection() @@ -77,10 +83,10 @@ public: bool IsSelected(T *key) const { - return selected_.contains(key); + return std::find(selected_.cbegin(), selected_.cend(), key) != selected_.cend(); } - const QVector &GetSelectedObjects() const + const std::vector &GetSelectedObjects() const { return selected_; } @@ -142,7 +148,7 @@ public: bool IsDragging() const { - return !dragging_.isEmpty(); + return !dragging_.empty(); } void DragStart(T *initial_item, QMouseEvent *event) @@ -150,7 +156,7 @@ public: initial_drag_item_ = initial_item; dragging_.resize(selected_.size()); - for (int i=0; itime(); @@ -164,7 +170,7 @@ public: rational time_diff = view_->SceneToTimeNoGrid(view_->mapToScene(event->pos()).x() - drag_mouse_start_.x()); // Validate movement - for (int i=0; iset_time(dragging_.at(i) + time_diff); } @@ -219,7 +225,7 @@ public: { QToolTip::hideText(); - for (int i=0; iadd_child(new SetTimeCommand(selected_.at(i), selected_.at(i)->time(), dragging_.at(i))); } @@ -314,11 +320,11 @@ private: TimeBasedView *view_; using DrawnObject = QPair; - QVector drawn_objects_; + std::vector drawn_objects_; - QVector selected_; + std::vector selected_; - QVector dragging_; + std::vector dragging_; T *initial_drag_item_; @@ -328,7 +334,7 @@ private: QRubberBand *rubberband_; QPoint rubberband_start_; - QVector rubberband_preselected_; + std::vector rubberband_preselected_; }; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 273aa7446..f62938346 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -31,6 +31,7 @@ #include "dialog/sequence/sequence.h" #include "dialog/speedduration/speeddurationdialog.h" #include "node/block/transition/transition.h" +#include "node/project/serializer/serializer.h" #include "tool/add.h" #include "tool/beam.h" #include "tool/edit.h" @@ -60,7 +61,6 @@ namespace olive { TimelineWidget::TimelineWidget(QWidget *parent) : super(true, true, parent), - NodeCopyPasteService(QStringLiteral("timeline")), rubberband_(QRubberBand::Rectangle, this), active_tool_(nullptr), use_audio_time_units_(false), @@ -312,66 +312,6 @@ void TimelineWidget::DisconnectNodeEvent(ViewerOutput *n) } } -void TimelineWidget::CopyNodesToClipboardCallback(const QVector &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& selected = *static_cast*>(userdata); - rational earliest_in = RATIONAL_MAX; - ProjectSerializer::SerializedProperties properties; - - foreach (Block* block, selected) { - earliest_in = qMin(earliest_in, block->in()); - } - - foreach (Block* block, selected) { - properties[block][QStringLiteral("in")] = (block->in() - earliest_in).toString(); - properties[block][QStringLiteral("track")] = block->track()->ToReference().ToString(); - } - - sdata->SetProperties(properties); -} - -void TimelineWidget::PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &load_data, void *userdata) -{ - bool insert = *(bool*)userdata; - - MultiUndoCommand *command = new MultiUndoCommand(); - - foreach (Node *n, nodes) { - command->add_child(new NodeAddCommand(GetConnectedNode()->project(), n)); - } - - rational paste_start = GetTime(); - - if (insert) { - rational paste_end = GetTime(); - - for (auto it=load_data.properties.cbegin(); it!=load_data.properties.cend(); it++) { - rational length = static_cast(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(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() { QVector newly_selected_blocks; @@ -633,20 +573,17 @@ void TimelineWidget::CopySelected(bool cut) return; } - if (ruler()->hasFocus()) { - ruler()->CopySelected(cut); + if (ruler()->hasFocus() && ruler()->CopySelected(cut)) { return; } - QVector selected = GetSelectedBlocks(); - - if (selected.isEmpty()) { + if (selected_blocks_.isEmpty()) { return; } QVector selected_nodes; - foreach (Block* block, selected) { + foreach (Block* block, selected_blocks_) { selected_nodes.append(block); QVector deps = block->GetDependencies(); @@ -658,7 +595,25 @@ void TimelineWidget::CopySelected(bool cut) } } - CopyNodesToClipboard(selected_nodes, &selected); + ProjectSerializer::SaveData sdata(selected_nodes.first()->project()); + sdata.SetOnlySerializeNodesAndResolveGroups(selected_nodes); + + // Cache the earliest in point so all copied clips have a "relative" in point that can be pasted anywhere + rational earliest_in = RATIONAL_MAX; + ProjectSerializer::SerializedProperties properties; + + foreach (Block* block, selected_blocks_) { + earliest_in = qMin(earliest_in, block->in()); + } + + foreach (Block* block, selected_blocks_) { + properties[block][QStringLiteral("in")] = (block->in() - earliest_in).toString(); + properties[block][QStringLiteral("track")] = block->track()->ToReference().ToString(); + } + + sdata.SetProperties(properties); + + ProjectSerializer::Copy(sdata, QStringLiteral("timeline")); if (cut) { DeleteSelected(); @@ -671,12 +626,50 @@ void TimelineWidget::Paste(bool insert) return; } - if (ruler()->underMouse()) { - ruler()->PasteMarkers(insert, GetTime()); + if (ruler()->hasFocus() && ruler()->PasteMarkers(insert, GetTime())) { return; } - PasteNodesFromClipboard(&insert); + ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("timeline")); + if (res.GetLoadedNodes().isEmpty()) { + return; + } + + MultiUndoCommand *command = new MultiUndoCommand(); + + foreach (Node *n, res.GetLoadedNodes()) { + command->add_child(new NodeAddCommand(GetConnectedNode()->project(), n)); + } + + rational paste_start = GetTime(); + + if (insert) { + rational paste_end = GetTime(); + + for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) { + rational length = static_cast(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=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) { + Block *block = static_cast(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::DeleteInToOut(bool ripple) diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index e96b657a4..b19159403 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -27,7 +27,6 @@ #include "core.h" #include "node/block/transition/transition.h" -#include "node/nodecopypaste.h" #include "node/output/viewer/viewer.h" #include "timeline/timelinecommon.h" #include "timelineandtrackview.h" @@ -45,7 +44,7 @@ namespace olive { * * Encapsulates TimelineViews, TimeRulers, and scrollbars for a complete widget to manipulate Timelines */ -class TimelineWidget : public TimeBasedWidget, public NodeCopyPasteService, public SnapService +class TimelineWidget : public TimeBasedWidget, public SnapService { Q_OBJECT public: @@ -277,9 +276,6 @@ protected: virtual void ConnectNodeEvent(ViewerOutput* n) override; virtual void DisconnectNodeEvent(ViewerOutput* n) override; - virtual void CopyNodesToClipboardCallback(const QVector &nodes, ProjectSerializer::SaveData *data, void *userdata) override; - virtual void PasteNodesToClipboardCallback(const QVector &nodes, const ProjectSerializer::LoadData &load_data, void *userdata) override; - private: QVector GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode); diff --git a/app/widget/timeruler/seekablewidget.cpp b/app/widget/timeruler/seekablewidget.cpp index fbbe714b9..c23a010c5 100644 --- a/app/widget/timeruler/seekablewidget.cpp +++ b/app/widget/timeruler/seekablewidget.cpp @@ -28,6 +28,7 @@ #include "common/qtutils.h" #include "core.h" #include "dialog/markerproperties/markerpropertiesdialog.h" +#include "node/project/serializer/serializer.h" #include "widget/colorlabelmenu/colorlabelmenu.h" #include "widget/menu/menushared.h" #include "widget/timebased/timebasedwidget.h" @@ -38,7 +39,6 @@ namespace olive { SeekableWidget::SeekableWidget(QWidget* parent) : super(parent), - NodeCopyPasteService(QStringLiteral("markers")), timeline_points_(nullptr), dragging_(false), ignore_next_focus_out_(false), @@ -91,21 +91,68 @@ void SeekableWidget::DeleteSelected() Core::instance()->undo_stack()->pushIfHasChildren(command); } -void SeekableWidget::CopySelected(bool cut) +bool SeekableWidget::CopySelected(bool cut) { - qDebug() << "COPY IS STUB"; - //CopyMarkersToClipboard(selection_manager_.GetSelectedObjects()); + if (!selection_manager_.GetSelectedObjects().empty()) { + ProjectSerializer::SaveData sdata(Project::GetProjectFromObject(timeline_points_)); + sdata.SetOnlySerializeMarkers(selection_manager_.GetSelectedObjects()); - if (cut) { - DeleteSelected(); + ProjectSerializer::Copy(sdata, QStringLiteral("markers")); + + if (cut) { + DeleteSelected(); + } + + return true; + } else { + return false; } } -void SeekableWidget::PasteMarkers(bool insert, rational insert_time) +bool SeekableWidget::PasteMarkers(bool insert, rational insert_time) { - //MultiUndoCommand *command = new MultiUndoCommand(); - //PasteMarkersFromClipboard(timeline_points()->markers(), command, insert_time); - qDebug() << "PASTE IS STUB"; + ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("markers")); + if (res == ProjectSerializer::kSuccess) { + const std::vector &markers = res.GetLoadData().markers; + if (!markers.empty()) { + MultiUndoCommand *command = new MultiUndoCommand(); + + // Normalize markers to start at playhead + rational min = RATIONAL_MAX; + for (auto it=markers.cbegin(); it!=markers.cend(); it++) { + min = qMin(min, (*it)->time()); + } + min -= GetTime(); + + // Avoid duplicates + bool loop; + do { + loop = false; + for (auto it=markers.cbegin(); it!=markers.cend(); it++) { + rational proposed_time = (*it)->time() - min; + + if (timeline_points_->markers()->GetMarkerAtTime(proposed_time)) { + min -= timebase(); + loop = true; + break; + } + } + } while (loop); + + for (auto it=markers.cbegin(); it!=markers.cend(); it++) { + TimelineMarker *m = *it; + + m->set_time(m->time() - min); + + command->add_child(new MarkerAddCommand(timeline_points_->markers(), m)); + } + + Core::instance()->undo_stack()->push(command); + return true; + } + } + + return false; } void SeekableWidget::mousePressEvent(QMouseEvent *event) @@ -148,7 +195,7 @@ void SeekableWidget::mouseDoubleClickEvent(QMouseEvent *event) { super::mouseDoubleClickEvent(event); - if (selection_manager_.GetObjectAtPoint(event->pos()) && !selection_manager_.GetSelectedObjects().isEmpty()) { + if (selection_manager_.GetObjectAtPoint(event->pos()) && !selection_manager_.GetSelectedObjects().empty()) { ShowMarkerProperties(); } } @@ -315,7 +362,7 @@ void SeekableWidget::DrawPlayhead(QPainter *p, int x, int y) bool SeekableWidget::ShowContextMenu(const QPoint &p) { - if (selection_manager_.GetObjectAtPoint(p) && !selection_manager_.GetSelectedObjects().isEmpty()) { + if (selection_manager_.GetObjectAtPoint(p) && !selection_manager_.GetSelectedObjects().empty()) { // Show marker-specific menu Menu m; diff --git a/app/widget/timeruler/seekablewidget.h b/app/widget/timeruler/seekablewidget.h index d06401989..37d87903c 100644 --- a/app/widget/timeruler/seekablewidget.h +++ b/app/widget/timeruler/seekablewidget.h @@ -25,7 +25,6 @@ #include #include "common/rational.h" -#include "node/nodecopypaste.h" #include "timeline/timelinepoints.h" #include "widget/menu/menu.h" #include "widget/snapservice/snapservice.h" @@ -33,7 +32,7 @@ namespace olive { -class SeekableWidget : public TimeBasedView, public NodeCopyPasteService +class SeekableWidget : public TimeBasedView { Q_OBJECT public: @@ -53,9 +52,9 @@ public: void DeleteSelected(); - void CopySelected(bool cut); + bool CopySelected(bool cut); - void PasteMarkers(bool insert, rational insert_time); + bool PasteMarkers(bool insert, rational insert_time); void DeselectAllMarkers();