/***
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;