diff --git a/effects/internal/crossdissolvetransition.cpp b/effects/internal/crossdissolvetransition.cpp index 9fa94e2e4..c5c35f12c 100644 --- a/effects/internal/crossdissolvetransition.cpp +++ b/effects/internal/crossdissolvetransition.cpp @@ -27,8 +27,9 @@ CrossDissolveTransition::CrossDissolveTransition(Clip* c, Clip* s, const EffectM } void CrossDissolveTransition::process_coords(double progress, GLTextureCoords& coords, int data) { - if (!(data == kTransitionClosing && secondary_clip != nullptr)) { - if (data == kTransitionClosing) progress = 1.0 - progress; + if (data == kTransitionClosing) { + coords.opacity *= (1.0 - progress); + } else { coords.opacity *= progress; } } diff --git a/panels/timeline.cpp b/panels/timeline.cpp index ed665ba1c..23109ebff 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -628,22 +628,27 @@ void Timeline::copy(bool del) { } void Timeline::ripple_delete() { + if (sequence_ != nullptr) { QVector selections = sequence_->Selections(); - if (selections.isEmpty()) { - - sequence_->RippleDeleteEmptySpace(cursor_track, cursor_frame); - - } else if (olive::config.hover_focus && get_focused_panel() == this) { + if (!selections.isEmpty()) { ComboAction* ca = new ComboAction(); sequence_->DeleteAreas(ca, selections, true, true); olive::undo_stack.push(ca); + } else if (olive::config.hover_focus && get_focused_panel() == this) { + + ComboAction* ca = new ComboAction(); + sequence_->RippleDeleteEmptySpace(ca, cursor_track, cursor_frame); + olive::undo_stack.push(ca); + } } + + repaint_timeline(); } void Timeline::ripple_delete_empty_space() diff --git a/timeline/ghost.cpp b/timeline/ghost.cpp index dccd5ecfd..ec48003c5 100644 --- a/timeline/ghost.cpp +++ b/timeline/ghost.cpp @@ -1,5 +1,12 @@ #include "ghost.h" +Ghost::Ghost() : + transition(nullptr), + track_movement(0), + clip(nullptr) +{ +} + Selection Ghost::ToSelection() const { return Selection(in, out, track->Sibling(track_movement)); diff --git a/timeline/ghost.h b/timeline/ghost.h index cd5dafd5b..f95314fe3 100644 --- a/timeline/ghost.h +++ b/timeline/ghost.h @@ -17,6 +17,8 @@ enum TrimType { } struct Ghost { + Ghost(); + Clip* clip; long in; diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 19fef2e98..c47e2fb6b 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -628,6 +628,10 @@ void Sequence::Split() void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas, bool ripple) { + if (areas.isEmpty()) { + return; + } + Selection::Tidy(areas); panel_graph_editor->set_row(nullptr); @@ -690,16 +694,22 @@ void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool desel } // deselect selected clip areas + long minimum_in = LONG_MAX; + long minimum_length = LONG_MAX; if (deselect_areas) { QVector area_copy = areas; for (int i=0;iDeselectArea(s.in(), s.out()); + + // Get ripple point and ripple length + minimum_in = qMin(minimum_in, s.in()); + minimum_length = qMin(minimum_length, s.in() - s.out()); } } if (ripple) { - + RippleDeleteArea(ca, minimum_in, minimum_length); } olive::timeline::RelinkClips(pre_clips, post_clips); @@ -776,7 +786,7 @@ bool Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector p return split_occurred; } -void Sequence::RippleDeleteEmptySpace(Track* track, long point) +void Sequence::RippleDeleteEmptySpace(ComboAction* ca, Track* track, long point) { QVector track_clips = track->GetAllClips(); @@ -805,6 +815,11 @@ void Sequence::RippleDeleteEmptySpace(Track* track, long point) // We now know the maximum ripple we could do to clear this empty space, but we need to ensure it won't cause // overlaps of clips in other tracks + RippleDeleteArea(ca, point, ripple_end - ripple_start); +} + +void Sequence::RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_length) { + for (int i=0;iTrackAt(j); // We've already tested `track`, so we don't need to test it again - if (t != track) { + long first_in_point_after_point = LONG_MAX; + long out_point_just_before_first_in_point = LONG_MIN; - long first_in_point_after_point = LONG_MAX; - long out_point_just_before_first_in_point = LONG_MIN; + QVector track_clips = t->GetAllClips(); - QVector track_clips = t->GetAllClips(); + // Find the in point of the clip directly after the point + for (int k=0;ktimeline_in() >= ripple_point) { + first_in_point_after_point = qMin(first_in_point_after_point, c->timeline_in()); + } + } + + // Ensure we found a valid in point before proceeding + if (first_in_point_after_point != LONG_MAX) { + + // Find the out point of the clip directly before the clip found above for (int k=0;ktimeline_in() > point) { - first_in_point_after_point = qMin(first_in_point_after_point, c->timeline_in()); + if (c->timeline_out() <= first_in_point_after_point) { + out_point_just_before_first_in_point = qMax(out_point_just_before_first_in_point, c->timeline_out()); } } - // Ensure we found a valid in point before proceeding - if (first_in_point_after_point != LONG_MAX) { + long ripple_test = first_in_point_after_point - out_point_just_before_first_in_point + ripple_length; - // Find the out point of the clip directly before the clip found above - for (int k=0;ktimeline_out() < first_in_point_after_point) { - out_point_just_before_first_in_point = qMax(out_point_just_before_first_in_point, c->timeline_out()); - } - } - - long gap_between_clips = first_in_point_after_point - out_point_just_before_first_in_point; - - if (gap_between_clips > (ripple_end - ripple_start)) { - ripple_end = ripple_start + gap_between_clips; - } + if (ripple_test < 0) { + ripple_length -= ripple_test; } } } } - if (ripple_start != ripple_end) { - ComboAction* ca = new ComboAction(); - Ripple(ca, ripple_start, ripple_start - ripple_end); - olive::undo_stack.push(ca); - } -} - -/* -QVector Sequence::SelectedClipIndexes() -{ - QVector selected_clips; - - for (int i=0;i positions, bool relink = true); - void RippleDeleteEmptySpace(Track *track, long point); + void RippleDeleteEmptySpace(ComboAction *ca, Track *track, long point); + void RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_length); Effect* GetSelectedGizmo(); diff --git a/ui/focusfilter.cpp b/ui/focusfilter.cpp index b3e6de286..be1736a48 100644 --- a/ui/focusfilter.cpp +++ b/ui/focusfilter.cpp @@ -210,7 +210,7 @@ void FocusFilter::delete_function() { ComboAction* ca = new ComboAction(); top_sequence->DeleteAreas(ca, top_sequence->Selections(), true); olive::undo_stack.push(ca); - Timeline::GetTopTimeline()->repaint_timeline(); + update_ui(false); } } } diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 23bc328ee..143a85b47 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -647,14 +647,12 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { g.in = g.old_in = g.out = g.old_out = ParentTimeline()->drag_frame_start; g.track = ParentTimeline()->drag_track_start; - g.track_movement = 0; if (g.track == nullptr) { g.track = track_list_->Last(); - g.track_movement = getTrackIndexFromScreenPoint(event->pos().x()) - g.track->Index(); + ParentTimeline()->drag_track_start = track_list_->Last(); + g.track_movement = getTrackIndexFromScreenPoint(event->pos().y()) - g.track->Index(); } - g.transition = nullptr; - g.clip = nullptr; g.trim_type = olive::timeline::TRIM_OUT; ParentTimeline()->ghosts.append(g); @@ -1010,7 +1008,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { panel_sequence_viewer->cue_recording(qMin(g.in, g.out), qMax(g.in, g.out), g.track); ParentTimeline()->creating = false; } else if (g.in != g.out) { - ClipPtr c = std::make_shared(g.track); + ClipPtr c = std::make_shared(g.track->Sibling(g.track_movement)); c->set_media(nullptr, 0); c->set_timeline_in(qMin(g.in, g.out)); c->set_timeline_out(qMax(g.in, g.out)); @@ -1097,6 +1095,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } if (process_moving) { + const Ghost& first_ghost = ParentTimeline()->ghosts.at(0); // start a ripple movement @@ -1462,7 +1461,16 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } } } + + // move selections to match new ghosts + QVector new_selections; + for (int i=0;ighosts.size();i++) { + new_selections.append(ParentTimeline()->ghosts.at(i).ToSelection()); + } + ca->append(new SetSelectionsCommand(sequence(), sequence()->Selections(), new_selections)); + push_undo = true; + } } else if (ParentTimeline()->selecting || ParentTimeline()->rect_select_proc) { } else if (ParentTimeline()->transition_tool_proc) { @@ -1561,7 +1569,6 @@ void TimelineView::init_ghosts() { Clip* c = g.clip; g.track = c->track(); - g.track_movement = 0; g.clip_in = g.old_clip_in = c->clip_in(); if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_SLIP) { @@ -1632,7 +1639,6 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { int effective_tool = olive::timeline::current_tool; if (ParentTimeline()->importing || ParentTimeline()->creating) effective_tool = olive::timeline::TIMELINE_TOOL_POINTER; - Track* mouse_track = getTrackFromScreenPoint(mouse_pos.y()); long frame_diff = (lock_frame) ? 0 : ParentTimeline()->getTimelineFrameFromScreenPoint(mouse_pos.x()) - ParentTimeline()->drag_frame_start; long validator; long earliest_in_point = LONG_MAX; @@ -1918,7 +1924,6 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.out = g.old_out + ghost_diff; } } else if (clips_are_movable) { - g.track_movement = 0; g.in = g.old_in + frame_diff; g.out = g.old_out + frame_diff; @@ -1931,7 +1936,7 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.track_movement = getTrackIndexFromScreenPoint(mouse_pos.y()); - } else if (g.track->type() == track_list_->type()) { + } else if (g.track->type() == track_list_->type() && g.transition == nullptr) { g.track_movement = track_diff; @@ -2192,7 +2197,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } else { // if not, repaint (seeking will trigger a repaint) ParentTimeline()->repaint_timeline(); - } + } } else if (ParentTimeline()->hand_moving) { @@ -2254,52 +2259,43 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (c != nullptr) { Ghost g; - g.transition = nullptr; - // check if whole clip is added - bool add = false; + // check if whole clip is selected + bool add = c->IsSelected(); - // check if a transition is selected (prioritize transition selection) - // (only the pointer tool supports moving transitions) - if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER - && (c->opening_transition != nullptr || c->closing_transition != nullptr)) { - - // check if any selections contain a whole transition - if (c->IsTransitionSelected(kTransitionOpening)) { - g.transition = c->opening_transition; - add = true; - } else if (c->IsTransitionSelected(kTransitionClosing)) { - g.transition = c->closing_transition; - add = true; - } - - } - - // if a transition isn't selected, check if the whole clip is if (!add) { - add = c->IsSelected(); - } + // check if a transition is selected + // (only the pointer tool supports moving transitions) + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER + && (c->opening_transition != nullptr || c->closing_transition != nullptr)) { - if (add) { - - if (g.transition != nullptr) { - - // transition may be a dual transition, check if it's already been added elsewhere - for (int j=0;jghosts.size();j++) { - if (ParentTimeline()->ghosts.at(j).transition == g.transition) { - add = false; - break; - } + // check if any selections contain a whole transition + if (c->IsTransitionSelected(kTransitionOpening)) { + g.transition = c->opening_transition; + add = true; + } else if (c->IsTransitionSelected(kTransitionClosing)) { + g.transition = c->closing_transition; + add = true; } } + } - if (add) { - g.clip = c; - g.trim_type = ParentTimeline()->trim_type; - ParentTimeline()->ghosts.append(g); + if (add && g.transition != nullptr) { + + // transition may be a shared transition, check if it's already been added elsewhere + for (int j=0;jghosts.size();j++) { + if (ParentTimeline()->ghosts.at(j).transition == g.transition) { + add = false; + break; + } } + } + if (add) { + g.clip = c; + g.trim_type = ParentTimeline()->trim_type; + ParentTimeline()->ghosts.append(g); } } } @@ -2489,7 +2485,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // See if this track touches this rectangle at all if (!(track_bottom < rect_top - || track_top > rect_bottom)) { + || track_top > rect_bottom)) { // Loop through track's clips for clips touching this rectangle for (int i=0;iClipCount();i++) { diff --git a/undo/undo.cpp b/undo/undo.cpp index ae8c80f11..1a7ba7e80 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -88,11 +88,13 @@ void MoveClipAction::doRedo() { } } -DeleteClipAction::DeleteClipAction(Clip *clip) +DeleteClipAction::DeleteClipAction(Clip *clip) : + done_(false) { // Get shared_ptr object to take ownership of this Clip clip_ = clip->track()->GetClipObjectFromRawPtr(clip); + doRedo(); } void DeleteClipAction::doUndo() { @@ -101,30 +103,38 @@ void DeleteClipAction::doUndo() { // restore links to this clip for (int i=0;ilinked.append(clip_.get()); } + + done_ = false; } void DeleteClipAction::doRedo() { - // remove ref to clip - if (clip_->IsOpen()) { - clip_->Close(true); - } + if (!done_) { - clip_->track()->RemoveClip(clip_.get()); + // remove ref to clip + if (clip_->IsOpen()) { + clip_->Close(true); + } - // delete link to this clip - QVector clips = clip_->track()->sequence()->GetAllClips(); - for (int i=0;itrack()->RemoveClip(clip_.get()); - for (int j=0;jlinked.size();j++) { - if (c->linked.at(j) == clip_.get()) { - c->linked.removeAt(j); - clips_linked_to_this_one_.append(c); - break; + // delete link to this clip + QVector clips = clip_->track()->sequence()->GetAllClips(); + for (int i=0;ilinked.size();j++) { + if (c->linked.at(j) == clip_.get()) { + c->linked.removeAt(j); + clips_linked_to_this_one_.append(c); + break; + } } } + + done_ = true; + } } @@ -783,23 +793,19 @@ void SetBool::doRedo() { SetSelectionsCommand::SetSelectionsCommand(Sequence *s, const QVector &old_data, const QVector &new_data) : + seq_(s), old_data_(old_data), - new_data_(new_data), - done_(true) + new_data_(new_data) { } void SetSelectionsCommand::doUndo() { seq_->SetSelections(old_data_); - done_ = false; } void SetSelectionsCommand::doRedo() { - if (!done_) { - seq_->SetSelections(new_data_); - done_ = true; - } + seq_->SetSelections(new_data_); } EditSequenceCommand::EditSequenceCommand(Media* i, SequencePtr s) { diff --git a/undo/undo.h b/undo/undo.h index 21a50113e..fd6bd0669 100644 --- a/undo/undo.h +++ b/undo/undo.h @@ -119,6 +119,7 @@ public: private: ClipPtr clip_; QVector clips_linked_to_this_one_; + bool done_; }; class AddEffectCommand : public OliveAction { @@ -417,7 +418,6 @@ private: QVector old_data_; QVector new_data_; Sequence* seq_; - bool done_; }; class EditSequenceCommand : public OliveAction {