From 3411e45774684a08c03c2862917e20ff7cb1c73a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 5 Oct 2019 07:33:27 +1000 Subject: [PATCH] preliminary support for multiple timeline sections --- app/node/graph.cpp | 8 +- app/node/graph.h | 1 + app/node/node.cpp | 60 ++-- app/node/node.h | 14 +- app/node/output/timeline/CMakeLists.txt | 2 + app/node/output/timeline/timeline.cpp | 222 +++------------ app/node/output/timeline/timeline.h | 88 ++---- app/node/output/timeline/tracklist.cpp | 219 +++++++++++++++ app/node/output/timeline/tracklist.h | 113 ++++++++ app/node/output/track/track.cpp | 14 + app/node/output/track/track.h | 2 + app/node/output/tracklist/CMakeLists.txt | 22 ++ app/node/output/tracklist/tracklist.cpp | 256 ++++++++++++++++++ app/node/output/tracklist/tracklist.h | 130 +++++++++ app/project/item/sequence/sequence.cpp | 18 +- app/project/item/sequence/sequence.h | 3 +- .../nodeparamview/nodeparamviewitem.cpp | 12 +- app/widget/nodeview/nodeview.cpp | 4 +- app/widget/nodeview/nodeviewitem.cpp | 19 +- app/widget/timelineview/timelineview.cpp | 2 +- app/widget/timelineview/timelineview.h | 4 +- app/widget/timelineview/undo/undo.cpp | 2 +- app/widget/timelineview/undo/undo.h | 4 +- app/widget/timelinewidget/CMakeLists.txt | 23 ++ app/widget/timelinewidget/timelinewidget.cpp | 11 +- app/widget/timelinewidget/timelinewidget.h | 7 - 26 files changed, 897 insertions(+), 363 deletions(-) create mode 100644 app/node/output/timeline/tracklist.cpp create mode 100644 app/node/output/timeline/tracklist.h create mode 100644 app/node/output/tracklist/CMakeLists.txt create mode 100644 app/node/output/tracklist/tracklist.cpp create mode 100644 app/node/output/tracklist/tracklist.h create mode 100644 app/widget/timelinewidget/CMakeLists.txt diff --git a/app/node/graph.cpp b/app/node/graph.cpp index 185bdb0f4..da4e9bc17 100644 --- a/app/node/graph.cpp +++ b/app/node/graph.cpp @@ -20,8 +20,6 @@ #include "graph.h" -#include "common/qobjectlistcast.h" - NodeGraph::NodeGraph() { @@ -38,6 +36,8 @@ void NodeGraph::AddNode(Node *node) connect(node, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SIGNAL(EdgeAdded(NodeEdgePtr))); connect(node, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SIGNAL(EdgeRemoved(NodeEdgePtr))); + node_children_.append(node); + emit NodeAdded(node); } @@ -71,6 +71,8 @@ void NodeGraph::TakeNode(Node *node, QObject* new_parent) node->setParent(new_parent); + node_children_.removeAll(node); + emit NodeRemoved(node); } @@ -91,7 +93,7 @@ QList NodeGraph::TakeNodeWithItsDependencies(Node *node, QObject *new_pa QList NodeGraph::nodes() { - return static_qobjectlist_cast(children()); + return node_children_; } bool NodeGraph::ContainsNode(Node *n) diff --git a/app/node/graph.h b/app/node/graph.h index 2eab8ab28..7144be466 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -107,6 +107,7 @@ signals: void EdgeRemoved(NodeEdgePtr edge); private: + QList node_children_; }; #endif // NODEGRAPH_H diff --git a/app/node/node.cpp b/app/node/node.cpp index 7645ee169..108e7b00b 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -22,8 +22,6 @@ #include -#include "common/qobjectlistcast.h" - Node::Node() : last_processed_time_(-1), can_be_deleted_(true) @@ -56,6 +54,7 @@ void Node::AddParameter(NodeParam *param) Q_ASSERT(!HasParamWithID(param->id())); param->setParent(this); + params_.append(param); connect(param, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SIGNAL(EdgeAdded(NodeEdgePtr))); connect(param, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SIGNAL(EdgeRemoved(NodeEdgePtr))); @@ -69,6 +68,7 @@ void Node::AddParameter(NodeParam *param) void Node::RemoveParameter(NodeParam *param) { + params_.removeAll(param); delete param; } @@ -83,10 +83,8 @@ void Node::InvalidateCache(const rational &start_range, const rational &end_rang void Node::SendInvalidateCache(const rational &start_range, const rational &end_range) { - QList params = parameters(); - // Loop through all parameters (there should be no children that are not NodeParams) - foreach (NodeParam* param, params) { + foreach (NodeParam* param, params_) { // If the Node is an output, relay the signal to any Nodes that are connected to it if (param->type() == NodeParam::kOutput) { @@ -117,8 +115,8 @@ void Node::CopyInputs(Node *source, Node *destination) { Q_ASSERT(source->id() == destination->id()); - QList src_param = source->parameters(); - QList dst_param = destination->parameters(); + const QList& src_param = source->params_; + const QList& dst_param = destination->params_; for (int i=0;itype() == NodeParam::kInput) { @@ -174,10 +172,8 @@ void Node::ClearCachedValuesInParameters(const rational &start_range, const rati Q_UNUSED(start_range) Q_UNUSED(end_range) - QList params = parameters(); - // Loop through all parameters and clear cached values - foreach (NodeParam* param, params) { + foreach (NodeParam* param, params_) { //if (param->LastRequestedTime() >= start_range && param->LastRequestedTime() <= end_range) { param->ClearCachedValue(); //} @@ -195,19 +191,9 @@ QVariant Node::Run(NodeOutput* output, const rational& time) return v; } -NodeParam *Node::ParamAt(int index) +const QList& Node::parameters() { - return static_cast(children().at(index)); -} - -QList Node::parameters() -{ - return static_qobjectlist_cast(children()); -} - -int Node::ParameterCount() -{ - return children().size(); + return params_; } int Node::IndexOfParameter(NodeParam *param) @@ -224,9 +210,7 @@ int Node::IndexOfParameter(NodeParam *param) * dependencies. */ void GetDependenciesInternal(Node* n, QList& list, bool traverse) { - QList params = n->parameters(); - - foreach (NodeParam* p, params) { + foreach (NodeParam* p, n->parameters()) { if (p->type() == NodeParam::kInput) { Node* connected = static_cast(p)->get_connected_node(); @@ -256,7 +240,7 @@ QList Node::GetExclusiveDependencies() // Filter out any dependencies that are used elsewhere for (int i=0;i params = deps.at(i)->parameters(); + QList params = deps.at(i)->params_; // See if any of this Node's outputs are used outside of this dep list for (int j=0;j Node::RunDependencies(NodeOutput *output, const rational & { Q_UNUSED(output) - QList params = parameters(); QList run_deps; - foreach (NodeParam* p, params) { + foreach (NodeParam* p, params_) { if (p->type() == NodeParam::kInput) { NodeInput* input = static_cast(p); @@ -321,9 +304,7 @@ QList Node::RunDependencies(NodeOutput *output, const rational & bool Node::OutputsTo(Node *n) { - QList params = parameters(); - - foreach (NodeParam* param, params) { + foreach (NodeParam* param, params_) { if (param->type() == NodeParam::kOutput) { QVector edges = param->edges(); @@ -360,10 +341,8 @@ bool Node::HasConnectedOutputs() void Node::DisconnectAll() { - QList param = parameters(); - - for (int i=0;iDisconnectAll(); + foreach (NodeParam* param, params_) { + param->DisconnectAll(); } } @@ -373,8 +352,7 @@ void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time hash->addData(id().toUtf8()); // Add each value - QList params = parameters(); - foreach (NodeParam* param, params) { + foreach (NodeParam* param, params_) { if (param->type() == NodeParam::kInput && !param->IsConnected() && static_cast(param)->dependent()) { @@ -400,9 +378,7 @@ QVariant Node::PtrToValue(void *ptr) bool Node::HasParamWithID(const QString &id) { - QList params = parameters(); - - foreach (NodeParam* p, params) + foreach (NodeParam* p, params_) { if (p->id() == id) { @@ -415,9 +391,7 @@ bool Node::HasParamWithID(const QString &id) bool Node::HasParamOfType(NodeParam::Type type, bool must_be_connected) { - QList params = parameters(); - - foreach (NodeParam* p, params) { + foreach (NodeParam* p, params_) { if (p->type() == type && (p->IsConnected() || !must_be_connected)) { return true; diff --git a/app/node/node.h b/app/node/node.h index 958b0689b..715ecd4a8 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -95,20 +95,10 @@ public: */ virtual void Retranslate(); - /** - * @brief Return the parameter at a given index - */ - NodeParam* ParamAt(int index); - /** * @brief Return a list of NodeParams */ - QList parameters(); - - /** - * @brief Return the current number of parameters - */ - int ParameterCount(); + const QList& parameters(); /** * @brief Return the index of a parameter @@ -307,6 +297,8 @@ private: bool HasParamOfType(NodeParam::Type type, bool must_be_connected); + QList params_; + /** * @brief The last timecode Process() was called with */ diff --git a/app/node/output/timeline/CMakeLists.txt b/app/node/output/timeline/CMakeLists.txt index e0f02ef2c..c37d1a788 100644 --- a/app/node/output/timeline/CMakeLists.txt +++ b/app/node/output/timeline/CMakeLists.txt @@ -18,5 +18,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} node/output/timeline/timeline.h node/output/timeline/timeline.cpp + node/output/timeline/tracklist.h + node/output/timeline/tracklist.cpp PARENT_SCOPE ) diff --git a/app/node/output/timeline/timeline.cpp b/app/node/output/timeline/timeline.cpp index fffb802e2..143b10a67 100644 --- a/app/node/output/timeline/timeline.cpp +++ b/app/node/output/timeline/timeline.cpp @@ -22,27 +22,31 @@ #include -#include "node/blend/alphaover/alphaover.h" #include "node/block/gap/gap.h" #include "node/graph.h" #include "panel/timeline/timeline.h" TimelineOutput::TimelineOutput() { - video_track_input_ = new NodeInput("video_track_in"); - video_track_input_->add_data_input(NodeParam::kTrack); - AddParameter(video_track_input_); + // Create TrackList instances + track_inputs_.resize(kTrackTypeCount); + track_lists_.resize(kTrackTypeCount); - audio_track_input_ = new NodeInput("audio_track_in"); - audio_track_input_->add_data_input(NodeParam::kTrack); - AddParameter(audio_track_input_); + for (int i=0;iadd_data_input(NodeParam::kTrack); + AddParameter(track_input); + track_inputs_[i] = track_input; + + TrackList* list = new TrackList(this, track_input); + track_lists_[i] = list; + connect(list, SIGNAL(TrackListChanged()), this, SLOT(UpdateTrackCache())); + } length_output_ = new NodeOutput("length_out"); length_output_->set_data_type(NodeParam::kRational); AddParameter(length_output_); - - connect(this, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackConnectionAdded(NodeEdgePtr))); - connect(this, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackConnectionRemoved(NodeEdgePtr))); } QString TimelineOutput::Name() @@ -65,14 +69,9 @@ QString TimelineOutput::Description() return tr("Node for communicating between a Timeline panel and the node graph."); } -const rational &TimelineOutput::Timebase() +QVector TimelineOutput::Tracks() { - return timebase_; -} - -NodeInput *TimelineOutput::track_input() -{ - return video_track_input_; + return track_cache_; } NodeOutput *TimelineOutput::length_output() @@ -80,201 +79,50 @@ NodeOutput *TimelineOutput::length_output() return length_output_; } -const QVector &TimelineOutput::Tracks() -{ - return track_cache_; -} - -TrackOutput *TimelineOutput::TrackAt(int index) -{ - return track_cache_.at(index); -} - QVariant TimelineOutput::Value(NodeOutput *output, const rational &time) { if (output == length_output_) { Q_UNUSED(time) - return QVariant::fromValue(TimelineLength()); + return QVariant::fromValue(timeline_length()); } return 0; } -rational TimelineOutput::TimelineLength() +void TimelineOutput::UpdateTrackCache() +{ + track_cache_.clear(); + + foreach (TrackList* list, track_lists_) { + track_cache_.append(list->Tracks()); + } +} + +rational TimelineOutput::timeline_length() { rational length = 0; - foreach (TrackOutput* track, track_cache_) { - length = qMax(length, track->in()); + foreach (TrackList* list, track_lists_) { + length = qMax(list->TrackListLength(), length); } return length; } -TrackOutput *TimelineOutput::attached_track() -{ - return ValueToPtr(video_track_input_->get_value(0)); -} - -void TimelineOutput::AttachTrack(TrackOutput *track) -{ - TrackOutput* current_track = track; - - // Traverse through Tracks caching and connecting them - while (current_track != nullptr) { - connect(current_track, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackEdgeAdded(NodeEdgePtr))); - connect(current_track, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackEdgeRemoved(NodeEdgePtr))); - connect(current_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*))); - connect(current_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*))); - - current_track->SetIndex(track_cache_.size()); - - track_cache_.append(current_track); - - // This function must be called after the track is added to track_cache_, since it uses track_cache_ to determine - // the track's index - emit TrackAdded(current_track); - - current_track = current_track->next_track(); - } -} - -void TimelineOutput::DetachTrack(TrackOutput *track) -{ - TrackOutput* current_track = track; - - // Traverse through Tracks uncaching and disconnecting them - while (current_track != nullptr) { - emit TrackRemoved(current_track); - - current_track->SetIndex(-1); - - disconnect(current_track, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackEdgeAdded(NodeEdgePtr))); - disconnect(current_track, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackEdgeRemoved(NodeEdgePtr))); - disconnect(current_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*))); - disconnect(current_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*))); - - track_cache_.removeAll(current_track); - - current_track = current_track->next_track(); - } -} - void TimelineOutput::SetTimebase(const rational &timebase) { - timebase_ = timebase; - - emit TimebaseChanged(timebase_); -} - -void TimelineOutput::AddTrack() -{ - TrackOutput* track = new TrackOutput(); - static_cast(parent())->AddNode(track); - - if (track_cache_.isEmpty()) { - // Connect this track directly to this output - NodeParam::ConnectEdge(track->track_output(), track_input()); - } else { - TrackOutput* current_last_track = track_cache_.last(); - - // Connect this track to the current last track - NodeParam::ConnectEdge(track->track_output(), current_last_track->track_input()); - - // FIXME: Test code only - AlphaOverBlend* blend = new AlphaOverBlend(); - static_cast(parent())->AddNode(blend); - - NodeParam::ConnectEdge(track->texture_output(), blend->blend_input()); - NodeParam::ConnectEdge(current_last_track->texture_output(), blend->base_input()); - NodeParam::ConnectEdge(blend->texture_output(), current_last_track->texture_output()->edges().first()->input()); - // End test code + foreach (TrackList* list, track_lists_) { + list->SetTimebase(timebase); } } -void TimelineOutput::RemoveTrack() +NodeInput *TimelineOutput::track_input(TimelineOutput::TrackType type) { - if (track_cache_.isEmpty()) { - return; - } - - TrackOutput* track = track_cache_.last(); - - static_cast(parent())->TakeNode(track); - - delete track; + return track_inputs_.at(type); } -TrackOutput *TimelineOutput::TrackFromBlock(Block *block) +TrackList *TimelineOutput::track_list(TimelineOutput::TrackType type) { - Block* n = block; - - // Find last valid block in Sequence and assume its a track - while (n->next() != nullptr) { - n = n->next(); - } - - // Downside of this approach is the usage of dynamic_cast, alternative would be looping through all known tracks and - // seeing if the contain the Block, but this seems slower - return dynamic_cast(n); -} - -void TimelineOutput::TrackConnectionAdded(NodeEdgePtr edge) -{ - if (edge->input() != track_input()) { - return; - } - - AttachTrack(attached_track()); - - // FIXME: Is this necessary? - emit TimebaseChanged(timebase_); -} - -void TimelineOutput::TrackConnectionRemoved(NodeEdgePtr edge) -{ - if (edge->input() != track_input()) { - return; - } - - DetachTrack(ValueToPtr(edge->output()->get_value(0))); - - emit TimelineCleared(); -} - -void TimelineOutput::TrackAddedBlock(Block *block) -{ - emit BlockAdded(block, static_cast(sender())->Index()); -} - -void TimelineOutput::TrackRemovedBlock(Block *block) -{ - emit BlockRemoved(block); -} - -void TimelineOutput::TrackEdgeAdded(NodeEdgePtr edge) -{ - // Assume this signal was sent from a TrackOutput - TrackOutput* track = static_cast(sender()); - - // If this edge pertains to the track's track input, all the tracks just added need attaching - if (edge->input() == track->track_input()) { - TrackOutput* added_track = ValueToPtr(edge->output()->get_value(0)); - - AttachTrack(added_track); - } -} - -void TimelineOutput::TrackEdgeRemoved(NodeEdgePtr edge) -{ - // Assume this signal was sent from a TrackOutput - TrackOutput* track = static_cast(sender()); - - // If this edge pertains to the track's track input, all the tracks just added need attaching - if (edge->input() == track->track_input()) { - TrackOutput* added_track = ValueToPtr(edge->output()->get_value(0)); - - DetachTrack(added_track); - } + return track_lists_.at(type); } diff --git a/app/node/output/timeline/timeline.h b/app/node/output/timeline/timeline.h index d98f8bcbc..bcfb4a4f7 100644 --- a/app/node/output/timeline/timeline.h +++ b/app/node/output/timeline/timeline.h @@ -24,6 +24,7 @@ #include "common/timelinecommon.h" #include "node/block/block.h" #include "node/output/track/track.h" +#include "tracklist.h" /** * @brief Node that represents the end of the Timeline as well as a time traversal Node @@ -33,9 +34,11 @@ class TimelineOutput : public Node Q_OBJECT public: enum TrackType { - kVideo, - kAudio, - kSubtitle + kTrackTypeNone = -1, + kTrackTypeVideo, + kTrackTypeAudio, + kTrackTypeSubtitle, + kTrackTypeCount }; TimelineOutput(); @@ -45,91 +48,34 @@ public: virtual QString Category() override; virtual QString Description() override; - const rational& Timebase(); - void SetTimebase(const rational& timebase); + QVector Tracks(); - NodeInput* track_input(); + NodeInput* track_input(TrackType type); + + TrackList* track_list(TrackType type); NodeOutput* length_output(); - const QVector& Tracks(); + rational timeline_length(); - TrackOutput* TrackAt(int index); - - void AddTrack(); - - void RemoveTrack(); - - rational TimelineLength(); - -public slots: - /** - * @brief Slot for when the track connection is added - */ - void TrackConnectionAdded(NodeEdgePtr edge); - - /** - * @brief Slot for when the track connection is removed - */ - void TrackConnectionRemoved(NodeEdgePtr edge); - - /** - * @brief Slot for when a connected Track has added a Block so we can update the UI - */ - void TrackAddedBlock(Block* block); - - /** - * @brief Slot for when a connected Track has added a Block so we can update the UI - */ - void TrackRemovedBlock(Block* block); - - /** - * @brief Slot for when an attached Track has an edge added - */ - void TrackEdgeAdded(NodeEdgePtr edge); - - /** - * @brief Slot for when an attached Track has an edge added - */ - void TrackEdgeRemoved(NodeEdgePtr edge); + void SetTimebase(const rational &timebase); signals: - void TimebaseChanged(const rational &timebase); - - void TimelineCleared(); - - void BlockAdded(Block* block, int index); - - void BlockRemoved(Block* block); - - void TrackAdded(TrackOutput* track); - - void TrackRemoved(TrackOutput* track); protected: virtual QVariant Value(NodeOutput* output, const rational& time) override; private: - TrackOutput* attached_track(); + QVector track_inputs_; - void AttachTrack(TrackOutput *track); + QVector track_lists_; - void DetachTrack(TrackOutput* track); - - static TrackOutput* TrackFromBlock(Block* block); - - NodeInput* video_track_input_; - - NodeInput* audio_track_input_; + QVector track_cache_; NodeOutput* length_output_; - /** - * @brief A cache of connected Tracks - */ - QVector track_cache_; - - rational timebase_; +private slots: + void UpdateTrackCache(); }; diff --git a/app/node/output/timeline/tracklist.cpp b/app/node/output/timeline/tracklist.cpp new file mode 100644 index 000000000..1cfb4c208 --- /dev/null +++ b/app/node/output/timeline/tracklist.cpp @@ -0,0 +1,219 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "tracklist.h" + +#include "node/blend/alphaover/alphaover.h" +#include "node/graph.h" +#include "timeline.h" + +TrackList::TrackList(TimelineOutput* parent, NodeInput *track_input) : + QObject(parent), + track_input_(track_input) +{ + connect(parent, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackConnectionAdded(NodeEdgePtr))); + connect(parent, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackConnectionRemoved(NodeEdgePtr))); +} + +TrackOutput *TrackList::attached_track() +{ + return Node::ValueToPtr(track_input_->get_value(0)); +} + +void TrackList::AttachTrack(TrackOutput *track) +{ + TrackOutput* current_track = track; + + // Traverse through Tracks caching and connecting them + while (current_track != nullptr) { + connect(current_track, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackEdgeAdded(NodeEdgePtr))); + connect(current_track, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackEdgeRemoved(NodeEdgePtr))); + connect(current_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*))); + connect(current_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*))); + + current_track->SetIndex(track_cache_.size()); + + track_cache_.append(current_track); + emit TrackListChanged(); + + // This function must be called after the track is added to track_cache_, since it uses track_cache_ to determine + // the track's index + emit TrackAdded(current_track); + + current_track = current_track->next_track(); + } +} + +void TrackList::DetachTrack(TrackOutput *track) +{ + TrackOutput* current_track = track; + + // Traverse through Tracks uncaching and disconnecting them + while (current_track != nullptr) { + emit TrackRemoved(current_track); + + current_track->SetIndex(-1); + + disconnect(current_track, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackEdgeAdded(NodeEdgePtr))); + disconnect(current_track, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackEdgeRemoved(NodeEdgePtr))); + disconnect(current_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*))); + disconnect(current_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*))); + + track_cache_.removeAll(current_track); + emit TrackListChanged(); + + current_track = current_track->next_track(); + } +} + +void TrackList::TrackAddedBlock(Block *block) +{ + emit BlockAdded(block, static_cast(sender())->Index()); +} + +void TrackList::TrackRemovedBlock(Block *block) +{ + emit BlockRemoved(block); +} + +const QVector &TrackList::Tracks() +{ + return track_cache_; +} + +TrackOutput *TrackList::TrackAt(int index) +{ + return track_cache_.at(index); +} + +rational TrackList::TrackListLength() +{ + rational length = 0; + + foreach (TrackOutput* track, track_cache_) { + length = qMax(length, track->in()); + } + + return length; +} + +rational TrackList::TimelineLength() +{ + return static_cast(parent())->timeline_length(); +} + +const rational &TrackList::Timebase() +{ + return timebase_; +} + +void TrackList::SetTimebase(const rational &timebase) +{ + timebase_ = timebase; + + emit TimebaseChanged(timebase_); +} + +void TrackList::AddTrack() +{ + TrackOutput* track = new TrackOutput(); + static_cast(parent())->AddNode(track); + + if (track_cache_.isEmpty()) { + // Connect this track directly to this output + NodeParam::ConnectEdge(track->track_output(), track_input_); + } else { + TrackOutput* current_last_track = track_cache_.last(); + + // Connect this track to the current last track + NodeParam::ConnectEdge(track->track_output(), current_last_track->track_input()); + + // FIXME: Test code only + AlphaOverBlend* blend = new AlphaOverBlend(); + static_cast(parent())->AddNode(blend); + + NodeParam::ConnectEdge(track->texture_output(), blend->blend_input()); + NodeParam::ConnectEdge(current_last_track->texture_output(), blend->base_input()); + NodeParam::ConnectEdge(blend->texture_output(), current_last_track->texture_output()->edges().first()->input()); + // End test code + } +} + +void TrackList::RemoveTrack() +{ + if (track_cache_.isEmpty()) { + return; + } + + TrackOutput* track = track_cache_.last(); + + static_cast(parent())->TakeNode(track); + + delete track; +} + +void TrackList::TrackConnectionAdded(NodeEdgePtr edge) +{ + if (edge->input() != track_input_) { + return; + } + + AttachTrack(attached_track()); + + // FIXME: Is this necessary? + emit TimebaseChanged(timebase_); +} + +void TrackList::TrackConnectionRemoved(NodeEdgePtr edge) +{ + if (edge->input() != track_input_) { + return; + } + + DetachTrack(Node::ValueToPtr(edge->output()->get_value(0))); + + emit TimelineCleared(); +} + +void TrackList::TrackEdgeAdded(NodeEdgePtr edge) +{ + // Assume this signal was sent from a TrackOutput + TrackOutput* track = static_cast(sender()); + + // If this edge pertains to the track's track input, all the tracks just added need attaching + if (edge->input() == track->track_input()) { + TrackOutput* added_track = Node::ValueToPtr(edge->output()->get_value(0)); + + AttachTrack(added_track); + } +} + +void TrackList::TrackEdgeRemoved(NodeEdgePtr edge) +{ + // Assume this signal was sent from a TrackOutput + TrackOutput* track = static_cast(sender()); + + // If this edge pertains to the track's track input, all the tracks just added need attaching + if (edge->input() == track->track_input()) { + TrackOutput* added_track = Node::ValueToPtr(edge->output()->get_value(0)); + + DetachTrack(added_track); + } +} diff --git a/app/node/output/timeline/tracklist.h b/app/node/output/timeline/tracklist.h new file mode 100644 index 000000000..8aeeedf97 --- /dev/null +++ b/app/node/output/timeline/tracklist.h @@ -0,0 +1,113 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef TRACKLIST_H +#define TRACKLIST_H + +#include + +#include "node/output/track/track.h" + +class TimelineOutput; + +class TrackList : public QObject { + Q_OBJECT +public: + TrackList(TimelineOutput *parent, NodeInput* track_input); + + TrackOutput* attached_track(); + + void AttachTrack(TrackOutput *track); + + void DetachTrack(TrackOutput* track); + + const QVector& Tracks(); + + TrackOutput* TrackAt(int index); + + void AddTrack(); + + void RemoveTrack(); + + rational TrackListLength(); + + rational TimelineLength(); + + const rational& Timebase(); + + void SetTimebase(const rational& timebase); + +public slots:/** + * @brief Slot for when the track connection is added + */ + void TrackConnectionAdded(NodeEdgePtr edge); + + /** + * @brief Slot for when the track connection is removed + */ + void TrackConnectionRemoved(NodeEdgePtr edge); + + /** + * @brief Slot for when a connected Track has added a Block so we can update the UI + */ + void TrackAddedBlock(Block* block); + + /** + * @brief Slot for when a connected Track has added a Block so we can update the UI + */ + void TrackRemovedBlock(Block* block); + + /** + * @brief Slot for when an attached Track has an edge added + */ + void TrackEdgeAdded(NodeEdgePtr edge); + + /** + * @brief Slot for when an attached Track has an edge added + */ + void TrackEdgeRemoved(NodeEdgePtr edge); + +signals: + void BlockAdded(Block* block, int index); + + void BlockRemoved(Block* block); + + void TrackAdded(TrackOutput* track); + + void TrackRemoved(TrackOutput* track); + + void TrackListChanged(); + + void TimelineCleared(); + + void TimebaseChanged(const rational &timebase); + +private: + /** + * @brief A cache of connected Tracks + */ + QVector track_cache_; + + rational timebase_; + + NodeInput* track_input_; +}; + +#endif // TRACKLIST_H diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 07bd12c1e..15f8cc6b7 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -405,3 +405,17 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace) InvalidateCache(replace->in(), replace->out()); } + +TrackOutput *TrackOutput::TrackFromBlock(Block *block) +{ + Block* n = block; + + // Find last valid block in Sequence and assume its a track + while (n->next() != nullptr) { + n = n->next(); + } + + // Downside of this approach is the usage of dynamic_cast, alternative would be looping through all known tracks and + // seeing if the contain the Block, but this seems slower + return dynamic_cast(n); +} diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 5129a02cc..53fe21bf9 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -160,6 +160,8 @@ private: */ void ValidateCurrentBlock(const rational& time); + static TrackOutput* TrackFromBlock(Block* block); + QVector block_cache_; Block* current_block_; diff --git a/app/node/output/tracklist/CMakeLists.txt b/app/node/output/tracklist/CMakeLists.txt new file mode 100644 index 000000000..011799eeb --- /dev/null +++ b/app/node/output/tracklist/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/output/tracklist/tracklist.h + node/output/tracklist/tracklist.cpp + PARENT_SCOPE +) diff --git a/app/node/output/tracklist/tracklist.cpp b/app/node/output/tracklist/tracklist.cpp new file mode 100644 index 000000000..ad4dcf647 --- /dev/null +++ b/app/node/output/tracklist/tracklist.cpp @@ -0,0 +1,256 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "tracklist.h" + +#include + +#include "node/blend/alphaover/alphaover.h" +#include "node/block/gap/gap.h" +#include "node/graph.h" +#include "panel/timeline/timeline.h" + +TrackListOutput::TrackListOutput() +{ + track_input_ = new NodeInput("track_in"); + track_input_->add_data_input(NodeParam::kTrack); + AddParameter(track_input_); + + connect(this, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackConnectionAdded(NodeEdgePtr))); + connect(this, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackConnectionRemoved(NodeEdgePtr))); +} + +QString TrackListOutput::Name() +{ + return tr("Track List"); +} + +QString TrackListOutput::id() +{ + return "org.olivevideoeditor.Olive.tracklist"; +} + +QString TrackListOutput::Category() +{ + return tr("Output"); +} + +QString TrackListOutput::Description() +{ + return tr("Node for collecting Tracks of the same type."); +} + +const rational &TrackListOutput::Timebase() +{ + return timebase_; +} + +NodeInput *TrackListOutput::track_input() +{ + return track_input_; +} + +const QVector &TrackListOutput::Tracks() +{ + return track_cache_; +} + +TrackOutput *TrackListOutput::TrackAt(int index) +{ + return track_cache_.at(index); +} + +rational TrackListOutput::TimelineLength() +{ + rational length = 0; + + foreach (TrackOutput* track, track_cache_) { + length = qMax(length, track->in()); + } + + return length; +} + +TrackOutput *TrackListOutput::attached_track() +{ + return ValueToPtr(track_input_->get_value(0)); +} + +void TrackListOutput::AttachTrack(TrackOutput *track) +{ + TrackOutput* current_track = track; + + // Traverse through Tracks caching and connecting them + while (current_track != nullptr) { + connect(current_track, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackEdgeAdded(NodeEdgePtr))); + connect(current_track, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackEdgeRemoved(NodeEdgePtr))); + connect(current_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*))); + connect(current_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*))); + + current_track->SetIndex(track_cache_.size()); + + track_cache_.append(current_track); + + // This function must be called after the track is added to track_cache_, since it uses track_cache_ to determine + // the track's index + emit TrackAdded(current_track); + + current_track = current_track->next_track(); + } +} + +void TrackListOutput::DetachTrack(TrackOutput *track) +{ + TrackOutput* current_track = track; + + // Traverse through Tracks uncaching and disconnecting them + while (current_track != nullptr) { + emit TrackRemoved(current_track); + + current_track->SetIndex(-1); + + disconnect(current_track, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackEdgeAdded(NodeEdgePtr))); + disconnect(current_track, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackEdgeRemoved(NodeEdgePtr))); + disconnect(current_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*))); + disconnect(current_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*))); + + track_cache_.removeAll(current_track); + + current_track = current_track->next_track(); + } +} + +void TrackListOutput::SetTimebase(const rational &timebase) +{ + timebase_ = timebase; + + emit TimebaseChanged(timebase_); +} + +void TrackListOutput::AddTrack() +{ + TrackOutput* track = new TrackOutput(); + static_cast(parent())->AddNode(track); + + if (track_cache_.isEmpty()) { + // Connect this track directly to this output + NodeParam::ConnectEdge(track->track_output(), track_input()); + } else { + TrackOutput* current_last_track = track_cache_.last(); + + // Connect this track to the current last track + NodeParam::ConnectEdge(track->track_output(), current_last_track->track_input()); + + // FIXME: Test code only + AlphaOverBlend* blend = new AlphaOverBlend(); + static_cast(parent())->AddNode(blend); + + NodeParam::ConnectEdge(track->texture_output(), blend->blend_input()); + NodeParam::ConnectEdge(current_last_track->texture_output(), blend->base_input()); + NodeParam::ConnectEdge(blend->texture_output(), current_last_track->texture_output()->edges().first()->input()); + // End test code + } +} + +void TrackListOutput::RemoveTrack() +{ + if (track_cache_.isEmpty()) { + return; + } + + TrackOutput* track = track_cache_.last(); + + static_cast(parent())->TakeNode(track); + + delete track; +} + +TrackOutput *TrackListOutput::TrackFromBlock(Block *block) +{ + Block* n = block; + + // Find last valid block in Sequence and assume its a track + while (n->next() != nullptr) { + n = n->next(); + } + + // Downside of this approach is the usage of dynamic_cast, alternative would be looping through all known tracks and + // seeing if the contain the Block, but this seems slower + return dynamic_cast(n); +} + +void TrackListOutput::TrackConnectionAdded(NodeEdgePtr edge) +{ + if (edge->input() != track_input()) { + return; + } + + AttachTrack(attached_track()); + + // FIXME: Is this necessary? + emit TimebaseChanged(timebase_); +} + +void TrackListOutput::TrackConnectionRemoved(NodeEdgePtr edge) +{ + if (edge->input() != track_input()) { + return; + } + + DetachTrack(ValueToPtr(edge->output()->get_value(0))); + + emit TimelineCleared(); +} + +void TrackListOutput::TrackAddedBlock(Block *block) +{ + emit BlockAdded(block, static_cast(sender())->Index()); +} + +void TrackListOutput::TrackRemovedBlock(Block *block) +{ + emit BlockRemoved(block); +} + +void TrackListOutput::TrackEdgeAdded(NodeEdgePtr edge) +{ + // Assume this signal was sent from a TrackOutput + TrackOutput* track = static_cast(sender()); + + // If this edge pertains to the track's track input, all the tracks just added need attaching + if (edge->input() == track->track_input()) { + TrackOutput* added_track = ValueToPtr(edge->output()->get_value(0)); + + AttachTrack(added_track); + } +} + +void TrackListOutput::TrackEdgeRemoved(NodeEdgePtr edge) +{ + // Assume this signal was sent from a TrackOutput + TrackOutput* track = static_cast(sender()); + + // If this edge pertains to the track's track input, all the tracks just added need attaching + if (edge->input() == track->track_input()) { + TrackOutput* added_track = ValueToPtr(edge->output()->get_value(0)); + + DetachTrack(added_track); + } +} diff --git a/app/node/output/tracklist/tracklist.h b/app/node/output/tracklist/tracklist.h new file mode 100644 index 000000000..2125080d9 --- /dev/null +++ b/app/node/output/tracklist/tracklist.h @@ -0,0 +1,130 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef TRACKLISTOUTPUT_H +#define TRACKLISTOUTPUT_H + +#include "common/timelinecommon.h" +#include "node/block/block.h" +#include "node/output/track/track.h" + +/** + * @brief Node that represents a collection of Tracks of a certain type + */ +class TrackListOutput : public Node +{ + Q_OBJECT +public: + enum TrackType { + kNone = -1, + kVideo, + kAudio, + kSubtitle + }; + + TrackListOutput(); + + virtual QString Name() override; + virtual QString id() override; + virtual QString Category() override; + virtual QString Description() override; + + const rational& Timebase(); + void SetTimebase(const rational& timebase); + + NodeInput* track_input(); + + const QVector& Tracks(); + + TrackOutput* TrackAt(int index); + + void AddTrack(); + + void RemoveTrack(); + + rational TimelineLength(); + +public slots: + /** + * @brief Slot for when the track connection is added + */ + void TrackConnectionAdded(NodeEdgePtr edge); + + /** + * @brief Slot for when the track connection is removed + */ + void TrackConnectionRemoved(NodeEdgePtr edge); + + /** + * @brief Slot for when a connected Track has added a Block so we can update the UI + */ + void TrackAddedBlock(Block* block); + + /** + * @brief Slot for when a connected Track has added a Block so we can update the UI + */ + void TrackRemovedBlock(Block* block); + + /** + * @brief Slot for when an attached Track has an edge added + */ + void TrackEdgeAdded(NodeEdgePtr edge); + + /** + * @brief Slot for when an attached Track has an edge added + */ + void TrackEdgeRemoved(NodeEdgePtr edge); + +signals: + void TimebaseChanged(const rational &timebase); + + void TimelineCleared(); + + void BlockAdded(Block* block, int index); + + void BlockRemoved(Block* block); + + void TrackAdded(TrackOutput* track); + + void TrackRemoved(TrackOutput* track); + +protected: + +private: + TrackOutput* attached_track(); + + void AttachTrack(TrackOutput *track); + + void DetachTrack(TrackOutput* track); + + static TrackOutput* TrackFromBlock(Block* block); + + NodeInput* track_input_; + + /** + * @brief A cache of connected Tracks + */ + QVector track_cache_; + + rational timebase_; + +}; + +#endif // TRACKLISTOUTPUT_H diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index db24c99f9..3af8939ce 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -34,7 +34,8 @@ Sequence::Sequence() : timeline_output_(nullptr), renderer_processor_(nullptr), viewer_output_(nullptr), - track_output_(nullptr) + video_track_output_(nullptr), + audio_track_output_(nullptr) { } @@ -68,18 +69,23 @@ void Sequence::AddDefaultNodes() viewer_output_->SetCanBeDeleted(false); AddNode(viewer_output_); - track_output_ = new TrackOutput(); - track_output_->SetCanBeDeleted(false); - AddNode(track_output_); + video_track_output_ = new TrackOutput(); + video_track_output_->SetCanBeDeleted(false); + AddNode(video_track_output_); + + audio_track_output_ = new TrackOutput(); + audio_track_output_->SetCanBeDeleted(false); + AddNode(audio_track_output_); // Connect track to renderer - NodeParam::ConnectEdge(track_output_->texture_output(), renderer_processor_->texture_input()); + NodeParam::ConnectEdge(video_track_output_->texture_output(), renderer_processor_->texture_input()); // Connect renderer to viewer NodeParam::ConnectEdge(renderer_processor_->texture_output(), viewer_output_->texture_input()); // Connect track to timeline - NodeParam::ConnectEdge(track_output_->track_output(), timeline_output_->track_input()); + NodeParam::ConnectEdge(video_track_output_->track_output(), timeline_output_->track_input(TimelineOutput::kTrackTypeVideo)); + NodeParam::ConnectEdge(audio_track_output_->track_output(), timeline_output_->track_input(TimelineOutput::kTrackTypeAudio)); // Connect timeline end point to renderer NodeParam::ConnectEdge(timeline_output_->length_output(), renderer_processor_->length_input()); diff --git a/app/project/item/sequence/sequence.h b/app/project/item/sequence/sequence.h index 325191a52..47e5d0962 100644 --- a/app/project/item/sequence/sequence.h +++ b/app/project/item/sequence/sequence.h @@ -80,7 +80,8 @@ private: TimelineOutput* timeline_output_; RendererProcessor* renderer_processor_; ViewerOutput* viewer_output_; - TrackOutput* track_output_; + TrackOutput* video_track_output_; + TrackOutput* audio_track_output_; int video_width_; int video_height_; diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index f87558c3b..741df6177 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -107,9 +107,7 @@ void NodeParamViewItem::SetupUI() int row_count = 0; - for (int i=0;iParameterCount();i++) { - NodeParam* param = first_node->ParamAt(i); - + foreach (NodeParam* param, first_node->parameters()) { // This widget only needs to show input parameters if (param->type() == NodeParam::kInput) { @@ -141,9 +139,7 @@ void NodeParamViewItem::AddAdditionalNode(Node *n) { int bridge_count = 0; - for (int i=0;iParameterCount();i++) { - NodeParam* param = n->ParamAt(i); - + foreach (NodeParam* param, n->parameters()) { if (param->type() == NodeParam::kInput) { bridges_.at(bridge_count)->AddInput(static_cast(param)); @@ -162,9 +158,7 @@ void NodeParamViewItem::Retranslate() int row_count = 0; - for (int i=0;iParameterCount();i++) { - NodeParam* param = first_node->ParamAt(i); - + foreach (NodeParam* param, first_node->parameters()) { // This widget only needs to show input parameters if (param->type() == NodeParam::kInput) { param_lbls_.at(row_count)->setText(tr("%1:").arg(param->name())); diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 8cd02d9a2..f4aad1240 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -127,9 +127,7 @@ void NodeView::AddNode(Node* node) scene_.addItem(item); // Add a NodeViewEdge for each connection - QList node_params = node->parameters(); - - foreach (NodeParam* param, node_params) { + foreach (NodeParam* param, node->parameters()) { // We only bother working with outputs since eventually this will cover all inputs too // (covering both would lead to duplicates since every edge connects to one input and one output) diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index 0027d9c7a..550759d1a 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -105,7 +105,7 @@ void NodeViewItem::SetExpanded(bool e) // If a node is connected, use its parameter count to set the height if (node_ != nullptr) { - full_size_rect.adjust(0, 0, 0, node_text_padding_*2 + font_metrics.height() * node_->ParameterCount()); + full_size_rect.adjust(0, 0, 0, node_text_padding_*2 + font_metrics.height() * node_->parameters().size()); } // Store content_rect (the rect without the titlebar) @@ -127,7 +127,7 @@ QRectF NodeViewItem::GetParameterConnectorRect(int index) return QRectF(); } - NodeParam* param = node_->ParamAt(index); + NodeParam* param = node_->parameters().at(index); QRectF connector_rect(rect().x(), content_rect_.y() + node_text_padding_ + font_metrics.height() / 2 - node_connector_size_ / 2, @@ -151,7 +151,7 @@ QPointF NodeViewItem::GetParameterTextPoint(int index) return QPointF(); } - NodeParam* param = node_->ParamAt(index); + NodeParam* param = node_->parameters().at(index); if (param->type() == NodeParam::kOutput) { return content_rect_.topRight() + QPointF(-(node_connector_size_ + node_text_padding_), @@ -193,9 +193,8 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti // Store the text points which will steadily increase sa we loop // Loop through all the parameters - QList node_params = node_->parameters(); - for (int i=0;iparameters().size();i++) { + NodeParam* param = node_->parameters().at(i); // Draw connector square painter->fillRect(GetParameterConnectorRect(i), connector_brush); @@ -274,11 +273,11 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event) // See if the mouse click was on a parameter connector if (IsExpanded() // This is only possible if the node is expanded && node_ != nullptr) { // We can only loop through a node's parameters if a valid node is attached - for (int i=0;iParameterCount();i++) { + for (int i=0;iparameters().size();i++) { if (GetParameterConnectorRect(i).contains(event->pos())) { // See if the cursor is in the rect - NodeParam* param = node_->ParamAt(i); + NodeParam* param = node_->parameters().at(i); // Create draggable object dragging_edge_ = new NodeViewEdge(); @@ -369,7 +368,7 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) } // See if the mouse is currently inside a connector rect - for (int i=0;inode()->ParameterCount();i++) { + for (int i=0;inode()->parameters().size();i++) { // Make a larger "hitbox" rect to make it easier to drag into QRectF param_hitbox = drop_item->GetParameterConnectorRect(i).adjusted(-node_connector_size_, @@ -378,7 +377,7 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) node_connector_size_); // Get the parameter we're dragging into - NodeParam* comp_param = drop_item->node()->ParamAt(i); + NodeParam* comp_param = drop_item->node()->parameters().at(i); if (param_hitbox.contains(drop_item->mapFromScene(event->scenePos())) // See if we're dragging inside the hitbox && NodeParam::AreDataTypesCompatible(drag_src_param_, comp_param)) { // Make sure the types are compatible diff --git a/app/widget/timelineview/timelineview.cpp b/app/widget/timelineview/timelineview.cpp index ee4916aad..e83e43b54 100644 --- a/app/widget/timelineview/timelineview.cpp +++ b/app/widget/timelineview/timelineview.cpp @@ -163,7 +163,7 @@ void TimelineView::Clear() block_items_.clear(); } -void TimelineView::ConnectTimelineNode(TimelineOutput *node) +void TimelineView::ConnectTimelineNode(TrackList *node) { if (timeline_node_ != nullptr) { disconnect(timeline_node_, SIGNAL(TimebaseChanged(const rational&)), this, SIGNAL(TimebaseChanged(const rational&))); diff --git a/app/widget/timelineview/timelineview.h b/app/widget/timelineview/timelineview.h index 3aa211e8d..9264f5c69 100644 --- a/app/widget/timelineview/timelineview.h +++ b/app/widget/timelineview/timelineview.h @@ -49,7 +49,7 @@ public: void SetScale(const double& scale); - void ConnectTimelineNode(TimelineOutput* node); + void ConnectTimelineNode(TrackList* node); void DisconnectTimelineNode(); @@ -338,7 +338,7 @@ private: HandTool hand_tool_; ZoomTool zoom_tool_; - TimelineOutput* timeline_node_; + TrackList* timeline_node_; void AddGhost(TimelineViewGhostItem* ghost); diff --git a/app/widget/timelineview/undo/undo.cpp b/app/widget/timelineview/undo/undo.cpp index 67f932a54..7ca339fc0 100644 --- a/app/widget/timelineview/undo/undo.cpp +++ b/app/widget/timelineview/undo/undo.cpp @@ -285,7 +285,7 @@ void TrackRippleRemoveAreaCommand::undo() track_->InvalidateCache(in_, out_); } -TrackPlaceBlockCommand::TrackPlaceBlockCommand(TimelineOutput* timeline, int track, Block *block, rational in, QUndoCommand *parent) : +TrackPlaceBlockCommand::TrackPlaceBlockCommand(TrackList *timeline, int track, Block *block, rational in, QUndoCommand *parent) : TrackRippleRemoveAreaCommand(nullptr, in, 0, parent), // Out gets set correctly in redo() timeline_(timeline), track_index_(track), diff --git a/app/widget/timelineview/undo/undo.h b/app/widget/timelineview/undo/undo.h index 75c9f89f3..ebf3547b1 100644 --- a/app/widget/timelineview/undo/undo.h +++ b/app/widget/timelineview/undo/undo.h @@ -159,13 +159,13 @@ protected: */ class TrackPlaceBlockCommand : public TrackRippleRemoveAreaCommand { public: - TrackPlaceBlockCommand(TimelineOutput *timeline, int track, Block* block, rational in, QUndoCommand* parent = nullptr); + TrackPlaceBlockCommand(TrackList *timeline, int track, Block* block, rational in, QUndoCommand* parent = nullptr); virtual void redo() override; virtual void undo() override; private: - TimelineOutput* timeline_; + TrackList* timeline_; int track_index_; bool append_; GapBlock* gap_; diff --git a/app/widget/timelinewidget/CMakeLists.txt b/app/widget/timelinewidget/CMakeLists.txt new file mode 100644 index 000000000..a966cbb00 --- /dev/null +++ b/app/widget/timelinewidget/CMakeLists.txt @@ -0,0 +1,23 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/timelinewidget/timelinewidget.h + widget/timelinewidget/timelinewidget.cpp + PARENT_SCOPE +) diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 818a47852..3cc302d6f 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -107,8 +107,12 @@ void TimelineWidget::ConnectTimelineNode(TimelineOutput *node) { timeline_node_ = node; + int track_type = 0; + foreach (TimelineView* view, views_) { - view->ConnectTimelineNode(node); + view->ConnectTimelineNode(node->track_list(static_cast(track_type))); + + track_type++; } } @@ -145,11 +149,6 @@ void TimelineWidget::DeselectAll() } } -TimelineView *TimelineWidget::GetView(const TimelineWidget::ViewType &view_type) -{ - return views_.at(view_type); -} - void TimelineWidget::RippleToIn() { RippleEditTo(olive::timeline::kTrimIn, false); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 82fcae24e..1d3ce85a7 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -56,13 +56,6 @@ signals: void TimeChanged(const int64_t& time); private: - enum ViewType { - kVideo, - kAudio - }; - - TimelineView* GetView(const ViewType& view_type); - void RippleEditTo(olive::timeline::MovementMode mode, bool insert_gaps); void SetTimeAndSignal(const int64_t& t);