From 8989333e990faf63ce573ef5e17e4046666d7e51 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 31 Mar 2019 14:14:37 +1100 Subject: [PATCH] more refactoring and timeline rewriting --- global/global.cpp | 143 +++++++++++++ global/global.h | 5 + global/math.cpp | 6 + global/math.h | 3 + olive.pro | 3 +- panels/effectcontrols.cpp | 6 +- panels/project.cpp | 20 +- panels/timeline.cpp | 440 +++----------------------------------- panels/timeline.h | 3 - project/clipboard.cpp | 63 +++++- project/clipboard.h | 37 +++- timeline/selection.cpp | 67 ++++++ timeline/selection.h | 27 ++- timeline/sequence.cpp | 180 ++++++++++++++-- timeline/sequence.h | 11 +- timeline/track.cpp | 97 ++++++--- timeline/track.h | 8 + 17 files changed, 621 insertions(+), 498 deletions(-) create mode 100644 timeline/selection.cpp diff --git a/global/global.cpp b/global/global.cpp index 72a0fb173..097549e31 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -30,6 +30,7 @@ #include "panels/panels.h" #include "global/path.h" #include "global/config.h" +#include "project/clipboard.h" #include "rendering/audio.h" #include "dialogs/demonotice.h" #include "dialogs/preferencesdialog.h" @@ -307,6 +308,148 @@ void OliveGlobal::save_recent_projects() } } +void OliveGlobal::PasteInternal(Sequence *s) +{ + if (!olive::clipboard.IsEmpty()) { + if (olive::clipboard.type() == Clipboard::CLIPBOARD_TYPE_CLIP) { + ComboAction* ca = new ComboAction(); + + // create copies and delete areas that we'll be pasting to + QVector delete_areas; + QVector pasted_clips; + long paste_start = LONG_MAX; + long paste_end = LONG_MIN; + + for (int i=0;i(olive::clipboard.at(i)); + + // create copy of clip and offset by playhead + ClipPtr cc = c->copy(olive::ActiveSequence.get()); + + // convert frame rates + cc->set_timeline_in(rescale_frame_number(cc->timeline_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); + cc->set_timeline_out(rescale_frame_number(cc->timeline_out(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); + cc->set_clip_in(rescale_frame_number(cc->clip_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); + + cc->set_timeline_in(cc->timeline_in() + olive::ActiveSequence->playhead); + cc->set_timeline_out(cc->timeline_out() + olive::ActiveSequence->playhead); + cc->set_track(c->track()); + + paste_start = qMin(paste_start, cc->timeline_in()); + paste_end = qMax(paste_end, cc->timeline_out()); + + pasted_clips.append(cc); + + if (!insert) { + delete_areas.append(Selection(cc->timeline_in(), cc->timeline_out(), c->track())); + } + } + if (insert) { + split_all_clips_at_point(ca, olive::ActiveSequence->playhead); + ripple_clips(ca, olive::ActiveSequence.get(), paste_start, paste_end - paste_start); + } else { + delete_areas_and_relink(ca, delete_areas, false); + } + + // correct linked clips + for (int i=0;i(clipboard.at(i)); + + for (int j=0;jlinked.size();j++) { + for (int k=0;k(clipboard.at(k)); + if (comp->load_id == oc->linked.at(j)) { + pasted_clips.at(i)->linked.append(k); + } + } + } + } + + ca->append(new AddClipCommand(olive::ActiveSequence.get(), pasted_clips)); + + olive::UndoStack.push(ca); + + update_ui(true); + + if (olive::CurrentConfig.paste_seeks) { + panel_sequence_viewer->seek(paste_end); + } + + } else if (clipboard_type == CLIPBOARD_TYPE_EFFECT) { + ComboAction* ca = new ComboAction(); + + bool replace = false; + bool skip = false; + bool ask_conflict = true; + + QVector selected_clips = olive::ActiveSequence->SelectedClips(); + + for (int i=0;i(clipboard.at(j)); + if ((c->track() < 0) == (e->meta->subtype == EFFECT_TYPE_VIDEO)) { + int found = -1; + if (ask_conflict) { + replace = false; + skip = false; + } + for (int k=0;keffects.size();k++) { + if (c->effects.at(k)->meta == e->meta) { + found = k; + break; + } + } + if (found >= 0 && ask_conflict) { + QMessageBox box(this); + box.setWindowTitle(tr("Effect already exists")); + box.setText(tr("Clip '%1' already contains a '%2' effect. " + "Would you like to replace it with the pasted one or add it as a separate effect?") + .arg(c->name(), e->meta->name)); + box.setIcon(QMessageBox::Icon::Question); + + box.addButton(tr("Add"), QMessageBox::YesRole); + QPushButton* replace_button = box.addButton(tr("Replace"), QMessageBox::NoRole); + QPushButton* skip_button = box.addButton(tr("Skip"), QMessageBox::RejectRole); + + QCheckBox* future_box = new QCheckBox(tr("Do this for all conflicts found"), &box); + box.setCheckBox(future_box); + + box.exec(); + + if (box.clickedButton() == replace_button) { + replace = true; + } else if (box.clickedButton() == skip_button) { + skip = true; + } + ask_conflict = !future_box->isChecked(); + } + + if (found >= 0 && skip) { + // do nothing + } else if (found >= 0 && replace) { + ca->append(new EffectDeleteCommand(c->effects.at(found).get())); + + ca->append(new AddEffectCommand(c, e->copy(c), nullptr, found)); + } else { + ca->append(new AddEffectCommand(c, e->copy(c), nullptr)); + } + } + } + } + if (ca->hasActions()) { + ca->appendPost(new ReloadEffectsCommand()); + olive::UndoStack.push(ca); + } else { + delete ca; + } + update_ui(true); + } + } +} + void OliveGlobal::ImportProject(const QString &fn) { LoadProject(fn, false); diff --git a/global/global.h b/global/global.h index 8d66c6ee6..5543fa0ef 100644 --- a/global/global.h +++ b/global/global.h @@ -472,6 +472,11 @@ private: */ void save_recent_projects(); + /** + * @brief Internal pasting function + */ + void PasteInternal(Sequence* s); + /** * @brief File filter used for any file dialogs relating to Olive project files. */ diff --git a/global/math.cpp b/global/math.cpp index f19a22b02..c11a79d4b 100644 --- a/global/math.cpp +++ b/global/math.cpp @@ -99,3 +99,9 @@ QRect fit_size_into_rect(const QRect &r, int width, int height) return QRect(r.x(), r.y() + (r.height() / 2 - new_height / 2), r.width(), new_height); } } + +template +const T &clamp(const T &val, T &min, T &max) +{ + return qMax(qMin(max, val), min); +} diff --git a/global/math.h b/global/math.h index face5003d..9d0c9a9f5 100644 --- a/global/math.h +++ b/global/math.h @@ -32,6 +32,9 @@ double cubic_from_t(double a, double b, double c, double d, double t); double cubic_t_from_x(double x_target, double a, double b, double c, double d); double solveCubicBezier(double p0, double p1, double p2, double p3, double x); +template +const T& clamp(const T& val, T& min, T& max); + QRect fit_size_into_rect(const QRect& r, int width, int height); // decibel conversion functions diff --git a/olive.pro b/olive.pro index 672286233..40be13dd2 100644 --- a/olive.pro +++ b/olive.pro @@ -181,7 +181,8 @@ SOURCES += \ ui/timelinearea.cpp \ timeline/timelineshared.cpp \ ui/timelineview.cpp \ - ui/timelinelabel.cpp + ui/timelinelabel.cpp \ + timeline/selection.cpp HEADERS += \ ui/mainwindow.h \ diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index f386c87f3..279ddc3c9 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -156,12 +156,12 @@ void EffectControls::copy(bool del) { if (e->meta->type == EFFECT_TYPE_EFFECT) { if (!cleared) { - clear_clipboard(); + olive::clipboard.Clear(); cleared = true; - clipboard_type = CLIPBOARD_TYPE_EFFECT; + olive::clipboard.SetType(Clipboard::CLIPBOARD_TYPE_EFFECT); } - clipboard.append(e->copy(nullptr)); + olive::clipboard.Append(e->copy(nullptr)); if (del) { diff --git a/panels/project.cpp b/panels/project.cpp index 0e0c614ac..e58f6ad74 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -354,20 +354,6 @@ bool Project::IsProjectWidget(QObject *child) return (child == tree_view || child == icon_view); } -bool delete_clips_in_clipboard_with_media(ComboAction* ca, Media* m) { - int delete_count = 0; - if (clipboard_type == CLIPBOARD_TYPE_CLIP) { - for (int i=0;i(clipboard.at(i)); - if (c->media() == m) { - ca->append(new RemoveClipsFromClipboard(i-delete_count)); - delete_count++; - } - } - } - return (delete_count > 0); -} - void Project::delete_selected_media() { ComboAction* ca = new ComboAction(); QModelIndexList selected_items = get_current_selected(); @@ -455,7 +441,7 @@ void Project::delete_selected_media() { } } if (confirm_delete) { - delete_clips_in_clipboard_with_media(ca, item); + olive::clipboard.DeleteClipsWithMedia(ca, item); } } } @@ -600,7 +586,9 @@ void Project::delete_clips_using_selected_media() { } for (int j=0;j& ignore) { - ca->append(new RippleAction(s, point, length, ignore)); -} - void Timeline::toggle_show_all() { if (olive::ActiveSequence != nullptr) { showing_all = !showing_all; @@ -453,9 +449,9 @@ void Timeline::nest() { Ghost& g = ghosts[i]; if (c->track() == g.track && !((c->timeline_in() < g.in - && c->timeline_out() < g.in) - || (c->timeline_in() > g.out - && c->timeline_out() > g.out))) { + && c->timeline_out() < g.in) + || (c->timeline_in() > g.out + && c->timeline_out() > g.out))) { // There's a clip occupied by the space taken up by this ghost. Move up a track, and seek again. g.track = g.track->track_list()->TrackAt(g.track->Index() + 1); @@ -547,17 +543,7 @@ void Timeline::repaint_timeline() { void Timeline::select_all() { if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->ClearSelections(); - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - Selection s; - s.in = c->timeline_in(); - s.out = c->timeline_out(); - s.track = c->track(); - olive::ActiveSequence->selections.append(s); - } - } + olive::ActiveSequence->SelectAll(); repaint_timeline(); } } @@ -567,62 +553,11 @@ void Timeline::scroll_to_frame(long frame) { } void Timeline::select_from_playhead() { - olive::ActiveSequence->selections.clear(); - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr - && c->timeline_in() <= olive::ActiveSequence->playhead - && c->timeline_out() > olive::ActiveSequence->playhead) { - Selection s; - s.in = c->timeline_in(); - s.out = c->timeline_out(); - s.track = c->track(); - olive::ActiveSequence->selections.append(s); - } + if (olive::ActiveSequence != nullptr) { + olive::ActiveSequence->SelectAtPlayhead(); } } -bool Timeline::can_ripple_empty_space(long frame, int track) { - bool can_ripple_delete = true; - bool at_end_of_sequence = true; - rc_ripple_min = 0; - rc_ripple_max = LONG_MAX; - - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - if (c->timeline_in() > frame || c->timeline_out() > frame) { - at_end_of_sequence = false; - } - if (c->track() == track) { - if (c->timeline_in() <= frame && c->timeline_out() >= frame) { - can_ripple_delete = false; - break; - } else if (c->timeline_out() < frame) { - rc_ripple_min = qMax(rc_ripple_min, c->timeline_out()); - } else if (c->timeline_in() > frame) { - rc_ripple_max = qMin(rc_ripple_max, c->timeline_in()); - } - } - } - } - - return (can_ripple_delete && !at_end_of_sequence); -} - -void Timeline::ripple_delete_empty_space() { - QVector sels; - - Selection s; - s.in = rc_ripple_min; - s.out = rc_ripple_max; - s.track = cursor_track; - - sels.append(s); - - delete_selection(sels, true); -} - void Timeline::resizeEvent(QResizeEvent *) { // adjust maximum scrollbar if (olive::ActiveSequence != nullptr) set_sb_max(); @@ -654,30 +589,6 @@ void Timeline::resizeEvent(QResizeEvent *) { tool_button_widget->setFixedWidth((tool_button_children.at(0)->sizeHint().width())*cols + horizontal_spacing*(cols-1) + 1); } -void Timeline::delete_in_out_internal(bool ripple) { - if (olive::ActiveSequence != nullptr && olive::ActiveSequence->using_workarea) { - QVector areas; - int video_tracks = 0, audio_tracks = 0; - olive::ActiveSequence->getTrackLimits(&video_tracks, &audio_tracks); - for (int i=video_tracks;i<=audio_tracks;i++) { - Selection s; - s.in = olive::ActiveSequence->workarea_in; - s.out = olive::ActiveSequence->workarea_out; - s.track = i; - areas.append(s); - } - ComboAction* ca = new ComboAction(); - delete_areas_and_relink(ca, areas, true); - if (ripple) ripple_clips(ca, - olive::ActiveSequence.get(), - olive::ActiveSequence->workarea_in, - olive::ActiveSequence->workarea_in - olive::ActiveSequence->workarea_out); - ca->append(new SetTimelineInOutCommand(olive::ActiveSequence.get(), false, 0, 0)); - olive::UndoStack.push(ca); - update_ui(true); - } -} - void Timeline::toggle_enable_on_selected_clips() { if (olive::ActiveSequence != nullptr) { @@ -701,76 +612,6 @@ void Timeline::toggle_enable_on_selected_clips() { } } -void Timeline::delete_selection(QVector& selections, bool ripple_delete) { - if (selections.size() > 0) { - panel_graph_editor->set_row(nullptr); - panel_effect_controls->Clear(true); - - ComboAction* ca = new ComboAction(); - - // delete the areas currently selected by `selections` - // if we're ripple deleting, we don't want to delete the selections since we still need them for the ripple - delete_areas_and_relink(ca, selections, !ripple_delete); - - if (ripple_delete) { - long ripple_point = selections.at(0).in; - long ripple_length = selections.at(0).out - selections.at(0).in; - - // retrieve ripple_point and ripple_length from current selection - for (int i=1;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr && c->timeline_in() < ripple_point && c->timeline_out() > ripple_point) { - // conflict detected, but this clip may be getting deleted so let's check - bool deleted = false; - for (int j=0;jtrack() - && !(c->timeline_in() < s.in && c->timeline_out() < s.in) - && !(c->timeline_in() > s.out && c->timeline_out() > s.out)) { - deleted = true; - break; - } - } - if (!deleted) { - for (int j=0;jclips.size();j++) { - ClipPtr cc = olive::ActiveSequence->clips.at(j); - if (cc != nullptr - && cc->track() == c->track() - && cc->timeline_in() > c->timeline_out() - && cc->timeline_in() < c->timeline_out() + ripple_length) { - ripple_length = cc->timeline_in() - c->timeline_out(); - } - } - } - } - } - - if (can_ripple) { - ripple_clips(ca, olive::ActiveSequence.get(), ripple_point, -ripple_length); - panel_sequence_viewer->seek(ripple_point-1); - } - - // if we're rippling, we can clear the selections here - if we're not rippling, delete_areas_and_relink() will - // clear the selections for us - selections.clear(); - } - - olive::UndoStack.push(ca); - - update_ui(true); - } -} - void Timeline::set_zoom_value(double v) { // set zoom value zoom = v; @@ -808,38 +649,9 @@ void Timeline::zoom_out() { multiply_zoom(0.5); } -int Timeline::GetTrackHeight(int track) { - for (int i=0;igetTrackLimits(&min_track, &max_track); - - // for each active track, set the track to increase/decrease based on `diff` - for (int i=min_track;i<=max_track;i++) { - SetTrackHeight(i, qMax(GetTrackHeight(i) + diff, olive::timeline::kTrackMinHeight)); + if (olive::ActiveSequence != nullptr) { + olive::ActiveSequence->ChangeTrackHeightsRelatively(diff); } // update the timeline @@ -904,205 +716,13 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool void Timeline::copy(bool del) { - bool cleared = false; - bool copied = false; - - long min_in = 0; - - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - for (int j=0;jselections.size();j++) { - const Selection& s = olive::ActiveSequence->selections.at(j); - if (s.track == c->track() && !((c->timeline_in() <= s.in && c->timeline_out() <= s.in) || (c->timeline_in() >= s.out && c->timeline_out() >= s.out))) { - if (!cleared) { - clear_clipboard(); - cleared = true; - clipboard_type = CLIPBOARD_TYPE_CLIP; - } - - ClipPtr copied_clip = c->copy(nullptr); - - // copy linked IDs (we correct these later in paste()) - copied_clip->linked = c->linked; - - if (copied_clip->timeline_in() < s.in) { - copied_clip->set_clip_in(copied_clip->clip_in() + (s.in - copied_clip->timeline_in())); - copied_clip->set_timeline_in(s.in); - } - - if (copied_clip->timeline_out() > s.out) { - copied_clip->set_timeline_out(s.out); - } - - if (copied) { - min_in = qMin(min_in, s.in); - } else { - min_in = s.in; - copied = true; - } - - copied_clip->load_id = i; - - clipboard.append(copied_clip); - } - } - } - } - - for (int i=0;i(clipboard.at(i)); - c->set_timeline_in(c->timeline_in() - min_in); - c->set_timeline_out(c->timeline_out() - min_in); - } - - if (del && copied) { - delete_selection(olive::ActiveSequence->selections, false); + if (olive::ActiveSequence != nullptr) { + olive::ActiveSequence->AddSelectionsToClipboard(del); } } void Timeline::paste(bool insert) { - if (clipboard.size() > 0) { - if (clipboard_type == CLIPBOARD_TYPE_CLIP) { - ComboAction* ca = new ComboAction(); - // create copies and delete areas that we'll be pasting to - QVector delete_areas; - QVector pasted_clips; - long paste_start = LONG_MAX; - long paste_end = LONG_MIN; - for (int i=0;i(clipboard.at(i)); - - // create copy of clip and offset by playhead - ClipPtr cc = c->copy(olive::ActiveSequence.get()); - - // convert frame rates - cc->set_timeline_in(rescale_frame_number(cc->timeline_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); - cc->set_timeline_out(rescale_frame_number(cc->timeline_out(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); - cc->set_clip_in(rescale_frame_number(cc->clip_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); - - cc->set_timeline_in(cc->timeline_in() + olive::ActiveSequence->playhead); - cc->set_timeline_out(cc->timeline_out() + olive::ActiveSequence->playhead); - cc->set_track(c->track()); - - paste_start = qMin(paste_start, cc->timeline_in()); - paste_end = qMax(paste_end, cc->timeline_out()); - - pasted_clips.append(cc); - - if (!insert) { - Selection s; - s.in = cc->timeline_in(); - s.out = cc->timeline_out(); - s.track = c->track(); - delete_areas.append(s); - } - } - if (insert) { - split_all_clips_at_point(ca, olive::ActiveSequence->playhead); - ripple_clips(ca, olive::ActiveSequence.get(), paste_start, paste_end - paste_start); - } else { - delete_areas_and_relink(ca, delete_areas, false); - } - - // correct linked clips - for (int i=0;i(clipboard.at(i)); - - for (int j=0;jlinked.size();j++) { - for (int k=0;k(clipboard.at(k)); - if (comp->load_id == oc->linked.at(j)) { - pasted_clips.at(i)->linked.append(k); - } - } - } - } - - ca->append(new AddClipCommand(olive::ActiveSequence.get(), pasted_clips)); - - olive::UndoStack.push(ca); - - update_ui(true); - - if (olive::CurrentConfig.paste_seeks) { - panel_sequence_viewer->seek(paste_end); - } - } else if (clipboard_type == CLIPBOARD_TYPE_EFFECT) { - ComboAction* ca = new ComboAction(); - - bool replace = false; - bool skip = false; - bool ask_conflict = true; - - QVector selected_clips = olive::ActiveSequence->SelectedClips(); - - for (int i=0;i(clipboard.at(j)); - if ((c->track() < 0) == (e->meta->subtype == EFFECT_TYPE_VIDEO)) { - int found = -1; - if (ask_conflict) { - replace = false; - skip = false; - } - for (int k=0;keffects.size();k++) { - if (c->effects.at(k)->meta == e->meta) { - found = k; - break; - } - } - if (found >= 0 && ask_conflict) { - QMessageBox box(this); - box.setWindowTitle(tr("Effect already exists")); - box.setText(tr("Clip '%1' already contains a '%2' effect. " - "Would you like to replace it with the pasted one or add it as a separate effect?") - .arg(c->name(), e->meta->name)); - box.setIcon(QMessageBox::Icon::Question); - - box.addButton(tr("Add"), QMessageBox::YesRole); - QPushButton* replace_button = box.addButton(tr("Replace"), QMessageBox::NoRole); - QPushButton* skip_button = box.addButton(tr("Skip"), QMessageBox::RejectRole); - - QCheckBox* future_box = new QCheckBox(tr("Do this for all conflicts found"), &box); - box.setCheckBox(future_box); - - box.exec(); - - if (box.clickedButton() == replace_button) { - replace = true; - } else if (box.clickedButton() == skip_button) { - skip = true; - } - ask_conflict = !future_box->isChecked(); - } - - if (found >= 0 && skip) { - // do nothing - } else if (found >= 0 && replace) { - ca->append(new EffectDeleteCommand(c->effects.at(found).get())); - - ca->append(new AddEffectCommand(c, e->copy(c), nullptr, found)); - } else { - ca->append(new AddEffectCommand(c, e->copy(c), nullptr)); - } - } - } - } - if (ca->hasActions()) { - ca->appendPost(new ReloadEffectsCommand()); - olive::UndoStack.push(ca); - } else { - delete ca; - } - update_ui(true); - } - } } void Timeline::edit_to_point_internal(bool in, bool ripple) { @@ -1379,30 +999,32 @@ bool Timeline::snap_to_timeline(long* l, bool use_playhead, bool use_markers, bo } // snap to clip/transition - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - if (snap_to_point(c->timeline_in(), l)) { - return true; - } else if (snap_to_point(c->timeline_out(), l)) { - return true; - } else if (c->opening_transition != nullptr - && snap_to_point(c->timeline_in() + c->opening_transition->get_true_length(), l)) { - return true; - } else if (c->closing_transition != nullptr - && snap_to_point(c->timeline_out() - c->closing_transition->get_true_length(), l)) { - return true; - } else { - // try to snap to clip markers - for (int j=0;jget_markers().size();j++) { - if (snap_to_point(c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(), l)) { - return true; - } + QVector all_clips = olive::ActiveSequence->GetAllClips(); + for (int i=0;itimeline_in(), l)) { + return true; + } else if (snap_to_point(c->timeline_out(), l)) { + return true; + } else if (c->opening_transition != nullptr + && snap_to_point(c->timeline_in() + c->opening_transition->get_true_length(), l)) { + return true; + } else if (c->closing_transition != nullptr + && snap_to_point(c->timeline_out() - c->closing_transition->get_true_length(), l)) { + return true; + } else { + // try to snap to clip markers + for (int j=0;jget_markers().size();j++) { + if (snap_to_point(c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(), l)) { + return true; } } } + } } + return false; } diff --git a/panels/timeline.h b/panels/timeline.h index 45cc380ad..82b31f153 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -41,8 +41,6 @@ int getScreenPointFromFrame(double zoom, long frame); long getFrameFromScreenPoint(double zoom, int x); bool selection_contains_transition(const Selection& s, Clip *c, int type); -void ripple_clips(ComboAction *ca, Sequence *s, long point, long length, const QVector& ignore = QVector()); - class Timeline : public Panel @@ -95,7 +93,6 @@ public: // selecting functions bool selecting; int selection_offset; - void delete_selection(QVector &selections, bool ripple); void select_all(); bool rect_select_init; bool rect_select_proc; diff --git a/project/clipboard.cpp b/project/clipboard.cpp index d8ee9de8d..9644dd840 100644 --- a/project/clipboard.cpp +++ b/project/clipboard.cpp @@ -24,11 +24,60 @@ #include "effects/effect.h" #include "effects/transition.h" -int clipboard_type = CLIPBOARD_TYPE_CLIP; -QVector clipboard; -QVector clipboard_transitions; - -void clear_clipboard() { - clipboard.clear(); - clipboard_transitions.clear(); +Clipboard::Clipboard() : + type_(CLIPBOARD_TYPE_CLIP) +{ } + +void Clipboard::Append(VoidPtr obj) +{ + clipboard_.append(obj); +} + +void Clipboard::Clear() +{ + clipboard_.clear(); + clipboard_transitions_.clear(); +} + +int Clipboard::Count() +{ + return clipboard_.size(); +} + +VoidPtr Clipboard::Get(int i) +{ + return clipboard_.at(i); +} + +bool Clipboard::IsEmpty() +{ + return clipboard_.isEmpty(); +} + +Clipboard::Type Clipboard::type() +{ + return type_; +} + +bool Clipboard::DeleteClipsWithMedia(ComboAction *ca, Media *m) +{ + if (type_ != CLIPBOARD_TYPE_CLIP) { + return false; + } + + int delete_count = 0; + + for (int i=0;i(clipboard_.at(i)).get(); + + if (c->media() == m) { + ca->append(new RemoveClipsFromClipboard(i-delete_count)); + delete_count++; + } + } + + return (delete_count > 0); +} + +Clipboard olive::clipboard; diff --git a/project/clipboard.h b/project/clipboard.h index 0de6c8f16..6cbdb0992 100644 --- a/project/clipboard.h +++ b/project/clipboard.h @@ -23,16 +23,37 @@ #include -#include - -#define CLIPBOARD_TYPE_CLIP 0 -#define CLIPBOARD_TYPE_EFFECT 1 +#include "effects/transition.h" using VoidPtr = std::shared_ptr; -extern int clipboard_type; -extern QVector clipboard_transitions; -extern QVector clipboard; -void clear_clipboard(); +class Clipboard { +public: + enum Type { + CLIPBOARD_TYPE_CLIP, + CLIPBOARD_TYPE_EFFECT + }; + + Clipboard(); + void Append(VoidPtr obj); + void Clear(); + int Count(); + VoidPtr Get(int i); + void SetType(Type type); + bool IsEmpty(); + Type type(); + + bool DeleteClipsWithMedia(ComboAction* ca, Media* m); + +private: + Type type_; + + QVector clipboard_transitions_; + QVector clipboard_; +}; + +namespace olive { +extern Clipboard clipboard; +} #endif // CLIPBOARD_H diff --git a/timeline/selection.cpp b/timeline/selection.cpp new file mode 100644 index 000000000..96c059613 --- /dev/null +++ b/timeline/selection.cpp @@ -0,0 +1,67 @@ +#include "selection.h" + +Selection::Selection(long in, long out, Track *track) : + in_(in), + out_(out), + track_(track), + old_in_(in), + old_out_(out), + old_track_(track) +{ +} + +long Selection::in() const +{ + return in_; +} + +long Selection::out() const +{ + return out_; +} + +Track *Selection::track() const +{ + return track_; +} + +void Selection::set_in(long in) +{ + in_ = in; +} + +void Selection::set_out(long out) +{ + out_ = out; +} + +void Selection::Tidy(QVector selections) +{ + for (int i=0;i ss.out()) { + // do nothing + } else if (s.in() >= ss.in() && s.out() <= ss.out()) { + remove = true; + } else if (s.in() <= ss.out() && s.out() > ss.out()) { + ss.out = s.out(); + remove = true; + } else if (s.out() >= ss.in() && s.in() < ss.in()) { + ss.in = s.in(); + remove = true; + } + if (remove) { + selections.removeAt(i); + i--; + break; + } + } + } + } + } +} diff --git a/timeline/selection.h b/timeline/selection.h index 3f66905f8..6b76e311d 100644 --- a/timeline/selection.h +++ b/timeline/selection.h @@ -21,14 +21,29 @@ #ifndef SELECTION_H #define SELECTION_H -struct Selection { - long in; - long out; +#include - long old_in; - long old_out; +class Track; - bool trim_in; +class Selection { +public: + Selection(long in, long out, Track* track); + + long in() const; + long out() const; + Track* track() const; + + void set_in(long in); + void set_out(long out); + + static void Tidy(QVector selections); + +private: + long in_; + long out_; + Track* track_; + + bool trim_in_; }; #endif // SELECTION_H diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 8f31a783e..c79900ddb 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -23,6 +23,7 @@ #include #include "panels/panels.h" +#include "project/clipboard.h" #include "global/debug.h" Sequence::Sequence() : @@ -176,9 +177,54 @@ QVector Sequence::SelectedClips(bool containing) return selected_clips; } -void Sequence::DeleteAreas(ComboAction* ca, QVector& areas, bool deselect_areas) +void Sequence::DeleteInToOut(bool ripple) { - clean_up_selections(areas); + if (using_workarea) { + + QVector areas_to_delete; + + for (int i=0;iTrackCount();j++) { + areas_to_delete.append(Selection(workarea_in, workarea_out, tl->TrackAt(j))); + } + + } + + ComboAction* ca = new ComboAction(); + DeleteAreas(ca, areas_to_delete, true); + if (ripple) Ripple(ca, + workarea_in, + workarea_in - workarea_out); + ca->append(new SetTimelineInOutCommand(olive::ActiveSequence.get(), false, 0, 0)); + olive::UndoStack.push(ca); + update_ui(true); + } +} + +void Sequence::Ripple(ComboAction *ca, long point, long length, const QVector &ignore) +{ + ca->append(new RippleAction(this, point, length, ignore)); +} + +void Sequence::ChangeTrackHeightsRelatively(int diff) +{ + for (int i=0;iTrackCount();j++) { + Track* t = tl->TrackAt(j); + + t->set_height(t->height() + diff); + } + } +} + +void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas) +{ + Selection::Tidy(areas); panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); @@ -192,41 +238,41 @@ void Sequence::DeleteAreas(ComboAction* ca, QVector& areas, bool dese const Selection& s = areas.at(i); for (int j=0;jtrack() == s.track && !c->undeletable) { + if (c->track() == s.track() && !c->undeletable) { if (selection_contains_transition(s, c, kTransitionOpening)) { // delete opening transition ca->append(new DeleteTransitionCommand(c->opening_transition)); } else if (selection_contains_transition(s, c, kTransitionClosing)) { // delete closing transition ca->append(new DeleteTransitionCommand(c->closing_transition)); - } else if (c->timeline_in() >= s.in && c->timeline_out() <= s.out) { + } else if (c->timeline_in() >= s.in() && c->timeline_out() <= s.out()) { // clips falls entirely within deletion area ca->append(new DeleteClipAction(c)); - } else if (c->timeline_in() < s.in && c->timeline_out() > s.out) { + } else if (c->timeline_in() < s.in() && c->timeline_out() > s.out()) { // middle of clip is within deletion area // duplicate clip - ClipPtr post = SplitClip(ca, true, c, s.in, s.out); + ClipPtr post = SplitClip(ca, true, c, s.in(), s.out()); pre_clips.append(j); post_clips.append(post); - } else if (c->timeline_in() < s.in && c->timeline_out() > s.in) { + } else if (c->timeline_in() < s.in() && c->timeline_out() > s.in()) { // only out point is in deletion area - c->move(ca, c->timeline_in(), s.in, c->clip_in(), c->track()); + c->move(ca, c->timeline_in(), s.in(), c->clip_in(), c->track()); if (c->closing_transition != nullptr) { - if (s.in < c->timeline_out() - c->closing_transition->get_true_length()) { + if (s.in() < c->timeline_out() - c->closing_transition->get_true_length()) { ca->append(new DeleteTransitionCommand(c->closing_transition)); } else { ca->append(new ModifyTransitionCommand(c->closing_transition, c->closing_transition->get_true_length() - (c->timeline_out() - s.in))); } } - } else if (c->timeline_in() < s.out && c->timeline_out() > s.out) { + } else if (c->timeline_in() < s.out() && c->timeline_out() > s.out()) { // only in point is in deletion area - c->move(ca, s.out, c->timeline_out(), c->clip_in() + (s.out - c->timeline_in()), c->track()); + c->move(ca, s.out(), c->timeline_out(), c->clip_in() + (s.out() - c->timeline_in()), c->track()); if (c->opening_transition != nullptr) { - if (s.out > c->timeline_in() + c->opening_transition->get_true_length()) { + if (s.out() > c->timeline_in() + c->opening_transition->get_true_length()) { ca->append(new DeleteTransitionCommand(c->opening_transition)); } else { ca->append(new ModifyTransitionCommand(c->opening_transition, c->opening_transition->get_true_length() - (s.out - c->timeline_in()))); @@ -370,6 +416,28 @@ Effect *Sequence::GetSelectedGizmo() return gizmo_ptr; } +void Sequence::SelectAll() +{ + for (int j=0;jTrackCount();i++) { + tl->TrackAt(i)->SelectAll(); + } + } +} + +void Sequence::SelectAtPlayhead() +{ + for (int j=0;jTrackCount();i++) { + tl->TrackAt(i)->SelectAtPoint(playhead); + } + } +} + void Sequence::ClearSelections() { for (int j=0;j original_clips; + QVector copied_clips; + + long min_in = LONG_MAX; + + QVector selections = Selections(); + for (int i=0;i track_clips = s.track()->GetAllClips(); + + for (int j=0;jtimeline_out() < s.in() || c->timeline_in() > s.out())) { + + // If so, we'll be copying this clip + original_clips.append(c); + + ClipPtr copy = c->copy(nullptr); + + // If we only copied part of this clip, adjust the copy so it's only that part of the clip + if (copy->timeline_in() < s.in()) { + copy->set_clip_in(copy->clip_in() + (s.in() - copy->timeline_in())); + copy->set_timeline_in(s.in()); + } + + if (copy->timeline_out() > s.out()) { + copy->set_timeline_out(s.out()); + } + + // Store the minimum in point as all copies will be stored offset from 0 + min_in = qMin(min_in, s.in()); + + copied_clips.append(copy); + olive::clipboard.Append(copy); + + } + } + } + + // Determine whether we actually copied anything + if (min_in < LONG_MAX) { + + // Offset all copied clips to 0 + for (int i=0;iset_timeline_in(copy->timeline_in() - min_in); + copy->set_timeline_out(copy->timeline_out() - min_in); + } + + // Relink the copied clips with each other + olive::timeline::RelinkClips(original_clips, copied_clips); + + // If we're deleting the originals (i.e. cutting), delete them now + if (delete_originals) { + ComboAction* ca = new ComboAction(); + DeleteAreas(ca, selections, true); + olive::UndoStack.push(ca); + } + + } +} + +QVector Sequence::Selections() +{ + QVector selections; + + for (int j=0;jTrackCount();i++) { + selections.append(tl->TrackAt(i)->Selections()); + } + } + + return selections; +} + ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame) { return SplitClip(ca, transitions, pre, frame, frame); diff --git a/timeline/sequence.h b/timeline/sequence.h index 5ae8cce88..9ec11fc38 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -65,7 +65,12 @@ public: QVector SelectedClips(bool containing = true); //QVector SelectedClipIndexes(); - void DeleteAreas(); + void DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas); + void DeleteInToOut(bool ripple); + + void Ripple(ComboAction *ca, long point, long length, const QVector& ignore = QVector()); + + void ChangeTrackHeightsRelatively(int diff); bool SplitAllClipsAtPoint(ComboAction *ca, long point); void SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector positions, bool relink = true); @@ -75,7 +80,11 @@ public: bool IsClipSelected(Clip* clip, bool containing = true); bool IsTransitionSelected(Transition* t); + void SelectAll(); + void SelectAtPlayhead(); void ClearSelections(); + void AddSelectionsToClipboard(bool delete_originals); + QVector Selections(); long playhead; diff --git a/timeline/track.cpp b/timeline/track.cpp index 088d09832..48d174c15 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -3,6 +3,7 @@ #include "timeline/clip.h" #include "timeline/tracklist.h" #include "timeline/sequence.h" +#include "global/math.h" int olive::timeline::kTrackDefaultHeight = 40; int olive::timeline::kTrackMinHeight = 30; @@ -72,7 +73,7 @@ int Track::height() void Track::set_height(int h) { - height_ = h; + height_ = qMax(h, olive::timeline::kTrackMinHeight); } void Track::AddClip(ClipPtr clip) @@ -158,9 +159,9 @@ bool Track::IsClipSelected(Clip *clip, bool containing) { for (int i=0;itimeline_in() >= s.in && clip->timeline_out() <= s.out) - || (!containing && !(clip->timeline_in() < s.in && clip->timeline_out() < s.in) - && !(clip->timeline_in() > s.in && clip->timeline_out() > s.in)))) { + if (((clip->timeline_in() >= s.in() && clip->timeline_out() <= s.out()) + || (!containing && !(clip->timeline_in() < s.in() && clip->timeline_out() < s.in()) + && !(clip->timeline_in() > s.in() && clip->timeline_out() > s.in())))) { return true; } } @@ -199,8 +200,8 @@ bool Track::IsTransitionSelected(Transition *t) // See if there's a selection matching this for (int i=0;i= transition_out_point) { + if (s.in() <= transition_in_point + && s.out() >= transition_out_point) { return true; } } @@ -208,33 +209,33 @@ bool Track::IsTransitionSelected(Transition *t) return false; } -void Track::TidySelections() +void Track::SelectClip(Clip* c) { - for (int i=0;i ss.out) { - // do nothing - } else if (s.in >= ss.in && s.out <= ss.out) { - remove = true; - } else if (s.in <= ss.out && s.out > ss.out) { - ss.out = s.out; - remove = true; - } else if (s.out >= ss.in && s.in < ss.in) { - ss.in = s.in; - remove = true; - } - if (remove) { - areas.removeAt(i); - i--; - break; - } - } - } + selections_.append(Selection(c->timeline_in(), c->timeline_out(), this)); +} + +void Track::SelectAll() +{ + ClearSelections(); + + // Select every clip + for (int i=0;itimeline_in() <= point + && c->timeline_out() > point) { + SelectClip(c); } } } @@ -244,6 +245,38 @@ void Track::ClearSelections() selections_.clear(); } +void Track::DeselectArea(long in, long out) +{ + int selection_count = selections_.size(); + for (int i=0;i= in && s.out() <= out) { + // whole selection is in deselect area + selections_.removeAt(i); + i--; + selection_count--; + } else if (s.in() < in && s.out() > out) { + // middle of selection is in deselect area + Selection new_sel(out, s.out(), s.track()); + selections_.append(new_sel); + + s.set_out(in); + } else if (s.in() < in && s.out() > in) { + // only out point is in deselect area + s.set_out(in); + } else if (s.in() < out && s.out() > out) { + // only in point is in deselect area + s.set_in(out); + } + } +} + +QVector Track::Selections() +{ + return selections_; +} + long Track::GetEndFrame() { long end_frame = 0; diff --git a/timeline/track.h b/timeline/track.h index e3d642e8f..168a69a91 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -68,8 +68,16 @@ public: bool IsClipSelected(Clip* clip, bool containing = true); bool IsTransitionSelected(Transition* t); + void DeleteArea(ComboAction *ca, const Selection& s); + void DeleteArea(ComboAction *ca, long in, long out); + + void SelectClip(Clip *c); + void SelectAll(); + void SelectAtPoint(long point); void TidySelections(); void ClearSelections(); + void DeselectArea(long in, long out); + QVector Selections(); long GetEndFrame();