keyframes: implemented copying/pasting
This commit is contained in:
@@ -41,6 +41,11 @@ NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, Type typ
|
||||
setParent(parent);
|
||||
}
|
||||
|
||||
NodeKeyframe::NodeKeyframe()
|
||||
{
|
||||
type_ = NodeKeyframe::kLinear;
|
||||
}
|
||||
|
||||
NodeKeyframe::~NodeKeyframe()
|
||||
{
|
||||
setParent(nullptr);
|
||||
|
||||
+8
-12
@@ -64,6 +64,7 @@ public:
|
||||
* @brief NodeKeyframe Constructor
|
||||
*/
|
||||
NodeKeyframe(const rational& time, const QVariant& value, Type type, int track, int element, const QString& input, QObject* parent = nullptr);
|
||||
NodeKeyframe();
|
||||
|
||||
virtual ~NodeKeyframe() override;
|
||||
|
||||
@@ -71,10 +72,9 @@ public:
|
||||
NodeKeyframe* copy(QObject* parent = nullptr) const;
|
||||
|
||||
Node* parent() const;
|
||||
const QString& input() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
const QString& input() const { return input_; }
|
||||
void set_input(const QString& input) { input_ = input; }
|
||||
|
||||
NodeKeyframeTrackReference key_track_ref() const
|
||||
{
|
||||
@@ -141,15 +141,11 @@ public:
|
||||
* For the majority of keyfreames, this will be 0, but for some types, such as kVec2, this will be 0 for X keyframes
|
||||
* and 1 for Y keyframes, etc.
|
||||
*/
|
||||
int track() const
|
||||
{
|
||||
return track_;
|
||||
}
|
||||
int track() const { return track_; }
|
||||
void set_track(int t) { track_ = t; }
|
||||
|
||||
int element() const
|
||||
{
|
||||
return element_;
|
||||
}
|
||||
int element() const { return element_; }
|
||||
void set_element(int e) { element_ = e; }
|
||||
|
||||
/**
|
||||
* @brief Convenience function for getting the opposite handle type (e.g. kInHandle <-> kOutHandle)
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
};
|
||||
|
||||
using SerializedProperties = QHash<Node*, QMap<QString, QString> >;
|
||||
using SerializedKeyframes = QHash<QString, QVector<NodeKeyframe*> >;
|
||||
|
||||
class LoadData
|
||||
{
|
||||
@@ -65,6 +66,8 @@ public:
|
||||
|
||||
std::vector<TimelineMarker*> markers;
|
||||
|
||||
SerializedKeyframes keyframes;
|
||||
|
||||
};
|
||||
|
||||
class Result
|
||||
@@ -105,7 +108,7 @@ public:
|
||||
class SaveData
|
||||
{
|
||||
public:
|
||||
SaveData(Project *project, const QString &filename = QString())
|
||||
SaveData(Project *project = nullptr, const QString &filename = QString())
|
||||
{
|
||||
project_ = project;
|
||||
filename_ = filename;
|
||||
@@ -128,6 +131,9 @@ public:
|
||||
const std::vector<TimelineMarker*> &GetOnlySerializeMarkers() const { return only_serialize_markers_; }
|
||||
void SetOnlySerializeMarkers(const std::vector<TimelineMarker*> &only) { only_serialize_markers_ = only; }
|
||||
|
||||
const std::vector<NodeKeyframe*> &GetOnlySerializeKeyframes() const { return only_serialize_keyframes_; }
|
||||
void SetOnlySerializeKeyframes(const std::vector<NodeKeyframe*> &only) { only_serialize_keyframes_ = only; }
|
||||
|
||||
const SerializedProperties &GetProperties() const { return properties_; }
|
||||
void SetProperties(const SerializedProperties &p) { properties_ = p; }
|
||||
|
||||
@@ -142,6 +148,8 @@ public:
|
||||
|
||||
std::vector<TimelineMarker*> only_serialize_markers_;
|
||||
|
||||
std::vector<NodeKeyframe*> only_serialize_keyframes_;
|
||||
|
||||
};
|
||||
|
||||
static void Initialize();
|
||||
|
||||
@@ -99,6 +99,102 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project
|
||||
}
|
||||
}
|
||||
|
||||
} else if (reader->name() == QStringLiteral("keyframes")) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
QString node_id;
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("id")) {
|
||||
node_id = attr.value().toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Node *n = nullptr;
|
||||
if (!node_id.isEmpty()) {
|
||||
n = NodeFactory::CreateFromID(node_id);
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("input")) {
|
||||
QString input_id;
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("id")) {
|
||||
input_id = attr.value().toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (input_id.isEmpty()) {
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("element")) {
|
||||
QString element_id;
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("id")) {
|
||||
element_id = attr.value().toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (element_id.isEmpty()) {
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("track")) {
|
||||
QString track_id;
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("id")) {
|
||||
track_id = attr.value().toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (track_id.isEmpty()) {
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("key")) {
|
||||
NodeKeyframe *key = new NodeKeyframe();
|
||||
key->set_input(input_id);
|
||||
key->set_element(element_id.toInt());
|
||||
key->set_track(track_id.toInt());
|
||||
|
||||
LoadKeyframe(reader, key, n->GetInputDataType(input_id));
|
||||
|
||||
load_data.keyframes[node_id].append(key);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete n;
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (reader->name() == QStringLiteral("markers")) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
@@ -221,8 +317,6 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project
|
||||
|
||||
void ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const
|
||||
{
|
||||
Project *project = data.GetProject();
|
||||
|
||||
if (!data.GetOnlySerializeMarkers().empty()) {
|
||||
|
||||
writer->writeStartElement(QStringLiteral("markers"));
|
||||
@@ -239,9 +333,63 @@ void ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &dat
|
||||
|
||||
writer->writeEndElement(); // markers
|
||||
|
||||
} else {
|
||||
} else if (!data.GetOnlySerializeKeyframes().empty()) {
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), data.GetProject()->GetUuid().toString());
|
||||
writer->writeStartElement(QStringLiteral("keyframes"));
|
||||
|
||||
// Organize keyframes into node+input
|
||||
QHash<QString, QHash<QString, QMap<int, QMap<int, QVector<NodeKeyframe*> > > > > organized;
|
||||
|
||||
for (auto it=data.GetOnlySerializeKeyframes().cbegin(); it!=data.GetOnlySerializeKeyframes().cend(); it++) {
|
||||
NodeKeyframe *key = *it;
|
||||
organized[key->parent()->id()][key->input()][key->element()][key->track()].append(key);
|
||||
}
|
||||
|
||||
for (auto it=organized.cbegin(); it!=organized.cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), it.key());
|
||||
|
||||
for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) {
|
||||
writer->writeStartElement(QStringLiteral("input"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), jt.key());
|
||||
|
||||
for (auto kt=jt.value().cbegin(); kt!=jt.value().cend(); kt++) {
|
||||
writer->writeStartElement(QStringLiteral("element"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), QString::number(kt.key()));
|
||||
|
||||
for (auto lt=kt.value().cbegin(); lt!=kt.value().cend(); lt++) {
|
||||
const QVector<NodeKeyframe *> &keys = lt.value();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), QString::number(lt.key()));
|
||||
|
||||
for (NodeKeyframe *key : keys) {
|
||||
writer->writeStartElement(QStringLiteral("key"));
|
||||
SaveKeyframe(writer, key, key->parent()->GetInputDataType(key->input()));
|
||||
writer->writeEndElement(); // key
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // element
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // input
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // node;
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // keyframes
|
||||
|
||||
} else if (Project *project = data.GetProject()) {
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), project->GetUuid().toString());
|
||||
|
||||
writer->writeStartElement(QStringLiteral("nodes"));
|
||||
|
||||
@@ -310,6 +458,10 @@ void ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &dat
|
||||
// Save main window project layout
|
||||
project->GetLayoutInfo().toXml(writer);
|
||||
|
||||
} else {
|
||||
|
||||
qCritical() << "Cannot save nodes without project object";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,40 +753,13 @@ void ProjectSerializer220403::LoadImmediate(QXmlStreamReader *reader, Node *node
|
||||
}
|
||||
|
||||
if (reader->name() == QStringLiteral("key")) {
|
||||
QString key_input;
|
||||
rational key_time;
|
||||
NodeKeyframe::Type key_type = NodeKeyframe::kLinear;
|
||||
QVariant key_value;
|
||||
QPointF key_in_handle;
|
||||
QPointF key_out_handle;
|
||||
NodeKeyframe* key = new NodeKeyframe();
|
||||
key->set_input(input);
|
||||
key->set_element(element);
|
||||
key->set_track(track);
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (IsCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attr.name() == QStringLiteral("input")) {
|
||||
key_input = attr.value().toString();
|
||||
} else if (attr.name() == QStringLiteral("time")) {
|
||||
key_time = rational::fromString(attr.value().toString());
|
||||
} else if (attr.name() == QStringLiteral("type")) {
|
||||
key_type = static_cast<NodeKeyframe::Type>(attr.value().toInt());
|
||||
} else if (attr.name() == QStringLiteral("inhandlex")) {
|
||||
key_in_handle.setX(attr.value().toDouble());
|
||||
} else if (attr.name() == QStringLiteral("inhandley")) {
|
||||
key_in_handle.setY(attr.value().toDouble());
|
||||
} else if (attr.name() == QStringLiteral("outhandlex")) {
|
||||
key_out_handle.setX(attr.value().toDouble());
|
||||
} else if (attr.name() == QStringLiteral("outhandley")) {
|
||||
key_out_handle.setY(attr.value().toDouble());
|
||||
}
|
||||
}
|
||||
|
||||
key_value = NodeValue::StringToValue(data_type, reader->readElementText(), true);
|
||||
|
||||
NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, key_input, node);
|
||||
key->set_bezier_control_in(key_in_handle);
|
||||
key->set_bezier_control_out(key_out_handle);
|
||||
LoadKeyframe(reader, key, data_type);
|
||||
key->setParent(node);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
@@ -695,15 +820,7 @@ void ProjectSerializer220403::SaveImmediate(QXmlStreamWriter *writer, Node *node
|
||||
for (NodeKeyframe* key : track) {
|
||||
writer->writeStartElement(QStringLiteral("key"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("input"), key->input());
|
||||
writer->writeAttribute(QStringLiteral("time"), key->time().toString());
|
||||
writer->writeAttribute(QStringLiteral("type"), QString::number(key->type()));
|
||||
writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(key->bezier_control_in().x()));
|
||||
writer->writeAttribute(QStringLiteral("inhandley"), QString::number(key->bezier_control_in().y()));
|
||||
writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(key->bezier_control_out().x()));
|
||||
writer->writeAttribute(QStringLiteral("outhandley"), QString::number(key->bezier_control_out().y()));
|
||||
|
||||
writer->writeCharacters(NodeValue::ValueToString(data_type, key->value(), true));
|
||||
SaveKeyframe(writer, key, data_type);
|
||||
|
||||
writer->writeEndElement(); // key
|
||||
}
|
||||
@@ -722,6 +839,53 @@ void ProjectSerializer220403::SaveImmediate(QXmlStreamWriter *writer, Node *node
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSerializer220403::LoadKeyframe(QXmlStreamReader *reader, NodeKeyframe *key, NodeValue::Type data_type) const
|
||||
{
|
||||
QString key_input;
|
||||
QPointF key_in_handle;
|
||||
QPointF key_out_handle;
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (IsCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attr.name() == QStringLiteral("input")) {
|
||||
key_input = attr.value().toString();
|
||||
} else if (attr.name() == QStringLiteral("time")) {
|
||||
key->set_time(rational::fromString(attr.value().toString()));
|
||||
} else if (attr.name() == QStringLiteral("type")) {
|
||||
key->set_type(static_cast<NodeKeyframe::Type>(attr.value().toInt()));
|
||||
} else if (attr.name() == QStringLiteral("inhandlex")) {
|
||||
key_in_handle.setX(attr.value().toDouble());
|
||||
} else if (attr.name() == QStringLiteral("inhandley")) {
|
||||
key_in_handle.setY(attr.value().toDouble());
|
||||
} else if (attr.name() == QStringLiteral("outhandlex")) {
|
||||
key_out_handle.setX(attr.value().toDouble());
|
||||
} else if (attr.name() == QStringLiteral("outhandley")) {
|
||||
key_out_handle.setY(attr.value().toDouble());
|
||||
}
|
||||
}
|
||||
|
||||
key->set_value(NodeValue::StringToValue(data_type, reader->readElementText(), true));
|
||||
|
||||
key->set_bezier_control_in(key_in_handle);
|
||||
key->set_bezier_control_out(key_out_handle);
|
||||
}
|
||||
|
||||
void ProjectSerializer220403::SaveKeyframe(QXmlStreamWriter *writer, NodeKeyframe *key, NodeValue::Type data_type) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("input"), key->input());
|
||||
writer->writeAttribute(QStringLiteral("time"), key->time().toString());
|
||||
writer->writeAttribute(QStringLiteral("type"), QString::number(key->type()));
|
||||
writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(key->bezier_control_in().x()));
|
||||
writer->writeAttribute(QStringLiteral("inhandley"), QString::number(key->bezier_control_in().y()));
|
||||
writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(key->bezier_control_out().x()));
|
||||
writer->writeAttribute(QStringLiteral("outhandley"), QString::number(key->bezier_control_out().y()));
|
||||
|
||||
writer->writeCharacters(NodeValue::ValueToString(data_type, key->value(), true));
|
||||
}
|
||||
|
||||
bool ProjectSerializer220403::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const
|
||||
{
|
||||
bool got_node_ptr = false;
|
||||
|
||||
@@ -86,6 +86,10 @@ private:
|
||||
|
||||
void SaveImmediate(QXmlStreamWriter *writer, Node *node, const QString &input, int element) const;
|
||||
|
||||
void LoadKeyframe(QXmlStreamReader *reader, NodeKeyframe *key, NodeValue::Type data_type) const;
|
||||
|
||||
void SaveKeyframe(QXmlStreamWriter *writer, NodeKeyframe *key, NodeValue::Type data_type) const;
|
||||
|
||||
bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const;
|
||||
|
||||
void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos) const;
|
||||
|
||||
@@ -216,4 +216,19 @@ void TimeBasedPanel::DeleteSelected()
|
||||
GetTimeBasedWidget()->DeleteSelected();
|
||||
}
|
||||
|
||||
void TimeBasedPanel::CutSelected()
|
||||
{
|
||||
GetTimeBasedWidget()->CopySelected(true);
|
||||
}
|
||||
|
||||
void TimeBasedPanel::CopySelected()
|
||||
{
|
||||
GetTimeBasedWidget()->CopySelected(false);
|
||||
}
|
||||
|
||||
void TimeBasedPanel::Paste()
|
||||
{
|
||||
GetTimeBasedWidget()->Paste();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -100,6 +100,12 @@ public:
|
||||
|
||||
virtual void DeleteSelected() override;
|
||||
|
||||
virtual void CutSelected() override;
|
||||
|
||||
virtual void CopySelected() override;
|
||||
|
||||
virtual void Paste() override;
|
||||
|
||||
public slots:
|
||||
void SetTimebase(const rational& timebase);
|
||||
|
||||
|
||||
@@ -108,24 +108,9 @@ void TimelinePanel::ToggleLinks()
|
||||
timeline_widget()->ToggleLinksOnSelected();
|
||||
}
|
||||
|
||||
void TimelinePanel::CutSelected()
|
||||
{
|
||||
timeline_widget()->CopySelected(true);
|
||||
}
|
||||
|
||||
void TimelinePanel::CopySelected()
|
||||
{
|
||||
timeline_widget()->CopySelected(false);
|
||||
}
|
||||
|
||||
void TimelinePanel::Paste()
|
||||
{
|
||||
timeline_widget()->Paste(false);
|
||||
}
|
||||
|
||||
void TimelinePanel::PasteInsert()
|
||||
{
|
||||
timeline_widget()->Paste(true);
|
||||
timeline_widget()->PasteInsert();
|
||||
}
|
||||
|
||||
void TimelinePanel::DeleteInToOut()
|
||||
|
||||
@@ -68,12 +68,6 @@ public:
|
||||
|
||||
virtual void ToggleLinks() override;
|
||||
|
||||
virtual void CutSelected() override;
|
||||
|
||||
virtual void CopySelected() override;
|
||||
|
||||
virtual void Paste() override;
|
||||
|
||||
virtual void PasteInsert() override;
|
||||
|
||||
virtual void DeleteInToOut() override;
|
||||
|
||||
@@ -41,6 +41,11 @@ public:
|
||||
|
||||
void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color);
|
||||
|
||||
const QHash<NodeKeyframeTrackReference, KeyframeViewInputConnection*> &GetConnections() const
|
||||
{
|
||||
return track_connections_;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void ZoomToFit();
|
||||
|
||||
|
||||
@@ -129,6 +129,36 @@ void CurveWidget::DeleteSelected()
|
||||
view_->DeleteSelected();
|
||||
}
|
||||
|
||||
Node *CurveWidget::GetSelectedNodeWithID(const QString &id)
|
||||
{
|
||||
for (auto it=view_->GetConnections().cbegin(); it!=view_->GetConnections().cend(); it++) {
|
||||
Node *n = it.key().input().node();
|
||||
if (n->id() == id) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CurveWidget::CopySelected(bool cut)
|
||||
{
|
||||
if (super::CopySelected(cut)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return view_->CopySelected(cut);
|
||||
}
|
||||
|
||||
bool CurveWidget::Paste()
|
||||
{
|
||||
if (super::Paste()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return view_->Paste(std::bind(&CurveWidget::GetSelectedNodeWithID, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void CurveWidget::SetNodes(const QVector<Node *> &nodes)
|
||||
{
|
||||
tree_view_->SetNodes(nodes);
|
||||
|
||||
@@ -55,6 +55,12 @@ public:
|
||||
view_->DeselectAll();
|
||||
}
|
||||
|
||||
Node *GetSelectedNodeWithID(const QString &id);
|
||||
|
||||
virtual bool CopySelected(bool cut) override;
|
||||
|
||||
virtual bool Paste() override;
|
||||
|
||||
public slots:
|
||||
void SetNodes(const QVector<Node *> &nodes);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "dialog/keyframeproperties/keyframeproperties.h"
|
||||
#include "keyframeviewundo.h"
|
||||
#include "node/node.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
@@ -181,6 +182,68 @@ void KeyframeView::SelectionManagerDeselectEvent(void *obj)
|
||||
emit SelectionChanged();
|
||||
}
|
||||
|
||||
bool KeyframeView::CopySelected(bool cut)
|
||||
{
|
||||
if (!selection_manager_.GetSelectedObjects().empty()) {
|
||||
ProjectSerializer::SaveData sdata;
|
||||
sdata.SetOnlySerializeKeyframes(selection_manager_.GetSelectedObjects());
|
||||
|
||||
ProjectSerializer::Copy(sdata, QStringLiteral("keyframes"));
|
||||
|
||||
if (cut) {
|
||||
DeleteSelected();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyframeView::Paste(std::function<Node *(const QString &)> find_node_function)
|
||||
{
|
||||
ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("keyframes"));
|
||||
if (res == ProjectSerializer::kSuccess) {
|
||||
const ProjectSerializer::SerializedKeyframes &keys = res.GetLoadData().keyframes;
|
||||
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
rational min = RATIONAL_MAX;
|
||||
for (auto it=keys.cbegin(); it!=keys.cend(); it++) {
|
||||
for (NodeKeyframe *key : it.value()) {
|
||||
min = std::min(min, key->time());
|
||||
}
|
||||
}
|
||||
min -= GetTime();
|
||||
|
||||
for (auto it=keys.cbegin(); it!=keys.cend(); it++) {
|
||||
const QString &paste_id = it.key();
|
||||
|
||||
// Find a node with this ID
|
||||
Node *node_with_id = find_node_function(paste_id);
|
||||
|
||||
if (node_with_id) {
|
||||
for (NodeKeyframe *key : it.value()) {
|
||||
key->set_time(key->time() - min);
|
||||
|
||||
if (NodeKeyframe *existing = node_with_id->GetKeyframeAtTimeOnTrack(key->input(), key->time(), key->track(), key->element())) {
|
||||
command->add_child(new NodeParamRemoveKeyframeCommand(existing));
|
||||
}
|
||||
|
||||
command->add_child(new NodeParamInsertKeyframeCommand(node_with_id, key));
|
||||
}
|
||||
} else {
|
||||
qDeleteAll(it.value());
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyframeView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
NodeKeyframe *key_under_cursor = selection_manager_.GetObjectAtPoint(event->pos());
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#ifndef KEYFRAMEVIEWBASE_H
|
||||
#define KEYFRAMEVIEWBASE_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "keyframeviewinputconnection.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "widget/menu/menu.h"
|
||||
@@ -77,6 +79,10 @@ public:
|
||||
UpdateSceneRect();
|
||||
}
|
||||
|
||||
bool CopySelected(bool cut);
|
||||
|
||||
bool Paste(std::function<Node *(const QString &)> find_node_function);
|
||||
|
||||
signals:
|
||||
void Dragged(int current_x, int current_y);
|
||||
|
||||
|
||||
@@ -60,6 +60,11 @@ public:
|
||||
return brush_;
|
||||
}
|
||||
|
||||
const NodeKeyframeTrackReference &GetReference() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
void SetBrush(const QBrush &brush);
|
||||
|
||||
signals:
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "common/functiontimer.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
@@ -519,6 +521,84 @@ void NodeParamView::SetSelectedNodes(const QVector<Node::ContextPair> &nodes, bo
|
||||
}
|
||||
}
|
||||
|
||||
Node *NodeParamView::GetNodeWithID(const QString &id)
|
||||
{
|
||||
for (NodeParamViewItem *item : selected_nodes_) {
|
||||
if (item->GetNode()->id() == id) {
|
||||
return item->GetNode();
|
||||
}
|
||||
}
|
||||
|
||||
for (NodeParamViewContext *ctx : context_items_) {
|
||||
for (NodeParamViewItem *item : ctx->GetItems()) {
|
||||
if (item->GetNode()->id() == id) {
|
||||
return item->GetNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool NodeParamView::CopySelected(bool cut)
|
||||
{
|
||||
if (super::CopySelected(cut)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyframe_view_ && keyframe_view_->hasFocus()) {
|
||||
if (keyframe_view_->CopySelected(cut)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (contexts_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectSerializer::SaveData sdata(contexts_.first()->project());
|
||||
ProjectSerializer::SerializedProperties properties;
|
||||
QVector<Node*> nodes;
|
||||
|
||||
for (NodeParamViewItem *item : selected_nodes_) {
|
||||
Node *n = item->GetNode();
|
||||
|
||||
if (!nodes.contains(n)) {
|
||||
nodes.append(n);
|
||||
|
||||
Node::Position pos = item->GetContext()->GetNodePositionDataInContext(n);
|
||||
|
||||
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.SetOnlySerializeNodesAndResolveGroups(nodes);
|
||||
sdata.SetProperties(properties);
|
||||
|
||||
ProjectSerializer::Copy(sdata, QStringLiteral("nodes"));
|
||||
|
||||
if (cut) {
|
||||
DeleteSelected();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NodeParamView::Paste()
|
||||
{
|
||||
if (keyframe_view_) {
|
||||
if (keyframe_view_->Paste(std::bind(&NodeParamView::GetNodeWithID, this, std::placeholders::_1))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Pasting nodes
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void NodeParamView::UpdateItemTime(const rational &time)
|
||||
{
|
||||
foreach (NodeParamViewContext* item, context_items_) {
|
||||
@@ -764,14 +844,14 @@ void NodeParamView::KeyframeViewDragged(int x, int y)
|
||||
|
||||
void NodeParamView::UpdateElementY()
|
||||
{
|
||||
foreach (NodeParamViewContext *ctx, context_items_) {
|
||||
for (NodeParamViewContext *ctx : context_items_) {
|
||||
for (auto it=ctx->GetItems().cbegin(); it!=ctx->GetItems().cend(); it++) {
|
||||
NodeParamViewItem *item = *it;
|
||||
Node *node = item->GetNode();
|
||||
const KeyframeView::NodeConnections &connections = item->GetKeyframeConnections();
|
||||
|
||||
if (!connections.isEmpty()) {
|
||||
foreach (const QString& input, node->inputs()) {
|
||||
for (const QString& input : node->inputs()) {
|
||||
if (!(node->GetInputFlags(input) & kInputFlagHidden)) {
|
||||
int arr_sz = NodeGroup::ResolveInput(NodeInput(node, input)).GetArraySize();
|
||||
|
||||
@@ -787,7 +867,7 @@ void NodeParamView::UpdateElementY()
|
||||
int use_index = i + 1;
|
||||
if (use_index < input_con.size()) {
|
||||
const KeyframeView::ElementConnections &ele_con = input_con.at(ic.element()+1);
|
||||
foreach (KeyframeViewInputConnection *track, ele_con) {
|
||||
for (KeyframeViewInputConnection *track : ele_con) {
|
||||
track->SetKeyframeY(y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +64,17 @@ public:
|
||||
void SetSelectedNodes(const QVector<NodeParamViewItem *> &nodes, bool handle_focused_node = true, bool emit_signal = true);
|
||||
void SetSelectedNodes(const QVector<Node::ContextPair> &nodes, bool emit_signal = true);
|
||||
|
||||
Node *GetNodeWithID(const QString &id);
|
||||
|
||||
const QVector<Node*> &GetContexts() const
|
||||
{
|
||||
return contexts_;
|
||||
}
|
||||
|
||||
virtual bool CopySelected(bool cut) override;
|
||||
|
||||
virtual bool Paste() override;
|
||||
|
||||
public slots:
|
||||
void SetContexts(const QVector<Node*> &contexts);
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
update();
|
||||
}
|
||||
|
||||
bool IsHighlighted() const { return highlighted_; }
|
||||
|
||||
bool IsExpanded() const;
|
||||
|
||||
static QString GetTitleBarTextFromNode(Node *n);
|
||||
|
||||
@@ -860,4 +860,22 @@ void TimeBasedWidget::HideSnaps()
|
||||
}
|
||||
}
|
||||
|
||||
bool TimeBasedWidget::CopySelected(bool cut)
|
||||
{
|
||||
if (ruler()->hasFocus() && ruler()->CopySelected(cut)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimeBasedWidget::Paste()
|
||||
{
|
||||
if (ruler()->hasFocus() && ruler()->PasteMarkers()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ public:
|
||||
void ShowSnaps(const std::vector<rational> ×);
|
||||
void HideSnaps();
|
||||
|
||||
virtual bool CopySelected(bool cut);
|
||||
|
||||
virtual bool Paste();
|
||||
|
||||
public slots:
|
||||
void SetTime(const rational &time);
|
||||
|
||||
|
||||
@@ -568,18 +568,14 @@ void TimelineWidget::ToggleLinksOnSelected()
|
||||
Core::instance()->undo_stack()->push(new NodeLinkManyCommand(blocks, link));
|
||||
}
|
||||
|
||||
void TimelineWidget::CopySelected(bool cut)
|
||||
bool TimelineWidget::CopySelected(bool cut)
|
||||
{
|
||||
if (!GetConnectedNode()) {
|
||||
return;
|
||||
if (super::CopySelected(cut)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ruler()->hasFocus() && ruler()->CopySelected(cut)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected_blocks_.isEmpty()) {
|
||||
return;
|
||||
if (!GetConnectedNode() || selected_blocks_.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<Node*> selected_nodes;
|
||||
@@ -619,58 +615,24 @@ void TimelineWidget::CopySelected(bool cut)
|
||||
if (cut) {
|
||||
DeleteSelected();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimelineWidget::Paste(bool insert)
|
||||
bool TimelineWidget::Paste()
|
||||
{
|
||||
if (!GetConnectedNode()) {
|
||||
return;
|
||||
if (super::Paste()) {
|
||||
return true;
|
||||
} if (!GetConnectedNode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ruler()->hasFocus() && ruler()->PasteMarkers(insert, GetTime())) {
|
||||
return;
|
||||
}
|
||||
return PasteInternal(false);
|
||||
}
|
||||
|
||||
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::PasteInsert()
|
||||
{
|
||||
PasteInternal(true);
|
||||
}
|
||||
|
||||
void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
@@ -1629,6 +1591,56 @@ QVector<Block *> TimelineWidget::GetBlocksInGlobalRect(const QPoint &p1, const Q
|
||||
return blocks_in_rect;
|
||||
}
|
||||
|
||||
bool TimelineWidget::PasteInternal(bool insert)
|
||||
{
|
||||
if (!GetConnectedNode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("timeline"));
|
||||
if (res.GetLoadedNodes().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray TimelineWidget::SaveSplitterState() const
|
||||
{
|
||||
return view_splitter_->saveState();
|
||||
|
||||
@@ -79,9 +79,11 @@ public:
|
||||
|
||||
void ToggleLinksOnSelected();
|
||||
|
||||
void CopySelected(bool cut);
|
||||
virtual bool CopySelected(bool cut) override;
|
||||
|
||||
void Paste(bool insert);
|
||||
virtual bool Paste() override;
|
||||
|
||||
void PasteInsert();
|
||||
|
||||
void DeleteInToOut(bool ripple);
|
||||
|
||||
@@ -294,6 +296,8 @@ private:
|
||||
|
||||
QVector<Block*> GetBlocksInGlobalRect(const QPoint &p1, const QPoint &p2);
|
||||
|
||||
bool PasteInternal(bool insert);
|
||||
|
||||
QPoint drag_origin_;
|
||||
|
||||
QRubberBand rubberband_;
|
||||
|
||||
@@ -102,7 +102,7 @@ void SeekableWidget::DeleteSelected()
|
||||
bool SeekableWidget::CopySelected(bool cut)
|
||||
{
|
||||
if (!selection_manager_.GetSelectedObjects().empty()) {
|
||||
ProjectSerializer::SaveData sdata(Project::GetProjectFromObject(timeline_points_));
|
||||
ProjectSerializer::SaveData sdata;
|
||||
sdata.SetOnlySerializeMarkers(selection_manager_.GetSelectedObjects());
|
||||
|
||||
ProjectSerializer::Copy(sdata, QStringLiteral("markers"));
|
||||
@@ -117,7 +117,7 @@ bool SeekableWidget::CopySelected(bool cut)
|
||||
}
|
||||
}
|
||||
|
||||
bool SeekableWidget::PasteMarkers(bool insert, rational insert_time)
|
||||
bool SeekableWidget::PasteMarkers()
|
||||
{
|
||||
ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("markers"));
|
||||
if (res == ProjectSerializer::kSuccess) {
|
||||
@@ -128,30 +128,19 @@ bool SeekableWidget::PasteMarkers(bool insert, rational insert_time)
|
||||
// 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 = std::min(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);
|
||||
|
||||
if (TimelineMarker *existing = timeline_points_->markers()->GetMarkerAtTime(m->time())) {
|
||||
command->add_child(new MarkerRemoveCommand(existing));
|
||||
}
|
||||
|
||||
command->add_child(new MarkerAddCommand(timeline_points_->markers(), m));
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
|
||||
bool CopySelected(bool cut);
|
||||
|
||||
bool PasteMarkers(bool insert, rational insert_time);
|
||||
bool PasteMarkers();
|
||||
|
||||
void DeselectAllMarkers();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user