timeline: added ability to move clips by sample in addition to by frame
This commit is contained in:
@@ -263,3 +263,13 @@ int64_t Timecode::time_to_timestamp(const double &time, const rational &timebase
|
||||
{
|
||||
return qRound64(time * timebase.flipped().toDouble());
|
||||
}
|
||||
|
||||
int64_t Timecode::rescale_timestamp(const int64_t &ts, const rational &source, const rational &dest)
|
||||
{
|
||||
return qRound64(static_cast<double>(ts) * source.toDouble() / dest.toDouble());
|
||||
}
|
||||
|
||||
int64_t Timecode::rescale_timestamp_ceil(const int64_t &ts, const rational &source, const rational &dest)
|
||||
{
|
||||
return qCeil(static_cast<double>(ts) * source.toDouble() / dest.toDouble());
|
||||
}
|
||||
|
||||
@@ -57,6 +57,9 @@ public:
|
||||
static int64_t time_to_timestamp(const rational& time, const rational& timebase);
|
||||
static int64_t time_to_timestamp(const double& time, const rational& timebase);
|
||||
|
||||
static int64_t rescale_timestamp(const int64_t& ts, const rational& source, const rational& dest);
|
||||
static int64_t rescale_timestamp_ceil(const int64_t& ts, const rational& source, const rational& dest);
|
||||
|
||||
static rational timestamp_to_time(const int64_t& timestamp, const rational& timebase);
|
||||
|
||||
static QString time_to_timecode(const rational& time, const rational& timebase, const Display &display, bool show_plus_if_positive = false);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "dialog/sequence/sequence.h"
|
||||
#include "dialog/speedduration/speedduration.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "tool/tool.h"
|
||||
@@ -16,7 +17,8 @@
|
||||
TimelineWidget::TimelineWidget(QWidget *parent) :
|
||||
TimeBasedWidget(true, true, parent),
|
||||
rubberband_(QRubberBand::Rectangle, this),
|
||||
active_tool_(nullptr)
|
||||
active_tool_(nullptr),
|
||||
use_audio_time_units_(false)
|
||||
{
|
||||
QVBoxLayout* vert_layout = new QVBoxLayout(this);
|
||||
vert_layout->setSpacing(0);
|
||||
@@ -72,6 +74,8 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
|
||||
connect(views_.first()->view()->horizontalScrollBar(), &QScrollBar::rangeChanged, scrollbar(), &QScrollBar::setRange);
|
||||
vert_layout->addWidget(scrollbar());
|
||||
|
||||
connect(ruler(), &TimeRuler::TimeChanged, this, &TimelineWidget::SetViewTimestamp);
|
||||
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
TimelineView* view = tview->view();
|
||||
|
||||
@@ -82,9 +86,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
|
||||
|
||||
connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, ruler(), &TimeRuler::SetScroll);
|
||||
connect(view, &TimelineView::ScaleChanged, this, &TimelineWidget::SetScale);
|
||||
connect(ruler(), &TimeRuler::TimeChanged, view, &TimelineView::SetTime);
|
||||
connect(view, &TimelineView::TimeChanged, ruler(), &TimeRuler::SetTime);
|
||||
connect(view, &TimelineView::TimeChanged, this, &TimelineWidget::TimeChanged);
|
||||
connect(view, &TimelineView::TimeChanged, this, &TimelineWidget::ViewTimestampChanged);
|
||||
connect(view, &TimelineView::customContextMenuRequested, this, &TimelineWidget::ShowContextMenu);
|
||||
connect(scrollbar(), &QScrollBar::valueChanged, view->horizontalScrollBar(), &QScrollBar::setValue);
|
||||
connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, scrollbar(), &QScrollBar::setValue);
|
||||
@@ -158,9 +160,7 @@ void TimelineWidget::TimebaseChangedEvent(const rational &timebase)
|
||||
}
|
||||
}
|
||||
|
||||
foreach (TimelineAndTrackView* view, views_) {
|
||||
view->view()->SetTimebase(timebase);
|
||||
}
|
||||
UpdateViewTimebases();
|
||||
}
|
||||
|
||||
void TimelineWidget::resizeEvent(QResizeEvent *event)
|
||||
@@ -173,9 +173,7 @@ void TimelineWidget::resizeEvent(QResizeEvent *event)
|
||||
|
||||
void TimelineWidget::TimeChangedEvent(const int64_t& timestamp)
|
||||
{
|
||||
foreach (TimelineAndTrackView* view, views_) {
|
||||
view->view()->SetTime(timestamp);
|
||||
}
|
||||
SetViewTimestamp(timestamp);
|
||||
|
||||
timecode_label_->SetValue(timestamp);
|
||||
}
|
||||
@@ -333,6 +331,14 @@ QList<TimelineWidget::DraggedFootage> TimelineWidget::FootageToDraggedFootage(QL
|
||||
return df;
|
||||
}
|
||||
|
||||
rational TimelineWidget::GetToolTipTimebase() const
|
||||
{
|
||||
if (GetConnectedNode() && use_audio_time_units_) {
|
||||
return GetConnectedNode()->audio_params().time_base();
|
||||
}
|
||||
return timebase();
|
||||
}
|
||||
|
||||
void TimelineWidget::SelectAll()
|
||||
{
|
||||
foreach (TimelineAndTrackView* view, views_) {
|
||||
@@ -1042,8 +1048,20 @@ void TimelineWidget::ShowContextMenu()
|
||||
if (!selected.isEmpty()) {
|
||||
QAction* speed_duration_action = menu.addAction(tr("Speed/Duration"));
|
||||
connect(speed_duration_action, &QAction::triggered, this, &TimelineWidget::ShowSpeedDurationDialog);
|
||||
|
||||
menu.addSeparator();
|
||||
}
|
||||
|
||||
QAction* toggle_audio_units = menu.addAction(tr("Use Audio Time Units"));
|
||||
toggle_audio_units->setCheckable(true);
|
||||
toggle_audio_units->setChecked(use_audio_time_units_);
|
||||
connect(toggle_audio_units, &QAction::triggered, this, &TimelineWidget::SetUseAudioTimeUnits);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
QAction* properties_action = menu.addAction(tr("Properties"));
|
||||
connect(properties_action, &QAction::triggered, this, &TimelineWidget::ShowSequenceDialog);
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
@@ -1072,6 +1090,57 @@ void TimelineWidget::DeferredScrollAction()
|
||||
scrollbar()->setValue(deferred_scroll_value_);
|
||||
}
|
||||
|
||||
void TimelineWidget::ShowSequenceDialog()
|
||||
{
|
||||
if (!GetConnectedNode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SequenceDialog sd(static_cast<Sequence*>(GetConnectedNode()->parent()), SequenceDialog::kExisting, this);
|
||||
sd.exec();
|
||||
}
|
||||
|
||||
void TimelineWidget::SetUseAudioTimeUnits(bool use)
|
||||
{
|
||||
use_audio_time_units_ = use;
|
||||
|
||||
// Update timebases
|
||||
UpdateViewTimebases();
|
||||
|
||||
// Force update of the viewer timestamps
|
||||
SetViewTimestamp(GetTimestamp());
|
||||
}
|
||||
|
||||
void TimelineWidget::SetViewTimestamp(const int64_t &ts)
|
||||
{
|
||||
for (int i=0;i<views_.size();i++) {
|
||||
TimelineAndTrackView* view = views_.at(i);
|
||||
|
||||
if (use_audio_time_units_ && i == Timeline::kTrackTypeAudio) {
|
||||
view->view()->SetTime(Timecode::rescale_timestamp(ts,
|
||||
timebase(),
|
||||
GetConnectedNode()->audio_params().time_base()));
|
||||
} else {
|
||||
view->view()->SetTime(ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::ViewTimestampChanged(int64_t ts)
|
||||
{
|
||||
if (use_audio_time_units_ && sender() == views_.at(Timeline::kTrackTypeAudio)) {
|
||||
ts = Timecode::rescale_timestamp(ts,
|
||||
GetConnectedNode()->audio_params().time_base(),
|
||||
timebase());
|
||||
}
|
||||
|
||||
// Update all other views
|
||||
SetViewTimestamp(ts);
|
||||
|
||||
ruler()->SetTime(ts);
|
||||
emit TimeChanged(ts);
|
||||
}
|
||||
|
||||
void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost)
|
||||
{
|
||||
ghost->SetScale(GetScale());
|
||||
@@ -1079,6 +1148,19 @@ void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost)
|
||||
views_.at(ghost->Track().type())->view()->scene()->addItem(ghost);
|
||||
}
|
||||
|
||||
void TimelineWidget::UpdateViewTimebases()
|
||||
{
|
||||
for (int i=0;i<views_.size();i++) {
|
||||
TimelineAndTrackView* view = views_.at(i);
|
||||
|
||||
if (use_audio_time_units_ && i == Timeline::kTrackTypeAudio) {
|
||||
view->view()->SetTimebase(GetConnectedNode()->audio_params().time_base());
|
||||
} else {
|
||||
view->view()->SetTimebase(timebase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected)
|
||||
{
|
||||
TimelineViewBlockItem* link_item;
|
||||
|
||||
@@ -391,6 +391,8 @@ private:
|
||||
bool dual_transition_;
|
||||
};
|
||||
|
||||
rational GetToolTipTimebase() const;
|
||||
|
||||
void InsertGapsAt(const rational& time, const rational& length, QUndoCommand* command);
|
||||
|
||||
void DeleteSelectedInternal(const QList<Block *>& blocks, bool transition_aware, bool remove_from_graph, QUndoCommand* command);
|
||||
@@ -431,6 +433,8 @@ private:
|
||||
|
||||
int deferred_scroll_value_;
|
||||
|
||||
bool use_audio_time_units_;
|
||||
|
||||
int GetTrackY(const TrackReference& ref);
|
||||
int GetTrackHeight(const TrackReference& ref);
|
||||
|
||||
@@ -438,6 +442,8 @@ private:
|
||||
|
||||
void AddGhost(TimelineViewGhostItem* ghost);
|
||||
|
||||
void UpdateViewTimebases();
|
||||
|
||||
private slots:
|
||||
void UpdateTimelineLength(const rational& length);
|
||||
|
||||
@@ -481,6 +487,14 @@ private slots:
|
||||
|
||||
void DeferredScrollAction();
|
||||
|
||||
void ShowSequenceDialog();
|
||||
|
||||
void SetUseAudioTimeUnits(bool use);
|
||||
|
||||
void SetViewTimestamp(const int64_t& ts);
|
||||
|
||||
void ViewTimestampChanged(int64_t ts);
|
||||
|
||||
};
|
||||
|
||||
#endif // TIMELINEWIDGET_H
|
||||
|
||||
@@ -142,9 +142,9 @@ void TimelineWidget::ImportTool::DragMove(TimelineViewMouseEvent *event)
|
||||
}
|
||||
|
||||
// Generate tooltip (showing earliest in point of imported clip)
|
||||
int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->timebase());
|
||||
int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->GetToolTipTimebase());
|
||||
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
|
||||
parent()->timebase(),
|
||||
parent()->GetToolTipTimebase(),
|
||||
Core::instance()->GetTimecodeDisplay());
|
||||
|
||||
// Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way
|
||||
@@ -232,11 +232,8 @@ void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QLi
|
||||
// Stream is essentially length-less - use config's default image length
|
||||
footage_duration = Config::Current()["DefaultStillLength"].value<rational>();
|
||||
} else {
|
||||
// Use duration from file
|
||||
int64_t stream_duration = stream->duration();
|
||||
|
||||
// Rescale to timeline timebase
|
||||
stream_duration = qCeil(static_cast<double>(stream_duration) * stream->timebase().toDouble() / dest_tb.toDouble());
|
||||
// Rescale stream duration to timeline timebase
|
||||
int64_t stream_duration = Timecode::rescale_timestamp_ceil(stream->duration(), stream->timebase(), dest_tb);
|
||||
|
||||
// Convert to rational time
|
||||
footage_duration = rational(dest_tb.numerator() * stream_duration,
|
||||
|
||||
@@ -401,9 +401,9 @@ void TimelineWidget::PointerTool::ProcessDrag(const TimelineCoordinate &mouse_po
|
||||
|
||||
// Show tooltip
|
||||
// Generate tooltip (showing earliest in point of imported clip)
|
||||
int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->timebase());
|
||||
int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->GetToolTipTimebase());
|
||||
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
|
||||
parent()->timebase(),
|
||||
parent()->GetToolTipTimebase(),
|
||||
Core::instance()->GetTimecodeDisplay(),
|
||||
true);
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ void TimelineWidget::SlipTool::ProcessDrag(const TimelineCoordinate &mouse_pos)
|
||||
|
||||
// Show tooltip
|
||||
// Generate tooltip (showing earliest in point of imported clip)
|
||||
int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->timebase());
|
||||
int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->GetToolTipTimebase());
|
||||
QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp,
|
||||
parent()->timebase(),
|
||||
parent()->GetToolTipTimebase(),
|
||||
Core::instance()->GetTimecodeDisplay(),
|
||||
true);
|
||||
// Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way
|
||||
|
||||
Reference in New Issue
Block a user