diff --git a/app/widget/timelinewidget/timelineundo.h b/app/widget/timelinewidget/timelineundo.h index 8c472a658..3e38eb34d 100644 --- a/app/widget/timelinewidget/timelineundo.h +++ b/app/widget/timelinewidget/timelineundo.h @@ -463,7 +463,6 @@ public: Q_ASSERT(i == 0 || time > times_.at(i-1)); QVector splits(blocks_.size()); - commands_.resize(blocks_.size()); for (int j=0;jredo(); splits.replace(j, split_command->new_block()); - commands_.replace(j, split_command); + commands_.append(split_command); } else { splits.replace(j, nullptr); } @@ -2045,8 +2044,8 @@ public: } foreach (auto add_gap, gaps_added_) { - add_gap.gap->setParent(add_gap.before->parent()); - add_gap.before->track()->InsertBlockAfter(add_gap.gap, add_gap.before); + add_gap.gap->setParent(add_gap.track->parent()); + add_gap.track->InsertBlockAfter(add_gap.gap, add_gap.before); } foreach (Track* track, working_tracks_) { @@ -2121,6 +2120,7 @@ private: QVector blocks_to_split; QVector blocks_to_append_gap_to; + QVector tracks_to_append_gap_to; foreach (Track* track, working_tracks_) { foreach (Block* b, track->Blocks()) { @@ -2129,11 +2129,24 @@ private: gaps_to_extend_.append(b); break; } else if (dynamic_cast(b) && b->out() >= point_) { - if (b->out() > point_) { + bool append_gap = true; + + if (b->in() == point_) { + // The only reason we should be here is if this block is at the start of the track, + // in which case no split needs to occur + b = nullptr; + } else if (b->out() > point_) { + // Block must be split as well as having a gap appended to it blocks_to_split.append(b); + } else if (!b->next()) { + // At the end of a track, no gap needs to be added at all + append_gap = false; } - blocks_to_append_gap_to.append(b); + if (append_gap) { + tracks_to_append_gap_to.append(track); + blocks_to_append_gap_to.append(b); + } break; } } @@ -2143,11 +2156,11 @@ private: split_command_ = new BlockSplitPreservingLinksCommand(blocks_to_split, {point_}); } - foreach (Block* block, blocks_to_append_gap_to) { + for (int i=0; iset_length_and_media_out(length_); gap->setParent(&memory_manager_); - gaps_added_.append({gap, block}); + gaps_added_.append({gap, blocks_to_append_gap_to.at(i), tracks_to_append_gap_to.at(i)}); } } @@ -2166,6 +2179,7 @@ private: struct AddGap { GapBlock* gap; Block* before; + Track* track; }; QVector gaps_added_; diff --git a/tests/timeline/timeline-tests.cpp b/tests/timeline/timeline-tests.cpp index 7c68284f2..529168e30 100644 --- a/tests/timeline/timeline-tests.cpp +++ b/tests/timeline/timeline-tests.cpp @@ -488,4 +488,112 @@ OLIVE_ADD_TEST(ReplaceBlockWithGap_ClipsAndTransitions) OLIVE_TEST_END; } +OLIVE_ADD_TEST(InsertGaps_SingleTrack) +{ + TIMELINE_TEST_START; + + sequence.add_default_nodes(); + + TrackList *list = sequence.track_list(Track::kVideo); + Track *track = list->GetTracks().first(); + + ClipBlock *a = new ClipBlock(); + a->setParent(&project); + track->AppendBlock(a); + + ClipBlock *b = new ClipBlock(); + b->setParent(&project); + track->AppendBlock(b); + + ClipBlock *c = new ClipBlock(); + c->setParent(&project); + track->AppendBlock(c); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + + { + // Insert gap at the start of the track, all blocks should be unsplit and shifted to the right + TrackListInsertGaps command(list, 0, 2); + command.redo(); + + OLIVE_ASSERT(track->Blocks().size() == 4); + OLIVE_ASSERT(dynamic_cast(track->Blocks().at(0))); + OLIVE_ASSERT(track->Blocks().at(0)->length() == 2); + OLIVE_ASSERT(track->Blocks().at(1) == a); + OLIVE_ASSERT(track->Blocks().at(2) == b); + OLIVE_ASSERT(track->Blocks().at(3) == c); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + } + + { + // Insert gap in the middle of block A, block A should be halved with a copy at 2 and the gap at 1 + TrackListInsertGaps command(list, rational(1, 2), 2); + command.redo(); + + OLIVE_ASSERT(track->Blocks().size() == 5); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(0)->length() == rational(1, 2)); + OLIVE_ASSERT(dynamic_cast(track->Blocks().at(1))); + OLIVE_ASSERT(dynamic_cast(track->Blocks().at(2))); + OLIVE_ASSERT(track->Blocks().at(3) == b); + OLIVE_ASSERT(track->Blocks().at(4) == c); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(0)->length() = 1); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + } + + { + // Insert gap between block A and B, blocks should be unsplit with a gap at 1 + TrackListInsertGaps command(list, 1, 2); + command.redo(); + + OLIVE_ASSERT(track->Blocks().size() == 4); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(dynamic_cast(track->Blocks().at(1))); + OLIVE_ASSERT(track->Blocks().at(2) == b); + OLIVE_ASSERT(track->Blocks().at(3) == c); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + } + + { + // Insert gap at end, nothing should be added + TrackListInsertGaps command(list, 3, 2); + command.redo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 3); + OLIVE_ASSERT(track->Blocks().at(0) == a); + OLIVE_ASSERT(track->Blocks().at(1) == b); + OLIVE_ASSERT(track->Blocks().at(2) == c); + } + + OLIVE_TEST_END; +} + }