From bbdc17a984538249ee586fdd64cd31a848f124ab Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 22 Oct 2018 09:25:24 +1100 Subject: [PATCH] reveal in project, improved still detection and playback, fixed some ripple crashes --- io/previewgenerator.cpp | 16 ++++- mainwindow.cpp | 4 +- panels/project.cpp | 32 +++++++++- panels/project.h | 1 + panels/timeline.cpp | 67 ++++++++++---------- panels/timeline.h | 2 +- panels/viewer.cpp | 8 ++- playback/cacher.cpp | 38 +++++++----- playback/playback.cpp | 8 ++- project/clip.cpp | 14 ++++- project/clip.h | 5 +- ui/sourcetable.cpp | 9 ++- ui/timelinewidget.cpp | 133 ++++++++++++++++++++++++++++++---------- ui/timelinewidget.h | 2 + 14 files changed, 242 insertions(+), 97 deletions(-) diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp index f8122d64a..0443e567c 100644 --- a/io/previewgenerator.cpp +++ b/io/previewgenerator.cpp @@ -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; diff --git a/mainwindow.cpp b/mainwindow.cpp index cd20e2cc8..397c77154 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -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() { diff --git a/panels/project.cpp b/panels/project.cpp index fdc45d4d0..eb15cdb1b 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -239,7 +239,7 @@ void Project::get_all_media_from_table(QList items, QListchild(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;itreeWidget->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(item->data(0, Qt::UserRole + 2).value()); + /*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) { diff --git a/panels/project.h b/panels/project.h index 7b7b35879..d05b8e84c 100644 --- a/panels/project.h +++ b/panels/project.h @@ -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(); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index b02098851..2818ee73f 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -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& 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;iselections.size();i++) { - const Selection& s = sequence->selections.at(i); + for (int i=1;itimeline_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;jselections.size();j++) { - const Selection& s = sequence->selections.at(j); + for (int j=0;jtrack && !(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;iclips.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) { diff --git a/panels/timeline.h b/panels/timeline.h index c992f5f93..c92db4b62 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -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 &selections, bool ripple); void select_all(); bool rect_select_init; bool rect_select_proc; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index e3f3b4eb0..c5e5ca61a 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -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) { diff --git a/playback/cacher.cpp b/playback/cacher.cpp index bdf02059c..641209d5d 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -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;iqueue.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;iqueue.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(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;iqueue.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); diff --git a/playback/playback.cpp b/playback/playback.cpp index 2d3152eee..555eba800 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -26,7 +26,7 @@ extern "C" { #include #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;iqueue.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; } } diff --git a/project/clip.cpp b/project/clip.cpp index b14775f24..5bfc55525 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -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;ipts < 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); diff --git a/project/clip.h b/project/clip.h index aa0decfa7..559f325f3 100644 --- a/project/clip.h +++ b/project/clip.h @@ -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; diff --git a/ui/sourcetable.cpp b/ui/sourcetable.cpp index 235188ad7..4702b9841 100644 --- a/ui/sourcetable.cpp +++ b/ui/sourcetable.cpp @@ -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); } } diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 81ca9412f..67b33a47b 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -53,34 +53,18 @@ TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent) { } void TimelineWidget::right_click_ripple() { - bool can_ripple = true; + QVector sels; - // validate the ripple - for (int i=0;iclips.size();i++) { - Clip* c = sequence->clips.at(i); - if (c != NULL && c->timeline_in > rc_ripple_min) { - for (int j=0;jclips.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 selected_clips; for (int i=0;iclips.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;imedia != 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;ighosts.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(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); +} diff --git a/ui/timelinewidget.h b/ui/timelinewidget.h index e1b2a5798..2597fd962 100644 --- a/ui/timelinewidget.h +++ b/ui/timelinewidget.h @@ -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();