Files
oak-editor/app/widget/timelinewidget/undo/undo.cpp
T

442 lines
11 KiB
C++

/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "undo.h"
#include "node/graph.h"
#include <QDebug>
Block* CreateSplitBlock(Block* block, rational point, QObject* parent = nullptr)
{
Block* copy = block->copy();
copy->set_length_and_media_in(block->length() - (point - block->in()));
copy->setParent(parent);
return copy;
}
Node* TakeNodeFromParentGraph(Node* n, QObject* new_parent = nullptr) {
static_cast<NodeGraph*>(n->parent())->TakeNode(n, new_parent);
return n;
}
BlockResizeCommand::BlockResizeCommand(Block *block, rational new_length, QUndoCommand* parent) :
QUndoCommand(parent),
block_(block),
old_length_(block->length()),
new_length_(new_length)
{
}
void BlockResizeCommand::redo()
{
block_->set_length(new_length_);
}
void BlockResizeCommand::undo()
{
block_->set_length(old_length_);
}
BlockResizeWithMediaInCommand::BlockResizeWithMediaInCommand(Block *block, rational new_length, QUndoCommand *parent) :
QUndoCommand(parent),
block_(block),
old_length_(block->length()),
new_length_(new_length)
{
}
void BlockResizeWithMediaInCommand::redo()
{
block_->set_length_and_media_in(new_length_);
}
void BlockResizeWithMediaInCommand::undo()
{
block_->set_length_and_media_in(old_length_);
}
BlockSetMediaInCommand::BlockSetMediaInCommand(Block *block, rational new_media_in, QUndoCommand* parent) :
QUndoCommand(parent),
block_(block),
old_media_in_(block->media_in()),
new_media_in_(new_media_in)
{
}
void BlockSetMediaInCommand::redo()
{
block_->set_media_in(new_media_in_);
}
void BlockSetMediaInCommand::undo()
{
block_->set_media_in(old_media_in_);
}
TrackRippleRemoveBlockCommand::TrackRippleRemoveBlockCommand(TrackOutput *track, Block *block, QUndoCommand *parent) :
QUndoCommand(parent),
track_(track),
block_(block),
before_(block->previous()),
after_(block->next())
{
}
void TrackRippleRemoveBlockCommand::redo()
{
track_->RippleRemoveBlock(block_);
}
void TrackRippleRemoveBlockCommand::undo()
{
track_->InsertBlockBetweenBlocks(block_, before_, after_);
}
TrackInsertBlockBetweenBlocksCommand::TrackInsertBlockBetweenBlocksCommand(TrackOutput *track,
Block *block,
Block *before,
Block *after,
QUndoCommand *parent) :
QUndoCommand(parent),
track_(track),
block_(block),
before_(before),
after_(after)
{
}
void TrackInsertBlockBetweenBlocksCommand::redo()
{
track_->InsertBlockBetweenBlocks(block_, before_, after_);
}
void TrackInsertBlockBetweenBlocksCommand::undo()
{
track_->RippleRemoveBlock(block_);
}
TrackRippleRemoveAreaCommand::TrackRippleRemoveAreaCommand(TrackOutput *track, rational in, rational out, QUndoCommand *parent) :
QUndoCommand(parent),
track_(track),
in_(in),
out_(out),
splice_(nullptr),
trim_out_(nullptr),
trim_in_(nullptr),
insert_(nullptr)
{
}
void TrackRippleRemoveAreaCommand::SetInsert(Block *insert)
{
insert_ = insert;
}
void TrackRippleRemoveAreaCommand::redo()
{
// Iterate through blocks determining which need trimming/removing/splitting
foreach (Block* block, track_->Blocks()) {
if (block->in() < in_ && block->out() > out_) {
// The area entirely within this Block
splice_ = block;
// We don't need to do anything else here
break;
} else if (block->in() >= in_ && block->out() <= out_) {
// This Block's is entirely within the area
removed_blocks_.append(block);
} else if (block->in() < in_ && block->out() >= in_) {
// This Block's out point exceeds `in`
trim_out_ = block;
} else if (block->in() <= out_ && block->out() > out_) {
// This Block's in point exceeds `out`
trim_in_ = block;
}
}
track_->BlockInvalidateCache();
// If we picked up a block to splice
if (splice_ != nullptr) {
// Split the block here
Block* copy = CreateSplitBlock(splice_, out_);
splice_original_length_ = splice_->length();
splice_->set_length(out_ - splice_->in());
track_->AddBlockToGraph(copy);
Node::CopyInputs(splice_, copy);
track_->InsertBlockAfter(copy, splice_);
// Perform all further actions as if we were just trimming these clips
trim_out_ = splice_;
trim_in_ = copy;
}
// If we picked up a block to trim the in point of
if (trim_in_ != nullptr) {
trim_in_old_length_ = trim_in_->length();
trim_in_new_length_ = trim_in_->out() - out_;
}
// If we picked up a block to trim the out point of
if (trim_out_ != nullptr) {
trim_out_old_length_ = trim_out_->length();
trim_out_new_length_ = in_ - trim_out_->in();
}
// If we picked up a block to trim the in point of
if (trim_in_old_length_ != trim_in_new_length_) {
trim_in_->set_length_and_media_in(trim_in_new_length_);
}
// Remove all blocks that are flagged for removal
foreach (Block* remove_block, removed_blocks_) {
track_->RippleRemoveBlock(remove_block);
}
// If we picked up a block to trim the out point of
if (trim_out_old_length_ != trim_out_new_length_) {
trim_out_->set_length(trim_out_new_length_);
}
// If we were given a block to insert, insert it here
if (insert_ != nullptr) {
track_->AddBlockToGraph(insert_);
if (trim_out_ == nullptr) {
// This is the start of the Sequence
track_->PrependBlock(insert_);
} else if (trim_in_ == nullptr) {
// This is the end of the Sequence
track_->AppendBlock(insert_);
} else {
// This is somewhere in the middle of the Sequence
track_->InsertBlockBetweenBlocks(insert_, trim_out_, trim_in_);
}
}
track_->UnblockInvalidateCache();
track_->InvalidateCache(in_, out_);
}
void TrackRippleRemoveAreaCommand::undo()
{
track_->BlockInvalidateCache();
// If we were given a block to insert, insert it here
if (insert_ != nullptr) {
track_->RippleRemoveBlock(insert_);
}
// If we picked up a block to trim the out point of
if (trim_out_old_length_ != trim_out_new_length_) {
trim_out_->set_length(trim_out_old_length_);
}
// Remove all blocks that are flagged for removal
foreach (Block* remove_block, removed_blocks_) {
if (trim_in_ == nullptr) {
track_->AppendBlock(remove_block);
} else {
track_->InsertBlockBefore(remove_block, trim_in_);
}
}
// If we picked up a block to trim the in point of
if (trim_in_old_length_ != trim_in_new_length_) {
trim_in_->set_length_and_media_in(trim_in_old_length_);
}
// If we're splicing, trim_in_ is a copy
if (splice_ != nullptr) {
track_->RippleRemoveBlock(trim_in_);
// Remove node
TakeNodeFromParentGraph(trim_in_, &memory_manager_);
splice_->set_length(splice_original_length_);
}
track_->UnblockInvalidateCache();
track_->InvalidateCache(in_, out_);
}
TrackPlaceBlockCommand::TrackPlaceBlockCommand(TrackList *timeline, int track, Block *block, rational in, QUndoCommand *parent) :
TrackRippleRemoveAreaCommand(nullptr, in, 0, parent), // Out gets set correctly in redo()
timeline_(timeline),
track_index_(track),
gap_(nullptr)
{
insert_ = block;
insert_->setParent(&memory_manager_);
}
void TrackPlaceBlockCommand::redo()
{
added_track_count_ = 0;
// Get track (or make it if necessary)
while (track_index_ >= timeline_->Tracks().size()) {
timeline_->AddTrack();
added_track_count_++;
}
track_ = timeline_->TrackAt(track_index_);
append_ = (in_ >= track_->in());
// Check if the placement location is past the end of the timeline
if (append_) {
if (in_ > track_->in()) {
// If so, insert a gap here
gap_ = new GapBlock();
gap_->set_length(in_ - track_->in());
track_->AppendBlock(gap_);
}
track_->AppendBlock(insert_);
} else {
out_ = in_ + insert_->length();
// Place the Block at this point
TrackRippleRemoveAreaCommand::redo();
}
}
void TrackPlaceBlockCommand::undo()
{
if (append_) {
track_->RippleRemoveBlock(insert_);
TakeNodeFromParentGraph(insert_, &memory_manager_);
if (gap_ != nullptr) {
track_->RippleRemoveBlock(gap_);
delete TakeNodeFromParentGraph(gap_);
}
} else {
TrackRippleRemoveAreaCommand::undo();
}
for (;added_track_count_>0;added_track_count_--) {
timeline_->RemoveTrack();
}
}
BlockSplitCommand::BlockSplitCommand(TrackOutput* track, Block *block, rational point, QUndoCommand *parent) :
QUndoCommand(parent),
track_(track),
block_(block),
new_length_(point - block->in()),
old_length_(block->length())
{
Q_ASSERT(point > block_->in() && point < block_->out());
// Ensures that this block is deleted if this action is undone
new_block_ = CreateSplitBlock(block_, point, &memory_manager_);
}
void BlockSplitCommand::redo()
{
track_->BlockInvalidateCache();
block_->set_length(new_length_);
track_->AddBlockToGraph(new_block_);
Node::CopyInputs(block_, new_block_);
// Will re-parent new_block_ to the track's graph
track_->InsertBlockAfter(new_block_, block_);
track_->UnblockInvalidateCache();
}
void BlockSplitCommand::undo()
{
track_->BlockInvalidateCache();
block_->set_length(old_length_);
track_->RippleRemoveBlock(new_block_);
TakeNodeFromParentGraph(new_block_, &memory_manager_);
track_->UnblockInvalidateCache();
}
TrackSplitAtTimeCommand::TrackSplitAtTimeCommand(TrackOutput *track, rational point, QUndoCommand *parent) :
QUndoCommand(parent)
{
// Find Block that contains this time
for (int i=0;i<track->Blocks().size();i++) {
Block* b = track->Blocks().at(i);
if (b->out() == point) {
// This time is between blocks, no split needs to occur
return;
} else if (b->in() < point && b->out() > point) {
// We found the Block, split it
new BlockSplitCommand(track, b, point, this);
return;
}
}
}
TrackReplaceBlockCommand::TrackReplaceBlockCommand(TrackOutput* track, Block *old, Block *replace, QUndoCommand *parent) :
QUndoCommand(parent),
track_(track),
old_(old),
replace_(replace)
{
}
void TrackReplaceBlockCommand::redo()
{
track_->ReplaceBlock(old_, replace_);
}
void TrackReplaceBlockCommand::undo()
{
track_->ReplaceBlock(replace_, old_);
}
TrackPrependBlockCommand::TrackPrependBlockCommand(TrackOutput *track, Block *block, QUndoCommand *parent) :
QUndoCommand(parent),
track_(track),
block_(block)
{
}
void TrackPrependBlockCommand::redo()
{
track_->PrependBlock(block_);
}
void TrackPrependBlockCommand::undo()
{
track_->RippleRemoveBlock(block_);
}