Merge branch 'master' into otio

This commit is contained in:
itsmattkc
2020-10-22 20:26:16 +11:00
149 changed files with 4350 additions and 2079 deletions
+267 -158
View File
@@ -30,6 +30,17 @@
#include "common/timecodefunctions.h"
#include "dialog/sequence/sequence.h"
#include "node/block/transition/transition.h"
#include "tool/add.h"
#include "tool/beam.h"
#include "tool/edit.h"
#include "tool/pointer.h"
#include "tool/razor.h"
#include "tool/ripple.h"
#include "tool/rolling.h"
#include "tool/slide.h"
#include "tool/slip.h"
#include "tool/transition.h"
#include "tool/zoom.h"
#include "tool/tool.h"
#include "trackview/trackview.h"
#include "widget/menu/menu.h"
@@ -107,6 +118,8 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
view->SetSnapService(this);
view->SetSelectionList(&selections_);
view->SetGhostList(&ghost_items_);
view_splitter_->addWidget(tview);
@@ -149,6 +162,8 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
SetMaximumScale(TimelineViewBase::kMaximumScale);
SetAutoSetTimebase(false);
connect(Core::instance(), &Core::ToolChanged, this, &TimelineWidget::ToolChanged);
}
TimelineWidget::~TimelineWidget()
@@ -161,20 +176,16 @@ TimelineWidget::~TimelineWidget()
void TimelineWidget::Clear()
{
QList<Block*> deselected_blocks;
QMap<Block*, TimelineViewBlockItem*>::const_iterator iterator;
for (iterator=block_items_.begin(); iterator!=block_items_.end(); iterator++) {
if (iterator.value()->isSelected()) {
deselected_blocks.append(iterator.key());
}
// Delete all items
for (auto iterator=block_items_.begin(); iterator!=block_items_.end(); iterator++) {
delete iterator.value();
}
block_items_.clear();
emit BlocksDeselected(deselected_blocks);
// Emit that we've deselected any selected blocks
SignalDeselectedAllBlocks();
// Set null timebase
SetTimebase(0);
}
@@ -224,10 +235,6 @@ void TimelineWidget::ScaleChangedEvent(const double &scale)
}
}
foreach (TimelineViewGhostItem* ghost, ghost_items_) {
ghost->SetScale(scale);
}
foreach (TimelineAndTrackView* view, views_) {
view->view()->SetScale(scale);
}
@@ -271,6 +278,10 @@ void TimelineWidget::DisconnectNodeInternal(ViewerOutput *n)
disconnect(n, &ViewerOutput::TimebaseChanged, this, &TimelineWidget::SetTimebase);
disconnect(n, &ViewerOutput::TrackHeightChanged, this, &TimelineWidget::TrackHeightChanged);
foreach (TrackOutput* track, n->GetTracks()) {
RemoveTrack(track);
}
ruler()->SetPlaybackCache(nullptr);
SetTimebase(0);
@@ -341,22 +352,6 @@ void TimelineWidget::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, X
}
}
TimelineWidget::DraggedFootage TimelineWidget::FootageToDraggedFootage(Footage *f)
{
return DraggedFootage(f, f->get_enabled_stream_flags());
}
QList<TimelineWidget::DraggedFootage> TimelineWidget::FootageToDraggedFootage(QList<Footage *> footage)
{
QList<DraggedFootage> df;
foreach (Footage* f, footage) {
df.append(FootageToDraggedFootage(f));
}
return df;
}
rational TimelineWidget::GetToolTipTimebase() const
{
if (GetConnectedNode() && use_audio_time_units_) {
@@ -367,34 +362,28 @@ rational TimelineWidget::GetToolTipTimebase() const
void TimelineWidget::SelectAll()
{
QList<Block*> blocks_selected;
QList<Block*> newly_selected_blocks;
QMap<Block*, TimelineViewBlockItem*>::const_iterator i;
for (i=block_items_.constBegin(); i!=block_items_.end(); i++) {
if (!i.value()->isSelected()) {
i.value()->setSelected(true);
blocks_selected.append(i.key());
for (auto it=block_items_.cbegin(); it!=block_items_.cend(); it++) {
if (!selected_blocks_.contains(it.key())) {
newly_selected_blocks.append(it.key());
AddSelection(it.key()->range(), it.value()->Track());
}
}
emit BlocksSelected(blocks_selected);
SignalSelectedBlocks(newly_selected_blocks, false);
}
void TimelineWidget::DeselectAll()
{
QList<Block*> blocks_deselected;
// Clear selections
selections_.clear();
QMap<Block*, TimelineViewBlockItem*>::const_iterator i;
// Update all viewports
UpdateViewports();
for (i=block_items_.constBegin(); i!=block_items_.end(); i++) {
if (i.value()->isSelected()) {
i.value()->setSelected(false);
blocks_deselected.append(i.key());
}
}
emit BlocksDeselected(blocks_deselected);
// Clear list and emit signal
SignalDeselectedAllBlocks();
}
void TimelineWidget::RippleToIn()
@@ -486,8 +475,6 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QList<Block *> &blocks,
new TrackReplaceBlockWithGapCommand(original_track, b, command);
if (remove_from_graph) {
new BlockUnlinkAllCommand(b, command);
new NodeRemoveWithExclusiveDeps(static_cast<NodeGraph*>(b->parent()), b, command);
}
}
@@ -497,16 +484,11 @@ void TimelineWidget::DeleteSelected(bool ripple)
{
QList<TimelineViewBlockItem *> selected_list = GetSelectedBlocks();
QList<Block*> blocks_to_delete;
QList<TrackReference> tracks_affected;
foreach (TimelineViewBlockItem* item, selected_list) {
Block* b = item->block();
blocks_to_delete.append(b);
if (!tracks_affected.contains(item->Track())) {
tracks_affected.append(item->Track());
}
}
// No-op if nothing is selected
@@ -541,6 +523,9 @@ void TimelineWidget::DeleteSelected(bool ripple)
// Replace clips with gaps (effectively deleting them)
ReplaceBlocksWithGaps(clips_to_delete, true, command);
// Remove all selections
new TimelineSetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections(), command);
// Insert ripple command now that it's all cleaned up gaps
if (ripple) {
TimeRangeList range_list;
@@ -767,16 +752,8 @@ QList<TimelineViewBlockItem *> TimelineWidget::GetSelectedBlocks()
{
QList<TimelineViewBlockItem *> list;
QMapIterator<Block*, TimelineViewBlockItem*> iterator(block_items_);
while (iterator.hasNext()) {
iterator.next();
TimelineViewBlockItem* item = iterator.value();
if (item && item->isSelected()) {
list.append(item);
}
foreach (Block* b, selected_blocks_) {
list.append(block_items_.value(b));
}
return list;
@@ -825,12 +802,7 @@ void TimelineWidget::ClearGhosts()
HideSnaps();
}
bool TimelineWidget::HasGhosts()
{
return !ghost_items_.isEmpty();
}
TimelineWidget::Tool *TimelineWidget::GetActiveTool()
TimelineTool *TimelineWidget::GetActiveTool()
{
return tools_.at(Core::instance()->tool());
}
@@ -857,7 +829,7 @@ void TimelineWidget::ViewMouseMoved(TimelineViewMouseEvent *event)
active_tool_->MouseMove(event);
} else {
// Mouse is not down, attempt a hover event
Tool* hover_tool = GetActiveTool();
TimelineTool* hover_tool = GetActiveTool();
if (hover_tool) {
hover_tool->HoverMove(event);
@@ -876,9 +848,8 @@ void TimelineWidget::ViewMouseReleased(TimelineViewMouseEvent *event)
void TimelineWidget::ViewMouseDoubleClicked(TimelineViewMouseEvent *event)
{
if (GetConnectedNode() && active_tool_ != nullptr) {
active_tool_->MouseDoubleClick(event);
active_tool_ = nullptr;
if (GetConnectedNode()) {
GetActiveTool()->MouseDoubleClick(event);
}
}
@@ -905,40 +876,72 @@ void TimelineWidget::ViewDragDropped(TimelineViewMouseEvent *event)
void TimelineWidget::AddBlock(Block *block, TrackReference track)
{
// Set up clip with view parameters (clip item will automatically size its rect accordingly)
TimelineViewBlockItem* item = new TimelineViewBlockItem(block);
TimelineViewBlockItem* item = block_items_.value(block);
item->SetYCoords(GetTrackY(track), GetTrackHeight(track));
item->SetScale(GetScale());
item->SetTrack(track);
item->SetTimebase(timebase());
if (!item) {
// Add to list of clip items that can be iterated through
block_items_.insert(block, item);
// Add to list of clip items that can be iterated through
item = new TimelineViewBlockItem(block);
block_items_.insert(block, item);
// Add item to graphics scene
views_.at(track.type())->view()->scene()->addItem(item);
// Set scale parameters
item->SetScale(GetScale());
item->SetTimebase(timebase());
item->SetYCoords(GetTrackY(track), GetTrackHeight(track));
item->SetTrack(track);
connect(block, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed);
connect(block, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated);
connect(block, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated);
connect(block, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated);
// Add item to graphics scene
views_.at(track.type())->view()->scene()->addItem(item);
connect(block, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed);
connect(block, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated);
connect(block, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated);
connect(block, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated);
} else if (item->Track() != track) {
item->SetYCoords(GetTrackY(track), GetTrackHeight(track));
item->SetTrack(track);
}
}
void TimelineWidget::RemoveBlock(Block *block)
void TimelineWidget::RemoveBlock(const QList<Block *> &blocks)
{
disconnect(block, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed);
disconnect(block, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated);
disconnect(block, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated);
disconnect(block, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated);
QList<TimelineViewBlockItem*> delete_items;
delete_items.reserve(blocks.size());
TimelineViewBlockItem* item = block_items_.take(block);
QList<Block*> deselect_blocks;
if (item->isSelected()) {
// Sending a list of one item all the time is not very efficient
emit BlocksDeselected({block});
foreach (Block* b, blocks) {
// Disconnect all signals
disconnect(b, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed);
disconnect(b, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated);
disconnect(b, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated);
disconnect(b, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated);
// Take item from map
TimelineViewBlockItem* item = block_items_.take(b);
// If selected, deselect it
int select_index = selected_blocks_.indexOf(b);
if (select_index > -1) {
selected_blocks_.removeAt(select_index);
deselect_blocks.append(b);
RemoveSelection(item);
}
// Finally, delete item
delete item;
}
delete item;
if (!deselect_blocks.isEmpty()) {
// We already removed the blocks from selected_blocks_, so we can signal directly rather than
// through
emit BlocksDeselected(deselect_blocks);
}
qDeleteAll(delete_items);
}
void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type)
@@ -956,9 +959,7 @@ void TimelineWidget::RemoveTrack(TrackOutput *track)
disconnect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged);
disconnect(track, &TrackOutput::PreviewChanged, this, &TimelineWidget::TrackPreviewUpdated);
foreach (Block* b, track->Blocks()) {
RemoveBlock(b);
}
RemoveBlock(track->Blocks());
}
void TimelineWidget::TrackIndexChanged()
@@ -1142,11 +1143,16 @@ void TimelineWidget::ViewTimestampChanged(int64_t ts)
emit TimeChanged(ts);
}
void TimelineWidget::ToolChanged()
{
HideSnaps();
}
void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost)
{
ghost->SetScale(GetScale());
ghost_items_.append(ghost);
views_.at(ghost->Track().type())->view()->scene()->addItem(ghost);
UpdateViewports(ghost->GetTrack().type());
}
void TimelineWidget::UpdateViewTimebases()
@@ -1174,12 +1180,80 @@ void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected)
TimelineViewBlockItem* link_item;
foreach (Block* link, block->linked_clips()) {
if ((link_item = block_items_[link]) != nullptr) {
link_item->setSelected(selected);
link_item = block_items_.value(link);
if (link_item) {
if (selected) {
AddSelection(link_item);
} else {
RemoveSelection(link_item);
}
}
}
}
void TimelineWidget::QueueScroll(int value)
{
// (using a hacky singleShot so the scroll occurs after the scene and its scrollbars have updated)
deferred_scroll_value_ = value;
QTimer::singleShot(0, this, &TimelineWidget::DeferredScrollAction);
}
TimelineView *TimelineWidget::GetFirstTimelineView()
{
return views_.first()->view();
}
const QRect& TimelineWidget::GetRubberBandGeometry() const
{
return rubberband_.geometry();
}
void TimelineWidget::SignalSelectedBlocks(QList<Block *> input, bool filter)
{
if (input.isEmpty()) {
return;
}
if (filter) {
// If filtering, remove all the blocks that are already selected
for (int i=0; i<input.size(); i++) {
Block* b = input.at(i);
if (selected_blocks_.contains(b)) {
input.removeAt(i);
i--;
}
}
}
selected_blocks_.append(input);
emit BlocksSelected(input);
}
void TimelineWidget::SignalDeselectedBlocks(const QList<Block *> &deselected_blocks)
{
if (deselected_blocks.isEmpty()) {
return;
}
foreach (Block* b, deselected_blocks) {
selected_blocks_.removeOne(b);
}
emit BlocksDeselected(deselected_blocks);
}
void TimelineWidget::SignalDeselectedAllBlocks()
{
if (!selected_blocks_.isEmpty()) {
emit BlocksDeselected(selected_blocks_);
selected_blocks_.clear();
}
}
QVector<Timeline::EditToInfo> TimelineWidget::GetEditToInfo(const rational& playhead_time,
Timeline::MovementMode mode)
{
@@ -1321,6 +1395,17 @@ void TimelineWidget::ShowSnap(const QList<rational> &times)
}
}
void TimelineWidget::UpdateViewports(const Timeline::TrackType &type)
{
if (type == Timeline::kTrackTypeNone) {
foreach (TimelineAndTrackView* tview, views_) {
tview->view()->viewport()->update();
}
} else {
views_.at(type)->view()->viewport()->update();
}
}
void TimelineWidget::HideSnaps()
{
foreach (TimelineAndTrackView* tview, views_) {
@@ -1344,12 +1429,8 @@ void TimelineWidget::StartRubberBandSelect(bool enable_selecting, bool select_li
rubberband_.show();
// We don't touch any blocks that are already selected. If you want these to be deselected by
// default, call DeselectAll() befoer calling StartRubberBandSelect()
foreach (TimelineViewBlockItem* block, block_items_) {
if (block->isSelected()) {
rubberband_already_selected_.append(block);
}
}
// default, call DeselectAll() before calling StartRubberBandSelect()
rubberband_old_selections_ = selections_;
MoveRubberBandSelect(enable_selecting, select_links);
}
@@ -1364,7 +1445,7 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin
return;
}
QList<QGraphicsItem*> new_selected_list;
QList<QGraphicsItem*> items_in_rubberband;
// Determine all items in the rubberband
foreach (TimelineAndTrackView* tview, views_) {
@@ -1377,60 +1458,45 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin
// Normalize and get items in rect
QList<QGraphicsItem*> rubberband_items = view->items(mapped_rect.normalized());
new_selected_list.append(rubberband_items);
items_in_rubberband.append(rubberband_items);
}
// Filter out any items that were already selected
if (!rubberband_already_selected_.isEmpty()) {
for (int i=0; i<new_selected_list.size(); i++) {
if (rubberband_already_selected_.contains(new_selected_list.at(i))) {
new_selected_list.removeAt(i);
i--;
}
}
}
// Reset selection to whatever it was before
SetSelections(rubberband_old_selections_);
foreach (QGraphicsItem* item, rubberband_now_selected_) {
item->setSelected(false);
}
// Add any blocks in rubberband
rubberband_now_selected_.clear();
// Cache limit because we append to this array in this loop and don't need to process those
int lim = new_selected_list.size();
for (int i=0;i<lim;i++) {
TimelineViewBlockItem* block_item = static_cast<TimelineViewBlockItem*>(new_selected_list.at(i));
if (block_item->block()->type() == Block::kGap) {
continue;
}
foreach (QGraphicsItem* item, items_in_rubberband) {
TimelineViewBlockItem* block_item = dynamic_cast<TimelineViewBlockItem*>(item);
TrackOutput* t = GetTrackFromReference(block_item->Track());
if (t && t->IsLocked()) {
continue;
}
// Since new_selected_list is filtered by rubberband_already_selected_, this should certainly
// be deselected by now
block_item->setSelected(true);
if (select_links) {
// Select the block's links
if (block_item) {
Block* b = block_item->block();
// Add its links to the list
TimelineViewBlockItem* link_item;
foreach (Block* link, b->linked_clips()) {
if ((link_item = block_items_[link]) != nullptr) {
link_item->setSelected(true);
if (b->type() == Block::kGap) {
continue;
}
if (!new_selected_list.contains(link_item)
&& !rubberband_already_selected_.contains(link_item)) {
new_selected_list.append(link_item);
TrackOutput* t = GetTrackFromReference(block_item->Track());
if (t && t->IsLocked()) {
continue;
}
if (!rubberband_now_selected_.contains(b)) {
AddSelection(block_item);
rubberband_now_selected_.append(b);
}
if (select_links) {
foreach (Block* link, b->linked_clips()) {
if (!rubberband_now_selected_.contains(link)) {
AddSelection(block_items_.value(link));
rubberband_now_selected_.append(link);
}
}
}
}
}
rubberband_now_selected_ = new_selected_list;
}
void TimelineWidget::EndRubberBandSelect()
@@ -1438,14 +1504,57 @@ void TimelineWidget::EndRubberBandSelect()
rubberband_.hide();
// Emit any blocks that were newly selected
QList<Block*> selected_blocks;
foreach (QGraphicsItem* item, rubberband_now_selected_) {
selected_blocks.append(static_cast<TimelineViewBlockItem*>(item)->block());
}
emit BlocksSelected(selected_blocks);
SignalSelectedBlocks(rubberband_now_selected_);
rubberband_now_selected_.clear();
rubberband_already_selected_.clear();
rubberband_old_selections_.clear();
}
void TimelineWidget::AddSelection(const TimeRange &time, const TrackReference &track)
{
selections_[track].InsertTimeRange(time);
UpdateViewports(track.type());
}
void TimelineWidget::AddSelection(TimelineViewBlockItem *item)
{
AddSelection(item->block()->range(), item->Track());
}
void TimelineWidget::RemoveSelection(const TimeRange &time, const TrackReference &track)
{
selections_[track].RemoveTimeRange(time);
UpdateViewports(track.type());
}
void TimelineWidget::RemoveSelection(TimelineViewBlockItem *item)
{
RemoveSelection(item->block()->range(), item->Track());
}
void TimelineWidget::SetSelections(const TimelineWidgetSelections &s)
{
selections_ = s;
UpdateViewports();
}
TimelineViewBlockItem *TimelineWidget::GetItemAtScenePos(const TimelineCoordinate& coord)
{
for (auto it=block_items_.cbegin(); it!=block_items_.cend(); it++) {
Block* b = it.key();
TimelineViewBlockItem* item = it.value();
if (b->in() <= coord.GetFrame()
&& b->out() > coord.GetFrame()
&& item->Track() == coord.GetTrack()) {
return item;
}
}
return nullptr;
}
struct SnapData {