implement entire project in nodes
Project items are now represented directly in nodes opening up more versatility and possibilities. This is the first iteration of this and will be buggy. Need to test thoroughly.
This commit is contained in:
@@ -22,9 +22,9 @@ add_subdirectory(colorlabelmenu)
|
||||
add_subdirectory(colorwheel)
|
||||
add_subdirectory(columnedgridlayout)
|
||||
add_subdirectory(curvewidget)
|
||||
add_subdirectory(filefield)
|
||||
add_subdirectory(flowlayout)
|
||||
add_subdirectory(focusablelineedit)
|
||||
add_subdirectory(footagecombobox)
|
||||
add_subdirectory(handmovableview)
|
||||
add_subdirectory(keyframeview)
|
||||
add_subdirectory(manageddisplay)
|
||||
|
||||
@@ -185,7 +185,7 @@ void CurveWidget::TimeTargetChangedEvent(Node *target)
|
||||
view_->SetTimeTarget(target);
|
||||
}
|
||||
|
||||
void CurveWidget::ConnectedNodeChanged(ViewerOutput *n)
|
||||
void CurveWidget::ConnectedNodeChanged(Sequence *n)
|
||||
{
|
||||
SetTimeTarget(n);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ protected:
|
||||
|
||||
virtual void TimeTargetChangedEvent(Node* target) override;
|
||||
|
||||
virtual void ConnectedNodeChanged(ViewerOutput* n) override;
|
||||
virtual void ConnectedNodeChanged(Sequence* n) override;
|
||||
|
||||
private:
|
||||
void SetKeyframeButtonEnabled(bool enable);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/footagecombobox/footagecombobox.h
|
||||
widget/footagecombobox/footagecombobox.cpp
|
||||
widget/filefield/filefield.cpp
|
||||
widget/filefield/filefield.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,69 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "filefield.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
FileField::FileField(QWidget* parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
|
||||
layout->setSpacing(0);
|
||||
layout->setMargin(0);
|
||||
|
||||
line_edit_ = new QLineEdit();
|
||||
connect(line_edit_, &QLineEdit::textChanged, this, &FileField::LineEditChanged);
|
||||
layout->addWidget(line_edit_);
|
||||
|
||||
browse_btn_ = new QPushButton();
|
||||
browse_btn_->setIcon(icon::Open);
|
||||
connect(browse_btn_, &QPushButton::clicked, this, &FileField::BrowseBtnClicked);
|
||||
layout->addWidget(browse_btn_);
|
||||
}
|
||||
|
||||
void FileField::BrowseBtnClicked()
|
||||
{
|
||||
QString s = QFileDialog::getOpenFileName(this, tr("Open File"));
|
||||
|
||||
if (!s.isEmpty()) {
|
||||
line_edit_->setText(s);
|
||||
}
|
||||
}
|
||||
|
||||
void FileField::LineEditChanged(const QString& text)
|
||||
{
|
||||
if (QFileInfo::exists(text)) {
|
||||
line_edit_->setStyleSheet(QString());
|
||||
} else {
|
||||
line_edit_->setStyleSheet(QStringLiteral("QLineEdit {color: red;}"));
|
||||
}
|
||||
|
||||
emit FilenameChanged(text);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,54 +18,45 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef FOOTAGECOMBOBOX_H
|
||||
#define FOOTAGECOMBOBOX_H
|
||||
#ifndef FILEFIELD_H
|
||||
#define FILEFIELD_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QMenu>
|
||||
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "project/project.h"
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
|
||||
namespace olive {
|
||||
|
||||
class FootageComboBox : public QComboBox
|
||||
class FileField : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FootageComboBox(QWidget* parent = nullptr);
|
||||
FileField(QWidget* parent = nullptr);
|
||||
|
||||
virtual void showPopup() override;
|
||||
|
||||
void SetRoot(const Folder *p);
|
||||
|
||||
void SetOnlyShowReadyFootage(bool e);
|
||||
|
||||
Stream* SelectedFootage() const
|
||||
QString GetFilename() const
|
||||
{
|
||||
return footage_;
|
||||
return line_edit_->text();
|
||||
}
|
||||
|
||||
public slots:
|
||||
void SetFootage(Stream* f);
|
||||
void SetFilename(const QString& s)
|
||||
{
|
||||
line_edit_->setText(s);
|
||||
}
|
||||
|
||||
signals:
|
||||
void FootageChanged(Stream* f);
|
||||
void FilenameChanged(const QString& filename);
|
||||
|
||||
private:
|
||||
void TraverseFolder(const Folder *f, QMenu* m) const;
|
||||
QLineEdit* line_edit_;
|
||||
|
||||
void UpdateText();
|
||||
QPushButton* browse_btn_;
|
||||
|
||||
static QString FootageToString(Stream* f);
|
||||
private slots:
|
||||
void BrowseBtnClicked();
|
||||
|
||||
const Folder* root_;
|
||||
void LineEditChanged(const QString &text);
|
||||
|
||||
Stream* footage_;
|
||||
|
||||
bool only_show_ready_footage_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FOOTAGECOMBOBOX_H
|
||||
#endif // FILEFIELD_H
|
||||
@@ -1,127 +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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "footagecombobox.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/menu/menu.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
FootageComboBox::FootageComboBox(QWidget *parent) :
|
||||
QComboBox(parent),
|
||||
root_(nullptr),
|
||||
footage_(nullptr),
|
||||
only_show_ready_footage_(true)
|
||||
{
|
||||
}
|
||||
|
||||
void FootageComboBox::showPopup()
|
||||
{
|
||||
if (root_ == nullptr || root_->item_child_count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Menu menu;
|
||||
|
||||
menu.setMinimumWidth(width());
|
||||
|
||||
TraverseFolder(root_, &menu);
|
||||
|
||||
QAction* selected = menu.exec(parentWidget()->mapToGlobal(pos()));
|
||||
|
||||
if (selected != nullptr) {
|
||||
SetFootage(Node::ValueToPtr<Stream>(selected->data()));
|
||||
|
||||
emit FootageChanged(footage_);
|
||||
}
|
||||
}
|
||||
|
||||
void FootageComboBox::SetRoot(const Folder *p)
|
||||
{
|
||||
root_ = p;
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
void FootageComboBox::SetOnlyShowReadyFootage(bool e)
|
||||
{
|
||||
only_show_ready_footage_ = e;
|
||||
}
|
||||
|
||||
void FootageComboBox::SetFootage(Stream *f)
|
||||
{
|
||||
// Remove existing single item used to show the footage name
|
||||
footage_ = f;
|
||||
|
||||
UpdateText();
|
||||
}
|
||||
|
||||
void FootageComboBox::TraverseFolder(const Folder *f, QMenu *m) const
|
||||
{
|
||||
foreach (Item* child, f->children()) {
|
||||
|
||||
if (child->CanHaveChildren()) {
|
||||
|
||||
Menu* sub = new Menu(child->name(), m);
|
||||
m->addMenu(sub);
|
||||
|
||||
TraverseFolder(static_cast<Folder*>(child), sub);
|
||||
|
||||
} else if (child->type() == Item::kFootage) {
|
||||
|
||||
Footage* footage = static_cast<Footage*>(child);
|
||||
|
||||
if (footage->IsValid() || !only_show_ready_footage_) {
|
||||
Menu* stream_menu = new Menu(footage->name(), m);
|
||||
m->addMenu(stream_menu);
|
||||
|
||||
foreach (Stream* stream, footage->streams()) {
|
||||
QAction* stream_action = stream_menu->addAction(FootageToString(stream));
|
||||
stream_action->setData(Node::PtrToValue(stream));
|
||||
stream_action->setIcon(stream->icon());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void FootageComboBox::UpdateText()
|
||||
{
|
||||
// Use combobox functions to show the footage name
|
||||
clear();
|
||||
|
||||
if (footage_) {
|
||||
// Use combobox functions to show the footage name
|
||||
addItem(FootageToString(footage_));
|
||||
}
|
||||
}
|
||||
|
||||
QString FootageComboBox::FootageToString(Stream *f)
|
||||
{
|
||||
return f->description();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,7 +34,7 @@ KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::
|
||||
|
||||
Project *KeyframeSetTypeCommand::GetRelevantProject() const
|
||||
{
|
||||
return key_->parent()->parent()->project();
|
||||
return key_->parent()->project();
|
||||
}
|
||||
|
||||
void KeyframeSetTypeCommand::redo()
|
||||
@@ -65,7 +65,7 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key,
|
||||
|
||||
Project *KeyframeSetBezierControlPoint::GetRelevantProject() const
|
||||
{
|
||||
return key_->parent()->parent()->project();
|
||||
return key_->parent()->project();
|
||||
}
|
||||
|
||||
void KeyframeSetBezierControlPoint::redo()
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "nodecombobox.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QEvent>
|
||||
#include <QDebug>
|
||||
|
||||
#include "node/factory.h"
|
||||
|
||||
@@ -220,7 +220,7 @@ void NodeParamView::TimeChangedEvent(const int64_t ×tamp)
|
||||
UpdateItemTime(timestamp);
|
||||
}
|
||||
|
||||
void NodeParamView::ConnectedNodeChanged(ViewerOutput *n)
|
||||
void NodeParamView::ConnectedNodeChanged(Sequence *n)
|
||||
{
|
||||
// Set viewer as a time target
|
||||
keyframe_view_->SetTimeTarget(n);
|
||||
|
||||
@@ -96,7 +96,7 @@ protected:
|
||||
virtual void TimebaseChangedEvent(const rational&) override;
|
||||
virtual void TimeChangedEvent(const int64_t &) override;
|
||||
|
||||
virtual void ConnectedNodeChanged(ViewerOutput* n) override;
|
||||
virtual void ConnectedNodeChanged(Sequence* n) override;
|
||||
|
||||
private:
|
||||
void UpdateItemTime(const int64_t ×tamp);
|
||||
|
||||
@@ -34,7 +34,7 @@ NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(const NodeInput &in
|
||||
|
||||
Project *NodeParamSetKeyframingCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_.node()->parent()->project();
|
||||
return input_.node()->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframingCommand::redo()
|
||||
@@ -64,7 +64,7 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe*
|
||||
|
||||
Project *NodeParamSetKeyframeValueCommand::GetRelevantProject() const
|
||||
{
|
||||
return key_->parent()->parent()->project();
|
||||
return key_->parent()->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeValueCommand::redo()
|
||||
@@ -87,7 +87,7 @@ NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(Node* node, NodeK
|
||||
|
||||
Project *NodeParamInsertKeyframeCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_->parent()->project();
|
||||
return input_->project();
|
||||
}
|
||||
|
||||
void NodeParamInsertKeyframeCommand::redo()
|
||||
@@ -108,7 +108,7 @@ NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeKeyframe* key
|
||||
|
||||
Project *NodeParamRemoveKeyframeCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_->parent()->project();
|
||||
return input_->project();
|
||||
}
|
||||
|
||||
void NodeParamRemoveKeyframeCommand::redo()
|
||||
@@ -138,7 +138,7 @@ NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* k
|
||||
|
||||
Project *NodeParamSetKeyframeTimeCommand::GetRelevantProject() const
|
||||
{
|
||||
return key_->parent()->parent()->project();
|
||||
return key_->parent()->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeTimeCommand::redo()
|
||||
@@ -167,7 +167,7 @@ NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKey
|
||||
|
||||
Project *NodeParamSetStandardValueCommand::GetRelevantProject() const
|
||||
{
|
||||
return ref_.input().node()->parent()->project();
|
||||
return ref_.input().node()->project();
|
||||
}
|
||||
|
||||
void NodeParamSetStandardValueCommand::redo()
|
||||
@@ -182,7 +182,7 @@ void NodeParamSetStandardValueCommand::undo()
|
||||
|
||||
Project *NodeParamArrayInsertCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_.node()->parent()->project();
|
||||
return input_.node()->project();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "undo/undostack.h"
|
||||
#include "widget/colorbutton/colorbutton.h"
|
||||
#include "widget/footagecombobox/footagecombobox.h"
|
||||
#include "widget/filefield/filefield.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
|
||||
@@ -77,9 +77,12 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kRational:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kVideoStreamProperties:
|
||||
case NodeValue::kAudioStreamProperties:
|
||||
break;
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
@@ -120,12 +123,16 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
break;
|
||||
}
|
||||
case NodeValue::kFile:
|
||||
// FIXME: File selector
|
||||
{
|
||||
FileField* file_field = new FileField();
|
||||
widgets_.append(file_field);
|
||||
connect(file_field, &FileField::FilenameChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kColor:
|
||||
{
|
||||
// NOTE: Very convoluted way to get back to the project's color manager
|
||||
ColorButton* color_button = new ColorButton(input_.node()->parent()->project()->color_manager());
|
||||
ColorButton* color_button = new ColorButton(input_.node()->project()->color_manager());
|
||||
widgets_.append(color_button);
|
||||
connect(color_button, &ColorButton::ColorChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
@@ -151,17 +158,6 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
connect(font_combobox, &QFontComboBox::currentFontChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kFootage:
|
||||
{
|
||||
FootageComboBox* footage_combobox = new FootageComboBox();
|
||||
footage_combobox->SetRoot(input_.node()->parent()->project()->root());
|
||||
|
||||
connect(footage_combobox, &FootageComboBox::FootageChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
|
||||
widgets_.append(footage_combobox);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check all properties
|
||||
@@ -252,9 +248,12 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kRational:
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kVideoStreamProperties:
|
||||
case NodeValue::kAudioStreamProperties:
|
||||
break;
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
@@ -307,8 +306,10 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
break;
|
||||
}
|
||||
case NodeValue::kFile:
|
||||
// FIXME: File selector
|
||||
{
|
||||
SetInputValue(static_cast<FileField*>(sender())->GetFilename(), 0);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kColor:
|
||||
{
|
||||
// Sender is a ColorButton
|
||||
@@ -350,12 +351,6 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
SetInputValue(static_cast<QFontComboBox*>(sender())->currentFont().family(), 0);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kFootage:
|
||||
{
|
||||
// Widget is a FootageComboBox
|
||||
SetInputValue(Node::PtrToValue(static_cast<FootageComboBox*>(sender())->SelectedFootage()), 0);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kCombo:
|
||||
{
|
||||
// Widget is a QComboBox
|
||||
@@ -403,9 +398,12 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kRational:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kFootageJob:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kVideoStreamProperties:
|
||||
case NodeValue::kAudioStreamProperties:
|
||||
break;
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
@@ -452,8 +450,13 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
break;
|
||||
}
|
||||
case NodeValue::kFile:
|
||||
// FIXME: File selector
|
||||
{
|
||||
FileField* ff = static_cast<FileField*>(widgets_.first());
|
||||
ff->blockSignals(true);
|
||||
ff->SetFilename(input_.GetValueAtTime(node_time).toString());
|
||||
ff->blockSignals(false);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kColor:
|
||||
{
|
||||
ManagedColor mc = input_.GetValueAtTime(node_time).value<Color>();
|
||||
@@ -494,9 +497,6 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
cb->blockSignals(false);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kFootage:
|
||||
static_cast<FootageComboBox*>(widgets_.first())->SetFootage(Node::ValueToPtr<Stream>(input_.GetValueAtTime(node_time)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,7 +620,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr
|
||||
}
|
||||
|
||||
// ComboBox strings changing
|
||||
if (data_type & NodeValue::kCombo) {
|
||||
if (data_type == NodeValue::kCombo) {
|
||||
QComboBox* cb = static_cast<QComboBox*>(widgets_.first());
|
||||
|
||||
int old_index = cb->currentIndex();
|
||||
|
||||
@@ -22,21 +22,14 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
QVariant NodeTableTraverser::ProcessVideoFootage(VideoStream *video_stream, const rational &input_time)
|
||||
QVariant NodeTableTraverser::ProcessVideoFootage(const Footage::StreamReference &video_stream, const rational &input_time)
|
||||
{
|
||||
return QVariant::fromValue(VideoParams(video_stream->width(),
|
||||
video_stream->height(),
|
||||
video_stream->timebase(),
|
||||
video_stream->format(),
|
||||
video_stream->channel_count(),
|
||||
video_stream->pixel_aspect_ratio()));
|
||||
return QVariant::fromValue(video_stream.video_params());
|
||||
}
|
||||
|
||||
QVariant NodeTableTraverser::ProcessAudioFootage(AudioStream *audio_stream, const TimeRange &input_time)
|
||||
QVariant NodeTableTraverser::ProcessAudioFootage(const Footage::StreamReference &audio_stream, const TimeRange &input_time)
|
||||
{
|
||||
return QVariant::fromValue(AudioParams(audio_stream->sample_rate(),
|
||||
audio_stream->channel_layout(),
|
||||
AudioParams::kInternalFormat));
|
||||
return QVariant::fromValue(audio_stream.audio_params());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ public:
|
||||
NodeTableTraverser() = default;
|
||||
|
||||
protected:
|
||||
virtual QVariant ProcessVideoFootage(VideoStream* video_stream, const rational &input_time);
|
||||
virtual QVariant ProcessVideoFootage(const Footage::StreamReference&video_stream, const rational &input_time);
|
||||
|
||||
virtual QVariant ProcessAudioFootage(AudioStream* audio_stream, const TimeRange &input_time);
|
||||
virtual QVariant ProcessAudioFootage(const Footage::StreamReference& audio_stream, const TimeRange &input_time);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "nodetreeview.h"
|
||||
|
||||
#include <QEvent>
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeTreeView::NodeTreeView(QWidget *parent) :
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef NODETREEVIEW_H
|
||||
#define NODETREEVIEW_H
|
||||
|
||||
|
||||
@@ -367,6 +367,10 @@ void NodeView::mousePressEvent(QMouseEvent *event)
|
||||
if (item) {
|
||||
create_edge_ = new NodeViewEdge();
|
||||
create_edge_src_ = item;
|
||||
|
||||
create_edge_->SetCurved(scene_.GetEdgesAreCurved());
|
||||
create_edge_->SetFlowDirection(scene_.GetFlowDirection());
|
||||
|
||||
scene_.addItem(create_edge_);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,9 @@ void NodeViewEdge::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
{
|
||||
flow_dir_ = dir;
|
||||
|
||||
Adjust();
|
||||
if (from_item_ && to_item_) {
|
||||
Adjust();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetCurved(bool e)
|
||||
|
||||
@@ -61,7 +61,7 @@ void NodeEdgeAddCommand::undo()
|
||||
|
||||
Project *NodeEdgeAddCommand::GetRelevantProject() const
|
||||
{
|
||||
return output_.node()->parent()->project();
|
||||
return output_.node()->project();
|
||||
}
|
||||
|
||||
NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(const NodeOutput &output, const NodeInput &input) :
|
||||
@@ -82,13 +82,17 @@ void NodeEdgeRemoveCommand::undo()
|
||||
|
||||
Project *NodeEdgeRemoveCommand::GetRelevantProject() const
|
||||
{
|
||||
return output_.node()->parent()->project();
|
||||
return output_.node()->project();
|
||||
}
|
||||
|
||||
NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node) :
|
||||
graph_(graph),
|
||||
node_(node)
|
||||
{
|
||||
if (memory_manager_.thread() != node->thread()) {
|
||||
memory_manager_.moveToThread(node_->thread());
|
||||
}
|
||||
|
||||
// Ensures that when this command is destroyed, if redo() is never called again, the node will be destroyed too
|
||||
node_->setParent(&memory_manager_);
|
||||
}
|
||||
@@ -105,7 +109,7 @@ void NodeAddCommand::undo()
|
||||
|
||||
Project *NodeAddCommand::GetRelevantProject() const
|
||||
{
|
||||
return graph_->project();
|
||||
return dynamic_cast<Project*>(graph_);
|
||||
}
|
||||
|
||||
NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include_connections) :
|
||||
@@ -139,4 +143,30 @@ void NodeRemoveAndDisconnectCommand::prep()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRenameCommand::AddNode(Node *node, const QString &new_name)
|
||||
{
|
||||
nodes_.append(node);
|
||||
new_labels_.append(new_name);
|
||||
old_labels_.append(node->GetLabel());
|
||||
}
|
||||
|
||||
void NodeRenameCommand::redo()
|
||||
{
|
||||
for (int i=0; i<nodes_.size(); i++) {
|
||||
nodes_.at(i)->SetLabel(new_labels_.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRenameCommand::undo()
|
||||
{
|
||||
for (int i=0; i<nodes_.size(); i++) {
|
||||
nodes_.at(i)->SetLabel(old_labels_.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
Project *NodeRenameCommand::GetRelevantProject() const
|
||||
{
|
||||
return nodes_.isEmpty() ? nullptr : nodes_.first()->project();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "node/graph.h"
|
||||
#include "node/node.h"
|
||||
#include "project/project.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -104,11 +105,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
if (graph_) {
|
||||
return graph_->project();
|
||||
} else {
|
||||
return node_->parent()->project();
|
||||
}
|
||||
return dynamic_cast<Project*>(graph_);
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -165,7 +162,7 @@ public:
|
||||
if (command_) {
|
||||
return static_cast<const NodeRemoveAndDisconnectCommand*>(command_->child(0))->GetRelevantProject();
|
||||
} else {
|
||||
return node_->parent()->project();
|
||||
return node_->project();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +233,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return a_->parent()->project();
|
||||
return a_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -276,7 +273,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return node_->parent()->project();
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -320,7 +317,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return nodes_.first()->parent()->project();
|
||||
return nodes_.first()->project();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -328,6 +325,27 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class NodeRenameCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeRenameCommand() = default;
|
||||
|
||||
void AddNode(Node* node, const QString& new_name);
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
virtual Project * GetRelevantProject() const override;
|
||||
|
||||
private:
|
||||
QVector<Node*> nodes_;
|
||||
|
||||
QStringList new_labels_;
|
||||
QStringList old_labels_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEVIEWUNDO_H
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
#include "common/define.h"
|
||||
#include "core.h"
|
||||
#include "dialog/footageproperties/footageproperties.h"
|
||||
#include "dialog/sequence/sequence.h"
|
||||
#include "projectexplorerundo.h"
|
||||
#include "task/precache/precachetask.h"
|
||||
@@ -149,7 +148,7 @@ void ProjectExplorer::BrowseToFolder(const QModelIndex &index)
|
||||
// Set navbar text to folder's name
|
||||
if (index.isValid()) {
|
||||
Folder* f = static_cast<Folder*>(sort_model_.mapToSource(index).internalPointer());
|
||||
nav_bar_->set_text(f->name());
|
||||
nav_bar_->set_text(f->GetLabel());
|
||||
} else {
|
||||
// Or set it to an empty string if the index is valid (which means we're browsing to the root directory)
|
||||
nav_bar_->set_text(QString());
|
||||
@@ -202,8 +201,7 @@ void ProjectExplorer::ItemDoubleClickedSlot(const QModelIndex &index)
|
||||
Item* i = static_cast<Item*>(sort_model_.mapToSource(index).internalPointer());
|
||||
|
||||
// If the item is a folder, browse to it
|
||||
if (i->CanHaveChildren()
|
||||
&& (view_type() == ProjectToolbar::ListView || view_type() == ProjectToolbar::IconView)) {
|
||||
if (dynamic_cast<Folder*>(i) && (view_type() == ProjectToolbar::ListView || view_type() == ProjectToolbar::IconView)) {
|
||||
|
||||
BrowseToFolder(index);
|
||||
|
||||
@@ -273,18 +271,16 @@ void ProjectExplorer::ShowContextMenu()
|
||||
if (context_menu_items_.size() == 1) {
|
||||
Item* context_menu_item = context_menu_items_.first();
|
||||
|
||||
switch (context_menu_item->type()) {
|
||||
case Item::kFolder:
|
||||
{
|
||||
if (dynamic_cast<Folder*>(context_menu_item)) {
|
||||
|
||||
QAction* open_in_new_tab = menu.addAction(tr("Open in New Tab"));
|
||||
connect(open_in_new_tab, &QAction::triggered, this, &ProjectExplorer::OpenContextMenuItemInNewTab);
|
||||
|
||||
QAction* open_in_new_window = menu.addAction(tr("Open in New Window"));
|
||||
connect(open_in_new_window, &QAction::triggered, this, &ProjectExplorer::OpenContextMenuItemInNewWindow);
|
||||
break;
|
||||
}
|
||||
case Item::kFootage:
|
||||
{
|
||||
|
||||
} else if (dynamic_cast<Footage*>(context_menu_item)) {
|
||||
|
||||
QString reveal_text;
|
||||
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
@@ -297,10 +293,7 @@ void ProjectExplorer::ShowContextMenu()
|
||||
|
||||
QAction* reveal_action = menu.addAction(reveal_text);
|
||||
connect(reveal_action, &QAction::triggered, this, &ProjectExplorer::RevealSelectedFootage);
|
||||
break;
|
||||
}
|
||||
case Item::kSequence:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
@@ -311,15 +304,18 @@ void ProjectExplorer::ShowContextMenu()
|
||||
bool all_items_are_footage_or_sequence = true;
|
||||
|
||||
foreach (Item* i, context_menu_items_) {
|
||||
if (i->type() == Item::kFootage && !static_cast<Footage*>(i)->HasEnabledStreamsOfType(Stream::kVideo)) {
|
||||
Footage* footage_cast_test = dynamic_cast<Footage*>(i);
|
||||
Sequence* sequence_cast_test = dynamic_cast<Sequence*>(i);
|
||||
|
||||
if (footage_cast_test && !footage_cast_test->HasEnabledStreamsOfType(Stream::kVideo)) {
|
||||
all_items_have_video_streams = false;
|
||||
}
|
||||
|
||||
if (i->type() != Item::kFootage) {
|
||||
if (!footage_cast_test) {
|
||||
all_items_are_footage = false;
|
||||
}
|
||||
|
||||
if (i->type() != Item::kFootage && i->type() != Item::kSequence) {
|
||||
if (!footage_cast_test && !sequence_cast_test) {
|
||||
all_items_are_footage_or_sequence = false;
|
||||
}
|
||||
}
|
||||
@@ -328,14 +324,14 @@ void ProjectExplorer::ShowContextMenu()
|
||||
Menu* proxy_menu = new Menu(tr("Pre-Cache"), &menu);
|
||||
menu.addMenu(proxy_menu);
|
||||
|
||||
QVector<Item*> sequences = project()->get_items_of_type(Item::kSequence);
|
||||
QVector<Sequence*> sequences = project()->root()->ListOutputsOfType<Sequence>();
|
||||
|
||||
if (sequences.isEmpty()) {
|
||||
QAction* a = proxy_menu->addAction(tr("No sequences exist in project"));
|
||||
a->setEnabled(false);
|
||||
} else {
|
||||
foreach (Item* i, sequences) {
|
||||
QAction* a = proxy_menu->addAction(tr("For \"%1\"").arg(i->name()));
|
||||
foreach (Sequence* i, sequences) {
|
||||
QAction* a = proxy_menu->addAction(tr("For \"%1\"").arg(i->GetLabel()));
|
||||
a->setData(Node::PtrToValue(i));
|
||||
}
|
||||
|
||||
@@ -360,26 +356,20 @@ void ProjectExplorer::ShowItemPropertiesDialog()
|
||||
{
|
||||
Item* sel = context_menu_items_.first();
|
||||
|
||||
switch (sel->type()) {
|
||||
case Item::kFootage:
|
||||
{
|
||||
// FIXME: Support for multiple items
|
||||
FootagePropertiesDialog fpd(this, static_cast<Footage*>(sel));
|
||||
fpd.exec();
|
||||
break;
|
||||
}
|
||||
case Item::kFolder:
|
||||
{
|
||||
// FIXME: Rename dialog probably
|
||||
break;
|
||||
}
|
||||
case Item::kSequence:
|
||||
{
|
||||
// FIXME: Support for multiple items
|
||||
// FIXME: Support for multiple items
|
||||
if (dynamic_cast<Footage*>(sel)) {
|
||||
|
||||
Core::instance()->LabelNodes({static_cast<Footage*>(sel)});
|
||||
|
||||
} else if (dynamic_cast<Folder*>(sel)) {
|
||||
|
||||
Core::instance()->LabelNodes({static_cast<Folder*>(sel)});
|
||||
|
||||
} else if (dynamic_cast<Sequence*>(sel)) {
|
||||
|
||||
SequenceDialog sd(static_cast<Sequence*>(sel), SequenceDialog::kExisting, this);
|
||||
sd.exec();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,35 +400,30 @@ void ProjectExplorer::RevealSelectedFootage()
|
||||
|
||||
void ProjectExplorer::OpenContextMenuItemInNewTab()
|
||||
{
|
||||
Core::instance()->main_window()->FolderOpen(project(), context_menu_items_.first(), false);
|
||||
Core::instance()->main_window()->FolderOpen(project(), static_cast<Folder*>(context_menu_items_.first()), false);
|
||||
}
|
||||
|
||||
void ProjectExplorer::OpenContextMenuItemInNewWindow()
|
||||
{
|
||||
Core::instance()->main_window()->FolderOpen(project(), context_menu_items_.first(), true);
|
||||
Core::instance()->main_window()->FolderOpen(project(), static_cast<Folder*>(context_menu_items_.first()), true);
|
||||
}
|
||||
|
||||
void ProjectExplorer::ContextMenuStartProxy(QAction *a)
|
||||
{
|
||||
QVector<VideoStream*> video_streams;
|
||||
Sequence* sequence = Node::ValueToPtr<Sequence>(a->data());
|
||||
|
||||
// To get here, the `context_menu_items_` must be all kFootage
|
||||
foreach (Item* i, context_menu_items_) {
|
||||
Footage* f = static_cast<Footage*>(i);
|
||||
VideoStream* s = static_cast<VideoStream*>(f->get_first_enabled_stream_of_type(Stream::kVideo));
|
||||
|
||||
if (s) {
|
||||
video_streams.append(s);
|
||||
QVector<int> video_streams = f->GetStreamIndexesOfType(Stream::kVideo);
|
||||
|
||||
foreach (int stream, video_streams) {
|
||||
// Start a background task for proxying
|
||||
PreCacheTask* proxy_task = new PreCacheTask(f, stream, sequence);
|
||||
TaskManager::instance()->AddTask(proxy_task);
|
||||
}
|
||||
}
|
||||
|
||||
Sequence* sequence = Node::ValueToPtr<Sequence>(a->data());
|
||||
|
||||
// Start a background task for proxying
|
||||
foreach (VideoStream* video_stream, video_streams) {
|
||||
PreCacheTask* proxy_task = new PreCacheTask(video_stream, sequence);
|
||||
TaskManager::instance()->AddTask(proxy_task);
|
||||
}
|
||||
}
|
||||
|
||||
Project *ProjectExplorer::project() const
|
||||
@@ -456,7 +441,7 @@ QModelIndex ProjectExplorer::get_root_index() const
|
||||
return tree_view_->rootIndex();
|
||||
}
|
||||
|
||||
void ProjectExplorer::set_root(Item *item)
|
||||
void ProjectExplorer::set_root(Folder *item)
|
||||
{
|
||||
QModelIndex index = sort_model_.mapFromSource(model_.CreateIndexFromItem(item));
|
||||
|
||||
@@ -505,10 +490,8 @@ Folder *ProjectExplorer::GetSelectedFolder() const
|
||||
Item* sel_item = selected_items.at(i);
|
||||
|
||||
// If this item is not a folder, presumably it's parent is
|
||||
if (!sel_item->CanHaveChildren()) {
|
||||
if (!dynamic_cast<Folder*>(sel_item)) {
|
||||
sel_item = sel_item->item_parent();
|
||||
|
||||
Q_ASSERT(sel_item->CanHaveChildren());
|
||||
}
|
||||
|
||||
if (folder == nullptr) {
|
||||
@@ -545,28 +528,6 @@ void ProjectExplorer::DeselectAll()
|
||||
CurrentView()->selectionModel()->clearSelection();
|
||||
}
|
||||
|
||||
QVector<MediaInput *> ProjectExplorer::GetMediaNodesUsingFootage(Footage *item)
|
||||
{
|
||||
QVector<MediaInput *> list;
|
||||
|
||||
// Get all sequences.
|
||||
QVector<Item*> sequences = model_.project()->get_items_of_type(Item::kSequence);
|
||||
|
||||
// Footage can contain multiple streams, all of which need to be dealt with
|
||||
foreach (Item* s, sequences) {
|
||||
const QVector<Node*>& nodes = static_cast<Sequence*>(s)->nodes();
|
||||
foreach (Node* n, nodes) {
|
||||
MediaInput* media_node = dynamic_cast<MediaInput*>(n);
|
||||
|
||||
if (media_node && media_node->stream()->footage() == item) {
|
||||
list.append(media_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void ProjectExplorer::DeleteSelected()
|
||||
{
|
||||
QVector<Item*> selected = SelectedItems();
|
||||
@@ -577,110 +538,78 @@ void ProjectExplorer::DeleteSelected()
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
bool dont_confirm_footage_in_use = false;
|
||||
|
||||
foreach (Item* item, selected) {
|
||||
// Verify whether this item is in use anywhere
|
||||
switch (item->type()) {
|
||||
case Item::kSequence:
|
||||
{
|
||||
// If this is a sequence, check if it's open and close it if necessary
|
||||
Sequence* s = static_cast<Sequence*>(item);
|
||||
Footage* footage_cast_test = dynamic_cast<Footage*>(item);
|
||||
Sequence* sequence_cast_test = dynamic_cast<Sequence*>(item);
|
||||
|
||||
if (Core::instance()->main_window()->IsSequenceOpen(s)) {
|
||||
Core::instance()->main_window()->CloseSequence(s);
|
||||
bool cleared_to_delete = true;
|
||||
|
||||
if (sequence_cast_test) {
|
||||
if (Core::instance()->main_window()->IsSequenceOpen(sequence_cast_test)) {
|
||||
Core::instance()->main_window()->CloseSequence(sequence_cast_test);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Item::kFootage:
|
||||
{
|
||||
// If this is footage, check if it's used anywhere in any sequence
|
||||
Footage* footage = static_cast<Footage*>(item);
|
||||
} else if (footage_cast_test) {
|
||||
if (!footage_cast_test->output_connections().empty() && !dont_confirm_footage_in_use) {
|
||||
// Footage outputs to other nodes, warn the user
|
||||
QMessageBox msgbox(this);
|
||||
msgbox.setWindowTitle(tr("Confirm Footage Deletion"));
|
||||
msgbox.setIcon(QMessageBox::Warning);
|
||||
|
||||
QVector<MediaInput*> footage_nodes = GetMediaNodesUsingFootage(footage);
|
||||
QStringList connected_nodes;
|
||||
foreach (const Node::OutputConnection& c, footage_cast_test->output_connections()) {
|
||||
Node* connected = c.second.node();
|
||||
|
||||
if (!footage_nodes.isEmpty()) {
|
||||
// Footage is in use, show messagebox asking what to do about it
|
||||
QList<Sequence*> used_in_sequences;
|
||||
|
||||
// Compile list of sequences to assist the user in making this decision
|
||||
foreach (MediaInput* i, footage_nodes) {
|
||||
Sequence* media_parent = static_cast<Sequence*>(i->parent());
|
||||
|
||||
if (!used_in_sequences.contains(media_parent)) {
|
||||
used_in_sequences.append(media_parent);
|
||||
if (connected->GetLabel().isEmpty()) {
|
||||
connected_nodes.append(connected->Name());
|
||||
} else {
|
||||
connected_nodes.append(QStringLiteral("%1 (%2)").arg(connected->GetLabel(), connected->Name()));
|
||||
}
|
||||
}
|
||||
|
||||
QString sequence_list_str;
|
||||
foreach (Sequence* s, used_in_sequences) {
|
||||
sequence_list_str.append(QStringLiteral("%1\n").arg(s->name()));
|
||||
}
|
||||
msgbox.setText(tr("The footage \"%1\" is currently connected to the following nodes:\n\n"
|
||||
"%2\n\n"
|
||||
"Are you sure you wish to delete this footage?")
|
||||
.arg(footage_cast_test->filename(), connected_nodes.join('\n')));
|
||||
|
||||
QMessageBox msgbox(this);
|
||||
msgbox.setWindowTitle(tr("Confirm Footage Deletion"));
|
||||
msgbox.setText(tr("The footage \"%1\" is currently used in the following sequence(s):\n\n"
|
||||
"%2\nWhat would you like to do with these clips?")
|
||||
.arg(footage->filename(), sequence_list_str));
|
||||
msgbox.setIcon(QMessageBox::Warning);
|
||||
|
||||
// Set up buttons
|
||||
QPushButton* offline_btn = msgbox.addButton(tr("Offline Footage"), QMessageBox::YesRole);
|
||||
QPushButton* delete_clip_btn = msgbox.addButton(tr("Delete Clips"), QMessageBox::NoRole);
|
||||
msgbox.addButton(QMessageBox::Yes);
|
||||
msgbox.addButton(QMessageBox::YesToAll);
|
||||
msgbox.addButton(QMessageBox::No);
|
||||
msgbox.addButton(QMessageBox::Cancel);
|
||||
|
||||
// Run messagebox
|
||||
msgbox.exec();
|
||||
int r = msgbox.exec();
|
||||
|
||||
if (msgbox.clickedButton() == offline_btn || msgbox.clickedButton() == delete_clip_btn) {
|
||||
|
||||
// For safety, even if we're deleting clips, we'll offline the footage nodes too
|
||||
command->add_child(new OfflineFootageCommand(footage_nodes));
|
||||
|
||||
}
|
||||
|
||||
if (msgbox.clickedButton() == delete_clip_btn) {
|
||||
|
||||
// Delete any blocks that use this footage
|
||||
QVector<Block*> blocks_to_remove;
|
||||
|
||||
foreach (Sequence* s, used_in_sequences) {
|
||||
foreach (Track* track, s->viewer_output()->GetTracks()) {
|
||||
foreach (Block* b, track->Blocks()) {
|
||||
QVector<Node*> deps = b->GetDependencies();
|
||||
|
||||
foreach (MediaInput* i, footage_nodes) {
|
||||
if (deps.contains(i)) {
|
||||
blocks_to_remove.append(b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimelineWidget::ReplaceBlocksWithGaps(blocks_to_remove, true, command);
|
||||
|
||||
} else if (msgbox.clickedButton() != offline_btn) {
|
||||
|
||||
// Must have cancelled
|
||||
switch (r) {
|
||||
case QMessageBox::Cancel:
|
||||
// Stop this entire function
|
||||
delete command;
|
||||
return;
|
||||
|
||||
case QMessageBox::No:
|
||||
cleared_to_delete = false;
|
||||
break;
|
||||
case QMessageBox::YesToAll:
|
||||
dont_confirm_footage_in_use = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Close footage if currently open in footage panel
|
||||
FootageViewerPanel* footage_panel = PanelManager::instance()->GetPanelsOfType<FootageViewerPanel>().first();
|
||||
if (footage_panel->GetSelectedFootage().contains(footage)) {
|
||||
footage_panel->SetFootage(nullptr);
|
||||
if (cleared_to_delete) {
|
||||
// Close footage if currently open in footage panel
|
||||
FootageViewerPanel* footage_panel = PanelManager::instance()->GetPanelsOfType<FootageViewerPanel>().first();
|
||||
if (footage_panel->GetSelectedFootage().contains(footage_cast_test)) {
|
||||
footage_panel->SetFootage(nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Item::kFolder:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
command->add_child(new ProjectViewModel::RemoveItemCommand(&model_, item));
|
||||
if (cleared_to_delete) {
|
||||
command->add_child(new NodeRemoveAndDisconnectCommand(reinterpret_cast<Node*>(item)));
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <QTimer>
|
||||
#include <QTreeView>
|
||||
|
||||
#include "node/input/media/media.h"
|
||||
#include "project/project.h"
|
||||
#include "project/projectviewmodel.h"
|
||||
#include "widget/projectexplorer/projectexplorericonview.h"
|
||||
@@ -58,7 +57,7 @@ public:
|
||||
|
||||
QModelIndex get_root_index() const;
|
||||
|
||||
void set_root(Item* item);
|
||||
void set_root(Folder *item);
|
||||
|
||||
QVector<Item *> SelectedItems() const;
|
||||
|
||||
@@ -103,11 +102,6 @@ signals:
|
||||
void DoubleClickedItem(Item* item);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Check if an item is in use anywhere and return any relevant input nodes
|
||||
*/
|
||||
QVector<MediaInput*> GetMediaNodesUsingFootage(Footage* item);
|
||||
|
||||
/**
|
||||
* @brief Get all the blocks that solely rely on an input node
|
||||
*
|
||||
|
||||
@@ -21,51 +21,10 @@
|
||||
#ifndef PROJECTEXPLORERUNDO_H
|
||||
#define PROJECTEXPLORERUNDO_H
|
||||
|
||||
#include "node/input/media/media.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief An undo command for offlining footage when it is deleted from the project explorer
|
||||
*/
|
||||
class OfflineFootageCommand : public UndoCommand {
|
||||
public:
|
||||
OfflineFootageCommand(const QVector<MediaInput*>& media)
|
||||
{
|
||||
foreach (MediaInput* i, media) {
|
||||
stream_data_.insert(i, i->stream());
|
||||
}
|
||||
|
||||
project_ = media.first()->parent()->project();
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return project_;
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
for (auto it=stream_data_.cbegin(); it!=stream_data_.cend(); it++) {
|
||||
it.key()->SetStream(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
for (auto it=stream_data_.cbegin(); it!=stream_data_.cend(); it++) {
|
||||
it.key()->SetStream(it.value());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QMap<MediaInput*, Stream*> stream_data_;
|
||||
|
||||
Project* project_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PROJECTEXPLORERUNDO_H
|
||||
|
||||
@@ -68,12 +68,12 @@ const int64_t &TimeBasedWidget::GetTimestamp() const
|
||||
return ruler_->GetTime();
|
||||
}
|
||||
|
||||
ViewerOutput *TimeBasedWidget::GetConnectedNode() const
|
||||
Sequence *TimeBasedWidget::GetConnectedNode() const
|
||||
{
|
||||
return viewer_node_;
|
||||
}
|
||||
|
||||
void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node)
|
||||
void TimeBasedWidget::ConnectViewerNode(Sequence *node)
|
||||
{
|
||||
if (viewer_node_ == node) {
|
||||
return;
|
||||
@@ -244,12 +244,12 @@ void TimeBasedWidget::resizeEvent(QResizeEvent *event)
|
||||
|
||||
TimelinePoints *TimeBasedWidget::ConnectTimelinePoints()
|
||||
{
|
||||
return static_cast<Sequence*>(viewer_node_->parent());
|
||||
return viewer_node_->timeline_points();
|
||||
}
|
||||
|
||||
Project *TimeBasedWidget::GetTimelinePointsProject()
|
||||
{
|
||||
return static_cast<Sequence*>(viewer_node_->parent())->project();
|
||||
return viewer_node_->project();
|
||||
}
|
||||
|
||||
TimelinePoints *TimeBasedWidget::GetConnectedTimelinePoints() const
|
||||
@@ -323,7 +323,10 @@ void TimeBasedWidget::ZoomOut()
|
||||
|
||||
void TimeBasedWidget::GoToPrevCut()
|
||||
{
|
||||
if (!GetConnectedNode()) {
|
||||
// Cuts are only possible in sequences
|
||||
Sequence* sequence = dynamic_cast<Sequence*>(viewer_node_);
|
||||
|
||||
if (!sequence) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -333,7 +336,7 @@ void TimeBasedWidget::GoToPrevCut()
|
||||
|
||||
int64_t closest_cut = 0;
|
||||
|
||||
foreach (Track* track, viewer_node_->GetTracks()) {
|
||||
foreach (Track* track, sequence->GetTracks()) {
|
||||
int64_t this_track_closest_cut = 0;
|
||||
|
||||
foreach (Block* block, track->Blocks()) {
|
||||
@@ -354,13 +357,16 @@ void TimeBasedWidget::GoToPrevCut()
|
||||
|
||||
void TimeBasedWidget::GoToNextCut()
|
||||
{
|
||||
if (!GetConnectedNode()) {
|
||||
// Cuts are only possible in sequences
|
||||
Sequence* sequence = dynamic_cast<Sequence*>(viewer_node_);
|
||||
|
||||
if (!sequence) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t closest_cut = INT64_MAX;
|
||||
|
||||
foreach (Track* track, GetConnectedNode()->GetTracks()) {
|
||||
foreach (Track* track, sequence->GetTracks()) {
|
||||
int64_t this_track_closest_cut = Timecode::time_to_timestamp(track->track_length(), timebase());
|
||||
|
||||
if (this_track_closest_cut <= GetTimestamp()) {
|
||||
@@ -573,7 +579,7 @@ void TimeBasedWidget::SetMarker()
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
Core::instance()->undo_stack()->push(new MarkerAddCommand(static_cast<Sequence*>(GetConnectedNode()->parent())->project(),
|
||||
Core::instance()->undo_stack()->push(new MarkerAddCommand(GetConnectedNode()->project(),
|
||||
points_->markers(), TimeRange(GetTime(), GetTime()), marker_name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ public:
|
||||
|
||||
void ZoomOut();
|
||||
|
||||
ViewerOutput* GetConnectedNode() const;
|
||||
Sequence* GetConnectedNode() const;
|
||||
|
||||
void ConnectViewerNode(ViewerOutput *node);
|
||||
void ConnectViewerNode(Sequence *node);
|
||||
|
||||
void SetScaleAndCenterOnPlayhead(const double& scale);
|
||||
|
||||
@@ -105,11 +105,11 @@ protected:
|
||||
|
||||
virtual void ScaleChangedEvent(const double &) override;
|
||||
|
||||
virtual void ConnectedNodeChanged(ViewerOutput*){}
|
||||
virtual void ConnectedNodeChanged(Sequence*){}
|
||||
|
||||
virtual void ConnectNodeInternal(ViewerOutput*){}
|
||||
virtual void ConnectNodeInternal(Sequence*){}
|
||||
|
||||
virtual void DisconnectNodeInternal(ViewerOutput*){}
|
||||
virtual void DisconnectNodeInternal(Sequence*){}
|
||||
|
||||
void SetAutoMaxScrollBar(bool e);
|
||||
|
||||
@@ -189,7 +189,7 @@ private:
|
||||
|
||||
bool UserIsDraggingPlayhead() const;
|
||||
|
||||
ViewerOutput* viewer_node_;
|
||||
Sequence* viewer_node_;
|
||||
|
||||
TimeRuler* ruler_;
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->parent()->project();
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->parent()->project();
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -147,7 +147,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->parent()->project();
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +371,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->parent()->project();
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -401,7 +401,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->parent()->project();
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -434,7 +434,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->parent()->project();
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -463,7 +463,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->parent()->project();
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -500,7 +500,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->parent()->project();
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -652,7 +652,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return blocks_.first()->parent()->project();
|
||||
return blocks_.first()->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -749,7 +749,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->parent()->project();
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -815,7 +815,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->parent()->project();
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1024,7 +1024,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return static_cast<ViewerOutput*>(list_->parent())->parent()->project();
|
||||
return list_->parent()->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -1049,9 +1049,9 @@ public:
|
||||
// We can optimize here by simply shifting the whole cache forward instead of re-caching
|
||||
// everything following this time
|
||||
if (list_->type() == Track::kVideo) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->ShiftVideoCache(out_, in_);
|
||||
list_->parent()->ShiftVideoCache(out_, in_);
|
||||
} else if (list_->type() == Track::kAudio) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->ShiftAudioCache(out_, in_);
|
||||
list_->parent()->ShiftAudioCache(out_, in_);
|
||||
}
|
||||
|
||||
foreach (Track* track, working_tracks_) {
|
||||
@@ -1076,9 +1076,9 @@ public:
|
||||
// We can optimize here by simply shifting the whole cache forward instead of re-caching
|
||||
// everything following this time
|
||||
if (list_->type() == Track::kVideo) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->ShiftVideoCache(in_, out_);
|
||||
list_->parent()->ShiftVideoCache(in_, out_);
|
||||
} else if (list_->type() == Track::kAudio) {
|
||||
static_cast<ViewerOutput*>(list_->parent())->ShiftAudioCache(in_, out_);
|
||||
list_->parent()->ShiftAudioCache(in_, out_);
|
||||
}
|
||||
|
||||
foreach (Track* track, working_tracks_) {
|
||||
@@ -1114,7 +1114,7 @@ private:
|
||||
|
||||
class TimelineRippleRemoveAreaCommand : public MultiUndoCommand {
|
||||
public:
|
||||
TimelineRippleRemoveAreaCommand(ViewerOutput* timeline, rational in, rational out) :
|
||||
TimelineRippleRemoveAreaCommand(Sequence* timeline, rational in, rational out) :
|
||||
timeline_(timeline)
|
||||
{
|
||||
for (int i=0; i<Track::kCount; i++) {
|
||||
@@ -1126,11 +1126,11 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return timeline_->parent()->project();
|
||||
return timeline_->project();
|
||||
}
|
||||
|
||||
private:
|
||||
ViewerOutput* timeline_;
|
||||
Sequence* timeline_;
|
||||
|
||||
};
|
||||
|
||||
@@ -1155,7 +1155,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return static_cast<ViewerOutput*>(track_list_->parent())->parent()->project();
|
||||
return track_list_->parent()->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -1325,9 +1325,9 @@ private:
|
||||
if (all_tracks_unlocked_) {
|
||||
// We rippled all the tracks, so we can shift the whole cache
|
||||
if (track_list_->type() == Track::kVideo) {
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->ShiftVideoCache(pre_latest_out, post_latest_out);
|
||||
track_list_->parent()->ShiftVideoCache(pre_latest_out, post_latest_out);
|
||||
} else if (track_list_->type() == Track::kAudio) {
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->ShiftAudioCache(pre_latest_out, post_latest_out);
|
||||
track_list_->parent()->ShiftAudioCache(pre_latest_out, post_latest_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1383,7 +1383,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return timeline_->GetParentGraph()->project();
|
||||
return timeline_->parent()->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -1419,9 +1419,9 @@ public:
|
||||
QString relevant_input;
|
||||
|
||||
if (timeline_->type() == Track::kVideo) {
|
||||
relevant_input = ViewerOutput::kTextureInput;
|
||||
relevant_input = Sequence::kTextureInput;
|
||||
} else {
|
||||
relevant_input = ViewerOutput::kSamplesInput;
|
||||
relevant_input = Sequence::kSamplesInput;
|
||||
}
|
||||
|
||||
if (!timeline_->parent()->IsInputConnected(relevant_input)) {
|
||||
@@ -1505,7 +1505,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return timeline_->GetParentGraph()->project();
|
||||
return timeline_->parent()->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -1604,7 +1604,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->parent()->project();
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -1637,7 +1637,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->parent()->project();
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -1784,7 +1784,7 @@ private:
|
||||
|
||||
class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand {
|
||||
public:
|
||||
TimelineRippleDeleteGapsAtRegionsCommand(ViewerOutput* vo, const TimeRangeList& regions) :
|
||||
TimelineRippleDeleteGapsAtRegionsCommand(Sequence* vo, const TimeRangeList& regions) :
|
||||
timeline_(vo),
|
||||
regions_(regions)
|
||||
{
|
||||
@@ -1797,7 +1797,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return timeline_->parent()->project();
|
||||
return timeline_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -1851,7 +1851,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
ViewerOutput* timeline_;
|
||||
Sequence* timeline_;
|
||||
TimeRangeList regions_;
|
||||
|
||||
QVector<UndoCommand*> commands_;
|
||||
@@ -1941,7 +1941,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->parent()->project();
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -1986,7 +1986,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->parent()->project();
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -2153,7 +2153,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return static_cast<ViewerOutput*>(track_list_->parent())->parent()->project();
|
||||
return track_list_->parent()->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
@@ -2166,9 +2166,9 @@ public:
|
||||
if (all_tracks_unlocked_) {
|
||||
// Optimize by shifting over since we have a constant amount of time being inserted
|
||||
if (track_list_->type() == Track::kVideo) {
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->ShiftVideoCache(point_, point_ + length_);
|
||||
track_list_->parent()->ShiftVideoCache(point_, point_ + length_);
|
||||
} else if (track_list_->type() == Track::kAudio) {
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->ShiftAudioCache(point_, point_ + length_);
|
||||
track_list_->parent()->ShiftAudioCache(point_, point_ + length_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2205,9 +2205,9 @@ public:
|
||||
if (all_tracks_unlocked_) {
|
||||
// Optimize by shifting over since we have a constant amount of time being inserted
|
||||
if (track_list_->type() == Track::kVideo) {
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->ShiftVideoCache(point_ + length_, point_);
|
||||
track_list_->parent()->ShiftVideoCache(point_ + length_, point_);
|
||||
} else if (track_list_->type() == Track::kAudio) {
|
||||
static_cast<ViewerOutput*>(track_list_->parent())->ShiftAudioCache(point_ + length_, point_);
|
||||
track_list_->parent()->ShiftAudioCache(point_ + length_, point_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2326,7 +2326,7 @@ public:
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->parent()->project();
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
|
||||
@@ -218,7 +218,7 @@ void TimelineWidget::ScaleChangedEvent(const double &scale)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
void TimelineWidget::ConnectNodeInternal(Sequence *n)
|
||||
{
|
||||
connect(n, &ViewerOutput::TrackAdded, this, &TimelineWidget::AddTrack);
|
||||
connect(n, &ViewerOutput::TrackRemoved, this, &TimelineWidget::RemoveTrack);
|
||||
@@ -229,23 +229,24 @@ void TimelineWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
SetTimebase(n->video_params().time_base());
|
||||
|
||||
for (int i=0;i<views_.size();i++) {
|
||||
Sequence* s = static_cast<Sequence*>(n);
|
||||
Track::Type track_type = static_cast<Track::Type>(i);
|
||||
TimelineView* view = views_.at(i)->view();
|
||||
TrackList* track_list = n->track_list(track_type);
|
||||
TrackList* track_list = s->track_list(track_type);
|
||||
TrackView* track_view = views_.at(i)->track_view();
|
||||
|
||||
track_view->ConnectTrackList(track_list);
|
||||
view->ConnectTrackList(track_list);
|
||||
|
||||
// Defer to the track to make all the block UI items necessary
|
||||
const QVector<Track*> tracks = n->track_list(track_type)->GetTracks();
|
||||
const QVector<Track*> tracks = s->track_list(track_type)->GetTracks();
|
||||
foreach (Track* track, tracks) {
|
||||
AddTrack(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
void TimelineWidget::DisconnectNodeInternal(Sequence *n)
|
||||
{
|
||||
disconnect(n, &ViewerOutput::TrackAdded, this, &TimelineWidget::AddTrack);
|
||||
disconnect(n, &ViewerOutput::TrackRemoved, this, &TimelineWidget::RemoveTrack);
|
||||
@@ -253,7 +254,8 @@ void TimelineWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
|
||||
DeselectAll();
|
||||
|
||||
foreach (Track* track, n->GetTracks()) {
|
||||
Sequence* s = static_cast<Sequence*>(n);
|
||||
foreach (Track* track, s->GetTracks()) {
|
||||
RemoveTrack(track);
|
||||
}
|
||||
|
||||
@@ -383,7 +385,7 @@ void TimelineWidget::SplitAtPlayhead()
|
||||
bool some_blocks_are_selected = false;
|
||||
|
||||
// Get all blocks at the playhead
|
||||
foreach (Track* track, GetConnectedNode()->GetTracks()) {
|
||||
foreach (Track* track, sequence()->GetTracks()) {
|
||||
Block* b = track->BlockContainingTime(playhead_time);
|
||||
|
||||
if (b && b->type() == Block::kClip) {
|
||||
@@ -488,7 +490,7 @@ void TimelineWidget::DeleteSelected(bool ripple)
|
||||
range_list.insert(TimeRange(b->in(), b->out()));
|
||||
}
|
||||
|
||||
command->add_child(new TimelineRippleDeleteGapsAtRegionsCommand(GetConnectedNode(), range_list));
|
||||
command->add_child(new TimelineRippleDeleteGapsAtRegionsCommand(sequence(), range_list));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
@@ -501,7 +503,7 @@ void TimelineWidget::IncreaseTrackHeight()
|
||||
}
|
||||
|
||||
// Increase the height of each track by one "unit"
|
||||
foreach (Track* t, GetConnectedNode()->GetTracks()) {
|
||||
foreach (Track* t, sequence()->GetTracks()) {
|
||||
t->SetTrackHeight(t->GetTrackHeight() + Track::kTrackHeightInterval);
|
||||
}
|
||||
}
|
||||
@@ -513,7 +515,7 @@ void TimelineWidget::DecreaseTrackHeight()
|
||||
}
|
||||
|
||||
// Decrease the height of each track by one "unit"
|
||||
foreach (Track* t, GetConnectedNode()->GetTracks()) {
|
||||
foreach (Track* t, sequence()->GetTracks()) {
|
||||
t->SetTrackHeight(qMax(t->GetTrackHeight() - Track::kTrackHeightInterval, Track::kTrackHeightMinimum));
|
||||
}
|
||||
}
|
||||
@@ -614,7 +616,7 @@ void TimelineWidget::Paste(bool insert)
|
||||
|
||||
foreach (const BlockPasteData& bpd, paste_data) {
|
||||
qDebug() << "Placing" << bpd.block;
|
||||
command->add_child(new TrackPlaceBlockCommand(GetConnectedNode()->track_list(bpd.track_type),
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(bpd.track_type),
|
||||
bpd.track_index,
|
||||
bpd.block,
|
||||
paste_start + bpd.in));
|
||||
@@ -635,12 +637,12 @@ void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
|
||||
if (ripple) {
|
||||
|
||||
command->add_child(new TimelineRippleRemoveAreaCommand(GetConnectedNode(),
|
||||
command->add_child(new TimelineRippleRemoveAreaCommand(sequence(),
|
||||
GetConnectedTimelinePoints()->workarea()->in(),
|
||||
GetConnectedTimelinePoints()->workarea()->out()));
|
||||
|
||||
} else {
|
||||
QVector<Track*> unlocked_tracks = GetConnectedNode()->GetUnlockedTracks();
|
||||
QVector<Track*> unlocked_tracks = sequence()->GetUnlockedTracks();
|
||||
|
||||
foreach (Track* track, unlocked_tracks) {
|
||||
GapBlock* gap = new GapBlock();
|
||||
@@ -650,7 +652,7 @@ void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
command->add_child(new NodeAddCommand(static_cast<NodeGraph*>(track->parent()),
|
||||
gap));
|
||||
|
||||
command->add_child(new TrackPlaceBlockCommand(GetConnectedNode()->track_list(track->type()),
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track->type()),
|
||||
track->Index(),
|
||||
gap,
|
||||
GetConnectedTimelinePoints()->workarea()->in()));
|
||||
@@ -698,7 +700,7 @@ void TimelineWidget::SetColorLabel(int index)
|
||||
void TimelineWidget::InsertGapsAt(const rational &earliest_point, const rational &insert_length, MultiUndoCommand *command)
|
||||
{
|
||||
for (int i=0;i<Track::kCount;i++) {
|
||||
command->add_child(new TrackListInsertGaps(GetConnectedNode()->track_list(static_cast<Track::Type>(i)),
|
||||
command->add_child(new TrackListInsertGaps(sequence()->track_list(static_cast<Track::Type>(i)),
|
||||
earliest_point,
|
||||
insert_length));
|
||||
}
|
||||
@@ -706,7 +708,7 @@ void TimelineWidget::InsertGapsAt(const rational &earliest_point, const rational
|
||||
|
||||
Track *TimelineWidget::GetTrackFromReference(const Track::Reference &ref) const
|
||||
{
|
||||
return GetConnectedNode()->track_list(ref.type())->GetTrackAt(ref.index());
|
||||
return sequence()->track_list(ref.type())->GetTrackAt(ref.index());
|
||||
}
|
||||
|
||||
int TimelineWidget::GetTrackY(const Track::Reference &ref)
|
||||
@@ -970,7 +972,7 @@ void TimelineWidget::ShowSequenceDialog()
|
||||
return;
|
||||
}
|
||||
|
||||
SequenceDialog sd(static_cast<Sequence*>(GetConnectedNode()->parent()), SequenceDialog::kExisting, this);
|
||||
SequenceDialog sd(sequence(), SequenceDialog::kExisting, this);
|
||||
sd.exec();
|
||||
}
|
||||
|
||||
@@ -1127,7 +1129,7 @@ QVector<Timeline::EditToInfo> TimelineWidget::GetEditToInfo(const rational& play
|
||||
Timeline::MovementMode mode)
|
||||
{
|
||||
// Get list of unlocked tracks
|
||||
QVector<Track*> tracks = GetConnectedNode()->GetUnlockedTracks();
|
||||
QVector<Track*> tracks = sequence()->GetUnlockedTracks();
|
||||
|
||||
// Create list to cache nearest times and the blocks at this point
|
||||
QVector<Timeline::EditToInfo> info_list(tracks.size());
|
||||
@@ -1206,7 +1208,7 @@ void TimelineWidget::RippleTo(Timeline::MovementMode mode)
|
||||
rational in_ripple = qMin(closest_point_to_playhead, playhead_time);
|
||||
rational out_ripple = qMax(closest_point_to_playhead, playhead_time);
|
||||
|
||||
TimelineRippleRemoveAreaCommand* c = new TimelineRippleRemoveAreaCommand(GetConnectedNode(),
|
||||
TimelineRippleRemoveAreaCommand* c = new TimelineRippleRemoveAreaCommand(sequence(),
|
||||
in_ripple,
|
||||
out_ripple);
|
||||
|
||||
@@ -1290,7 +1292,7 @@ QVector<Block *> TimelineWidget::GetBlocksInGlobalRect(const QPoint &p1, const Q
|
||||
mapped_rect = mapped_rect.normalized();
|
||||
|
||||
// Get tracks
|
||||
TrackList* track_list = GetConnectedNode()->track_list(static_cast<Track::Type>(i));
|
||||
TrackList* track_list = sequence()->track_list(static_cast<Track::Type>(i));
|
||||
|
||||
for (int j=0; j<track_list->GetTrackCount(); j++) {
|
||||
int track_top = view->GetTrackY(j);
|
||||
|
||||
@@ -91,6 +91,14 @@ public:
|
||||
|
||||
void SetColorLabel(int index);
|
||||
|
||||
/**
|
||||
* @brief Timelines should always be connected to sequences
|
||||
*/
|
||||
Sequence* sequence() const
|
||||
{
|
||||
return static_cast<Sequence*>(GetConnectedNode());
|
||||
}
|
||||
|
||||
const QVector<Block*>& GetSelectedBlocks() const
|
||||
{
|
||||
return selected_blocks_;
|
||||
@@ -237,8 +245,8 @@ protected:
|
||||
virtual void TimeChangedEvent(const int64_t &) override;
|
||||
virtual void ScaleChangedEvent(const double &) override;
|
||||
|
||||
virtual void ConnectNodeInternal(ViewerOutput* n) override;
|
||||
virtual void DisconnectNodeInternal(ViewerOutput* n) override;
|
||||
virtual void ConnectNodeInternal(Sequence* n) override;
|
||||
virtual void DisconnectNodeInternal(Sequence* n) override;
|
||||
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata) override;
|
||||
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override;
|
||||
|
||||
@@ -102,7 +102,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
command->add_child(new NodeAddCommand(graph,
|
||||
clip));
|
||||
|
||||
command->add_child(new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track.type()),
|
||||
command->add_child(new TrackPlaceBlockCommand(static_cast<Sequence*>(parent()->GetConnectedNode())->track_list(track.type()),
|
||||
track.index(),
|
||||
clip,
|
||||
ghost_->GetAdjustedIn()));
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "node/audio/volume/volume.h"
|
||||
#include "node/distort/transform/transformdistortnode.h"
|
||||
#include "node/generator/matrix/matrix.h"
|
||||
#include "node/input/media/media.h"
|
||||
#include "node/math/math/math.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
@@ -97,7 +96,7 @@ void ImportTool::DragEnter(TimelineViewMouseEvent *event)
|
||||
Item* item = reinterpret_cast<Item*>(item_ptr);
|
||||
|
||||
// Check if Item is Footage
|
||||
if (item->type() == Item::kFootage) {
|
||||
if (dynamic_cast<Footage*>(item)) {
|
||||
|
||||
Footage* f = static_cast<Footage*>(item);
|
||||
|
||||
@@ -225,8 +224,10 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QVector<DraggedFoot
|
||||
quint64 enabled_streams = footage.streams();
|
||||
|
||||
// Loop through all streams in footage
|
||||
foreach (Stream* stream, footage.footage()->streams()) {
|
||||
Track::Type track_type = TrackTypeFromStreamType(stream->type());
|
||||
foreach (const QString& output, footage.footage()->outputs()) {
|
||||
Footage::StreamReference ref = footage.footage()->GetReferenceFromOutput(output);
|
||||
|
||||
Track::Type track_type = TrackTypeFromStreamType(ref.type());
|
||||
|
||||
quint64 cached_enabled_streams = enabled_streams;
|
||||
enabled_streams >>= 1;
|
||||
@@ -239,8 +240,10 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QVector<DraggedFoot
|
||||
|
||||
TimelineViewGhostItem* ghost = new TimelineViewGhostItem();
|
||||
|
||||
if (stream->type() == Stream::kVideo
|
||||
&& static_cast<VideoStream*>(stream)->video_type() == VideoStream::kVideoTypeStill) {
|
||||
Stream stream = ref.GetStream();
|
||||
|
||||
if (ref.type() == Stream::kVideo
|
||||
&& stream.video_type() == Stream::kVideoTypeStill) {
|
||||
// Stream is essentially length-less - we may use the default still image length in config,
|
||||
// or we may use another stream's length depending on the circumstance
|
||||
contains_image_stream = true;
|
||||
@@ -251,7 +254,7 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QVector<DraggedFoot
|
||||
footage_duration = qMax(footage_duration, footage.footage()->workarea()->range().length());
|
||||
ghost->SetMediaIn(footage.footage()->workarea()->in());
|
||||
} else {
|
||||
int64_t stream_duration = Timecode::rescale_timestamp_ceil(stream->duration(), stream->timebase(), dest_tb);
|
||||
int64_t stream_duration = Timecode::rescale_timestamp_ceil(stream.duration(), stream.timebase(), dest_tb);
|
||||
footage_duration = qMax(footage_duration, Timecode::timestamp_to_time(stream_duration, dest_tb));
|
||||
}
|
||||
}
|
||||
@@ -261,7 +264,7 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QVector<DraggedFoot
|
||||
// Increment track count for this track type
|
||||
track_offsets[track_type]++;
|
||||
|
||||
ghost->SetData(TimelineViewGhostItem::kAttachedFootage, Node::PtrToValue(stream));
|
||||
ghost->SetData(TimelineViewGhostItem::kAttachedFootage, QVariant::fromValue(TimelineViewGhostItem::AttachedFootage({footage.footage(), output})));
|
||||
ghost->SetMode(Timeline::kMove);
|
||||
|
||||
footage_ghosts.append(ghost);
|
||||
@@ -310,12 +313,12 @@ void ImportTool::DropGhosts(bool insert)
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
NodeGraph* dst_graph = nullptr;
|
||||
ViewerOutput* viewer_node = nullptr;
|
||||
Sequence* open_sequence = nullptr;
|
||||
Sequence* sequence = nullptr;
|
||||
bool open_sequence = false;
|
||||
|
||||
if (parent()->GetConnectedNode()) {
|
||||
viewer_node = parent()->GetConnectedNode();
|
||||
dst_graph = static_cast<NodeGraph*>(parent()->GetConnectedNode()->parent());
|
||||
sequence = parent()->GetConnectedNode();
|
||||
dst_graph = parent()->GetConnectedNode()->parent();
|
||||
} else {
|
||||
// There's no active timeline here, ask the user what to do
|
||||
|
||||
@@ -382,19 +385,18 @@ void ImportTool::DropGhosts(bool insert)
|
||||
}
|
||||
|
||||
if (sequence_is_valid) {
|
||||
new_sequence->add_default_nodes();
|
||||
dst_graph = Core::instance()->GetActiveProject();
|
||||
|
||||
command->add_child(new ProjectViewModel::AddItemCommand(Core::instance()->GetActiveProjectModel(),
|
||||
Core::instance()->GetSelectedFolderInActiveProject(),
|
||||
new_sequence));
|
||||
command->add_child(new NodeAddCommand(dst_graph, new_sequence));
|
||||
command->add_child(new NodeEdgeAddCommand(Core::instance()->GetSelectedFolderInActiveProject(), NodeInput(new_sequence, Item::kParentInput)));
|
||||
new_sequence->add_default_nodes(command);
|
||||
|
||||
FootageToGhosts(0, dragged_footage_, new_sequence->video_params().time_base(), 0);
|
||||
|
||||
dst_graph = new_sequence;
|
||||
viewer_node = new_sequence->viewer_output();
|
||||
sequence = new_sequence;
|
||||
|
||||
// Set this as the sequence to open
|
||||
open_sequence = new_sequence;
|
||||
open_sequence = true;
|
||||
} else {
|
||||
// If the sequence is valid, ownership is passed to AddItemCommand.
|
||||
// Otherwise, we're responsible for deleting it.
|
||||
@@ -416,38 +418,32 @@ void ImportTool::DropGhosts(bool insert)
|
||||
for (int i=0;i<parent()->GetGhostItems().size();i++) {
|
||||
TimelineViewGhostItem* ghost = parent()->GetGhostItems().at(i);
|
||||
|
||||
Stream* footage_stream = Node::ValueToPtr<Stream>(ghost->GetData(TimelineViewGhostItem::kAttachedFootage));
|
||||
TimelineViewGhostItem::AttachedFootage footage_stream = ghost->GetData(TimelineViewGhostItem::kAttachedFootage).value<TimelineViewGhostItem::AttachedFootage>();
|
||||
|
||||
NodeOutput corresponding_output(footage_stream.footage, footage_stream.output);
|
||||
|
||||
ClipBlock* clip = new ClipBlock();
|
||||
clip->set_media_in(ghost->GetMediaIn());
|
||||
clip->set_length_and_media_out(ghost->GetLength());
|
||||
clip->SetLabel(footage_stream->footage()->name());
|
||||
clip->SetLabel(footage_stream.footage->GetLabel());
|
||||
command->add_child(new NodeAddCommand(dst_graph, clip));
|
||||
|
||||
switch (footage_stream->type()) {
|
||||
switch (footage_stream.footage->GetTypeFromOutput(footage_stream.output)) {
|
||||
case Stream::kVideo:
|
||||
{
|
||||
MediaInput* video_input = new MediaInput();
|
||||
video_input->SetStream(footage_stream);
|
||||
command->add_child(new NodeAddCommand(dst_graph, video_input));
|
||||
|
||||
TransformDistortNode* transform = new TransformDistortNode();
|
||||
command->add_child(new NodeAddCommand(dst_graph, transform));
|
||||
|
||||
command->add_child(new NodeEdgeAddCommand(video_input, NodeInput(transform, TransformDistortNode::kTextureInput)));
|
||||
command->add_child(new NodeEdgeAddCommand(corresponding_output, NodeInput(transform, TransformDistortNode::kTextureInput)));
|
||||
command->add_child(new NodeEdgeAddCommand(transform, NodeInput(clip, ClipBlock::kBufferIn)));
|
||||
break;
|
||||
}
|
||||
case Stream::kAudio:
|
||||
{
|
||||
MediaInput* audio_input = new MediaInput();
|
||||
audio_input->SetStream(footage_stream);
|
||||
command->add_child(new NodeAddCommand(dst_graph, audio_input));
|
||||
|
||||
VolumeNode* volume_node = new VolumeNode();
|
||||
command->add_child(new NodeAddCommand(dst_graph, volume_node));
|
||||
|
||||
command->add_child(new NodeEdgeAddCommand(audio_input, NodeInput(volume_node, VolumeNode::kSamplesInput)));
|
||||
command->add_child(new NodeEdgeAddCommand(corresponding_output, NodeInput(volume_node, VolumeNode::kSamplesInput)));
|
||||
command->add_child(new NodeEdgeAddCommand(volume_node, NodeInput(clip, ClipBlock::kBufferIn)));
|
||||
break;
|
||||
}
|
||||
@@ -455,7 +451,7 @@ void ImportTool::DropGhosts(bool insert)
|
||||
break;
|
||||
}
|
||||
|
||||
command->add_child(new TrackPlaceBlockCommand(viewer_node->track_list(ghost->GetAdjustedTrack().type()),
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence->track_list(ghost->GetAdjustedTrack().type()),
|
||||
ghost->GetAdjustedTrack().index(),
|
||||
clip,
|
||||
ghost->GetAdjustedIn()));
|
||||
@@ -464,9 +460,9 @@ void ImportTool::DropGhosts(bool insert)
|
||||
|
||||
// Link any clips so far that share the same Footage with this one
|
||||
for (int j=0;j<i;j++) {
|
||||
Stream* footage_compare = Node::ValueToPtr<Stream>(parent()->GetGhostItems().at(j)->GetData(TimelineViewGhostItem::kAttachedFootage));
|
||||
TimelineViewGhostItem::AttachedFootage footage_compare = parent()->GetGhostItems().at(j)->GetData(TimelineViewGhostItem::kAttachedFootage).value<TimelineViewGhostItem::AttachedFootage>();
|
||||
|
||||
if (footage_compare->footage() == footage_stream->footage()) {
|
||||
if (footage_compare.footage == footage_stream.footage) {
|
||||
Block::Link(block_items.at(j), clip);
|
||||
}
|
||||
}
|
||||
@@ -474,7 +470,7 @@ void ImportTool::DropGhosts(bool insert)
|
||||
}
|
||||
|
||||
if (open_sequence) {
|
||||
command->add_child(new OpenSequenceCommand(open_sequence));
|
||||
command->add_child(new OpenSequenceCommand(sequence));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
|
||||
@@ -644,7 +644,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
}
|
||||
|
||||
const Track::Reference& track_ref = p.ghost->GetAdjustedTrack();
|
||||
command->add_child(new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track_ref.type()),
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track_ref.type()),
|
||||
track_ref.index(),
|
||||
block,
|
||||
p.ghost->GetAdjustedIn()));
|
||||
|
||||
@@ -58,7 +58,7 @@ void RippleTool::InitiateDrag(Block *clicked_item,
|
||||
}
|
||||
|
||||
// For each track that does NOT have a ghost, we need to make one for Gaps
|
||||
foreach (Track* track, parent()->GetConnectedNode()->GetTracks()) {
|
||||
foreach (Track* track, sequence()->GetTracks()) {
|
||||
if (track->IsLocked()) {
|
||||
continue;
|
||||
}
|
||||
@@ -144,7 +144,7 @@ void RippleTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
|
||||
for (int i=0;i<info_list.size();i++) {
|
||||
if (!info_list.at(i).isEmpty()) {
|
||||
command->add_child(new TrackListRippleToolCommand(parent()->GetConnectedNode()->track_list(static_cast<Track::Type>(i)),
|
||||
command->add_child(new TrackListRippleToolCommand(sequence()->track_list(static_cast<Track::Type>(i)),
|
||||
info_list.at(i),
|
||||
movement,
|
||||
drag_movement_mode()));
|
||||
|
||||
@@ -39,6 +39,11 @@ TimelineWidget *TimelineTool::parent()
|
||||
return parent_;
|
||||
}
|
||||
|
||||
Sequence *TimelineTool::sequence()
|
||||
{
|
||||
return parent_->sequence();
|
||||
}
|
||||
|
||||
Timeline::MovementMode TimelineTool::FlipTrimMode(const Timeline::MovementMode &trim_mode)
|
||||
{
|
||||
if (trim_mode == Timeline::kTrimIn) {
|
||||
|
||||
@@ -53,6 +53,8 @@ public:
|
||||
|
||||
TimelineWidget* parent();
|
||||
|
||||
Sequence* sequence();
|
||||
|
||||
static Timeline::MovementMode FlipTrimMode(const Timeline::MovementMode& trim_mode);
|
||||
|
||||
static rational SnapMovementToTimebase(const rational& start, rational movement, const rational& timebase);
|
||||
|
||||
@@ -126,7 +126,7 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
command->add_child(new NodeAddCommand(static_cast<NodeGraph*>(parent()->GetConnectedNode()->parent()),
|
||||
transition));
|
||||
|
||||
command->add_child(new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track.type()),
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()),
|
||||
track.index(),
|
||||
transition,
|
||||
ghost_->GetAdjustedIn()));
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "common/flipmodifiers.h"
|
||||
#include "common/qtutils.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "node/input/media/media.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "ui/colorcoding.h"
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include "node/output/track/track.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "timeline/timelinecommon.h"
|
||||
|
||||
@@ -42,6 +43,11 @@ public:
|
||||
kTrimShouldBeIgnored
|
||||
};
|
||||
|
||||
struct AttachedFootage {
|
||||
Footage* footage;
|
||||
QString output;
|
||||
};
|
||||
|
||||
TimelineViewGhostItem() :
|
||||
track_adj_(0),
|
||||
mode_(Timeline::kNone),
|
||||
@@ -267,4 +273,6 @@ private:
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::TimelineViewGhostItem::AttachedFootage)
|
||||
|
||||
#endif // TIMELINEVIEWGHOSTITEM_H
|
||||
|
||||
@@ -32,12 +32,6 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) :
|
||||
ViewerWidget(parent),
|
||||
footage_(nullptr)
|
||||
{
|
||||
video_node_ = new MediaInput();
|
||||
video_node_->setParent(&sequence_);
|
||||
|
||||
audio_node_ = new MediaInput();
|
||||
audio_node_->setParent(&sequence_);
|
||||
|
||||
connect(display_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
|
||||
|
||||
controls_->SetAudioVideoDragButtonsVisible(true);
|
||||
@@ -57,56 +51,25 @@ void FootageViewerWidget::SetFootage(Footage *footage)
|
||||
|
||||
ConnectViewerNode(nullptr);
|
||||
|
||||
video_node_->SetStream(nullptr);
|
||||
audio_node_->SetStream(nullptr);
|
||||
|
||||
Node::DisconnectEdge(video_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kTextureInput));
|
||||
Node::DisconnectEdge(audio_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kSamplesInput));
|
||||
Node::DisconnectEdge(sequence_.GetConnectedOutput(ViewerOutput::kTextureInput), NodeInput(&sequence_, ViewerOutput::kTextureInput));
|
||||
Node::DisconnectEdge(sequence_.GetConnectedOutput(ViewerOutput::kSamplesInput), NodeInput(&sequence_, ViewerOutput::kSamplesInput));
|
||||
}
|
||||
|
||||
footage_ = footage;
|
||||
|
||||
if (footage_) {
|
||||
// Update sequence media name
|
||||
sequence_.viewer_output()->SetLabel(footage_->name());
|
||||
sequence_.SetLabel(footage_->GetLabel());
|
||||
|
||||
// Reset parameters and then attempt to set from footage
|
||||
sequence_.set_default_parameters();
|
||||
sequence_.set_parameters_from_footage({footage_});
|
||||
|
||||
// Use first of each stream
|
||||
VideoStream* video_stream = nullptr;
|
||||
AudioStream* audio_stream = nullptr;
|
||||
// Try to connect video stream
|
||||
TryConnectingType(footage, Stream::kVideo);
|
||||
TryConnectingType(footage, Stream::kAudio);
|
||||
|
||||
foreach (Stream* s, footage_->streams()) {
|
||||
if (!s->enabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!audio_stream && s->type() == Stream::kAudio) {
|
||||
audio_stream = static_cast<AudioStream*>(s);
|
||||
}
|
||||
|
||||
if (!video_stream && s->type() == Stream::kVideo) {
|
||||
video_stream = static_cast<VideoStream*>(s);
|
||||
}
|
||||
|
||||
if (audio_stream && video_stream) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (video_stream) {
|
||||
video_node_->SetStream(video_stream);
|
||||
Node::ConnectEdge(video_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kTextureInput));
|
||||
}
|
||||
|
||||
if (audio_stream) {
|
||||
audio_node_->SetStream(audio_stream);
|
||||
Node::ConnectEdge(audio_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kSamplesInput));
|
||||
}
|
||||
|
||||
ConnectViewerNode(sequence_.viewer_output(), footage_->project()->color_manager());
|
||||
ConnectViewerNode(&sequence_, footage_->project()->color_manager());
|
||||
|
||||
SetTimestamp(cached_timestamps_.value(footage_, 0));
|
||||
} else {
|
||||
@@ -134,7 +97,7 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl
|
||||
QMimeData* mimedata = new QMimeData();
|
||||
|
||||
QByteArray encoded_data;
|
||||
QDataStream stream(&encoded_data, QIODevice::WriteOnly);
|
||||
QDataStream data_stream(&encoded_data, QIODevice::WriteOnly);
|
||||
|
||||
quint64 enabled_stream_flags = GetFootage()->get_enabled_stream_flags();
|
||||
|
||||
@@ -142,9 +105,11 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl
|
||||
if (!enable_video || !enable_audio) {
|
||||
quint64 stream_disabler = 0x1;
|
||||
|
||||
foreach (Stream* s, GetFootage()->streams()) {
|
||||
if ((s->type() == Stream::kVideo && !enable_video)
|
||||
|| (s->type() == Stream::kAudio && !enable_audio)) {
|
||||
for (int i=0; i<GetFootage()->GetStreamCount(); i++) {
|
||||
Stream stream = GetFootage()->GetStreamAt(i);
|
||||
|
||||
if ((stream.type() == Stream::kVideo && !enable_video)
|
||||
|| (stream.type() == Stream::kAudio && !enable_audio)) {
|
||||
enabled_stream_flags &= ~stream_disabler;
|
||||
}
|
||||
|
||||
@@ -152,7 +117,7 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl
|
||||
}
|
||||
}
|
||||
|
||||
stream << enabled_stream_flags << -1 << reinterpret_cast<quintptr>(GetFootage());
|
||||
data_stream << enabled_stream_flags << -1 << reinterpret_cast<quintptr>(GetFootage());
|
||||
|
||||
mimedata->setData("application/x-oliveprojectitemdata", encoded_data);
|
||||
drag->setMimeData(mimedata);
|
||||
@@ -160,6 +125,32 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl
|
||||
drag->exec();
|
||||
}
|
||||
|
||||
void FootageViewerWidget::TryConnectingType(Footage *footage, Stream::Type type)
|
||||
{
|
||||
for (int i=0; ; i++) {
|
||||
Stream stream = footage_->GetStreamAt(type, i);
|
||||
|
||||
// Found end of stream list
|
||||
if (!stream.IsValid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream.enabled()) {
|
||||
QString s = Footage::GetStringFromReference(type, i);
|
||||
|
||||
QString input_param;
|
||||
|
||||
if (type == Stream::kVideo) {
|
||||
input_param = ViewerOutput::kTextureInput;
|
||||
} else {
|
||||
input_param = ViewerOutput::kSamplesInput;
|
||||
}
|
||||
|
||||
Node::ConnectEdge(NodeOutput(footage, s), NodeInput(&sequence_, input_param));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FootageViewerWidget::StartFootageDrag()
|
||||
{
|
||||
StartFootageDragInternal(true, true);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#ifndef FOOTAGEVIEWERWIDGET_H
|
||||
#define FOOTAGEVIEWERWIDGET_H
|
||||
|
||||
#include "node/input/media/media.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "viewer.h"
|
||||
|
||||
@@ -44,14 +43,12 @@ protected:
|
||||
private:
|
||||
void StartFootageDragInternal(bool enable_video, bool enable_audio);
|
||||
|
||||
void TryConnectingType(Footage* footage, Stream::Type type);
|
||||
|
||||
Footage* footage_;
|
||||
|
||||
Sequence sequence_;
|
||||
|
||||
MediaInput* video_node_;
|
||||
|
||||
MediaInput* audio_node_;
|
||||
|
||||
QHash<Footage*, int64_t> cached_timestamps_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
QVariant GizmoTraverser::ProcessVideoFootage(VideoStream *stream, const rational &input_time)
|
||||
QVariant GizmoTraverser::ProcessVideoFootage(const Footage::StreamReference &ref, const rational &input_time)
|
||||
{
|
||||
Q_UNUSED(input_time)
|
||||
|
||||
VideoStream* image_stream = static_cast<VideoStream*>(stream);
|
||||
Stream stream = ref.GetStream();
|
||||
|
||||
return QVector2D(image_stream->width() * image_stream->pixel_aspect_ratio().toDouble(),
|
||||
image_stream->height());
|
||||
return QVector2D(stream.width() * stream.pixel_aspect_ratio().toDouble(),
|
||||
stream.height());
|
||||
}
|
||||
|
||||
QVariant GizmoTraverser::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job)
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual QVariant ProcessVideoFootage(VideoStream* stream, const rational &input_time) override;
|
||||
virtual QVariant ProcessVideoFootage(const Footage::StreamReference& stream, const rational &input_time) override;
|
||||
|
||||
virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ void ViewerWidget::TimeChangedEvent(const int64_t &i)
|
||||
last_time_ = i;
|
||||
}
|
||||
|
||||
void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
void ViewerWidget::ConnectNodeInternal(Sequence *n)
|
||||
{
|
||||
connect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SetViewerResolution);
|
||||
connect(n, &ViewerOutput::PixelAspectChanged, this, &ViewerWidget::SetViewerPixelAspect);
|
||||
@@ -193,7 +193,7 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
if (override_color_manager_) {
|
||||
using_manager = override_color_manager_;
|
||||
} else if (n->parent()) {
|
||||
using_manager = static_cast<Sequence*>(n->parent())->project()->color_manager();
|
||||
using_manager = n->project()->color_manager();
|
||||
} else {
|
||||
qWarning() << "Failed to find a suitable color manager for the connected viewer node";
|
||||
using_manager = nullptr;
|
||||
@@ -220,7 +220,7 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
ForceUpdate();
|
||||
}
|
||||
|
||||
void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
void ViewerWidget::DisconnectNodeInternal(Sequence *n)
|
||||
{
|
||||
PauseInternal();
|
||||
|
||||
@@ -252,7 +252,7 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
QMetaObject::invokeMethod(this, "UpdateStack", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void ViewerWidget::ConnectedNodeChanged(ViewerOutput *n)
|
||||
void ViewerWidget::ConnectedNodeChanged(Sequence *n)
|
||||
{
|
||||
auto_cacher_.SetViewerNode(n);
|
||||
}
|
||||
@@ -285,7 +285,7 @@ bool ViewerWidget::IsPlaying() const
|
||||
return playback_speed_ != 0;
|
||||
}
|
||||
|
||||
void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_manager)
|
||||
void ViewerWidget::ConnectViewerNode(Sequence *node, ColorManager* color_manager)
|
||||
{
|
||||
override_color_manager_ = color_manager;
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
bool IsPlaying() const;
|
||||
|
||||
void ConnectViewerNode(ViewerOutput* node, ColorManager *color_manager = nullptr);
|
||||
void ConnectViewerNode(Sequence *node, ColorManager *color_manager = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the color management menu
|
||||
@@ -153,9 +153,9 @@ protected:
|
||||
virtual void TimebaseChangedEvent(const rational &) override;
|
||||
virtual void TimeChangedEvent(const int64_t &) override;
|
||||
|
||||
virtual void ConnectNodeInternal(ViewerOutput *) override;
|
||||
virtual void DisconnectNodeInternal(ViewerOutput *) override;
|
||||
virtual void ConnectedNodeChanged(ViewerOutput*n) override;
|
||||
virtual void ConnectNodeInternal(Sequence *) override;
|
||||
virtual void DisconnectNodeInternal(Sequence *) override;
|
||||
virtual void ConnectedNodeChanged(Sequence*n) override;
|
||||
|
||||
virtual void ScaleChangedEvent(const double& s) override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user