timeline: rewrote ripple deleting into an undoable command that takes place
after the gaps are made and cleaned Results in more reliable ripple delete behavior. Also fixes bug that would cause gaps that were cleaned and then restored in an undo to crash when selected.
This commit is contained in:
@@ -338,89 +338,8 @@ void TimelineWidget::SplitAtPlayhead()
|
||||
void TimelineWidget::DeleteSelectedInternal(QList<Block *> blocks,
|
||||
bool transition_aware,
|
||||
bool remove_from_graph,
|
||||
bool ripple,
|
||||
QUndoCommand *command)
|
||||
{
|
||||
if (ripple) {
|
||||
for (int i=0;i<blocks.size();i++) {
|
||||
Block* b = blocks.at(i);
|
||||
TrackOutput* b_track = TrackOutput::TrackFromBlock(b);
|
||||
|
||||
// See if we can ripple the length of this block from all tracks
|
||||
rational max_ripple_length = b->length();
|
||||
|
||||
QList<Block*> blocks_at_time;
|
||||
|
||||
foreach (TrackOutput* track, GetConnectedNode()->Tracks()) {
|
||||
|
||||
// Ignore our track since we've already taking account of the block at this time on our track
|
||||
if (track == b_track) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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(b->in());
|
||||
|
||||
// If we found a block, see what it is
|
||||
if (block_at_time) {
|
||||
|
||||
// If it's a gap, or we're deleting it (which means it will soon become a gap), it's viable for removing
|
||||
// or resizing
|
||||
if (block_at_time->type() == Block::kGap || blocks.contains(block_at_time)) {
|
||||
|
||||
// In an effort to keep all tracks synchronized, we can only ripple a maximum of the smallest gap we find
|
||||
max_ripple_length = qMin(max_ripple_length, block_at_time->length());
|
||||
|
||||
} else {
|
||||
|
||||
// If there is no gap here, we cannot ripple at all and must abort this ripple
|
||||
max_ripple_length = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
blocks_at_time.append(block_at_time);
|
||||
}
|
||||
}
|
||||
|
||||
// If we can ripple all the tracks
|
||||
if (max_ripple_length > 0) {
|
||||
|
||||
// Ripple everything including the main block
|
||||
blocks_at_time.append(b);
|
||||
|
||||
foreach (Block* resize, blocks_at_time) {
|
||||
|
||||
// If we can remove this whole block, remove the whole block
|
||||
if (resize->length() == max_ripple_length) {
|
||||
new TrackRippleRemoveBlockCommand(TrackOutput::TrackFromBlock(resize), resize, command);
|
||||
|
||||
// Also remove this block from our block list so we don't bother replacing it with a gap later
|
||||
int resize_index = blocks.indexOf(resize);
|
||||
|
||||
if (resize_index >= 0) {
|
||||
blocks.removeOne(resize);
|
||||
|
||||
// Ensure our iteration remain correct after removing blocks
|
||||
if (resize_index <= i) {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Otherwise, we'll simply shorten the gap/clip
|
||||
BlockResizeCommand* brc = new BlockResizeCommand(resize, resize->length() - max_ripple_length, command);
|
||||
|
||||
// Perform the resize NOW so that if it's a clip that we're replacing with a gap later, the gap will have
|
||||
// the correct length
|
||||
brc->redo();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Block* b, blocks) {
|
||||
TrackOutput* original_track = TrackOutput::TrackFromBlock(b);
|
||||
|
||||
@@ -490,7 +409,7 @@ void TimelineWidget::DeleteSelected(bool ripple)
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
// Replace blocks with gaps (effectively deleting them)
|
||||
DeleteSelectedInternal(blocks_to_delete, true, true, ripple, command);
|
||||
DeleteSelectedInternal(blocks_to_delete, true, true, command);
|
||||
|
||||
// Clean each track
|
||||
foreach (const TrackReference& track, tracks_affected) {
|
||||
@@ -499,6 +418,17 @@ void TimelineWidget::DeleteSelected(bool ripple)
|
||||
command);
|
||||
}
|
||||
|
||||
// Insert ripple command now that it's all cleaned up gaps
|
||||
if (ripple) {
|
||||
TimeRangeList range_list;
|
||||
|
||||
foreach (Block* b, blocks_to_delete) {
|
||||
range_list.InsertTimeRange(TimeRange(b->in(), b->out()));
|
||||
}
|
||||
|
||||
new TimelineRippleDeleteGapsAtRegions(GetConnectedNode(), range_list, command);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
|
||||
@@ -349,7 +349,7 @@ private:
|
||||
bool dual_transition_;
|
||||
};
|
||||
|
||||
void DeleteSelectedInternal(QList<Block *> blocks, bool transition_aware, bool remove_from_graph, bool ripple, QUndoCommand* command);
|
||||
void DeleteSelectedInternal(QList<Block *> blocks, bool transition_aware, bool remove_from_graph, QUndoCommand* command);
|
||||
|
||||
void SetBlockLinksSelected(Block *block, bool selected);
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ void TimelineWidget::PointerTool::MouseReleaseInternal(TimelineViewMouseEvent *e
|
||||
bool inserting = (event->GetModifiers() & Qt::ControlModifier);
|
||||
|
||||
// If there are any blocks to remove, remove them
|
||||
parent()->DeleteSelectedInternal(blocks_to_temp_remove, false, false, inserting, command);
|
||||
parent()->DeleteSelectedInternal(blocks_to_temp_remove, false, false, command);
|
||||
|
||||
if (inserting) {
|
||||
// Make room to insert clips to
|
||||
|
||||
@@ -547,7 +547,7 @@ void TrackCleanGapsCommand::redo_internal()
|
||||
rational new_gap_length = on_gap->length();
|
||||
foreach (GapBlock* gap, consecutive_gaps) {
|
||||
track->RippleRemoveBlock(gap);
|
||||
static_cast<NodeGraph*>(gap->parent())->TakeNode(gap, &memory_manager_);
|
||||
static_cast<NodeGraph*>(track->parent())->TakeNode(gap, &memory_manager_);
|
||||
|
||||
new_gap_length += gap->length();
|
||||
}
|
||||
@@ -568,7 +568,7 @@ void TrackCleanGapsCommand::redo_internal()
|
||||
|
||||
foreach (GapBlock* gap, removed_end_gaps_) {
|
||||
track->RippleRemoveBlock(gap);
|
||||
static_cast<NodeGraph*>(gap->parent())->TakeNode(gap, &memory_manager_);
|
||||
static_cast<NodeGraph*>(track->parent())->TakeNode(gap, &memory_manager_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,7 +579,7 @@ void TrackCleanGapsCommand::undo_internal()
|
||||
|
||||
// Restored removed end gaps
|
||||
foreach (GapBlock* gap, removed_end_gaps_) {
|
||||
static_cast<NodeGraph*>(gap->parent())->AddNode(gap);
|
||||
static_cast<NodeGraph*>(track->parent())->AddNode(gap);
|
||||
track->AppendBlock(gap);
|
||||
}
|
||||
removed_end_gaps_.clear();
|
||||
@@ -594,7 +594,7 @@ void TrackCleanGapsCommand::undo_internal()
|
||||
GapBlock* last_gap_added = merge_info.merged;
|
||||
|
||||
foreach (GapBlock* gap, merge_info.removed) {
|
||||
static_cast<NodeGraph*>(gap->parent())->AddNode(gap);
|
||||
static_cast<NodeGraph*>(track->parent())->AddNode(gap);
|
||||
track->InsertBlockAfter(gap, last_gap_added);
|
||||
last_gap_added = gap;
|
||||
}
|
||||
@@ -622,3 +622,60 @@ void BlockSetSpeedCommand::undo_internal()
|
||||
{
|
||||
block_->set_speed(old_speed_);
|
||||
}
|
||||
|
||||
TimelineRippleDeleteGapsAtRegions::TimelineRippleDeleteGapsAtRegions(ViewerOutput *vo, const TimeRangeList ®ions, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
timeline_(vo),
|
||||
regions_(regions)
|
||||
{
|
||||
}
|
||||
|
||||
void TimelineRippleDeleteGapsAtRegions::redo_internal()
|
||||
{
|
||||
foreach (const TimeRange& range, regions_) {
|
||||
rational max_ripple_length = range.length();
|
||||
|
||||
QList<Block*> blocks_around_range;
|
||||
|
||||
foreach (TrackOutput* track, timeline_->Tracks()) {
|
||||
// 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());
|
||||
|
||||
if (block_at_time) {
|
||||
if (block_at_time->type() == Block::kGap) {
|
||||
max_ripple_length = qMin(block_at_time->length(), max_ripple_length);
|
||||
} else {
|
||||
max_ripple_length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
blocks_around_range.append(block_at_time);
|
||||
}
|
||||
}
|
||||
|
||||
if (max_ripple_length > 0) {
|
||||
foreach (Block* resize, blocks_around_range) {
|
||||
if (resize->length() == max_ripple_length) {
|
||||
// Remove block entirely
|
||||
TrackRippleRemoveBlockCommand* remove_command = new TrackRippleRemoveBlockCommand(TrackOutput::TrackFromBlock(resize), resize);
|
||||
remove_command->redo();
|
||||
commands_.append(remove_command);
|
||||
} else {
|
||||
// Resize block
|
||||
BlockResizeCommand* resize_command = new BlockResizeCommand(resize, resize->length() - max_ripple_length);
|
||||
resize_command->redo();
|
||||
commands_.append(resize_command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineRippleDeleteGapsAtRegions::undo_internal()
|
||||
{
|
||||
for (int i=commands_.size()-1;i>=0;i--) {
|
||||
commands_.at(i)->undo();
|
||||
delete commands_.at(i);
|
||||
}
|
||||
commands_.empty();
|
||||
}
|
||||
|
||||
@@ -280,4 +280,20 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class TimelineRippleDeleteGapsAtRegions : public UndoCommand {
|
||||
public:
|
||||
TimelineRippleDeleteGapsAtRegions(ViewerOutput* vo, const TimeRangeList& regions, QUndoCommand* parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void redo_internal() override;
|
||||
virtual void undo_internal() override;
|
||||
|
||||
private:
|
||||
ViewerOutput* timeline_;
|
||||
TimeRangeList regions_;
|
||||
|
||||
QList<UndoCommand*> commands_;
|
||||
|
||||
};
|
||||
|
||||
#endif // TIMELINEUNDOABLE_H
|
||||
|
||||
Reference in New Issue
Block a user