handle cache properly in ripple delete
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -462,7 +462,8 @@ void TimelineWidget::SplitAtPlayhead()
|
||||
void TimelineWidget::ReplaceBlocksWithGaps(const QVector<Block *> &blocks,
|
||||
bool remove_from_graph,
|
||||
MultiUndoCommand *command,
|
||||
bool handle_transitions)
|
||||
bool handle_transitions,
|
||||
bool handle_invalidations)
|
||||
{
|
||||
foreach (Block* b, blocks) {
|
||||
if (dynamic_cast<GapBlock*>(b)) {
|
||||
@@ -473,7 +474,7 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QVector<Block *> &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);
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
|
||||
void RestoreSplitterState(const QByteArray& state);
|
||||
|
||||
static void ReplaceBlocksWithGaps(const QVector<Block *> &blocks, bool remove_from_graph, MultiUndoCommand *command, bool handle_transitions = true);
|
||||
static void ReplaceBlocksWithGaps(const QVector<Block *> &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),
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<GapBlock*, rational> gap_lengths;
|
||||
for (int gap_index=0; gap_index<max_gaps; gap_index++) {
|
||||
rational earliest_point = RATIONAL_MAX;
|
||||
rational ripple_length = RATIONAL_MAX;
|
||||
rational latest_point = RATIONAL_MIN;
|
||||
|
||||
foreach (const QVector<RemovalRequest> &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<GapBlock*> 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_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<QPair<Track*, TimeRange> >& 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
|
||||
|
||||
Reference in New Issue
Block a user