Merge branch 'node-view-show-ctx'
This commit is contained in:
@@ -20,6 +20,7 @@ add_subdirectory(color)
|
||||
add_subdirectory(distort)
|
||||
add_subdirectory(filter)
|
||||
add_subdirectory(generator)
|
||||
add_subdirectory(group)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(math)
|
||||
add_subdirectory(output)
|
||||
|
||||
@@ -47,6 +47,8 @@ Block::Block() :
|
||||
IgnoreHashingFrom(kLengthInput);
|
||||
|
||||
AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
SetFlags(kDontShowInParamView);
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> Block::Category() const
|
||||
|
||||
@@ -233,13 +233,11 @@ void CropDistortNode::GizmoMove(const QPointF &p, const rational &time, const Qt
|
||||
}
|
||||
}
|
||||
|
||||
void CropDistortNode::GizmoRelease()
|
||||
void CropDistortNode::GizmoRelease(MultiUndoCommand *command)
|
||||
{
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
for (NodeInputDragger& i : gizmo_dragger_) {
|
||||
i.End(command);
|
||||
}
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
gizmo_dragger_.clear();
|
||||
|
||||
gizmo_start_.clear();
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
|
||||
virtual void GizmoRelease() override;
|
||||
virtual void GizmoRelease(MultiUndoCommand *command) override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kLeftInput;
|
||||
|
||||
@@ -301,13 +301,11 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time, con
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDistortNode::GizmoRelease()
|
||||
void TransformDistortNode::GizmoRelease(MultiUndoCommand *command)
|
||||
{
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
for (NodeInputDragger& i : gizmo_dragger_) {
|
||||
i.End(command);
|
||||
}
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
gizmo_dragger_.clear();
|
||||
|
||||
gizmo_start_.clear();
|
||||
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow &row, const NodeGlobals &globals, const QPointF &p) override;
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
|
||||
virtual void GizmoRelease() override;
|
||||
virtual void GizmoRelease(MultiUndoCommand *command) override;
|
||||
|
||||
enum AutoScaleType {
|
||||
kAutoScaleNone,
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
|
||||
namespace olive {
|
||||
QList<Node*> NodeFactory::library_;
|
||||
QVector<int> NodeFactory::hidden_;
|
||||
|
||||
void NodeFactory::Initialize()
|
||||
{
|
||||
@@ -65,6 +66,9 @@ void NodeFactory::Initialize()
|
||||
|
||||
library_.append(created_node);
|
||||
}
|
||||
|
||||
hidden_.append(kTextGeneratorLegacy);
|
||||
hidden_.append(kGroupNode);
|
||||
}
|
||||
|
||||
void NodeFactory::Destroy()
|
||||
@@ -86,6 +90,11 @@ Menu *NodeFactory::CreateMenu(QWidget* parent, bool create_none_item, Node::Cate
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hidden_.contains(i)) {
|
||||
// Skip this node
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure nodes are up-to-date with the current translation
|
||||
n->Retranslate();
|
||||
|
||||
@@ -241,6 +250,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
||||
return new SubtitleBlock();
|
||||
case kShapeGenerator:
|
||||
return new ShapeNode();
|
||||
case kGroupNode:
|
||||
return new NodeGroup();
|
||||
|
||||
case kInternalNodeCount:
|
||||
break;
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
kTimeRemapNode,
|
||||
kSubtitleBlock,
|
||||
kShapeGenerator,
|
||||
kGroupNode,
|
||||
|
||||
// Count value
|
||||
kInternalNodeCount
|
||||
@@ -87,6 +88,8 @@ public:
|
||||
private:
|
||||
static QList<Node*> library_;
|
||||
|
||||
static QVector<int> hidden_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -297,13 +297,11 @@ void ShapeNodeBase::GizmoMove(const QPointF &p, const rational &time, const Qt::
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeNodeBase::GizmoRelease()
|
||||
void ShapeNodeBase::GizmoRelease(MultiUndoCommand *command)
|
||||
{
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
for (NodeInputDragger& i : gizmo_dragger_) {
|
||||
i.End(command);
|
||||
}
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
gizmo_dragger_.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
|
||||
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
|
||||
virtual void GizmoRelease() override;
|
||||
virtual void GizmoRelease(MultiUndoCommand *command) override;
|
||||
|
||||
private:
|
||||
static QVector2D GenerateGizmoAnchor(const QVector2D &pos, const QVector2D &size, int drag, QVector2D *pt);
|
||||
|
||||
+18
-40
@@ -44,26 +44,12 @@ void NodeGraph::Clear()
|
||||
}
|
||||
}
|
||||
|
||||
qreal NodeGraph::GetNodeContextHeight(Node *context)
|
||||
{
|
||||
const PositionMap &map = position_map_.value(context);
|
||||
|
||||
qreal top = 0, bottom = 0;
|
||||
|
||||
foreach (const QPointF &pt, map) {
|
||||
top = qMin(pt.y(), top);
|
||||
bottom = qMax(pt.y(), bottom);
|
||||
}
|
||||
|
||||
return bottom - top;
|
||||
}
|
||||
|
||||
int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node) const
|
||||
int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node, bool except_itself) const
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (auto it=position_map_.cbegin(); it!=position_map_.cend(); it++) {
|
||||
if (it.value().contains(node)) {
|
||||
foreach (Node *ctx, node_children_) {
|
||||
if (ctx->ContextContainsNode(node) && (!except_itself || ctx != node)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -71,18 +57,6 @@ int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node) const
|
||||
return count;
|
||||
}
|
||||
|
||||
bool NodeGraph::NodeOutputsToContext(Node *node) const
|
||||
{
|
||||
for (auto it=position_map_.cbegin(); it!=position_map_.cend(); it++) {
|
||||
const PositionMap &pm = it.value();
|
||||
if (pm.contains(node) && node->OutputsTo(it.key(), true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void NodeGraph::childEvent(QChildEvent *event)
|
||||
{
|
||||
super::childEvent(event);
|
||||
@@ -100,6 +74,12 @@ void NodeGraph::childEvent(QChildEvent *event)
|
||||
connect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged, Qt::DirectConnection);
|
||||
connect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged, Qt::DirectConnection);
|
||||
|
||||
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
|
||||
connect(group, &NodeGroup::InputPassthroughAdded, this, &NodeGraph::GroupAddedInputPassthrough, Qt::DirectConnection);
|
||||
connect(group, &NodeGroup::InputPassthroughRemoved, this, &NodeGraph::GroupRemovedInputPassthrough, Qt::DirectConnection);
|
||||
connect(group, &NodeGroup::OutputPassthroughChanged, this, &NodeGraph::GroupChangedOutputPassthrough, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
emit NodeAdded(node);
|
||||
emit node->AddedToGraph(this);
|
||||
|
||||
@@ -113,21 +93,19 @@ void NodeGraph::childEvent(QChildEvent *event)
|
||||
disconnect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged);
|
||||
disconnect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged);
|
||||
|
||||
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
|
||||
disconnect(group, &NodeGroup::InputPassthroughAdded, this, &NodeGraph::GroupAddedInputPassthrough);
|
||||
disconnect(group, &NodeGroup::InputPassthroughRemoved, this, &NodeGraph::GroupRemovedInputPassthrough);
|
||||
disconnect(group, &NodeGroup::OutputPassthroughChanged, this, &NodeGraph::GroupChangedOutputPassthrough);
|
||||
}
|
||||
|
||||
emit NodeRemoved(node);
|
||||
emit node->RemovedFromGraph(this);
|
||||
|
||||
for (auto it=position_map_.begin(); it!=position_map_.end(); it++) {
|
||||
PositionMap &map = it.value();
|
||||
for (auto jt=map.begin(); jt!=map.end(); ) {
|
||||
if (jt.key() == node) {
|
||||
jt = map.erase(jt);
|
||||
emit NodePositionRemoved(node, it.key());
|
||||
} else {
|
||||
jt++;
|
||||
}
|
||||
}
|
||||
// Remove from any contexts
|
||||
foreach (Node *context, node_children_) {
|
||||
context->RemoveNodeFromContext(node);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-54
@@ -21,6 +21,7 @@
|
||||
#ifndef NODEGRAPH_H
|
||||
#define NODEGRAPH_H
|
||||
|
||||
#include "node/group/group.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -63,54 +64,7 @@ public:
|
||||
return default_nodes_;
|
||||
}
|
||||
|
||||
bool NodeMapContainsNode(Node* node, Node* context) const
|
||||
{
|
||||
return position_map_.value(context).contains(node);
|
||||
}
|
||||
|
||||
QPointF GetNodePosition(Node* node, Node* context)
|
||||
{
|
||||
return position_map_.value(context).value(node);
|
||||
}
|
||||
|
||||
void SetNodePosition(Node* node, Node* context, const QPointF& pos)
|
||||
{
|
||||
position_map_[context].insert(node, pos);
|
||||
emit NodePositionAdded(node, context, pos);
|
||||
}
|
||||
|
||||
void RemoveNodePosition(Node* node, Node* context)
|
||||
{
|
||||
PositionMap& map = position_map_[context];
|
||||
map.remove(node);
|
||||
if (map.isEmpty()) {
|
||||
position_map_.remove(context);
|
||||
}
|
||||
emit NodePositionRemoved(node, context);
|
||||
}
|
||||
|
||||
bool ContextContainsNode(Node *node, Node *context)
|
||||
{
|
||||
return position_map_[context].contains(node);
|
||||
}
|
||||
|
||||
qreal GetNodeContextHeight(Node *context);
|
||||
|
||||
using PositionMap = QHash<Node*, QPointF>;
|
||||
|
||||
const PositionMap &GetNodesForContext(Node *context)
|
||||
{
|
||||
return position_map_[context];
|
||||
}
|
||||
|
||||
const QMap<Node *, PositionMap> &GetPositionMap() const
|
||||
{
|
||||
return position_map_;
|
||||
}
|
||||
|
||||
int GetNumberOfContextsNodeIsIn(Node *node) const;
|
||||
|
||||
bool NodeOutputsToContext(Node *node) const;
|
||||
int GetNumberOfContextsNodeIsIn(Node *node, bool except_itself = false) const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
@@ -131,9 +85,11 @@ signals:
|
||||
|
||||
void InputValueHintChanged(const NodeInput& input);
|
||||
|
||||
void NodePositionAdded(Node *node, Node *relative, const QPointF &position);
|
||||
void GroupAddedInputPassthrough(NodeGroup *group, const NodeInput &input);
|
||||
|
||||
void NodePositionRemoved(Node *node, Node *relative);
|
||||
void GroupRemovedInputPassthrough(NodeGroup *group, const NodeInput &input);
|
||||
|
||||
void GroupChangedOutputPassthrough(NodeGroup *group, Node *output);
|
||||
|
||||
protected:
|
||||
void AddDefaultNode(Node* n)
|
||||
@@ -148,10 +104,6 @@ private:
|
||||
|
||||
QVector<Node*> default_nodes_;
|
||||
|
||||
QMap<Node *, PositionMap> position_map_;
|
||||
|
||||
PositionMap root_position_map_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/group/group.cpp
|
||||
node/group/group.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,215 @@
|
||||
/***
|
||||
|
||||
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 "group.h"
|
||||
|
||||
#include "node/graph.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super Node
|
||||
|
||||
NodeGroup::NodeGroup() :
|
||||
output_passthrough_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
QString NodeGroup::Name() const
|
||||
{
|
||||
return tr("Group");
|
||||
}
|
||||
|
||||
QString NodeGroup::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.group");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> NodeGroup::Category() const
|
||||
{
|
||||
return {kCategoryGeneral};
|
||||
}
|
||||
|
||||
QString NodeGroup::Description() const
|
||||
{
|
||||
return tr("A group of nodes that is represented as a single node.");
|
||||
}
|
||||
|
||||
void NodeGroup::Retranslate()
|
||||
{
|
||||
for (auto it=GetContextPositions().cbegin(); it!=GetContextPositions().cend(); it++) {
|
||||
it.key()->Retranslate();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGroup::AddInputPassthrough(const NodeInput &input)
|
||||
{
|
||||
Q_ASSERT(ContextContainsNode(input.node()));
|
||||
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it.value() == input) {
|
||||
// Already passing this input through
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add input
|
||||
QString id = GetGroupInputIDFromInput(input);
|
||||
|
||||
AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags());
|
||||
|
||||
input_passthroughs_.insert(id, input);
|
||||
|
||||
emit InputPassthroughAdded(this, input);
|
||||
}
|
||||
|
||||
void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
|
||||
{
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it.value() == input) {
|
||||
RemoveInput(it.key());
|
||||
input_passthroughs_.erase(it);
|
||||
emit InputPassthroughRemoved(this, it.value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGroup::SetOutputPassthrough(Node *node)
|
||||
{
|
||||
Q_ASSERT(!node || ContextContainsNode(node));
|
||||
|
||||
output_passthrough_ = node;
|
||||
|
||||
emit OutputPassthroughChanged(this, output_passthrough_);
|
||||
}
|
||||
|
||||
QString NodeGroup::GetGroupInputIDFromInput(const NodeInput &input)
|
||||
{
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
|
||||
hash.addData(input.node()->GetUUID().toByteArray());
|
||||
|
||||
hash.addData(input.input().toUtf8());
|
||||
|
||||
hash.addData((const char*) &input.element(), sizeof(input.element()));
|
||||
|
||||
return QString::fromLatin1(hash.result().toHex());
|
||||
}
|
||||
|
||||
bool NodeGroup::ContainsInputPassthrough(const NodeInput &input) const
|
||||
{
|
||||
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
|
||||
if (it.value() == input) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString NodeGroup::GetInputName(const QString &id) const
|
||||
{
|
||||
return input_passthroughs_.value(id).name();
|
||||
}
|
||||
|
||||
bool NodeGroup::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled)
|
||||
{
|
||||
if (reader->name() == QStringLiteral("inputpassthroughs")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("inputpassthrough")) {
|
||||
XMLNodeData::GroupLink link;
|
||||
|
||||
link.group = this;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
link.input_node = reader->readElementText().toULongLong();
|
||||
} else if (reader->name() == QStringLiteral("input")) {
|
||||
link.input_id = reader->readElementText();
|
||||
} else if (reader->name() == QStringLiteral("element")) {
|
||||
link.input_element = reader->readElementText().toInt();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
xml_node_data.group_input_links.append(link);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (reader->name() == QStringLiteral("outputpassthrough")) {
|
||||
xml_node_data.group_output_links.insert(this, reader->readElementText().toULongLong());
|
||||
return true;
|
||||
} else {
|
||||
return super::LoadCustom(reader, xml_node_data, version, cancelled);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGroup::SaveCustom(QXmlStreamWriter *writer) const
|
||||
{
|
||||
super::SaveCustom(writer);
|
||||
|
||||
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
|
||||
|
||||
foreach (const NodeInput &ip, input_passthroughs_) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthrough"));
|
||||
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.node())));
|
||||
writer->writeTextElement(QStringLiteral("input"), ip.input());
|
||||
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.element()));
|
||||
writer->writeEndElement(); // input
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // inputpassthroughs
|
||||
|
||||
writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast<quintptr>(output_passthrough_)));
|
||||
}
|
||||
|
||||
void NodeGroupAddInputPassthrough::redo()
|
||||
{
|
||||
if (!group_->ContainsInputPassthrough(input_)) {
|
||||
group_->AddInputPassthrough(input_);
|
||||
actually_added_ = true;
|
||||
} else {
|
||||
actually_added_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGroupAddInputPassthrough::undo()
|
||||
{
|
||||
if (actually_added_) {
|
||||
group_->RemoveInputPassthrough(input_);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGroupSetOutputPassthrough::redo()
|
||||
{
|
||||
old_output_ = group_->GetOutputPassthrough();
|
||||
group_->SetOutputPassthrough(new_output_);
|
||||
}
|
||||
|
||||
void NodeGroupSetOutputPassthrough::undo()
|
||||
{
|
||||
group_->SetOutputPassthrough(old_output_);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/***
|
||||
|
||||
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 NODEGROUP_H
|
||||
#define NODEGROUP_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeGroup : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeGroup();
|
||||
|
||||
NODE_DEFAULT_DESTRUCTOR(NodeGroup)
|
||||
NODE_COPY_FUNCTION(NodeGroup)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
void AddInputPassthrough(const NodeInput &input);
|
||||
|
||||
void RemoveInputPassthrough(const NodeInput &input);
|
||||
|
||||
Node *GetOutputPassthrough() const
|
||||
{
|
||||
return output_passthrough_;
|
||||
}
|
||||
|
||||
void SetOutputPassthrough(Node *node);
|
||||
|
||||
static QString GetGroupInputIDFromInput(const NodeInput &input);
|
||||
|
||||
const QHash<QString, NodeInput> &GetInputPassthroughs() const
|
||||
{
|
||||
return input_passthroughs_;
|
||||
}
|
||||
|
||||
bool ContainsInputPassthrough(const NodeInput &input) const;
|
||||
|
||||
virtual QString GetInputName(const QString& id) const override;
|
||||
|
||||
signals:
|
||||
void InputPassthroughAdded(NodeGroup *group, const NodeInput &input);
|
||||
|
||||
void InputPassthroughRemoved(NodeGroup *group, const NodeInput &input);
|
||||
|
||||
void OutputPassthroughChanged(NodeGroup *group, Node *output);
|
||||
|
||||
protected:
|
||||
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled) override;
|
||||
|
||||
virtual void SaveCustom(QXmlStreamWriter* writer) const override;
|
||||
|
||||
private:
|
||||
QHash<QString, NodeInput> input_passthroughs_;
|
||||
|
||||
Node *output_passthrough_;
|
||||
|
||||
};
|
||||
|
||||
class NodeGroupAddInputPassthrough : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeGroupAddInputPassthrough(NodeGroup *group, const NodeInput &input) :
|
||||
group_(group),
|
||||
input_(input),
|
||||
actually_added_(false)
|
||||
{}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return group_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeGroup *group_;
|
||||
|
||||
NodeInput input_;
|
||||
|
||||
bool actually_added_;
|
||||
|
||||
};
|
||||
|
||||
class NodeGroupSetOutputPassthrough : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeGroupSetOutputPassthrough(NodeGroup *group, Node *output) :
|
||||
group_(group),
|
||||
new_output_(output)
|
||||
{}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return group_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeGroup *group_;
|
||||
|
||||
Node *new_output_;
|
||||
Node *old_output_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEGROUP_H
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
* @brief Methods of interpolation to use with this keyframe
|
||||
*/
|
||||
enum Type {
|
||||
kInvalid = -1,
|
||||
kLinear,
|
||||
kHold,
|
||||
kBezier
|
||||
|
||||
@@ -32,6 +32,8 @@ MergeNode::MergeNode()
|
||||
AddInput(kBaseIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kBlendIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
SetFlags(kDontShowInParamView);
|
||||
}
|
||||
|
||||
Node *MergeNode::copy() const
|
||||
|
||||
+130
-130
@@ -47,8 +47,10 @@ Node::Node() :
|
||||
override_color_(-1),
|
||||
folder_(nullptr),
|
||||
operation_stack_(0),
|
||||
cache_result_(false)
|
||||
cache_result_(false),
|
||||
flags_(kNone)
|
||||
{
|
||||
uuid_ = QUuid::createUuid();
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
@@ -88,6 +90,8 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi
|
||||
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this);
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("uuid")) {
|
||||
SetUUID(QUuid::fromString(reader->readElementText()));
|
||||
} else if (reader->name() == QStringLiteral("color")) {
|
||||
override_color_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("links")) {
|
||||
@@ -169,6 +173,7 @@ void Node::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), uuid_.toString());
|
||||
writer->writeTextElement(QStringLiteral("label"), GetLabel());
|
||||
writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_));
|
||||
|
||||
@@ -219,7 +224,16 @@ void Node::Save(QXmlStreamWriter *writer) const
|
||||
|
||||
Project* Node::project() const
|
||||
{
|
||||
return dynamic_cast<Project*>(parent());
|
||||
QObject *t = this->parent();
|
||||
|
||||
while (t) {
|
||||
if (Project *p = dynamic_cast<Project*>(t)) {
|
||||
return p;
|
||||
}
|
||||
t = t->parent();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString Node::ShortName() const
|
||||
@@ -243,6 +257,40 @@ QIcon Node::icon() const
|
||||
return icon::New;
|
||||
}
|
||||
|
||||
bool Node::SetNodePositionInContext(Node *node, const QPointF &pos)
|
||||
{
|
||||
Position p = context_positions_.value(node);
|
||||
|
||||
p.position = pos;
|
||||
|
||||
return SetNodePositionInContext(node, p);
|
||||
}
|
||||
|
||||
bool Node::SetNodePositionInContext(Node *node, const Position &pos)
|
||||
{
|
||||
bool added = !ContextContainsNode(node);
|
||||
context_positions_.insert(node, pos);
|
||||
|
||||
if (added) {
|
||||
emit NodeAddedToContext(node);
|
||||
}
|
||||
|
||||
emit NodePositionInContextChanged(node, pos.position);
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
bool Node::RemoveNodeFromContext(Node *node)
|
||||
{
|
||||
if (ContextContainsNode(node)) {
|
||||
context_positions_.remove(node);
|
||||
emit NodeRemovedFromContext(node);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Color Node::color() const
|
||||
{
|
||||
int c;
|
||||
@@ -429,6 +477,11 @@ void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const
|
||||
writer->writeEndElement(); // subelements
|
||||
}
|
||||
|
||||
bool Node::IsInputHidden(const QString &input) const
|
||||
{
|
||||
return (GetInputFlags(input) & kInputFlagHidden);
|
||||
}
|
||||
|
||||
bool Node::IsInputConnectable(const QString &input) const
|
||||
{
|
||||
return !(GetInputFlags(input) & kInputFlagNotConnectable);
|
||||
@@ -1050,7 +1103,7 @@ NodeInputImmediate *Node::GetImmediate(const QString &input, int element) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node::InputFlags Node::GetInputFlags(const QString &input) const
|
||||
InputFlags Node::GetInputFlags(const QString &input) const
|
||||
{
|
||||
const Input* i = GetInternalInputData(input);
|
||||
|
||||
@@ -1196,13 +1249,10 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& cre
|
||||
command->add_child(new NodeSetValueHintCommand(copied_input, node->GetValueHintForInput(input.input(), input.element())));
|
||||
}
|
||||
|
||||
if (node->parent()->GetPositionMap().contains(node)) {
|
||||
// This node is a context, copy the context
|
||||
const NodeGraph::PositionMap &map = node->parent()->GetPositionMap().value(node);
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
// Add either the copy (if it exists) or the original node to the context
|
||||
command->add_child(new NodeSetPositionCommand(created.value(it.key(), it.key()), copy, it.value(), false));
|
||||
}
|
||||
const PositionMap &map = node->GetContextPositions();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
// Add either the copy (if it exists) or the original node to the context
|
||||
command->add_child(new NodeSetPositionCommand(created.value(it.key(), it.key()), copy, it.value()));
|
||||
}
|
||||
|
||||
return copy;
|
||||
@@ -1229,13 +1279,10 @@ Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command)
|
||||
|
||||
command->add_child(new NodeCopyInputsCommand(node, copy, true));
|
||||
|
||||
if (node->parent()->GetPositionMap().contains(node)) {
|
||||
// This node is a context, copy the context
|
||||
const NodeGraph::PositionMap &map = node->parent()->GetPositionMap().value(node);
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
// Add to the context
|
||||
command->add_child(new NodeSetPositionCommand(it.key(), copy, it.value(), false));
|
||||
}
|
||||
const PositionMap &map = node->GetContextPositions();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
// Add to the context
|
||||
command->add_child(new NodeSetPositionCommand(it.key(), copy, it.value()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1310,7 +1357,7 @@ void Node::HashAddNodeSignature(QCryptographicHash &hash) const
|
||||
hash.addData(id().toUtf8());
|
||||
}
|
||||
|
||||
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, Node::InputFlags flags, int index)
|
||||
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, InputFlags flags, int index)
|
||||
{
|
||||
if (id.isEmpty()) {
|
||||
qWarning() << "Rejected adding input with an empty ID on node" << this->id();
|
||||
@@ -1457,7 +1504,7 @@ void Node::GizmoMove(const QPointF &, const rational&, const Qt::KeyboardModifie
|
||||
{
|
||||
}
|
||||
|
||||
void Node::GizmoRelease()
|
||||
void Node::GizmoRelease(MultiUndoCommand *)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1564,7 +1611,9 @@ void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input,
|
||||
dst->SetSplitStandardValue(input, src->GetSplitStandardValue(input, src_element), dst_element);
|
||||
|
||||
// Copy keyframes
|
||||
dst->GetImmediate(input, dst_element)->delete_all_keyframes();
|
||||
if (NodeInputImmediate *immediate = dst->GetImmediate(input, dst_element)) {
|
||||
immediate->delete_all_keyframes();
|
||||
}
|
||||
foreach (const NodeKeyframeTrack& track, src->GetImmediate(input, src_element)->keyframe_tracks()) {
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
key->copy(dst_element, dst);
|
||||
@@ -2198,7 +2247,6 @@ void Node::childEvent(QChildEvent *event)
|
||||
GetImmediate(key->input(), key->element())->insert_keyframe(key);
|
||||
|
||||
connect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange);
|
||||
connect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged);
|
||||
connect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange);
|
||||
connect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged);
|
||||
connect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange);
|
||||
@@ -2210,15 +2258,14 @@ void Node::childEvent(QChildEvent *event)
|
||||
TimeRange time_affected = GetRangeAffectedByKeyframe(key);
|
||||
|
||||
disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange);
|
||||
disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged);
|
||||
disconnect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange);
|
||||
disconnect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged);
|
||||
disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange);
|
||||
disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &Node::InvalidateFromKeyframeBezierOutChange);
|
||||
|
||||
GetImmediate(key->input(), key->element())->remove_keyframe(key);
|
||||
|
||||
emit KeyframeRemoved(key);
|
||||
|
||||
GetImmediate(key->input(), key->element())->remove_keyframe(key);
|
||||
ParameterValueChanged(i, time_affected);
|
||||
}
|
||||
}
|
||||
@@ -2281,12 +2328,16 @@ void Node::InvalidateFromKeyframeTimeChange()
|
||||
foreach (const TimeRange& r, invalidate_range) {
|
||||
ParameterValueChanged(key->key_track_ref().input(), r);
|
||||
}
|
||||
|
||||
emit KeyframeTimeChanged(key);
|
||||
}
|
||||
|
||||
void Node::InvalidateFromKeyframeValueChange()
|
||||
{
|
||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
||||
ParameterValueChanged(key->key_track_ref().input(), GetRangeAffectedByKeyframe(key));
|
||||
|
||||
emit KeyframeValueChanged(key);
|
||||
}
|
||||
|
||||
void Node::InvalidateFromKeyframeTypeChanged()
|
||||
@@ -2301,6 +2352,8 @@ void Node::InvalidateFromKeyframeTypeChanged()
|
||||
|
||||
// Invalidate entire range
|
||||
ParameterValueChanged(key->key_track_ref().input(), GetRangeAroundIndex(key->input(), track.indexOf(key), key->track(), key->element()));
|
||||
|
||||
emit KeyframeTypeChanged(key);
|
||||
}
|
||||
|
||||
Project *Node::ArrayInsertCommand::GetRelevantProject() const
|
||||
@@ -2318,119 +2371,40 @@ Project *Node::ArrayResizeCommand::GetRelevantProject() const
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
void NodeSetPositionAndShiftSurroundingsCommand::redo()
|
||||
{
|
||||
if (commands_.isEmpty()) {
|
||||
// Move first node
|
||||
NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, relative_, position_, move_dependencies_);
|
||||
set_pos_command->redo_now();
|
||||
commands_.append(set_pos_command);
|
||||
|
||||
// Get bounding rect
|
||||
qreal bounding_rect_sz = 1.0;
|
||||
qreal bounding_rect_half_sz = bounding_rect_sz * 0.5;
|
||||
QRectF bounding_rect(position_.x() - bounding_rect_half_sz, position_.y() - bounding_rect_half_sz, bounding_rect_sz, bounding_rect_sz);
|
||||
|
||||
// Start moving other nodes
|
||||
foreach (Node* surrounding, node_->parent()->nodes()) {
|
||||
if (surrounding != node_) {
|
||||
QPointF surrounding_position = node_->parent()->GetNodePosition(surrounding, relative_);
|
||||
if (bounding_rect.contains(surrounding_position)) {
|
||||
QPointF new_pos = surrounding_position;
|
||||
|
||||
qreal move_rate = 0.50;
|
||||
|
||||
if (surrounding_position.y() < position_.y()) {
|
||||
move_rate = -move_rate;
|
||||
}
|
||||
|
||||
new_pos.setY(new_pos.y() + move_rate);
|
||||
|
||||
auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, relative_, new_pos, true);
|
||||
sur_command->redo();
|
||||
commands_.append(sur_command);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<commands_.size(); i++) {
|
||||
commands_.at(i)->redo_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeSetPositionCommand::redo()
|
||||
{
|
||||
graph_ = node_->parent();
|
||||
if (!(added_ = !graph_->NodeMapContainsNode(node_, relevant_))) {
|
||||
old_pos_ = graph_->GetNodePosition(node_, relevant_);
|
||||
added_ = !context_->ContextContainsNode(node_);
|
||||
|
||||
if (!added_) {
|
||||
old_pos_ = context_->GetNodePositionDataInContext(node_);
|
||||
}
|
||||
graph_->SetNodePosition(node_, relevant_, pos_);
|
||||
|
||||
context_->SetNodePositionInContext(node_, pos_);
|
||||
}
|
||||
|
||||
void NodeSetPositionCommand::undo()
|
||||
{
|
||||
if (added_) {
|
||||
graph_->RemoveNodePosition(node_, relevant_);
|
||||
context_->RemoveNodeFromContext(node_);
|
||||
} else {
|
||||
graph_->SetNodePosition(node_, relevant_, old_pos_);
|
||||
context_->SetNodePositionInContext(node_, old_pos_);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeSetPositionAsChildCommand::redo()
|
||||
{
|
||||
if (!sub_command_) {
|
||||
// Calculate position of node
|
||||
NodeGraph *graph = parent_->parent();
|
||||
QPointF pos = graph->GetNodePosition(parent_, relative_);
|
||||
|
||||
// This is a dependency, so we'll place it one X before
|
||||
pos.setX(pos.x() - 1);
|
||||
|
||||
// The Y will be calculated using the index and child count
|
||||
pos.setY(pos.y() - (double(child_count_)*0.5) + this_index_ + 0.5);
|
||||
|
||||
sub_command_ = new MultiUndoCommand();
|
||||
if (shift_surroundings_) {
|
||||
sub_command_->add_child(new NodeSetPositionAndShiftSurroundingsCommand(node_, relative_, pos, true));
|
||||
} else {
|
||||
sub_command_->add_child(new NodeSetPositionCommand(node_, relative_, pos, true));
|
||||
}
|
||||
}
|
||||
|
||||
sub_command_->redo_now();
|
||||
}
|
||||
|
||||
void NodeSetPositionToOffsetOfAnotherNodeCommand::redo()
|
||||
{
|
||||
NodeGraph *graph = node_->parent();
|
||||
old_pos_ = graph->GetNodePosition(node_, relative_);
|
||||
graph->SetNodePosition(node_, relative_, graph->GetNodePosition(other_node_, relative_) + offset_);
|
||||
}
|
||||
|
||||
void NodeSetPositionToOffsetOfAnotherNodeCommand::undo()
|
||||
{
|
||||
NodeGraph *graph = node_->parent();
|
||||
graph->SetNodePosition(node_, relative_, old_pos_);
|
||||
}
|
||||
|
||||
void NodeRemovePositionFromContextCommand::redo()
|
||||
{
|
||||
NodeGraph *graph = node_->parent();
|
||||
|
||||
contained_ = graph->ContextContainsNode(node_, context_);
|
||||
contained_ = context_->ContextContainsNode(node_);
|
||||
|
||||
if (contained_) {
|
||||
old_pos_ = graph->GetNodePosition(node_, context_);
|
||||
graph->RemoveNodePosition(node_, context_);
|
||||
old_pos_ = context_->GetNodePositionDataInContext(node_);
|
||||
context_->RemoveNodeFromContext(node_);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRemovePositionFromContextCommand::undo()
|
||||
{
|
||||
if (contained_) {
|
||||
NodeGraph *graph = node_->parent();
|
||||
graph->SetNodePosition(node_, context_, old_pos_);
|
||||
context_->SetNodePositionInContext(node_, old_pos_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2438,28 +2412,21 @@ void NodeRemovePositionFromAllContextsCommand::redo()
|
||||
{
|
||||
NodeGraph *graph = node_->parent();
|
||||
|
||||
if (points_.empty()) {
|
||||
// No points yet, let's see what points we should remove
|
||||
auto map = graph->GetPositionMap();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
if (it.value().contains(node_)) {
|
||||
points_.insert({it.key(), it.value().value(node_)});
|
||||
}
|
||||
foreach (Node* context, graph->nodes()) {
|
||||
if (context->ContextContainsNode(node_)) {
|
||||
contexts_.insert({context, context->GetNodePositionInContext(node_)});
|
||||
context->RemoveNodeFromContext(node_);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=points_.cbegin(); it!=points_.cend(); it++) {
|
||||
graph->RemoveNodePosition(node_, it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRemovePositionFromAllContextsCommand::undo()
|
||||
{
|
||||
NodeGraph *graph = node_->parent();
|
||||
|
||||
for (auto it=points_.crbegin(); it!=points_.crend(); it++) {
|
||||
graph->SetNodePosition(node_, it->first, it->second);
|
||||
for (auto it = contexts_.crbegin(); it != contexts_.crend(); it++) {
|
||||
it->first->SetNodePositionInContext(node_, it->second);
|
||||
}
|
||||
|
||||
contexts_.clear();
|
||||
}
|
||||
|
||||
void Node::ValueHint::Hash(QCryptographicHash &hash) const
|
||||
@@ -2508,4 +2475,37 @@ void Node::ValueHint::Save(QXmlStreamWriter *writer) const
|
||||
writer->writeTextElement(QStringLiteral("tag"), tag_);
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::prepare()
|
||||
{
|
||||
move_recursively(node_, pos_.position - context_->GetNodePositionDataInContext(node_).position);
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::redo()
|
||||
{
|
||||
for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) {
|
||||
(*it)->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::undo()
|
||||
{
|
||||
for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) {
|
||||
(*it)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(Node *node, const QPointF &diff)
|
||||
{
|
||||
Node::Position pos = context_->GetNodePositionDataInContext(node);
|
||||
pos += diff;
|
||||
commands_.append(new NodeSetPositionCommand(node_, context_, pos));
|
||||
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
Node *output = it->second;
|
||||
if (context_->ContextContainsNode(output)) {
|
||||
move_recursively(output, diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+133
-170
@@ -27,6 +27,7 @@
|
||||
#include <QObject>
|
||||
#include <QPainter>
|
||||
#include <QPointF>
|
||||
#include <QUuid>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "codec/frame.h"
|
||||
@@ -93,6 +94,11 @@ public:
|
||||
kCategoryCount
|
||||
};
|
||||
|
||||
enum Flag {
|
||||
kNone = 0,
|
||||
kDontShowInParamView = 0x1
|
||||
};
|
||||
|
||||
Node();
|
||||
|
||||
virtual ~Node() override;
|
||||
@@ -112,6 +118,14 @@ public:
|
||||
|
||||
Project* project() const;
|
||||
|
||||
const QUuid &GetUUID() const {return uuid_;}
|
||||
void SetUUID(const QUuid &uuid) {uuid_ = uuid;}
|
||||
|
||||
const uint64_t &GetFlags() const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear current node variables and replace them with
|
||||
*/
|
||||
@@ -208,6 +222,79 @@ public:
|
||||
return HasInputWithID(id);
|
||||
}
|
||||
|
||||
struct Position
|
||||
{
|
||||
Position(const QPointF &p = QPointF(0, 0), bool e = false)
|
||||
{
|
||||
position = p;
|
||||
expanded = e;
|
||||
}
|
||||
|
||||
QPointF position;
|
||||
bool expanded;
|
||||
|
||||
inline Position &operator+=(const Position &p)
|
||||
{
|
||||
position += p.position;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Position &operator-=(const Position &p)
|
||||
{
|
||||
position -= p.position;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend inline const Position operator+(Position a, const Position &b)
|
||||
{
|
||||
a += b;
|
||||
return a;
|
||||
}
|
||||
|
||||
friend inline const Position operator-(Position a, const Position &b)
|
||||
{
|
||||
a -= b;
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
using PositionMap = QHash<Node*, Position>;
|
||||
const PositionMap &GetContextPositions() const
|
||||
{
|
||||
return context_positions_;
|
||||
}
|
||||
|
||||
bool IsNodeExpandedInContext(Node *node) const
|
||||
{
|
||||
return context_positions_.value(node).expanded;
|
||||
}
|
||||
|
||||
bool ContextContainsNode(Node *node) const
|
||||
{
|
||||
return context_positions_.contains(node);
|
||||
}
|
||||
|
||||
Position GetNodePositionDataInContext(Node *node)
|
||||
{
|
||||
return context_positions_.value(node);
|
||||
}
|
||||
|
||||
QPointF GetNodePositionInContext(Node *node)
|
||||
{
|
||||
return GetNodePositionDataInContext(node).position;
|
||||
}
|
||||
|
||||
bool SetNodePositionInContext(Node *node, const QPointF &pos);
|
||||
|
||||
bool SetNodePositionInContext(Node *node, const Position &pos);
|
||||
|
||||
void SetNodeExpandedInContext(Node *node, bool e)
|
||||
{
|
||||
context_positions_[node].expanded = e;
|
||||
}
|
||||
|
||||
bool RemoveNodeFromContext(Node *node);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the color of this node
|
||||
*/
|
||||
@@ -243,11 +330,12 @@ public:
|
||||
|
||||
static void DisconnectEdge(Node *output, const NodeInput& input);
|
||||
|
||||
QString GetInputName(const QString& id) const;
|
||||
virtual QString GetInputName(const QString& id) const;
|
||||
|
||||
void LoadInput(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled);
|
||||
void SaveInput(QXmlStreamWriter* writer, const QString& id) const;
|
||||
|
||||
bool IsInputHidden(const QString& input) const;
|
||||
bool IsInputConnectable(const QString& input) const;
|
||||
bool IsInputKeyframable(const QString& input) const;
|
||||
|
||||
@@ -767,7 +855,7 @@ public:
|
||||
|
||||
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF& p);
|
||||
virtual void GizmoMove(const QPointF& p, const rational &time, const Qt::KeyboardModifiers &modifiers);
|
||||
virtual void GizmoRelease();
|
||||
virtual void GizmoRelease(MultiUndoCommand *command);
|
||||
|
||||
const QString& GetLabel() const;
|
||||
void SetLabel(const QString& s);
|
||||
@@ -861,37 +949,9 @@ public:
|
||||
|
||||
};
|
||||
|
||||
InputFlags GetInputFlags(const QString& input) const;
|
||||
|
||||
protected:
|
||||
enum InputFlag {
|
||||
/// By default, inputs are keyframable, connectable, and NOT arrays
|
||||
kInputFlagNormal = 0x0,
|
||||
kInputFlagArray = 0x1,
|
||||
kInputFlagNotKeyframable = 0x2,
|
||||
kInputFlagNotConnectable = 0x4
|
||||
};
|
||||
|
||||
class InputFlags {
|
||||
public:
|
||||
explicit InputFlags()
|
||||
{
|
||||
f_ = kInputFlagNormal;
|
||||
}
|
||||
|
||||
explicit InputFlags(uint64_t flags)
|
||||
{
|
||||
f_ = flags;
|
||||
}
|
||||
|
||||
bool operator&(const InputFlag& f) const
|
||||
{
|
||||
return f_ & f;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t f_;
|
||||
|
||||
};
|
||||
|
||||
virtual void Hash(QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params) const;
|
||||
|
||||
void HashAddNodeSignature(QCryptographicHash &hash) const;
|
||||
@@ -986,12 +1046,12 @@ protected:
|
||||
tooltip_ = s;
|
||||
}
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted whenever the position is set through SetPosition()
|
||||
*/
|
||||
void PositionChanged(const QPointF& pos);
|
||||
void SetFlags(const uint64_t &f)
|
||||
{
|
||||
flags_ = f;
|
||||
}
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when SetLabel() is called
|
||||
*/
|
||||
@@ -1021,7 +1081,11 @@ signals:
|
||||
|
||||
void KeyframeRemoved(NodeKeyframe* key);
|
||||
|
||||
void KeyframeTimeChanged();
|
||||
void KeyframeTimeChanged(NodeKeyframe* key);
|
||||
|
||||
void KeyframeTypeChanged(NodeKeyframe* key);
|
||||
|
||||
void KeyframeValueChanged(NodeKeyframe* key);
|
||||
|
||||
void KeyframeEnableChanged(const NodeInput& input, bool enabled);
|
||||
|
||||
@@ -1037,6 +1101,12 @@ signals:
|
||||
|
||||
void RemovedFromGraph(NodeGraph* graph);
|
||||
|
||||
void NodeAddedToContext(Node *node);
|
||||
|
||||
void NodePositionInContextChanged(Node *node, const QPointF &pos);
|
||||
|
||||
void NodeRemovedFromContext(Node *node);
|
||||
|
||||
private:
|
||||
class ArrayInsertCommand : public UndoCommand
|
||||
{
|
||||
@@ -1140,8 +1210,6 @@ private:
|
||||
return input_ids_.indexOf(input);
|
||||
}
|
||||
|
||||
InputFlags GetInputFlags(const QString& input) const;
|
||||
|
||||
Input* GetInternalInputData(const QString& input)
|
||||
{
|
||||
int i = GetInternalInputIndex(input);
|
||||
@@ -1258,6 +1326,12 @@ private:
|
||||
|
||||
QMap<InputElementPair, ValueHint> value_hints_;
|
||||
|
||||
PositionMap context_positions_;
|
||||
|
||||
QUuid uuid_;
|
||||
|
||||
uint64_t flags_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
|
||||
@@ -1367,12 +1441,11 @@ using NodePtr = std::shared_ptr<Node>;
|
||||
class NodeSetPositionCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeSetPositionCommand(Node* node, Node* relevant, const QPointF& pos, bool move_dependencies_relatively)
|
||||
NodeSetPositionCommand(Node* node, Node* context, const Node::Position& pos)
|
||||
{
|
||||
node_ = node;
|
||||
relevant_ = relevant;
|
||||
context_ = context;
|
||||
pos_ = pos;
|
||||
move_deps_ = move_dependencies_relatively;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
@@ -1387,153 +1460,43 @@ protected:
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
Node* relevant_;
|
||||
QPointF pos_;
|
||||
QPointF old_pos_;
|
||||
Node* context_;
|
||||
Node::Position pos_;
|
||||
Node::Position old_pos_;
|
||||
bool added_;
|
||||
bool move_deps_;
|
||||
NodeGraph *graph_;
|
||||
|
||||
};
|
||||
|
||||
class NodeSetPositionAndShiftSurroundingsCommand : public UndoCommand
|
||||
{
|
||||
class NodeSetPositionAndDependenciesRecursivelyCommand : public UndoCommand{
|
||||
public:
|
||||
NodeSetPositionAndShiftSurroundingsCommand(Node* node, Node *relative, const QPointF& pos, bool move_dependencies_relatively) :
|
||||
NodeSetPositionAndDependenciesRecursivelyCommand(Node* node, Node* context, const Node::Position& pos) :
|
||||
node_(node),
|
||||
relative_(relative),
|
||||
position_(pos),
|
||||
move_dependencies_(move_dependencies_relatively)
|
||||
context_(context),
|
||||
pos_(pos)
|
||||
{}
|
||||
|
||||
virtual ~NodeSetPositionAndShiftSurroundingsCommand() override
|
||||
{
|
||||
qDeleteAll(commands_);
|
||||
}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
for (int i=commands_.size()-1; i>=0; i--) {
|
||||
commands_.at(i)->undo_now();
|
||||
}
|
||||
}
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
void move_recursively(Node *node, const QPointF &diff);
|
||||
|
||||
Node* node_;
|
||||
|
||||
Node *relative_;
|
||||
|
||||
QPointF position_;
|
||||
|
||||
bool move_dependencies_;
|
||||
|
||||
Node* context_;
|
||||
Node::Position pos_;
|
||||
QVector<UndoCommand*> commands_;
|
||||
|
||||
};
|
||||
|
||||
class NodeSetPositionAsChildCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeSetPositionAsChildCommand(Node* node, Node* parent, Node *relative, double this_index, int child_count, bool shift_surroundings) :
|
||||
node_(node),
|
||||
parent_(parent),
|
||||
relative_(relative),
|
||||
this_index_(this_index),
|
||||
child_count_(child_count),
|
||||
shift_surroundings_(shift_surroundings),
|
||||
sub_command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~NodeSetPositionAsChildCommand() override
|
||||
{
|
||||
delete sub_command_;
|
||||
}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
sub_command_->undo_now();
|
||||
}
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
Node* parent_;
|
||||
Node *relative_;
|
||||
|
||||
double this_index_;
|
||||
int child_count_;
|
||||
|
||||
bool shift_surroundings_;
|
||||
|
||||
MultiUndoCommand* sub_command_;
|
||||
|
||||
};
|
||||
|
||||
class NodePositionCloseChildGapCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodePositionCloseChildGapCommand(Node *parent, void *relative, int remove_index, int child_count, bool shift_surroundings);
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return parent_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node *parent_;
|
||||
|
||||
};
|
||||
|
||||
class NodeSetPositionToOffsetOfAnotherNodeCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeSetPositionToOffsetOfAnotherNodeCommand(Node* node, Node* other_node, Node *relative, const QPointF& offset) :
|
||||
node_(node),
|
||||
other_node_(other_node),
|
||||
relative_(relative),
|
||||
offset_(offset)
|
||||
{}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
Node* other_node_;
|
||||
Node *relative_;
|
||||
QPointF offset_;
|
||||
QPointF old_pos_;
|
||||
|
||||
};
|
||||
|
||||
class NodeRemovePositionFromContextCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
@@ -1558,7 +1521,7 @@ private:
|
||||
|
||||
Node *context_;
|
||||
|
||||
QPointF old_pos_;
|
||||
Node::Position old_pos_;
|
||||
|
||||
bool contained_;
|
||||
|
||||
@@ -1585,7 +1548,7 @@ protected:
|
||||
private:
|
||||
Node *node_;
|
||||
|
||||
std::map<Node *, QPointF> points_;
|
||||
std::map<Node *, QPointF> contexts_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+32
-18
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
void NodeCopyPasteService::CopyNodesToClipboard(const QVector<Node *> &nodes, void *userdata)
|
||||
void NodeCopyPasteService::CopyNodesToClipboard(QVector<Node *> nodes, void *userdata)
|
||||
{
|
||||
QString copy_str;
|
||||
|
||||
@@ -42,22 +42,33 @@ void NodeCopyPasteService::CopyNodesToClipboard(const QVector<Node *> &nodes, vo
|
||||
writer.writeTextElement(QStringLiteral("version"), QString::number(Core::kProjectVersion));
|
||||
|
||||
writer.writeStartElement(QStringLiteral("nodes"));
|
||||
foreach (Node* n, nodes) {
|
||||
for (int i=0; i<nodes.size(); i++) {
|
||||
Node *n = nodes.at(i);
|
||||
|
||||
writer.writeStartElement(QStringLiteral("node"));
|
||||
writer.writeAttribute(QStringLiteral("id"), n->id());
|
||||
n->Save(&writer);
|
||||
writer.writeEndElement(); // node
|
||||
|
||||
// If this is a group, add the child nodes too
|
||||
if (NodeGroup *g = dynamic_cast<NodeGroup*>(n)) {
|
||||
for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) {
|
||||
if (!nodes.contains(it.key())) {
|
||||
nodes.append(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.writeEndElement(); // nodes
|
||||
|
||||
writer.writeStartElement(QStringLiteral("contexts"));
|
||||
foreach (Node* n, nodes) {
|
||||
// Determine if this node is a context
|
||||
if (n->parent()->GetPositionMap().contains(n)) {
|
||||
if (!n->GetContextPositions().isEmpty()) {
|
||||
writer.writeStartElement(QStringLiteral("context"));
|
||||
writer.writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(n)));
|
||||
|
||||
const NodeGraph::PositionMap &map = n->parent()->GetNodesForContext(n);
|
||||
const Node::PositionMap &map = n->GetContextPositions();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
writer.writeStartElement(QStringLiteral("node"));
|
||||
Project::SavePosition(&writer, it.key(), it.value());
|
||||
@@ -92,7 +103,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
|
||||
|
||||
QVector<Node*> pasted_nodes;
|
||||
XMLNodeData xml_node_data;
|
||||
QMap<quintptr, QMap<quintptr, QPointF> > pasted_contexts;
|
||||
QMap<quintptr, QMap<quintptr, Node::Position> > pasted_contexts;
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("olive")) {
|
||||
@@ -131,7 +142,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("context")) {
|
||||
// Get context ptr
|
||||
QMap<quintptr, QPointF> map;
|
||||
QMap<quintptr, Node::Position> map;
|
||||
quintptr context_ptr = 0;
|
||||
XMLAttributeLoop((&reader), attr) {
|
||||
if (attr.name() == QStringLiteral("ptr")) {
|
||||
@@ -144,7 +155,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("node")) {
|
||||
quintptr node_ptr;
|
||||
QPointF node_pos;
|
||||
Node::Position node_pos;
|
||||
|
||||
if (Project::LoadPosition(&reader, &node_ptr, &node_pos)) {
|
||||
map.insert(node_ptr, node_pos);
|
||||
@@ -201,31 +212,34 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
|
||||
|
||||
// Add all nodes to graph
|
||||
foreach (Node* n, pasted_nodes) {
|
||||
command->add_child(new NodeAddCommand(graph, n));
|
||||
if (command) {
|
||||
command->add_child(new NodeAddCommand(graph, n));
|
||||
} else {
|
||||
n->setParent(graph);
|
||||
}
|
||||
}
|
||||
|
||||
// Make connections
|
||||
if (!xml_node_data.desired_connections.isEmpty()) {
|
||||
XMLConnectNodes(xml_node_data, data_version, command);
|
||||
}
|
||||
|
||||
// Link blocks
|
||||
XMLLinkBlocks(xml_node_data);
|
||||
|
||||
// Process contexts
|
||||
for (auto it=pasted_contexts.cbegin(); it!=pasted_contexts.cend(); it++) {
|
||||
Node *context = xml_node_data.node_ptrs.value(it.key());
|
||||
if (context) {
|
||||
const QMap<quintptr, QPointF> &map = it.value();
|
||||
auto map = it.value();
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
Node *subnode = xml_node_data.node_ptrs.value(jt.key());
|
||||
if (subnode) {
|
||||
command->add_child(new NodeSetPositionCommand(subnode, context, jt.value(), false));
|
||||
if (command) {
|
||||
command->add_child(new NodeSetPositionCommand(subnode, context, jt.value()));
|
||||
} else {
|
||||
context->SetNodePositionInContext(subnode, jt.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make connections
|
||||
xml_node_data.PostConnect(data_version, command);
|
||||
|
||||
return pasted_nodes;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
NodeCopyPasteService() = default;
|
||||
|
||||
protected:
|
||||
void CopyNodesToClipboard(const QVector<Node *> &nodes, void* userdata = nullptr);
|
||||
void CopyNodesToClipboard(QVector<Node *> nodes, void* userdata = nullptr);
|
||||
|
||||
QVector<Node*> PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand *command, void* userdata = nullptr);
|
||||
|
||||
|
||||
@@ -41,9 +41,10 @@ const QString Track::kMutedInput = QStringLiteral("muted_in");
|
||||
Track::Track() :
|
||||
track_type_(Track::kNone),
|
||||
index_(-1),
|
||||
locked_(false)
|
||||
locked_(false),
|
||||
sequence_(nullptr)
|
||||
{
|
||||
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable));
|
||||
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable | kInputFlagHidden));
|
||||
|
||||
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
|
||||
// on our end
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class Sequence;
|
||||
|
||||
/**
|
||||
* @brief A time traversal Node for sorting through one channel/track of Blocks
|
||||
*/
|
||||
@@ -168,17 +170,48 @@ public:
|
||||
|
||||
QString ToString() const
|
||||
{
|
||||
QString type_string;
|
||||
|
||||
if (type_ == Track::kVideo) {
|
||||
type_string = QStringLiteral("v");
|
||||
} else if (type_ == Track::kAudio) {
|
||||
type_string = QStringLiteral("a");
|
||||
} else {
|
||||
QString type_string = TypeToString(type_);
|
||||
if (type_string.isEmpty()) {
|
||||
return QString();
|
||||
} else {
|
||||
return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
|
||||
}
|
||||
}
|
||||
|
||||
/// For IDs that shouldn't change between localizations
|
||||
static QString TypeToString(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case kVideo:
|
||||
return QStringLiteral("v");
|
||||
case kAudio:
|
||||
return QStringLiteral("a");
|
||||
case kSubtitle:
|
||||
return QStringLiteral("s");
|
||||
case kCount:
|
||||
case kNone:
|
||||
break;
|
||||
}
|
||||
|
||||
return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
|
||||
return QString();
|
||||
}
|
||||
|
||||
/// For human-facing strings
|
||||
static QString TypeToTranslatedString(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case kVideo:
|
||||
return tr("V");
|
||||
case kAudio:
|
||||
return tr("A");
|
||||
case kSubtitle:
|
||||
return tr("S");
|
||||
case kCount:
|
||||
case kNone:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
static Type TypeFromString(const QString& s)
|
||||
@@ -359,9 +392,18 @@ public:
|
||||
|
||||
bool IsLocked() const;
|
||||
|
||||
|
||||
int GetArrayIndexFromBlock(Block* block) const;
|
||||
|
||||
Sequence *sequence() const
|
||||
{
|
||||
return sequence_;
|
||||
}
|
||||
|
||||
void set_sequence(Sequence *sequence)
|
||||
{
|
||||
sequence_ = sequence;
|
||||
}
|
||||
|
||||
static const double kTrackHeightDefault;
|
||||
static const double kTrackHeightMinimum;
|
||||
static const double kTrackHeightInterval;
|
||||
@@ -443,6 +485,8 @@ private:
|
||||
|
||||
bool locked_;
|
||||
|
||||
Sequence *sequence_;
|
||||
|
||||
private slots:
|
||||
void BlockLengthChanged();
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ void TrackList::TrackConnected(Node *node, int element)
|
||||
connect(track, &Track::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
|
||||
|
||||
track->set_type(type_);
|
||||
track->set_sequence(parent());
|
||||
|
||||
emit TrackListChanged();
|
||||
|
||||
@@ -121,6 +122,7 @@ void TrackList::TrackDisconnected(Node *node, int element)
|
||||
|
||||
track->SetIndex(-1);
|
||||
track->set_type(Track::kNone);
|
||||
track->set_sequence(nullptr);
|
||||
|
||||
disconnect(track, &Track::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "node/traverser.h"
|
||||
#include "widget/videoparamedit/videoparamedit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -34,8 +33,6 @@ const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in");
|
||||
const QString ViewerOutput::kVideoAutoCacheInput = QStringLiteral("video_autocache_in");
|
||||
const QString ViewerOutput::kAudioAutoCacheInput = QStringLiteral("audio_autocache_in");
|
||||
|
||||
const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight | VideoParamEdit::kInterlacing | VideoParamEdit::kFrameRate | VideoParamEdit::kPixelAspect;
|
||||
|
||||
#define super Node
|
||||
|
||||
ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_streams) :
|
||||
@@ -47,10 +44,9 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream
|
||||
video_cache_enabled_(true),
|
||||
audio_cache_enabled_(true)
|
||||
{
|
||||
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
|
||||
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(kVideoParamEditMask));
|
||||
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
|
||||
|
||||
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
|
||||
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
|
||||
|
||||
if (create_buffer_inputs) {
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
@@ -70,6 +66,8 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream
|
||||
AddStream(Track::kAudio, QVariant());
|
||||
set_default_parameters();
|
||||
}
|
||||
|
||||
SetFlags(kDontShowInParamView);
|
||||
}
|
||||
|
||||
Node *ViewerOutput::copy() const
|
||||
|
||||
@@ -200,8 +200,6 @@ public:
|
||||
static const QString kVideoAutoCacheInput;
|
||||
static const QString kAudioAutoCacheInput;
|
||||
|
||||
static const uint64_t kVideoParamEditMask;
|
||||
|
||||
signals:
|
||||
void FrameRateChanged(const rational&);
|
||||
|
||||
@@ -254,8 +252,6 @@ private:
|
||||
|
||||
AudioPlaybackCache audio_playback_cache_;
|
||||
|
||||
int operation_stack_;
|
||||
|
||||
VideoParams cached_video_params_;
|
||||
|
||||
AudioParams cached_audio_params_;
|
||||
|
||||
@@ -33,6 +33,15 @@ QString NodeInput::name() const
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeInput::IsHidden() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
return node_->IsInputHidden(input_);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeInput::IsConnected() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
@@ -60,6 +69,15 @@ bool NodeInput::IsArray() const
|
||||
}
|
||||
}
|
||||
|
||||
InputFlags NodeInput::GetFlags() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
return node_->GetInputFlags(input_);
|
||||
} else {
|
||||
return InputFlags(kInputFlagNormal);
|
||||
}
|
||||
}
|
||||
|
||||
Node *NodeInput::GetConnectedOutput() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
@@ -78,6 +96,15 @@ NodeValue::Type NodeInput::GetDataType() const
|
||||
}
|
||||
}
|
||||
|
||||
QVariant NodeInput::GetDefaultValue() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
return node_->GetDefaultValue(input_);
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList NodeInput::GetComboBoxStrings() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
@@ -123,6 +150,15 @@ QVariant NodeInput::GetSplitDefaultValueForTrack(int track) const
|
||||
}
|
||||
}
|
||||
|
||||
int NodeInput::GetArraySize() const
|
||||
{
|
||||
if (IsValid() && element_ == -1) {
|
||||
return node_->InputArraySize(input_);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint qHash(const NodeInput &i)
|
||||
{
|
||||
return qHash(i.node()) ^ qHash(i.input()) ^ qHash(i.element());
|
||||
|
||||
+50
-1
@@ -31,6 +31,37 @@ namespace olive {
|
||||
class Node;
|
||||
class NodeKeyframe;
|
||||
|
||||
enum InputFlag {
|
||||
/// By default, inputs are keyframable, connectable, and NOT arrays
|
||||
kInputFlagNormal = 0x0,
|
||||
kInputFlagArray = 0x1,
|
||||
kInputFlagNotKeyframable = 0x2,
|
||||
kInputFlagNotConnectable = 0x4,
|
||||
kInputFlagHidden = 0x8
|
||||
};
|
||||
|
||||
class InputFlags {
|
||||
public:
|
||||
explicit InputFlags()
|
||||
{
|
||||
f_ = kInputFlagNormal;
|
||||
}
|
||||
|
||||
explicit InputFlags(uint64_t flags)
|
||||
{
|
||||
f_ = flags;
|
||||
}
|
||||
|
||||
bool operator&(const InputFlag& f) const
|
||||
{
|
||||
return f_ & f;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t f_;
|
||||
|
||||
};
|
||||
|
||||
struct NodeInputPair {
|
||||
bool operator==(const NodeInputPair& rhs) const
|
||||
{
|
||||
@@ -98,11 +129,21 @@ public:
|
||||
return input_;
|
||||
}
|
||||
|
||||
int element() const
|
||||
const int &element() const
|
||||
{
|
||||
return element_;
|
||||
}
|
||||
|
||||
void set_node(Node *node)
|
||||
{
|
||||
node_ = node;
|
||||
}
|
||||
|
||||
void set_input(const QString &input)
|
||||
{
|
||||
input_ = input;
|
||||
}
|
||||
|
||||
void set_element(int e)
|
||||
{
|
||||
element_ = e;
|
||||
@@ -115,16 +156,22 @@ public:
|
||||
return node_ && !input_.isEmpty() && element_ >= -1;
|
||||
}
|
||||
|
||||
bool IsHidden() const;
|
||||
|
||||
bool IsConnected() const;
|
||||
|
||||
bool IsKeyframing() const;
|
||||
|
||||
bool IsArray() const;
|
||||
|
||||
InputFlags GetFlags() const;
|
||||
|
||||
Node *GetConnectedOutput() const;
|
||||
|
||||
NodeValue::Type GetDataType() const;
|
||||
|
||||
QVariant GetDefaultValue() const;
|
||||
|
||||
QStringList GetComboBoxStrings() const;
|
||||
|
||||
QVariant GetProperty(const QString& key) const;
|
||||
@@ -135,6 +182,8 @@ public:
|
||||
|
||||
QVariant GetSplitDefaultValueForTrack(int track) const;
|
||||
|
||||
int GetArraySize() const;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
*this = NodeInput();
|
||||
|
||||
@@ -113,19 +113,12 @@ void Folder::InputDisconnectedEvent(const QString &input, int element, Node *out
|
||||
}
|
||||
}
|
||||
|
||||
FolderAddChild::FolderAddChild(Folder *folder, Node *child, bool autoposition) :
|
||||
FolderAddChild::FolderAddChild(Folder *folder, Node *child) :
|
||||
folder_(folder),
|
||||
child_(child),
|
||||
autoposition_(autoposition),
|
||||
position_command_(nullptr)
|
||||
child_(child)
|
||||
{
|
||||
}
|
||||
|
||||
FolderAddChild::~FolderAddChild()
|
||||
{
|
||||
delete position_command_;
|
||||
}
|
||||
|
||||
Project *FolderAddChild::GetRelevantProject() const
|
||||
{
|
||||
return folder_->project();
|
||||
@@ -136,21 +129,10 @@ void FolderAddChild::redo()
|
||||
int array_index = folder_->InputArraySize(Folder::kChildInput);
|
||||
folder_->InputArrayAppend(Folder::kChildInput, false);
|
||||
Node::ConnectEdge(child_, NodeInput(folder_, Folder::kChildInput, array_index));
|
||||
|
||||
if (autoposition_) {
|
||||
if (!position_command_) {
|
||||
position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, folder_->project()->root(), array_index, array_index+1, true);
|
||||
}
|
||||
position_command_->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderAddChild::undo()
|
||||
{
|
||||
if (position_command_) {
|
||||
position_command_->undo_now();
|
||||
}
|
||||
|
||||
Node::DisconnectEdge(child_, NodeInput(folder_, Folder::kChildInput, folder_->InputArraySize(Folder::kChildInput)-1));
|
||||
folder_->InputArrayRemoveLast(Folder::kChildInput);
|
||||
}
|
||||
|
||||
@@ -208,9 +208,7 @@ private:
|
||||
class FolderAddChild : public UndoCommand
|
||||
{
|
||||
public:
|
||||
FolderAddChild(Folder* folder, Node* child, bool autoposition = true);
|
||||
|
||||
virtual ~FolderAddChild() override;
|
||||
FolderAddChild(Folder* folder, Node* child);
|
||||
|
||||
virtual Project * GetRelevantProject() const override;
|
||||
|
||||
@@ -224,10 +222,6 @@ private:
|
||||
|
||||
Node* child_;
|
||||
|
||||
bool autoposition_;
|
||||
|
||||
NodeSetPositionAsChildCommand* position_command_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "core.h"
|
||||
#include "render/job/footagejob.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/videoparamedit/videoparamedit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -144,39 +143,6 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
|
||||
AddStream(Track::kVideo, QVariant::fromValue(vp));
|
||||
}
|
||||
|
||||
if (!footage_info.GetVideoStreams().isEmpty()) {
|
||||
// FIXME: This will break on multiple video streams. Currently we don't have
|
||||
// infrastructure for different properties per element. We'll see if this becomes
|
||||
// a problem.
|
||||
VideoParams vp = footage_info.GetVideoStreams().first();
|
||||
|
||||
uint64_t video_param_mask = 0;
|
||||
|
||||
video_param_mask |= VideoParamEdit::kEnabled;
|
||||
video_param_mask |= VideoParamEdit::kColorspace;
|
||||
video_param_mask |= VideoParamEdit::kPixelAspect;
|
||||
video_param_mask |= VideoParamEdit::kInterlacing;
|
||||
video_param_mask |= VideoParamEdit::kFrameRateIsArbitrary;
|
||||
|
||||
if (vp.channel_count() == VideoParams::kRGBAChannelCount) {
|
||||
// Add premultiplied setting if this footage has an alpha channel
|
||||
video_param_mask |= VideoParamEdit::kPremultipliedAlpha;
|
||||
}
|
||||
|
||||
if (vp.video_type() == VideoParams::kVideoTypeVideo) {
|
||||
// This is video, ensure that the frame rate does not overwrite the timebase
|
||||
video_param_mask |= VideoParamEdit::kFrameRateIsNotTimebase;
|
||||
} else {
|
||||
// This is not a video, so it's either a still image or an image sequence
|
||||
video_param_mask |= VideoParamEdit::kIsImageSequence;
|
||||
video_param_mask |= VideoParamEdit::kStartTime;
|
||||
video_param_mask |= VideoParamEdit::kEndTime;
|
||||
video_param_mask |= VideoParamEdit::kFrameRate;
|
||||
}
|
||||
|
||||
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(video_param_mask));
|
||||
}
|
||||
|
||||
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
|
||||
AddStream(Track::kAudio, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
|
||||
}
|
||||
|
||||
@@ -44,19 +44,16 @@ Project::Project() :
|
||||
root_->setParent(this);
|
||||
root_->SetLabel(tr("Root"));
|
||||
root_->SetCanBeDeleted(false);
|
||||
SetNodePosition(root_, root_, QPointF(0, 0));
|
||||
|
||||
// Adds a color manager "node" to this project so that it synchronizes
|
||||
color_manager_ = new ColorManager();
|
||||
color_manager_->setParent(this);
|
||||
SetNodePosition(color_manager_, root_, QPointF(1, 0));
|
||||
color_manager_->SetCanBeDeleted(false);
|
||||
AddDefaultNode(color_manager_);
|
||||
|
||||
// Same with project settings
|
||||
settings_ = new ProjectSettingsNode();
|
||||
settings_->setParent(this);
|
||||
SetNodePosition(settings_, root_, QPointF(2, 0));
|
||||
settings_->SetCanBeDeleted(false);
|
||||
AddDefaultNode(settings_);
|
||||
|
||||
@@ -161,13 +158,13 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
quintptr node_ptr;
|
||||
QPointF node_pos;
|
||||
Node::Position node_pos;
|
||||
|
||||
if (LoadPosition(reader, &node_ptr, &node_pos)) {
|
||||
Node *node = xml_node_data.node_ptrs.value(node_ptr);
|
||||
|
||||
if (node) {
|
||||
SetNodePosition(node, context, node_pos);
|
||||
context->SetNodePositionInContext(node, node_pos);
|
||||
} else {
|
||||
qWarning() << "Failed to find pointer for node position";
|
||||
reader->skipCurrentElement();
|
||||
@@ -196,10 +193,7 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
|
||||
}
|
||||
|
||||
// Make connections
|
||||
XMLConnectNodes(xml_node_data, version);
|
||||
|
||||
// Link blocks
|
||||
XMLLinkBlocks(xml_node_data);
|
||||
xml_node_data.PostConnect(version);
|
||||
}
|
||||
|
||||
void Project::Save(QXmlStreamWriter *writer) const
|
||||
@@ -230,20 +224,22 @@ void Project::Save(QXmlStreamWriter *writer) const
|
||||
|
||||
writer->writeStartElement(QStringLiteral("positions"));
|
||||
|
||||
for (auto it=GetPositionMap().cbegin(); it!=GetPositionMap().cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("context"));
|
||||
foreach (Node* context, nodes()) {
|
||||
const Node::PositionMap &map = context->GetContextPositions();
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
|
||||
if (!map.isEmpty()) {
|
||||
writer->writeStartElement(QStringLiteral("context"));
|
||||
|
||||
const PositionMap &map = it.value();
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(context)));
|
||||
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
SavePosition(writer, jt.key(), jt.value());
|
||||
writer->writeEndElement(); // node
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
SavePosition(writer, jt.key(), jt.value());
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // context
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // context
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // positions
|
||||
@@ -351,7 +347,7 @@ void Project::RegenerateUuid()
|
||||
uuid_ = QUuid::createUuid();
|
||||
}
|
||||
|
||||
bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF *pos)
|
||||
bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos)
|
||||
{
|
||||
bool got_node_ptr = false;
|
||||
bool got_pos_x = false;
|
||||
@@ -367,11 +363,13 @@ bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("x")) {
|
||||
pos->setX(reader->readElementText().toDouble());
|
||||
pos->position.setX(reader->readElementText().toDouble());
|
||||
got_pos_x = true;
|
||||
} else if (reader->name() == QStringLiteral("y")) {
|
||||
pos->setY(reader->readElementText().toDouble());
|
||||
pos->position.setY(reader->readElementText().toDouble());
|
||||
got_pos_y = true;
|
||||
} else if (reader->name() == QStringLiteral("expanded")) {
|
||||
pos->expanded = reader->readElementText().toInt();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
@@ -380,12 +378,13 @@ bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF
|
||||
return got_node_ptr && got_pos_x && got_pos_y;
|
||||
}
|
||||
|
||||
void Project::SavePosition(QXmlStreamWriter *writer, Node *node, const QPointF &pos)
|
||||
void Project::SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos)
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(node)));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.x()));
|
||||
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.y()));
|
||||
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x()));
|
||||
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y()));
|
||||
writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded));
|
||||
}
|
||||
|
||||
void Project::ColorManagerValueChanged(const NodeInput &input, const TimeRange &range)
|
||||
|
||||
@@ -82,8 +82,8 @@ public:
|
||||
|
||||
void RegenerateUuid();
|
||||
|
||||
static bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF *pos);
|
||||
static void SavePosition(QXmlStreamWriter *writer, Node *node, const QPointF &pos);
|
||||
static bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos);
|
||||
static void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos);
|
||||
|
||||
signals:
|
||||
void NameChanged();
|
||||
|
||||
@@ -41,7 +41,7 @@ Sequence::Sequence()
|
||||
// Create track input
|
||||
QString track_input_id = kTrackInputFormat.arg(i);
|
||||
|
||||
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray));
|
||||
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
|
||||
|
||||
IgnoreInvalidationsFrom(track_input_id);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user