/*** Olive - Non-Linear Video Editor Copyright (C) 2019 Olive Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #include "sequence.h" #include #include "panels/panels.h" #include "global/debug.h" Sequence::Sequence() : playhead(0), using_workarea(false), workarea_in(0), workarea_out(0), wrapper_sequence(false) { // Set up tracks track_lists_.resize(Track::kTypeCount); for (int i=0;i(i)); } } SequencePtr Sequence::copy() { SequencePtr s = std::make_shared(); s->name = QCoreApplication::translate("Sequence", "%1 (copy)").arg(name); s->width = width; s->height = height; s->frame_rate = frame_rate; s->audio_frequency = audio_frequency; s->audio_layout = audio_layout; // deep copy all of the sequence's clips for (int i=0;itrack_lists_[i] = track_lists_.at(i)->copy(s.get()); } // copy all of the sequence's markers s->markers = markers; return s; } void Sequence::Save(QXmlStreamWriter &stream) { // Provide unique IDs for each Clip QVector all_clips = GetAllClips(); for (int i=0;iload_id = i; } stream.writeStartElement("sequence"); stream.writeAttribute("id", QString::number(save_id)); stream.writeAttribute("name", name); stream.writeAttribute("width", QString::number(width)); stream.writeAttribute("height", QString::number(height)); stream.writeAttribute("framerate", QString::number(frame_rate, 'f', 10)); stream.writeAttribute("afreq", QString::number(audio_frequency)); stream.writeAttribute("alayout", QString::number(audio_layout)); if (this == olive::ActiveSequence.get()) { stream.writeAttribute("open", "1"); } stream.writeAttribute("workarea", QString::number(using_workarea)); stream.writeAttribute("workareaIn", QString::number(workarea_in)); stream.writeAttribute("workareaOut", QString::number(workarea_out)); QVector transition_save_cache; QVector transition_clip_save_cache; for (int j=0;jSave(stream); } for (int j=0;jTrackCount();i++) { end_frame = qMax(track_list->TrackAt(i)->GetEndFrame(), end_frame); } } return end_frame; } QVector Sequence::GetAllClips() { QVector all_clips; for (int j=0;jTrackCount();i++) { all_clips.append(track_list->TrackAt(i)->GetAllClips()); } } return all_clips; } TrackList *Sequence::GetTrackList(Track::Type type) { return track_lists_.at(type); } void Sequence::Close() { QVector all_clips = GetAllClips(); for (int i=0;iClose(true); } } void Sequence::RefreshClipsUsingMedia(Media *m) { QVector all_clips = GetAllClips(); for (int i=0;imedia() == m) { c->Close(true); c->refresh(); } } } QVector Sequence::SelectedClips(bool containing) { QVector selected_clips; for (int i=0;iTrackCount();j++) { Track* t = tl->TrackAt(j); selected_clips.append(t->GetAllClips()); } } return selected_clips; } void Sequence::DeleteAreas(ComboAction* ca, QVector& areas, bool deselect_areas) { clean_up_selections(areas); panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); QVector pre_clips; QVector post_clips; QVector all_clips = GetAllClips(); for (int i=0;itrack() == 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) { // clips falls entirely within deletion area ca->append(new DeleteClipAction(c)); } 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); pre_clips.append(j); post_clips.append(post); } 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()); if (c->closing_transition != nullptr) { 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) { // 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()); if (c->opening_transition != nullptr) { 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()))); } } } } } } // deselect selected clip areas if (deselect_areas) { QVector area_copy = areas; for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_clips)); } bool Sequence::SplitAllClipsAtPoint(ComboAction *ca, long point) { bool split = false; QVector all_clips = GetAllClips(); for (int j=0;jIsActiveAt(point)) { SplitClipAtPositions(ca, c, {point}, true); split = true; } } return split; } void Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector positions, bool relink) { // Add the clip and each of its links to the pre_splits array QVector pre_splits; pre_splits.append(clip); if (relink) { for (int i=0;ilinked.size();i++) { pre_splits.append(clip->linked.at(i)); } } std::sort(positions.begin(), positions.end()); // Remove any duplicate positions for (int i=1;i > post_splits(positions.size()); for (int i=positions.size()-1;i>=0;i--) { post_splits[i].resize(pre_splits.size()); for (int j=0;jset_timeline_out(positions.at(i+1)); } } } for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_splits[i])); } } /* QVector Sequence::SelectedClipIndexes() { QVector selected_clips; for (int i=0;i clips = GetAllClips(); for (int i=0;iIsActiveAt(playhead) && IsClipSelected(c, true)) { // This clip is selected and currently active - we'll use this for gizmos if (!c->effects.isEmpty()) { // find which effect has gizmos selected, or default to the first gizmo effect we find if there is // none selected for (int j=0;jeffects.size();j++) { Effect* e = c->effects.at(j).get(); // retrieve gizmo data from effect if (e->are_gizmos_enabled()) { if (gizmo_ptr == nullptr) { gizmo_ptr = e; } if (panel_effect_controls->IsEffectSelected(e)) { gizmo_ptr = e; break; } } } } if (gizmo_ptr != nullptr) { break; } } } return gizmo_ptr; } void Sequence::ClearSelections() { for (int j=0;jTrackCount();i++) { tl->TrackAt(i)->ClearSelections(); } } } ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame) { return SplitClip(ca, transitions, pre, frame, frame); } ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame, long post_in) { if (pre == nullptr) { return nullptr; } if (pre->timeline_in() < frame && pre->timeline_out() > frame) { // duplicate clip without duplicating its transitions, we'll restore them later ClipPtr post = pre->copy(pre->track()); long new_clip_length = frame - pre->timeline_in(); post->set_timeline_in(post_in); post->set_clip_in(pre->clip_in() + (post->timeline_in() - pre->timeline_in())); pre->move(ca, pre->timeline_in(), frame, pre->clip_in(), pre->track(), false); if (transitions) { // check if this clip has a closing transition if (pre->closing_transition != nullptr) { // if so, move closing transition to the post clip post->closing_transition = pre->closing_transition; // and set the original clip's closing transition to nothing ca->append(new SetPointer(reinterpret_cast(&pre->closing_transition), nullptr)); // and set the transition's reference to the post clip if (post->closing_transition->parent_clip == pre) { ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->parent_clip), post.get())); } if (post->closing_transition->secondary_clip == pre) { ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->secondary_clip), post.get())); } // and make sure it's at the correct size to the closing clip if (post->closing_transition != nullptr && post->closing_transition->get_true_length() > post->length()) { ca->append(new ModifyTransitionCommand(post->closing_transition, post->length())); post->closing_transition->set_length(post->length()); } } // we're keeping the opening clip, so ensure that's a correct size too if (pre->opening_transition != nullptr && pre->opening_transition->get_true_length() > new_clip_length) { ca->append(new ModifyTransitionCommand(pre->opening_transition, new_clip_length)); } } return post; } else if (frame == pre->timeline_in() && pre->opening_transition != nullptr && pre->opening_transition->secondary_clip != nullptr) { // special case for shared transitions to split it into two // set transition to single-clip mode ca->append(new SetPointer(reinterpret_cast(&pre->opening_transition->secondary_clip), nullptr)); // clone transition for other clip ca->append(new AddTransitionCommand(nullptr, pre->opening_transition->secondary_clip, pre->opening_transition, nullptr, 0) ); } return nullptr; } // static variable for the currently active sequence SequencePtr olive::ActiveSequence = nullptr;