Merge branch 'nodeview-redux'
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "core.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "transition/transition.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
@@ -29,6 +30,8 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super Node
|
||||
|
||||
const QString Block::kLengthInput = QStringLiteral("length_in");
|
||||
const QString Block::kMediaInInput = QStringLiteral("media_in_in");
|
||||
const QString Block::kEnabledInput = QStringLiteral("enabled_in");
|
||||
@@ -47,7 +50,6 @@ Block::Block() :
|
||||
SetInputProperty(kLengthInput, QStringLiteral("min"), QVariant::fromValue(rational(0, 1)));
|
||||
SetInputProperty(kLengthInput, QStringLiteral("view"), RationalSlider::kTime);
|
||||
SetInputProperty(kLengthInput, QStringLiteral("viewlock"), true);
|
||||
IgnoreInvalidationsFrom(kLengthInput);
|
||||
IgnoreHashingFrom(kLengthInput);
|
||||
|
||||
AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
@@ -221,7 +223,7 @@ void Block::set_length_internal(const rational &length)
|
||||
|
||||
void Block::Retranslate()
|
||||
{
|
||||
Node::Retranslate();
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kLengthInput, tr("Length"));
|
||||
SetInputName(kMediaInInput, tr("Media In"));
|
||||
@@ -235,4 +237,24 @@ void Block::Hash(const QString &, QCryptographicHash &, const rational &, const
|
||||
// A block does nothing by default, so we hash nothing
|
||||
}
|
||||
|
||||
void Block::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
TimeRange r;
|
||||
|
||||
if (from == kLengthInput) {
|
||||
// We must intercept the signal here
|
||||
r = TimeRange(qMin(length(), last_length_), RATIONAL_MAX);
|
||||
|
||||
if (!Core::instance()->EffectsSliderIsBeingDragged()) {
|
||||
last_length_ = length();
|
||||
}
|
||||
|
||||
options.insert(QStringLiteral("lengthevent"), true);
|
||||
} else {
|
||||
r = range;
|
||||
}
|
||||
|
||||
super::InvalidateCache(r, from, element, options);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -155,6 +155,8 @@ public:
|
||||
|
||||
virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override;
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override;
|
||||
|
||||
static const QString kLengthInput;
|
||||
static const QString kMediaInInput;
|
||||
static const QString kEnabledInput;
|
||||
@@ -193,6 +195,8 @@ private:
|
||||
|
||||
QVector<Block*> block_links_;
|
||||
|
||||
rational last_length_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ QString ClipBlock::Description() const
|
||||
return tr("A time-based node that represents a media source.");
|
||||
}
|
||||
|
||||
void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time)
|
||||
void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
@@ -63,10 +63,10 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int
|
||||
rational start = MediaToSequenceTime(range.in());
|
||||
rational end = MediaToSequenceTime(range.out());
|
||||
|
||||
super::InvalidateCache(TimeRange(start, end), from, element, job_time);
|
||||
super::InvalidateCache(TimeRange(start, end), from, element, options);
|
||||
} else {
|
||||
// Otherwise, pass signal along normally
|
||||
super::InvalidateCache(range, from, element, job_time);
|
||||
super::InvalidateCache(range, from, element, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
virtual QString id() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override;
|
||||
|
||||
virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||
|
||||
|
||||
@@ -44,6 +44,45 @@ 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 count = 0;
|
||||
|
||||
for (auto it=position_map_.cbegin(); it!=position_map_.cend(); it++) {
|
||||
if (it.value().contains(node)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -75,6 +114,18 @@ void NodeGraph::childEvent(QChildEvent *event)
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,55 @@ 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 = QMap<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;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when a Node is added to the graph
|
||||
@@ -80,6 +129,10 @@ signals:
|
||||
|
||||
void ValueChanged(const NodeInput& input);
|
||||
|
||||
void NodePositionAdded(Node *node, Node *relative, const QPointF &position);
|
||||
|
||||
void NodePositionRemoved(Node *node, Node *relative);
|
||||
|
||||
protected:
|
||||
void AddDefaultNode(Node* n)
|
||||
{
|
||||
@@ -93,6 +146,10 @@ private:
|
||||
|
||||
QVector<Node*> default_nodes_;
|
||||
|
||||
QMap<Node *, PositionMap> position_map_;
|
||||
|
||||
PositionMap root_position_map_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+167
-85
@@ -46,7 +46,6 @@ const QString Node::kDefaultOutput = QStringLiteral("output");
|
||||
Node::Node(bool create_default_output) :
|
||||
can_be_deleted_(true),
|
||||
override_color_(-1),
|
||||
last_change_time_(0),
|
||||
folder_(nullptr),
|
||||
operation_stack_(0),
|
||||
cache_result_(false)
|
||||
@@ -91,20 +90,6 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi
|
||||
LoadInput(reader, xml_node_data, cancelled);
|
||||
} else if (reader->name() == QStringLiteral("ptr")) {
|
||||
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this);
|
||||
} else if (reader->name() == QStringLiteral("pos")) {
|
||||
QPointF p;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("x")) {
|
||||
p.setX(reader->readElementText().toDouble());
|
||||
} else if (reader->name() == QStringLiteral("y")) {
|
||||
p.setY(reader->readElementText().toDouble());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
SetPosition(p);
|
||||
} else if (reader->name() == QStringLiteral("label")) {
|
||||
SetLabel(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("color")) {
|
||||
@@ -166,11 +151,6 @@ void Node::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
|
||||
|
||||
writer->writeStartElement(QStringLiteral("pos"));
|
||||
writer->writeTextElement(QStringLiteral("x"), QString::number(GetPosition().x()));
|
||||
writer->writeTextElement(QStringLiteral("y"), QString::number(GetPosition().y()));
|
||||
writer->writeEndElement(); // pos
|
||||
|
||||
writer->writeTextElement(QStringLiteral("label"), GetLabel());
|
||||
writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_));
|
||||
|
||||
@@ -282,9 +262,6 @@ void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input)
|
||||
input.node()->input_connections_[input] = output;
|
||||
output.node()->output_connections_.push_back(std::pair<NodeOutput, NodeInput>({output, input}));
|
||||
|
||||
// Update change times
|
||||
input.node()->UpdateLastChangedTime();
|
||||
|
||||
// Call internal events
|
||||
input.node()->InputConnectedEvent(input.input(), input.element(), output);
|
||||
output.node()->OutputConnectedEvent(output.output(), input);
|
||||
@@ -314,9 +291,6 @@ void Node::DisconnectEdge(const NodeOutput &output, const NodeInput &input)
|
||||
OutputConnections& outputs = output.node()->output_connections_;
|
||||
outputs.erase(std::find(outputs.begin(), outputs.end(), std::pair<NodeOutput, NodeInput>({output, input})));
|
||||
|
||||
// Update change times
|
||||
input.node()->UpdateLastChangedTime();
|
||||
|
||||
// Call internal events
|
||||
input.node()->InputDisconnectedEvent(input.input(), input.element(), output);
|
||||
output.node()->OutputDisconnectedEvent(output.output(), input);
|
||||
@@ -953,7 +927,7 @@ void Node::InputArrayResize(const QString &id, int size, bool undoable)
|
||||
if (undoable) {
|
||||
Core::instance()->undo_stack()->push(c);
|
||||
} else {
|
||||
c->redo();
|
||||
c->redo_now();
|
||||
delete c;
|
||||
}
|
||||
}
|
||||
@@ -1045,12 +1019,12 @@ NodeValueTable Node::Value(const QString& output, NodeValueDatabase &value) cons
|
||||
return value.Merge();
|
||||
}
|
||||
|
||||
void Node::InvalidateCache(const TimeRange &range, const QString &from, int element, qint64 job_time)
|
||||
void Node::InvalidateCache(const TimeRange &range, const QString &from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
Q_UNUSED(from)
|
||||
Q_UNUSED(element)
|
||||
|
||||
SendInvalidateCache(range, job_time);
|
||||
SendInvalidateCache(range, options);
|
||||
}
|
||||
|
||||
void Node::BeginOperation()
|
||||
@@ -1134,7 +1108,7 @@ void Node::CopyDependencyGraph(const QVector<Node *> &src, const QVector<Node *>
|
||||
}
|
||||
}
|
||||
|
||||
Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<const Node*, Node*>& created, const Node *node, MultiUndoCommand *command)
|
||||
Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& created, Node *node, MultiUndoCommand *command)
|
||||
{
|
||||
// Make a new node of the same type
|
||||
Node* copy = node->copy();
|
||||
@@ -1171,17 +1145,26 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<const Node*, Node*
|
||||
NodeInput(copy, 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));
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
Node *Node::CopyNodeAndDependencyGraphMinusItems(const Node *node, MultiUndoCommand *command)
|
||||
Node *Node::CopyNodeAndDependencyGraphMinusItems(Node *node, MultiUndoCommand *command)
|
||||
{
|
||||
QMap<const Node*, Node*> created;
|
||||
QMap<Node*, Node*> created;
|
||||
|
||||
return CopyNodeAndDependencyGraphMinusItemsInternal(created, node, command);
|
||||
}
|
||||
|
||||
Node *Node::CopyNodeInGraph(const Node *node, MultiUndoCommand *command)
|
||||
Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command)
|
||||
{
|
||||
Node* copy;
|
||||
|
||||
@@ -1194,19 +1177,28 @@ Node *Node::CopyNodeInGraph(const Node *node, MultiUndoCommand *command)
|
||||
copy));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void Node::SendInvalidateCache(const TimeRange &range, qint64 job_time)
|
||||
void Node::SendInvalidateCache(const TimeRange &range, const InvalidateCacheOptions &options)
|
||||
{
|
||||
if (GetOperationStack() == 0) {
|
||||
for (const OutputConnection& conn : output_connections_) {
|
||||
// Send clear cache signal to the Node
|
||||
const NodeInput& in = conn.second;
|
||||
|
||||
in.node()->InvalidateCache(range, in.input(), in.element(), job_time);
|
||||
in.node()->InvalidateCache(range, in.input(), in.element(), options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1482,7 +1474,6 @@ void Node::CopyInputs(const Node *source, Node *destination, bool include_connec
|
||||
CopyInput(source, destination, input, include_connections, true);
|
||||
}
|
||||
|
||||
destination->SetPosition(source->GetPosition());
|
||||
destination->SetLabel(source->GetLabel());
|
||||
destination->SetOverrideColor(source->GetOverrideColor());
|
||||
}
|
||||
@@ -1642,15 +1633,28 @@ void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const
|
||||
Q_UNUSED(job)
|
||||
}
|
||||
|
||||
bool Node::OutputsTo(Node *n, bool recursively) const
|
||||
bool Node::OutputsTo(Node *n, bool recursively, const OutputConnections &ignore_edges, const OutputConnection &added_edge) const
|
||||
{
|
||||
for (const OutputConnection& conn : output_connections_) {
|
||||
if (std::find(ignore_edges.cbegin(), ignore_edges.cend(), conn) != ignore_edges.cend()) {
|
||||
// If this edge is in the "ignore edges" list, skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
Node* connected = conn.second.node();
|
||||
|
||||
if (connected == n) {
|
||||
return true;
|
||||
} else if (recursively && connected->OutputsTo(n, recursively)) {
|
||||
} else if (recursively && connected->OutputsTo(n, recursively, ignore_edges, added_edge)) {
|
||||
return true;
|
||||
} else if (added_edge.first.node() == this) {
|
||||
Node *proposed_connected = added_edge.second.node();
|
||||
|
||||
if (proposed_connected == n) {
|
||||
return true;
|
||||
} else if (recursively && proposed_connected->OutputsTo(n, recursively, ignore_edges, added_edge)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1717,7 +1721,7 @@ bool Node::InputsFrom(const QString &id, bool recursively) const
|
||||
return false;
|
||||
}
|
||||
|
||||
int Node::GetRoutesTo(Node *n) const
|
||||
int Node::GetNumberOfRoutesTo(Node *n) const
|
||||
{
|
||||
bool outputs_directly = false;
|
||||
int routes = 0;
|
||||
@@ -1728,7 +1732,7 @@ int Node::GetRoutesTo(Node *n) const
|
||||
if (connected_node == n) {
|
||||
outputs_directly = true;
|
||||
} else {
|
||||
routes += connected_node->GetRoutesTo(n);
|
||||
routes += connected_node->GetNumberOfRoutesTo(n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1831,33 +1835,8 @@ QVariant Node::PtrToValue(void *ptr)
|
||||
return reinterpret_cast<quintptr>(ptr);
|
||||
}
|
||||
|
||||
const QPointF &Node::GetPosition() const
|
||||
{
|
||||
return position_;
|
||||
}
|
||||
|
||||
void Node::SetPosition(const QPointF &pos, bool move_dependencies_relatively_too)
|
||||
{
|
||||
QPointF old_pos = position_;
|
||||
|
||||
position_ = pos;
|
||||
|
||||
emit PositionChanged(position_);
|
||||
|
||||
if (move_dependencies_relatively_too) {
|
||||
QPointF difference = pos - old_pos;
|
||||
|
||||
for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) {
|
||||
Node* c = it->second.node();
|
||||
c->SetPosition(c->GetPosition() + difference, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ParameterValueChanged(const QString& input, int element, const TimeRange& range)
|
||||
{
|
||||
UpdateLastChangedTime();
|
||||
|
||||
InputValueChangedEvent(input, element);
|
||||
|
||||
emit ValueChanged(NodeInput(this, input, element), range);
|
||||
@@ -2049,11 +2028,6 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString& input, int ele
|
||||
}
|
||||
}
|
||||
|
||||
void Node::UpdateLastChangedTime()
|
||||
{
|
||||
last_change_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
TimeRange Node::GetRangeAffectedByKeyframe(NodeKeyframe *key) const
|
||||
{
|
||||
const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key);
|
||||
@@ -2302,36 +2276,144 @@ void NodeSetPositionAndShiftSurroundingsCommand::redo()
|
||||
{
|
||||
if (commands_.isEmpty()) {
|
||||
// Move first node
|
||||
NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, position_, move_dependencies_);
|
||||
set_pos_command->redo();
|
||||
NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, relative_, position_, move_dependencies_);
|
||||
set_pos_command->redo_now();
|
||||
commands_.append(set_pos_command);
|
||||
|
||||
// Get bounding rect
|
||||
QRectF bounding_rect(position_.x() - 0.5, position_.y() - 0.5, 1, 1);
|
||||
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 (bounding_rect.contains(surrounding->GetPosition()) && surrounding != node_) {
|
||||
QPointF new_pos = surrounding->GetPosition();
|
||||
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;
|
||||
qreal move_rate = 0.50;
|
||||
|
||||
if (surrounding->GetPosition().y() < position_.y()) {
|
||||
move_rate = -move_rate;
|
||||
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);
|
||||
}
|
||||
|
||||
new_pos.setY(new_pos.y() + move_rate);
|
||||
|
||||
auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, new_pos, true);
|
||||
sur_command->redo();
|
||||
commands_.append(sur_command);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<commands_.size(); i++) {
|
||||
commands_.at(i)->redo();
|
||||
commands_.at(i)->redo_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeSetPositionCommand::redo()
|
||||
{
|
||||
graph_ = node_->parent();
|
||||
if (!(added_ = !graph_->NodeMapContainsNode(node_, relevant_))) {
|
||||
old_pos_ = graph_->GetNodePosition(node_, relevant_);
|
||||
}
|
||||
graph_->SetNodePosition(node_, relevant_, pos_);
|
||||
}
|
||||
|
||||
void NodeSetPositionCommand::undo()
|
||||
{
|
||||
if (added_) {
|
||||
graph_->RemoveNodePosition(node_, relevant_);
|
||||
} else {
|
||||
graph_->SetNodePosition(node_, relevant_, 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();
|
||||
}
|
||||
|
||||
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_);
|
||||
|
||||
if (contained_) {
|
||||
old_pos_ = graph->GetNodePosition(node_, context_);
|
||||
graph->RemoveNodePosition(node_, context_);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRemovePositionFromContextCommand::undo()
|
||||
{
|
||||
if (contained_) {
|
||||
NodeGraph *graph = node_->parent();
|
||||
graph->SetNodePosition(node_, context_, old_pos_);
|
||||
}
|
||||
}
|
||||
|
||||
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_)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+125
-81
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <QCryptographicHash>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QPainter>
|
||||
#include <QPointF>
|
||||
@@ -557,7 +558,8 @@ public:
|
||||
* Whether to keep traversing down outputs to find this node (TRUE) or stick to immediate outputs
|
||||
* (FALSE).
|
||||
*/
|
||||
bool OutputsTo(Node* n, bool recursively) const;
|
||||
bool OutputsTo(Node* n, bool recursively, const OutputConnections &ignore_edges = OutputConnections(), const OutputConnection &added_edge = OutputConnection()) const;
|
||||
|
||||
/**
|
||||
* @brief Same as OutputsTo(Node*), but for a node ID rather than a specific instance.
|
||||
*/
|
||||
@@ -581,7 +583,7 @@ public:
|
||||
/**
|
||||
* @brief Determines how many paths go from this node out to another node
|
||||
*/
|
||||
int GetRoutesTo(Node* n) const;
|
||||
int GetNumberOfRoutesTo(Node* n) const;
|
||||
|
||||
/**
|
||||
* @brief Severs all input and output connections
|
||||
@@ -621,6 +623,8 @@ public:
|
||||
*/
|
||||
static T* ValueToPtr(const QVariant& ptr);
|
||||
|
||||
using InvalidateCacheOptions = QHash<QString, QVariant>;
|
||||
|
||||
/**
|
||||
* @brief Signal all dependent Nodes that anything cached between start_range and end_range is now invalid and
|
||||
* requires re-rendering
|
||||
@@ -630,16 +634,11 @@ public:
|
||||
* the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can
|
||||
* call this function with transformed time and relay the signal that way.
|
||||
*/
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time);
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions());
|
||||
|
||||
void InvalidateCache(const TimeRange& range, const QString& from, int element = -1)
|
||||
void InvalidateCache(const TimeRange& range, const NodeInput& from, const InvalidateCacheOptions &options = InvalidateCacheOptions())
|
||||
{
|
||||
InvalidateCache(range, from, element, last_change_time_);
|
||||
}
|
||||
|
||||
void InvalidateCache(const TimeRange& range, const NodeInput& from)
|
||||
{
|
||||
InvalidateCache(range, from.input(), from.element());
|
||||
InvalidateCache(range, from.input(), from.element(), options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -689,9 +688,9 @@ public:
|
||||
static QVector<Node*> CopyDependencyGraph(const QVector<Node*>& nodes, MultiUndoCommand *command);
|
||||
static void CopyDependencyGraph(const QVector<Node*>& src, const QVector<Node*>& dst, MultiUndoCommand *command);
|
||||
|
||||
static Node* CopyNodeAndDependencyGraphMinusItems(const Node* node, MultiUndoCommand* command);
|
||||
static Node* CopyNodeAndDependencyGraphMinusItems(Node* node, MultiUndoCommand* command);
|
||||
|
||||
static Node* CopyNodeInGraph(const Node* node, MultiUndoCommand* command);
|
||||
static Node* CopyNodeInGraph(Node *node, MultiUndoCommand* command);
|
||||
|
||||
/**
|
||||
* @brief Return whether this Node can be deleted or not
|
||||
@@ -718,10 +717,6 @@ public:
|
||||
*/
|
||||
virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const;
|
||||
|
||||
const QPointF& GetPosition() const;
|
||||
|
||||
void SetPosition(const QPointF& pos, bool move_dependencies_relatively_too = false);
|
||||
|
||||
virtual bool HasGizmos() const;
|
||||
|
||||
virtual void DrawGizmos(NodeValueDatabase& db, QPainter* p);
|
||||
@@ -889,7 +884,7 @@ protected:
|
||||
SetInputProperty(id, QStringLiteral("combo_str"), strings);
|
||||
}
|
||||
|
||||
void SendInvalidateCache(const TimeRange &range, qint64 job_time);
|
||||
void SendInvalidateCache(const TimeRange &range, const InvalidateCacheOptions &options);
|
||||
|
||||
/**
|
||||
* @brief Don't send cache invalidation signals if `input` is connected or disconnected
|
||||
@@ -1014,6 +1009,7 @@ private:
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
node_->InputArrayInsert(input_, index_, false);
|
||||
@@ -1040,6 +1036,9 @@ private:
|
||||
size_(size)
|
||||
{}
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
old_size_ = node_->InputArraySize(input_);
|
||||
@@ -1072,8 +1071,6 @@ private:
|
||||
node_->ArrayResizeInternal(input_, old_size_);
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
QString input_;
|
||||
@@ -1130,7 +1127,7 @@ private:
|
||||
|
||||
void ArrayResizeInternal(const QString& id, int size);
|
||||
|
||||
static Node *CopyNodeAndDependencyGraphMinusItemsInternal(QMap<const Node*, Node*>& created, const Node *node, MultiUndoCommand *command);
|
||||
static Node *CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node *, Node *> &created, Node *node, MultiUndoCommand *command);
|
||||
|
||||
/**
|
||||
* @brief Immediates aren't deleted, so the actual array size may be larger than ArraySize()
|
||||
@@ -1157,8 +1154,6 @@ private:
|
||||
|
||||
void SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const;
|
||||
|
||||
void UpdateLastChangedTime();
|
||||
|
||||
/**
|
||||
* @brief Intelligently determine how what time range is affected by a keyframe
|
||||
*/
|
||||
@@ -1180,11 +1175,6 @@ private:
|
||||
*/
|
||||
bool can_be_deleted_;
|
||||
|
||||
/**
|
||||
* @brief UI position for NodeViews
|
||||
*/
|
||||
QPointF position_;
|
||||
|
||||
/**
|
||||
* @brief Custom user label for node
|
||||
*/
|
||||
@@ -1213,8 +1203,6 @@ private:
|
||||
|
||||
OutputConnections output_connections_;
|
||||
|
||||
qint64 last_change_time_;
|
||||
|
||||
QString tooltip_;
|
||||
|
||||
Folder* folder_;
|
||||
@@ -1312,44 +1300,41 @@ using NodePtr = std::shared_ptr<Node>;
|
||||
class NodeSetPositionCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeSetPositionCommand(Node* node, const QPointF& position, bool move_dependencies_relatively) :
|
||||
node_(node),
|
||||
new_pos_(position),
|
||||
move_deps_(move_dependencies_relatively)
|
||||
NodeSetPositionCommand(Node* node, Node* relevant, const QPointF& pos, bool move_dependencies_relatively)
|
||||
{
|
||||
node_ = node;
|
||||
relevant_ = relevant;
|
||||
pos_ = pos;
|
||||
move_deps_ = move_dependencies_relatively;
|
||||
}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
old_pos_ = node_->GetPosition();
|
||||
node_->SetPosition(new_pos_, move_deps_);
|
||||
}
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
node_->SetPosition(old_pos_, move_deps_);
|
||||
}
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
|
||||
QPointF new_pos_;
|
||||
Node* relevant_;
|
||||
QPointF pos_;
|
||||
QPointF old_pos_;
|
||||
|
||||
bool added_;
|
||||
bool move_deps_;
|
||||
NodeGraph *graph_;
|
||||
|
||||
};
|
||||
|
||||
class NodeSetPositionAndShiftSurroundingsCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeSetPositionAndShiftSurroundingsCommand(Node* node, const QPointF& pos, bool move_dependencies_relatively) :
|
||||
NodeSetPositionAndShiftSurroundingsCommand(Node* node, Node *relative, const QPointF& pos, bool move_dependencies_relatively) :
|
||||
node_(node),
|
||||
relative_(relative),
|
||||
position_(pos),
|
||||
move_dependencies_(move_dependencies_relatively)
|
||||
{}
|
||||
@@ -1364,18 +1349,21 @@ public:
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
for (int i=commands_.size()-1; i>=0; i--) {
|
||||
commands_.at(i)->undo();
|
||||
commands_.at(i)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
|
||||
Node *relative_;
|
||||
|
||||
QPointF position_;
|
||||
|
||||
bool move_dependencies_;
|
||||
@@ -1387,9 +1375,10 @@ private:
|
||||
class NodeSetPositionAsChildCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeSetPositionAsChildCommand(Node* node, Node* parent, int this_index, int child_count, bool shift_surroundings) :
|
||||
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),
|
||||
@@ -1407,27 +1396,8 @@ public:
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
if (!sub_command_) {
|
||||
// Calculate position of node
|
||||
QPointF pos = parent_->GetPosition();
|
||||
|
||||
// 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);
|
||||
|
||||
if (shift_surroundings_) {
|
||||
sub_command_ = new NodeSetPositionAndShiftSurroundingsCommand(node_, pos, true);
|
||||
} else {
|
||||
sub_command_ = new NodeSetPositionCommand(node_, pos, true);
|
||||
}
|
||||
}
|
||||
|
||||
sub_command_->redo();
|
||||
}
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
@@ -1437,22 +1407,44 @@ public:
|
||||
private:
|
||||
Node* node_;
|
||||
Node* parent_;
|
||||
Node *relative_;
|
||||
|
||||
int this_index_;
|
||||
double this_index_;
|
||||
int child_count_;
|
||||
|
||||
bool shift_surroundings_;
|
||||
|
||||
UndoCommand* sub_command_;
|
||||
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, const QPointF& offset) :
|
||||
NodeSetPositionToOffsetOfAnotherNodeCommand(Node* node, Node* other_node, Node *relative, const QPointF& offset) :
|
||||
node_(node),
|
||||
other_node_(other_node),
|
||||
relative_(relative),
|
||||
offset_(offset)
|
||||
{}
|
||||
|
||||
@@ -1461,20 +1453,72 @@ public:
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
node_->SetPosition(other_node_->GetPosition() + offset_);
|
||||
}
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
node_->SetPosition(other_node_->GetPosition() - offset_);
|
||||
}
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
Node* other_node_;
|
||||
Node *relative_;
|
||||
QPointF offset_;
|
||||
QPointF old_pos_;
|
||||
|
||||
};
|
||||
|
||||
class NodeRemovePositionFromContextCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeRemovePositionFromContextCommand(Node *node, Node *context) :
|
||||
node_(node),
|
||||
context_(context)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node *node_;
|
||||
|
||||
Node *context_;
|
||||
|
||||
QPointF old_pos_;
|
||||
|
||||
bool contained_;
|
||||
|
||||
};
|
||||
|
||||
class NodeRemovePositionFromAllContextsCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeRemovePositionFromAllContextsCommand(Node *node) :
|
||||
node_(node)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project * GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node *node_;
|
||||
|
||||
std::map<Node *, QPointF> points_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -40,9 +40,6 @@ const QString Track::kMutedInput = QStringLiteral("muted_in");
|
||||
|
||||
Track::Track() :
|
||||
track_type_(Track::kNone),
|
||||
track_length_(0),
|
||||
midop_track_length_(0),
|
||||
preop_track_length_(0),
|
||||
index_(-1),
|
||||
locked_(false)
|
||||
{
|
||||
@@ -280,11 +277,8 @@ void Track::InputDisconnectedEvent(const QString &input, int element, const Node
|
||||
// Update lengths
|
||||
if (next) {
|
||||
UpdateInOutFrom(blocks_.indexOf(next));
|
||||
} else if (blocks_.isEmpty()) {
|
||||
SetLengthInternal(0);
|
||||
} else {
|
||||
SetLengthInternal(blocks_.last()->out());
|
||||
}
|
||||
emit TrackLengthChanged();
|
||||
|
||||
disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
||||
|
||||
@@ -431,7 +425,7 @@ QVector<Block *> Track::BlocksAtTimeRange(const TimeRange &range) const
|
||||
return list;
|
||||
}
|
||||
|
||||
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time)
|
||||
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
if (GetOperationStack() != 0) {
|
||||
return;
|
||||
@@ -443,7 +437,8 @@ void Track::InvalidateCache(const TimeRange& range, const QString& from, int ele
|
||||
|
||||
if (from == kBlockInput
|
||||
&& element >= 0
|
||||
&& (b = dynamic_cast<const Block*>(GetConnectedOutput(from, element).node()))) {
|
||||
&& (b = dynamic_cast<const Block*>(GetConnectedOutput(from, element).node()))
|
||||
&& !options.value(QStringLiteral("lengthevent")).toBool()) {
|
||||
// Limit the range signal to the corresponding block
|
||||
if (range.out() <= b->in() || range.in() >= b->out()) {
|
||||
return;
|
||||
@@ -451,11 +446,14 @@ void Track::InvalidateCache(const TimeRange& range, const QString& from, int ele
|
||||
|
||||
limited = TimeRange(qMax(range.in(), b->in()), qMin(range.out(), b->out()));
|
||||
} else {
|
||||
limited = TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), qMax(preop_track_length_, track_length())));
|
||||
preop_track_length_ = track_length_;
|
||||
limited = range;
|
||||
}
|
||||
|
||||
Node::InvalidateCache(limited, from, element, job_time);
|
||||
// NOTE: For now, I figure we drop this key, but we may find in the future that it's advantageous
|
||||
// to keep it
|
||||
options.remove(QStringLiteral("lengthevent"));
|
||||
|
||||
Node::InvalidateCache(limited, from, element, options);
|
||||
}
|
||||
|
||||
void Track::InsertBlockBefore(Block* block, Block* after)
|
||||
@@ -520,7 +518,7 @@ void Track::AppendBlock(Block *block)
|
||||
EndOperation();
|
||||
|
||||
// Invalidate area that block was added to
|
||||
Node::InvalidateCache(TimeRange(block->in(), track_length()), kBlockInput);
|
||||
Node::InvalidateCache(TimeRange(block->in(), block->out()), kBlockInput);
|
||||
}
|
||||
|
||||
void Track::RippleRemoveBlock(Block *block)
|
||||
@@ -552,13 +550,17 @@ void Track::ReplaceBlock(Block *old, Block *replace)
|
||||
if (old->length() == replace->length()) {
|
||||
Node::InvalidateCache(TimeRange(replace->in(), replace->out()), kBlockInput);
|
||||
} else {
|
||||
Node::InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX), kBlockInput);
|
||||
Node::InvalidateCache(TimeRange(replace->in(), track_length()), kBlockInput);
|
||||
}
|
||||
}
|
||||
|
||||
const rational &Track::track_length() const
|
||||
rational Track::track_length() const
|
||||
{
|
||||
return track_length_;
|
||||
if (blocks_.isEmpty()) {
|
||||
return 0;
|
||||
} else {
|
||||
return blocks_.last()->out();
|
||||
}
|
||||
}
|
||||
|
||||
QString Track::GetDefaultTrackName(Track::Type type, int index)
|
||||
@@ -600,15 +602,6 @@ void Track::Hash(const QString &output, QCryptographicHash &hash, const rational
|
||||
}
|
||||
}
|
||||
|
||||
void Track::EndOperation()
|
||||
{
|
||||
super::EndOperation();
|
||||
|
||||
if (track_length_ != midop_track_length_) {
|
||||
SetLengthInternal(midop_track_length_);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::SetMuted(bool e)
|
||||
{
|
||||
SetStandardValue(kMutedInput, e);
|
||||
@@ -640,7 +633,7 @@ void Track::UpdateInOutFrom(int index)
|
||||
emit BlocksRefreshed();
|
||||
|
||||
// Update track length
|
||||
SetLengthInternal(last_out);
|
||||
emit TrackLengthChanged();
|
||||
}
|
||||
|
||||
int Track::GetArrayIndexFromBlock(Block *block) const
|
||||
@@ -658,48 +651,12 @@ int Track::GetCacheIndexFromArrayIndex(int index) const
|
||||
return block_array_indexes_.indexOf(index);
|
||||
}
|
||||
|
||||
void Track::SetLengthInternal(const rational &r, bool invalidate)
|
||||
{
|
||||
// Hold track length until operation stack is empty
|
||||
midop_track_length_ = r;
|
||||
|
||||
if (GetOperationStack() == 0 && track_length_ != r) {
|
||||
TimeRange invalidate_range(track_length_, r);
|
||||
track_length_ = r;
|
||||
preop_track_length_ = qMax(preop_track_length_, track_length_);
|
||||
emit TrackLengthChanged();
|
||||
|
||||
if (invalidate) {
|
||||
Node::InvalidateCache(invalidate_range, kBlockInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Track::BlockLengthChanged()
|
||||
{
|
||||
// Assumes sender is a Block
|
||||
Block* b = static_cast<Block*>(sender());
|
||||
|
||||
rational old_out = b->out();
|
||||
|
||||
UpdateInOutFrom(blocks_.indexOf(b));
|
||||
|
||||
rational new_out = b->out();
|
||||
|
||||
TimeRange invalidate_region(qMin(old_out, new_out), track_length());
|
||||
|
||||
// The cache won't start while dragging, so we store up our invalidations if it's held down
|
||||
// and release them once the mouse is no longer pressed
|
||||
if (qApp->mouseButtons() & Qt::LeftButton) {
|
||||
block_length_pending_invalidations_.insert(invalidate_region);
|
||||
} else if (!block_length_pending_invalidations_.isEmpty()) {
|
||||
foreach (const TimeRange& r, block_length_pending_invalidations_) {
|
||||
Node::InvalidateCache(r, kBlockInput);
|
||||
}
|
||||
block_length_pending_invalidations_.clear();
|
||||
}
|
||||
|
||||
Node::InvalidateCache(invalidate_region, kBlockInput);
|
||||
}
|
||||
|
||||
uint qHash(const Track::Reference &r, uint seed)
|
||||
|
||||
@@ -286,7 +286,7 @@ public:
|
||||
return blocks_;
|
||||
}
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override;
|
||||
|
||||
/**
|
||||
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
||||
@@ -330,7 +330,7 @@ public:
|
||||
*/
|
||||
void ReplaceBlock(Block* old, Block* replace);
|
||||
|
||||
const rational& track_length() const;
|
||||
rational track_length() const;
|
||||
|
||||
static QString GetDefaultTrackName(Track::Type type, int index);
|
||||
|
||||
@@ -345,8 +345,6 @@ public:
|
||||
return waveform_;
|
||||
}
|
||||
|
||||
virtual void EndOperation() override;
|
||||
|
||||
static const double kTrackHeightDefault;
|
||||
static const double kTrackHeightMinimum;
|
||||
static const double kTrackHeightInterval;
|
||||
@@ -420,8 +418,6 @@ private:
|
||||
|
||||
int GetCacheIndexFromArrayIndex(int index) const;
|
||||
|
||||
void SetLengthInternal(const rational& r, bool invalidate = true);
|
||||
|
||||
TimeRangeList block_length_pending_invalidations_;
|
||||
|
||||
QVector<Block*> blocks_;
|
||||
@@ -429,12 +425,6 @@ private:
|
||||
|
||||
Track::Type track_type_;
|
||||
|
||||
rational track_length_;
|
||||
|
||||
rational midop_track_length_;
|
||||
|
||||
rational preop_track_length_;
|
||||
|
||||
double track_height_;
|
||||
|
||||
int index_;
|
||||
|
||||
@@ -222,7 +222,7 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to)
|
||||
ShiftAudioCache(from, to);
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time)
|
||||
void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
@@ -233,16 +233,16 @@ void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from,
|
||||
|
||||
if (invalidated_range.in() != invalidated_range.out()) {
|
||||
if (from == kTextureInput || from == kVideoParamsInput) {
|
||||
video_frame_cache_.Invalidate(invalidated_range, job_time);
|
||||
video_frame_cache_.Invalidate(invalidated_range);
|
||||
} else {
|
||||
audio_playback_cache_.Invalidate(invalidated_range, job_time);
|
||||
audio_playback_cache_.Invalidate(invalidated_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerifyLength();
|
||||
|
||||
super::InvalidateCache(range, from, element, job_time);
|
||||
super::InvalidateCache(range, from, element, options);
|
||||
}
|
||||
|
||||
QVector<QString> ViewerOutput::inputs_for_output(const QString &output) const
|
||||
@@ -300,14 +300,8 @@ void ViewerOutput::Retranslate()
|
||||
void ViewerOutput::VerifyLength()
|
||||
{
|
||||
video_length_ = VerifyLengthInternal(Track::kVideo);
|
||||
if (video_cache_enabled_) {
|
||||
video_frame_cache_.SetLength(video_length_);
|
||||
}
|
||||
|
||||
audio_length_ = VerifyLengthInternal(Track::kAudio);
|
||||
if (audio_cache_enabled_) {
|
||||
audio_playback_cache_.SetLength(audio_length_);
|
||||
}
|
||||
|
||||
rational subtitle_length = VerifyLengthInternal(Track::kSubtitle);
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
void ShiftAudioCache(const rational& from, const rational& to);
|
||||
void ShiftCache(const rational& from, const rational& to);
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override;
|
||||
|
||||
virtual QVector<QString> inputs_for_output(const QString& output) const override;
|
||||
|
||||
@@ -154,6 +154,9 @@ public:
|
||||
|
||||
virtual NodeOutput GetConnectedSampleOutput();
|
||||
|
||||
void SetViewerVideoCacheEnabled(bool e) { video_cache_enabled_ = e; }
|
||||
void SetViewerAudioCacheEnabled(bool e) { audio_cache_enabled_ = e; }
|
||||
|
||||
static const QString kVideoParamsInput;
|
||||
static const QString kAudioParamsInput;
|
||||
|
||||
@@ -202,9 +205,6 @@ protected:
|
||||
|
||||
int AddStream(Track::Type type, const QVariant &value);
|
||||
|
||||
void SetViewerVideoCacheEnabled(bool e) { video_cache_enabled_ = e; }
|
||||
void SetViewerAudioCacheEnabled(bool e) { audio_cache_enabled_ = e; }
|
||||
|
||||
private:
|
||||
rational last_length_;
|
||||
rational video_length_;
|
||||
|
||||
@@ -138,18 +138,17 @@ void FolderAddChild::redo()
|
||||
Node::ConnectEdge(child_, NodeInput(folder_, Folder::kChildInput, array_index));
|
||||
|
||||
if (autoposition_) {
|
||||
old_position_ = child_->GetPosition();
|
||||
if (!position_command_) {
|
||||
position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, array_index, array_index+1, true);
|
||||
position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, folder_->project()->root(), array_index, array_index+1, true);
|
||||
}
|
||||
position_command_->redo();
|
||||
position_command_->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderAddChild::undo()
|
||||
{
|
||||
if (position_command_) {
|
||||
position_command_->undo();
|
||||
position_command_->undo_now();
|
||||
}
|
||||
|
||||
Node::DisconnectEdge(child_, NodeInput(folder_, Folder::kChildInput, folder_->InputArraySize(Folder::kChildInput)-1));
|
||||
|
||||
@@ -135,6 +135,7 @@ public:
|
||||
return folder_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override
|
||||
@@ -209,6 +210,7 @@ public:
|
||||
|
||||
virtual Project * GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
@@ -220,8 +222,6 @@ private:
|
||||
|
||||
bool autoposition_;
|
||||
|
||||
QPointF old_position_;
|
||||
|
||||
NodeSetPositionAsChildCommand* position_command_;
|
||||
|
||||
};
|
||||
|
||||
@@ -39,26 +39,27 @@ Project::Project() :
|
||||
// Generate UUID for this project
|
||||
RegenerateUuid();
|
||||
|
||||
// Folder root for project
|
||||
root_ = new Folder();
|
||||
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);
|
||||
color_manager_->SetPosition(QPointF(1, 0));
|
||||
SetNodePosition(color_manager_, root_, QPointF(1, 0));
|
||||
color_manager_->SetCanBeDeleted(false);
|
||||
AddDefaultNode(color_manager_);
|
||||
|
||||
// Same with project settings
|
||||
settings_ = new ProjectSettingsNode();
|
||||
settings_->setParent(this);
|
||||
settings_->SetPosition(QPointF(2, 0));
|
||||
SetNodePosition(settings_, root_, QPointF(2, 0));
|
||||
settings_->SetCanBeDeleted(false);
|
||||
AddDefaultNode(settings_);
|
||||
|
||||
// Folder root for project
|
||||
root_ = new Folder();
|
||||
root_->setParent(this);
|
||||
root_->SetLabel(tr("Root"));
|
||||
root_->SetCanBeDeleted(false);
|
||||
|
||||
connect(color_manager(), &ColorManager::ValueChanged,
|
||||
this, &Project::ColorManagerValueChanged);
|
||||
}
|
||||
@@ -135,6 +136,71 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
|
||||
}
|
||||
}
|
||||
|
||||
} else if (reader->name() == QStringLiteral("positions")) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
|
||||
if (reader->name() == QStringLiteral("context")) {
|
||||
|
||||
quintptr context_ptr = 0;
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("ptr")) {
|
||||
context_ptr = attr.value().toULongLong();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Node *context = xml_node_data.node_ptrs.value(context_ptr);
|
||||
|
||||
if (!context) {
|
||||
qWarning() << "Failed to find pointer for context";
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("node")) {
|
||||
quintptr node_ptr = 0;
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("ptr")) {
|
||||
node_ptr = attr.value().toULongLong();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Node *node = xml_node_data.node_ptrs.value(node_ptr);
|
||||
|
||||
if (!node) {
|
||||
qWarning() << "Failed to find pointer for node position";
|
||||
reader->skipCurrentElement();
|
||||
} else {
|
||||
QPointF pos;
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("x")) {
|
||||
pos.setX(reader->readElementText().toDouble());
|
||||
} else if (reader->name() == QStringLiteral("y")) {
|
||||
pos.setY(reader->readElementText().toDouble());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
SetNodePosition(node, context, pos);
|
||||
}
|
||||
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Skip this
|
||||
@@ -176,6 +242,32 @@ void Project::Save(QXmlStreamWriter *writer) const
|
||||
|
||||
writer->writeEndElement(); // nodes
|
||||
|
||||
writer->writeStartElement(QStringLiteral("positions"));
|
||||
|
||||
for (auto it=GetPositionMap().cbegin(); it!=GetPositionMap().cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("context"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
|
||||
|
||||
const PositionMap &map = it.value();
|
||||
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(jt.key())));
|
||||
|
||||
const QPointF &pos = jt.value();
|
||||
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.x()));
|
||||
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.y()));
|
||||
|
||||
writer->writeEndElement(); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // context
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // positions
|
||||
|
||||
// Save main window project layout
|
||||
MainWindowLayoutInfo main_window_info = Core::instance()->main_window()->SaveLayout();
|
||||
main_window_info.toXml(writer);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "panel/timeline/timeline.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/timelinewidget/undo/timelineundogeneral.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -63,8 +64,8 @@ void Sequence::add_default_nodes(MultiUndoCommand* command)
|
||||
command->add_child(video_track_command);
|
||||
command->add_child(audio_track_command);
|
||||
} else {
|
||||
video_track_command->redo();
|
||||
audio_track_command->redo();
|
||||
video_track_command->redo_now();
|
||||
audio_track_command->redo_now();
|
||||
delete video_track_command;
|
||||
delete audio_track_command;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user