added many linking features

This commit is contained in:
itsmattkc
2018-06-13 22:39:05 -07:00
parent 093b68cb67
commit 124b76d58c
16 changed files with 312 additions and 100 deletions
+15
View File
@@ -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
View File
@@ -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);
+10
View File
@@ -0,0 +1,10 @@
#include "previewgenerator.h"
PreviewGenerator::PreviewGenerator(QWidget* parent) : QThread(parent)
{
}
void PreviewGenerator::run() {
}
+13
View File
@@ -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
+7
View File
@@ -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();
}
}
+2
View File
@@ -71,6 +71,8 @@ private slots:
void on_actionSave_Project_As_triggered();
void on_actionDeselect_All_triggered();
private:
Ui::MainWindow *ui;
void setup_layout();
+6
View File
@@ -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>
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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());