markers: reimplemented copy/paste
This commit is contained in:
@@ -30,7 +30,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector<NodeKeyframe*> &keys, const rational &timebase, QWidget *parent) :
|
||||
KeyframePropertiesDialog::KeyframePropertiesDialog(const std::vector<NodeKeyframe*> &keys, const rational &timebase, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
keys_(keys),
|
||||
timebase_(timebase)
|
||||
@@ -91,7 +91,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector<NodeKeyframe*>
|
||||
bool all_same_bezier_out_x = true;
|
||||
bool all_same_bezier_out_y = true;
|
||||
|
||||
for (int i=0;i<keys_.size();i++) {
|
||||
for (size_t i=0;i<keys_.size();i++) {
|
||||
if (i > 0) {
|
||||
NodeKeyframe* prev_key = keys_.at(i-1);
|
||||
NodeKeyframe* this_key = keys_.at(i);
|
||||
@@ -126,7 +126,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector<NodeKeyframe*>
|
||||
|
||||
// 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;j<keys_.size();j++) {
|
||||
for (size_t j=0;j<keys_.size();j++) {
|
||||
if (i != j
|
||||
&& keys_.at(j)->track() == keys_.at(i)->track()) {
|
||||
can_set_time = false;
|
||||
@@ -147,7 +147,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector<NodeKeyframe*>
|
||||
}
|
||||
|
||||
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<NodeKeyframe*>
|
||||
if (all_same_type) {
|
||||
// If all keyframes are the same type, set it here
|
||||
for (int i=0;i<type_select_->count();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<NodeKeyframe*>
|
||||
}
|
||||
}
|
||||
|
||||
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++;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class KeyframePropertiesDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframePropertiesDialog(const QVector<NodeKeyframe*>& keys, const rational& timebase, QWidget* parent = nullptr);
|
||||
KeyframePropertiesDialog(const std::vector<NodeKeyframe*>& 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<NodeKeyframe*>& keys_;
|
||||
const std::vector<NodeKeyframe*>& keys_;
|
||||
|
||||
rational timebase_;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace olive {
|
||||
|
||||
#define super QDialog
|
||||
|
||||
MarkerPropertiesDialog::MarkerPropertiesDialog(const QVector<TimelineMarker *> &markers, const rational &timebase, QWidget *parent) :
|
||||
MarkerPropertiesDialog::MarkerPropertiesDialog(const std::vector<TimelineMarker *> &markers, const rational &timebase, QWidget *parent) :
|
||||
super(parent),
|
||||
markers_(markers)
|
||||
{
|
||||
@@ -60,10 +60,10 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(const QVector<TimelineMarker *> &
|
||||
}
|
||||
|
||||
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<TimelineMarker *> &
|
||||
color_menu_ = new ColorCodingComboBox();
|
||||
layout->addWidget(color_menu_, row, 1);
|
||||
|
||||
color_menu_->SetColor(markers.first()->color());
|
||||
for (int i=1; i<markers.size(); i++) {
|
||||
color_menu_->SetColor(markers.front()->color());
|
||||
for (size_t i=1; i<markers.size(); i++) {
|
||||
if (markers.at(i)->color() != color_menu_->GetSelectedColor()) {
|
||||
color_menu_->SetColor(-1);
|
||||
break;
|
||||
@@ -102,8 +102,8 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(const QVector<TimelineMarker *> &
|
||||
layout->addWidget(label_edit_, row, 1);
|
||||
|
||||
// Determine what the startup label text should be
|
||||
label_edit_->setText(markers.first()->name());
|
||||
for (int i=1; i<markers.size(); i++) {
|
||||
label_edit_->setText(markers.front()->name());
|
||||
for (size_t i=1; i<markers.size(); i++) {
|
||||
if (markers.at(i)->name() != 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);
|
||||
|
||||
@@ -55,13 +55,13 @@ class MarkerPropertiesDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MarkerPropertiesDialog(const QVector<TimelineMarker*> &markers, const rational &timebase, QWidget *parent = nullptr);
|
||||
MarkerPropertiesDialog(const std::vector<TimelineMarker*> &markers, const rational &timebase, QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
QVector<TimelineMarker*> markers_;
|
||||
std::vector<TimelineMarker*> markers_;
|
||||
|
||||
LineEditWithFocusSignal *label_edit_;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(©_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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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(©_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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -66,6 +66,7 @@ bool ProjectSaveTask::Run()
|
||||
case ProjectSerializer::kProjectTooNew:
|
||||
case ProjectSerializer::kProjectTooOld:
|
||||
case ProjectSerializer::kUnknownVersion:
|
||||
case ProjectSerializer::kNoData:
|
||||
SetError(tr("Unknown error."));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -406,7 +406,7 @@ void CurveView::FirstChanceMouseRelease(QMouseEvent *event)
|
||||
void CurveView::KeyframeDragStart(QMouseEvent *event)
|
||||
{
|
||||
drag_keyframe_values_.resize(GetSelectedKeyframes().size());
|
||||
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
for (size_t i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
NodeKeyframe *key = GetSelectedKeyframes().at(i);
|
||||
drag_keyframe_values_[i] = key->value();
|
||||
}
|
||||
@@ -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; i<GetSelectedKeyframes().size(); i++) {
|
||||
for (size_t i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
NodeKeyframe *key = GetSelectedKeyframes().at(i);
|
||||
key->set_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; i<GetSelectedKeyframes().size(); i++) {
|
||||
for (size_t i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
NodeKeyframe *key = GetSelectedKeyframes().at(i);
|
||||
|
||||
FloatSlider::DisplayType display = GetFloatDisplayTypeFromKeyframe(key);
|
||||
@@ -453,13 +453,13 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip)
|
||||
}
|
||||
|
||||
// Set values
|
||||
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
for (size_t i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
NodeKeyframe *key = GetSelectedKeyframes().at(i);
|
||||
FloatSlider::DisplayType display = GetFloatDisplayTypeFromKeyframe(key);
|
||||
key->set_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; i<GetSelectedKeyframes().size(); i++) {
|
||||
for (size_t i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
NodeKeyframe *k = GetSelectedKeyframes().at(i);
|
||||
if (!qFuzzyCompare(k->value().toDouble(), drag_keyframe_values_.at(i).toDouble())) {
|
||||
command->add_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i)));
|
||||
|
||||
@@ -251,16 +251,16 @@ void CurveWidget::ConnectInputInternal(Node *node, const QString &input, int ele
|
||||
|
||||
void CurveWidget::SelectionChanged()
|
||||
{
|
||||
const QVector<NodeKeyframe*> &selected = view_->GetSelectedKeyframes();
|
||||
const std::vector<NodeKeyframe*> &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.size();i++) {
|
||||
for (size_t i=1;i<selected.size();i++) {
|
||||
NodeKeyframe* prev_item = selected.at(i-1);
|
||||
NodeKeyframe* this_item = selected.at(i);
|
||||
|
||||
@@ -287,8 +287,8 @@ void CurveWidget::KeyframeTypeButtonTriggered(bool checked)
|
||||
}
|
||||
|
||||
// Get selected items and do nothing if there are none
|
||||
const QVector<NodeKeyframe*> &selected = view_->GetSelectedKeyframes();
|
||||
if (selected.isEmpty()) {
|
||||
const std::vector<NodeKeyframe*> &selected = view_->GetSelectedKeyframes();
|
||||
if (selected.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -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().size();i++) {
|
||||
for (size_t i=1;i<GetSelectedKeyframes().size();i++) {
|
||||
NodeKeyframe* key_item = GetSelectedKeyframes().at(i);
|
||||
NodeKeyframe* prev_item = GetSelectedKeyframes().at(i-1);
|
||||
|
||||
@@ -507,7 +507,7 @@ void KeyframeView::ShowContextMenu()
|
||||
|
||||
ContextMenuEvent(m);
|
||||
|
||||
if (!GetSelectedKeyframes().isEmpty()) {
|
||||
if (!GetSelectedKeyframes().empty()) {
|
||||
m.addSeparator();
|
||||
|
||||
QAction* properties_action = m.addAction(tr("P&roperties"));
|
||||
@@ -542,7 +542,7 @@ void KeyframeView::ShowContextMenu()
|
||||
|
||||
void KeyframeView::ShowKeyframePropertiesDialog()
|
||||
{
|
||||
if (!GetSelectedKeyframes().isEmpty()) {
|
||||
if (!GetSelectedKeyframes().empty()) {
|
||||
KeyframePropertiesDialog kd(GetSelectedKeyframes(), timebase(), this);
|
||||
kd.exec();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
|
||||
void Clear();
|
||||
|
||||
const QVector<NodeKeyframe*> &GetSelectedKeyframes() const
|
||||
const std::vector<NodeKeyframe*> &GetSelectedKeyframes() const
|
||||
{
|
||||
return selection_manager_.GetSelectedObjects();
|
||||
}
|
||||
|
||||
@@ -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<QString, QString> &node_props = it.value();
|
||||
pos.position.setX(node_props.value(QStringLiteral("x")).toDouble());
|
||||
pos.position.setY(node_props.value(QStringLiteral("y")).toDouble());
|
||||
pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble();
|
||||
|
||||
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<Node*> &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<Node *> &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<QString, QString> &node_props = it.value();
|
||||
pos.position.setX(node_props.value(QStringLiteral("x")).toDouble());
|
||||
pos.position.setY(node_props.value(QStringLiteral("y")).toDouble());
|
||||
pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble();
|
||||
|
||||
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<Node *> &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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Node *> &nodes, ProjectSerializer::SaveData *data, void* userdata) override;
|
||||
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &ldata, void *userdata) override;
|
||||
|
||||
virtual void changeEvent(QEvent *e) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -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<T*> &GetSelectedObjects() const
|
||||
const std::vector<T*> &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; i<selected_.size(); i++) {
|
||||
for (size_t i=0; i<selected_.size(); i++) {
|
||||
T *obj = selected_.at(i);
|
||||
|
||||
dragging_[i] = obj->time();
|
||||
@@ -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; i<selected_.size(); i++) {
|
||||
for (size_t i=0; i<selected_.size(); i++) {
|
||||
rational proposed_time = dragging_.at(i) + time_diff;
|
||||
T *sel = selected_.at(i);
|
||||
|
||||
@@ -199,7 +205,7 @@ public:
|
||||
}
|
||||
|
||||
// Apply movement
|
||||
for (int i=0; i<selected_.size(); i++) {
|
||||
for (size_t i=0; i<selected_.size(); i++) {
|
||||
selected_.at(i)->set_time(dragging_.at(i) + time_diff);
|
||||
}
|
||||
|
||||
@@ -219,7 +225,7 @@ public:
|
||||
{
|
||||
QToolTip::hideText();
|
||||
|
||||
for (int i=0; i<selected_.size(); i++) {
|
||||
for (size_t i=0; i<selected_.size(); i++) {
|
||||
command->add_child(new SetTimeCommand(selected_.at(i), selected_.at(i)->time(), dragging_.at(i)));
|
||||
}
|
||||
|
||||
@@ -314,11 +320,11 @@ private:
|
||||
TimeBasedView *view_;
|
||||
|
||||
using DrawnObject = QPair<T*, QRectF>;
|
||||
QVector<DrawnObject> drawn_objects_;
|
||||
std::vector<DrawnObject> drawn_objects_;
|
||||
|
||||
QVector<T*> selected_;
|
||||
std::vector<T*> selected_;
|
||||
|
||||
QVector<rational> dragging_;
|
||||
std::vector<rational> dragging_;
|
||||
|
||||
T *initial_drag_item_;
|
||||
|
||||
@@ -328,7 +334,7 @@ private:
|
||||
|
||||
QRubberBand *rubberband_;
|
||||
QPoint rubberband_start_;
|
||||
QVector<T*> rubberband_preselected_;
|
||||
std::vector<T*> rubberband_preselected_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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<Node *> &nodes, ProjectSerializer::SaveData *sdata, void *userdata)
|
||||
{
|
||||
// Cache the earliest in point so all copied clips have a "relative" in point that can be pasted anywhere
|
||||
QVector<Block*>& selected = *static_cast<QVector<Block*>*>(userdata);
|
||||
rational earliest_in = RATIONAL_MAX;
|
||||
ProjectSerializer::SerializedProperties properties;
|
||||
|
||||
foreach (Block* block, selected) {
|
||||
earliest_in = qMin(earliest_in, block->in());
|
||||
}
|
||||
|
||||
foreach (Block* block, selected) {
|
||||
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<Node *> &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<Block*>(it.key())->length();
|
||||
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
|
||||
|
||||
paste_end = qMax(paste_end, paste_start + in + length);
|
||||
}
|
||||
|
||||
if (paste_end != paste_start) {
|
||||
InsertGapsAt(paste_start, paste_end - paste_start, command);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=load_data.properties.cbegin(); it!=load_data.properties.cend(); it++) {
|
||||
Block *block = static_cast<Block*>(it.key());
|
||||
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
|
||||
Track::Reference track = Track::Reference::FromString(it.value()[QStringLiteral("track")]);
|
||||
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()),
|
||||
track.index(),
|
||||
block,
|
||||
paste_start + in));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void TimelineWidget::SelectAll()
|
||||
{
|
||||
QVector<Block*> 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<Block*> selected = GetSelectedBlocks();
|
||||
|
||||
if (selected.isEmpty()) {
|
||||
if (selected_blocks_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<Node*> selected_nodes;
|
||||
|
||||
foreach (Block* block, selected) {
|
||||
foreach (Block* block, selected_blocks_) {
|
||||
selected_nodes.append(block);
|
||||
|
||||
QVector<Node*> 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<Block*>(it.key())->length();
|
||||
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
|
||||
|
||||
paste_end = qMax(paste_end, paste_start + in + length);
|
||||
}
|
||||
|
||||
if (paste_end != paste_start) {
|
||||
InsertGapsAt(paste_start, paste_end - paste_start, command);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) {
|
||||
Block *block = static_cast<Block*>(it.key());
|
||||
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
|
||||
Track::Reference track = Track::Reference::FromString(it.value()[QStringLiteral("track")]);
|
||||
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()),
|
||||
track.index(),
|
||||
block,
|
||||
paste_start + in));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
|
||||
@@ -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<Node*> &nodes, ProjectSerializer::SaveData *data, void *userdata) override;
|
||||
virtual void PasteNodesToClipboardCallback(const QVector<Node*> &nodes, const ProjectSerializer::LoadData &load_data, void *userdata) override;
|
||||
|
||||
private:
|
||||
QVector<Timeline::EditToInfo> GetEditToInfo(const rational &playhead_time, Timeline::MovementMode mode);
|
||||
|
||||
|
||||
@@ -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<TimelineMarker*> &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;
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <QScrollBar>
|
||||
|
||||
#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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user