@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user