diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index af7959b8c..b625e686a 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -235,6 +235,10 @@ void Block::Link(Block *a, Block *b) return; } + if (a == nullptr || b == nullptr) { + return; + } + // Assume both clips are already linked since Link() and Unlink() should be the only entry points to this array if (a->linked_clips_.contains(b)) { return; @@ -259,6 +263,11 @@ void Block::Unlink(Block *a, Block *b) b->linked_clips_.removeOne(a); } +bool Block::AreLinked(Block *a, Block *b) +{ + return a->linked_clips_.contains(b); +} + const QVector &Block::linked_clips() { return linked_clips_; diff --git a/app/node/block/block.h b/app/node/block/block.h index dfcc02d85..a200e39e6 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -74,6 +74,7 @@ public: static void Link(Block* a, Block* b); static void Link(QList blocks); static void Unlink(Block* a, Block* b); + static bool AreLinked(Block* a, Block* b); const QVector& linked_clips(); public slots: diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 15900f7ba..be3c9a5ab 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -318,6 +318,8 @@ private: void RippleEditTo(olive::timeline::MovementMode mode, bool insert_gaps); + void SplitBlocksPreservingLinks(const QVector& blocks, const QList ×); + void SetTimeAndSignal(const int64_t& t); TrackOutput* GetTrackFromReference(const TrackReference& ref); diff --git a/app/widget/timelinewidget/tool/razor.cpp b/app/widget/timelinewidget/tool/razor.cpp index f0bbee60e..26e704f0a 100644 --- a/app/widget/timelinewidget/tool/razor.cpp +++ b/app/widget/timelinewidget/tool/razor.cpp @@ -54,15 +54,31 @@ void TimelineWidget::RazorTool::MouseRelease(TimelineViewMouseEvent *event) // Always split at the same time rational split_time = drag_start_.GetFrame(); - QUndoCommand* command = new QUndoCommand(); + QVector blocks_to_split; - foreach (const TrackReference& track, split_tracks_) { - new TrackSplitAtTimeCommand(parent()->GetTrackFromReference(track), split_time, command); + foreach (const TrackReference& track_ref, split_tracks_) { + TrackOutput* track = parent()->GetTrackFromReference(track_ref); + Block* block_at_time = track->NearestBlockBefore(split_time); + + // Ensure there's a valid block here + if (block_at_time != track + && !blocks_to_split.contains(block_at_time)) { + blocks_to_split.append(block_at_time); + + // Add links if no alt is held + if (!(event->GetModifiers() & Qt::AltModifier)) { + foreach (Block* link, block_at_time->linked_clips()) { + if (!blocks_to_split.contains(link)) { + blocks_to_split.append(link); + } + } + } + } } split_tracks_.clear(); - olive::undo_stack.pushIfHasChildren(command); + olive::undo_stack.push(new BlockSplitPreservingLinksCommand(blocks_to_split, {split_time})); dragging_ = false; } diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 7ca339fc0..5413a00d6 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -32,12 +32,25 @@ Block* CreateSplitBlock(Block* block, rational point, QObject* parent = nullptr) return copy; } -Node* TakeNodeFromParentGraph(Node* n, QObject* new_parent = nullptr) { +Node* TakeNodeFromParentGraph(Node* n, QObject* new_parent = nullptr) +{ static_cast(n->parent())->TakeNode(n, new_parent); return n; } +TrackOutput* TrackFromBlock(Block* b) +{ + Block* next = b->next(); + + do { + next = b->next(); + } while (next != nullptr && next->type() != Block::kEnd); + + // A little hacky, but this should either be a TrackOutput* or nullptr + return static_cast(next); +} + BlockResizeCommand::BlockResizeCommand(Block *block, rational new_length, QUndoCommand* parent) : QUndoCommand(parent), block_(block), @@ -387,13 +400,16 @@ void BlockSplitCommand::undo() track_->UnblockInvalidateCache(); } +Block *BlockSplitCommand::new_block() +{ + return new_block_; +} + TrackSplitAtTimeCommand::TrackSplitAtTimeCommand(TrackOutput *track, rational point, QUndoCommand *parent) : QUndoCommand(parent) { // Find Block that contains this time - for (int i=0;iBlocks().size();i++) { - Block* b = track->Blocks().at(i); - + foreach (Block* b, track->Blocks()) { if (b->out() == point) { // This time is between blocks, no split needs to occur return; @@ -439,3 +455,51 @@ void TrackPrependBlockCommand::undo() { track_->RippleRemoveBlock(block_); } + +BlockSplitPreservingLinksCommand::BlockSplitPreservingLinksCommand(const QVector &blocks, const QList ×, QUndoCommand *parent) : + QUndoCommand(parent), + blocks_(blocks), + times_(times) +{ + QVector< QVector > split_blocks(times.size()); + + for (int i=0;i splits(blocks.size()); + + for (int j=0;jin() < time && b->out() > time) { + BlockSplitCommand* split_command = new BlockSplitCommand(TrackFromBlock(b), b, time, this); + splits.replace(j, split_command->new_block()); + } else { + splits.replace(j, nullptr); + } + } + + split_blocks.replace(i, splits); + } + + // Now that we've determined all the splits, we can relink everything + for (int i=0;i& split_list, split_blocks) { + Block::Link(split_list.at(i), split_list.at(j)); + } + } + } + } +} diff --git a/app/widget/timelinewidget/undo/undo.h b/app/widget/timelinewidget/undo/undo.h index ebf3547b1..10da80859 100644 --- a/app/widget/timelinewidget/undo/undo.h +++ b/app/widget/timelinewidget/undo/undo.h @@ -179,6 +179,8 @@ public: virtual void redo() override; virtual void undo() override; + Block* new_block(); + private: TrackOutput* track_; Block* block_; @@ -196,6 +198,16 @@ public: TrackSplitAtTimeCommand(TrackOutput* track, rational point, QUndoCommand* parent = nullptr); }; +class BlockSplitPreservingLinksCommand : public QUndoCommand { +public: + BlockSplitPreservingLinksCommand(const QVector &blocks, const QList& times, QUndoCommand* parent = nullptr); + +private: + QVector blocks_; + + QList times_; +}; + /** * @brief Replaces Block `old` with Block `replace` *