diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index cb7cdd4ae..23a182cde 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -520,20 +520,28 @@ void TimelineWidget::DeleteSelected(bool ripple) command->add_child(new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections(), false)); // Insert ripple command now that it's all cleaned up gaps + TimelineRippleDeleteGapsAtRegionsCommand *ripple_command = nullptr; + rational new_playhead = RATIONAL_MAX; if (ripple) { - TimeRangeList range_list; + QVector > range_list; foreach (Block* b, blocks_to_delete) { - range_list.insert(TimeRange(b->in(), b->out())); + range_list.append({b->track(), b->range()}); + new_playhead = qMin(new_playhead, b->in()); } - command->add_child(new TimelineRippleDeleteGapsAtRegionsCommand(sequence(), range_list)); + ripple_command = new TimelineRippleDeleteGapsAtRegionsCommand(sequence(), range_list); + command->add_child(ripple_command); } Core::instance()->undo_stack()->pushIfHasChildren(command); // Ensures any current drag operations are cancelled ClearGhosts(); + + if (ripple_command && ripple_command->HasCommands() && new_playhead != RATIONAL_MAX) { + SetTimeAndSignal(new_playhead); + } } void TimelineWidget::IncreaseTrackHeight() diff --git a/app/widget/timelinewidget/undo/timelineundoripple.cpp b/app/widget/timelinewidget/undo/timelineundoripple.cpp index 391feeede..acbbe083e 100644 --- a/app/widget/timelinewidget/undo/timelineundoripple.cpp +++ b/app/widget/timelinewidget/undo/timelineundoripple.cpp @@ -457,46 +457,128 @@ void TrackListRippleToolCommand::ripple(bool redo) // // TimelineRippleDeleteGapsAtRegionsCommand // -void TimelineRippleDeleteGapsAtRegionsCommand::redo() +void TimelineRippleDeleteGapsAtRegionsCommand::prepare() { - if (commands_.isEmpty()) { - foreach (const TimeRange& range, regions_) { - rational max_ripple_length = range.length(); + int max_gaps = 0; + QHash > requested_gaps; - QVector blocks_around_range; + // Convert regions to gaps + for (const QPair ®ion : qAsConst(regions_)) { + Track *track = region.first; + const TimeRange &range = region.second; - foreach (Track* track, timeline_->GetTracks()) { - // Get the block from every other track that is either at or just before our block's in point - Block* block_at_time = track->NearestBlockBeforeOrAt(range.in()); + GapBlock *gap = dynamic_cast(track->NearestBlockBeforeOrAt(range.in())); - if (block_at_time) { - if (dynamic_cast(block_at_time)) { - max_ripple_length = qMin(block_at_time->length(), max_ripple_length); - } else { - max_ripple_length = 0; - break; + if (gap) { + QVector &gaps_on_track = requested_gaps[track]; + + RemovalRequest this_req = {gap, range}; + + // Insertion sort + bool inserted = false; + for (int i=0; i &gaps_on_track, requested_gaps) { + if (gap_index < gaps_on_track.size()) { + const RemovalRequest &gap = gaps_on_track.at(gap_index); + earliest_point = qMin(earliest_point, gap.range.in()); + ripple_length = qMin(ripple_length, gap.range.length()); + } + } + + // Determine which gaps will be involved in this operation + QVector gaps; + + foreach (Track* track, timeline_->GetTracks()) { + if (track->IsLocked()) { + continue; + } + + const QVector &requested_gaps_on_track = requested_gaps.value(track); + GapBlock *gap = nullptr; + if (gap_index < requested_gaps_on_track.size()) { + // A requested gap was at this index, use it + gap = requested_gaps_on_track.at(gap_index).gap; + } else { + // No requested gap was at this index, find one + Block *block = track->NearestBlockAfterOrAt(earliest_point); + + if (block) { + // Found a block, test if it's a gap + gap = dynamic_cast(block); + + if (!gap) { + if (block->in() == earliest_point) { + if (block->next()) { + gap = dynamic_cast(block->next()); + + if (!gap) { + ripple_length = 0; + } + } + } else { + gap = dynamic_cast(block->previous()); + + if (!gap) { + ripple_length = 0; + } + } } - - blocks_around_range.append(block_at_time); + } else { + // Assume track finishes here and track won't be affected by this operation } } - if (max_ripple_length > 0) { - foreach (Block* resize, blocks_around_range) { - if (resize->length() == max_ripple_length) { - // Remove block entirely - commands_.append(new TrackRippleRemoveBlockCommand(resize->track(), resize)); - } else { - // Resize block - commands_.append(new BlockResizeCommand(resize, resize->length() - max_ripple_length)); - } + if (gap) { + gaps.append(gap); + ripple_length = qMin(ripple_length, gap->length()); + } + + if (ripple_length == 0) { + break; + } + } + + if (ripple_length > 0) { + foreach (GapBlock *gap, gaps) { + if (gap->length() == ripple_length) { + commands_.append(new TrackRippleRemoveBlockCommand(gap->track(), gap)); + } else { + commands_.append(new BlockResizeCommand(gap, gap->length() - ripple_length)); } } } } +} - foreach (UndoCommand* c, commands_) { - c->redo_now(); +void TimelineRippleDeleteGapsAtRegionsCommand::redo() +{ + for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) { + (*it)->redo_now(); + } +} + +void TimelineRippleDeleteGapsAtRegionsCommand::undo() +{ + for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) { + (*it)->undo_now(); } } diff --git a/app/widget/timelinewidget/undo/timelineundoripple.h b/app/widget/timelinewidget/undo/timelineundoripple.h index 9164ce9f3..8c15222fc 100644 --- a/app/widget/timelinewidget/undo/timelineundoripple.h +++ b/app/widget/timelinewidget/undo/timelineundoripple.h @@ -202,7 +202,7 @@ private: class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand { public: - TimelineRippleDeleteGapsAtRegionsCommand(Sequence* vo, const TimeRangeList& regions) : + TimelineRippleDeleteGapsAtRegionsCommand(Sequence* vo, const QVector >& regions) : timeline_(vo), regions_(regions) { @@ -218,22 +218,29 @@ public: return timeline_->project(); } + bool HasCommands() const + { + return !commands_.isEmpty(); + } + protected: + virtual void prepare() override; + virtual void redo() override; - virtual void undo() override - { - for (int i=commands_.size()-1;i>=0;i--) { - commands_.at(i)->undo_now(); - } - } + virtual void undo() override; private: Sequence* timeline_; - TimeRangeList regions_; + QVector > regions_; QVector commands_; + struct RemovalRequest { + GapBlock *gap; + TimeRange range; + }; + }; }