Previous iteration used some "magic code" that added clips automatically to the timeline. This was functional but ultimately outside of the undo commands' control meaning nodes could be infinitely added and abandoned. This makes the add process part of the undo command which means it's all undoable as the user would expect.
153 lines
5.3 KiB
C++
153 lines
5.3 KiB
C++
#include "widget/timelinewidget/timelinewidget.h"
|
|
|
|
#include "node/block/transition/crossdissolve/crossdissolve.h"
|
|
#include "node/block/transition/diptoblack/diptoblack.h"
|
|
#include "widget/nodeview/nodeviewundo.h"
|
|
|
|
TimelineWidget::TransitionTool::TransitionTool(TimelineWidget *parent) :
|
|
AddTool(parent)
|
|
{
|
|
}
|
|
|
|
void TimelineWidget::TransitionTool::MousePress(TimelineViewMouseEvent *event)
|
|
{
|
|
const TrackReference& track = event->GetCoordinates().GetTrack();
|
|
TrackOutput* t = parent()->GetTrackFromReference(track);
|
|
rational cursor_frame = event->GetCoordinates().GetFrame();
|
|
|
|
if (!t || t->IsLocked()) {
|
|
return;
|
|
}
|
|
|
|
Block* block_at_time = t->BlockAtTime(event->GetCoordinates().GetFrame());
|
|
if (!block_at_time || block_at_time->type() != Block::kClip) {
|
|
return;
|
|
}
|
|
|
|
// Determine which side of the clip the transition belongs to
|
|
rational transition_start_point;
|
|
olive::timeline::MovementMode trim_mode;
|
|
rational halfway_point = block_at_time->in() + block_at_time->length() / 2;
|
|
rational tenth_point = block_at_time->in() + block_at_time->length() / 10;
|
|
Block* other_block = nullptr;
|
|
if (cursor_frame < halfway_point) {
|
|
transition_start_point = block_at_time->in();
|
|
trim_mode = olive::timeline::kTrimIn;
|
|
|
|
if (cursor_frame < tenth_point
|
|
&& block_at_time->previous()
|
|
&& block_at_time->previous()->type() == Block::kClip) {
|
|
other_block = block_at_time->previous();
|
|
}
|
|
} else {
|
|
transition_start_point = block_at_time->out();
|
|
trim_mode = olive::timeline::kTrimOut;
|
|
dual_transition_ = (cursor_frame > block_at_time->length() - tenth_point);
|
|
|
|
if (cursor_frame > block_at_time->length() - tenth_point
|
|
&& block_at_time->next()
|
|
&& block_at_time->next()->type() == Block::kClip) {
|
|
other_block = block_at_time->next();
|
|
}
|
|
}
|
|
|
|
// Create ghost
|
|
ghost_ = new TimelineViewGhostItem();
|
|
ghost_->SetTrack(track);
|
|
ghost_->SetYCoords(parent()->GetTrackY(track), parent()->GetTrackHeight(track));
|
|
ghost_->SetIn(transition_start_point);
|
|
ghost_->SetOut(transition_start_point);
|
|
ghost_->SetMode(trim_mode);
|
|
ghost_->setData(TimelineViewGhostItem::kAttachedBlock, Node::PtrToValue(block_at_time));
|
|
|
|
dual_transition_ = (other_block);
|
|
if (other_block)
|
|
ghost_->setData(TimelineViewGhostItem::kReferenceBlock, Node::PtrToValue(other_block));
|
|
|
|
parent()->AddGhost(ghost_);
|
|
|
|
snap_points_.append(transition_start_point);
|
|
|
|
// Set the drag start point
|
|
drag_start_point_ = cursor_frame;
|
|
}
|
|
|
|
void TimelineWidget::TransitionTool::MouseMove(TimelineViewMouseEvent *event)
|
|
{
|
|
if (!ghost_) {
|
|
return;
|
|
}
|
|
|
|
MouseMoveInternal(event->GetCoordinates().GetFrame(), dual_transition_);
|
|
}
|
|
|
|
void TimelineWidget::TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
|
{
|
|
MouseMove(event);
|
|
|
|
const TrackReference& track = ghost_->Track();
|
|
|
|
if (ghost_) {
|
|
if (!ghost_->AdjustedLength().isNull()) {
|
|
CrossDissolveTransition* transition = new CrossDissolveTransition();
|
|
|
|
QUndoCommand* command = new QUndoCommand();
|
|
|
|
// Place transition in place
|
|
new NodeAddCommand(static_cast<NodeGraph*>(parent()->timeline_node_->parent()),
|
|
transition,
|
|
command);
|
|
new TrackPlaceBlockCommand(parent()->timeline_node_->track_list(track.type()),
|
|
track.index(),
|
|
transition,
|
|
ghost_->GetAdjustedIn(),
|
|
command);
|
|
|
|
if (dual_transition_) {
|
|
transition->set_in_and_out_offset(ghost_->AdjustedLength()/2, ghost_->AdjustedLength()/2);
|
|
|
|
// Block mouse is hovering over
|
|
Block* active_block = Node::ValueToPtr<Block>(ghost_->data(TimelineViewGhostItem::kAttachedBlock));
|
|
|
|
// Block mouse is next to
|
|
Block* friend_block = Node::ValueToPtr<Block>(ghost_->data(TimelineViewGhostItem::kReferenceBlock));
|
|
|
|
// Use ghost mode to determine which block is which
|
|
Block* out_block = (ghost_->mode() == olive::timeline::kTrimIn) ? friend_block : active_block;
|
|
Block* in_block = (ghost_->mode() == olive::timeline::kTrimIn) ? active_block : friend_block;
|
|
|
|
// Connect block to transition
|
|
new NodeEdgeAddCommand(out_block->output(),
|
|
transition->out_block_input(),
|
|
command);
|
|
|
|
new NodeEdgeAddCommand(in_block->output(),
|
|
transition->in_block_input(),
|
|
command);
|
|
} else {
|
|
Block* block_to_transition = Node::ValueToPtr<Block>(ghost_->data(TimelineViewGhostItem::kAttachedBlock));
|
|
NodeInput* transition_input_to_connect;
|
|
|
|
if (ghost_->mode() == olive::timeline::kTrimIn) {
|
|
transition->set_in_and_out_offset(ghost_->AdjustedLength(), 0);
|
|
transition_input_to_connect = transition->in_block_input();
|
|
} else {
|
|
transition->set_in_and_out_offset(0, ghost_->AdjustedLength());
|
|
transition_input_to_connect = transition->out_block_input();
|
|
}
|
|
|
|
// Connect block to transition
|
|
new NodeEdgeAddCommand(block_to_transition->output(),
|
|
transition_input_to_connect,
|
|
command);
|
|
}
|
|
|
|
olive::undo_stack.push(command);
|
|
}
|
|
|
|
parent()->ClearGhosts();
|
|
snap_points_.clear();
|
|
ghost_ = nullptr;
|
|
}
|
|
}
|