markers: reimplemented copy/paste

This commit is contained in:
itsmattkc
2022-05-01 22:33:58 -07:00
parent 6a0b5856c9
commit f7396de121
26 changed files with 452 additions and 424 deletions
-2
View File
@@ -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
-91
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#include "nodecopypaste.h"
#include <QMessageBox>
#include "core.h"
#include "node/factory.h"
#include "widget/nodeview/nodeviewundo.h"
#include "window/mainwindow/mainwindow.h"
namespace olive {
void NodeCopyPasteService::CopyNodesToClipboard(QVector<Node *> nodes, void *userdata)
{
QString copy_str;
QXmlStreamWriter writer(&copy_str);
// For any groups, add children
for (int i=0; i<nodes.size(); i++) {
// If this is a group, add the child nodes too
if (NodeGroup *g = dynamic_cast<NodeGroup*>(nodes.at(i))) {
for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) {
if (!nodes.contains(it.key())) {
nodes.append(it.key());
}
}
}
}
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<Node*> 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);
}
}
-57
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#ifndef NODECOPYPASTEWIDGET_H
#define NODECOPYPASTEWIDGET_H
#include <QWidget>
#include <QUndoCommand>
#include "node/node.h"
#include "node/project/project.h"
#include "node/project/sequence/sequence.h"
#include "node/project/serializer/serializer.h"
namespace olive {
class NodeCopyPasteService
{
public:
NodeCopyPasteService(const QString &type) :
type_(type)
{}
protected:
void CopyNodesToClipboard(QVector<Node *> nodes, void* userdata = nullptr);
void PasteNodesFromClipboard(void* userdata = nullptr);
virtual void CopyNodesToClipboardCallback(const QVector<Node*> &nodes, ProjectSerializer::SaveData *data, void *userdata){}
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata){}
private:
QString type_;
};
}
#endif // NODECOPYPASTEWIDGET_H
@@ -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<Node*> 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(&copy_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<Node *> nodes)
{
// For any groups, add children
for (int i=0; i<nodes.size(); i++) {
// If this is a group, add the child nodes too
if (NodeGroup *g = dynamic_cast<NodeGroup*>(nodes.at(i))) {
for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) {
if (!nodes.contains(it.key())) {
nodes.append(it.key());
}
}
}
}
SetOnlySerializeNodes(nodes);
}
}
+19 -6
View File
@@ -50,7 +50,8 @@ public:
kUnknownVersion,
kFileError,
kXmlError,
kOverwriteError
kOverwriteError,
kNoData
};
using SerializedProperties = QHash<Node*, QMap<QString, QString> >;
@@ -62,6 +63,8 @@ public:
SerializedProperties properties;
std::vector<TimelineMarker*> markers;
};
class Result
@@ -84,6 +87,10 @@ public:
void SetLoadData(const LoadData &p) { load_data_ = p; }
const QVector<Node*> &GetLoadedNodes() const { return loaded_nodes_; }
void SetLoadedNodes(const QVector<Node*> &n) { loaded_nodes_ = n; }
private:
ResultCode code_;
@@ -91,17 +98,17 @@ public:
LoadData load_data_;
QVector<Node*> loaded_nodes_;
};
class SaveData
{
public:
SaveData(Project *project, const QString &filename, const QVector<Node*> &only = QVector<Node*>(), 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<Node*> &GetOnlySerializeNodes() const { return only_serialize_nodes_; }
void SetOnlySerializeNodes(const QVector<Node*> &only) { only_serialize_nodes_ = only; }
void SetOnlySerializeNodesAndResolveGroups(QVector<Node*> only);
const std::vector<TimelineMarker*> &GetOnlySerializeMarkers() const { return only_serialize_markers_; }
void SetOnlySerializeMarkers(const std::vector<TimelineMarker*> &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<TimelineMarker*> 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;
+118 -75
View File
@@ -31,6 +31,8 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project
QMap<quintptr, QMap<quintptr, Node::Position> > 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<Node*> &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<Node*> &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<quintptr>(context)));
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) {
writer->writeStartElement(QStringLiteral("node"));
SavePosition(writer, jt.key(), jt.value());
writer->writeEndElement(); // node
}
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<quintptr>(it.key())));
for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) {
writer->writeTextElement(jt.key(), jt.value());
writer->writeEndElement(); // node
}
writer->writeEndElement(); // node
writer->writeEndElement(); // nodes
writer->writeStartElement(QStringLiteral("positions"));
foreach (Node* context, using_node_list) {
const Node::PositionMap &map = context->GetContextPositions();
if (!map.isEmpty()) {
writer->writeStartElement(QStringLiteral("context"));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(context)));
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) {
writer->writeStartElement(QStringLiteral("node"));
SavePosition(writer, jt.key(), jt.value());
writer->writeEndElement(); // node
}
}
writer->writeEndElement(); // context
}
}
writer->writeEndElement(); // positions
writer->writeStartElement(QStringLiteral("properties"));
for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) {
writer->writeStartElement(QStringLiteral("node"));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) {
writer->writeTextElement(jt.key(), jt.value());
}
writer->writeEndElement(); // node
}
writer->writeEndElement(); // properties
// Save main window project layout
project->GetLayoutInfo().toXml(writer);
}
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
}
@@ -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;