diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 85485ecd6..ccbff20b8 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -46,6 +46,7 @@ #include "panels/grapheditor.h" #include "ui/viewerwidget.h" #include "io/clipboard.h" +#include "io/config.h" #include "ui/timelineheader.h" #include "ui/keyframeview.h" #include "ui/resizablescrollbar.h" @@ -103,10 +104,18 @@ void EffectControls::menu_select(QAction* q) { const EffectMeta* meta = reinterpret_cast(q->data().value()); if (effect_menu_type == EFFECT_TYPE_TRANSITION) { if (c->opening_transition == nullptr) { - ca->append(new AddTransitionCommand(c, nullptr, nullptr, meta, kTransitionOpening, 30)); + ca->append(new AddTransitionCommand(c, + nullptr, + nullptr, + meta, + olive::CurrentConfig.default_transition_length)); } if (c->closing_transition == nullptr) { - ca->append(new AddTransitionCommand(c, nullptr, nullptr, meta, kTransitionClosing, 30)); + ca->append(new AddTransitionCommand(nullptr, + c, + nullptr, + meta, + olive::CurrentConfig.default_transition_length)); } } else { ca->append(new AddEffectCommand(c, nullptr, meta)); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 6488f94b3..2ded033b8 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -78,8 +78,8 @@ Timeline::Timeline(QWidget *parent) : creating(false), transition_tool_init(false), transition_tool_proc(false), - transition_tool_pre_clip(-1), - transition_tool_post_clip(-1), + transition_tool_open_clip(-1), + transition_tool_close_clip(-1), hand_moving(false), block_repaints(false), scroll(0) @@ -379,11 +379,19 @@ void Timeline::add_transition() { if (c != nullptr && is_clip_selected(c, true)) { int transition_to_add = (c->track < 0) ? TRANSITION_INTERNAL_CROSSDISSOLVE : TRANSITION_INTERNAL_LINEARFADE; if (c->get_opening_transition() == nullptr) { - ca->append(new AddTransitionCommand(c, nullptr, nullptr, get_internal_meta(transition_to_add, EFFECT_TYPE_TRANSITION), kTransitionOpening, 30)); + ca->append(new AddTransitionCommand(c, + nullptr, + nullptr, + get_internal_meta(transition_to_add, EFFECT_TYPE_TRANSITION), + olive::CurrentConfig.default_transition_length)); adding = true; } if (c->get_closing_transition() == nullptr) { - ca->append(new AddTransitionCommand(c, nullptr, nullptr, get_internal_meta(transition_to_add, EFFECT_TYPE_TRANSITION), kTransitionClosing, 30)); + ca->append(new AddTransitionCommand(nullptr, + c, + nullptr, + get_internal_meta(transition_to_add, EFFECT_TYPE_TRANSITION), + olive::CurrentConfig.default_transition_length)); adding = true; } } @@ -2047,16 +2055,30 @@ void move_clip(ComboAction* ca, ClipPtr c, long iin, long iout, long iclip_in, i ca->append(new MoveClipAction(c, iin, iout, iclip_in, itrack, relative)); if (verify_transitions) { - if (c->get_opening_transition() != nullptr && c->get_opening_transition()->secondary_clip != nullptr && c->get_opening_transition()->secondary_clip->timeline_out != iin) { + + // if this is a shared transition, and the corresponding clip will be moved away somehow + if (c->get_opening_transition() != nullptr + && c->get_opening_transition()->secondary_clip != nullptr + && c->get_opening_transition()->secondary_clip->timeline_out != iin) { // separate transition ca->append(new SetPointer(reinterpret_cast(&c->get_opening_transition()->secondary_clip), nullptr)); - ca->append(new AddTransitionCommand(c->get_opening_transition()->secondary_clip, nullptr, c->get_opening_transition(), nullptr, kTransitionClosing, 0)); + ca->append(new AddTransitionCommand(nullptr, + c->get_opening_transition()->secondary_clip, + c->get_opening_transition(), + nullptr, + 0)); } - if (c->get_closing_transition() != nullptr && c->get_closing_transition()->secondary_clip != nullptr && c->get_closing_transition()->parent_clip->timeline_in != iout) { + if (c->get_closing_transition() != nullptr + && c->get_closing_transition()->secondary_clip != nullptr + && c->get_closing_transition()->parent_clip->timeline_in != iout) { // separate transition ca->append(new SetPointer(reinterpret_cast(&c->get_closing_transition()->secondary_clip), nullptr)); - ca->append(new AddTransitionCommand(c, nullptr, c->get_closing_transition(), nullptr, kTransitionClosing, 0)); + ca->append(new AddTransitionCommand(nullptr, + c, + c->get_closing_transition(), + nullptr, + 0)); } } } diff --git a/panels/timeline.h b/panels/timeline.h index 9376d089b..141658298 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -177,9 +177,8 @@ public: // transition variables bool transition_tool_init; bool transition_tool_proc; - int transition_tool_pre_clip; - int transition_tool_post_clip; - int transition_tool_type; + int transition_tool_open_clip; + int transition_tool_close_clip; const EffectMeta* transition_tool_meta; int transition_tool_side; diff --git a/project/undo.cpp b/project/undo.cpp index 7a5c596dc..5a0a998f2 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -220,55 +220,64 @@ void AddEffectCommand::doRedo() { done = true; } -AddTransitionCommand::AddTransitionCommand(ClipPtr c, - ClipPtr s, +AddTransitionCommand::AddTransitionCommand(ClipPtr iopen, + ClipPtr iclose, TransitionPtr copy, const EffectMeta *itransition, - int itype, int ilength) { - primary = c; - secondary = s; - transition_to_copy = copy; + open_ = iopen; + close_ = iclose; + transition_to_copy_ = copy; transition_meta_ = itransition; - type = itype; - length = ilength; + length_ = ilength; + new_transition_ref_ = nullptr; } void AddTransitionCommand::doUndo() { - if (type == kTransitionOpening) { - primary->opening_transition = old_ptransition; - if (secondary != nullptr) secondary->closing_transition = old_stransition; - } else { - primary->closing_transition = old_ptransition; - if (secondary != nullptr) secondary->opening_transition = old_stransition; + if (open_ != nullptr) { + open_->opening_transition = old_open_transition_; + } + + if (close_ != nullptr) { + close_->closing_transition = old_close_transition_; } } void AddTransitionCommand::doRedo() { - // store old transition of primary clip - old_ptransition = primary->opening_transition; - - // create new transition object - TransitionPtr new_transition; - if (transition_to_copy == nullptr) { - new_transition = get_transition_from_meta(primary, secondary, transition_meta_); - } else { - new_transition = transition_to_copy->copy(primary, nullptr); + // convert open/close clips to primary/secondary for transition object + ClipPtr primary = open_; + ClipPtr secondary = close_; + if (primary == nullptr) { + primary = secondary; + secondary = nullptr; } - primary->opening_transition = new_transition; + // create new transition object + if (new_transition_ref_ == nullptr) { + if (transition_to_copy_ == nullptr) { + new_transition_ref_ = get_transition_from_meta(primary, secondary, transition_meta_); + } else { + new_transition_ref_ = transition_to_copy_->copy(primary, nullptr); + } + } - if (secondary != nullptr) { - // store old secondary transition - old_stransition = secondary->closing_transition; + // set opening clip's opening transition to this and store the old one + if (open_ != nullptr) { + old_open_transition_ = open_->opening_transition; - // set secondary transition to the same transition - secondary->closing_transition = new_transition; + open_->opening_transition = new_transition_ref_; + } + + // set closing clip's closing transition to this and store the old one + if (close_ != nullptr) { + old_close_transition_ = close_->closing_transition; + + close_->closing_transition = new_transition_ref_; } // if a length was specified, set it now - if (length > 0) { - new_transition->set_length(length); + if (length_ > 0) { + new_transition_ref_->set_length(length_); } } diff --git a/project/undo.h b/project/undo.h index 830a9973f..0ec1d1005 100644 --- a/project/undo.h +++ b/project/undo.h @@ -138,18 +138,18 @@ private: class AddTransitionCommand : public OliveAction { public: - AddTransitionCommand(ClipPtr c, ClipPtr s, TransitionPtr copy, const EffectMeta* itransition, int itype, int ilength); + AddTransitionCommand(ClipPtr iopen, ClipPtr iclose, TransitionPtr copy, const EffectMeta* itransition, int ilength); virtual void doUndo() override; virtual void doRedo() override; private: - ClipPtr primary; - ClipPtr secondary; - TransitionPtr transition_to_copy; + ClipPtr open_; + ClipPtr close_; + TransitionPtr transition_to_copy_; const EffectMeta* transition_meta_; - int type; - int length; - TransitionPtr old_ptransition; - TransitionPtr old_stransition; + int length_; + TransitionPtr old_open_transition_; + TransitionPtr old_close_transition_; + TransitionPtr new_transition_ref_; }; class ModifyTransitionCommand : public OliveAction { diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index ea7d3bcd1..199b3e396 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -836,7 +836,8 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) { { // if there is a clip to run the transition tool on, initiate the transition tool - if (panel_timeline->transition_tool_pre_clip > -1) { + if (panel_timeline->transition_tool_open_clip > -1 + || panel_timeline->transition_tool_close_clip > -1) { panel_timeline->transition_tool_init = true; } @@ -1167,75 +1168,85 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { long transition_start = qMin(g.in, g.out); long transition_end = qMax(g.in, g.out); + ClipPtr open = (panel_timeline->transition_tool_open_clip > -1) + ? olive::ActiveSequence->clips.at(panel_timeline->transition_tool_open_clip) + : nullptr; - ClipPtr pre = olive::ActiveSequence->clips.at(g.clip); - ClipPtr post = pre; + ClipPtr close = (panel_timeline->transition_tool_close_clip > -1) + ? olive::ActiveSequence->clips.at(panel_timeline->transition_tool_close_clip) + : nullptr; - make_room_for_transition(ca, pre, panel_timeline->transition_tool_type, transition_start, transition_end, true); + bool shared_transition = (open != nullptr && close != nullptr); - if (panel_timeline->transition_tool_post_clip > -1) { - // post_clip == -1 means this will be just one transition on one clip rather than a shared transition - // between two clips + if (open != nullptr) { + open->undeletable = true; + } + if (close != nullptr) { + close->undeletable = true; + } - post = olive::ActiveSequence->clips.at(panel_timeline->transition_tool_post_clip); + // delete everything under this new transition + QVector areas; + Selection s; + s.in = transition_start; + s.out = transition_end; + s.track = g.track; + areas.append(s); + panel_timeline->delete_areas_and_relink(ca, areas, false); - // get opposite transition type - int opposite_type = (panel_timeline->transition_tool_type == kTransitionOpening) ? - kTransitionClosing : kTransitionOpening; + if (open != nullptr) { + open->undeletable = false; + } + if (close != nullptr) { + close->undeletable = false; + } - make_room_for_transition( - ca, - post, - opposite_type, - transition_start, - transition_end, - true - ); - if (panel_timeline->transition_tool_type == kTransitionClosing) { - // swap - ClipPtr temp = pre; - pre = post; - post = temp; + if (open != nullptr) { + make_room_for_transition(ca, open, kTransitionOpening, transition_start, transition_end, true); + + if (transition_start < open->timeline_in || transition_end > open->timeline_out) { +// long effective_out = (close != nullptr) ? close->timeline_out : open->timeline_out; + long new_in = qMin(transition_start, open->timeline_in); + long new_out = qMax(transition_end, open->timeline_out); + + move_clip(ca, + open, + new_in, + new_out, + open->clip_in - (open->timeline_in - new_in), + open->track); } } - if (transition_start < post->timeline_in || transition_end > pre->timeline_out) { - // if the user extended the transition beyond the clip's boundaries, delete the content there and extend - // the clip to fill these new boundaries + if (close != nullptr) { + make_room_for_transition(ca, close, kTransitionClosing, transition_start, transition_end, true); - QVector areas; - Selection s; - s.track = post->track; + if (transition_start < close->timeline_in || transition_end > close->timeline_out) { +// long effective_in = (open != nullptr) ? open->timeline_in : close->timeline_in; + long new_in = qMin(transition_start, close->timeline_in); + long new_out = qMax(transition_end, close->timeline_out); - bool move_post = false; - bool move_pre = false; - - if (transition_start < post->timeline_in) { - s.in = transition_start; - s.out = post->timeline_in; - areas.append(s); - move_post = true; + move_clip(ca, + close, + new_in, + new_out, + close->clip_in - (close->timeline_in - new_in), + close->track); } - if (transition_end > pre->timeline_out) { - s.in = pre->timeline_out; - s.out = transition_end; - areas.append(s); - move_pre = true; - } - - panel_timeline->delete_areas_and_relink(ca, areas, false); - - if (move_post) move_clip(ca, post, qMin(transition_start, post->timeline_in), post->timeline_out, post->clip_in - (post->timeline_in - transition_start), post->track); - if (move_pre) move_clip(ca, pre, pre->timeline_in, qMax(transition_end, pre->timeline_out), pre->clip_in, pre->track); } - if (panel_timeline->transition_tool_post_clip > -1) { - ca->append(new AddTransitionCommand(pre, post, nullptr, panel_timeline->transition_tool_meta, kTransitionOpening, transition_end - pre->timeline_in)); - } else { - ca->append(new AddTransitionCommand(pre, nullptr, nullptr, panel_timeline->transition_tool_meta, panel_timeline->transition_tool_type, transition_end - transition_start)); + long transition_length = transition_end - transition_start; + if (shared_transition) { + transition_length /= 2; } + ca->append(new AddTransitionCommand(open, + close, + nullptr, + panel_timeline->transition_tool_meta, + transition_length)); + push_undo = true; } } else if (panel_timeline->splitting) { @@ -1573,13 +1584,14 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } } } else if (effective_tool == TIMELINE_TOOL_TRANSITION) { - if (panel_timeline->transition_tool_post_clip == -1) { - validate_transitions(c, panel_timeline->transition_tool_type, frame_diff); + if (panel_timeline->transition_tool_open_clip == -1 + || panel_timeline->transition_tool_close_clip == -1) { + validate_transitions(c, g.media_stream, frame_diff); } else { - ClipPtr otc = c; // open transition clip - ClipPtr ctc = olive::ActiveSequence->clips.at(panel_timeline->transition_tool_post_clip); // close transition clip + ClipPtr otc = olive::ActiveSequence->clips.at(panel_timeline->transition_tool_open_clip); // open transition clip + ClipPtr ctc = olive::ActiveSequence->clips.at(panel_timeline->transition_tool_close_clip); // close transition clip - if (panel_timeline->transition_tool_type == kTransitionClosing) { + if (g.media_stream == kTransitionClosing) { // swap ClipPtr temp = otc; otc = ctc; @@ -1658,10 +1670,11 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.track += track_diff; } } else if (effective_tool == TIMELINE_TOOL_TRANSITION) { - if (panel_timeline->transition_tool_post_clip > -1) { + if (panel_timeline->transition_tool_open_clip > -1 + && panel_timeline->transition_tool_close_clip > -1) { g.in = g.old_in - frame_diff; g.out = g.old_out + frame_diff; - } else if (panel_timeline->transition_tool_type == kTransitionOpening) { + } else if (panel_timeline->transition_tool_open_clip == g.clip) { g.out = g.old_out + frame_diff; } else { g.in = g.old_in + frame_diff; @@ -2441,22 +2454,30 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { } else { // transition tool is being used but ghosts haven't been set up yet, set them up now - ClipPtr c = olive::ActiveSequence->clips.at(panel_timeline->transition_tool_pre_clip); + int primary_type = kTransitionOpening; + int primary = panel_timeline->transition_tool_open_clip; + if (primary == -1) { + primary_type = kTransitionClosing; + primary = panel_timeline->transition_tool_close_clip; + } + + ClipPtr c = olive::ActiveSequence->clips.at(primary); Ghost g; - g.in = g.old_in = g.out = g.old_out = (panel_timeline->transition_tool_type == kTransitionOpening) ? + g.in = g.old_in = g.out = g.old_out = (primary_type == kTransitionOpening) ? c->timeline_in : c->timeline_out; g.track = c->track; - g.clip = panel_timeline->transition_tool_pre_clip; - g.media_stream = panel_timeline->transition_tool_type; + g.clip = primary; + g.media_stream = primary_type; g.trim_type = TRIM_NONE; panel_timeline->ghosts.append(g); panel_timeline->transition_tool_proc = true; + } } else { @@ -2467,8 +2488,8 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { int mouse_clip = getClipIndexFromCoords(panel_timeline->cursor_frame, panel_timeline->cursor_track); // set default transition tool references to no clip - panel_timeline->transition_tool_pre_clip = -1; - panel_timeline->transition_tool_post_clip = -1; + panel_timeline->transition_tool_open_clip = -1; + panel_timeline->transition_tool_close_clip = -1; if (mouse_clip > -1) { @@ -2479,35 +2500,28 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { // check if the clip and transition are both the same sign (meaning video/audio are the same) if (same_sign(c->track, panel_timeline->transition_tool_side)) { - // set "pre" clip to the hovered clip - panel_timeline->transition_tool_pre_clip = mouse_clip; - - // set whether the transition is opening or closing based on whether the cursor is on the left half - // or right half of the clip - if (panel_timeline->cursor_frame > (c->timeline_in + (c->getLength()/2))) { - panel_timeline->transition_tool_type = kTransitionClosing; - } else { - panel_timeline->transition_tool_type = kTransitionOpening; - } - // the range within which the transition tool will assume the user wants to make a shared transition // between two clips rather than just one transition on one clip long between_range = getFrameFromScreenPoint(panel_timeline->zoom, TRANSITION_BETWEEN_RANGE) + 1; - // if the cursor is within this range, set the post_clip to be the next clip touching - // - // getClipIndexFromCoords() will automatically set to -1 if there's no clip there which means the - // end result will be the same as not setting a clip here at all - if (panel_timeline->cursor_frame < c->timeline_in + between_range) { + // set whether the transition is opening or closing based on whether the cursor is on the left half + // or right half of the clip + if (panel_timeline->cursor_frame > (c->timeline_in + (c->getLength()/2))) { + panel_timeline->transition_tool_close_clip = mouse_clip; - // get clip touching to the left - panel_timeline->transition_tool_post_clip = getClipIndexFromCoords(c->timeline_in-1, c->track); - - } else if (panel_timeline->cursor_frame > c->timeline_out - between_range) { - - // get clip touching to the right - panel_timeline->transition_tool_post_clip = getClipIndexFromCoords(c->timeline_out+1, c->track); + // if the cursor is within this range, set the post_clip to be the next clip touching + // + // getClipIndexFromCoords() will automatically set to -1 if there's no clip there which means the + // end result will be the same as not setting a clip here at all + if (panel_timeline->cursor_frame > c->timeline_out - between_range) { + panel_timeline->transition_tool_open_clip = getClipIndexFromCoords(c->timeline_out+1, c->track); + } + } else { + panel_timeline->transition_tool_open_clip = mouse_clip; + if (panel_timeline->cursor_frame < c->timeline_in + between_range) { + panel_timeline->transition_tool_close_clip = getClipIndexFromCoords(c->timeline_in-1, c->track); + } } } @@ -2858,27 +2872,33 @@ void TimelineWidget::paintEvent(QPaintEvent*) { if (clip_rect.bottom() >= 0 && clip_rect.bottom() < height()) p.drawLine(QPoint(qMax(0, clip_rect.left()), clip_rect.bottom()), QPoint(qMin(width(), clip_rect.right()), clip_rect.bottom())); // draw transition tool - if (panel_timeline->tool == TIMELINE_TOOL_TRANSITION && (panel_timeline->transition_tool_pre_clip == i || panel_timeline->transition_tool_post_clip == i)) { - int type = panel_timeline->transition_tool_type; - if (panel_timeline->transition_tool_post_clip == i) { - // invert transition type - type = (type == kTransitionClosing) ? kTransitionOpening : kTransitionClosing; - } + if (panel_timeline->tool == TIMELINE_TOOL_TRANSITION) { + + bool shared_transition = (panel_timeline->transition_tool_open_clip > -1 + && panel_timeline->transition_tool_close_clip > -1); + QRect transition_tool_rect = clip_rect; - if (type == kTransitionClosing) { - if (panel_timeline->transition_tool_post_clip > -1) { - transition_tool_rect.setLeft(transition_tool_rect.right() - TRANSITION_BETWEEN_RANGE); - } else { - transition_tool_rect.setLeft(transition_tool_rect.left() + (3*(transition_tool_rect.width()>>2))); - } - } else { - if (panel_timeline->transition_tool_post_clip > -1) { + bool draw_transition_tool_rect = false; + + if (panel_timeline->transition_tool_open_clip == i) { + if (shared_transition) { transition_tool_rect.setWidth(TRANSITION_BETWEEN_RANGE); } else { transition_tool_rect.setWidth(transition_tool_rect.width()>>2); } + draw_transition_tool_rect = true; + } else if (panel_timeline->transition_tool_close_clip == i) { + if (shared_transition) { + transition_tool_rect.setLeft(transition_tool_rect.right() - TRANSITION_BETWEEN_RANGE); + } else { + transition_tool_rect.setLeft(transition_tool_rect.left() + (3*(transition_tool_rect.width()>>2))); + } + draw_transition_tool_rect = true; } - if (transition_tool_rect.left() < width() && transition_tool_rect.right() > 0) { + + if (draw_transition_tool_rect + && transition_tool_rect.left() < width() + && transition_tool_rect.right() > 0) { if (transition_tool_rect.left() < 0) { transition_tool_rect.setLeft(0); }