From 500fb58d49bb6bb12d7660c5fa020b786f51d46b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Mar 2021 12:22:19 +1100 Subject: [PATCH] arranged items so they are inputs of folder rather than outputs --- app/common/xmlutils.h | 2 - app/core.cpp | 5 +- app/node/graph.h | 1 - app/node/node.cpp | 7 +- app/node/node.h | 34 ++++++++ app/panel/project/project.cpp | 12 +-- app/panel/project/project.h | 6 +- app/project/item/CMakeLists.txt | 2 - app/project/item/folder/folder.cpp | 66 ++++++++++++-- app/project/item/folder/folder.h | 53 ++++++++--- app/project/item/footage/footage.cpp | 17 ++-- app/project/item/footage/footage.h | 12 ++- app/project/item/item.cpp | 72 --------------- app/project/item/item.h | 75 ---------------- app/project/item/sequence/sequence.cpp | 9 +- app/project/item/sequence/sequence.h | 14 +-- app/project/projectviewmodel.cpp | 87 +++++++++---------- app/project/projectviewmodel.h | 16 ++-- app/task/project/import/import.cpp | 4 +- app/task/project/saveotio/saveotio.cpp | 4 +- .../nodeparamview/nodeparamviewundo.cpp | 20 ++++- app/widget/nodeparamview/nodeparamviewundo.h | 23 ++--- .../projectexplorer/projectexplorer.cpp | 28 +++--- app/widget/projectexplorer/projectexplorer.h | 8 +- app/widget/timelinewidget/tool/import.cpp | 4 +- .../mainwindow/mainwindowlayoutinfo.cpp | 9 +- 26 files changed, 281 insertions(+), 309 deletions(-) delete mode 100644 app/project/item/item.cpp delete mode 100644 app/project/item/item.h diff --git a/app/common/xmlutils.h b/app/common/xmlutils.h index b0a1b08f9..a8517338c 100644 --- a/app/common/xmlutils.h +++ b/app/common/xmlutils.h @@ -29,7 +29,6 @@ namespace olive { class Block; -class Item; class Node; class NodeInput; @@ -51,7 +50,6 @@ struct XMLNodeData { QHash node_ptrs; QList desired_connections; QList block_links; - QHash item_ptrs; }; diff --git a/app/core.cpp b/app/core.cpp index f58eac1a9..969ad5196 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -65,6 +65,7 @@ #include "widget/menu/menushared.h" #include "widget/taskview/taskviewitem.h" #include "widget/viewer/viewer.h" +#include "widget/nodeparamview/nodeparamviewundo.h" #include "window/mainwindow/mainstatusbar.h" #include "window/mainwindow/mainwindow.h" @@ -370,7 +371,7 @@ void Core::CreateNewFolder() MultiUndoCommand* command = new MultiUndoCommand(); command->add_child(new NodeAddCommand(active_project, new_folder)); - command->add_child(new NodeEdgeAddCommand(folder, NodeInput(new_folder, Item::kParentInput))); + command->add_child(new FolderAddChild(folder, new_folder)); Core::instance()->undo_stack()->push(command); @@ -405,7 +406,7 @@ void Core::CreateNewSequence() MultiUndoCommand* command = new MultiUndoCommand(); command->add_child(new NodeAddCommand(active_project, new_sequence)); - command->add_child(new NodeEdgeAddCommand(GetSelectedFolderInActiveProject(), NodeInput(new_sequence, Item::kParentInput))); + command->add_child(new FolderAddChild(GetSelectedFolderInActiveProject(), new_sequence)); // Create and connect default nodes to new sequence new_sequence->add_default_nodes(command); diff --git a/app/node/graph.h b/app/node/graph.h index e2f23fb14..bb65bdb42 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -22,7 +22,6 @@ #define NODEGRAPH_H #include "node/node.h" -#include "project/item/item.h" namespace olive { diff --git a/app/node/node.cpp b/app/node/node.cpp index 70d6b1907..486cb90c6 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -46,7 +46,8 @@ const QString Node::kDefaultOutput = QStringLiteral("output"); Node::Node(bool create_default_output) : can_be_deleted_(true), override_color_(-1), - last_change_time_(0) + last_change_time_(0), + folder_(nullptr) { if (create_default_output) { AddOutput(); @@ -1126,7 +1127,7 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap(connected)) { + if (connected->IsItem()) { // This is an item and we avoid copying those and just connect to them directly connected_copy = connected; } else { @@ -1529,7 +1530,7 @@ void GetDependenciesRecursively(QVector& list, const Node* node, bool tra Node* connected_node = it->second.node(); if (!exclusive_only - || (connected_node->outputs().size() == 1 && !dynamic_cast(connected_node))) { + || (connected_node->outputs().size() == 1 && !connected_node->IsItem())) { if (!list.contains(connected_node)) { list.append(connected_node); diff --git a/app/node/node.h b/app/node/node.h index dc4f4f664..6c0d155f9 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -46,6 +46,7 @@ namespace olive { class NodeGraph; +class Folder; /** * @brief A single processing unit that can be connected with others to create intricate processing systems @@ -155,6 +156,21 @@ public: */ virtual QString Description() const; + const QString& ToolTip() const + { + return tooltip_; + } + + Folder* folder() const + { + return folder_; + } + + virtual bool IsItem() const + { + return false; + } + /** * @brief Function called to retranslate parameter names (should be overridden in derivatives) */ @@ -162,6 +178,10 @@ public: virtual QIcon icon() const; + virtual QString duration() const {return QString();} + + virtual QString rate() const {return QString();} + const QVector& inputs() const { return input_ids_; @@ -718,6 +738,11 @@ public: static bool Unlink(Node* a, Node* b); static bool AreLinked(Node* a, Node* b); + void SetFolder(Folder* folder) + { + folder_ = folder; + } + static const QString kDefaultOutput; protected: @@ -819,6 +844,11 @@ protected: virtual void childEvent(QChildEvent *event) override; + void SetToolTip(const QString& s) + { + tooltip_ = s; + } + signals: /** * @brief Signal emitted whenever the position is set through SetPosition() @@ -1137,6 +1167,10 @@ private: qint64 last_change_time_; + QString tooltip_; + + Folder* folder_; + private slots: /** * @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time diff --git a/app/panel/project/project.cpp b/app/panel/project/project.cpp index 3df3b2a45..07c0ba40e 100644 --- a/app/panel/project/project.cpp +++ b/app/panel/project/project.cpp @@ -115,7 +115,7 @@ void ProjectPanel::set_root(Folder *item) Retranslate(); } -QVector ProjectPanel::SelectedItems() const +QVector ProjectPanel::SelectedItems() const { return explorer_->SelectedItems(); } @@ -163,7 +163,7 @@ void ProjectPanel::DeleteSelected() explorer_->DeleteSelected(); } -void ProjectPanel::Edit(Item* item) +void ProjectPanel::Edit(Node* item) { explorer_->Edit(item); } @@ -179,7 +179,7 @@ void ProjectPanel::Retranslate() UpdateSubtitle(); } -void ProjectPanel::ItemDoubleClickSlot(Item *item) +void ProjectPanel::ItemDoubleClickSlot(Node *item) { if (item == nullptr) { // If the user double clicks on empty space, show the import dialog @@ -215,7 +215,7 @@ void ProjectPanel::UpdateSubtitle() do { folder_path.prepend(QStringLiteral("/%1").arg(item->GetLabel())); - item = item->item_parent(); + item = item->folder(); } while (item != project()->root()); project_title.append(folder_path); @@ -234,10 +234,10 @@ void ProjectPanel::SaveConnectedProject() QVector ProjectPanel::GetSelectedFootage() const { - QVector items = SelectedItems(); + QVector items = SelectedItems(); QVector footage; - foreach (Item* i, items) { + foreach (Node* i, items) { if (dynamic_cast(i)) { footage.append(static_cast(i)); } diff --git a/app/panel/project/project.h b/app/panel/project/project.h index b3c2e3fa5..ce527fef1 100644 --- a/app/panel/project/project.h +++ b/app/panel/project/project.h @@ -44,7 +44,7 @@ public: void set_root(Folder* item); - QVector SelectedItems() const; + QVector SelectedItems() const; Folder* GetSelectedFolder() const; @@ -61,7 +61,7 @@ public: virtual void DeleteSelected() override; public slots: - void Edit(Item *item); + void Edit(Node *item); signals: void ProjectNameChanged(); @@ -72,7 +72,7 @@ private: ProjectExplorer* explorer_; private slots: - void ItemDoubleClickSlot(Item* item); + void ItemDoubleClickSlot(Node *item); void ShowNewMenu(); diff --git a/app/project/item/CMakeLists.txt b/app/project/item/CMakeLists.txt index 801a78e72..6ac91b1f1 100644 --- a/app/project/item/CMakeLists.txt +++ b/app/project/item/CMakeLists.txt @@ -20,7 +20,5 @@ add_subdirectory(sequence) set(OLIVE_SOURCES ${OLIVE_SOURCES} - project/item/item.h - project/item/item.cpp PARENT_SCOPE ) diff --git a/app/project/item/folder/folder.cpp b/app/project/item/folder/folder.cpp index 9ce15ca9b..6d9c5c9cf 100644 --- a/app/project/item/folder/folder.cpp +++ b/app/project/item/folder/folder.cpp @@ -27,8 +27,13 @@ namespace olive { +#define super Node + +const QString Folder::kChildInput = QStringLiteral("child_in"); + Folder::Folder() { + AddInput(kChildInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable)); } QIcon Folder::icon() const @@ -36,6 +41,13 @@ QIcon Folder::icon() const return icon::Folder; } +void Folder::Retranslate() +{ + super::Retranslate(); + + SetInputName(kChildInput, tr("Children")); +} + bool ChildExistsWithNameInternal(const Folder* n, const QString& s) { foreach (const Node::OutputConnection& c, n->output_connections()) { @@ -60,33 +72,69 @@ bool Folder::ChildExistsWithName(const QString &s) const return ChildExistsWithNameInternal(this, s); } -void Folder::OutputConnectedEvent(const QString &output, const NodeInput &input) +int Folder::index_of_child_in_array(Node *item) const { - Q_UNUSED(output) + int index_of_item = item_children_.indexOf(item); - Item* item = dynamic_cast(input.node()); + if (index_of_item == -1) { + return -1; + } + + return item_element_index_.at(index_of_item); +} + +void Folder::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kChildInput && element != -1) { + Node* item = output.node(); - if (item) { // The insert index is always our "count" because we only support appending in our internal // model. For sorting/organizing, a QSortFilterProxyModel is used instead. emit BeginInsertItem(item, item_child_count()); item_children_.append(item); + item_element_index_.append(element); + item->SetFolder(this); emit EndInsertItem(); } } -void Folder::OutputDisconnectedEvent(const QString &output, const NodeInput &input) +void Folder::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) { - Q_UNUSED(output) + if (input == kChildInput && element != -1) { + Node* item = output.node(); - Item* item = dynamic_cast(input.node()); - - if (item) { int child_index = item_children_.indexOf(item); emit BeginRemoveItem(item, child_index); item_children_.removeAt(child_index); + item_element_index_.removeAt(child_index); + item->SetFolder(nullptr); emit EndRemoveItem(); } } +FolderAddChild::FolderAddChild(Folder *folder, Node *child, bool autoposition) : + folder_(folder), + child_(child), + autoposition_(autoposition) +{ +} + +Project *FolderAddChild::GetRelevantProject() const +{ + return folder_->project(); +} + +void FolderAddChild::redo() +{ + int array_index = folder_->InputArraySize(Folder::kChildInput); + folder_->InputArrayAppend(Folder::kChildInput, false); + Node::ConnectEdge(child_, NodeInput(folder_, Folder::kChildInput, array_index)); +} + +void FolderAddChild::undo() +{ + Node::DisconnectEdge(child_, NodeInput(folder_, Folder::kChildInput, folder_->InputArraySize(Folder::kChildInput)-1)); + folder_->InputArrayRemoveLast(Folder::kChildInput); +} + } diff --git a/app/project/item/folder/folder.h b/app/project/item/folder/folder.h index 899b300a8..212112d16 100644 --- a/app/project/item/folder/folder.h +++ b/app/project/item/folder/folder.h @@ -22,7 +22,6 @@ #define FOLDER_H #include "node/node.h" -#include "project/item/item.h" namespace olive { @@ -32,7 +31,7 @@ namespace olive { * The Item base class already has support for children, but this functionality is disabled by default * (see CanHaveChildren() override). The Folder is a specific type that enables this functionality. */ -class Folder : public Item +class Folder : public Node { Q_OBJECT public: @@ -65,6 +64,8 @@ public: virtual QIcon icon() const override; + virtual void Retranslate() override; + bool ChildExistsWithName(const QString& s) const; int item_child_count() const @@ -72,16 +73,21 @@ public: return item_children_.size(); } - Item* item_child(int i) const + Node* item_child(int i) const { return item_children_.at(i); } - const QVector& children() const + const QVector& children() const { return item_children_; } + virtual bool IsItem() const override + { + return true; + } + /** * @brief Returns a list of nodes that are of a certain type that this node outputs to */ @@ -95,24 +101,28 @@ public: return list; } - int index_of_child(Item* item) const + int index_of_child(Node* item) const { return item_children_.indexOf(item); } + int index_of_child_in_array(Node* item) const; + + static const QString kChildInput; + signals: - void BeginInsertItem(Item* n, int index); + void BeginInsertItem(Node* n, int index); void EndInsertItem(); - void BeginRemoveItem(Item* n, int index); + void BeginRemoveItem(Node* n, int index); void EndRemoveItem(); protected: - virtual void OutputConnectedEvent(const QString& output, const NodeInput& input) override; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output) override; - virtual void OutputDisconnectedEvent(const QString& output, const NodeInput& input) override; + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output) override; private: template @@ -140,7 +150,30 @@ private: } } - QVector item_children_; + QVector item_children_; + QVector item_element_index_; + +}; + +class FolderAddChild : public UndoCommand +{ +public: + FolderAddChild(Folder* folder, Node* child, bool autoposition = true); + + virtual Project * GetRelevantProject() const override; + + virtual void redo() override; + + virtual void undo() override; + +private: + Folder* folder_; + + Node* child_; + + bool autoposition_; + + QPointF old_position_; }; diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index a72340674..35e076e24 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -38,10 +38,9 @@ namespace olive { const QString Footage::kFilenameInput = QStringLiteral("file_in"); const QString Footage::kStreamPropertiesFormat = QStringLiteral("stream_properties:%1"); -#define super Item +#define super Node Footage::Footage(const QString &filename) : - super(true, false), cancelled_(nullptr) { AddInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); @@ -381,7 +380,7 @@ QIcon Footage::icon() const return icon::Error; } -QString Footage::duration() +QString Footage::duration() const { // Try video first VideoParams video = GetFirstEnabledVideoStream(); @@ -420,7 +419,7 @@ QString Footage::duration() return QString(); } -QString Footage::rate() +QString Footage::rate() const { if (inputs_for_stream_properties_.isEmpty()) { return QString(); @@ -431,12 +430,12 @@ QString Footage::rate() VideoParams video_stream = GetFirstEnabledVideoStream(); if (video_stream.video_type() != VideoParams::kVideoTypeStill) { - return QCoreApplication::translate("Footage", "%1 FPS").arg(video_stream.frame_rate().toDouble()); + return tr("%1 FPS").arg(video_stream.frame_rate().toDouble()); } } else if (HasEnabledAudioStreams()) { // No video streams, return audio AudioParams audio_stream = GetFirstEnabledAudioStream(); - return QCoreApplication::translate("Footage", "%1 Hz").arg(audio_stream.sample_rate()); + return tr("%1 Hz").arg(audio_stream.sample_rate()); } return QString(); @@ -620,7 +619,7 @@ QString Footage::GetStreamTypeName(Stream::Type type) void Footage::UpdateTooltip() { if (valid_) { - QString tip = QCoreApplication::translate("Footage", "Filename: %1").arg(filename()); + QString tip = tr("Filename: %1").arg(filename()); for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) { if (it.key().type() == Stream::kVideo) { @@ -640,9 +639,9 @@ void Footage::UpdateTooltip() } } - set_tooltip(tip); + SetToolTip(tip); } else { - set_tooltip(QCoreApplication::translate("Footage", "This footage is not valid for use")); + SetToolTip(tr("This footage is not valid for use")); } } diff --git a/app/project/item/footage/footage.h b/app/project/item/footage/footage.h index f6a98ec81..56eb3ba76 100644 --- a/app/project/item/footage/footage.h +++ b/app/project/item/footage/footage.h @@ -27,7 +27,6 @@ #include "common/rational.h" #include "footagedescription.h" #include "node/node.h" -#include "project/item/item.h" #include "render/audioparams.h" #include "render/videoparams.h" #include "stream.h" @@ -42,7 +41,7 @@ namespace olive { * Footage objects store a list of Stream objects which store the majority of video/audio metadata. These streams * are identical to the stream data in the files. */ -class Footage : public Item, public TimelinePoints +class Footage : public Node, public TimelinePoints { Q_OBJECT public: @@ -263,9 +262,14 @@ public: virtual QIcon icon() const override; - virtual QString duration() override; + virtual QString duration() const override; - virtual QString rate() override; + virtual QString rate() const override; + + virtual bool IsItem() const override + { + return true; + } bool HasEnabledVideoStreams() const; bool HasEnabledAudioStreams() const; diff --git a/app/project/item/item.cpp b/app/project/item/item.cpp deleted file mode 100644 index 729d2dc22..000000000 --- a/app/project/item/item.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "item.h" - -#include "folder/folder.h" - -namespace olive { - -#define super QObject - -const QString Item::kParentInput = QStringLiteral("parent_in"); - -Item::Item(bool create_folder_input, bool create_default_output) : - Node(create_default_output) -{ - if (create_folder_input) { - // Hierarchy input for items - AddInput(kParentInput, NodeValue::kNone); - IgnoreHashingFrom(kParentInput); - IgnoreInvalidationsFrom(kParentInput); - } -} - -const QString &Item::tooltip() const -{ - return tooltip_; -} - -void Item::set_tooltip(const QString &t) -{ - tooltip_ = t; -} - -QString Item::duration() -{ - return QString(); -} - -QString Item::rate() -{ - return QString(); -} - -Folder *Item::item_parent() const -{ - return dynamic_cast(GetConnectedNode(kParentInput)); -} - -void Item::Retranslate() -{ - SetInputName(kParentInput, tr("Folder")); -} - -} diff --git a/app/project/item/item.h b/app/project/item/item.h deleted file mode 100644 index b0a5f4a03..000000000 --- a/app/project/item/item.h +++ /dev/null @@ -1,75 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 ITEM_H -#define ITEM_H - -#include -#include -#include -#include -#include -#include - -#include "common/threadedobject.h" -#include "common/xmlutils.h" -#include "node/node.h" - -namespace olive { - -class Folder; -class Project; - -/** - * @brief A base-class representing any element in a Project - * - * Project objects implement a parent-child hierarchy of Items that can be used throughout the Project. The Item class - * itself is abstract and will need to be subclassed to be used in a Project. - */ -class Item : public Node -{ - Q_OBJECT -public: - /** - * @brief Item constructor - */ - Item(bool create_folder_input = true, bool create_default_output = true); - - const QString& tooltip() const; - void set_tooltip(const QString& t); - - virtual QString duration(); - - virtual QString rate(); - - Folder *item_parent() const; - - static const QString kParentInput; - - virtual void Retranslate() override; - -private: - QString tooltip_; - -}; - -} - -#endif // ITEM_H diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index d39c08e75..e1ca10a86 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -46,10 +46,9 @@ const QString Sequence::kTrackInputFormat = QStringLiteral("track_in_%1"); const uint64_t Sequence::kVideoParamEditMask = VideoParamEdit::kWidthHeight | VideoParamEdit::kInterlacing | VideoParamEdit::kFrameRate | VideoParamEdit::kPixelAspect; -#define super Item +#define super Node Sequence::Sequence(bool viewer_only_mode) : - Item(!viewer_only_mode, true), video_frame_cache_(this), audio_playback_cache_(this), operation_stack_(0) @@ -104,7 +103,7 @@ QIcon Sequence::icon() const return icon::Sequence; } -QString Sequence::duration() +QString Sequence::duration() const { rational timeline_length = GetLength(); @@ -113,7 +112,7 @@ QString Sequence::duration() return Timecode::timestamp_to_timecode(timestamp, video_params().time_base(), Core::instance()->GetTimecodeDisplay()); } -QString Sequence::rate() +QString Sequence::rate() const { return tr("%1 FPS").arg(video_params().time_base().flipped().toDouble()); } @@ -341,7 +340,7 @@ void Sequence::InvalidateCache(const TimeRange& range, const QString& from, int super::InvalidateCache(range, from, element, job_time); } -rational Sequence::GetLength() +const rational& Sequence::GetLength() const { return last_length_; } diff --git a/app/project/item/sequence/sequence.h b/app/project/item/sequence/sequence.h index a6a809326..db46d836b 100644 --- a/app/project/item/sequence/sequence.h +++ b/app/project/item/sequence/sequence.h @@ -31,7 +31,6 @@ #include "node/output/track/tracklist.h" #include "node/traverser.h" #include "project/item/footage/footage.h" -#include "project/item/item.h" #include "render/audioparams.h" #include "render/audioplaybackcache.h" #include "render/framehashcache.h" @@ -46,7 +45,7 @@ namespace olive { /** * @brief The main timeline object, an graph of edited clips that forms a complete edit */ -class Sequence : public Item, public TimelinePoints +class Sequence : public Node, public TimelinePoints { Q_OBJECT public: @@ -83,8 +82,8 @@ public: virtual QIcon icon() const override; - virtual QString duration() override; - virtual QString rate() override; + virtual QString duration() const override; + virtual QString rate() const override; void set_default_parameters(); @@ -136,7 +135,7 @@ public: SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio)); } - rational GetLength(); + const rational &GetLength() const; virtual void Retranslate() override; @@ -173,6 +172,11 @@ public: return uuid_; } + virtual bool IsItem() const override + { + return true; + } + signals: void TimebaseChanged(const rational&); diff --git a/app/project/projectviewmodel.cpp b/app/project/projectviewmodel.cpp index ed6cd47ba..1e2d79f8c 100644 --- a/app/project/projectviewmodel.cpp +++ b/app/project/projectviewmodel.cpp @@ -26,6 +26,7 @@ #include "core.h" #include "widget/nodeview/nodeviewundo.h" +#include "widget/nodeparamview/nodeparamviewundo.h" namespace olive { @@ -78,10 +79,10 @@ QModelIndex ProjectViewModel::index(int row, int column, const QModelIndex &pare QModelIndex ProjectViewModel::parent(const QModelIndex &child) const { // Get the Item object from the index - Item* item = GetItemObjectFromIndex(child); + Node* item = GetItemObjectFromIndex(child); // Get Item's parent object - Item* par = item->item_parent(); + Folder* par = item->folder(); // If the parent is the root, return an empty index if (par == project_->root()) { @@ -128,7 +129,7 @@ int ProjectViewModel::columnCount(const QModelIndex &parent) const QVariant ProjectViewModel::data(const QModelIndex &index, int role) const { - Item* internal_item = GetItemObjectFromIndex(index); + Node* internal_item = GetItemObjectFromIndex(index); ColumnType column_type = columns_.at(index.column()); @@ -154,7 +155,7 @@ QVariant ProjectViewModel::data(const QModelIndex &index, int role) const } break; case Qt::ToolTipRole: - return internal_item->tooltip(); + return internal_item->ToolTip(); } return QVariant(); @@ -185,7 +186,7 @@ bool ProjectViewModel::hasChildren(const QModelIndex &parent) const { // If it's a folder, we always return TRUE in order to always show the "expand triangle" icon, // even when there are no "physical" children - Item* item = GetItemObjectFromIndex(parent); + Node* item = GetItemObjectFromIndex(parent); return dynamic_cast(item); } @@ -194,7 +195,7 @@ bool ProjectViewModel::setData(const QModelIndex &index, const QVariant &value, { // The name is editable if (index.isValid() && columns_.at(index.column()) == kName && role == Qt::EditRole) { - Item* item = GetItemObjectFromIndex(index); + Node* item = GetItemObjectFromIndex(index); QString new_name = value.toString(); @@ -268,7 +269,7 @@ QMimeData *ProjectViewModel::mimeData(const QModelIndexList &indexes) const // Check if we've dragged this item before if (!dragged_items.contains(index.internalPointer())) { // If not, add it to the stream (and also keep track of it in the vector) - Footage* footage = dynamic_cast(static_cast(index.internalPointer())); + Footage* footage = dynamic_cast(static_cast(index.internalPointer())); if (footage) { QVector streams = footage->GetEnabledStreamsAsReferences(); @@ -309,10 +310,10 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action QDataStream stream(&model_data, QIODevice::ReadOnly); // Get the Item object that the items were dropped on - Item* drop_location = GetItemObjectFromIndex(drop); + Folder* drop_location = dynamic_cast(GetItemObjectFromIndex(drop)); // If this is not a folder, we cannot drop these items here - if (!dynamic_cast(drop_location)) { + if (!drop_location) { return false; } @@ -328,16 +329,15 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action while (!stream.atEnd()) { stream >> streams >> item_ptr; - Item* item = reinterpret_cast(item_ptr); + Node* item = reinterpret_cast(item_ptr); // Check if Item is already the drop location or if its parent is the drop location, in which case this is a // no-op - if (item != drop_location && item->item_parent() != drop_location && !ItemIsParentOfChild(item, drop_location)) { - NodeInput child_input(item, Item::kParentInput); - - move_command->add_child(new NodeEdgeRemoveCommand(item->item_parent(), child_input)); - move_command->add_child(new NodeEdgeAddCommand(static_cast(drop_location), child_input)); + if (item != drop_location && item->folder() != drop_location + && (!dynamic_cast(item) || !ItemIsParentOfChild(static_cast(item), drop_location))) { + move_command->add_child(new NodeEdgeRemoveCommand(item, NodeInput(item->folder(), Folder::kChildInput, item->folder()->index_of_child_in_array(item)))); + move_command->add_child(new FolderAddChild(drop_location, item)); } } @@ -363,24 +363,31 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action } // Get folder dropped onto - Item* drop_item = GetItemObjectFromIndex(drop); + Node* drop_item = GetItemObjectFromIndex(drop); // If we didn't drop onto an item, find the nearest parent folder (should eventually terminate at root either way) - while (!dynamic_cast(drop_item)) { - drop_item = drop_item->item_parent(); + if (!dynamic_cast(drop_item)) { + drop_item = drop_item->folder(); + + if (!drop_item) { + // Failed to find folder to place this in + return false; + } } // Trigger an import Core::instance()->ImportFiles(urls, this, static_cast(drop_item)); + + return true; } return false; } -int ProjectViewModel::IndexOfChild(Item *item) const +int ProjectViewModel::IndexOfChild(Node *item) const { // Find parent's index within its own parent - Folder* parent = item->item_parent(); + Folder* parent = item->folder(); if (parent) { return parent->index_of_child(item); @@ -389,20 +396,20 @@ int ProjectViewModel::IndexOfChild(Item *item) const return -1; } -Item *ProjectViewModel::GetItemObjectFromIndex(const QModelIndex &index) const +Node *ProjectViewModel::GetItemObjectFromIndex(const QModelIndex &index) const { if (index.isValid()) { - return static_cast(index.internalPointer()); + return static_cast(index.internalPointer()); } return project_ ? project_->root() : nullptr; } -bool ProjectViewModel::ItemIsParentOfChild(Item *parent, Item *child) const +bool ProjectViewModel::ItemIsParentOfChild(Folder *parent, Node *child) const { // Loop through parent hierarchy checking if `parent` is one of its parents do { - child = child->item_parent(); + child = child->folder(); if (parent == child) { return true; @@ -412,9 +419,9 @@ bool ProjectViewModel::ItemIsParentOfChild(Item *parent, Item *child) const return false; } -void ProjectViewModel::ConnectItem(Item *n) +void ProjectViewModel::ConnectItem(Node *n) { - connect(n, &Item::LabelChanged, this, &ProjectViewModel::ItemRenamed); + connect(n, &Node::LabelChanged, this, &ProjectViewModel::ItemRenamed); Folder* f = dynamic_cast(n); if (f) { @@ -423,19 +430,15 @@ void ProjectViewModel::ConnectItem(Item *n) connect(f, &Folder::BeginRemoveItem, this, &ProjectViewModel::FolderBeginRemoveItem); connect(f, &Folder::EndRemoveItem, this, &ProjectViewModel::FolderEndRemoveItem); - foreach (const Node::OutputConnection& c, f->output_connections()) { - Item* item = dynamic_cast(c.second.node()); - - if (item) { - ConnectItem(item); - } + foreach (Node* c, f->children()) { + ConnectItem(c); } } } -void ProjectViewModel::DisconnectItem(Item *n) +void ProjectViewModel::DisconnectItem(Node *n) { - disconnect(n, &Item::LabelChanged, this, &ProjectViewModel::ItemRenamed); + disconnect(n, &Node::LabelChanged, this, &ProjectViewModel::ItemRenamed); Folder* f = dynamic_cast(n); if (f) { @@ -444,17 +447,13 @@ void ProjectViewModel::DisconnectItem(Item *n) disconnect(f, &Folder::BeginRemoveItem, this, &ProjectViewModel::FolderBeginRemoveItem); disconnect(f, &Folder::EndRemoveItem, this, &ProjectViewModel::FolderEndRemoveItem); - foreach (const Node::OutputConnection& c, f->output_connections()) { - Item* item = dynamic_cast(c.second.node()); - - if (item) { - DisconnectItem(item); - } + foreach (Node* c, f->children()) { + ConnectItem(c); } } } -void ProjectViewModel::FolderBeginInsertItem(Item *n, int insert_index) +void ProjectViewModel::FolderBeginInsertItem(Node *n, int insert_index) { Folder* folder = static_cast(sender()); @@ -474,7 +473,7 @@ void ProjectViewModel::FolderEndInsertItem() endInsertRows(); } -void ProjectViewModel::FolderBeginRemoveItem(Item *n, int child_index) +void ProjectViewModel::FolderBeginRemoveItem(Node *n, int child_index) { Folder* folder = static_cast(sender()); @@ -496,14 +495,14 @@ void ProjectViewModel::FolderEndRemoveItem() void ProjectViewModel::ItemRenamed() { - Item* item = static_cast(sender()); + Node* item = static_cast(sender()); QModelIndex index = CreateIndexFromItem(item); emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); } -QModelIndex ProjectViewModel::CreateIndexFromItem(Item *item, int column) +QModelIndex ProjectViewModel::CreateIndexFromItem(Node *item, int column) { return createIndex(IndexOfChild(item), column, item); } diff --git a/app/project/projectviewmodel.h b/app/project/projectviewmodel.h index 5bb2fecb9..9a57c80da 100644 --- a/app/project/projectviewmodel.h +++ b/app/project/projectviewmodel.h @@ -102,7 +102,7 @@ public: /** * @brief Convenience function for creating QModelIndexes from an Item object */ - QModelIndex CreateIndexFromItem(Item* item, int column = 0); + QModelIndex CreateIndexFromItem(Node *item, int column = 0); private: /** @@ -115,36 +115,36 @@ private: * * Index of the specified item, or -1 if the item is root (in which case it has no parent). */ - int IndexOfChild(Item* item) const; + int IndexOfChild(Node* item) const; /** * @brief Retrieves the Item object from a given index * * A convenience function for retrieving Item objects. If the index is not valid, this returns the root Item. */ - Item* GetItemObjectFromIndex(const QModelIndex& index) const; + Node* GetItemObjectFromIndex(const QModelIndex& index) const; /** * @brief Check if an Item is a parent of a Child * * Checks entire "parent hierarchy" of `child` to see if `parent` is one of its parents. */ - bool ItemIsParentOfChild(Item* parent, Item* child) const; + bool ItemIsParentOfChild(Folder *parent, Node* child) const; - void ConnectItem(Item* n); + void ConnectItem(Node* n); - void DisconnectItem(Item* n); + void DisconnectItem(Node *n); Project* project_; QVector columns_; private slots: - void FolderBeginInsertItem(Item* n, int insert_index); + void FolderBeginInsertItem(Node *n, int insert_index); void FolderEndInsertItem(); - void FolderBeginRemoveItem(Item* n, int child_index); + void FolderBeginRemoveItem(Node* n, int child_index); void FolderEndRemoveItem(); diff --git a/app/task/project/import/import.cpp b/app/task/project/import/import.cpp index 210596b43..28c852e61 100644 --- a/app/task/project/import/import.cpp +++ b/app/task/project/import/import.cpp @@ -102,7 +102,7 @@ void ProjectImportTask::Import(Folder *folder, QFileInfoList import, int &counte // Create undoable command that adds the items to the model parent_command->add_child(new NodeAddCommand(folder->parent(), f)); - parent_command->add_child(new NodeEdgeAddCommand(folder, NodeInput(f, Item::kParentInput))); + parent_command->add_child(new FolderAddChild(folder, f)); // Recursively follow this path Import(f, entry_list, counter, parent_command); @@ -123,7 +123,7 @@ void ProjectImportTask::Import(Folder *folder, QFileInfoList import, int &counte // Create undoable command that adds the items to the model parent_command->add_child(new NodeAddCommand(folder->parent(), footage)); - parent_command->add_child(new NodeEdgeAddCommand(folder, NodeInput(footage, Item::kParentInput))); + parent_command->add_child(new FolderAddChild(folder, footage)); } else { // Add to list so we can tell the user about it later invalid_files_.append(file_info.absoluteFilePath()); diff --git a/app/task/project/saveotio/saveotio.cpp b/app/task/project/saveotio/saveotio.cpp index 8f19bbe29..33b75f6c8 100644 --- a/app/task/project/saveotio/saveotio.cpp +++ b/app/task/project/saveotio/saveotio.cpp @@ -47,9 +47,7 @@ bool SaveOTIOTask::Run() std::vector serialized; - foreach (Item* item, sequences) { - Sequence* seq = static_cast(item); - + foreach (Sequence* seq, sequences) { auto otio_timeline = SerializeTimeline(seq); if (otio_timeline) { diff --git a/app/widget/nodeparamview/nodeparamviewundo.cpp b/app/widget/nodeparamview/nodeparamviewundo.cpp index 45268c5e1..105e47fae 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.cpp +++ b/app/widget/nodeparamview/nodeparamviewundo.cpp @@ -180,9 +180,25 @@ void NodeParamSetStandardValueCommand::undo() ref_.input().node()->SetSplitStandardValueOnTrack(ref_, old_value_); } -Project *NodeParamArrayInsertCommand::GetRelevantProject() const +NodeParamArrayAppendCommand::NodeParamArrayAppendCommand(Node *node, const QString &input) : + node_(node), + input_(input) { - return input_.node()->project(); +} + +Project *NodeParamArrayAppendCommand::GetRelevantProject() const +{ + return node_->project(); +} + +void NodeParamArrayAppendCommand::redo() +{ + node_->InputArrayAppend(input_, false); +} + +void NodeParamArrayAppendCommand::undo() +{ + node_->InputArrayRemoveLast(input_, false); } } diff --git a/app/widget/nodeparamview/nodeparamviewundo.h b/app/widget/nodeparamview/nodeparamviewundo.h index 7191369f8..f0e172a97 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.h +++ b/app/widget/nodeparamview/nodeparamviewundo.h @@ -139,30 +139,21 @@ private: }; -class NodeParamArrayInsertCommand : public UndoCommand +class NodeParamArrayAppendCommand : public UndoCommand { public: - NodeParamArrayInsertCommand(const NodeInput& input, int index) : - input_(input), - index_(index) - { - } + NodeParamArrayAppendCommand(Node* node, const QString& input); virtual Project* GetRelevantProject() const override; - virtual void redo() override - { - input_.node()->InputArrayInsert(input_.input(), index_); - } + virtual void redo() override; - virtual void undo() override - { - input_.node()->InputArrayRemove(input_.input(), index_); - } + virtual void undo() override; private: - NodeInput input_; - int index_; + Node* node_; + + QString input_; }; diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index f8b179fb4..f0b627fbb 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -122,7 +122,7 @@ void ProjectExplorer::set_view_type(ProjectToolbar::ViewType type) } } -void ProjectExplorer::Edit(Item *item) +void ProjectExplorer::Edit(Node *item) { CurrentView()->edit(sort_model_.mapFromSource(model_.CreateIndexFromItem(item))); } @@ -199,7 +199,7 @@ void ProjectExplorer::ItemDoubleClickedSlot(const QModelIndex &index) rename_timer_.stop(); // Retrieve source item from index - Item* i = static_cast(sort_model_.mapToSource(index).internalPointer()); + Node* i = static_cast(sort_model_.mapToSource(index).internalPointer()); // If the item is a folder, browse to it if (dynamic_cast(i) && (view_type() == ProjectToolbar::ListView || view_type() == ProjectToolbar::IconView)) { @@ -264,7 +264,7 @@ void ProjectExplorer::ShowContextMenu() // Actions to add when only one item is selected if (context_menu_items_.size() == 1) { - Item* context_menu_item = context_menu_items_.first(); + Node* context_menu_item = context_menu_items_.first(); if (dynamic_cast(context_menu_item)) { @@ -298,7 +298,7 @@ void ProjectExplorer::ShowContextMenu() bool all_items_have_video_streams = true; bool all_items_are_footage_or_sequence = true; - foreach (Item* i, context_menu_items_) { + foreach (Node* i, context_menu_items_) { Footage* footage_cast_test = dynamic_cast(i); Sequence* sequence_cast_test = dynamic_cast(i); @@ -349,7 +349,7 @@ void ProjectExplorer::ShowContextMenu() void ProjectExplorer::ShowItemPropertiesDialog() { - Item* sel = context_menu_items_.first(); + Node* sel = context_menu_items_.first(); // FIXME: Support for multiple items if (dynamic_cast(sel)) { @@ -408,7 +408,7 @@ void ProjectExplorer::ContextMenuStartProxy(QAction *a) Sequence* sequence = Node::ValueToPtr(a->data()); // To get here, the `context_menu_items_` must be all kFootage - foreach (Item* i, context_menu_items_) { + foreach (Node* i, context_menu_items_) { Footage* f = static_cast(i); QVector enabled_streams = f->GetEnabledVideoStreams(); @@ -444,18 +444,18 @@ void ProjectExplorer::set_root(Folder *item) tree_view_->setRootIndex(index); } -QVector ProjectExplorer::SelectedItems() const +QVector ProjectExplorer::SelectedItems() const { // Determine which view is active and get its selected indexes QModelIndexList index_list = CurrentView()->selectionModel()->selectedRows(); // Convert indexes to item objects - QVector selected_items; + QVector selected_items; for (int i=0;i(index.internalPointer()); + Node* item = static_cast(index.internalPointer()); selected_items.append(item); } @@ -472,7 +472,7 @@ Folder *ProjectExplorer::GetSelectedFolder() const Folder* folder = nullptr; // Get the selected items from the panel - QVector selected_items = SelectedItems(); + QVector selected_items = SelectedItems(); // Heuristic for finding the selected folder: // @@ -482,11 +482,11 @@ Folder *ProjectExplorer::GetSelectedFolder() const // - If more than one folder is found, we play it safe and import into the root folder for (int i=0;i(sel_item)) { - sel_item = sel_item->item_parent(); + sel_item = sel_item->folder(); } if (folder == nullptr) { @@ -525,7 +525,7 @@ void ProjectExplorer::DeselectAll() void ProjectExplorer::DeleteSelected() { - QVector selected = SelectedItems(); + QVector selected = SelectedItems(); if (selected.isEmpty()) { return; @@ -535,7 +535,7 @@ void ProjectExplorer::DeleteSelected() bool dont_confirm_footage_in_use = false; - foreach (Item* item, selected) { + foreach (Node* item, selected) { // Verify whether this item is in use anywhere Footage* footage_cast_test = dynamic_cast(item); Sequence* sequence_cast_test = dynamic_cast(item); diff --git a/app/widget/projectexplorer/projectexplorer.h b/app/widget/projectexplorer/projectexplorer.h index 01bc409fe..d929dff15 100644 --- a/app/widget/projectexplorer/projectexplorer.h +++ b/app/widget/projectexplorer/projectexplorer.h @@ -59,7 +59,7 @@ public: void set_root(Folder *item); - QVector SelectedItems() const; + QVector SelectedItems() const; /** * @brief Use a heuristic to determine which (if any) folder is selected @@ -89,7 +89,7 @@ public: public slots: void set_view_type(ProjectToolbar::ViewType type); - void Edit(Item* item); + void Edit(Node* item); signals: /** @@ -99,7 +99,7 @@ signals: * * The Item that was double clicked, or nullptr if empty area was double clicked */ - void DoubleClickedItem(Item* item); + void DoubleClickedItem(Node* item); private: /** @@ -153,7 +153,7 @@ private: QTimer rename_timer_; - QVector context_menu_items_; + QVector context_menu_items_; private slots: void ItemClickedSlot(const QModelIndex& index); diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index b8ec5d26a..8343b531e 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -92,7 +92,7 @@ void ImportTool::DragEnter(TimelineViewMouseEvent *event) stream >> enabled_streams >> item_ptr; // Get Item object - Item* item = reinterpret_cast(item_ptr); + Node* item = reinterpret_cast(item_ptr); // Check if Item is Footage if (dynamic_cast(item)) { @@ -390,7 +390,7 @@ void ImportTool::DropGhosts(bool insert) dst_graph = Core::instance()->GetActiveProject(); command->add_child(new NodeAddCommand(dst_graph, new_sequence)); - command->add_child(new NodeEdgeAddCommand(Core::instance()->GetSelectedFolderInActiveProject(), NodeInput(new_sequence, Item::kParentInput))); + command->add_child(new FolderAddChild(Core::instance()->GetSelectedFolderInActiveProject(), new_sequence)); new_sequence->add_default_nodes(command); FootageToGhosts(0, dragged_footage_, new_sequence->video_params().time_base(), 0); diff --git a/app/window/mainwindow/mainwindowlayoutinfo.cpp b/app/window/mainwindow/mainwindowlayoutinfo.cpp index 3466f26ea..bb13647b8 100644 --- a/app/window/mainwindow/mainwindowlayoutinfo.cpp +++ b/app/window/mainwindow/mainwindowlayoutinfo.cpp @@ -43,11 +43,8 @@ MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, XML if (reader->name() == QStringLiteral("folder")) { quintptr item_id = reader->readElementText().toULongLong(); - Item* open_item = xml_data.item_ptrs.value(item_id); - - if (open_item) { - info.open_folders_.append(static_cast(open_item)); - } + Folder* open_item = static_cast(xml_data.node_ptrs.value(item_id)); + info.open_folders_.append(open_item); } else { reader->skipCurrentElement(); } @@ -62,7 +59,7 @@ MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, XML if (reader->name() == QStringLiteral("sequence")) { quintptr item_id = reader->readElementText().toULongLong(); - open_seq = dynamic_cast(xml_data.item_ptrs.value(item_id)); + open_seq = static_cast(xml_data.node_ptrs.value(item_id)); } else if (reader->name() == QStringLiteral("state")) { tl_state = QByteArray::fromBase64(reader->readElementText().toUtf8()); } else {