added many linking features
This commit is contained in:
@@ -4,6 +4,21 @@ extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
Media::Media() {
|
||||
|
||||
}
|
||||
|
||||
Media::~Media() {
|
||||
for (int i=0;i<video_tracks.size();i++) {
|
||||
delete video_tracks.at(i);
|
||||
}
|
||||
for (int i=0;i<audio_tracks.size();i++) {
|
||||
delete audio_tracks.at(i);
|
||||
}
|
||||
video_tracks.clear();
|
||||
audio_tracks.clear();
|
||||
}
|
||||
|
||||
long Media::get_length_in_frames(float frame_rate) {
|
||||
return ceil((float) length / (float) AV_TIME_BASE * frame_rate);
|
||||
}
|
||||
|
||||
+10
-2
@@ -5,6 +5,7 @@
|
||||
#include <QVector>
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include <QMutex>
|
||||
|
||||
struct Sequence;
|
||||
|
||||
@@ -13,16 +14,23 @@ struct MediaStream {
|
||||
int video_width;
|
||||
int video_height;
|
||||
bool infinite_length;
|
||||
|
||||
// preview thumbnail/waveform
|
||||
QPixmap* preview;
|
||||
QMutex preview_lock;
|
||||
};
|
||||
|
||||
struct Media
|
||||
{
|
||||
Media();
|
||||
~Media();
|
||||
|
||||
QString url;
|
||||
QString name;
|
||||
bool is_sequence;
|
||||
int64_t length;
|
||||
QVector<MediaStream> video_tracks;
|
||||
QVector<MediaStream> audio_tracks;
|
||||
QVector<MediaStream*> video_tracks;
|
||||
QVector<MediaStream*> audio_tracks;
|
||||
Sequence* sequence;
|
||||
int save_id;
|
||||
long get_length_in_frames(float frame_rate);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "previewgenerator.h"
|
||||
|
||||
PreviewGenerator::PreviewGenerator(QWidget* parent) : QThread(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PreviewGenerator::run() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef PREVIEWGENERATOR_H
|
||||
#define PREVIEWGENERATOR_H
|
||||
|
||||
#include <QThread>
|
||||
|
||||
class PreviewGenerator : public QThread
|
||||
{
|
||||
public:
|
||||
PreviewGenerator(QWidget* parent = 0);
|
||||
void run() override;
|
||||
};
|
||||
|
||||
#endif // PREVIEWGENERATOR_H
|
||||
@@ -265,3 +265,10 @@ void MainWindow::on_actionSave_Project_As_triggered()
|
||||
{
|
||||
save_project_as();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionDeselect_All_triggered()
|
||||
{
|
||||
if (panel_timeline->focused()) {
|
||||
panel_timeline->deselect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,8 @@ private slots:
|
||||
|
||||
void on_actionSave_Project_As_triggered();
|
||||
|
||||
void on_actionDeselect_All_triggered();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
void setup_layout();
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
<addaction name="actionRipple_Delete"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSelect_All"/>
|
||||
<addaction name="actionDeselect_All"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSplit_at_Playhead"/>
|
||||
</widget>
|
||||
@@ -297,6 +298,11 @@
|
||||
<string>Ctrl+K</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDeselect_All">
|
||||
<property name="text">
|
||||
<string>Deselect All</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
||||
@@ -52,7 +52,8 @@ SOURCES += \
|
||||
effects/effects.cpp \
|
||||
playback/cacher.cpp \
|
||||
io/exportthread.cpp \
|
||||
ui/timelineheader.cpp
|
||||
ui/timelineheader.cpp \
|
||||
io/previewgenerator.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
@@ -80,7 +81,8 @@ HEADERS += \
|
||||
playback/cacher.h \
|
||||
io/exportthread.h \
|
||||
ui/timelinetools.h \
|
||||
ui/timelineheader.h
|
||||
ui/timelineheader.h \
|
||||
io/previewgenerator.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.6.1, 2018-06-10T11:24:49. -->
|
||||
<!-- Written by QtCreator 4.6.1, 2018-06-13T22:15:23. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
+11
-7
@@ -109,15 +109,19 @@ Media* Project::import_file(QString file) {
|
||||
if (avcodec_find_decoder(pFormatCtx->streams[i]->codecpar->codec_id) == NULL) {
|
||||
qDebug() << "[ERROR] Unsupported codec in stream %d.\n";
|
||||
} else {
|
||||
MediaStream* ms = new MediaStream();
|
||||
ms->file_index = i;
|
||||
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
qDebug() << "[WARNING] INFINITE_LENGTH calculation is inaccurate in this build\n";
|
||||
// TODO BETTER infinite length calculator
|
||||
bool infinite_length = (pFormatCtx->streams[i]->nb_frames == 0);
|
||||
// bool infinite_length = false;
|
||||
|
||||
m->video_tracks.append({i, pFormatCtx->streams[i]->codecpar->width, pFormatCtx->streams[i]->codecpar->height, infinite_length});
|
||||
ms->video_width = pFormatCtx->streams[i]->codecpar->width;
|
||||
ms->video_height = pFormatCtx->streams[i]->codecpar->height;
|
||||
ms->infinite_length = infinite_length;
|
||||
m->video_tracks.append(ms);
|
||||
} else if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
m->audio_tracks.append({i, 0, 0, false});
|
||||
m->audio_tracks.append(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,16 +359,16 @@ void Project::load_project() {
|
||||
int stream_index = stream.text().toInt();
|
||||
bool found = false;
|
||||
for (int i=0;i<temp_clip->media->video_tracks.size();i++) {
|
||||
if (temp_clip->media->video_tracks.at(i).file_index == stream_index) {
|
||||
temp_clip->media_stream = &temp_clip->media->video_tracks[i];
|
||||
if (temp_clip->media->video_tracks.at(i)->file_index == stream_index) {
|
||||
temp_clip->media_stream = temp_clip->media->video_tracks[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
for (int i=0;i<temp_clip->media->audio_tracks.size();i++) {
|
||||
if (temp_clip->media->audio_tracks.at(i).file_index == stream_index) {
|
||||
temp_clip->media_stream = &temp_clip->media->audio_tracks[i];
|
||||
if (temp_clip->media->audio_tracks.at(i)->file_index == stream_index) {
|
||||
temp_clip->media_stream = temp_clip->media->audio_tracks[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
+141
-17
@@ -183,14 +183,14 @@ void Timeline::delete_selection(bool ripple_delete) {
|
||||
long ripple_point = selections.at(0).in;
|
||||
long ripple_length = selections.at(0).out - selections.at(0).in;
|
||||
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
sequence->delete_area(s.in, s.out, s.track);
|
||||
if (ripple_delete) {
|
||||
if (ripple_point > s.in) ripple_point = s.in;
|
||||
if (ripple_length > s.out - s.in) ripple_length = s.out - s.in;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
if (ripple_delete) {
|
||||
if (ripple_point > s.in) ripple_point = s.in;
|
||||
if (ripple_length > s.out - s.in) ripple_length = s.out - s.in;
|
||||
}
|
||||
}
|
||||
delete_areas_and_relink(selections);
|
||||
selections.clear();
|
||||
|
||||
if (ripple_delete) {
|
||||
@@ -293,8 +293,7 @@ void Timeline::on_pushButton_5_clicked()
|
||||
zoom_out();
|
||||
}
|
||||
|
||||
bool Timeline::is_clip_selected(int clip_index) {
|
||||
Clip* clip = sequence->get_clip(clip_index);
|
||||
bool Timeline::is_clip_selected(Clip* clip) {
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
if (clip->track == s.track && clip->timeline_in >= s.in && clip->timeline_out <= s.out) {
|
||||
@@ -336,6 +335,86 @@ void Timeline::on_snappingButton_toggled(bool checked)
|
||||
snapping = checked;
|
||||
}
|
||||
|
||||
void Timeline::split_clip_and_relink(Clip* pre_clip, long frame, bool relink) {
|
||||
Clip* post = sequence->split_clip(pre_clip, frame);
|
||||
|
||||
// if alt is not down, split clips links too
|
||||
if (relink) {
|
||||
QVector<Clip*> splits;
|
||||
splits.append(post);
|
||||
|
||||
// find linked clips of old clip
|
||||
for (int i=0;i<pre_clip->linked.size();i++) {
|
||||
Clip* link = pre_clip->linked.at(i);
|
||||
if (!panel_timeline->is_clip_selected(link)) {
|
||||
Clip* s = sequence->split_clip(link, frame);
|
||||
if (s != NULL) splits.append(s);
|
||||
}
|
||||
}
|
||||
|
||||
// re-link all new clips
|
||||
for (int i=0;i<splits.size();i++) {
|
||||
Clip* c = splits.at(i);
|
||||
for (int j=0;j<splits.size();j++) {
|
||||
Clip* cc = splits.at(j);
|
||||
if (c != cc) {
|
||||
c->linked.append(cc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::delete_areas_and_relink(QVector<Selection> areas) {
|
||||
QVector<Clip*> pre_clips;
|
||||
QVector<Clip*> post_clips;
|
||||
|
||||
for (int i=0;i<areas.size();i++) {
|
||||
const Selection& s = areas.at(i);
|
||||
for (int j=0;j<sequence->clip_count();j++) {
|
||||
Clip* c = sequence->get_clip(j);
|
||||
if (c->track == s.track && !c->undeletable) {
|
||||
if (c->timeline_in >= s.in && c->timeline_out <= s.out) {
|
||||
// clips falls entirely within deletion area
|
||||
sequence->delete_clip(j);
|
||||
j--;
|
||||
} else if (c->timeline_in < s.in && c->timeline_out > s.out) {
|
||||
// middle of clip is within deletion area
|
||||
|
||||
// duplicate clip
|
||||
Clip* post = c->copy();
|
||||
|
||||
c->timeline_out = s.in;
|
||||
post->timeline_in = s.out;
|
||||
post->clip_in = c->clip_in + c->getLength() + (s.out - s.in);
|
||||
|
||||
pre_clips.append(c);
|
||||
post_clips.append(post);
|
||||
|
||||
sequence->add_clip(post);
|
||||
} else if (c->timeline_in < s.in && c->timeline_out > s.in) {
|
||||
// only out point is in deletion area
|
||||
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->clip_in += s.out - c->timeline_in;
|
||||
c->timeline_in = s.out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i=0;i<pre_clips.size();i++) {
|
||||
Clip* c = pre_clips.at(i);
|
||||
for (int j=0;j<pre_clips.size();j++) {
|
||||
for (int k=0;k<c->linked.size();k++) {
|
||||
if (c->linked.at(k) == pre_clips.at(j)) {
|
||||
post_clips.at(i)->linked.append(post_clips.at(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::copy(bool del) {
|
||||
bool cleared = false;
|
||||
bool copied = false;
|
||||
@@ -388,12 +467,18 @@ void Timeline::copy(bool del) {
|
||||
|
||||
void Timeline::paste() {
|
||||
if (clip_clipboard.size() > 0) {
|
||||
QVector<Selection> delete_areas;
|
||||
for (int i=0;i<clip_clipboard.size();i++) {
|
||||
Clip* c = clip_clipboard.at(i);
|
||||
delete_areas.append({c->timeline_in + playhead, c->timeline_out + playhead, c->track});
|
||||
}
|
||||
delete_areas_and_relink(delete_areas);
|
||||
for (int i=0;i<clip_clipboard.size();i++) {
|
||||
Clip* c = clip_clipboard.at(i);
|
||||
sequence->delete_area(c->timeline_in + playhead, c->timeline_out + playhead, c->track);
|
||||
Clip* cc = c->copy();
|
||||
cc->timeline_in += playhead;
|
||||
cc->timeline_out += playhead;
|
||||
cc->sequence = sequence;
|
||||
sequence->add_clip(cc);
|
||||
}
|
||||
redraw_all_clips();
|
||||
@@ -402,16 +487,47 @@ void Timeline::paste() {
|
||||
|
||||
bool Timeline::split_selection() {
|
||||
bool split = false;
|
||||
|
||||
// temporary relinking vectors
|
||||
QVector<Clip*> pre_splits;
|
||||
QVector<Clip*> post_splits;
|
||||
|
||||
// find clips within selection and split
|
||||
for (int j=0;j<sequence->clip_count();j++) {
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
const Selection& s = selections.at(i);
|
||||
if (s.track == sequence->get_clip(j)->track) {
|
||||
sequence->split_clip(j, s.in);
|
||||
sequence->split_clip(j, s.out);
|
||||
Clip* clip = sequence->get_clip(j);
|
||||
if (s.track == clip->track) {
|
||||
Clip* post_a = sequence->split_clip(clip, s.in);
|
||||
Clip* post_b = sequence->split_clip(clip, s.out);
|
||||
|
||||
if (post_a != NULL) {
|
||||
pre_splits.append(clip);
|
||||
post_splits.append(post_a);
|
||||
}
|
||||
if (post_b != NULL) {
|
||||
pre_splits.append(clip);
|
||||
post_splits.append(post_b);
|
||||
}
|
||||
|
||||
split = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// relink after splitting
|
||||
for (int i=0;i<pre_splits.size();i++) {
|
||||
// see how many "pre"s were linked and relink the "post"s
|
||||
Clip* pre = pre_splits.at(i);
|
||||
for (int j=0;j<pre_splits.size();j++) {
|
||||
for (int k=0;k<pre->linked.size();k++) {
|
||||
if (pre->linked.at(k) == pre_splits.at(j)) {
|
||||
post_splits.at(i)->linked.append(post_splits.at(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
@@ -421,8 +537,10 @@ void Timeline::split_at_playhead() {
|
||||
if (selections.size() > 0) {
|
||||
// see if whole clips are selected
|
||||
for (int j=0;j<sequence->clip_count();j++) {
|
||||
if (is_clip_selected(j)) {
|
||||
sequence->split_clip(j, playhead);
|
||||
Clip* clip = sequence->get_clip(j);
|
||||
if (is_clip_selected(clip)) {
|
||||
// should this relink?
|
||||
sequence->split_clip(clip, playhead);
|
||||
split_selected = true;
|
||||
}
|
||||
}
|
||||
@@ -436,7 +554,8 @@ void Timeline::split_at_playhead() {
|
||||
// if nothing was selected or no selections fell within playhead, simply split at playhead
|
||||
if (!split_selected) {
|
||||
for (int j=0;j<sequence->clip_count();j++) {
|
||||
sequence->split_clip(j, playhead);
|
||||
// always relinks
|
||||
split_clip_and_relink(sequence->get_clip(j), playhead, true);
|
||||
split_selected = true;
|
||||
}
|
||||
}
|
||||
@@ -465,6 +584,11 @@ void Timeline::snap_to_clip(long* l) {
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::deselect() {
|
||||
selections.clear();
|
||||
repaint_timeline();
|
||||
}
|
||||
|
||||
long Timeline::getFrameFromScreenPoint(int x) {
|
||||
long f = round((float) x / zoom);
|
||||
if (f < 0) {
|
||||
|
||||
+4
-1
@@ -65,8 +65,11 @@ public:
|
||||
void redo();
|
||||
void copy(bool del);
|
||||
void paste();
|
||||
void deselect();
|
||||
bool split_selection();
|
||||
void split_at_playhead();
|
||||
void split_clip_and_relink(Clip* clip, long frame, bool relink);
|
||||
void delete_areas_and_relink(QVector<Selection> areas);
|
||||
void update_sequence();
|
||||
|
||||
int getScreenPointFromFrame(long frame);
|
||||
@@ -107,7 +110,7 @@ public:
|
||||
// selecting functions
|
||||
bool selecting;
|
||||
QVector<Selection> selections;
|
||||
bool is_clip_selected(int clip_index);
|
||||
bool is_clip_selected(Clip* clip);
|
||||
void delete_selection(bool ripple);
|
||||
void select_all();
|
||||
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ struct AVPacket;
|
||||
struct SwsContext;
|
||||
struct SwrContext;
|
||||
class QOpenGLTexture;
|
||||
class QPixmap;
|
||||
|
||||
struct ClipCache {
|
||||
AVFrame** frames;
|
||||
@@ -56,7 +57,7 @@ struct Clip
|
||||
|
||||
// other variables (should be "duplicated" in copy())
|
||||
QList<Effect*> effects;
|
||||
// QVector<Clip&> linkedClips;
|
||||
QVector<Clip*> linked;
|
||||
|
||||
// media handling
|
||||
AVFormatContext* formatCtx;
|
||||
|
||||
+15
-40
@@ -29,14 +29,15 @@ Clip* Sequence::get_clip(int i) {
|
||||
|
||||
void Sequence::delete_clip(int i) {
|
||||
// remove any potential link references to clip
|
||||
// for (size_t j=0;j<clip_count();j++) {
|
||||
// Clip* c = getClip(j);
|
||||
// for (size_t k=0;k<c->linkedClips.size();k++) {
|
||||
// if (c->linkedClips[k] == clips[i]) {
|
||||
// c->linkedClips.erase(c->linkedClips.begin()+k);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
for (int j=0;j<clip_count();j++) {
|
||||
Clip* c = get_clip(j);
|
||||
for (int k=0;k<c->linked.size();k++) {
|
||||
if (c->linked[k] == clips[i]) {
|
||||
c->linked.removeAt(k);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finally remove from vector
|
||||
delete clips.at(i);
|
||||
@@ -69,40 +70,11 @@ void Sequence::get_track_limits(int* video_tracks, int* audio_tracks) {
|
||||
if (audio_tracks != NULL) *audio_tracks = at;
|
||||
}
|
||||
|
||||
void Sequence::delete_area(long in, long out, int track) {
|
||||
for (int i=0;i<clip_count();i++) {
|
||||
Clip* c = get_clip(i);
|
||||
if (c->track == track && !c->undeletable) {
|
||||
if (c->timeline_in >= in && c->timeline_out <= out) {
|
||||
// clips falls entirely within deletion area
|
||||
delete_clip(i);
|
||||
i--;
|
||||
} else if (c->timeline_in < in && c->timeline_out > out) {
|
||||
// middle of clip is within deletion area
|
||||
//void Sequence::delete_area(long in, long out, int track) {
|
||||
|
||||
// duplicate clip
|
||||
Clip* pre = get_clip(i);
|
||||
Clip* post = pre->copy();
|
||||
//}
|
||||
|
||||
pre->timeline_out = in;
|
||||
post->timeline_in = out;
|
||||
post->clip_in = pre->clip_in + pre->getLength() + (out - in);
|
||||
|
||||
add_clip(post);
|
||||
} else if (c->timeline_in < in && c->timeline_out > in) {
|
||||
// only out point is in deletion area
|
||||
c->timeline_out = in;
|
||||
} else if (c->timeline_in < out && c->timeline_out > out) {
|
||||
// only in point is in deletion area
|
||||
c->clip_in += out - c->timeline_in;
|
||||
c->timeline_in = out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::split_clip(int i, long frame) {
|
||||
Clip* pre = get_clip(i);
|
||||
Clip* Sequence::split_clip(Clip* pre, long frame) {
|
||||
if (pre->timeline_in < frame && pre->timeline_out > frame) { // guard against attempts to split at in/out points
|
||||
Clip* post = pre->copy();
|
||||
|
||||
@@ -111,7 +83,10 @@ void Sequence::split_clip(int i, long frame) {
|
||||
post->clip_in = pre->clip_in + pre->getLength();
|
||||
|
||||
add_clip(post);
|
||||
|
||||
return post;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Sequence::undo_add_current() {
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ public:
|
||||
int clip_count();
|
||||
Clip* get_clip(int i);
|
||||
void delete_clip(int i);
|
||||
void delete_area(long in, long out, int track);
|
||||
void split_clip(int i, long frame);
|
||||
// void delete_area(long in, long out, int track);
|
||||
Clip* split_clip(Clip* pre, long frame);
|
||||
void get_track_limits(int* video_tracks, int* audio_tracks);
|
||||
long getEndFrame();
|
||||
int width;
|
||||
|
||||
+69
-27
@@ -54,14 +54,14 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
g.clip_in = 0;
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
g.track = j;
|
||||
g.media_stream = &m->audio_tracks[j];
|
||||
g.media_stream = m->audio_tracks[j];
|
||||
ignore_infinite_length = true;
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = &m->video_tracks[j];
|
||||
if (m->video_tracks[j].infinite_length && !ignore_infinite_length) g.out = g.in + 100;
|
||||
g.media_stream = m->video_tracks[j];
|
||||
if (m->video_tracks[j]->infinite_length && !ignore_infinite_length) g.out = g.in + 100;
|
||||
panel_timeline->ghosts.append(g);
|
||||
}
|
||||
entry_point += duration;
|
||||
@@ -91,10 +91,19 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
if (panel_timeline->importing) {
|
||||
event->accept();
|
||||
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
QVector<Clip*> added_clips;
|
||||
|
||||
sequence->delete_area(g.in, g.out, g.track);
|
||||
// delete areas before adding
|
||||
QVector<Selection> delete_areas;
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
delete_areas.append({g.in, g.out, g.track});
|
||||
}
|
||||
panel_timeline->delete_areas_and_relink(delete_areas);
|
||||
|
||||
// add clips
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
|
||||
Clip* c = new Clip();
|
||||
c->media = g.media;
|
||||
@@ -119,8 +128,20 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
}
|
||||
|
||||
sequence->add_clip(c);
|
||||
added_clips.append(c);
|
||||
}
|
||||
|
||||
// link clips from the same media
|
||||
for (int i=0;i<added_clips.size();i++) {
|
||||
Clip* c = added_clips.at(i);
|
||||
for (int j=0;j<added_clips.size();j++) {
|
||||
Clip* cc = added_clips.at(j);
|
||||
if (c != cc && c->media == cc->media) {
|
||||
c->linked.append(cc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panel_timeline->ghosts.clear();
|
||||
panel_timeline->importing = false;
|
||||
panel_timeline->snapped = false;
|
||||
@@ -148,7 +169,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
|
||||
case TIMELINE_TOOL_SLIP:
|
||||
{
|
||||
if (clip_index >= 0) {
|
||||
if (panel_timeline->is_clip_selected(clip_index)) {
|
||||
if (panel_timeline->is_clip_selected(sequence->get_clip(clip_index))) {
|
||||
// TODO if shift is down, deselect it
|
||||
} else {
|
||||
// if "shift" is not down
|
||||
@@ -158,6 +179,16 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
|
||||
|
||||
Clip* clip = sequence->get_clip(clip_index);
|
||||
panel_timeline->selections.append({clip->timeline_in, clip->timeline_out, clip->track});
|
||||
|
||||
// if alt is not down, select links
|
||||
if (!(event->modifiers() & Qt::AltModifier)) {
|
||||
for (int i=0;i<clip->linked.size();i++) {
|
||||
Clip* link = clip->linked.at(i);
|
||||
if (!panel_timeline->is_clip_selected(link)) {
|
||||
panel_timeline->selections.append({link->timeline_in, link->timeline_out, link->track});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
panel_timeline->moving_init = true;
|
||||
} else {
|
||||
@@ -173,7 +204,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
|
||||
case TIMELINE_TOOL_RAZOR:
|
||||
{
|
||||
if (clip_index >= 0) {
|
||||
sequence->split_clip(clip_index, panel_timeline->drag_frame_start);
|
||||
panel_timeline->split_clip_and_relink(sequence->get_clip(clip_index), panel_timeline->drag_frame_start, !(event->modifiers() & Qt::AltModifier));
|
||||
}
|
||||
panel_timeline->splitting = true;
|
||||
panel_timeline->redraw_all_clips();
|
||||
@@ -186,19 +217,24 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
if (panel_timeline->moving_proc) {
|
||||
if (event->modifiers() & Qt::AltModifier) { // if holding alt, duplicate rather than move
|
||||
// duplicate clips
|
||||
QVector<Clip*> copy_clips;
|
||||
QVector<Selection> delete_areas;
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
|
||||
Clip* c = g.clip->copy();
|
||||
|
||||
c->timeline_in = g.in;
|
||||
c->timeline_out = g.out;
|
||||
c->track = g.track;
|
||||
|
||||
// step 2 - delete anything that exists in area that clip is moving to
|
||||
sequence->delete_area(g.in, g.out, g.track);
|
||||
|
||||
sequence->add_clip(c);
|
||||
copy_clips.append(c);
|
||||
}
|
||||
panel_timeline->delete_areas_and_relink(delete_areas);
|
||||
for (int i=0;i<copy_clips.size();i++) {
|
||||
// step 2 - delete anything that exists in area that clip is moving to
|
||||
sequence->add_clip(copy_clips.at(i));
|
||||
}
|
||||
} else {
|
||||
// move clips
|
||||
// TODO can we do this better than 3 consecutive for loops?
|
||||
@@ -206,14 +242,20 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
// step 1 - set clips that are moving to "undeletable" (to avoid step 2 deleting any part of them)
|
||||
panel_timeline->ghosts[i].clip->undeletable = true;
|
||||
}
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
// step 2 - delete areas
|
||||
QVector<Selection> delete_areas;
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_POINTER) {
|
||||
// step 2 - delete anything that exists in area that clip is moving to
|
||||
// note: ripples are non-destructive so this is pointer-tool exclusive
|
||||
sequence->delete_area(g.in, g.out, g.track);
|
||||
}
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_POINTER) {
|
||||
// step 2 - delete anything that exists in area that clip is moving to
|
||||
// note: ripples are non-destructive so this is pointer-tool exclusive
|
||||
delete_areas.append({g.in, g.out, g.track});
|
||||
}
|
||||
}
|
||||
panel_timeline->delete_areas_and_relink(delete_areas);
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
|
||||
// step 3 - move clips
|
||||
g.clip->timeline_in = g.in;
|
||||
@@ -271,7 +313,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
bool single_select = false;
|
||||
int selected_clip = 0;
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
if (panel_timeline->is_clip_selected(i)) {
|
||||
if (panel_timeline->is_clip_selected(sequence->get_clip(i))) {
|
||||
if (!single_select) {
|
||||
// found ONE selected clip
|
||||
selected_clip = i;
|
||||
@@ -528,7 +570,6 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
|
||||
g.clip_in = g.old_clip_in - frame_diff;
|
||||
qDebug() << "slipping ghost" << i << "to" << g.clip_in;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -573,8 +614,8 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
// set up movement
|
||||
// create ghosts
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
if (panel_timeline->is_clip_selected(i)) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
Clip* c = sequence->get_clip(i);
|
||||
if (panel_timeline->is_clip_selected(c)) {
|
||||
panel_timeline->ghosts.append({c, c->timeline_in, c->timeline_out, c->track, c->clip_in});
|
||||
}
|
||||
}
|
||||
@@ -648,8 +689,9 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
int track = panel_timeline->cursor_track;
|
||||
bool repaint = false;
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
if (sequence->get_clip(i)->track == track) {
|
||||
sequence->split_clip(i, panel_timeline->drag_frame_start);
|
||||
Clip* clip = sequence->get_clip(i);
|
||||
if (clip->track == track) {
|
||||
panel_timeline->split_clip_and_relink(clip, panel_timeline->drag_frame_start, !(event->modifiers() & Qt::AltModifier));
|
||||
repaint = true;
|
||||
}
|
||||
}
|
||||
@@ -726,7 +768,7 @@ void TimelineWidget::redraw_clips() {
|
||||
clip_painter.setPen(Qt::white);
|
||||
clip_painter.drawLine(clip_rect.bottomLeft(), clip_rect.topLeft());
|
||||
clip_painter.drawLine(clip_rect.topLeft(), clip_rect.topRight());
|
||||
clip_painter.setPen(QColor(0, 0, 0, 128));
|
||||
clip_painter.setPen(Qt::gray);
|
||||
clip_painter.drawLine(clip_rect.bottomLeft(), clip_rect.bottomRight());
|
||||
clip_painter.drawLine(clip_rect.bottomRight(), clip_rect.topRight());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user