more refactoring and timeline rewriting
This commit is contained in:
@@ -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<Selection> delete_areas;
|
||||
QVector<ClipPtr> pasted_clips;
|
||||
long paste_start = LONG_MAX;
|
||||
long paste_end = LONG_MIN;
|
||||
|
||||
for (int i=0;i<olive::clipboard.size();i++) {
|
||||
ClipPtr c = std::static_pointer_cast<Clip>(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.size();i++) {
|
||||
// these indices should correspond
|
||||
ClipPtr oc = std::static_pointer_cast<Clip>(clipboard.at(i));
|
||||
|
||||
for (int j=0;j<oc->linked.size();j++) {
|
||||
for (int k=0;k<clipboard.size();k++) { // find clip with that ID
|
||||
ClipPtr comp = std::static_pointer_cast<Clip>(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<Clip*> selected_clips = olive::ActiveSequence->SelectedClips();
|
||||
|
||||
for (int i=0;i<selected_clips.size();i++) {
|
||||
Clip* c = selected_clips.at(i);
|
||||
|
||||
for (int j=0;j<clipboard.size();j++) {
|
||||
EffectPtr e = std::static_pointer_cast<Effect>(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;k<c->effects.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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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<typename T>
|
||||
const T &clamp(const T &val, T &min, T &max)
|
||||
{
|
||||
return qMax(qMin(max, val), min);
|
||||
}
|
||||
|
||||
@@ -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 <typename T>
|
||||
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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
+4
-16
@@ -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.size();i++) {
|
||||
ClipPtr c = std::static_pointer_cast<Clip>(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<items.size();j++) {
|
||||
Media* m = item_to_media(items.at(j));
|
||||
if (delete_clips_in_clipboard_with_media(ca, m)) deleted = true;
|
||||
if (olive::clipboard.DeleteClipsWithMedia(ca, m)) {
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
if (deleted) {
|
||||
olive::UndoStack.push(ca);
|
||||
|
||||
+31
-409
@@ -142,10 +142,6 @@ void Timeline::Retranslate() {
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
void ripple_clips(ComboAction* ca, Sequence* s, long point, long length, const QVector<int>& 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;i<olive::ActiveSequence->clips.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;i<olive::ActiveSequence->clips.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;i<olive::ActiveSequence->clips.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<Selection> 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<Selection> 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<Selection>& 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;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
ripple_point = qMin(ripple_point, s.in);
|
||||
ripple_length = qMin(ripple_length, s.out - s.in);
|
||||
}
|
||||
|
||||
// it feels a little more intuitive with this here
|
||||
ripple_point++;
|
||||
|
||||
bool can_ripple = true;
|
||||
for (int i=0;i<olive::ActiveSequence->clips.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;j<selections.size();j++) {
|
||||
const Selection& s = 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)) {
|
||||
deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!deleted) {
|
||||
for (int j=0;j<olive::ActiveSequence->clips.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;i<track_heights.size();i++) {
|
||||
if (track_heights.at(i).index == track) {
|
||||
return track_heights.at(i).height;
|
||||
}
|
||||
}
|
||||
return olive::timeline::kTrackDefaultHeight;
|
||||
}
|
||||
|
||||
void Timeline::SetTrackHeight(int track, int height) {
|
||||
for (int i=0;i<track_heights.size();i++) {
|
||||
if (track_heights.at(i).index == track) {
|
||||
track_heights[i].height = height;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we don't have a track height, so set a new one
|
||||
TimelineTrackHeight t;
|
||||
t.index = track;
|
||||
t.height = height;
|
||||
track_heights.append(t);
|
||||
}
|
||||
|
||||
void Timeline::ChangeTrackHeightUniformly(int diff) {
|
||||
// get range of tracks currently active
|
||||
int min_track, max_track;
|
||||
olive::ActiveSequence->getTrackLimits(&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;i<olive::ActiveSequence->clips.size();i++) {
|
||||
ClipPtr c = olive::ActiveSequence->clips.at(i);
|
||||
if (c != nullptr) {
|
||||
for (int j=0;j<olive::ActiveSequence->selections.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.size();i++) {
|
||||
// initialize all timeline_ins to 0 or offsets of
|
||||
ClipPtr c = std::static_pointer_cast<Clip>(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<Selection> delete_areas;
|
||||
QVector<ClipPtr> pasted_clips;
|
||||
long paste_start = LONG_MAX;
|
||||
long paste_end = LONG_MIN;
|
||||
for (int i=0;i<clipboard.size();i++) {
|
||||
ClipPtr c = std::static_pointer_cast<Clip>(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.size();i++) {
|
||||
// these indices should correspond
|
||||
ClipPtr oc = std::static_pointer_cast<Clip>(clipboard.at(i));
|
||||
|
||||
for (int j=0;j<oc->linked.size();j++) {
|
||||
for (int k=0;k<clipboard.size();k++) { // find clip with that ID
|
||||
ClipPtr comp = std::static_pointer_cast<Clip>(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<Clip*> selected_clips = olive::ActiveSequence->SelectedClips();
|
||||
|
||||
for (int i=0;i<selected_clips.size();i++) {
|
||||
Clip* c = selected_clips.at(i);
|
||||
|
||||
for (int j=0;j<clipboard.size();j++) {
|
||||
EffectPtr e = std::static_pointer_cast<Effect>(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;k<c->effects.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;i<olive::ActiveSequence->clips.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;j<c->get_markers().size();j++) {
|
||||
if (snap_to_point(c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(), l)) {
|
||||
return true;
|
||||
}
|
||||
QVector<Clip*> all_clips = olive::ActiveSequence->GetAllClips();
|
||||
for (int i=0;i<all_clips.size();i++) {
|
||||
|
||||
Clip* c = all_clips.at(i);
|
||||
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;j<c->get_markers().size();j++) {
|
||||
if (snap_to_point(c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(), l)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<int>& ignore = QVector<int>());
|
||||
|
||||
|
||||
|
||||
class Timeline : public Panel
|
||||
@@ -95,7 +93,6 @@ public:
|
||||
// selecting functions
|
||||
bool selecting;
|
||||
int selection_offset;
|
||||
void delete_selection(QVector<Selection> &selections, bool ripple);
|
||||
void select_all();
|
||||
bool rect_select_init;
|
||||
bool rect_select_proc;
|
||||
|
||||
+56
-7
@@ -24,11 +24,60 @@
|
||||
#include "effects/effect.h"
|
||||
#include "effects/transition.h"
|
||||
|
||||
int clipboard_type = CLIPBOARD_TYPE_CLIP;
|
||||
QVector<VoidPtr> clipboard;
|
||||
QVector<TransitionPtr> 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_.size();i++) {
|
||||
Clip* c = std::static_pointer_cast<Clip>(clipboard_.at(i)).get();
|
||||
|
||||
if (c->media() == m) {
|
||||
ca->append(new RemoveClipsFromClipboard(i-delete_count));
|
||||
delete_count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (delete_count > 0);
|
||||
}
|
||||
|
||||
Clipboard olive::clipboard;
|
||||
|
||||
+29
-8
@@ -23,16 +23,37 @@
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include <effects/transition.h>
|
||||
|
||||
#define CLIPBOARD_TYPE_CLIP 0
|
||||
#define CLIPBOARD_TYPE_EFFECT 1
|
||||
#include "effects/transition.h"
|
||||
|
||||
using VoidPtr = std::shared_ptr<void>;
|
||||
|
||||
extern int clipboard_type;
|
||||
extern QVector<TransitionPtr> clipboard_transitions;
|
||||
extern QVector<VoidPtr> 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<TransitionPtr> clipboard_transitions_;
|
||||
QVector<VoidPtr> clipboard_;
|
||||
};
|
||||
|
||||
namespace olive {
|
||||
extern Clipboard clipboard;
|
||||
}
|
||||
|
||||
#endif // CLIPBOARD_H
|
||||
|
||||
@@ -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<Selection &> selections)
|
||||
{
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
Selection& s = selections[i];
|
||||
for (int j=0;j<selections.size();j++) {
|
||||
if (i != j) {
|
||||
Selection& ss = selections[j];
|
||||
if (s.track() == ss.track()) {
|
||||
bool remove = false;
|
||||
if (s.in() < ss.in() && s.out() > 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-6
@@ -21,14 +21,29 @@
|
||||
#ifndef SELECTION_H
|
||||
#define SELECTION_H
|
||||
|
||||
struct Selection {
|
||||
long in;
|
||||
long out;
|
||||
#include <QVector>
|
||||
|
||||
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<Selection&> selections);
|
||||
|
||||
private:
|
||||
long in_;
|
||||
long out_;
|
||||
Track* track_;
|
||||
|
||||
bool trim_in_;
|
||||
};
|
||||
|
||||
#endif // SELECTION_H
|
||||
|
||||
+168
-12
@@ -23,6 +23,7 @@
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "project/clipboard.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
Sequence::Sequence() :
|
||||
@@ -176,9 +177,54 @@ QVector<Clip *> Sequence::SelectedClips(bool containing)
|
||||
return selected_clips;
|
||||
}
|
||||
|
||||
void Sequence::DeleteAreas(ComboAction* ca, QVector<Selection>& areas, bool deselect_areas)
|
||||
void Sequence::DeleteInToOut(bool ripple)
|
||||
{
|
||||
clean_up_selections(areas);
|
||||
if (using_workarea) {
|
||||
|
||||
QVector<Selection> areas_to_delete;
|
||||
|
||||
for (int i=0;i<track_lists_.size();i++) {
|
||||
|
||||
TrackList* tl = track_lists_.at(i);
|
||||
|
||||
for (int j=0;j<tl->TrackCount();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<int> &ignore)
|
||||
{
|
||||
ca->append(new RippleAction(this, point, length, ignore));
|
||||
}
|
||||
|
||||
void Sequence::ChangeTrackHeightsRelatively(int diff)
|
||||
{
|
||||
for (int i=0;i<track_lists_.size();i++) {
|
||||
TrackList* tl = track_lists_.at(i);
|
||||
|
||||
for (int j=0;j<tl->TrackCount();j++) {
|
||||
Track* t = tl->TrackAt(j);
|
||||
|
||||
t->set_height(t->height() + diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::DeleteAreas(ComboAction* ca, QVector<Selection> 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<Selection>& areas, bool dese
|
||||
const Selection& s = areas.at(i);
|
||||
for (int j=0;j<all_clips.size();j++) {
|
||||
Clip* c = all_clips.at(j);
|
||||
if (c != nullptr && c->track() == 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;j<track_lists_.size();j++) {
|
||||
TrackList* tl = track_lists_.at(j);
|
||||
|
||||
for (int i=0;i<tl->TrackCount();i++) {
|
||||
tl->TrackAt(i)->SelectAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::SelectAtPlayhead()
|
||||
{
|
||||
for (int j=0;j<track_lists_.size();j++) {
|
||||
TrackList* tl = track_lists_.at(j);
|
||||
|
||||
for (int i=0;i<tl->TrackCount();i++) {
|
||||
tl->TrackAt(i)->SelectAtPoint(playhead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::ClearSelections()
|
||||
{
|
||||
for (int j=0;j<track_lists_.size();j++) {
|
||||
@@ -381,6 +449,94 @@ void Sequence::ClearSelections()
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::AddSelectionsToClipboard(bool delete_originals)
|
||||
{
|
||||
olive::clipboard.Clear();
|
||||
olive::clipboard.SetType(Clipboard::CLIPBOARD_TYPE_CLIP);
|
||||
|
||||
QVector<Clip*> original_clips;
|
||||
QVector<ClipPtr> copied_clips;
|
||||
|
||||
long min_in = LONG_MAX;
|
||||
|
||||
QVector<Selection> selections = Selections();
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
|
||||
// Get the clips contained in the track this selection pertains to
|
||||
QVector<Clip*> track_clips = s.track()->GetAllClips();
|
||||
|
||||
for (int j=0;j<track_clips.size();j++) {
|
||||
|
||||
Clip* c = track_clips.at(j);
|
||||
|
||||
// Check if this selection contains this clip
|
||||
if (!(c->timeline_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;i<copied_clips.size();i++) {
|
||||
Clip* copy = copied_clips.at(i).get();
|
||||
|
||||
copy->set_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<Selection> Sequence::Selections()
|
||||
{
|
||||
QVector<Selection> selections;
|
||||
|
||||
for (int j=0;j<track_lists_.size();j++) {
|
||||
TrackList* tl = track_lists_.at(j);
|
||||
|
||||
for (int i=0;i<tl->TrackCount();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);
|
||||
|
||||
+10
-1
@@ -65,7 +65,12 @@ public:
|
||||
QVector<Clip*> SelectedClips(bool containing = true);
|
||||
//QVector<int> SelectedClipIndexes();
|
||||
|
||||
void DeleteAreas();
|
||||
void DeleteAreas(ComboAction* ca, QVector<Selection> areas, bool deselect_areas);
|
||||
void DeleteInToOut(bool ripple);
|
||||
|
||||
void Ripple(ComboAction *ca, long point, long length, const QVector<int>& ignore = QVector<int>());
|
||||
|
||||
void ChangeTrackHeightsRelatively(int diff);
|
||||
|
||||
bool SplitAllClipsAtPoint(ComboAction *ca, long point);
|
||||
void SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector<long> 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<Selection> Selections();
|
||||
|
||||
long playhead;
|
||||
|
||||
|
||||
+65
-32
@@ -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;i<selections_.size();i++) {
|
||||
const Selection& s = selections_.at(i);
|
||||
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)))) {
|
||||
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<selections_.size();i++) {
|
||||
const Selection& s = selections_.at(i);
|
||||
if (s.in <= transition_in_point
|
||||
&& s.out >= 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<areas.size();i++) {
|
||||
Selection& s = areas[i];
|
||||
for (int j=0;j<areas.size();j++) {
|
||||
if (i != j) {
|
||||
Selection& ss = areas[j];
|
||||
if (s.track == ss.track) {
|
||||
bool remove = false;
|
||||
if (s.in < ss.in && s.out > 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;i<clips_.size();i++) {
|
||||
SelectClip(clips_.at(i).get());
|
||||
}
|
||||
}
|
||||
|
||||
void Track::SelectAtPoint(long point)
|
||||
{
|
||||
ClearSelections();
|
||||
|
||||
// Select every clip that this point is within
|
||||
for (int i=0;i<clips_.size();i++) {
|
||||
|
||||
Clip* c = clips_.at(i).get();
|
||||
|
||||
if (c->timeline_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<selection_count;i++) {
|
||||
Selection& s = selections_[i];
|
||||
|
||||
if (s.in() >= 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<Selection> Track::Selections()
|
||||
{
|
||||
return selections_;
|
||||
}
|
||||
|
||||
long Track::GetEndFrame()
|
||||
{
|
||||
long end_frame = 0;
|
||||
|
||||
@@ -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<Selection> Selections();
|
||||
|
||||
long GetEndFrame();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user