From cce379ad229cf1b20099557e89051dfebe11cee0 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 28 Jan 2022 13:49:49 -0800 Subject: [PATCH] handle cache properly in ripple delete --- app/render/playbackcache.cpp | 15 +++-- app/widget/timelinewidget/timelinewidget.cpp | 17 ++--- app/widget/timelinewidget/timelinewidget.h | 4 +- app/widget/timelinewidget/tool/pointer.cpp | 6 +- .../undo/timelineundogeneral.cpp | 8 ++- .../timelinewidget/undo/timelineundogeneral.h | 62 ++++++++++++++++++- .../undo/timelineundoripple.cpp | 44 ++++++++++++- .../timelinewidget/undo/timelineundoripple.h | 43 +++++++++++-- 8 files changed, 168 insertions(+), 31 deletions(-) diff --git a/app/render/playbackcache.cpp b/app/render/playbackcache.cpp index 1f50f736d..7a51597c0 100644 --- a/app/render/playbackcache.cpp +++ b/app/render/playbackcache.cpp @@ -57,21 +57,24 @@ void PlaybackCache::Shift(rational from, rational to) // An region between `from` and `to` will be inserted or spliced out TimeRangeList ranges_to_shift = validated_.Intersects(TimeRange(from, RATIONAL_MAX)); - // Remove everything from the minimum point - TimeRange remove_range = TimeRange(qMin(from, to), RATIONAL_MAX); - Invalidate(remove_range, false); + // Remove all ranges starting at to + validated_.remove(TimeRange(qMin(from, to), RATIONAL_MAX)); - // Shift invalidated ranges - // (`diff` is POSITIVE when moving forward -> and NEGATIVE when moving backward <-) + // Restore ranges shifted rational diff = to - from; foreach (const TimeRange& r, ranges_to_shift) { - Validate(r + diff, false); + validated_.insert(r + diff); } + // Tell derivatives that a shift has occurred ShiftEvent(from, to); // Emit signals emit Shifted(from, to); + + if (diff > 0) { + //emit Invalidated(TimeRange(from, to)); + } } void PlaybackCache::Validate(const TimeRange &r, bool signal) diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index d827af5d5..af0844486 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -462,7 +462,8 @@ void TimelineWidget::SplitAtPlayhead() void TimelineWidget::ReplaceBlocksWithGaps(const QVector &blocks, bool remove_from_graph, MultiUndoCommand *command, - bool handle_transitions) + bool handle_transitions, + bool handle_invalidations) { foreach (Block* b, blocks) { if (dynamic_cast(b)) { @@ -473,7 +474,7 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QVector &blocks, Track* original_track = b->track(); - command->add_child(new TrackReplaceBlockWithGapCommand(original_track, b, handle_transitions)); + command->add_child(new TrackReplaceBlockWithGapCommand(original_track, b, handle_transitions, handle_invalidations)); if (remove_from_graph) { command->add_child(new NodeRemoveWithExclusiveDependenciesAndDisconnect(b)); @@ -508,6 +509,9 @@ void TimelineWidget::DeleteSelected(bool ripple) MultiUndoCommand* command = new MultiUndoCommand(); + // Remove all selections + command->add_child(new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections())); + // For transitions, remove them but extend their attached blocks to fill their place foreach (TransitionBlock* transition, transitions_to_delete) { TransitionRemoveCommand *trc = new TransitionRemoveCommand(transition, true); @@ -519,10 +523,7 @@ void TimelineWidget::DeleteSelected(bool ripple) } // Replace clips with gaps (effectively deleting them) - ReplaceBlocksWithGaps(clips_to_delete, true, command); - - // Remove all selections - command->add_child(new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections(), false)); + ReplaceBlocksWithGaps(clips_to_delete, true, command, false, !ripple); // Insert ripple command now that it's all cleaned up gaps TimelineRippleDeleteGapsAtRegionsCommand *ripple_command = nullptr; @@ -1207,7 +1208,7 @@ void TimelineWidget::NudgeInternal(rational amount) // Nudge selections TimelineWidgetSelections new_sel = GetSelections(); new_sel.ShiftTime(amount); - command->add_child(new TimelineWidget::SetSelectionsCommand(this, new_sel, GetSelections(), true)); + command->add_child(new TimelineWidget::SetSelectionsCommand(this, new_sel, GetSelections())); Core::instance()->undo_stack()->push(command); } @@ -1259,7 +1260,7 @@ void TimelineWidget::MoveToPlayheadInternal(bool out) it.value().shift(track_adj); } } - command->add_child(new SetSelectionsCommand(this, new_sel, GetSelections(), true)); + command->add_child(new SetSelectionsCommand(this, new_sel, GetSelections())); Core::instance()->undo_stack()->push(command); } diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 6515b8369..f746b545b 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -122,7 +122,7 @@ public: void RestoreSplitterState(const QByteArray& state); - static void ReplaceBlocksWithGaps(const QVector &blocks, bool remove_from_graph, MultiUndoCommand *command, bool handle_transitions = true); + static void ReplaceBlocksWithGaps(const QVector &blocks, bool remove_from_graph, MultiUndoCommand *command, bool handle_transitions = true, bool handle_invalidations = true); /** * @brief Retrieve the QGraphicsItem at a particular scene position @@ -230,7 +230,7 @@ public: class SetSelectionsCommand : public UndoCommand { public: - SetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old, bool process_block_changes) : + SetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old, bool process_block_changes = true) : timeline_(timeline), old_(old), now_(now), diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 171bdbee9..5e3161323 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -572,7 +572,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) } else { new_sel.TrimOut(reference_ghost->GetOutAdjustment()); } - command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), true)); + command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections())); } } @@ -621,7 +621,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) TimelineWidgetSelections new_sel = parent()->GetSelections(); new_sel.ShiftTime(blocks_moving.first().ghost->GetInAdjustment()); new_sel.ShiftTracks(drag_track_type_, blocks_moving.first().ghost->GetTrackAdjustment()); - command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), true)); + command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections())); } if (!blocks_sliding.isEmpty()) { @@ -682,7 +682,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) // Adjust selections TimelineWidgetSelections new_sel = parent()->GetSelections(); new_sel.ShiftTime(movement); - command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), true)); + command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections())); } } diff --git a/app/widget/timelinewidget/undo/timelineundogeneral.cpp b/app/widget/timelinewidget/undo/timelineundogeneral.cpp index 659b276b2..59e77a35d 100644 --- a/app/widget/timelinewidget/undo/timelineundogeneral.cpp +++ b/app/widget/timelinewidget/undo/timelineundogeneral.cpp @@ -490,7 +490,9 @@ void TrackReplaceBlockWithGapCommand::redo() track_->EndOperation(); - track_->Node::InvalidateCache(invalidate_range, Track::kBlockInput); + if (handle_invalidations_) { + track_->Node::InvalidateCache(invalidate_range, Track::kBlockInput); + } } else { // Block is at the end of the track, simply remove it @@ -547,7 +549,9 @@ void TrackReplaceBlockWithGapCommand::undo() track_->EndOperation(); - track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput); + if (handle_invalidations_) { + 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 diff --git a/app/widget/timelinewidget/undo/timelineundogeneral.h b/app/widget/timelinewidget/undo/timelineundogeneral.h index b8670a063..40396f549 100644 --- a/app/widget/timelinewidget/undo/timelineundogeneral.h +++ b/app/widget/timelinewidget/undo/timelineundogeneral.h @@ -233,13 +233,14 @@ private: class TrackReplaceBlockWithGapCommand : public UndoCommand { public: - TrackReplaceBlockWithGapCommand(Track* track, Block* block, bool handle_transitions = true) : + TrackReplaceBlockWithGapCommand(Track* track, Block* block, bool handle_transitions = true, bool handle_invalidations = true) : track_(track), block_(block), existing_gap_(nullptr), existing_merged_gap_(nullptr), our_gap_(nullptr), - handle_transitions_(handle_transitions) + handle_transitions_(handle_transitions), + handle_invalidations_(handle_invalidations) { } @@ -265,6 +266,7 @@ private: GapBlock* our_gap_; bool handle_transitions_; + bool handle_invalidations_; QObject memory_manager_; @@ -360,6 +362,62 @@ private: }; +class NodeBeginOperationCommand : public UndoCommand +{ +public: + NodeBeginOperationCommand(Node *node) : + node_(node) + {} + + virtual Project* GetRelevantProject() const override + { + return node_->project(); + } + +protected: + virtual void redo() override + { + node_->BeginOperation(); + } + + virtual void undo() override + { + node_->EndOperation(); + } + +private: + Node *node_; + +}; + +class NodeEndOperationCommand : public UndoCommand +{ +public: + NodeEndOperationCommand(Node *node) : + node_(node) + {} + + virtual Project* GetRelevantProject() const override + { + return node_->project(); + } + +protected: + virtual void redo() override + { + node_->EndOperation(); + } + + virtual void undo() override + { + node_->BeginOperation(); + } + +private: + Node *node_; + +}; + } #endif // TIMELINEUNDOGENERAL_H diff --git a/app/widget/timelinewidget/undo/timelineundoripple.cpp b/app/widget/timelinewidget/undo/timelineundoripple.cpp index acbbe083e..ef4c890ae 100644 --- a/app/widget/timelinewidget/undo/timelineundoripple.cpp +++ b/app/widget/timelinewidget/undo/timelineundoripple.cpp @@ -488,26 +488,36 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() } max_gaps = qMax(max_gaps, gaps_on_track.size()); + } else { + qWarning() << "Failed to find corresponding gap to region"; } } + // For each gap on each track, find a corresponding gap on every other track (which may include + // a requested gap) to ripple in order to keep everything synchronized + QHash gap_lengths; for (int gap_index=0; gap_index &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()); + latest_point = qMax(latest_point, gap.range.out()); } } // Determine which gaps will be involved in this operation QVector gaps; + bool all_tracks_unlocked = true; + foreach (Track* track, timeline_->GetTracks()) { if (track->IsLocked()) { + all_tracks_unlocked = false; continue; } @@ -548,7 +558,12 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() if (gap) { gaps.append(gap); - ripple_length = qMin(ripple_length, gap->length()); + + if (!gap_lengths.contains(gap)) { + gap_lengths.insert(gap, gap->length()); + } + + ripple_length = qMin(ripple_length, gap_lengths.value(gap)); } if (ripple_length == 0) { @@ -558,11 +573,24 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() if (ripple_length > 0) { foreach (GapBlock *gap, gaps) { - if (gap->length() == ripple_length) { + if (all_tracks_unlocked) { + commands_.append(new NodeBeginOperationCommand(gap->track())); + } + + if (gap_lengths.value(gap) == ripple_length) { commands_.append(new TrackRippleRemoveBlockCommand(gap->track(), gap)); } else { - commands_.append(new BlockResizeCommand(gap, gap->length() - ripple_length)); + gap_lengths[gap] -= ripple_length; + commands_.append(new BlockResizeCommand(gap, gap_lengths.value(gap))); } + + if (all_tracks_unlocked) { + commands_.append(new NodeEndOperationCommand(gap->track())); + } + } + + if (all_tracks_unlocked) { + commands_.append(new TimelineShiftCacheCommand(timeline_, latest_point, latest_point - ripple_length)); } } } @@ -582,4 +610,14 @@ void TimelineRippleDeleteGapsAtRegionsCommand::undo() } } +void TimelineShiftCacheCommand::redo() +{ + timeline_->ShiftCache(from_, to_); +} + +void TimelineShiftCacheCommand::undo() +{ + timeline_->ShiftCache(to_, from_); +} + } diff --git a/app/widget/timelinewidget/undo/timelineundoripple.h b/app/widget/timelinewidget/undo/timelineundoripple.h index 8c15222fc..17556f935 100644 --- a/app/widget/timelinewidget/undo/timelineundoripple.h +++ b/app/widget/timelinewidget/undo/timelineundoripple.h @@ -38,7 +38,8 @@ namespace olive { * By default, nothing takes this area meaning all subsequent clips are pushed backward, however you can specify * a block to insert at the `in` point. No checking is done to ensure `insert` is the same length as `in` to `out`. */ -class TrackRippleRemoveAreaCommand : public UndoCommand { +class TrackRippleRemoveAreaCommand : public UndoCommand +{ public: TrackRippleRemoveAreaCommand(Track* track, const TimeRange& range); @@ -98,7 +99,8 @@ private: }; -class TrackListRippleRemoveAreaCommand : public UndoCommand { +class TrackListRippleRemoveAreaCommand : public UndoCommand +{ public: TrackListRippleRemoveAreaCommand(TrackList* list, rational in, rational out) : list_(list), @@ -134,7 +136,8 @@ private: }; -class TimelineRippleRemoveAreaCommand : public MultiUndoCommand { +class TimelineRippleRemoveAreaCommand : public MultiUndoCommand +{ public: TimelineRippleRemoveAreaCommand(Sequence* timeline, rational in, rational out); @@ -148,7 +151,8 @@ private: }; -class TrackListRippleToolCommand : public UndoCommand { +class TrackListRippleToolCommand : public UndoCommand +{ public: struct RippleInfo { Block* block; @@ -200,7 +204,8 @@ private: }; -class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand { +class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand +{ public: TimelineRippleDeleteGapsAtRegionsCommand(Sequence* vo, const QVector >& regions) : timeline_(vo), @@ -243,6 +248,34 @@ private: }; +class TimelineShiftCacheCommand : public UndoCommand +{ +public: + TimelineShiftCacheCommand(Sequence* timeline, const rational &from, const rational &to) : + timeline_(timeline), + from_(from), + to_(to) + {} + + virtual Project* GetRelevantProject() const override + { + return timeline_->project(); + } + +protected: + virtual void redo() override; + + virtual void undo() override; + +private: + Sequence* timeline_; + + rational from_; + + rational to_; + +}; + } #endif // TIMELINEUNDORIPPLE_H