timeline: remove transitions when removing clips

Fixes #1224
This commit is contained in:
itsmattkc
2021-04-26 20:29:21 +10:00
parent 67f48bc37c
commit e2a590da26
6 changed files with 564 additions and 245 deletions
+172
View File
@@ -200,4 +200,176 @@ void BlockTrimCommand::prep()
}
}
void TrackReplaceBlockWithGapCommand::redo()
{
// Determine if this block is connected to any transitions that should also be removed by this operation
if (transition_remove_commands_.isEmpty()) {
CreateRemoveTransitionCommandIfNecessary(false);
CreateRemoveTransitionCommandIfNecessary(true);
}
for (auto it=transition_remove_commands_.cbegin(); it!=transition_remove_commands_.cend(); it++) {
(*it)->redo();
}
if (block_->next()) {
track_->BeginOperation();
// Invalidate the range inhabited by this block
TimeRange invalidate_range(block_->in(), block_->out());
// Block has a next, which means it's NOT at the end of the sequence and thus requires a gap
rational new_gap_length = block_->length();
Block* previous = block_->previous();
Block* next = block_->next();
bool previous_is_a_gap = dynamic_cast<GapBlock*>(previous);
bool next_is_a_gap = dynamic_cast<GapBlock*>(next);
if (previous_is_a_gap && next_is_a_gap) {
// Clip is preceded and followed by a gap, so we'll merge the two
existing_gap_ = static_cast<GapBlock*>(previous);
existing_merged_gap_ = static_cast<GapBlock*>(next);
new_gap_length += existing_merged_gap_->length();
track_->RippleRemoveBlock(existing_merged_gap_);
existing_merged_gap_->setParent(&memory_manager_);
} else if (previous_is_a_gap) {
// Extend this gap to fill space left by block
existing_gap_ = static_cast<GapBlock*>(previous);
} else if (next_is_a_gap) {
// Extend this gap to fill space left by block
existing_gap_ = static_cast<GapBlock*>(next);
}
if (existing_gap_) {
// Extend an existing gap
new_gap_length += existing_gap_->length();
existing_gap_->set_length_and_media_out(new_gap_length);
track_->RippleRemoveBlock(block_);
existing_gap_precedes_ = (existing_gap_ == previous);
} else {
// No gap exists to fill this space, create a new one and swap it in
if (!our_gap_) {
our_gap_ = new GapBlock();
our_gap_->set_length_and_media_out(new_gap_length);
}
our_gap_->setParent(track_->parent());
track_->ReplaceBlock(block_, our_gap_);
if (!position_command_) {
position_command_ = new NodeSetPositionAsChildCommand(our_gap_, track_, our_gap_->index(), track_->Blocks().size(), true);
}
position_command_->redo();
}
track_->EndOperation();
track_->Node::InvalidateCache(invalidate_range, Track::kBlockInput);
} else {
// Block is at the end of the track, simply remove it
// Determine if it's proceeded by a gap, and remove that gap if so
Block* preceding = block_->previous();
if (dynamic_cast<GapBlock*>(preceding)) {
track_->RippleRemoveBlock(preceding);
preceding->setParent(&memory_manager_);
existing_merged_gap_ = static_cast<GapBlock*>(preceding);
}
// Remove block in question
track_->RippleRemoveBlock(block_);
}
}
void TrackReplaceBlockWithGapCommand::undo()
{
if (our_gap_ || existing_gap_) {
track_->BeginOperation();
if (our_gap_) {
// We made this gap, simply swap our gap back
track_->ReplaceBlock(our_gap_, block_);
our_gap_->setParent(&memory_manager_);
position_command_->undo();
} else {
// If we're here, assume that we extended an existing gap
rational original_gap_length = existing_gap_->length() - block_->length();
// If we merged two gaps together, restore the second one now
if (existing_merged_gap_) {
original_gap_length -= existing_merged_gap_->length();
existing_merged_gap_->setParent(track_->parent());
track_->InsertBlockAfter(existing_merged_gap_, existing_gap_);
existing_merged_gap_ = nullptr;
}
// Restore original block
if (existing_gap_precedes_) {
track_->InsertBlockAfter(block_, existing_gap_);
} else {
track_->InsertBlockBefore(block_, existing_gap_);
}
// Restore gap's original length
existing_gap_->set_length_and_media_out(original_gap_length);
existing_gap_ = nullptr;
}
track_->EndOperation();
track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput);
} else {
// Our gap and existing gap were both null, our block must have been at the end and thus
// required no gap extension/replacement
// However, we may have removed an unnecessary gap that preceded it
if (existing_merged_gap_) {
existing_merged_gap_->setParent(track_->parent());
track_->AppendBlock(existing_merged_gap_);
existing_merged_gap_ = nullptr;
}
// Restore block
track_->AppendBlock(block_);
}
for (auto it=transition_remove_commands_.crbegin(); it!=transition_remove_commands_.crend(); it++) {
(*it)->undo();
}
}
void TrackReplaceBlockWithGapCommand::CreateRemoveTransitionCommandIfNecessary(bool next)
{
Block* relevant_block;
if (next) {
relevant_block = block_->next();
} else {
relevant_block = block_->previous();
}
TransitionBlock* transition_cast_test = dynamic_cast<TransitionBlock*>(relevant_block);
if (transition_cast_test) {
if ((next && transition_cast_test->connected_out_block() == block_ && !transition_cast_test->connected_in_block())
|| (!next && transition_cast_test->connected_in_block() == block_ && !transition_cast_test->connected_out_block())) {
TransitionRemoveCommand* command = new TransitionRemoveCommand(transition_cast_test, true);
transition_remove_commands_.append(command);
}
}
}
}