diff --git a/app/common/xmlutils.h b/app/common/xmlutils.h index e6a290fbd..d29032320 100644 --- a/app/common/xmlutils.h +++ b/app/common/xmlutils.h @@ -50,7 +50,7 @@ struct XMLNodeData { }; struct BlockLink { - Block* block; + Node* block; quintptr link; }; diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 09289ac8d..3dada5b15 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -170,24 +170,6 @@ rational Block::MediaToSequenceTime(const rational &media_time) const return sequence_time; } -void Block::LoadInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data) -{ - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("link")) { - xml_node_data.block_links.append({this, reader->readElementText().toULongLong()}); - } else { - reader->skipCurrentElement(); - } - } -} - -void Block::SaveInternal(QXmlStreamWriter *writer) const -{ - foreach (Block* link, linked_clips_) { - writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast(link))); - } -} - QVector Block::GetInputsToHash() const { QVector inputs = Node::GetInputsToHash(); @@ -200,74 +182,24 @@ QVector Block::GetInputsToHash() const return inputs; } +void Block::LinkChangeEvent() +{ + block_links_.clear(); + + foreach (Node* n, links()) { + Block* b = dynamic_cast(n); + + if (b) { + block_links_.append(b); + } + } +} + void Block::set_length_internal(const rational &length) { length_input_->SetStandardValue(QVariant::fromValue(length)); } -bool Block::Link(Block *a, Block *b) -{ - if (a == b || !a || !b) { - return false; - } - - // Prevent duplicate link entries (assume that we only need to check one clip since this should be the only function - // that adds to the linked array) - if (Block::AreLinked(a, b)) { - return false; - } - - a->linked_clips_.append(b); - b->linked_clips_.append(a); - - emit a->LinksChanged(); - emit b->LinksChanged(); - - return true; -} - -void Block::Link(const QList& blocks) -{ - foreach (Block* a, blocks) { - foreach (Block* b, blocks) { - Link(a, b); - } - } -} - -bool Block::Unlink(Block *a, Block *b) -{ - if (a == b || !a || !b) { - return false; - } - - if (!Block::AreLinked(a, b)) { - return false; - } - - a->linked_clips_.removeOne(b); - b->linked_clips_.removeOne(a); - - emit a->LinksChanged(); - emit b->LinksChanged(); - - return true; -} - -void Block::Unlink(const QList &blocks) -{ - foreach (Block* a, blocks) { - foreach (Block* b, blocks) { - Unlink(a, b); - } - } -} - -bool Block::AreLinked(Block *a, Block *b) -{ - return a->linked_clips_.contains(b); -} - void Block::Retranslate() { Node::Retranslate(); diff --git a/app/node/block/block.h b/app/node/block/block.h index 53bc42a31..f6866dfd6 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -112,22 +112,6 @@ public: bool is_enabled() const; void set_enabled(bool e); - static bool Link(Block* a, Block* b); - static void Link(const QList& blocks); - static bool Unlink(Block* a, Block* b); - static void Unlink(const QList& blocks); - static bool AreLinked(Block* a, Block* b); - - const QVector& linked_clips() const - { - return linked_clips_; - } - - bool HasLinks() const - { - return !linked_clips_.isEmpty(); - } - virtual void Retranslate() override; NodeInput* length_input() const @@ -175,13 +159,16 @@ public: index_ = i; } + const QVector& block_links() const + { + return block_links_; + } + virtual void Hash(QCryptographicHash &hash, const rational &time) const override; public slots: signals: - void LinksChanged(); - void EnabledChanged(); void LengthChanged(); @@ -191,12 +178,10 @@ protected: rational MediaToSequenceTime(const rational& media_time) const; - virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data) override; - - virtual void SaveInternal(QXmlStreamWriter* writer) const override; - virtual QVector GetInputsToHash() const override; + virtual void LinkChangeEvent() override; + Block* previous_; Block* next_; @@ -216,7 +201,7 @@ private: TransitionBlock* in_transition_; TransitionBlock* out_transition_; - QVector linked_clips_; + QVector block_links_; }; diff --git a/app/node/node.cpp b/app/node/node.cpp index f36e6e40e..4af806131 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -108,6 +108,14 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto SetLabel(reader->readElementText()); } else if (reader->name() == QStringLiteral("color")) { override_color_ = reader->readElementText().toInt(); + } else if (reader->name() == QStringLiteral("links")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("link")) { + xml_node_data.block_links.append({this, reader->readElementText().toULongLong()}); + } else { + reader->skipCurrentElement(); + } + } } else if (reader->name() == QStringLiteral("custom")) { LoadInternal(reader, xml_node_data); } else { @@ -136,6 +144,12 @@ void Node::Save(QXmlStreamWriter *writer) const writer->writeEndElement(); // input } + writer->writeStartElement(QStringLiteral("links")); + foreach (Node* link, links_) { + writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast(link))); + } + writer->writeEndElement(); // links + writer->writeStartElement(QStringLiteral("custom")); SaveInternal(writer); writer->writeEndElement(); // custom @@ -299,6 +313,51 @@ void Node::InvalidateAll(NodeInput* input, int element) InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), {input, element}); } +bool Node::Link(Node *a, Node *b) +{ + if (a == b || !a || !b) { + return false; + } + + if (AreLinked(a, b)) { + return false; + } + + a->links_.append(b); + b->links_.append(a); + + a->LinkChangeEvent(); + b->LinkChangeEvent(); + + emit a->LinksChanged(); + emit b->LinksChanged(); + + return true; +} + +bool Node::Unlink(Node *a, Node *b) +{ + if (!AreLinked(a, b)) { + return false; + } + + a->links_.removeOne(b); + b->links_.removeOne(a); + + a->LinkChangeEvent(); + b->LinkChangeEvent(); + + emit a->LinksChanged(); + emit b->LinksChanged(); + + return true; +} + +bool Node::AreLinked(Node *a, Node *b) +{ + return a->links_.contains(b); +} + void Node::IgnoreInvalidationsFrom(NodeInput *input) { ignore_connections_.append(input); diff --git a/app/node/node.h b/app/node/node.h index 779599c49..a13180741 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -425,6 +425,20 @@ public: void InvalidateAll(NodeInput *input, int element); + bool HasLinks() const + { + return !links_.isEmpty(); + } + + const QVector& links() const + { + return links_; + } + + static bool Link(Node* a, Node* b); + static bool Unlink(Node* a, Node* b); + static bool AreLinked(Node* a, Node* b); + protected: void SendInvalidateCache(const TimeRange &range); @@ -463,6 +477,8 @@ protected: virtual void childEvent(QChildEvent* event) override; + virtual void LinkChangeEvent(){} + signals: /** * @brief Signal emitted whenever the position is set through SetPosition() @@ -486,6 +502,8 @@ signals: void OutputDisconnected(NodeInput* destination, int element); + void LinksChanged(); + private: template static void FindInputNodeInternal(const Node* n, QVector& list); @@ -521,6 +539,11 @@ private: */ int override_color_; + /** + * @brief Nodes that are linked with this one + */ + QVector links_; + private slots: void ParameterValueChanged(const olive::TimeRange &range, int element); diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp index 370657cd2..f7f6449ce 100644 --- a/app/widget/nodeview/nodeviewundo.cpp +++ b/app/widget/nodeview/nodeviewundo.cpp @@ -113,9 +113,8 @@ void NodeRemoveAndDisconnectCommand::prep() command_ = new QUndoCommand(); // If this is a block, remove all links - Block* block = dynamic_cast(node_); - if (block) { - new BlockUnlinkAllCommand(block, command_); + if (node_->HasLinks()) { + new NodeUnlinkAllCommand(node_, command_); } // Disconnect everything diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h index c440827c3..a653e64fa 100644 --- a/app/widget/nodeview/nodeviewundo.h +++ b/app/widget/nodeview/nodeviewundo.h @@ -230,6 +230,114 @@ private: }; +class NodeLinkCommand : public UndoCommand { +public: + NodeLinkCommand(Node* a, Node* b, bool link, QUndoCommand* parent = nullptr) : + UndoCommand(parent), + a_(a), + b_(b), + link_(link) + { + } + + virtual Project* GetRelevantProject() const override + { + return a_->parent()->project(); + } + +protected: + virtual void redo_internal() override + { + if (link_) { + done_ = Node::Link(a_, b_); + } else { + done_ = Node::Unlink(a_, b_); + } + } + + virtual void undo_internal() override + { + if (done_) { + if (link_) { + Node::Unlink(a_, b_); + } else { + Node::Link(a_, b_); + } + } + } + +private: + Node* a_; + Node* b_; + bool link_; + bool done_; + +}; + +class NodeUnlinkAllCommand : public UndoCommand { +public: + NodeUnlinkAllCommand(Node* node, QUndoCommand* parent = nullptr) : + UndoCommand(parent), + node_(node) + { + } + + virtual Project* GetRelevantProject() const override + { + return node_->parent()->project(); + } + +protected: + virtual void redo_internal() override + { + unlinked_ = node_->links(); + + foreach (Node* link, unlinked_) { + Node::Unlink(node_, link); + } + } + + virtual void undo_internal() override + { + foreach (Node* link, unlinked_) { + Node::Link(node_, link); + } + + unlinked_.clear(); + } + +private: + Node* node_; + + QVector unlinked_; + +}; + +class NodeLinkManyCommand : public UndoCommand { +public: + NodeLinkManyCommand(const QVector nodes, bool link, QUndoCommand* parent = nullptr) : + UndoCommand(parent), + nodes_(nodes) + { + foreach (Node* a, nodes_) { + foreach (Node* b, nodes_) { + if (a != b) { + new NodeLinkCommand(a, b, link, this); + } + } + } + } + + virtual Project* GetRelevantProject() const override + { + return nodes_.first()->parent()->project(); + } + +private: + QVector nodes_; + +}; + } #endif // NODEVIEWUNDO_H diff --git a/app/widget/timelinewidget/timelineundo.h b/app/widget/timelinewidget/timelineundo.h index 9da4d496b..995acb524 100644 --- a/app/widget/timelinewidget/timelineundo.h +++ b/app/widget/timelinewidget/timelineundo.h @@ -500,117 +500,6 @@ private: Block* before_; }; -class BlockLinkCommand : public UndoCommand { -public: - BlockLinkCommand(Block* a, Block* b, bool link, QUndoCommand* parent = nullptr) : - UndoCommand(parent), - a_(a), - b_(b), - link_(link) - { - } - - virtual Project* GetRelevantProject() const override - { - return a_->parent()->project(); - } - -protected: - virtual void redo_internal() override - { - if (link_) { - done_ = Block::Link(a_, b_); - } else { - done_ = Block::Unlink(a_, b_); - } - } - - virtual void undo_internal() override - { - if (done_) { - if (link_) { - Block::Unlink(a_, b_); - } else { - Block::Link(a_, b_); - } - } - } - -private: - Block* a_; - - Block* b_; - - bool link_; - - bool done_; - -}; - -class BlockUnlinkAllCommand : public UndoCommand { -public: - BlockUnlinkAllCommand(Block* block, QUndoCommand* parent = nullptr) : - UndoCommand(parent), - block_(block) - { - } - - virtual Project* GetRelevantProject() const override - { - return static_cast(block_->parent())->project(); - } - -protected: - virtual void redo_internal() override - { - unlinked_ = block_->linked_clips(); - - foreach (Block* link, unlinked_) { - Block::Unlink(block_, link); - } - } - - virtual void undo_internal() override - { - foreach (Block* link, unlinked_) { - Block::Link(block_, link); - } - - unlinked_.clear(); - } - -private: - Block* block_; - - QVector unlinked_; - -}; - -class BlockLinkManyCommand : public UndoCommand { -public: - BlockLinkManyCommand(const QVector blocks, bool link, QUndoCommand* parent = nullptr) : - UndoCommand(parent), - blocks_(blocks) - { - foreach (Block* a, blocks_) { - foreach (Block* b, blocks_) { - if (a != b) { - new BlockLinkCommand(a, b, link, this); - } - } - } - } - - virtual Project* GetRelevantProject() const override - { - return blocks_.first()->parent()->project(); - } - -private: - QVector blocks_; - -}; - class BlockSplitCommand : public UndoCommand { public: BlockSplitCommand(Block* block, rational point, QUndoCommand* parent = nullptr) : @@ -833,7 +722,7 @@ protected: // These blocks are linked, ensure all the splits are linked too foreach (const QVector& split_list, split_blocks) { - BlockLinkCommand* blc = new BlockLinkCommand(split_list.at(i), split_list.at(j), true); + NodeLinkCommand* blc = new NodeLinkCommand(split_list.at(i), split_list.at(j), true); blc->redo(); commands_.append(blc); } diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 415a96bcc..55ddd54e0 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -530,7 +530,7 @@ void TimelineWidget::OverwriteFootageAtPlayhead(const QVector &footag void TimelineWidget::ToggleLinksOnSelected() { - QVector blocks; + QVector blocks; bool link = true; foreach (Block* item, GetSelectedBlocks()) { @@ -551,7 +551,7 @@ void TimelineWidget::ToggleLinksOnSelected() return; } - Core::instance()->undo_stack()->push(new BlockLinkManyCommand(blocks, link)); + Core::instance()->undo_stack()->push(new NodeLinkManyCommand(blocks, link)); } void TimelineWidget::CopySelected(bool cut) @@ -1050,7 +1050,7 @@ void TimelineWidget::SetViewBeamCursor(const TimelineCoordinate &coord) void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected) { - foreach (Block* link, block->linked_clips()) { + foreach (Block* link, block->block_links()) { if (selected) { AddSelection(link); } else { @@ -1385,7 +1385,7 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin } if (select_links) { - foreach (Block* link, b->linked_clips()) { + foreach (Block* link, b->block_links()) { if (!rubberband_now_selected_.contains(link)) { AddSelection(link); rubberband_now_selected_.append(link); diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 64511719a..5c39f7951 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -93,7 +93,7 @@ void PointerTool::MousePress(TimelineViewMouseEvent *event) // If not holding alt, deselect all links as well if (!(event->GetModifiers() & Qt::AltModifier)) { parent()->SetBlockLinksSelected(clicked_item_, false); - deselected_blocks.append(clicked_item_->linked_clips()); + deselected_blocks.append(clicked_item_->block_links()); } } @@ -120,7 +120,7 @@ void PointerTool::MousePress(TimelineViewMouseEvent *event) // If not holding alt, select all links as well if (!(event->GetModifiers() & Qt::AltModifier)) { parent()->SetBlockLinksSelected(clicked_item_, true); - selected_blocks.append(clicked_item_->linked_clips()); + selected_blocks.append(clicked_item_->block_links()); } parent()->SignalSelectedBlocks(selected_blocks); diff --git a/app/widget/timelinewidget/tool/razor.cpp b/app/widget/timelinewidget/tool/razor.cpp index 45be13358..e02b1fea9 100644 --- a/app/widget/timelinewidget/tool/razor.cpp +++ b/app/widget/timelinewidget/tool/razor.cpp @@ -77,7 +77,7 @@ void RazorTool::MouseRelease(TimelineViewMouseEvent *event) // Add links if no alt is held if (!(event->GetModifiers() & Qt::AltModifier)) { - foreach (Block* link, block_at_time->linked_clips()) { + foreach (Block* link, block_at_time->block_links()) { if (!blocks_to_split.contains(link)) { blocks_to_split.append(link); }