reveal in project, improved still detection and playback, fixed some ripple crashes

This commit is contained in:
itsmattkc
2018-10-22 09:25:24 +11:00
parent 386693d372
commit bbdc17a984
14 changed files with 242 additions and 97 deletions
+14 -2
View File
@@ -51,13 +51,25 @@ void PreviewGenerator::parse_media() {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
&& fmt_ctx->streams[i]->codecpar->width > 0
&& fmt_ctx->streams[i]->codecpar->height > 0) {
if (fmt_ctx->streams[i]->avg_frame_rate.den == 0) { // source is LIKELY a still image
/*qDebug() << "avg_frame_rate was:" << fmt_ctx->streams[i]->avg_frame_rate.num << "/" << fmt_ctx->streams[i]->avg_frame_rate.den;
qDebug() << "r_frame_rate was:" << fmt_ctx->streams[i]->r_frame_rate.num << "/" << fmt_ctx->streams[i]->r_frame_rate.den;
qDebug() << "codec_frame_rate was:" << fmt_ctx->streams[i]->codec->framerate.num << "/" << fmt_ctx->streams[i]->codec->framerate.den;
qDebug() << "nb_frames was:" << fmt_ctx->streams[i]->nb_frames;
qDebug() << "duration was:" << fmt_ctx->streams[i]->duration << "OR fmt_ctx's duration is:" << fmt_ctx->duration;*/
if (fmt_ctx->streams[i]->avg_frame_rate.den == 0
&& fmt_ctx->streams[i]->duration == AV_NOPTS_VALUE) { // source is LIKELY a still image
ms->infinite_length = true;
contains_still_image = true;
ms->video_frame_rate = 0;
} else {
ms->infinite_length = false;
ms->video_frame_rate = av_q2d(fmt_ctx->streams[i]->avg_frame_rate);
if (fmt_ctx->streams[i]->r_frame_rate.den == 0) {
ms->video_frame_rate = av_q2d(fmt_ctx->streams[i]->avg_frame_rate);
} else {
ms->video_frame_rate = av_q2d(fmt_ctx->streams[i]->r_frame_rate);
}
}
ms->video_width = fmt_ctx->streams[i]->codecpar->width;
ms->video_height = fmt_ctx->streams[i]->codecpar->height;
+2 -2
View File
@@ -207,7 +207,7 @@ void MainWindow::on_actionDelete_triggered()
if (panel_timeline->ui->headers->hasFocus()) {
panel_timeline->ui->headers->delete_markers();
} else if (panel_timeline->focused()) {
panel_timeline->delete_selection(false);
panel_timeline->delete_selection(sequence->selections, false);
} else if (panel_effect_controls->is_focused()) {
panel_effect_controls->delete_effects();
} else if (panel_project->is_focused()) {
@@ -281,7 +281,7 @@ void MainWindow::on_actionTimeline_toggled(bool arg1)
void MainWindow::on_actionRipple_Delete_triggered()
{
panel_timeline->delete_selection(true);
panel_timeline->delete_selection(sequence->selections, true);
}
void MainWindow::editMenu_About_To_Be_Shown() {
+29 -3
View File
@@ -239,7 +239,7 @@ void Project::get_all_media_from_table(QList<QTreeWidgetItem*> items, QList<QTre
children.append(item->child(j));
}
get_all_media_from_table(children, list, search_type);
} else if (search_type == type) {
} else if (search_type == type || search_type == -1) {
list.append(item);
}
}
@@ -523,6 +523,31 @@ QTreeWidgetItem* Project::get_selected_folder() {
return NULL;
}
bool Project::reveal_media(void *media, QTreeWidgetItem* parent) {
int count = (parent == NULL) ? ui->treeWidget->topLevelItemCount() : parent->childCount();
for (int i=0;i<count;i++) {
QTreeWidgetItem* item = (parent == NULL) ? ui->treeWidget->topLevelItem(i) : parent->child(i);
if (get_type_from_tree(item) == MEDIA_TYPE_FOLDER) {
if (reveal_media(media, item)) return true;
} else if (get_media_from_tree(item) == media) {
// expand all folders leading to this media
QTreeWidgetItem* hierarchy = item->parent();
while (hierarchy != NULL) {
hierarchy->setExpanded(true);
hierarchy = hierarchy->parent();
}
// select item
item->setSelected(true);
return true;
}
}
return false;
}
void Project::import_dialog() {
QFileDialog fd(this, "Import media...", "", "All Files (*)");
fd.setFileMode(QFileDialog::ExistingFiles);
@@ -538,13 +563,14 @@ void set_item_to_folder(QTreeWidgetItem* item) {
}
void* get_media_from_tree(QTreeWidgetItem* item) {
int type = get_type_from_tree(item);
return reinterpret_cast<void*>(item->data(0, Qt::UserRole + 2).value<quintptr>());
/*int type = get_type_from_tree(item);
switch (type) {
case MEDIA_TYPE_FOOTAGE: return get_footage_from_tree(item);
case MEDIA_TYPE_SEQUENCE: return get_sequence_from_tree(item);
default: qDebug() << "[ERROR] Invalid media type when retrieving media";
}
return NULL;
return NULL;*/
}
Media* get_footage_from_tree(QTreeWidgetItem* item) {
+1
View File
@@ -55,6 +55,7 @@ public:
void process_file_list(bool recursive, QStringList& files, QTreeWidgetItem *parent, QTreeWidgetItem* replace);
void replace_media(QTreeWidgetItem* item, QString filename);
QTreeWidgetItem* get_selected_folder();
bool reveal_media(void* media, QTreeWidgetItem *parent = 0);
void new_project();
void load_project();
+36 -31
View File
@@ -220,7 +220,7 @@ void Timeline::repaint_timeline() {
if (sequence != NULL) {
long sequenceEndFrame = sequence->getEndFrame();
panel_timeline->ui->horizontalScrollBar->setMaximum(qMax(0, getScreenPointFromFrame(panel_timeline->zoom, sequenceEndFrame) + 100 - ui->editAreas->width()));
panel_timeline->ui->horizontalScrollBar->setMaximum(qMax(0, getScreenPointFromFrame(panel_timeline->zoom, sequenceEndFrame) - (ui->editAreas->width()/2)));
if (last_frame != sequence->playhead) {
ui->audio_monitor->update();
@@ -267,21 +267,21 @@ void Timeline::delete_in_out(bool ripple) {
}
}
void Timeline::delete_selection(bool ripple_delete) {
if (sequence->selections.size() > 0) {
void Timeline::delete_selection(QVector<Selection>& selections, bool ripple_delete) {
if (selections.size() > 0) {
panel_effect_controls->clear_effects(true);
ComboAction* ca = new ComboAction();
delete_areas_and_relink(ca, sequence->selections);
delete_areas_and_relink(ca, selections);
if (ripple_delete) {
long ripple_point = sequence->selections.at(0).in;
long ripple_length = sequence->selections.at(0).out - sequence->selections.at(0).in;
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<sequence->selections.size();i++) {
const Selection& s = sequence->selections.at(i);
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);
}
@@ -295,8 +295,8 @@ void Timeline::delete_selection(bool ripple_delete) {
if (c != NULL && 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<sequence->selections.size();j++) {
const Selection& s = sequence->selections.at(j);
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)) {
@@ -317,10 +317,13 @@ void Timeline::delete_selection(bool ripple_delete) {
}
}
}
qDebug() << "going to ripple at" << ripple_point << "by" << ripple_length;
if (can_ripple) ca->append(new RippleCommand(sequence, ripple_point, -ripple_length));
}
sequence->selections.clear();
selections.clear();
undo_stack.push(ca);
@@ -617,7 +620,7 @@ void Timeline::copy(bool del) {
}
if (del && copied) {
delete_selection(false);
delete_selection(sequence->selections, false);
}
}
@@ -703,24 +706,26 @@ void Timeline::ripple_to_in_point(bool in) {
long in_point = in ? 0 : LONG_MAX;
for (int i=0;i<sequence->clips.size();i++) {
Clip* c = sequence->clips.at(i);
track_min = qMin(track_min, c->track);
track_max = qMax(track_max, c->track);
if ((in && c->timeline_in > in_point && c->timeline_in <= sequence->playhead)
|| (!in && c->timeline_in < in_point && c->timeline_in >= sequence->playhead)) {
in_point = c->timeline_in;
if (sequence->playhead == in_point) {
one_frame_mode = true;
break;
}
}
if ((in && c->timeline_out > in_point && c->timeline_out <= sequence->playhead)
|| (!in && c->timeline_out < in_point && c->timeline_out > sequence->playhead)) {
in_point = c->timeline_out;
if (sequence->playhead == in_point) {
one_frame_mode = true;
break;
}
}
if (c != NULL) {
track_min = qMin(track_min, c->track);
track_max = qMax(track_max, c->track);
if ((in && c->timeline_in > in_point && c->timeline_in <= sequence->playhead)
|| (!in && c->timeline_in < in_point && c->timeline_in >= sequence->playhead)) {
in_point = c->timeline_in;
if (sequence->playhead == in_point) {
one_frame_mode = true;
break;
}
}
if ((in && c->timeline_out > in_point && c->timeline_out <= sequence->playhead)
|| (!in && c->timeline_out < in_point && c->timeline_out > sequence->playhead)) {
in_point = c->timeline_out;
if (sequence->playhead == in_point) {
one_frame_mode = true;
break;
}
}
}
}
if (one_frame_mode) {
+1 -1
View File
@@ -129,7 +129,7 @@ public:
bool selecting;
int selection_offset;
bool is_clip_selected(Clip* clip, bool containing);
void delete_selection(bool ripple);
void delete_selection(QVector<Selection> &selections, bool ripple);
void select_all();
bool rect_select_init;
bool rect_select_proc;
+5 -3
View File
@@ -181,7 +181,7 @@ QString frame_to_timecode(long f, int view, double frame_rate) {
// non-drop timecode
int int_fps = qRound(frame_rate);
hours = f/ (3600 * int_fps);
hours = f / (3600 * int_fps);
mins = f / (60*int_fps) % 60;
secs = f/int_fps % 60;
frames = f%int_fps;
@@ -327,11 +327,13 @@ void Viewer::set_media(int type, void* media) {
seq->workarea_in = footage->in;
seq->workarea_out = footage->out;
seq->frame_rate = 30;
if (footage->video_tracks.size() > 0) {
MediaStream* video_stream = footage->video_tracks.at(0);
seq->width = video_stream->video_width;
seq->height = video_stream->video_height;
if (video_stream->video_frame_rate > 0) seq->frame_rate = video_stream->video_frame_rate;
if (video_stream->video_frame_rate > 0 && !video_stream->infinite_length) seq->frame_rate = video_stream->video_frame_rate;
Clip* c = new Clip(seq);
c->media = footage;
@@ -339,6 +341,7 @@ void Viewer::set_media(int type, void* media) {
c->media_stream = video_stream->file_index;
c->timeline_in = 0;
c->timeline_out = footage->get_length_in_frames(seq->frame_rate);
if (c->timeline_out <= 0) c->timeline_out = 150;
c->track = -1;
c->clip_in = 0;
c->recalculateMaxLength();
@@ -346,7 +349,6 @@ void Viewer::set_media(int type, void* media) {
} else {
seq->width = 1920;
seq->height = 1080;
seq->frame_rate = 30;
}
if (footage->audio_tracks.size() > 0) {
+23 -15
View File
@@ -125,7 +125,7 @@ void cache_audio_worker(Clip* c, Clip* nest) {
#ifdef AUDIOWARNINGS
qDebug() << "reached EOF while reading";
#endif
// TODO likewise, I'm not sure if this if statement breaks anything (see equivalent section in cache_video_worker)
// TODO revise usage of reached_end in audio
if (!c->reverse) {
c->reached_end = true;
} else {
@@ -358,13 +358,27 @@ void cache_audio_worker(Clip* c, Clip* nest) {
void cache_video_worker(Clip* c, long playhead) {
int read_ret, send_ret, retr_ret;
int64_t target_pts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, playhead));
int limit = c->max_queue_size;
if (c->reverse) limit *= 2;
/*if (c->reverse) {
bool found = false;
for (int i=0;i<c->queue.size();i++) {
if (target_pts > c->queue.at(i)->pts && target_pts <= c->queue.at(i)->pts + c->queue.at(i)->pkt_duration) {
found = true;
break;
}
}
if (!found) {
// we need like one more frame
limit = c->queue.size() + 1;
}
}*/
if (c->queue.size() < limit) {
int64_t eighth_second = av_q2d(av_inv_q(c->stream->time_base))*0.125;
int64_t smallest_pts = INT64_MAX;
int64_t target_pts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, playhead));
if (c->reverse && c->queue.size() > 0) {
int64_t quarter_sec = qRound(av_q2d(av_inv_q(c->stream->time_base))) >> 2;
for (int i=0;i<c->queue.size();i++) {
@@ -392,8 +406,10 @@ void cache_video_worker(Clip* c, long playhead) {
send_it = true;
} else if (send_frame->pts > target_pts - eighth_second) {
send_it = true;
} else if (static_cast<Media*>(c->media)->get_stream_from_file_index(true, c->media_stream)->infinite_length) {
send_it = true;
} else {
qDebug() << "skipped adding a frame to the queue" << target_pts << "(" << send_frame->pts << send_frame->pkt_duration;
qDebug() << "skipped adding a frame to the queue - fpts:" << send_frame->pts << "target:" << target_pts;
}
if (send_it) {
@@ -447,15 +463,7 @@ void cache_video_worker(Clip* c, long playhead) {
break;
} else {
// remove earliest frame and loop to store another
int earliest_frame = 0;
for (int i=1;i<c->queue.size();i++) {
// TODO/NOTE: this will not work on sped up AND reversed video
if (c->queue.at(i)->pts < c->queue.at(earliest_frame)->pts) {
earliest_frame = i;
}
}
av_frame_free(&c->queue[earliest_frame]);
c->queue.removeAt(earliest_frame);
c->queue_remove_earliest();
}
// } else {
// break;
@@ -477,7 +485,7 @@ void reset_cache(Clip* c, long target_frame) {
if (!ms->infinite_length) {
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
// clear current queue
c->clear_queue();
c->queue_clear();
// seeks to nearest keyframe (target_frame represents internal clip frame)
int64_t target_ts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
@@ -585,7 +593,7 @@ void open_clip_worker(Clip* clip) {
clip->codecCtx = avcodec_alloc_context3(clip->codec);
avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar);
clip->max_queue_size = (ms->infinite_length) ? 1 : qCeil(ms->video_frame_rate*0.25);
clip->max_queue_size = (ms->infinite_length) ? 1 : qCeil(ms->video_frame_rate*0.5);
if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->max_queue_size *= 2;
AVDictionary* opts = NULL;
@@ -783,7 +791,7 @@ void close_clip_worker(Clip* clip) {
clip->finished_opening = false;
if (clip->media_type == MEDIA_TYPE_FOOTAGE) {
clip->clear_queue();
clip->queue_clear();
avfilter_graph_free(&clip->filter_graph);
+6 -2
View File
@@ -26,7 +26,7 @@ extern "C" {
#include <QOpenGLFramebufferObject>
#ifdef QT_DEBUG
//#define GCF_DEBUG
#define GCF_DEBUG
#endif
bool texture_failed = false;
@@ -115,6 +115,7 @@ void get_clip_frame(Clip* c, long playhead) {
int64_t target_pts = playhead_to_timestamp(c, playhead);
int64_t second_pts = qRound(av_q2d(av_inv_q(c->stream->time_base)));
int64_t quarter_pts = second_pts >> 2;
if (ms->video_interlacing != VIDEO_PROGRESSIVE) {
target_pts *= 2;
second_pts *= 2;
@@ -151,9 +152,11 @@ void get_clip_frame(Clip* c, long playhead) {
// remove all frames earlier than this one from the queue
target_frame = c->queue.at(closest_frame);
int64_t minimum_ts = target_frame->pts;
if (c->reverse) minimum_ts += quarter_pts; else minimum_ts -= quarter_pts;
//qDebug() << "closest frame was" << closest_frame << "with" << target_frame->pts << "/" << target_pts;
for (int i=0;i<c->queue.size();i++) {
if (c->queue.at(i) != target_frame && ((c->queue.at(i)->pts > target_frame->pts) == c->reverse)) {
if (c->queue.at(i) != target_frame && ((c->queue.at(i)->pts > minimum_ts) == c->reverse)) {
//qDebug() << "removed frame at" << i << "because its pts was" << c->queue.at(i)->pts << "compared to" << target_frame->pts;
av_frame_free(&c->queue[i]); // may be a little heavy for the UI thread?
c->queue.removeAt(i);
@@ -186,6 +189,7 @@ void get_clip_frame(Clip* c, long playhead) {
#ifdef GCF_DEBUG
qDebug() << "GCF ==> WAIT - target:" << target_pts << "closest frame:" << target_frame->pts;
#endif
//if (c->queue.size() >= c->max_queue_size) c->queue_remove_earliest();
target_frame = NULL;
}
}
+13 -1
View File
@@ -126,13 +126,25 @@ void Clip::refresh() {
recalculateMaxLength();
}
void Clip::clear_queue() {
void Clip::queue_clear() {
while (queue.size() > 0) {
av_frame_free(&queue.first());
queue.removeFirst();
}
}
void Clip::queue_remove_earliest() {
int earliest_frame = 0;
for (int i=1;i<queue.size();i++) {
// TODO/NOTE: this will not work on sped up AND reversed video
if (queue.at(i)->pts < queue.at(earliest_frame)->pts) {
earliest_frame = i;
}
}
av_frame_free(&queue[earliest_frame]);
queue.removeAt(earliest_frame);
}
Clip::~Clip() {
if (open) {
close_clip(this);
+4 -1
View File
@@ -37,7 +37,6 @@ struct Clip
void reset_audio();
void reset();
void refresh();
void clear_queue();
long get_timeline_in_with_transition();
long get_timeline_out_with_transition();
long getLength();
@@ -49,6 +48,10 @@ struct Clip
void refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points);
Sequence* sequence;
// queue functions
void queue_clear();
void queue_remove_earliest();
// timeline variables (should be copied in copy())
bool enabled;
long clip_in;
+7 -2
View File
@@ -178,9 +178,14 @@ void SourceTable::dropEvent(QDropEvent* event) {
}
if (!replace) {
QTreeWidgetItem* parent = NULL;
if (drop_item != NULL && get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER) {
parent = drop_item;
if (drop_item != NULL) {
if (get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER) {
parent = drop_item;
} else {
parent = drop_item->parent();
}
}
if (parent != NULL) parent->setExpanded(true);
panel_project->process_file_list(false, paths, parent, NULL);
}
}
+99 -34
View File
@@ -53,34 +53,18 @@ TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent) {
}
void TimelineWidget::right_click_ripple() {
bool can_ripple = true;
QVector<Selection> sels;
// validate the ripple
for (int i=0;i<sequence->clips.size();i++) {
Clip* c = sequence->clips.at(i);
if (c != NULL && c->timeline_in > rc_ripple_min) {
for (int j=0;j<sequence->clips.size();j++) {
Clip* cc = sequence->clips.at(j);
if (cc != NULL && cc->track == c->track) {
if (cc->timeline_in < rc_ripple_min && cc->timeline_out == c->timeline_in) {
can_ripple = false;
break;
} else if (cc->timeline_out < c->timeline_in) {
long validator = (c->timeline_in - (rc_ripple_max - rc_ripple_min)) - cc->timeline_out;
if (validator < 0) {
rc_ripple_min = cc->timeline_out;
rc_ripple_max = c->timeline_in;
}
}
}
}
}
}
Selection s;
s.in = rc_ripple_min;
s.out = rc_ripple_max;
s.track = panel_timeline->cursor_track;
if (can_ripple) {
undo_stack.push(new RippleCommand(sequence, rc_ripple_min, rc_ripple_min - rc_ripple_max));
update_ui(true);
}
qDebug() << s.in << s.out << s.track;
sels.append(s);
panel_timeline->delete_selection(sels, true);
}
void TimelineWidget::show_context_menu(const QPoint& pos) {
@@ -99,6 +83,8 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
menu.addSeparator();
if (sequence->selections.size() == 0) {
// no clips are selected
panel_timeline->cursor_frame = panel_timeline->getTimelineFrameFromScreenPoint(pos.x());
panel_timeline->cursor_track = getTrackFromScreenPoint(pos.y());
@@ -133,6 +119,8 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
menu.addAction("Sequence settings coming soon...");
} else {
// clips are selected
QAction* cutAction = menu.addAction("C&ut");
connect(cutAction, SIGNAL(triggered(bool)), mainWindow, SLOT(cut()));
QAction* copyAction = menu.addAction("Cop&y");
@@ -146,13 +134,33 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
autoscaleAction->setCheckable(true);
connect(autoscaleAction, SIGNAL(triggered(bool)), this, SLOT(toggle_autoscale()));
// collect all the selected clips
QVector<Clip*> selected_clips;
for (int i=0;i<sequence->clips.size();i++) {
Clip* c = sequence->clips.at(i);
if (c != NULL && panel_timeline->is_clip_selected(c, true)) {
autoscaleAction->setChecked(c->autoscale);
break;
selected_clips.append(c);
}
}
// set autoscale arbitrarily to the first selected clip
autoscaleAction->setChecked(selected_clips.at(0)->autoscale);
// check if all selected clips have the same media for a "Reveal In Project"
bool same_media = true;
rc_reveal_media = selected_clips.at(0)->media;
for (int i=1;i<selected_clips.size();i++) {
if (selected_clips.at(i)->media != rc_reveal_media) {
same_media = false;
break;
}
}
if (same_media) {
QAction* revealInProjectAction = menu.addAction("&Reveal in Project");
connect(revealInProjectAction, SIGNAL(triggered(bool)), this, SLOT(reveal_media()));
}
}
menu.exec(mapToGlobal(pos));
@@ -1317,12 +1325,29 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
if (panel_timeline->importing) {
QToolTip::showText(mapToGlobal(mouse_pos), frame_to_timecode(earliest_in_point, config.timecode_view, sequence->frame_rate));
} else {
QToolTip::showText(mapToGlobal(mouse_pos),
((frame_diff < 0) ? "-" : "+")
+ frame_to_timecode(qAbs(frame_diff), config.timecode_view, sequence->frame_rate)
+ " Duration: "
+ frame_to_timecode((panel_timeline->ghosts.at(0).old_out-panel_timeline->ghosts.at(0).old_in)+frame_diff, config.timecode_view, sequence->frame_rate)
);
QString tip = ((frame_diff < 0) ? "-" : "+") + frame_to_timecode(qAbs(frame_diff), config.timecode_view, sequence->frame_rate);
if (panel_timeline->trim_target > -1) {
// find which clip is being moved
const Ghost* g = NULL;
for (int i=0;i<panel_timeline->ghosts.size();i++) {
if (panel_timeline->ghosts.at(i).clip == panel_timeline->trim_target) {
g = &panel_timeline->ghosts.at(i);
break;
}
}
if (g != NULL) {
tip += " Duration: ";
long len = (g->old_out-g->old_in);
if (panel_timeline->trim_in_point) {
len -= frame_diff;
} else {
len += frame_diff;
}
tip += frame_to_timecode(len, config.timecode_view, sequence->frame_rate);
}
}
QToolTip::showText(mapToGlobal(mouse_pos), tip);
}
}
@@ -1908,6 +1933,40 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
if (actual_clip_rect.bottom() > height()) actual_clip_rect.setBottom(height());
p.fillRect(actual_clip_rect, (clip->enabled) ? QColor(clip->color_r, clip->color_g, clip->color_b) : QColor(96, 96, 96));
// draw top and tail triangles
if (clip->media_type == MEDIA_TYPE_FOOTAGE && !static_cast<Media*>(clip->media)->get_stream_from_file_index(clip->track < 0, clip->media_stream)->infinite_length) {
int triangle_size = TRACK_MIN_HEIGHT >> 2;
p.setPen(Qt::NoPen);
p.setBrush(QColor(80, 80, 80));
if (clip->clip_in == 0
&& clip_rect.x() + triangle_size > 0
&& clip_rect.y() + triangle_size > 0
&& clip_rect.x() < width()
&& clip_rect.y() < height()) {
const QPoint points[3] = {
QPoint(clip_rect.x(), clip_rect.y()),
QPoint(clip_rect.x() + triangle_size, clip_rect.y()),
QPoint(clip_rect.x(), clip_rect.y() + triangle_size)
};
p.drawPolygon(points, 3);
text_rect.setLeft(text_rect.left() + (triangle_size >> 2));
}
if (clip->timeline_out - clip->timeline_in + clip->clip_in == clip->getMaximumLength()
&& clip_rect.right() - triangle_size < width()
&& clip_rect.y() + triangle_size > 0
&& clip_rect.right() > 0
&& clip_rect.y() < height()) {
const QPoint points[3] = {
QPoint(clip_rect.right(), clip_rect.y()),
QPoint(clip_rect.right() - triangle_size, clip_rect.y()),
QPoint(clip_rect.right(), clip_rect.y() + triangle_size)
};
p.drawPolygon(points, 3);
text_rect.setRight(text_rect.right() - (triangle_size >> 2));
}
p.setBrush(Qt::NoBrush);
}
int thumb_x = clip_rect.x() + 1;
if (clip->media_type == MEDIA_TYPE_FOOTAGE) {
@@ -2091,6 +2150,8 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
if (is_track_visible(s.track)) {
int selection_y = getScreenPointFromTrack(s.track);
int selection_x = panel_timeline->getTimelineScreenPointFromFrame(s.in);
p.setPen(Qt::NoPen);
p.setBrush(Qt::NoBrush);
p.fillRect(selection_x, selection_y, panel_timeline->getTimelineScreenPointFromFrame(s.out) - selection_x, panel_timeline->calculate_track_height(s.track, -1), QColor(0, 0, 0, 64));
}
}
@@ -2221,3 +2282,7 @@ void TimelineWidget::setScroll(int s) {
scroll = s;
update();
}
void TimelineWidget::reveal_media() {
panel_project->reveal_media(rc_reveal_media);
}
+2
View File
@@ -69,6 +69,7 @@ private:
// used for "right click ripple"
long rc_ripple_min;
long rc_ripple_max;
void* rc_reveal_media;
QTimer tooltip_timer;
int tooltip_clip;
@@ -85,6 +86,7 @@ public slots:
void setScroll(int);
private slots:
void reveal_media();
void right_click_ripple();
void show_context_menu(const QPoint& pos);
void toggle_autoscale();