diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 200073325..6d16ff2e0 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -38,6 +38,11 @@ Block::Block() : media_in_input_->SetConnectable(false); media_in_input_->set_data_type(NodeParam::kRational); AddInput(media_in_input_); + + media_out_input_ = new NodeInput("media_out_in"); + media_out_input_->SetConnectable(false); + media_out_input_->set_data_type(NodeParam::kRational); + AddInput(media_out_input_); } QString Block::Category() const @@ -81,12 +86,31 @@ void Block::set_length(const rational &length) length_input_->set_value_at_time(0, QVariant::fromValue(length)); } +void Block::set_length_and_media_out(const rational &length) +{ + Q_ASSERT(length > 0); + + if (length == this->length()) { + return; + } + + set_media_out(media_out() + (length - this->length())); + + set_length(length); +} + void Block::set_length_and_media_in(const rational &length) { + Q_ASSERT(length > 0); + + if (length == this->length()) { + return; + } + // Calculate media_in adjustment set_media_in(media_in() + (this->length() - length)); - // Set the length + // Set the length without setting media out set_length(length); } @@ -120,6 +144,21 @@ void Block::set_media_in(const rational &media_in) media_in_input_->set_value_at_time(0, QVariant::fromValue(media_in)); } +rational Block::media_out() const +{ + return media_out_input_->get_value_at_time(0).value(); +} + +void Block::set_media_out(const rational &media_out) +{ + media_out_input_->set_value_at_time(0, QVariant::fromValue(media_out)); +} + +rational Block::media_length() const +{ + return media_out() - media_in(); +} + const QString &Block::block_name() const { return block_name_; @@ -137,7 +176,7 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const return sequence_time; } - return sequence_time - in() + media_in(); + return (sequence_time - in() + media_in()) * media_length() / length(); } rational Block::MediaToSequenceTime(const rational &media_time) const @@ -147,7 +186,7 @@ rational Block::MediaToSequenceTime(const rational &media_time) const return media_time; } - return media_time - media_in() + in(); + return media_time * length() / media_length() - media_in() + in(); } void Block::CopyParameters(const Block *source, Block *dest) @@ -161,7 +200,7 @@ void Block::CopyParameters(const Block *source, Block *dest) dst_t->set_in_and_out_offset(src_t->in_offset(), src_t->out_offset()); } else { - dest->set_length(source->length()); + dest->set_length_and_media_out(source->length()); } } diff --git a/app/node/block/block.h b/app/node/block/block.h index dd010a89f..650f66cff 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -57,6 +57,7 @@ public: rational length() const; virtual void set_length(const rational &length); + virtual void set_length_and_media_out(const rational &length); virtual void set_length_and_media_in(const rational &length); Block* previous(); @@ -67,6 +68,11 @@ public: rational media_in() const; void set_media_in(const rational& media_in); + rational media_out() const; + void set_media_out(const rational& media_out); + + rational media_length() const; + const QString& block_name() const; void set_block_name(const QString& name); @@ -106,6 +112,7 @@ protected: private: NodeInput* length_input_; NodeInput* media_in_input_; + NodeInput* media_out_input_; rational in_point_; rational out_point_; diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index 143c0c0a5..e37e4e353 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -37,7 +37,7 @@ void TransitionBlock::Retranslate() in_block_input_->set_name(tr("To")); } -void TransitionBlock::set_length(const rational &length) +void TransitionBlock::set_length_and_media_out(const rational &length) { Q_UNUSED(length) qCritical() << "Set length is not permitted on a transition"; @@ -70,5 +70,5 @@ void TransitionBlock::set_in_and_out_offset(const rational &in_offset, const rat void TransitionBlock::RecalculateLength() { - Block::set_length(in_offset_ + out_offset_); + Block::set_length_and_media_out(in_offset_ + out_offset_); } diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index 227844633..21bf30c2b 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -16,7 +16,7 @@ public: virtual void Retranslate() override; - virtual void set_length(const rational &length) override; + virtual void set_length_and_media_out(const rational &length) override; virtual void set_length_and_media_in(const rational &length) override; const rational& in_offset() const; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index dbf9bdc20..9aa7e1b5f 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -404,7 +404,7 @@ void TimelineWidget::DeleteSelectedInternal(const QList blocks, bool re } else if (!previous_is_gap && !next_is_gap) { // Make new gap and replace old Block with it for now GapBlock* gap = new GapBlock(); - gap->set_length(b->length()); + gap->set_length_and_media_out(b->length()); new NodeAddCommand(static_cast(b->parent()), gap, @@ -536,7 +536,7 @@ void TimelineWidget::RippleEditTo(olive::timeline::MovementMode mode, bool inser GapBlock* gap = nullptr; if (insert_gaps) { gap = new GapBlock(); - gap->set_length(ripple_length); + gap->set_length_and_media_out(ripple_length); new NodeAddCommand(static_cast(track->parent()), gap, command); } diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index 11cd20bae..f301a0328 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -50,7 +50,7 @@ void TimelineWidget::AddTool::MouseRelease(TimelineViewMouseEvent *event) QUndoCommand* command = new QUndoCommand(); ClipBlock* clip = new ClipBlock(); - clip->set_length(ghost_->AdjustedLength()); + clip->set_length_and_media_out(ghost_->AdjustedLength()); new NodeAddCommand(static_cast(parent()->timeline_node_->parent()), clip, command); diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 1c0d99e5b..5ebe3eb12 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -217,7 +217,7 @@ void TimelineWidget::ImportTool::DragDrop(TimelineViewMouseEvent *event) StreamPtr footage_stream = ghost->data(TimelineViewGhostItem::kAttachedFootage).value(); ClipBlock* clip = new ClipBlock(); - clip->set_length(ghost->Length()); + clip->set_length_and_media_out(ghost->Length()); clip->set_block_name(footage_stream->footage()->name()); new NodeAddCommand(dst_graph, clip, command); diff --git a/app/widget/timelinewidget/tool/ripple.cpp b/app/widget/timelinewidget/tool/ripple.cpp index 9bc3148b7..b5d684c5f 100644 --- a/app/widget/timelinewidget/tool/ripple.cpp +++ b/app/widget/timelinewidget/tool/ripple.cpp @@ -48,7 +48,7 @@ void TimelineWidget::RippleTool::MouseReleaseInternal(TimelineViewMouseEvent *ev // Make sure there's actually a gap being created if (ghost->AdjustedLength() > 0) { GapBlock* gap = new GapBlock(); - gap->set_length(ghost->AdjustedLength()); + gap->set_length_and_media_out(ghost->AdjustedLength()); new NodeAddCommand(static_cast(parent()->timeline_node_->parent()), gap, command); Block* block_to_append_gap_to = Node::ValueToPtr(ghost->data(TimelineViewGhostItem::kReferenceBlock)); diff --git a/app/widget/timelinewidget/tool/rolling.cpp b/app/widget/timelinewidget/tool/rolling.cpp index 1435c0d4f..f87225a8e 100644 --- a/app/widget/timelinewidget/tool/rolling.cpp +++ b/app/widget/timelinewidget/tool/rolling.cpp @@ -43,7 +43,7 @@ void TimelineWidget::RollingTool::MouseReleaseInternal(TimelineViewMouseEvent *e if (b->previous() == nullptr) { // We'll need to insert a gap here, so we'll do a Place command instead GapBlock* gap = new GapBlock(); - gap->set_length(ghost->Length()); + gap->set_length_and_media_out(ghost->Length()); new NodeAddCommand(static_cast(b->parent()), gap, command); diff --git a/app/widget/timelinewidget/tool/slide.cpp b/app/widget/timelinewidget/tool/slide.cpp index cc2b38e62..40f71b515 100644 --- a/app/widget/timelinewidget/tool/slide.cpp +++ b/app/widget/timelinewidget/tool/slide.cpp @@ -46,7 +46,7 @@ void TimelineWidget::SlideTool::MouseReleaseInternal(TimelineViewMouseEvent *eve new BlockResizeCommand(b, ghost->AdjustedLength(), command); } else if (ghost->mode() == olive::timeline::kMove && b->previous() == nullptr) { GapBlock* gap = new GapBlock(); - gap->set_length(ghost->InAdjustment()); + gap->set_length_and_media_out(ghost->InAdjustment()); new NodeAddCommand(static_cast(b->parent()), gap, command); new TrackPrependBlockCommand(parent()->GetTrackFromReference(ghost->Track()), gap, command); } diff --git a/app/widget/timelinewidget/tool/slip.cpp b/app/widget/timelinewidget/tool/slip.cpp index 97c6348ac..1c5d14a6a 100644 --- a/app/widget/timelinewidget/tool/slip.cpp +++ b/app/widget/timelinewidget/tool/slip.cpp @@ -72,6 +72,7 @@ void TimelineWidget::SlipTool::MouseReleaseInternal(TimelineViewMouseEvent *even Block* b = Node::ValueToPtr(ghost->data(TimelineViewGhostItem::kAttachedBlock)); new BlockSetMediaInCommand(b, ghost->GetAdjustedMediaIn(), command); + new BlockSetMediaOutCommand(b, ghost->GetAdjustedMediaIn() + b->media_length(), command); } olive::undo_stack.pushIfHasChildren(command); diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index f0fd5a55a..0d0b973d3 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -49,12 +49,12 @@ BlockResizeCommand::BlockResizeCommand(Block *block, rational new_length, QUndoC void BlockResizeCommand::redo() { - block_->set_length(new_length_); + block_->set_length_and_media_out(new_length_); } void BlockResizeCommand::undo() { - block_->set_length(old_length_); + block_->set_length_and_media_out(old_length_); } BlockResizeWithMediaInCommand::BlockResizeWithMediaInCommand(Block *block, rational new_length, QUndoCommand *parent) : @@ -93,6 +93,24 @@ void BlockSetMediaInCommand::undo() block_->set_media_in(old_media_in_); } +BlockSetMediaOutCommand::BlockSetMediaOutCommand(Block *block, rational new_media_out, QUndoCommand *parent) : + QUndoCommand(parent), + block_(block), + old_media_out_(block->media_out()), + new_media_out_(new_media_out) +{ +} + +void BlockSetMediaOutCommand::redo() +{ + block_->set_media_in(new_media_out_); +} + +void BlockSetMediaOutCommand::undo() +{ + block_->set_media_in(old_media_out_); +} + TrackRippleRemoveBlockCommand::TrackRippleRemoveBlockCommand(TrackOutput *track, Block *block, QUndoCommand *parent) : QUndoCommand(parent), track_(track), @@ -186,7 +204,7 @@ void TrackRippleRemoveAreaCommand::redo() Block* copy = CreateSplitBlock(splice_, out_); splice_original_length_ = splice_->length(); - splice_->set_length(out_ - splice_->in()); + splice_->set_length_and_media_out(out_ - splice_->in()); static_cast(track_->parent())->AddNode(copy); Node::CopyInputs(splice_, copy); @@ -224,7 +242,7 @@ void TrackRippleRemoveAreaCommand::redo() // 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_); + trim_out_->set_length_and_media_out(trim_out_new_length_); } // If we were given a block to insert, insert it here @@ -257,7 +275,7 @@ void TrackRippleRemoveAreaCommand::undo() // 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_); + trim_out_->set_length_and_media_out(trim_out_old_length_); } // Remove all blocks that are flagged for removal @@ -281,7 +299,7 @@ void TrackRippleRemoveAreaCommand::undo() // Remove node TakeNodeFromParentGraph(trim_in_, &memory_manager_); - splice_->set_length(splice_original_length_); + splice_->set_length_and_media_out(splice_original_length_); } track_->UnblockInvalidateCache(); @@ -318,7 +336,7 @@ void TrackPlaceBlockCommand::redo() if (in_ > track_->track_length()) { // If so, insert a gap here gap_ = new GapBlock(); - gap_->set_length(in_ - track_->track_length()); + gap_->set_length_and_media_out(in_ - track_->track_length()); static_cast(track_->parent())->AddNode(gap_); track_->AppendBlock(gap_); } @@ -376,7 +394,7 @@ void BlockSplitCommand::redo() { track_->BlockInvalidateCache(); - block_->set_length(new_length_); + block_->set_length_and_media_out(new_length_); static_cast(block_->parent())->AddNode(new_block_); Node::CopyInputs(block_, new_block_); @@ -396,7 +414,7 @@ void BlockSplitCommand::undo() { track_->BlockInvalidateCache(); - block_->set_length(old_length_); + block_->set_length_and_media_out(old_length_); track_->RippleRemoveBlock(new_block_); TakeNodeFromParentGraph(new_block_, &memory_manager_); diff --git a/app/widget/timelinewidget/undo/undo.h b/app/widget/timelinewidget/undo/undo.h index 2bcf09423..d5fa4b896 100644 --- a/app/widget/timelinewidget/undo/undo.h +++ b/app/widget/timelinewidget/undo/undo.h @@ -67,6 +67,19 @@ private: rational new_media_in_; }; +class BlockSetMediaOutCommand : public QUndoCommand { +public: + BlockSetMediaOutCommand(Block* block, rational new_media_out, QUndoCommand* parent = nullptr); + + virtual void redo() override; + virtual void undo() override; + +private: + Block* block_; + rational old_media_out_; + rational new_media_out_; +}; + class TrackRippleRemoveBlockCommand : public QUndoCommand { public: TrackRippleRemoveBlockCommand(TrackOutput* track, Block* block, QUndoCommand* parent = nullptr);