Merge branch 'master' into OTIO_load

This commit is contained in:
ThomasWilshaw
2022-01-06 13:56:08 +00:00
committed by GitHub
195 changed files with 11848 additions and 9102 deletions
+13 -16
View File
@@ -64,6 +64,7 @@ jobs:
cd $GITHUB_WORKSPACE/app/dialog/about
python3 patreon.py
if: github.event_name == 'push'
continue-on-error: true
- name: Configure CMake
run: |
@@ -196,6 +197,7 @@ jobs:
cd $GITHUB_WORKSPACE/app/dialog/about
python3 patreon.py
if: github.event_name == 'push'
continue-on-error: true
- name: Configure CMake
shell: bash
@@ -283,6 +285,8 @@ jobs:
os-arch: x86_64
os: macos-10.15
cmake-gen: Ninja
env:
DEP_LOCATION: /opt/olive-editor
name: |
${{ matrix.os-name }}
<${{ matrix.compiler-name }},
@@ -294,9 +298,6 @@ jobs:
- name: Checkout Source Code
uses: actions/checkout@v2
- name: Set up Homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Automatically Generate Package Name
shell: bash
env:
@@ -313,14 +314,8 @@ jobs:
shell: bash
working-directory: ${{ runner.workspace }}
run: |
# HACK: Workaround for https://github.com/actions/virtual-environments/issues/4020
rm -rf /usr/local/bin/2to3
brew update
brew upgrade
brew tap olive-editor/homebrew
brew install -f qt5 ffmpeg-olive openimageio-olive opencolorio opentimelineio portaudio
echo "/usr/local/opt/qt@5/bin" >> $GITHUB_PATH
$DOWNLOAD_TOOL https://github.com/olive-editor/dependencies/releases/download/continuous/olive-dep-mac-x86_64.tar.gz
sudo tar xzf olive-dep-mac-x86_64.tar.gz -C /
- name: Acquire Google Crashpad
shell: bash
@@ -337,14 +332,16 @@ jobs:
cd $GITHUB_WORKSPACE/app/dialog/about
python3 patreon.py
if: github.event_name == 'push'
continue-on-error: true
- name: Configure CMake
shell: bash
working-directory: ${{ runner.workspace }}/build
run: |
brew install ninja
PATH=/opt/olive-editor/bin:/opt/olive-editor/crashpad:$PATH \
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -G "${{ matrix.cmake-gen }}"
PATH=$DEP_LOCATION:$DEP_LOCATION/bin:$DEP_LOCATION/include:$DEP_LOCATION/lib:$DEP_LOCATION/crashpad:$PATH \
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -G "${{ matrix.cmake-gen }}"
- name: Build
working-directory: ${{ runner.workspace }}/build
@@ -366,9 +363,9 @@ jobs:
run: |
# Use macdeployqt and macdeployqt to bundle dependencies
mv app/$BUNDLE_NAME .
macdeployqt $BUNDLE_NAME -executable=$BUNDLE_NAME/Contents/MacOS/olive-crashhandler
$DEP_LOCATION/bin/macdeployqt $BUNDLE_NAME -executable=$BUNDLE_NAME/Contents/MacOS/olive-crashhandler
$DOWNLOAD_TOOL https://github.com/arl/macdeployqtfix/raw/master/macdeployqtfix.py
python2 macdeployqtfix.py $BUNDLE_NAME/Contents/MacOS/Olive $(dirname $(which qmake))
python2 macdeployqtfix.py $BUNDLE_NAME/Contents/MacOS/Olive $VCPKG_ROOT/installed/x64-osx
# Manual corrections
for f in $BUNDLE_NAME/Contents/Frameworks/*.dylib
@@ -380,7 +377,7 @@ jobs:
done
# Crashpad symbols
/opt/olive-editor/bin/dump_syms $BUNDLE_NAME/Contents/MacOS/Olive > Olive.sym
$DEP_LOCATION/bin/dump_syms $BUNDLE_NAME/Contents/MacOS/Olive > Olive.sym
SYM_HEADER=($(head -n 1 Olive.sym)) # Read first line of symbol file
SYM_DIR=$BUNDLE_NAME/Contents/Resources/symbols/Olive/${SYM_HEADER[3]}
mkdir -p "$SYM_DIR"
+20 -3
View File
@@ -55,6 +55,9 @@ else()
endif()
set(OLIVE_DEFINITIONS -DQT_DEPRECATED_WARNINGS)
if (WIN32)
list(APPEND OLIVE_DEFINITIONS -DUNICODE -D_UNICODE)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
@@ -81,8 +84,7 @@ list(APPEND OLIVE_LIBRARIES ${OPENEXR_LIBRARIES})
list(APPEND OLIVE_INCLUDE_DIRS ${OPENEXR_INCLUDES})
# Link Qt 5
find_package(Qt5 5.6 REQUIRED
COMPONENTS
set(QT_LIBRARIES
Core
Gui
Widgets
@@ -90,8 +92,15 @@ find_package(Qt5 5.6 REQUIRED
Svg
LinguistTools
Concurrent
)
if (UNIX AND NOT APPLE)
list(APPEND QT_LIBRARIES DBus)
endif()
find_package(Qt5 5.6 REQUIRED
COMPONENTS
${QT_LIBRARIES}
OPTIONAL_COMPONENTS
Network
Network
)
if (NOT Qt5Network_FOUND)
message(" Qt5::Network module not found, crash reporting will be disabled.")
@@ -156,6 +165,14 @@ else()
endif()
endif()
if (WIN32)
list(APPEND OLIVE_DEFINITIONS "-DUNICODE -D_UNICODE")
elseif (APPLE)
list(APPEND OLIVE_LIBRARIES "-framework IOKit")
elseif(UNIX)
list(APPEND OLIVE_LIBRARIES Qt5::DBus)
endif()
# Generate Git hash
set(PROJECT_LONG_VERSION ${PROJECT_VERSION})
if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
+1 -3
View File
@@ -66,9 +66,7 @@ add_library(olive-version-obj
version.h
)
target_link_libraries(olive-version-obj PRIVATE Qt5::Core)
if(DEFINED GIT_HASH)
target_compile_options(olive-version-obj PRIVATE -DAPPVERSION="${PROJECT_VERSION}" -DAPPVERSIONLONG="${PROJECT_LONG_VERSION}" )
endif()
target_compile_options(olive-version-obj PRIVATE -DAPPVERSION="${PROJECT_VERSION}" -DAPPVERSIONLONG="${PROJECT_LONG_VERSION}" )
# Add main library
add_library(libolive-editor
+1 -1
View File
@@ -572,7 +572,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
// Find encoder
AVCodec* encoder = GetEncoder(codec);
if (!encoder) {
SetError(tr("Failed to find codec for %1").arg(codec));
SetError(tr("Failed to find codec for 0x%1").arg(codec, 16));
return false;
}
+4
View File
@@ -58,6 +58,10 @@ double Bezier::CalculateTFromX(bool cubic, double x, double a, double b, double
double top = 1.0;
while (true) {
if (bottom == top) {
return bottom;
}
double mid = (bottom + top) * 0.5;
double test = cubic ? CubicTtoY(a, b, c, d, mid) : QuadraticTtoY(a, b, c, mid);
+4 -4
View File
@@ -52,16 +52,16 @@ const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArg
return a;
}
void CommandLineParser::Process(int argc, char **argv)
void CommandLineParser::Process(const QVector<QString> &argv)
{
int positional_index = 0;
for (int i=1; i<argc; i++) {
for (int i=1; i<argv.size(); i++) {
if (argv[i][0] == '-') {
// Must be an option
// Skip past first dashes
const char* arg_basename = &argv[i][1];
QString arg_basename = argv[i].mid(1);
bool matched_known = false;
@@ -73,7 +73,7 @@ void CommandLineParser::Process(int argc, char **argv)
// Flag discovered!
o.option->Set();
if (o.takes_arg && i+1 < argc) {
if (o.takes_arg && i+1 < argv.size()) {
o.option->SetSetting(argv[i+1]);
i++;
}
+1 -1
View File
@@ -91,7 +91,7 @@ public:
const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false);
void Process(int argc, char** argv);
void Process(const QVector<QString> &argv);
void PrintHelp(const char* filename);
+53 -31
View File
@@ -24,37 +24,10 @@
#include "node/factory.h"
#include "widget/nodeparamview/nodeparamviewundo.h"
#include "widget/nodeview/nodeviewundo.h"
//#include "widget/timelinewidget/undo/timelineundogeneral.h"
namespace olive {
void XMLConnectNodes(const XMLNodeData &xml_node_data, uint version, MultiUndoCommand *command)
{
foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) {
Node *out = xml_node_data.node_ptrs.value(con.output_node);
if (out) {
// Use output param as hint tag since we grandfathered those in
Node::ValueHint hint(con.output_param);
if (command) {
command->add_child(new NodeEdgeAddCommand(out, con.input));
if (version < 210907) {
/// Deprecated: backwards compatibility only
command->add_child(new NodeSetValueHintCommand(con.input, hint));
}
} else {
Node::ConnectEdge(out, con.input);
if (version < 210907) {
/// Deprecated: backwards compatibility only
con.input.node()->SetValueHintForInput(con.input.input(), hint, con.input.element());
}
}
}
}
}
bool XMLReadNextStartElement(QXmlStreamReader *reader)
{
QXmlStreamReader::TokenType token;
@@ -71,10 +44,59 @@ bool XMLReadNextStartElement(QXmlStreamReader *reader)
return false;
}
void XMLLinkBlocks(const XMLNodeData &xml_node_data)
void XMLNodeData::PostConnect(uint version, MultiUndoCommand *command) const
{
foreach (const XMLNodeData::BlockLink& l, xml_node_data.block_links) {
Block::Link(l.block, static_cast<Block*>(xml_node_data.node_ptrs.value(l.link)));
foreach (const XMLNodeData::SerializedConnection& con, desired_connections) {
if (Node *out = node_ptrs.value(con.output_node)) {
// Use output param as hint tag since we grandfathered those in
Node::ValueHint hint(con.output_param);
if (command) {
command->add_child(new NodeEdgeAddCommand(out, con.input));
} else {
Node::ConnectEdge(out, con.input);
}
if (version < 210907) {
/// Deprecated: backwards compatibility only
if (command) {
command->add_child(new NodeSetValueHintCommand(con.input, hint));
} else {
con.input.node()->SetValueHintForInput(con.input.input(), hint, con.input.element());
}
}
}
}
foreach (const XMLNodeData::BlockLink& l, block_links) {
Node *a = l.block;
Node *b = node_ptrs.value(l.link);
if (command) {
command->add_child(new NodeLinkCommand(a, b, true));
} else {
Node::Link(a, b);
}
}
foreach (const XMLNodeData::GroupLink &l, group_input_links) {
if (Node *input_node = node_ptrs.value(l.input_node)) {
NodeInput resolved(input_node, l.input_id, l.input_element);
if (command) {
command->add_child(new NodeGroupAddInputPassthrough(l.group, resolved));
} else {
l.group->AddInputPassthrough(resolved);
}
}
}
for (auto it=group_output_links.cbegin(); it!=group_output_links.cend(); it++) {
if (Node *output_node = node_ptrs.value(it.value())) {
if (command) {
command->add_child(new NodeGroupSetOutputPassthrough(it.key(), output_node));
} else {
it.key()->SetOutputPassthrough(output_node);
}
}
}
}
+12 -4
View File
@@ -31,6 +31,7 @@ namespace olive {
class Block;
class Node;
class NodeInput;
class NodeGroup;
#define XMLAttributeLoop(reader, item) \
foreach (const QXmlStreamAttribute& item, reader->attributes())
@@ -49,14 +50,23 @@ struct XMLNodeData {
quintptr link;
};
struct GroupLink {
NodeGroup *group;
quintptr input_node;
QString input_id;
int input_element;
};
QHash<quintptr, Node*> node_ptrs;
QList<SerializedConnection> desired_connections;
QList<BlockLink> block_links;
QVector<GroupLink> group_input_links;
QHash<NodeGroup*, quintptr> group_output_links;
void PostConnect(uint version, MultiUndoCommand *command = nullptr) const;
};
void XMLConnectNodes(const XMLNodeData& xml_node_data, uint version, MultiUndoCommand *command = nullptr);
/**
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
*
@@ -68,8 +78,6 @@ void XMLConnectNodes(const XMLNodeData& xml_node_data, uint version, MultiUndoCo
*/
bool XMLReadNextStartElement(QXmlStreamReader* reader);
void XMLLinkBlocks(const XMLNodeData& xml_node_data);
}
#endif // XMLREADLOOP_H
+1
View File
@@ -100,6 +100,7 @@ void Config::SetDefaults()
SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true);
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
+29 -25
View File
@@ -78,7 +78,7 @@
namespace olive {
Core* Core::instance_ = nullptr;
const uint Core::kProjectVersion = 210907;
const uint Core::kProjectVersion = 211228;
Core::Core(const CoreParams& params) :
main_window_(nullptr),
@@ -202,10 +202,7 @@ void Core::Stop()
{
QFile recent_projects_file(GetRecentProjectsFilePath());
if (recent_projects_file.open(QFile::WriteOnly | QFile::Text)) {
QTextStream ts(&recent_projects_file);
ts << recent_projects_.join('\n');
recent_projects_file.write(recent_projects_.join('\n').toUtf8());
recent_projects_file.close();
}
}
@@ -448,7 +445,7 @@ void Core::CreateNewSequence()
command->add_child(new NodeAddCommand(active_project, new_sequence));
command->add_child(new FolderAddChild(GetSelectedFolderInActiveProject(), new_sequence));
command->add_child(new NodeSetPositionCommand(new_sequence, new_sequence, QPointF(0, 0), false));
command->add_child(new NodeSetPositionCommand(new_sequence, new_sequence, Node::Position()));
// Create and connect default nodes to new sequence
new_sequence->add_default_nodes(command);
@@ -493,19 +490,21 @@ bool Core::AddOpenProjectFromTask(Task *task)
{
ProjectLoadBaseTask* load_task = static_cast<ProjectLoadBaseTask*>(task);
Project* project = load_task->GetLoadedProject();
MainWindowLayoutInfo layout = load_task->GetLoadedLayout();
if (!load_task->IsCancelled()) {
Project* project = load_task->GetLoadedProject();
MainWindowLayoutInfo layout = load_task->GetLoadedLayout();
if (ValidateFootageInLoadedProject(project, load_task->GetFilenameProjectWasSavedAs())) {
AddOpenProject(project);
main_window_->LoadLayout(layout);
if (ValidateFootageInLoadedProject(project, load_task->GetFilenameProjectWasSavedAs())) {
AddOpenProject(project);
main_window_->LoadLayout(layout);
return true;
} else {
delete project;
return false;
return true;
} else {
delete project;
}
}
return false;
}
void Core::ImportTaskComplete(Task* task)
@@ -740,13 +739,10 @@ void Core::StartGUI(bool full_screen)
{
QFile recent_projects_file(GetRecentProjectsFilePath());
if (recent_projects_file.open(QFile::ReadOnly | QFile::Text)) {
QTextStream ts(&recent_projects_file);
QString s;
while (!(s = ts.readLine()).isEmpty()) {
recent_projects_.append(s);
QString r = QString::fromUtf8(recent_projects_file.readAll());
if (!r.isEmpty()) {
recent_projects_ = r.split('\n');
}
recent_projects_file.close();
}
@@ -1369,10 +1365,10 @@ void Core::SetPreferenceForRenderMode(RenderMode::Mode mode, const QString &pref
Config::Current()[GetRenderModePreferencePrefix(mode, preference)] = value;
}
void Core::LabelNodes(const QVector<Node *> &nodes)
bool Core::LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent)
{
if (nodes.isEmpty()) {
return;
return false;
}
bool ok;
@@ -1401,8 +1397,16 @@ void Core::LabelNodes(const QVector<Node *> &nodes)
rename_command->AddNode(n, s);
}
undo_stack_.push(rename_command);
if (parent) {
parent->add_child(rename_command);
} else {
undo_stack_.push(rename_command);
}
return true;
}
return false;
}
Sequence *Core::CreateNewSequenceForProject(Project* project) const
+1 -1
View File
@@ -253,7 +253,7 @@ public:
/**
* @brief Show a dialog to the user to rename a set of nodes
*/
void LabelNodes(const QVector<Node *> &nodes);
bool LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent = nullptr);
/**
* @brief Create a new sequence named appropriately for the active project
+1 -1
View File
@@ -21,9 +21,9 @@ add_subdirectory(color)
add_subdirectory(configbase)
add_subdirectory(diskcache)
add_subdirectory(export)
add_subdirectory(footageproperties)
add_subdirectory(footagerelink)
add_subdirectory(keyframeproperties)
add_subdirectory(nodeproperties)
if(OpenTimelineIO_FOUND)
add_subdirectory(otioproperties)
endif()
+1 -1
View File
@@ -27,6 +27,6 @@ while True:
else:
break
text_file = open("patreon.h", "w")
text_file = open("patreon.h", "w", encoding="utf-8")
text_file.write("#ifndef PATREON_H\n#define PATREON_H\n\n#include <QStringList>\n\nQStringList patrons = {\n%s\n};\n\n#endif // PATREON_H\n" % name_list)
text_file.close()
@@ -1,5 +1,5 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 Olive Team
# 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
@@ -14,9 +14,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(streamproperties)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
dialog/nodeproperties/nodepropertiesdialog.cpp
dialog/nodeproperties/nodepropertiesdialog.h
dialog/footageproperties/footageproperties.cpp
dialog/footageproperties/footageproperties.h
PARENT_SCOPE
)
@@ -0,0 +1,251 @@
/***
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 "footageproperties.h"
#include <QGridLayout>
#include <QLabel>
#include <QComboBox>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QTreeWidgetItem>
#include <QGroupBox>
#include <QListWidget>
#include <QCheckBox>
#include <QSpinBox>
#include "core.h"
#include "streamproperties/audiostreamproperties.h"
#include "streamproperties/videostreamproperties.h"
#include "widget/nodeview/nodeviewundo.h"
namespace olive {
FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent, Footage *footage) :
QDialog(parent),
footage_(footage)
{
QGridLayout* layout = new QGridLayout(this);
setWindowTitle(tr("\"%1\" Properties").arg(footage_->GetLabelOrName()));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
int row = 0;
layout->addWidget(new QLabel(tr("Name:")), row, 0);
footage_name_field_ = new QLineEdit(footage_->GetLabel());
layout->addWidget(footage_name_field_, row, 1);
row++;
layout->addWidget(new QLabel(tr("Tracks:")), row, 0, 1, 2);
row++;
track_list = new QListWidget();
layout->addWidget(track_list, row, 0, 1, 2);
row++;
stacked_widget_ = new QStackedWidget();
layout->addWidget(stacked_widget_, row, 0, 1, 2);
int first_usable_stream = -1;
for (int i=0; i<footage_->GetTotalStreamCount(); i++) {
Track::Reference reference = footage_->GetReferenceFromRealIndex(i);
QString description;
bool is_enabled = false;
switch (reference.type()) {
case Track::kVideo:
{
stacked_widget_->addWidget(new VideoStreamProperties(footage_, reference.index()));
VideoParams vp = footage_->GetVideoParams(reference.index());
is_enabled = vp.enabled();
description = tr("%1x%2 %3 FPS").arg(QString::number(vp.width()), QString::number(vp.height()), QString::number(vp.frame_rate().toDouble()));
break;
}
case Track::kAudio:
{
stacked_widget_->addWidget(new AudioStreamProperties(footage_, reference.index()));
AudioParams ap = footage_->GetAudioParams(reference.index());
is_enabled = ap.enabled();
description = tr("%1 Hz %2 channels").arg(QString::number(ap.sample_rate()), QString::number(ap.channel_count()));
break;
}
default:
stacked_widget_->addWidget(new StreamProperties());
description = tr("Unknown");
break;
}
QListWidgetItem* item = new QListWidgetItem(description, track_list);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(is_enabled ? Qt::Checked : Qt::Unchecked);
track_list->addItem(item);
if (first_usable_stream == -1
&& (reference.type() == Track::kVideo
|| reference.type() == Track::kAudio)) {
first_usable_stream = i;
}
}
row++;
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttons->setCenterButtons(true);
layout->addWidget(buttons, row, 0, 1, 2);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(track_list, &QListWidget::currentRowChanged, stacked_widget_, &QStackedWidget::setCurrentIndex);
// Auto-select first item that actually has properties
if (first_usable_stream >= 0) {
track_list->setCurrentRow(first_usable_stream);
}
track_list->setFocus();
}
void FootagePropertiesDialog::accept()
{
// Perform sanity check on all pages
for (int i=0;i<stacked_widget_->count();i++) {
if (!static_cast<StreamProperties*>(stacked_widget_->widget(i))->SanityCheck()) {
// Switch to the failed panel in question
stacked_widget_->setCurrentIndex(i);
// Do nothing (it's up to the property panel itself to throw the error message)
return;
}
}
MultiUndoCommand* command = new MultiUndoCommand();
if (footage_->GetLabel() != footage_name_field_->text()) {
NodeRenameCommand *nrc = new NodeRenameCommand();
nrc->AddNode(footage_, footage_name_field_->text());
command->add_child(nrc);
}
for (int i=0; i<footage_->GetTotalStreamCount(); i++) {
Track::Reference reference = footage_->GetReferenceFromRealIndex(i);
bool new_stream_enabled = (track_list->item(i)->checkState() == Qt::Checked);
bool old_stream_enabled = new_stream_enabled;
switch (reference.type()) {
case Track::kVideo:
old_stream_enabled = footage_->GetVideoParams(reference.index()).enabled();
break;
case Track::kAudio:
old_stream_enabled = footage_->GetAudioParams(reference.index()).enabled();
break;
case Track::kSubtitle:
case Track::kNone:
case Track::kCount:
break;
}
if (old_stream_enabled != new_stream_enabled) {
command->add_child(new StreamEnableChangeCommand(footage_,
reference.type(),
reference.index(),
new_stream_enabled));
}
}
for (int i=0;i<stacked_widget_->count();i++) {
static_cast<StreamProperties*>(stacked_widget_->widget(i))->Accept(command);
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
QDialog::accept();
}
FootagePropertiesDialog::StreamEnableChangeCommand::StreamEnableChangeCommand(Footage *footage, Track::Type type, int index_in_type, bool enabled) :
footage_(footage),
type_(type),
index_(index_in_type),
new_enabled_(enabled)
{
}
Project *FootagePropertiesDialog::StreamEnableChangeCommand::GetRelevantProject() const
{
return footage_->project();
}
void FootagePropertiesDialog::StreamEnableChangeCommand::redo()
{
switch (type_) {
case Track::kVideo:
{
VideoParams vp = footage_->GetVideoParams(index_);
old_enabled_ = vp.enabled();
vp.set_enabled(new_enabled_);
footage_->SetVideoParams(vp, index_);
break;
}
case Track::kAudio:
{
AudioParams ap = footage_->GetAudioParams(index_);
old_enabled_ = ap.enabled();
ap.set_enabled(new_enabled_);
footage_->SetAudioParams(ap, index_);
break;
}
case Track::kSubtitle:
case Track::kNone:
case Track::kCount:
break;
}
}
void FootagePropertiesDialog::StreamEnableChangeCommand::undo()
{
switch (type_) {
case Track::kVideo:
{
VideoParams vp = footage_->GetVideoParams(index_);
vp.set_enabled(old_enabled_);
footage_->SetVideoParams(vp, index_);
break;
}
case Track::kAudio:
{
AudioParams ap = footage_->GetAudioParams(index_);
ap.set_enabled(old_enabled_);
footage_->SetAudioParams(ap, index_);
break;
}
case Track::kSubtitle:
case Track::kNone:
case Track::kCount:
break;
}
}
}
@@ -0,0 +1,121 @@
/***
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 MEDIAPROPERTIESDIALOG_H
#define MEDIAPROPERTIESDIALOG_H
#include <QCheckBox>
#include <QComboBox>
#include <QDialog>
#include <QDoubleSpinBox>
#include <QLineEdit>
#include <QListWidget>
#include <QStackedWidget>
#include "node/project/footage/footage.h"
#include "undo/undocommand.h"
namespace olive {
/**
* @brief The MediaPropertiesDialog class
*
* A dialog for setting properties on Media. This can be loaded from any part of the application provided it's given
* a valid Media object.
*/
class FootagePropertiesDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief MediaPropertiesDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow or Project panel.
*
* @param i
*
* Media object to set properties for.
*/
FootagePropertiesDialog(QWidget *parent, Footage* footage);
private:
class StreamEnableChangeCommand : public UndoCommand {
public:
StreamEnableChangeCommand(Footage *footage,
Track::Type type,
int index_in_type,
bool enabled);
virtual Project* GetRelevantProject() const override;
protected:
virtual void redo() override;
virtual void undo() override;
private:
Footage *footage_;
Track::Type type_;
int index_;
bool old_enabled_;
bool new_enabled_;
};
/**
* @brief Stack of widgets that changes based on whether the stream is a video or audio stream
*/
QStackedWidget* stacked_widget_;
/**
* @brief ComboBox for interlacing setting
*/
QComboBox* interlacing_box;
/**
* @brief Media name text field
*/
QLineEdit* footage_name_field_;
/**
* @brief Internal pointer to Media object (set in constructor)
*/
Footage* footage_;
/**
* @brief A list widget for listing the tracks in Media
*/
QListWidget* track_list;
/**
* @brief Frame rate to conform to
*/
QDoubleSpinBox* conform_fr;
private slots:
/**
* @brief Overridden accept function for saving the properties back to the Media class
*/
void accept();
};
}
#endif // MEDIAPROPERTIESDIALOG_H
@@ -0,0 +1,26 @@
# 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
dialog/footageproperties/streamproperties/streamproperties.h
dialog/footageproperties/streamproperties/streamproperties.cpp
dialog/footageproperties/streamproperties/audiostreamproperties.h
dialog/footageproperties/streamproperties/audiostreamproperties.cpp
dialog/footageproperties/streamproperties/videostreamproperties.h
dialog/footageproperties/streamproperties/videostreamproperties.cpp
PARENT_SCOPE
)
@@ -0,0 +1,37 @@
/***
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 "audiostreamproperties.h"
namespace olive {
AudioStreamProperties::AudioStreamProperties(Footage *footage, int audio_index) :
footage_(footage),
audio_index_(audio_index)
{
}
void AudioStreamProperties::Accept(MultiUndoCommand*)
{
Q_UNUSED(footage_)
Q_UNUSED(audio_index_)
}
}
@@ -0,0 +1,45 @@
/***
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 AUDIOSTREAMPROPERTIES_H
#define AUDIOSTREAMPROPERTIES_H
#include "node/project/footage/footage.h"
#include "streamproperties.h"
namespace olive {
class AudioStreamProperties : public StreamProperties
{
public:
AudioStreamProperties(Footage *footage, int audio_index);
virtual void Accept(MultiUndoCommand* parent) override;
private:
Footage *footage_;
int audio_index_;
};
}
#endif // AUDIOSTREAMPROPERTIES_H
@@ -0,0 +1,30 @@
/***
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 "streamproperties.h"
namespace olive {
StreamProperties::StreamProperties(QWidget *parent) :
QWidget(parent)
{
}
}
@@ -0,0 +1,44 @@
/***
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 STREAMPROPERTIES_H
#define STREAMPROPERTIES_H
#include <QWidget>
#include "common/define.h"
#include "undo/undocommand.h"
namespace olive {
class StreamProperties : public QWidget
{
public:
StreamProperties(QWidget* parent = nullptr);
virtual void Accept(MultiUndoCommand*){}
virtual bool SanityCheck(){return true;}
};
}
#endif // STREAMPROPERTIES_H
@@ -0,0 +1,271 @@
/***
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 "videostreamproperties.h"
#include <QGridLayout>
#include <QGroupBox>
#include <QInputDialog>
#include <QLabel>
#include <QMessageBox>
#include "common/ocioutils.h"
#include "core.h"
#include "undo/undostack.h"
namespace olive {
VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index) :
footage_(footage),
video_index_(video_index),
video_premultiply_alpha_(nullptr)
{
QGridLayout* video_layout = new QGridLayout(this);
video_layout->setMargin(0);
int row = 0;
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
VideoParams vp = footage_->GetVideoParams(video_index_);
pixel_aspect_combo_ = new PixelAspectRatioComboBox();
pixel_aspect_combo_->SetPixelAspectRatio(vp.pixel_aspect_ratio());
video_layout->addWidget(pixel_aspect_combo_, row, 1);
row++;
video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
video_interlace_combo_ = new InterlacedComboBox();
video_interlace_combo_->SetInterlaceMode(vp.interlacing());
video_layout->addWidget(video_interlace_combo_, row, 1);
row++;
video_layout->addWidget(new QLabel(tr("Color Space:")), row, 0);
video_color_space_ = new QComboBox();
OCIO::ConstConfigRcPtr config = footage_->project()->color_manager()->GetConfig();
int number_of_colorspaces = config->getNumColorSpaces();
video_color_space_->addItem(tr("Default (%1)").arg(footage_->project()->color_manager()->GetDefaultInputColorSpace()));
for (int i=0;i<number_of_colorspaces;i++) {
QString colorspace = config->getColorSpaceNameByIndex(i);
video_color_space_->addItem(colorspace);
}
video_color_space_->setCurrentText(vp.colorspace());
video_layout->addWidget(video_color_space_, row, 1);
if (vp.channel_count() == VideoParams::kRGBAChannelCount) {
row++;
video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha"));
video_premultiply_alpha_->setChecked(vp.premultiplied_alpha());
video_layout->addWidget(video_premultiply_alpha_, row, 0, 1, 2);
}
row++;
if (vp.video_type() == VideoParams::kVideoTypeImageSequence) {
QGroupBox* imgseq_group = new QGroupBox(tr("Image Sequence"));
QGridLayout* imgseq_layout = new QGridLayout(imgseq_group);
int imgseq_row = 0;
imgseq_layout->addWidget(new QLabel(tr("Start Index:")), imgseq_row, 0);
imgseq_start_time_ = new IntegerSlider();
imgseq_start_time_->SetMinimum(0);
imgseq_start_time_->SetValue(vp.start_time());
imgseq_layout->addWidget(imgseq_start_time_, imgseq_row, 1);
imgseq_row++;
imgseq_layout->addWidget(new QLabel(tr("End Index:")), imgseq_row, 0);
imgseq_end_time_ = new IntegerSlider();
imgseq_end_time_->SetMinimum(0);
imgseq_end_time_->SetValue(vp.start_time() + vp.duration() - 1);
imgseq_layout->addWidget(imgseq_end_time_, imgseq_row, 1);
imgseq_row++;
imgseq_layout->addWidget(new QLabel(tr("Frame Rate:")), imgseq_row, 0);
imgseq_frame_rate_ = new FrameRateComboBox();
imgseq_frame_rate_->SetFrameRate(vp.frame_rate());
imgseq_layout->addWidget(imgseq_frame_rate_, imgseq_row, 1);
video_layout->addWidget(imgseq_group, row, 0, 1, 2);
}
}
void VideoStreamProperties::Accept(MultiUndoCommand *parent)
{
QString set_colorspace;
if (video_color_space_->currentIndex() > 0) {
set_colorspace = video_color_space_->currentText();
}
VideoParams vp = footage_->GetVideoParams(video_index_);
if ((video_premultiply_alpha_ && video_premultiply_alpha_->isChecked() != vp.premultiplied_alpha())
|| set_colorspace != vp.colorspace()
|| static_cast<VideoParams::Interlacing>(video_interlace_combo_->currentIndex()) != vp.interlacing()
|| pixel_aspect_combo_->GetPixelAspectRatio() != vp.pixel_aspect_ratio()) {
parent->add_child(new VideoStreamChangeCommand(footage_,
video_index_,
video_premultiply_alpha_ ? video_premultiply_alpha_->isChecked() : vp.premultiplied_alpha(),
set_colorspace,
static_cast<VideoParams::Interlacing>(video_interlace_combo_->currentIndex()),
pixel_aspect_combo_->GetPixelAspectRatio()));
}
if (vp.video_type() == VideoParams::kVideoTypeImageSequence) {
int64_t new_dur = imgseq_end_time_->GetValue() - imgseq_start_time_->GetValue() + 1;
if (vp.start_time() != imgseq_start_time_->GetValue()
|| vp.duration() != new_dur
|| vp.frame_rate() != imgseq_frame_rate_->GetFrameRate()) {
parent->add_child(new ImageSequenceChangeCommand(footage_,
video_index_,
imgseq_start_time_->GetValue(),
new_dur,
imgseq_frame_rate_->GetFrameRate()));
}
}
}
bool VideoStreamProperties::SanityCheck()
{
if (footage_->GetVideoParams(video_index_).video_type() == VideoParams::kVideoTypeImageSequence) {
if (imgseq_start_time_->GetValue() >= imgseq_end_time_->GetValue()) {
QMessageBox::critical(this,
tr("Invalid Configuration"),
tr("Image sequence end index must be a value higher than the start index."),
QMessageBox::Ok);
return false;
}
}
return true;
}
VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(Footage *footage,
int video_index,
bool premultiplied,
QString colorspace,
VideoParams::Interlacing interlacing,
const rational &pixel_ar) :
footage_(footage),
video_index_(video_index),
new_premultiplied_(premultiplied),
new_colorspace_(colorspace),
new_interlacing_(interlacing),
new_pixel_ar_(pixel_ar)
{
}
Project *VideoStreamProperties::VideoStreamChangeCommand::GetRelevantProject() const
{
return footage_->project();
}
void VideoStreamProperties::VideoStreamChangeCommand::redo()
{
VideoParams vp = footage_->GetVideoParams(video_index_);
old_premultiplied_ = vp.premultiplied_alpha();
old_colorspace_ = vp.colorspace();
old_interlacing_ = vp.interlacing();
old_pixel_ar_ = vp.pixel_aspect_ratio();
vp.set_premultiplied_alpha(new_premultiplied_);
vp.set_colorspace(new_colorspace_);
vp.set_interlacing(new_interlacing_);
vp.set_pixel_aspect_ratio(new_pixel_ar_);
footage_->SetVideoParams(vp, video_index_);
}
void VideoStreamProperties::VideoStreamChangeCommand::undo()
{
VideoParams vp = footage_->GetVideoParams(video_index_);
vp.set_premultiplied_alpha(old_premultiplied_);
vp.set_colorspace(old_colorspace_);
vp.set_interlacing(old_interlacing_);
vp.set_pixel_aspect_ratio(old_pixel_ar_);
footage_->SetVideoParams(vp, video_index_);
}
VideoStreamProperties::ImageSequenceChangeCommand::ImageSequenceChangeCommand(Footage *footage, int video_index, int64_t start_index, int64_t duration, const rational &frame_rate) :
footage_(footage),
video_index_(video_index),
new_start_index_(start_index),
new_duration_(duration),
new_frame_rate_(frame_rate)
{
}
Project *VideoStreamProperties::ImageSequenceChangeCommand::GetRelevantProject() const
{
return footage_->project();
}
void VideoStreamProperties::ImageSequenceChangeCommand::redo()
{
VideoParams vp = footage_->GetVideoParams(video_index_);
old_start_index_ = vp.start_time();
vp.set_start_time(new_start_index_);
old_duration_ = vp.duration();
vp.set_duration(new_duration_);
old_frame_rate_ = vp.frame_rate();
vp.set_frame_rate(new_frame_rate_);
vp.set_time_base(new_frame_rate_.flipped());
footage_->SetVideoParams(vp, video_index_);
}
void VideoStreamProperties::ImageSequenceChangeCommand::undo()
{
VideoParams vp = footage_->GetVideoParams(video_index_);
vp.set_start_time(old_start_index_);
vp.set_duration(old_duration_);
vp.set_frame_rate(old_frame_rate_);
vp.set_time_base(old_frame_rate_.flipped());
footage_->SetVideoParams(vp, video_index_);
}
}
@@ -0,0 +1,148 @@
/***
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 VIDEOSTREAMPROPERTIES_H
#define VIDEOSTREAMPROPERTIES_H
#include <QCheckBox>
#include <QComboBox>
#include "node/project/footage/footage.h"
#include "streamproperties.h"
#include "widget/slider/integerslider.h"
#include "widget/standardcombos/standardcombos.h"
namespace olive {
class VideoStreamProperties : public StreamProperties
{
Q_OBJECT
public:
VideoStreamProperties(Footage *footage, int video_index);
virtual void Accept(MultiUndoCommand *parent) override;
virtual bool SanityCheck() override;
private:
Footage *footage_;
int video_index_;
/**
* @brief Setting for associated/premultiplied alpha
*/
QCheckBox* video_premultiply_alpha_;
/**
* @brief Setting for this media's color space
*/
QComboBox* video_color_space_;
/**
* @brief Setting for video interlacing
*/
InterlacedComboBox* video_interlace_combo_;
/**
* @brief Sets the start index for image sequences
*/
IntegerSlider* imgseq_start_time_;
/**
* @brief Sets the end index for image sequences
*/
IntegerSlider* imgseq_end_time_;
/**
* @brief Sets the frame rate for image sequences
*/
FrameRateComboBox* imgseq_frame_rate_;
/**
* @brief Sets the pixel aspect ratio of the stream
*/
PixelAspectRatioComboBox* pixel_aspect_combo_;
class VideoStreamChangeCommand : public UndoCommand {
public:
VideoStreamChangeCommand(Footage *footage,
int video_index,
bool premultiplied,
QString colorspace,
VideoParams::Interlacing interlacing,
const rational& pixel_ar);
virtual Project* GetRelevantProject() const override;
protected:
virtual void redo() override;
virtual void undo() override;
private:
Footage *footage_;
int video_index_;
bool new_premultiplied_;
QString new_colorspace_;
VideoParams::Interlacing new_interlacing_;
rational new_pixel_ar_;
bool old_premultiplied_;
QString old_colorspace_;
VideoParams::Interlacing old_interlacing_;
rational old_pixel_ar_;
};
class ImageSequenceChangeCommand : public UndoCommand {
public:
ImageSequenceChangeCommand(Footage *footage,
int video_index,
int64_t start_index,
int64_t duration,
const rational& frame_rate);
virtual Project* GetRelevantProject() const override;
protected:
virtual void redo() override;
virtual void undo() override;
private:
Footage *footage_;
int video_index_;
int64_t new_start_index_;
int64_t old_start_index_;
int64_t new_duration_;
int64_t old_duration_;
rational new_frame_rate_;
rational old_frame_rate_;
};
};
}
#endif // VIDEOSTREAMPROPERTIES_H
@@ -1,74 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "nodepropertiesdialog.h"
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include "core.h"
#include "widget/nodeview/nodeviewundo.h"
namespace olive {
NodePropertiesDialog::NodePropertiesDialog(Node *node, const rational &timebase, QWidget *parent) :
QDialog(parent),
node_(node)
{
setWindowTitle(tr("Node Properties"));
QVBoxLayout *layout = new QVBoxLayout(this);
QHBoxLayout *label_layout = new QHBoxLayout();
label_layout->setMargin(0);
layout->addLayout(label_layout);
label_layout->addWidget(new QLabel(tr("Name:")));
label_edit_ = new QLineEdit();
label_edit_->setText(node->GetLabel());
label_layout->addWidget(label_edit_);
NodeParamViewItem *item = new NodeParamViewItem(node);
item->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
item->SetTimebase(timebase);
item->setTitleBarWidget(new QWidget());
layout->addWidget(item);
layout->addStretch();
QDialogButtonBox *btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(btns, &QDialogButtonBox::accepted, this, &NodePropertiesDialog::accept);
connect(btns, &QDialogButtonBox::rejected, this, &NodePropertiesDialog::reject);
layout->addWidget(btns);
}
void NodePropertiesDialog::accept()
{
if (label_edit_->text() != node_->GetLabel()) {
NodeRenameCommand* rename_command = new NodeRenameCommand();
rename_command->AddNode(node_, label_edit_->text());
Core::instance()->undo_stack()->push(rename_command);
}
QDialog::accept();
}
}
@@ -19,11 +19,28 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg
// Set up video section
QGroupBox* video_group = new QGroupBox();
video_group->setTitle(tr("Video"));
QHBoxLayout* video_layout = new QHBoxLayout(video_group);
video_section_ = new VideoParamEdit();
video_section_->SetParameterMask(Sequence::kVideoParamEditMask);
connect(video_section_, &VideoParamEdit::Changed, this, &SequenceDialogParameterTab::UpdatePreviewResolutionLabel);
video_layout->addWidget(video_section_);
QGridLayout *video_layout = new QGridLayout(video_group);
video_layout->addWidget(new QLabel(tr("Width:")), row, 0);
width_slider_ = new IntegerSlider();
width_slider_->SetMinimum(0);
video_layout->addWidget(width_slider_, row, 1);
row++;
video_layout->addWidget(new QLabel(tr("Height:")), row, 0);
height_slider_ = new IntegerSlider();
height_slider_->SetMinimum(0);
video_layout->addWidget(height_slider_, row, 1);
row++;
video_layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0);
framerate_combo_ = new FrameRateComboBox();
video_layout->addWidget(framerate_combo_, row, 1);
row++;
video_layout->addWidget(new QLabel(tr("Pixel Aspect Ratio:")), row, 0);
pixelaspect_combo_ = new PixelAspectRatioComboBox();
video_layout->addWidget(pixelaspect_combo_, row, 1);
row++;
video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
interlacing_combo_ = new InterlacedComboBox();
video_layout->addWidget(interlacing_combo_, row, 1);
layout->addWidget(video_group);
row = 0;
@@ -65,7 +82,11 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg
// Set values based on input sequence
VideoParams vp = sequence->GetVideoParams();
AudioParams ap = sequence->GetAudioParams();
video_section_->SetVideoParams(vp);
width_slider_->SetValue(vp.width());
height_slider_->SetValue(vp.height());
framerate_combo_->SetFrameRate(vp.time_base().flipped());
pixelaspect_combo_->SetPixelAspectRatio(vp.pixel_aspect_ratio());
interlacing_combo_->SetInterlaceMode(vp.interlacing());
preview_resolution_field_->SetDivider(vp.divider());
preview_format_field_->SetPixelFormat(vp.format());
preview_autocache_field_->setChecked(sequence->GetVideoAutoCacheEnabled());
@@ -86,11 +107,11 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg
void SequenceDialogParameterTab::PresetChanged(const SequencePreset &preset)
{
video_section_->SetWidth(preset.width());
video_section_->SetHeight(preset.height());
video_section_->SetFrameRate(preset.frame_rate());
video_section_->SetPixelAspectRatio(preset.pixel_aspect());
video_section_->SetInterlaceMode(preset.interlacing());
width_slider_->SetValue(preset.width());
height_slider_->SetValue(preset.height());
framerate_combo_->SetFrameRate(preset.frame_rate());
pixelaspect_combo_->SetPixelAspectRatio(preset.pixel_aspect());
interlacing_combo_->SetInterlaceMode(preset.interlacing());
audio_sample_rate_field_->SetSampleRate(preset.sample_rate());
audio_channels_field_->SetChannelLayout(preset.channel_layout());
preview_resolution_field_->SetDivider(preset.preview_divider());
@@ -115,8 +136,8 @@ void SequenceDialogParameterTab::SavePresetClicked()
void SequenceDialogParameterTab::UpdatePreviewResolutionLabel()
{
VideoParams test_param(video_section_->GetWidth(),
video_section_->GetHeight(),
VideoParams test_param(GetSelectedVideoWidth(),
GetSelectedVideoHeight(),
VideoParams::kFormatInvalid,
VideoParams::kInternalChannelCount,
rational(1),
@@ -1,6 +1,7 @@
#ifndef SEQUENCEDIALOGPARAMETERTAB_H
#define SEQUENCEDIALOGPARAMETERTAB_H
#include <QCheckBox>
#include <QComboBox>
#include <QList>
#include <QSpinBox>
@@ -9,7 +10,6 @@
#include "sequencepreset.h"
#include "widget/slider/integerslider.h"
#include "widget/standardcombos/standardcombos.h"
#include "widget/videoparamedit/videoparamedit.h"
namespace olive {
@@ -21,27 +21,27 @@ public:
int GetSelectedVideoWidth() const
{
return video_section_->GetWidth();
return width_slider_->GetValue();
}
int GetSelectedVideoHeight() const
{
return video_section_->GetHeight();
return height_slider_->GetValue();
}
rational GetSelectedVideoFrameRate() const
{
return video_section_->GetFrameRate();
return framerate_combo_->GetFrameRate();
}
rational GetSelectedVideoPixelAspect() const
{
return video_section_->GetPixelAspectRatio();
return pixelaspect_combo_->GetPixelAspectRatio();
}
VideoParams::Interlacing GetSelectedVideoInterlacingMode() const
{
return video_section_->GetInterlaceMode();
return interlacing_combo_->GetInterlaceMode();
}
int GetSelectedAudioSampleRate() const
@@ -76,7 +76,15 @@ signals:
void SaveParametersAsPreset(const SequencePreset& preset);
private:
VideoParamEdit* video_section_;
IntegerSlider *width_slider_;
IntegerSlider *height_slider_;
FrameRateComboBox *framerate_combo_;
PixelAspectRatioComboBox *pixelaspect_combo_;
InterlacedComboBox *interlacing_combo_;
SampleRateComboBox* audio_sample_rate_field_;
+17 -1
View File
@@ -71,6 +71,22 @@ int main(int argc, char *argv[])
// Parse command line arguments
//
QVector<QString> args;
#if defined(_WIN32) && defined(UNICODE)
int wargc;
LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);
args.resize(wargc);
for (int i=0; i<wargc; i++) {
args[i] = QString::fromWCharArray(wargv[i]);
}
LocalFree(wargv);
#else
args.resize(argc);
for (int i=0; i<argc; i++) {
args[i] = QString::fromLocal8Bit(argv[i]);
}
#endif
olive::Core::CoreParams startup_params;
CommandLineParser parser;
@@ -130,7 +146,7 @@ int main(int argc, char *argv[])
// Hidden crash option for debugging the crash handling
auto crash_option = parser.AddOption({QStringLiteral("-crash")}, QString(), true, QString(), true);
parser.Process(argc, argv);
parser.Process(args);
if (help_option->IsSet()) {
// Show help
+1
View File
@@ -20,6 +20,7 @@ add_subdirectory(color)
add_subdirectory(distort)
add_subdirectory(filter)
add_subdirectory(generator)
add_subdirectory(group)
add_subdirectory(input)
add_subdirectory(math)
add_subdirectory(output)
+2
View File
@@ -47,6 +47,8 @@ Block::Block() :
IgnoreHashingFrom(kLengthInput);
AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
SetFlags(kDontShowInParamView);
}
QVector<Node::CategoryID> Block::Category() const
+1 -3
View File
@@ -233,13 +233,11 @@ void CropDistortNode::GizmoMove(const QPointF &p, const rational &time, const Qt
}
}
void CropDistortNode::GizmoRelease()
void CropDistortNode::GizmoRelease(MultiUndoCommand *command)
{
MultiUndoCommand *command = new MultiUndoCommand();
for (NodeInputDragger& i : gizmo_dragger_) {
i.End(command);
}
Core::instance()->undo_stack()->push(command);
gizmo_dragger_.clear();
gizmo_start_.clear();
+1 -1
View File
@@ -76,7 +76,7 @@ public:
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
virtual void GizmoRelease() override;
virtual void GizmoRelease(MultiUndoCommand *command) override;
static const QString kTextureInput;
static const QString kLeftInput;
@@ -301,13 +301,11 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time, con
}
}
void TransformDistortNode::GizmoRelease()
void TransformDistortNode::GizmoRelease(MultiUndoCommand *command)
{
MultiUndoCommand *command = new MultiUndoCommand();
for (NodeInputDragger& i : gizmo_dragger_) {
i.End(command);
}
Core::instance()->undo_stack()->push(command);
gizmo_dragger_.clear();
gizmo_start_.clear();
@@ -78,7 +78,7 @@ public:
virtual bool GizmoPress(const NodeValueRow &row, const NodeGlobals &globals, const QPointF &p) override;
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
virtual void GizmoRelease() override;
virtual void GizmoRelease(MultiUndoCommand *command) override;
enum AutoScaleType {
kAutoScaleNone,
+11
View File
@@ -54,6 +54,7 @@
namespace olive {
QList<Node*> NodeFactory::library_;
QVector<int> NodeFactory::hidden_;
void NodeFactory::Initialize()
{
@@ -65,6 +66,9 @@ void NodeFactory::Initialize()
library_.append(created_node);
}
hidden_.append(kTextGeneratorLegacy);
hidden_.append(kGroupNode);
}
void NodeFactory::Destroy()
@@ -86,6 +90,11 @@ Menu *NodeFactory::CreateMenu(QWidget* parent, bool create_none_item, Node::Cate
continue;
}
if (hidden_.contains(i)) {
// Skip this node
continue;
}
// Make sure nodes are up-to-date with the current translation
n->Retranslate();
@@ -241,6 +250,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new SubtitleBlock();
case kShapeGenerator:
return new ShapeNode();
case kGroupNode:
return new NodeGroup();
case kInternalNodeCount:
break;
+3
View File
@@ -61,6 +61,7 @@ public:
kTimeRemapNode,
kSubtitleBlock,
kShapeGenerator,
kGroupNode,
// Count value
kInternalNodeCount
@@ -87,6 +88,8 @@ public:
private:
static QList<Node*> library_;
static QVector<int> hidden_;
};
}
+1 -3
View File
@@ -297,13 +297,11 @@ void ShapeNodeBase::GizmoMove(const QPointF &p, const rational &time, const Qt::
}
}
void ShapeNodeBase::GizmoRelease()
void ShapeNodeBase::GizmoRelease(MultiUndoCommand *command)
{
MultiUndoCommand *command = new MultiUndoCommand();
for (NodeInputDragger& i : gizmo_dragger_) {
i.End(command);
}
Core::instance()->undo_stack()->push(command);
gizmo_dragger_.clear();
}
+1 -1
View File
@@ -49,7 +49,7 @@ public:
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
virtual void GizmoRelease() override;
virtual void GizmoRelease(MultiUndoCommand *command) override;
private:
static QVector2D GenerateGizmoAnchor(const QVector2D &pos, const QVector2D &size, int drag, QVector2D *pt);
+7 -1
View File
@@ -117,7 +117,13 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
QImage img(frame->width(), frame->height(), QImage::Format_Grayscale8);
img.fill(Qt::transparent);
// 72 DPI in DPM (72 / 2.54 * 100)
const int dpm = 2835;
img.setDotsPerMeterX(dpm);
img.setDotsPerMeterY(dpm);
QTextDocument text_doc;
text_doc.documentLayout()->setPaintDevice(&img);
// Set default font
QFont default_font;
@@ -170,7 +176,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
// Transplant alpha channel to frame
Color rgba = job.GetValue(kColorInput).data().value<Color>();
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
__m128 sse_color = _mm_load_ps(rgba.data());
__m128 sse_color = _mm_loadu_ps(rgba.data());
#endif
float *frame_dst = reinterpret_cast<float*>(frame->data());
+18 -40
View File
@@ -44,26 +44,12 @@ void NodeGraph::Clear()
}
}
qreal NodeGraph::GetNodeContextHeight(Node *context)
{
const PositionMap &map = position_map_.value(context);
qreal top = 0, bottom = 0;
foreach (const QPointF &pt, map) {
top = qMin(pt.y(), top);
bottom = qMax(pt.y(), bottom);
}
return bottom - top;
}
int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node) const
int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node, bool except_itself) const
{
int count = 0;
for (auto it=position_map_.cbegin(); it!=position_map_.cend(); it++) {
if (it.value().contains(node)) {
foreach (Node *ctx, node_children_) {
if (ctx->ContextContainsNode(node) && (!except_itself || ctx != node)) {
count++;
}
}
@@ -71,18 +57,6 @@ int NodeGraph::GetNumberOfContextsNodeIsIn(Node *node) const
return count;
}
bool NodeGraph::NodeOutputsToContext(Node *node) const
{
for (auto it=position_map_.cbegin(); it!=position_map_.cend(); it++) {
const PositionMap &pm = it.value();
if (pm.contains(node) && node->OutputsTo(it.key(), true)) {
return true;
}
}
return false;
}
void NodeGraph::childEvent(QChildEvent *event)
{
super::childEvent(event);
@@ -100,6 +74,12 @@ void NodeGraph::childEvent(QChildEvent *event)
connect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged, Qt::DirectConnection);
connect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged, Qt::DirectConnection);
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
connect(group, &NodeGroup::InputPassthroughAdded, this, &NodeGraph::GroupAddedInputPassthrough, Qt::DirectConnection);
connect(group, &NodeGroup::InputPassthroughRemoved, this, &NodeGraph::GroupRemovedInputPassthrough, Qt::DirectConnection);
connect(group, &NodeGroup::OutputPassthroughChanged, this, &NodeGraph::GroupChangedOutputPassthrough, Qt::DirectConnection);
}
emit NodeAdded(node);
emit node->AddedToGraph(this);
@@ -113,21 +93,19 @@ void NodeGraph::childEvent(QChildEvent *event)
disconnect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged);
disconnect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged);
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
disconnect(group, &NodeGroup::InputPassthroughAdded, this, &NodeGraph::GroupAddedInputPassthrough);
disconnect(group, &NodeGroup::InputPassthroughRemoved, this, &NodeGraph::GroupRemovedInputPassthrough);
disconnect(group, &NodeGroup::OutputPassthroughChanged, this, &NodeGraph::GroupChangedOutputPassthrough);
}
emit NodeRemoved(node);
emit node->RemovedFromGraph(this);
for (auto it=position_map_.begin(); it!=position_map_.end(); it++) {
PositionMap &map = it.value();
for (auto jt=map.begin(); jt!=map.end(); ) {
if (jt.key() == node) {
jt = map.erase(jt);
emit NodePositionRemoved(node, it.key());
} else {
jt++;
}
}
// Remove from any contexts
foreach (Node *context, node_children_) {
context->RemoveNodeFromContext(node);
}
}
}
}
+6 -54
View File
@@ -21,6 +21,7 @@
#ifndef NODEGRAPH_H
#define NODEGRAPH_H
#include "node/group/group.h"
#include "node/node.h"
namespace olive {
@@ -63,54 +64,7 @@ public:
return default_nodes_;
}
bool NodeMapContainsNode(Node* node, Node* context) const
{
return position_map_.value(context).contains(node);
}
QPointF GetNodePosition(Node* node, Node* context)
{
return position_map_.value(context).value(node);
}
void SetNodePosition(Node* node, Node* context, const QPointF& pos)
{
position_map_[context].insert(node, pos);
emit NodePositionAdded(node, context, pos);
}
void RemoveNodePosition(Node* node, Node* context)
{
PositionMap& map = position_map_[context];
map.remove(node);
if (map.isEmpty()) {
position_map_.remove(context);
}
emit NodePositionRemoved(node, context);
}
bool ContextContainsNode(Node *node, Node *context)
{
return position_map_[context].contains(node);
}
qreal GetNodeContextHeight(Node *context);
using PositionMap = QHash<Node*, QPointF>;
const PositionMap &GetNodesForContext(Node *context)
{
return position_map_[context];
}
const QMap<Node *, PositionMap> &GetPositionMap() const
{
return position_map_;
}
int GetNumberOfContextsNodeIsIn(Node *node) const;
bool NodeOutputsToContext(Node *node) const;
int GetNumberOfContextsNodeIsIn(Node *node, bool except_itself = false) const;
signals:
/**
@@ -131,9 +85,11 @@ signals:
void InputValueHintChanged(const NodeInput& input);
void NodePositionAdded(Node *node, Node *relative, const QPointF &position);
void GroupAddedInputPassthrough(NodeGroup *group, const NodeInput &input);
void NodePositionRemoved(Node *node, Node *relative);
void GroupRemovedInputPassthrough(NodeGroup *group, const NodeInput &input);
void GroupChangedOutputPassthrough(NodeGroup *group, Node *output);
protected:
void AddDefaultNode(Node* n)
@@ -148,10 +104,6 @@ private:
QVector<Node*> default_nodes_;
QMap<Node *, PositionMap> position_map_;
PositionMap root_position_map_;
};
}
@@ -16,7 +16,7 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/videoparamedit/videoparamedit.cpp
widget/videoparamedit/videoparamedit.h
node/group/group.cpp
node/group/group.h
PARENT_SCOPE
)
+215
View File
@@ -0,0 +1,215 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "group.h"
#include "node/graph.h"
namespace olive {
#define super Node
NodeGroup::NodeGroup() :
output_passthrough_(nullptr)
{
}
QString NodeGroup::Name() const
{
return tr("Group");
}
QString NodeGroup::id() const
{
return QStringLiteral("org.olivevideoeditor.Olive.group");
}
QVector<Node::CategoryID> NodeGroup::Category() const
{
return {kCategoryGeneral};
}
QString NodeGroup::Description() const
{
return tr("A group of nodes that is represented as a single node.");
}
void NodeGroup::Retranslate()
{
for (auto it=GetContextPositions().cbegin(); it!=GetContextPositions().cend(); it++) {
it.key()->Retranslate();
}
}
void NodeGroup::AddInputPassthrough(const NodeInput &input)
{
Q_ASSERT(ContextContainsNode(input.node()));
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
// Already passing this input through
return;
}
}
// Add input
QString id = GetGroupInputIDFromInput(input);
AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags());
input_passthroughs_.insert(id, input);
emit InputPassthroughAdded(this, input);
}
void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
RemoveInput(it.key());
input_passthroughs_.erase(it);
emit InputPassthroughRemoved(this, it.value());
break;
}
}
}
void NodeGroup::SetOutputPassthrough(Node *node)
{
Q_ASSERT(!node || ContextContainsNode(node));
output_passthrough_ = node;
emit OutputPassthroughChanged(this, output_passthrough_);
}
QString NodeGroup::GetGroupInputIDFromInput(const NodeInput &input)
{
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(input.node()->GetUUID().toByteArray());
hash.addData(input.input().toUtf8());
hash.addData((const char*) &input.element(), sizeof(input.element()));
return QString::fromLatin1(hash.result().toHex());
}
bool NodeGroup::ContainsInputPassthrough(const NodeInput &input) const
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
return true;
}
}
return false;
}
QString NodeGroup::GetInputName(const QString &id) const
{
return input_passthroughs_.value(id).name();
}
bool NodeGroup::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled)
{
if (reader->name() == QStringLiteral("inputpassthroughs")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("inputpassthrough")) {
XMLNodeData::GroupLink link;
link.group = this;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
link.input_node = reader->readElementText().toULongLong();
} else if (reader->name() == QStringLiteral("input")) {
link.input_id = reader->readElementText();
} else if (reader->name() == QStringLiteral("element")) {
link.input_element = reader->readElementText().toInt();
} else {
reader->skipCurrentElement();
}
}
xml_node_data.group_input_links.append(link);
} else {
reader->skipCurrentElement();
}
}
return true;
} else if (reader->name() == QStringLiteral("outputpassthrough")) {
xml_node_data.group_output_links.insert(this, reader->readElementText().toULongLong());
return true;
} else {
return super::LoadCustom(reader, xml_node_data, version, cancelled);
}
}
void NodeGroup::SaveCustom(QXmlStreamWriter *writer) const
{
super::SaveCustom(writer);
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
foreach (const NodeInput &ip, input_passthroughs_) {
writer->writeStartElement(QStringLiteral("inputpassthrough"));
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.node())));
writer->writeTextElement(QStringLiteral("input"), ip.input());
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.element()));
writer->writeEndElement(); // input
}
writer->writeEndElement(); // inputpassthroughs
writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast<quintptr>(output_passthrough_)));
}
void NodeGroupAddInputPassthrough::redo()
{
if (!group_->ContainsInputPassthrough(input_)) {
group_->AddInputPassthrough(input_);
actually_added_ = true;
} else {
actually_added_ = false;
}
}
void NodeGroupAddInputPassthrough::undo()
{
if (actually_added_) {
group_->RemoveInputPassthrough(input_);
}
}
void NodeGroupSetOutputPassthrough::redo()
{
old_output_ = group_->GetOutputPassthrough();
group_->SetOutputPassthrough(new_output_);
}
void NodeGroupSetOutputPassthrough::undo()
{
group_->SetOutputPassthrough(old_output_);
}
}
+141
View File
@@ -0,0 +1,141 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 NODEGROUP_H
#define NODEGROUP_H
#include "node/node.h"
namespace olive {
class NodeGroup : public Node
{
Q_OBJECT
public:
NodeGroup();
NODE_DEFAULT_DESTRUCTOR(NodeGroup)
NODE_COPY_FUNCTION(NodeGroup)
virtual QString Name() const override;
virtual QString id() const override;
virtual QVector<CategoryID> Category() const override;
virtual QString Description() const override;
virtual void Retranslate() override;
void AddInputPassthrough(const NodeInput &input);
void RemoveInputPassthrough(const NodeInput &input);
Node *GetOutputPassthrough() const
{
return output_passthrough_;
}
void SetOutputPassthrough(Node *node);
static QString GetGroupInputIDFromInput(const NodeInput &input);
const QHash<QString, NodeInput> &GetInputPassthroughs() const
{
return input_passthroughs_;
}
bool ContainsInputPassthrough(const NodeInput &input) const;
virtual QString GetInputName(const QString& id) const override;
signals:
void InputPassthroughAdded(NodeGroup *group, const NodeInput &input);
void InputPassthroughRemoved(NodeGroup *group, const NodeInput &input);
void OutputPassthroughChanged(NodeGroup *group, Node *output);
protected:
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled) override;
virtual void SaveCustom(QXmlStreamWriter* writer) const override;
private:
QHash<QString, NodeInput> input_passthroughs_;
Node *output_passthrough_;
};
class NodeGroupAddInputPassthrough : public UndoCommand
{
public:
NodeGroupAddInputPassthrough(NodeGroup *group, const NodeInput &input) :
group_(group),
input_(input),
actually_added_(false)
{}
virtual Project * GetRelevantProject() const override
{
return group_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
NodeGroup *group_;
NodeInput input_;
bool actually_added_;
};
class NodeGroupSetOutputPassthrough : public UndoCommand
{
public:
NodeGroupSetOutputPassthrough(NodeGroup *group, Node *output) :
group_(group),
new_output_(output)
{}
virtual Project * GetRelevantProject() const override
{
return group_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
NodeGroup *group_;
Node *new_output_;
Node *old_output_;
};
}
#endif // NODEGROUP_H
+1
View File
@@ -43,6 +43,7 @@ public:
* @brief Methods of interpolation to use with this keyframe
*/
enum Type {
kInvalid = -1,
kLinear,
kHold,
kBezier
+2
View File
@@ -32,6 +32,8 @@ MergeNode::MergeNode()
AddInput(kBaseIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kBlendIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
SetFlags(kDontShowInParamView);
}
Node *MergeNode::copy() const
@@ -74,6 +74,8 @@ void TrigonometryNode::Retranslate()
SetComboBoxStrings(kMethodIn, strings);
SetInputName(kMethodIn, tr("Method"));
SetInputName(kXIn, tr("Value"));
}
void TrigonometryNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
+130 -130
View File
@@ -47,8 +47,10 @@ Node::Node() :
override_color_(-1),
folder_(nullptr),
operation_stack_(0),
cache_result_(false)
cache_result_(false),
flags_(kNone)
{
uuid_ = QUuid::createUuid();
}
Node::~Node()
@@ -88,6 +90,8 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this);
} else if (reader->name() == QStringLiteral("label")) {
SetLabel(reader->readElementText());
} else if (reader->name() == QStringLiteral("uuid")) {
SetUUID(QUuid::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("color")) {
override_color_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("links")) {
@@ -169,6 +173,7 @@ void Node::Save(QXmlStreamWriter *writer) const
{
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeTextElement(QStringLiteral("uuid"), uuid_.toString());
writer->writeTextElement(QStringLiteral("label"), GetLabel());
writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_));
@@ -219,7 +224,16 @@ void Node::Save(QXmlStreamWriter *writer) const
Project* Node::project() const
{
return dynamic_cast<Project*>(parent());
QObject *t = this->parent();
while (t) {
if (Project *p = dynamic_cast<Project*>(t)) {
return p;
}
t = t->parent();
}
return nullptr;
}
QString Node::ShortName() const
@@ -243,6 +257,40 @@ QIcon Node::icon() const
return icon::New;
}
bool Node::SetNodePositionInContext(Node *node, const QPointF &pos)
{
Position p = context_positions_.value(node);
p.position = pos;
return SetNodePositionInContext(node, p);
}
bool Node::SetNodePositionInContext(Node *node, const Position &pos)
{
bool added = !ContextContainsNode(node);
context_positions_.insert(node, pos);
if (added) {
emit NodeAddedToContext(node);
}
emit NodePositionInContextChanged(node, pos.position);
return added;
}
bool Node::RemoveNodeFromContext(Node *node)
{
if (ContextContainsNode(node)) {
context_positions_.remove(node);
emit NodeRemovedFromContext(node);
return true;
} else {
return false;
}
}
Color Node::color() const
{
int c;
@@ -429,6 +477,11 @@ void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const
writer->writeEndElement(); // subelements
}
bool Node::IsInputHidden(const QString &input) const
{
return (GetInputFlags(input) & kInputFlagHidden);
}
bool Node::IsInputConnectable(const QString &input) const
{
return !(GetInputFlags(input) & kInputFlagNotConnectable);
@@ -1050,7 +1103,7 @@ NodeInputImmediate *Node::GetImmediate(const QString &input, int element) const
return nullptr;
}
Node::InputFlags Node::GetInputFlags(const QString &input) const
InputFlags Node::GetInputFlags(const QString &input) const
{
const Input* i = GetInternalInputData(input);
@@ -1196,13 +1249,10 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& cre
command->add_child(new NodeSetValueHintCommand(copied_input, node->GetValueHintForInput(input.input(), input.element())));
}
if (node->parent()->GetPositionMap().contains(node)) {
// This node is a context, copy the context
const NodeGraph::PositionMap &map = node->parent()->GetPositionMap().value(node);
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add either the copy (if it exists) or the original node to the context
command->add_child(new NodeSetPositionCommand(created.value(it.key(), it.key()), copy, it.value(), false));
}
const PositionMap &map = node->GetContextPositions();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add either the copy (if it exists) or the original node to the context
command->add_child(new NodeSetPositionCommand(created.value(it.key(), it.key()), copy, it.value()));
}
return copy;
@@ -1229,13 +1279,10 @@ Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command)
command->add_child(new NodeCopyInputsCommand(node, copy, true));
if (node->parent()->GetPositionMap().contains(node)) {
// This node is a context, copy the context
const NodeGraph::PositionMap &map = node->parent()->GetPositionMap().value(node);
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add to the context
command->add_child(new NodeSetPositionCommand(it.key(), copy, it.value(), false));
}
const PositionMap &map = node->GetContextPositions();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add to the context
command->add_child(new NodeSetPositionCommand(it.key(), copy, it.value()));
}
}
@@ -1310,7 +1357,7 @@ void Node::HashAddNodeSignature(QCryptographicHash &hash) const
hash.addData(id().toUtf8());
}
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, Node::InputFlags flags, int index)
void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, InputFlags flags, int index)
{
if (id.isEmpty()) {
qWarning() << "Rejected adding input with an empty ID on node" << this->id();
@@ -1457,7 +1504,7 @@ void Node::GizmoMove(const QPointF &, const rational&, const Qt::KeyboardModifie
{
}
void Node::GizmoRelease()
void Node::GizmoRelease(MultiUndoCommand *)
{
}
@@ -1564,7 +1611,9 @@ void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input,
dst->SetSplitStandardValue(input, src->GetSplitStandardValue(input, src_element), dst_element);
// Copy keyframes
dst->GetImmediate(input, dst_element)->delete_all_keyframes();
if (NodeInputImmediate *immediate = dst->GetImmediate(input, dst_element)) {
immediate->delete_all_keyframes();
}
foreach (const NodeKeyframeTrack& track, src->GetImmediate(input, src_element)->keyframe_tracks()) {
foreach (NodeKeyframe* key, track) {
key->copy(dst_element, dst);
@@ -2198,7 +2247,6 @@ void Node::childEvent(QChildEvent *event)
GetImmediate(key->input(), key->element())->insert_keyframe(key);
connect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange);
connect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged);
connect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange);
connect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged);
connect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange);
@@ -2210,15 +2258,14 @@ void Node::childEvent(QChildEvent *event)
TimeRange time_affected = GetRangeAffectedByKeyframe(key);
disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange);
disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged);
disconnect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange);
disconnect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged);
disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange);
disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &Node::InvalidateFromKeyframeBezierOutChange);
GetImmediate(key->input(), key->element())->remove_keyframe(key);
emit KeyframeRemoved(key);
GetImmediate(key->input(), key->element())->remove_keyframe(key);
ParameterValueChanged(i, time_affected);
}
}
@@ -2281,12 +2328,16 @@ void Node::InvalidateFromKeyframeTimeChange()
foreach (const TimeRange& r, invalidate_range) {
ParameterValueChanged(key->key_track_ref().input(), r);
}
emit KeyframeTimeChanged(key);
}
void Node::InvalidateFromKeyframeValueChange()
{
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
ParameterValueChanged(key->key_track_ref().input(), GetRangeAffectedByKeyframe(key));
emit KeyframeValueChanged(key);
}
void Node::InvalidateFromKeyframeTypeChanged()
@@ -2301,6 +2352,8 @@ void Node::InvalidateFromKeyframeTypeChanged()
// Invalidate entire range
ParameterValueChanged(key->key_track_ref().input(), GetRangeAroundIndex(key->input(), track.indexOf(key), key->track(), key->element()));
emit KeyframeTypeChanged(key);
}
Project *Node::ArrayInsertCommand::GetRelevantProject() const
@@ -2318,119 +2371,40 @@ Project *Node::ArrayResizeCommand::GetRelevantProject() const
return node_->project();
}
void NodeSetPositionAndShiftSurroundingsCommand::redo()
{
if (commands_.isEmpty()) {
// Move first node
NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, relative_, position_, move_dependencies_);
set_pos_command->redo_now();
commands_.append(set_pos_command);
// Get bounding rect
qreal bounding_rect_sz = 1.0;
qreal bounding_rect_half_sz = bounding_rect_sz * 0.5;
QRectF bounding_rect(position_.x() - bounding_rect_half_sz, position_.y() - bounding_rect_half_sz, bounding_rect_sz, bounding_rect_sz);
// Start moving other nodes
foreach (Node* surrounding, node_->parent()->nodes()) {
if (surrounding != node_) {
QPointF surrounding_position = node_->parent()->GetNodePosition(surrounding, relative_);
if (bounding_rect.contains(surrounding_position)) {
QPointF new_pos = surrounding_position;
qreal move_rate = 0.50;
if (surrounding_position.y() < position_.y()) {
move_rate = -move_rate;
}
new_pos.setY(new_pos.y() + move_rate);
auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, relative_, new_pos, true);
sur_command->redo();
commands_.append(sur_command);
}
}
}
} else {
for (int i=0; i<commands_.size(); i++) {
commands_.at(i)->redo_now();
}
}
}
void NodeSetPositionCommand::redo()
{
graph_ = node_->parent();
if (!(added_ = !graph_->NodeMapContainsNode(node_, relevant_))) {
old_pos_ = graph_->GetNodePosition(node_, relevant_);
added_ = !context_->ContextContainsNode(node_);
if (!added_) {
old_pos_ = context_->GetNodePositionDataInContext(node_);
}
graph_->SetNodePosition(node_, relevant_, pos_);
context_->SetNodePositionInContext(node_, pos_);
}
void NodeSetPositionCommand::undo()
{
if (added_) {
graph_->RemoveNodePosition(node_, relevant_);
context_->RemoveNodeFromContext(node_);
} else {
graph_->SetNodePosition(node_, relevant_, old_pos_);
context_->SetNodePositionInContext(node_, old_pos_);
}
}
void NodeSetPositionAsChildCommand::redo()
{
if (!sub_command_) {
// Calculate position of node
NodeGraph *graph = parent_->parent();
QPointF pos = graph->GetNodePosition(parent_, relative_);
// This is a dependency, so we'll place it one X before
pos.setX(pos.x() - 1);
// The Y will be calculated using the index and child count
pos.setY(pos.y() - (double(child_count_)*0.5) + this_index_ + 0.5);
sub_command_ = new MultiUndoCommand();
if (shift_surroundings_) {
sub_command_->add_child(new NodeSetPositionAndShiftSurroundingsCommand(node_, relative_, pos, true));
} else {
sub_command_->add_child(new NodeSetPositionCommand(node_, relative_, pos, true));
}
}
sub_command_->redo_now();
}
void NodeSetPositionToOffsetOfAnotherNodeCommand::redo()
{
NodeGraph *graph = node_->parent();
old_pos_ = graph->GetNodePosition(node_, relative_);
graph->SetNodePosition(node_, relative_, graph->GetNodePosition(other_node_, relative_) + offset_);
}
void NodeSetPositionToOffsetOfAnotherNodeCommand::undo()
{
NodeGraph *graph = node_->parent();
graph->SetNodePosition(node_, relative_, old_pos_);
}
void NodeRemovePositionFromContextCommand::redo()
{
NodeGraph *graph = node_->parent();
contained_ = graph->ContextContainsNode(node_, context_);
contained_ = context_->ContextContainsNode(node_);
if (contained_) {
old_pos_ = graph->GetNodePosition(node_, context_);
graph->RemoveNodePosition(node_, context_);
old_pos_ = context_->GetNodePositionDataInContext(node_);
context_->RemoveNodeFromContext(node_);
}
}
void NodeRemovePositionFromContextCommand::undo()
{
if (contained_) {
NodeGraph *graph = node_->parent();
graph->SetNodePosition(node_, context_, old_pos_);
context_->SetNodePositionInContext(node_, old_pos_);
}
}
@@ -2438,28 +2412,21 @@ void NodeRemovePositionFromAllContextsCommand::redo()
{
NodeGraph *graph = node_->parent();
if (points_.empty()) {
// No points yet, let's see what points we should remove
auto map = graph->GetPositionMap();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
if (it.value().contains(node_)) {
points_.insert({it.key(), it.value().value(node_)});
}
foreach (Node* context, graph->nodes()) {
if (context->ContextContainsNode(node_)) {
contexts_.insert({context, context->GetNodePositionInContext(node_)});
context->RemoveNodeFromContext(node_);
}
}
for (auto it=points_.cbegin(); it!=points_.cend(); it++) {
graph->RemoveNodePosition(node_, it->first);
}
}
void NodeRemovePositionFromAllContextsCommand::undo()
{
NodeGraph *graph = node_->parent();
for (auto it=points_.crbegin(); it!=points_.crend(); it++) {
graph->SetNodePosition(node_, it->first, it->second);
for (auto it = contexts_.crbegin(); it != contexts_.crend(); it++) {
it->first->SetNodePositionInContext(node_, it->second);
}
contexts_.clear();
}
void Node::ValueHint::Hash(QCryptographicHash &hash) const
@@ -2508,4 +2475,37 @@ void Node::ValueHint::Save(QXmlStreamWriter *writer) const
writer->writeTextElement(QStringLiteral("tag"), tag_);
}
void NodeSetPositionAndDependenciesRecursivelyCommand::prepare()
{
move_recursively(node_, pos_.position - context_->GetNodePositionDataInContext(node_).position);
}
void NodeSetPositionAndDependenciesRecursivelyCommand::redo()
{
for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) {
(*it)->redo_now();
}
}
void NodeSetPositionAndDependenciesRecursivelyCommand::undo()
{
for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) {
(*it)->undo_now();
}
}
void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(Node *node, const QPointF &diff)
{
Node::Position pos = context_->GetNodePositionDataInContext(node);
pos += diff;
commands_.append(new NodeSetPositionCommand(node_, context_, pos));
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
Node *output = it->second;
if (context_->ContextContainsNode(output)) {
move_recursively(output, diff);
}
}
}
}
+133 -170
View File
@@ -27,6 +27,7 @@
#include <QObject>
#include <QPainter>
#include <QPointF>
#include <QUuid>
#include <QXmlStreamWriter>
#include "codec/frame.h"
@@ -93,6 +94,11 @@ public:
kCategoryCount
};
enum Flag {
kNone = 0,
kDontShowInParamView = 0x1
};
Node();
virtual ~Node() override;
@@ -112,6 +118,14 @@ public:
Project* project() const;
const QUuid &GetUUID() const {return uuid_;}
void SetUUID(const QUuid &uuid) {uuid_ = uuid;}
const uint64_t &GetFlags() const
{
return flags_;
}
/**
* @brief Clear current node variables and replace them with
*/
@@ -208,6 +222,79 @@ public:
return HasInputWithID(id);
}
struct Position
{
Position(const QPointF &p = QPointF(0, 0), bool e = false)
{
position = p;
expanded = e;
}
QPointF position;
bool expanded;
inline Position &operator+=(const Position &p)
{
position += p.position;
return *this;
}
inline Position &operator-=(const Position &p)
{
position -= p.position;
return *this;
}
friend inline const Position operator+(Position a, const Position &b)
{
a += b;
return a;
}
friend inline const Position operator-(Position a, const Position &b)
{
a -= b;
return a;
}
};
using PositionMap = QHash<Node*, Position>;
const PositionMap &GetContextPositions() const
{
return context_positions_;
}
bool IsNodeExpandedInContext(Node *node) const
{
return context_positions_.value(node).expanded;
}
bool ContextContainsNode(Node *node) const
{
return context_positions_.contains(node);
}
Position GetNodePositionDataInContext(Node *node)
{
return context_positions_.value(node);
}
QPointF GetNodePositionInContext(Node *node)
{
return GetNodePositionDataInContext(node).position;
}
bool SetNodePositionInContext(Node *node, const QPointF &pos);
bool SetNodePositionInContext(Node *node, const Position &pos);
void SetNodeExpandedInContext(Node *node, bool e)
{
context_positions_[node].expanded = e;
}
bool RemoveNodeFromContext(Node *node);
/**
* @brief Retrieve the color of this node
*/
@@ -243,11 +330,12 @@ public:
static void DisconnectEdge(Node *output, const NodeInput& input);
QString GetInputName(const QString& id) const;
virtual QString GetInputName(const QString& id) const;
void LoadInput(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled);
void SaveInput(QXmlStreamWriter* writer, const QString& id) const;
bool IsInputHidden(const QString& input) const;
bool IsInputConnectable(const QString& input) const;
bool IsInputKeyframable(const QString& input) const;
@@ -767,7 +855,7 @@ public:
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF& p);
virtual void GizmoMove(const QPointF& p, const rational &time, const Qt::KeyboardModifiers &modifiers);
virtual void GizmoRelease();
virtual void GizmoRelease(MultiUndoCommand *command);
const QString& GetLabel() const;
void SetLabel(const QString& s);
@@ -861,37 +949,9 @@ public:
};
InputFlags GetInputFlags(const QString& input) const;
protected:
enum InputFlag {
/// By default, inputs are keyframable, connectable, and NOT arrays
kInputFlagNormal = 0x0,
kInputFlagArray = 0x1,
kInputFlagNotKeyframable = 0x2,
kInputFlagNotConnectable = 0x4
};
class InputFlags {
public:
explicit InputFlags()
{
f_ = kInputFlagNormal;
}
explicit InputFlags(uint64_t flags)
{
f_ = flags;
}
bool operator&(const InputFlag& f) const
{
return f_ & f;
}
private:
uint64_t f_;
};
virtual void Hash(QCryptographicHash& hash, const NodeGlobals &globals, const VideoParams& video_params) const;
void HashAddNodeSignature(QCryptographicHash &hash) const;
@@ -986,12 +1046,12 @@ protected:
tooltip_ = s;
}
signals:
/**
* @brief Signal emitted whenever the position is set through SetPosition()
*/
void PositionChanged(const QPointF& pos);
void SetFlags(const uint64_t &f)
{
flags_ = f;
}
signals:
/**
* @brief Signal emitted when SetLabel() is called
*/
@@ -1021,7 +1081,11 @@ signals:
void KeyframeRemoved(NodeKeyframe* key);
void KeyframeTimeChanged();
void KeyframeTimeChanged(NodeKeyframe* key);
void KeyframeTypeChanged(NodeKeyframe* key);
void KeyframeValueChanged(NodeKeyframe* key);
void KeyframeEnableChanged(const NodeInput& input, bool enabled);
@@ -1037,6 +1101,12 @@ signals:
void RemovedFromGraph(NodeGraph* graph);
void NodeAddedToContext(Node *node);
void NodePositionInContextChanged(Node *node, const QPointF &pos);
void NodeRemovedFromContext(Node *node);
private:
class ArrayInsertCommand : public UndoCommand
{
@@ -1140,8 +1210,6 @@ private:
return input_ids_.indexOf(input);
}
InputFlags GetInputFlags(const QString& input) const;
Input* GetInternalInputData(const QString& input)
{
int i = GetInternalInputIndex(input);
@@ -1258,6 +1326,12 @@ private:
QMap<InputElementPair, ValueHint> value_hints_;
PositionMap context_positions_;
QUuid uuid_;
uint64_t flags_;
private slots:
/**
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
@@ -1367,12 +1441,11 @@ using NodePtr = std::shared_ptr<Node>;
class NodeSetPositionCommand : public UndoCommand
{
public:
NodeSetPositionCommand(Node* node, Node* relevant, const QPointF& pos, bool move_dependencies_relatively)
NodeSetPositionCommand(Node* node, Node* context, const Node::Position& pos)
{
node_ = node;
relevant_ = relevant;
context_ = context;
pos_ = pos;
move_deps_ = move_dependencies_relatively;
}
virtual Project* GetRelevantProject() const override
@@ -1387,153 +1460,43 @@ protected:
private:
Node* node_;
Node* relevant_;
QPointF pos_;
QPointF old_pos_;
Node* context_;
Node::Position pos_;
Node::Position old_pos_;
bool added_;
bool move_deps_;
NodeGraph *graph_;
};
class NodeSetPositionAndShiftSurroundingsCommand : public UndoCommand
{
class NodeSetPositionAndDependenciesRecursivelyCommand : public UndoCommand{
public:
NodeSetPositionAndShiftSurroundingsCommand(Node* node, Node *relative, const QPointF& pos, bool move_dependencies_relatively) :
NodeSetPositionAndDependenciesRecursivelyCommand(Node* node, Node* context, const Node::Position& pos) :
node_(node),
relative_(relative),
position_(pos),
move_dependencies_(move_dependencies_relatively)
context_(context),
pos_(pos)
{}
virtual ~NodeSetPositionAndShiftSurroundingsCommand() override
{
qDeleteAll(commands_);
}
virtual Project * GetRelevantProject() const override
virtual Project* GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void prepare() override;
virtual void redo() override;
virtual void undo() override
{
for (int i=commands_.size()-1; i>=0; i--) {
commands_.at(i)->undo_now();
}
}
virtual void undo() override;
private:
void move_recursively(Node *node, const QPointF &diff);
Node* node_;
Node *relative_;
QPointF position_;
bool move_dependencies_;
Node* context_;
Node::Position pos_;
QVector<UndoCommand*> commands_;
};
class NodeSetPositionAsChildCommand : public UndoCommand
{
public:
NodeSetPositionAsChildCommand(Node* node, Node* parent, Node *relative, double this_index, int child_count, bool shift_surroundings) :
node_(node),
parent_(parent),
relative_(relative),
this_index_(this_index),
child_count_(child_count),
shift_surroundings_(shift_surroundings),
sub_command_(nullptr)
{
}
virtual ~NodeSetPositionAsChildCommand() override
{
delete sub_command_;
}
virtual Project * GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override;
virtual void undo() override
{
sub_command_->undo_now();
}
private:
Node* node_;
Node* parent_;
Node *relative_;
double this_index_;
int child_count_;
bool shift_surroundings_;
MultiUndoCommand* sub_command_;
};
class NodePositionCloseChildGapCommand : public UndoCommand
{
public:
NodePositionCloseChildGapCommand(Node *parent, void *relative, int remove_index, int child_count, bool shift_surroundings);
virtual Project * GetRelevantProject() const override
{
return parent_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
Node *parent_;
};
class NodeSetPositionToOffsetOfAnotherNodeCommand : public UndoCommand
{
public:
NodeSetPositionToOffsetOfAnotherNodeCommand(Node* node, Node* other_node, Node *relative, const QPointF& offset) :
node_(node),
other_node_(other_node),
relative_(relative),
offset_(offset)
{}
virtual Project * GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
Node* node_;
Node* other_node_;
Node *relative_;
QPointF offset_;
QPointF old_pos_;
};
class NodeRemovePositionFromContextCommand : public UndoCommand
{
public:
@@ -1558,7 +1521,7 @@ private:
Node *context_;
QPointF old_pos_;
Node::Position old_pos_;
bool contained_;
@@ -1585,7 +1548,7 @@ protected:
private:
Node *node_;
std::map<Node *, QPointF> points_;
std::map<Node *, QPointF> contexts_;
};
+32 -18
View File
@@ -29,7 +29,7 @@
namespace olive {
void NodeCopyPasteService::CopyNodesToClipboard(const QVector<Node *> &nodes, void *userdata)
void NodeCopyPasteService::CopyNodesToClipboard(QVector<Node *> nodes, void *userdata)
{
QString copy_str;
@@ -42,22 +42,33 @@ void NodeCopyPasteService::CopyNodesToClipboard(const QVector<Node *> &nodes, vo
writer.writeTextElement(QStringLiteral("version"), QString::number(Core::kProjectVersion));
writer.writeStartElement(QStringLiteral("nodes"));
foreach (Node* n, nodes) {
for (int i=0; i<nodes.size(); i++) {
Node *n = nodes.at(i);
writer.writeStartElement(QStringLiteral("node"));
writer.writeAttribute(QStringLiteral("id"), n->id());
n->Save(&writer);
writer.writeEndElement(); // node
// If this is a group, add the child nodes too
if (NodeGroup *g = dynamic_cast<NodeGroup*>(n)) {
for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) {
if (!nodes.contains(it.key())) {
nodes.append(it.key());
}
}
}
}
writer.writeEndElement(); // nodes
writer.writeStartElement(QStringLiteral("contexts"));
foreach (Node* n, nodes) {
// Determine if this node is a context
if (n->parent()->GetPositionMap().contains(n)) {
if (!n->GetContextPositions().isEmpty()) {
writer.writeStartElement(QStringLiteral("context"));
writer.writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(n)));
const NodeGraph::PositionMap &map = n->parent()->GetNodesForContext(n);
const Node::PositionMap &map = n->GetContextPositions();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
writer.writeStartElement(QStringLiteral("node"));
Project::SavePosition(&writer, it.key(), it.value());
@@ -92,7 +103,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
QVector<Node*> pasted_nodes;
XMLNodeData xml_node_data;
QMap<quintptr, QMap<quintptr, QPointF> > pasted_contexts;
QMap<quintptr, QMap<quintptr, Node::Position> > pasted_contexts;
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("olive")) {
@@ -131,7 +142,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("context")) {
// Get context ptr
QMap<quintptr, QPointF> map;
QMap<quintptr, Node::Position> map;
quintptr context_ptr = 0;
XMLAttributeLoop((&reader), attr) {
if (attr.name() == QStringLiteral("ptr")) {
@@ -144,7 +155,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("node")) {
quintptr node_ptr;
QPointF node_pos;
Node::Position node_pos;
if (Project::LoadPosition(&reader, &node_ptr, &node_pos)) {
map.insert(node_ptr, node_pos);
@@ -201,31 +212,34 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
// Add all nodes to graph
foreach (Node* n, pasted_nodes) {
command->add_child(new NodeAddCommand(graph, n));
if (command) {
command->add_child(new NodeAddCommand(graph, n));
} else {
n->setParent(graph);
}
}
// Make connections
if (!xml_node_data.desired_connections.isEmpty()) {
XMLConnectNodes(xml_node_data, data_version, command);
}
// Link blocks
XMLLinkBlocks(xml_node_data);
// Process contexts
for (auto it=pasted_contexts.cbegin(); it!=pasted_contexts.cend(); it++) {
Node *context = xml_node_data.node_ptrs.value(it.key());
if (context) {
const QMap<quintptr, QPointF> &map = it.value();
auto map = it.value();
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
Node *subnode = xml_node_data.node_ptrs.value(jt.key());
if (subnode) {
command->add_child(new NodeSetPositionCommand(subnode, context, jt.value(), false));
if (command) {
command->add_child(new NodeSetPositionCommand(subnode, context, jt.value()));
} else {
context->SetNodePositionInContext(subnode, jt.value());
}
}
}
}
}
// Make connections
xml_node_data.PostConnect(data_version, command);
return pasted_nodes;
}
+1 -1
View File
@@ -35,7 +35,7 @@ public:
NodeCopyPasteService() = default;
protected:
void CopyNodesToClipboard(const QVector<Node *> &nodes, void* userdata = nullptr);
void CopyNodesToClipboard(QVector<Node *> nodes, void* userdata = nullptr);
QVector<Node*> PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand *command, void* userdata = nullptr);
+3 -2
View File
@@ -41,9 +41,10 @@ const QString Track::kMutedInput = QStringLiteral("muted_in");
Track::Track() :
track_type_(Track::kNone),
index_(-1),
locked_(false)
locked_(false),
sequence_(nullptr)
{
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable));
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable | kInputFlagHidden));
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
// on our end
+53 -9
View File
@@ -26,6 +26,8 @@
namespace olive {
class Sequence;
/**
* @brief A time traversal Node for sorting through one channel/track of Blocks
*/
@@ -168,17 +170,48 @@ public:
QString ToString() const
{
QString type_string;
if (type_ == Track::kVideo) {
type_string = QStringLiteral("v");
} else if (type_ == Track::kAudio) {
type_string = QStringLiteral("a");
} else {
QString type_string = TypeToString(type_);
if (type_string.isEmpty()) {
return QString();
} else {
return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
}
}
/// For IDs that shouldn't change between localizations
static QString TypeToString(Type type)
{
switch (type) {
case kVideo:
return QStringLiteral("v");
case kAudio:
return QStringLiteral("a");
case kSubtitle:
return QStringLiteral("s");
case kCount:
case kNone:
break;
}
return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
return QString();
}
/// For human-facing strings
static QString TypeToTranslatedString(Type type)
{
switch (type) {
case kVideo:
return tr("V");
case kAudio:
return tr("A");
case kSubtitle:
return tr("S");
case kCount:
case kNone:
break;
}
return QString();
}
static Type TypeFromString(const QString& s)
@@ -359,9 +392,18 @@ public:
bool IsLocked() const;
int GetArrayIndexFromBlock(Block* block) const;
Sequence *sequence() const
{
return sequence_;
}
void set_sequence(Sequence *sequence)
{
sequence_ = sequence;
}
static const double kTrackHeightDefault;
static const double kTrackHeightMinimum;
static const double kTrackHeightInterval;
@@ -443,6 +485,8 @@ private:
bool locked_;
Sequence *sequence_;
private slots:
void BlockLengthChanged();
+2
View File
@@ -83,6 +83,7 @@ void TrackList::TrackConnected(Node *node, int element)
connect(track, &Track::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
track->set_type(type_);
track->set_sequence(parent());
emit TrackListChanged();
@@ -121,6 +122,7 @@ void TrackList::TrackDisconnected(Node *node, int element)
track->SetIndex(-1);
track->set_type(Track::kNone);
track->set_sequence(nullptr);
disconnect(track, &Track::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
+4 -6
View File
@@ -23,7 +23,6 @@
#include "config/config.h"
#include "core.h"
#include "node/traverser.h"
#include "widget/videoparamedit/videoparamedit.h"
namespace olive {
@@ -34,8 +33,6 @@ const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in");
const QString ViewerOutput::kVideoAutoCacheInput = QStringLiteral("video_autocache_in");
const QString ViewerOutput::kAudioAutoCacheInput = QStringLiteral("audio_autocache_in");
const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight | VideoParamEdit::kInterlacing | VideoParamEdit::kFrameRate | VideoParamEdit::kPixelAspect;
#define super Node
ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_streams) :
@@ -47,10 +44,9 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream
video_cache_enabled_(true),
audio_cache_enabled_(true)
{
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(kVideoParamEditMask));
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
if (create_buffer_inputs) {
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
@@ -70,6 +66,8 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream
AddStream(Track::kAudio, QVariant());
set_default_parameters();
}
SetFlags(kDontShowInParamView);
}
Node *ViewerOutput::copy() const
-4
View File
@@ -200,8 +200,6 @@ public:
static const QString kVideoAutoCacheInput;
static const QString kAudioAutoCacheInput;
static const uint64_t kVideoParamEditMask;
signals:
void FrameRateChanged(const rational&);
@@ -254,8 +252,6 @@ private:
AudioPlaybackCache audio_playback_cache_;
int operation_stack_;
VideoParams cached_video_params_;
AudioParams cached_audio_params_;
+36
View File
@@ -33,6 +33,15 @@ QString NodeInput::name() const
}
}
bool NodeInput::IsHidden() const
{
if (IsValid()) {
return node_->IsInputHidden(input_);
} else {
return false;
}
}
bool NodeInput::IsConnected() const
{
if (IsValid()) {
@@ -60,6 +69,15 @@ bool NodeInput::IsArray() const
}
}
InputFlags NodeInput::GetFlags() const
{
if (IsValid()) {
return node_->GetInputFlags(input_);
} else {
return InputFlags(kInputFlagNormal);
}
}
Node *NodeInput::GetConnectedOutput() const
{
if (IsValid()) {
@@ -78,6 +96,15 @@ NodeValue::Type NodeInput::GetDataType() const
}
}
QVariant NodeInput::GetDefaultValue() const
{
if (IsValid()) {
return node_->GetDefaultValue(input_);
} else {
return QVariant();
}
}
QStringList NodeInput::GetComboBoxStrings() const
{
if (IsValid()) {
@@ -123,6 +150,15 @@ QVariant NodeInput::GetSplitDefaultValueForTrack(int track) const
}
}
int NodeInput::GetArraySize() const
{
if (IsValid() && element_ == -1) {
return node_->InputArraySize(input_);
} else {
return 0;
}
}
uint qHash(const NodeInput &i)
{
return qHash(i.node()) ^ qHash(i.input()) ^ qHash(i.element());
+50 -1
View File
@@ -31,6 +31,37 @@ namespace olive {
class Node;
class NodeKeyframe;
enum InputFlag {
/// By default, inputs are keyframable, connectable, and NOT arrays
kInputFlagNormal = 0x0,
kInputFlagArray = 0x1,
kInputFlagNotKeyframable = 0x2,
kInputFlagNotConnectable = 0x4,
kInputFlagHidden = 0x8
};
class InputFlags {
public:
explicit InputFlags()
{
f_ = kInputFlagNormal;
}
explicit InputFlags(uint64_t flags)
{
f_ = flags;
}
bool operator&(const InputFlag& f) const
{
return f_ & f;
}
private:
uint64_t f_;
};
struct NodeInputPair {
bool operator==(const NodeInputPair& rhs) const
{
@@ -98,11 +129,21 @@ public:
return input_;
}
int element() const
const int &element() const
{
return element_;
}
void set_node(Node *node)
{
node_ = node;
}
void set_input(const QString &input)
{
input_ = input;
}
void set_element(int e)
{
element_ = e;
@@ -115,16 +156,22 @@ public:
return node_ && !input_.isEmpty() && element_ >= -1;
}
bool IsHidden() const;
bool IsConnected() const;
bool IsKeyframing() const;
bool IsArray() const;
InputFlags GetFlags() const;
Node *GetConnectedOutput() const;
NodeValue::Type GetDataType() const;
QVariant GetDefaultValue() const;
QStringList GetComboBoxStrings() const;
QVariant GetProperty(const QString& key) const;
@@ -135,6 +182,8 @@ public:
QVariant GetSplitDefaultValueForTrack(int track) const;
int GetArraySize() const;
void Reset()
{
*this = NodeInput();
+2 -20
View File
@@ -113,19 +113,12 @@ void Folder::InputDisconnectedEvent(const QString &input, int element, Node *out
}
}
FolderAddChild::FolderAddChild(Folder *folder, Node *child, bool autoposition) :
FolderAddChild::FolderAddChild(Folder *folder, Node *child) :
folder_(folder),
child_(child),
autoposition_(autoposition),
position_command_(nullptr)
child_(child)
{
}
FolderAddChild::~FolderAddChild()
{
delete position_command_;
}
Project *FolderAddChild::GetRelevantProject() const
{
return folder_->project();
@@ -136,21 +129,10 @@ void FolderAddChild::redo()
int array_index = folder_->InputArraySize(Folder::kChildInput);
folder_->InputArrayAppend(Folder::kChildInput, false);
Node::ConnectEdge(child_, NodeInput(folder_, Folder::kChildInput, array_index));
if (autoposition_) {
if (!position_command_) {
position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, folder_->project()->root(), array_index, array_index+1, true);
}
position_command_->redo_now();
}
}
void FolderAddChild::undo()
{
if (position_command_) {
position_command_->undo_now();
}
Node::DisconnectEdge(child_, NodeInput(folder_, Folder::kChildInput, folder_->InputArraySize(Folder::kChildInput)-1));
folder_->InputArrayRemoveLast(Folder::kChildInput);
}
+1 -7
View File
@@ -208,9 +208,7 @@ private:
class FolderAddChild : public UndoCommand
{
public:
FolderAddChild(Folder* folder, Node* child, bool autoposition = true);
virtual ~FolderAddChild() override;
FolderAddChild(Folder* folder, Node* child);
virtual Project * GetRelevantProject() const override;
@@ -224,10 +222,6 @@ private:
Node* child_;
bool autoposition_;
NodeSetPositionAsChildCommand* position_command_;
};
}
-34
View File
@@ -33,7 +33,6 @@
#include "core.h"
#include "render/job/footagejob.h"
#include "ui/icons/icons.h"
#include "widget/videoparamedit/videoparamedit.h"
namespace olive {
@@ -144,39 +143,6 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
AddStream(Track::kVideo, QVariant::fromValue(vp));
}
if (!footage_info.GetVideoStreams().isEmpty()) {
// FIXME: This will break on multiple video streams. Currently we don't have
// infrastructure for different properties per element. We'll see if this becomes
// a problem.
VideoParams vp = footage_info.GetVideoStreams().first();
uint64_t video_param_mask = 0;
video_param_mask |= VideoParamEdit::kEnabled;
video_param_mask |= VideoParamEdit::kColorspace;
video_param_mask |= VideoParamEdit::kPixelAspect;
video_param_mask |= VideoParamEdit::kInterlacing;
video_param_mask |= VideoParamEdit::kFrameRateIsArbitrary;
if (vp.channel_count() == VideoParams::kRGBAChannelCount) {
// Add premultiplied setting if this footage has an alpha channel
video_param_mask |= VideoParamEdit::kPremultipliedAlpha;
}
if (vp.video_type() == VideoParams::kVideoTypeVideo) {
// This is video, ensure that the frame rate does not overwrite the timebase
video_param_mask |= VideoParamEdit::kFrameRateIsNotTimebase;
} else {
// This is not a video, so it's either a still image or an image sequence
video_param_mask |= VideoParamEdit::kIsImageSequence;
video_param_mask |= VideoParamEdit::kStartTime;
video_param_mask |= VideoParamEdit::kEndTime;
video_param_mask |= VideoParamEdit::kFrameRate;
}
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(video_param_mask));
}
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
AddStream(Track::kAudio, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
}
+24 -25
View File
@@ -44,19 +44,16 @@ Project::Project() :
root_->setParent(this);
root_->SetLabel(tr("Root"));
root_->SetCanBeDeleted(false);
SetNodePosition(root_, root_, QPointF(0, 0));
// Adds a color manager "node" to this project so that it synchronizes
color_manager_ = new ColorManager();
color_manager_->setParent(this);
SetNodePosition(color_manager_, root_, QPointF(1, 0));
color_manager_->SetCanBeDeleted(false);
AddDefaultNode(color_manager_);
// Same with project settings
settings_ = new ProjectSettingsNode();
settings_->setParent(this);
SetNodePosition(settings_, root_, QPointF(2, 0));
settings_->SetCanBeDeleted(false);
AddDefaultNode(settings_);
@@ -161,13 +158,13 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
quintptr node_ptr;
QPointF node_pos;
Node::Position node_pos;
if (LoadPosition(reader, &node_ptr, &node_pos)) {
Node *node = xml_node_data.node_ptrs.value(node_ptr);
if (node) {
SetNodePosition(node, context, node_pos);
context->SetNodePositionInContext(node, node_pos);
} else {
qWarning() << "Failed to find pointer for node position";
reader->skipCurrentElement();
@@ -196,10 +193,7 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
}
// Make connections
XMLConnectNodes(xml_node_data, version);
// Link blocks
XMLLinkBlocks(xml_node_data);
xml_node_data.PostConnect(version);
}
void Project::Save(QXmlStreamWriter *writer) const
@@ -230,20 +224,22 @@ void Project::Save(QXmlStreamWriter *writer) const
writer->writeStartElement(QStringLiteral("positions"));
for (auto it=GetPositionMap().cbegin(); it!=GetPositionMap().cend(); it++) {
writer->writeStartElement(QStringLiteral("context"));
foreach (Node* context, nodes()) {
const Node::PositionMap &map = context->GetContextPositions();
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
if (!map.isEmpty()) {
writer->writeStartElement(QStringLiteral("context"));
const PositionMap &map = it.value();
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(context)));
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
writer->writeStartElement(QStringLiteral("node"));
SavePosition(writer, jt.key(), jt.value());
writer->writeEndElement(); // node
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
writer->writeStartElement(QStringLiteral("node"));
SavePosition(writer, jt.key(), jt.value());
writer->writeEndElement(); // node
}
writer->writeEndElement(); // context
}
writer->writeEndElement(); // context
}
writer->writeEndElement(); // positions
@@ -351,7 +347,7 @@ void Project::RegenerateUuid()
uuid_ = QUuid::createUuid();
}
bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF *pos)
bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos)
{
bool got_node_ptr = false;
bool got_pos_x = false;
@@ -367,11 +363,13 @@ bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("x")) {
pos->setX(reader->readElementText().toDouble());
pos->position.setX(reader->readElementText().toDouble());
got_pos_x = true;
} else if (reader->name() == QStringLiteral("y")) {
pos->setY(reader->readElementText().toDouble());
pos->position.setY(reader->readElementText().toDouble());
got_pos_y = true;
} else if (reader->name() == QStringLiteral("expanded")) {
pos->expanded = reader->readElementText().toInt();
} else {
reader->skipCurrentElement();
}
@@ -380,12 +378,13 @@ bool Project::LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF
return got_node_ptr && got_pos_x && got_pos_y;
}
void Project::SavePosition(QXmlStreamWriter *writer, Node *node, const QPointF &pos)
void Project::SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos)
{
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(node)));
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.x()));
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.y()));
writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x()));
writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y()));
writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded));
}
void Project::ColorManagerValueChanged(const NodeInput &input, const TimeRange &range)
+2 -2
View File
@@ -82,8 +82,8 @@ public:
void RegenerateUuid();
static bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, QPointF *pos);
static void SavePosition(QXmlStreamWriter *writer, Node *node, const QPointF &pos);
static bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos);
static void SavePosition(QXmlStreamWriter *writer, Node *node, const Node::Position &pos);
signals:
void NameChanged();
+8 -7
View File
@@ -297,15 +297,16 @@ 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)
ViewerOutput* footage = dynamic_cast<ViewerOutput*>(static_cast<Node*>(index.internalPointer()));
Node *item = static_cast<Node*>(index.internalPointer());
QVector<Track::Reference> streams;
if (footage) {
QVector<Track::Reference> streams = footage->GetEnabledStreamsAsReferences();
stream << streams << reinterpret_cast<quintptr>(footage);
dragged_items.append(footage);
if (ViewerOutput* footage = dynamic_cast<ViewerOutput*>(item)) {
streams = footage->GetEnabledStreamsAsReferences();
}
stream << streams << reinterpret_cast<quintptr>(item);
dragged_items.append(item);
}
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ Sequence::Sequence()
// Create track input
QString track_input_id = kTrackInputFormat.arg(i);
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray));
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
IgnoreInvalidationsFrom(track_input_id);
+12
View File
@@ -39,6 +39,18 @@ public:
virtual void DeselectAll() override;
public slots:
void SetNode(Node *node)
{
// Convert single pointer to either an empty vector or a vector of one
QVector<Node *> nodes;
if (node) {
nodes.append(node);
}
SetNodes(nodes);
}
void SetNodes(const QVector<Node *> &nodes);
virtual void IncreaseTrackHeight() override;
+1 -2
View File
@@ -29,8 +29,7 @@ FootageViewerPanel::FootageViewerPanel(QWidget *parent) :
{
// Set ViewerWidget as the central widget
FootageViewerWidget* fvw = new FootageViewerWidget();
connect(fvw, &FootageViewerWidget::RequestScopePanel, this, &FootageViewerPanel::CreateScopePanel);
SetTimeBasedWidget(fvw);
SetViewerWidget(fvw);
// Set strings
Retranslate();
+7 -25
View File
@@ -20,39 +20,21 @@
#include "node.h"
#include <QVBoxLayout>
namespace olive {
NodePanel::NodePanel(QWidget *parent) :
PanelWidget(QStringLiteral("NodePanel"), parent)
{
QWidget *outer_widget = new QWidget(this);
node_widget_ = new NodeWidget();
connect(this, &NodePanel::visibilityChanged, node_widget_->view(), &NodeView::CenterOnItemsBoundingRect);
QVBoxLayout *outer_layout = new QVBoxLayout(outer_widget);
outer_layout->setMargin(0);
toolbar_ = new NodeViewToolBar();
outer_layout->addWidget(toolbar_);
// Create NodeView widget
node_view_ = new NodeView(this);
outer_layout->addWidget(node_view_);
// Connect toolbar to NodeView
connect(toolbar_, &NodeViewToolBar::MiniMapEnabledToggled, node_view_, &NodeView::SetMiniMapEnabled);
connect(toolbar_, &NodeViewToolBar::AddNodeClicked, node_view_, &NodeView::ShowAddMenu);
// Set defaults
toolbar_->SetMiniMapEnabled(true);
node_view_->SetMiniMapEnabled(true);
// Connect node view signals to this panel
connect(node_view_, &NodeView::NodesSelected, this, &NodePanel::NodesSelected);
connect(node_view_, &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected);
// Connect node view signals to this panel - MAY REMOVE
connect(node_widget_->view(), &NodeView::NodesSelected, this, &NodePanel::NodesSelected);
connect(node_widget_->view(), &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected);
connect(node_widget_->view(), &NodeView::NodeGroupOpenRequested, this, &NodePanel::NodeGroupOpenRequested);
// Set it as the main widget of this panel
SetWidgetWithPadding(outer_widget);
SetWidgetWithPadding(node_widget_);
// Set strings
Retranslate();
+28 -29
View File
@@ -21,8 +21,7 @@
#ifndef NODEPANEL_H
#define NODEPANEL_H
#include "widget/nodeview/nodeview.h"
#include "widget/nodeview/nodeviewtoolbar.h"
#include "widget/nodeview/nodewidget.h"
#include "widget/panel/panel.h"
namespace olive {
@@ -36,86 +35,86 @@ class NodePanel : public PanelWidget
public:
NodePanel(QWidget* parent);
NodeGraph* GetGraph() const
NodeWidget *GetNodeWidget() const
{
return node_view_->GetGraph();
return node_widget_;
}
void SetGraph(NodeGraph *graph, const QVector<Node*> &nodes)
const QVector<Node*> &GetContexts() const
{
node_view_->SetGraph(graph, nodes);
toolbar_->setEnabled(graph);
return node_widget_->view()->GetContexts();
}
void ClearGraph()
void SetContexts(const QVector<Node*> &nodes)
{
node_view_->ClearGraph();
node_widget_->SetContexts(nodes);
}
void CloseContextsBelongingToProject(Project *project)
{
node_widget_->view()->CloseContextsBelongingToProject(project);
}
const QVector<Node*> &GetCurrentContexts() const
{
return node_view_->GetCurrentContexts();
return node_widget_->view()->GetCurrentContexts();
}
virtual void SelectAll() override
{
node_view_->SelectAll();
node_widget_->view()->SelectAll();
}
virtual void DeselectAll() override
{
node_view_->DeselectAll();
node_widget_->view()->DeselectAll();
}
virtual void DeleteSelected() override
{
node_view_->DeleteSelected();
node_widget_->view()->DeleteSelected();
}
virtual void CutSelected() override
{
node_view_->CopySelected(true);
node_widget_->view()->CopySelected(true);
}
virtual void CopySelected() override
{
node_view_->CopySelected(false);
node_widget_->view()->CopySelected(false);
}
virtual void Paste() override
{
node_view_->Paste();
node_widget_->view()->Paste();
}
virtual void Duplicate() override
{
node_view_->Duplicate();
node_widget_->view()->Duplicate();
}
virtual void SetColorLabel(int index) override
{
node_view_->SetColorLabel(index);
node_widget_->view()->SetColorLabel(index);
}
virtual void ZoomIn() override
{
node_view_->ZoomIn();
node_widget_->view()->ZoomIn();
}
virtual void ZoomOut() override
{
node_view_->ZoomOut();
node_widget_->view()->ZoomOut();
}
public slots:
void Select(const QVector<Node*>& nodes, bool center_view_on_item)
{
node_view_->Select(nodes, center_view_on_item);
}
void SelectWithDependencies(const QVector<Node*>& nodes, bool center_view_on_item)
{
node_view_->SelectWithDependencies(nodes, center_view_on_item);
node_widget_->view()->Select(nodes, center_view_on_item);
this->raise();
}
signals:
@@ -123,15 +122,15 @@ signals:
void NodesDeselected(const QVector<Node*>& nodes);
void NodeGroupOpenRequested(NodeGroup *group);
private:
virtual void Retranslate() override
{
SetTitle(tr("Node Editor"));
}
NodeView* node_view_;
NodeViewToolBar *toolbar_;
NodeWidget *node_widget_;
};
+34 -7
View File
@@ -95,6 +95,40 @@ PanelManager *PanelManager::instance()
return instance_;
}
void PanelManager::RegisterPanel(PanelWidget *panel)
{
// Add panel to the bottom of the focus history
focus_history_.append(panel);
panel->SetMovementLocked(locked_);
// Get panel parent (it's assumed it has one)
QWidget *parent = panel->parentWidget();
// Sane default for panel size
panel->resize(parent->size() / 3);
// We're about to center the panel relative to the parent (usually the main window), but for some
// reason this requires the panel to be shown first.
panel->show();
// Center the panel relative to the parent
QPoint parent_center = panel->mapFromGlobal(parent->mapToGlobal(parent->rect().center()));
QPoint panel_center = panel->rect().center();
panel->move(parent_center - panel_center);
if (focus_history_.size() == 1) {
// This is the first panel, focus it
panel->SetBorderVisible(true);
emit FocusedPanelChanged(panel);
}
}
void PanelManager::UnregisterPanel(PanelWidget *panel)
{
focus_history_.removeOne(panel);
}
void PanelManager::FocusChanged(QWidget *old, QWidget *now)
{
Q_UNUSED(old)
@@ -151,11 +185,4 @@ void PanelManager::SetPanelsLocked(bool locked)
locked_ = locked;
}
void PanelManager::PanelDestroyed()
{
PanelWidget* panel = static_cast<PanelWidget*>(sender());
focus_history_.removeOne(panel);
}
}
+10 -46
View File
@@ -84,12 +84,6 @@ public:
*/
T* MostRecentlyFocused();
template<class T>
/**
* @brief Create a panel
*/
T* CreatePanel(QWidget* parent);
/**
* @brief Get whether panels are currently prevented from moving
*/
@@ -118,6 +112,16 @@ public:
*/
QList<T*> GetPanelsOfType();
/**
* @brief Panel should call this upon construction so it can be kept track of
*/
void RegisterPanel(PanelWidget *panel);
/**
* @brief Panel should call this upon destruction so no invalid pointers will be kept for it
*/
void UnregisterPanel(PanelWidget *panel);
public slots:
/**
* @brief Connect this to a QApplication's SIGNAL(focusChanged())
@@ -153,48 +157,8 @@ private:
*/
static PanelManager* instance_;
private slots:
/**
* @brief Processing if a panel gets deleted
*/
void PanelDestroyed();
};
template<class T>
T *PanelManager::CreatePanel(QWidget *parent)
{
T* panel = new T(parent);
// Add panel to the bottom of the focus history
focus_history_.append(panel);
panel->SetMovementLocked(locked_);
// Sane default for panel size
panel->resize(parent->size() / 3);
// We're about to center the panel relative to the parent (usually the main window), but for some
// reason this requires the panel to be shown first.
panel->show();
// Center the panel relative to the parent
QPoint parent_center = panel->mapFromGlobal(parent->mapToGlobal(parent->rect().center()));
QPoint panel_center = panel->rect().center();
panel->move(parent_center - panel_center);
// Connect destroy signal so we can remove it from focus history
connect(panel, &PanelWidget::destroyed, this, &PanelManager::PanelDestroyed, Qt::DirectConnection);
if (focus_history_.size() == 1) {
// This is the first panel, focus it
panel->SetBorderVisible(true);
emit FocusedPanelChanged(panel);
}
return panel;
}
template<class T>
T* PanelManager::MostRecentlyFocused()
{
+5 -15
View File
@@ -29,7 +29,6 @@ ParamPanel::ParamPanel(QWidget* parent) :
{
NodeParamView* view = new NodeParamView();
connect(view, &NodeParamView::RequestSelectNode, this, &ParamPanel::RequestSelectNode);
connect(view, &NodeParamView::NodeOrderChanged, this, &ParamPanel::NodeOrderChanged);
connect(view, &NodeParamView::FocusedNodeChanged, this, &ParamPanel::FocusedNodeChanged);
SetTimeBasedWidget(view);
@@ -39,15 +38,11 @@ ParamPanel::ParamPanel(QWidget* parent) :
void ParamPanel::SelectNodes(const QVector<Node *> &nodes)
{
static_cast<NodeParamView*>(GetTimeBasedWidget())->SelectNodes(nodes);
Retranslate();
}
void ParamPanel::DeselectNodes(const QVector<Node *> &nodes)
{
static_cast<NodeParamView*>(GetTimeBasedWidget())->DeselectNodes(nodes);
Retranslate();
}
void ParamPanel::DeleteSelected()
@@ -65,19 +60,14 @@ void ParamPanel::DeselectAll()
static_cast<NodeParamView*>(GetTimeBasedWidget())->DeselectAll();
}
void ParamPanel::SetContexts(const QVector<Node *> &contexts)
{
static_cast<NodeParamView*>(GetTimeBasedWidget())->SetContexts(contexts);
}
void ParamPanel::Retranslate()
{
SetTitle(tr("Parameter Editor"));
NodeParamView* view = static_cast<NodeParamView*>(GetTimeBasedWidget());
if (view->GetItemMap().isEmpty()) {
SetSubtitle(tr("(none)"));
} else if (view->GetItemMap().size() == 1) {
SetSubtitle(view->GetItemMap().firstKey()->Name());
} else {
SetSubtitle(tr("(multiple)"));
}
}
}
+27 -2
View File
@@ -33,6 +33,31 @@ class ParamPanel : public TimeBasedPanel
public:
ParamPanel(QWidget* parent);
NodeParamView *GetParamView() const
{
return static_cast<NodeParamView *>(GetTimeBasedWidget());
}
const QVector<Node*> &GetContexts() const
{
return GetParamView()->GetContexts();
}
void SetCreateCheckBoxes(NodeParamViewCheckBoxBehavior e)
{
GetParamView()->SetCreateCheckBoxes(e);
}
void SetIgnoreNodeFlags(bool e)
{
GetParamView()->SetIgnoreNodeFlags(e);
}
void CloseContextsBelongingToProject(Project *p)
{
GetParamView()->CloseContextsBelongingToProject(p);
}
public slots:
void SelectNodes(const QVector<Node*>& nodes);
void DeselectNodes(const QVector<Node*>& nodes);
@@ -43,11 +68,11 @@ public slots:
virtual void DeselectAll() override;
void SetContexts(const QVector<Node*> &contexts);
signals:
void RequestSelectNode(const QVector<Node*>& target);
void NodeOrderChanged(const QVector<Node*>& nodes);
void FocusedNodeChanged(Node* n);
protected:
+1 -1
View File
@@ -25,7 +25,7 @@
namespace olive {
PixelSamplerPanel::PixelSamplerPanel(QWidget *parent) :
PanelWidget(QStringLiteral("ProjectPanel"), parent)
PanelWidget(QStringLiteral("PixelSamplerPanel"), parent)
{
sampler_widget_ = new ManagedPixelSamplerWidget();
SetWidgetWithPadding(sampler_widget_);
+30 -2
View File
@@ -27,7 +27,8 @@
namespace olive {
ScopePanel::ScopePanel(QWidget* parent) :
PanelWidget(QStringLiteral("ScopePanel"), parent)
PanelWidget(QStringLiteral("ScopePanel"), parent),
viewer_(nullptr)
{
QWidget* central = new QWidget();
setWidget(central);
@@ -84,6 +85,33 @@ QString ScopePanel::TypeToName(ScopePanel::Type t)
return QString();
}
void ScopePanel::SetViewerPanel(ViewerPanelBase *vp)
{
if (viewer_ == vp) {
return;
}
if (viewer_) {
disconnect(viewer_, &ViewerPanelBase::TextureChanged, this, &ScopePanel::SetReferenceBuffer);
disconnect(viewer_, &ViewerPanelBase::ColorManagerChanged, this, &ScopePanel::SetColorManager);
}
viewer_ = vp;
if (viewer_) {
// Connect viewer widget texture drawing to scope panel
connect(viewer_, &ViewerPanelBase::TextureChanged, this, &ScopePanel::SetReferenceBuffer);
connect(viewer_, &ViewerPanelBase::ColorManagerChanged, this, &ScopePanel::SetColorManager);
SetColorManager(viewer_->GetColorManager());
viewer_->UpdateTextureFromNode();
} else {
SetReferenceBuffer(nullptr);
SetColorManager(nullptr);
}
}
void ScopePanel::SetReferenceBuffer(TexturePtr frame)
{
histogram_->SetBuffer(frame);
@@ -98,7 +126,7 @@ void ScopePanel::SetColorManager(ColorManager *manager)
void ScopePanel::Retranslate()
{
SetTitle(tr("Scope"));
SetTitle(tr("Scopes"));
for (int i=0;i<ScopePanel::kTypeCount;i++) {
scope_type_combobox_->setItemText(i, TypeToName(static_cast<Type>(i)));
+10 -2
View File
@@ -24,14 +24,13 @@
#include <QComboBox>
#include <QStackedWidget>
#include "panel/viewer/viewerbase.h"
#include "widget/panel/panel.h"
#include "widget/scope/histogram/histogram.h"
#include "widget/scope/waveform/waveform.h"
namespace olive {
class ViewerPanel;
class ScopePanel : public PanelWidget
{
Q_OBJECT
@@ -49,6 +48,13 @@ public:
static QString TypeToName(Type t);
void SetViewerPanel(ViewerPanelBase *vp);
ViewerPanelBase *GetConnectedViewerPanel() const
{
return viewer_;
}
public slots:
void SetReferenceBuffer(TexturePtr frame);
@@ -68,6 +74,8 @@ private:
HistogramScope* histogram_;
ViewerPanelBase *viewer_;
};
}
+5 -17
View File
@@ -25,13 +25,12 @@ namespace olive {
ViewerPanel::ViewerPanel(const QString &object_name, QWidget *parent) :
ViewerPanelBase(object_name, parent)
{
Init();
}
// Set ViewerWidget as the central widget
ViewerWidget* vw = new ViewerWidget();
SetViewerWidget(vw);
ViewerPanel::ViewerPanel(QWidget *parent) :
ViewerPanelBase(QStringLiteral("ViewerPanel"), parent)
{
Init();
// Set strings
Retranslate();
}
void ViewerPanel::Retranslate()
@@ -41,15 +40,4 @@ void ViewerPanel::Retranslate()
SetTitle(tr("Viewer"));
}
void ViewerPanel::Init()
{
// Set ViewerWidget as the central widget
ViewerWidget* vw = new ViewerWidget();
connect(vw, &ViewerWidget::RequestScopePanel, this, &ViewerPanel::CreateScopePanel);
SetTimeBasedWidget(vw);
// Set strings
Retranslate();
}
}
+4 -4
View File
@@ -34,14 +34,14 @@ class ViewerPanel : public ViewerPanelBase {
Q_OBJECT
public:
ViewerPanel(const QString& object_name, QWidget* parent);
ViewerPanel(QWidget* parent);
ViewerPanel(QWidget *parent) :
ViewerPanel(QStringLiteral("ViewerPanel"), parent)
{
}
protected:
virtual void Retranslate() override;
private:
void Init();
};
}
+5 -19
View File
@@ -92,27 +92,13 @@ void ViewerPanelBase::CacheSequenceInOut()
static_cast<ViewerWidget*>(GetTimeBasedWidget())->CacheSequenceInOut();
}
void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type)
void ViewerPanelBase::SetViewerWidget(ViewerWidget *vw)
{
ViewerWidget* vw = static_cast<ViewerWidget*>(GetTimeBasedWidget());
ScopePanel* p = Core::instance()->main_window()->AppendScopePanel();
connect(vw, &ViewerWidget::TextureChanged, this, &ViewerPanelBase::TextureChanged);
connect(vw, &ViewerWidget::ColorProcessorChanged, this, &ViewerPanelBase::ColorProcessorChanged);
connect(vw, &ViewerWidget::ColorManagerChanged, this, &ViewerPanelBase::ColorManagerChanged);
p->SetType(type);
// Connect viewer widget texture drawing to scope panel
connect(vw, &ViewerWidget::TextureChanged, p, &ScopePanel::SetReferenceBuffer);
connect(vw, &ViewerWidget::ColorManagerChanged, p, &ScopePanel::SetColorManager);
p->SetColorManager(vw->color_manager());
vw->UpdateTextureFromNode();
}
void ViewerPanelBase::closeEvent(QCloseEvent *e)
{
static_cast<ViewerWidget*>(GetTimeBasedWidget())->Pause();
TimeBasedPanel::closeEvent(e);
SetTimeBasedWidget(vw);
}
}
+27 -3
View File
@@ -52,6 +52,16 @@ public:
*/
void SetFullScreen(QScreen* screen = nullptr);
ColorManager *GetColorManager()
{
return static_cast<ViewerWidget*>(GetTimeBasedWidget())->color_manager();
}
void UpdateTextureFromNode()
{
static_cast<ViewerWidget*>(GetTimeBasedWidget())->UpdateTextureFromNode();
}
public slots:
void SetGizmos(Node* node);
@@ -59,10 +69,24 @@ public slots:
void CacheSequenceInOut();
protected:
void CreateScopePanel(ScopePanel::Type type);
signals:
/**
* @brief Signal emitted when a new frame is loaded
*/
void TextureChanged(TexturePtr t);
virtual void closeEvent(QCloseEvent* e) override;
/**
* @brief Wrapper for ViewerGLWidget::ColorProcessorChanged()
*/
void ColorProcessorChanged(ColorProcessorPtr processor);
/**
* @brief Wrapper for ViewerGLWidget::ColorManagerChanged()
*/
void ColorManagerChanged(ColorManager* color_manager);
protected:
void SetViewerWidget(ViewerWidget *vw);
};
+16 -6
View File
@@ -367,12 +367,20 @@ void PreviewAutoCacher::ProcessUpdateQueue()
void PreviewAutoCacher::AddNode(Node *node)
{
if (dynamic_cast<NodeGroup*>(node)) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
// Copy node
Node* copy = node->copy();
// Add to project
copy->setParent(&copied_project_);
// Copy UUID
copy->SetUUID(node->GetUUID());
// Insert into map
InsertIntoCopyMap(node, copy);
@@ -730,9 +738,9 @@ void PreviewAutoCacher::RequeueFrames()
delayed_requeue_timer_.stop();
if (viewer_node_
&& (viewer_node_->GetVideoAutoCacheEnabled() || use_custom_range_)
&& viewer_node_->video_frame_cache()->HasInvalidatedRanges(viewer_node_->GetVideoLength())
&& hash_tasks_.isEmpty()
&& (viewer_node_->GetVideoAutoCacheEnabled() || use_custom_range_)
&& !IsRenderingCustomRange()) {
TimeRange using_range = use_custom_range_ ? custom_autocache_range_ : cache_range_;
@@ -743,11 +751,13 @@ void PreviewAutoCacher::RequeueFrames()
emit StopCacheProxyTasks();
CustomCacheTask *cct = new CustomCacheTask(viewer_node_->GetLabelOrName());
connect(this, &PreviewAutoCacher::StopCacheProxyTasks, cct, &CustomCacheTask::Finish);
connect(this, &PreviewAutoCacher::SignalCacheProxyTaskProgress, cct, &CustomCacheTask::ProgressChanged);
connect(cct, &CustomCacheTask::Cancelled, this, &PreviewAutoCacher::CacheProxyTaskCancelled);
TaskManager::instance()->AddTask(cct);
if (use_custom_range_) {
CustomCacheTask *cct = new CustomCacheTask(viewer_node_->GetLabelOrName());
connect(this, &PreviewAutoCacher::StopCacheProxyTasks, cct, &CustomCacheTask::Finish);
connect(this, &PreviewAutoCacher::SignalCacheProxyTaskProgress, cct, &CustomCacheTask::ProgressChanged);
connect(cct, &CustomCacheTask::Cancelled, this, &PreviewAutoCacher::CacheProxyTaskCancelled);
TaskManager::instance()->AddTask(cct);
}
use_custom_range_ = false;
+3 -1
View File
@@ -24,8 +24,9 @@
#include <QtConcurrent/QtConcurrent>
#include "config/config.h"
#include "node/graph.h"
#include "node/color/colormanager/colormanager.h"
#include "node/graph.h"
#include "node/group/group.h"
#include "node/node.h"
#include "node/output/viewer/viewer.h"
#include "node/project/project.h"
@@ -172,6 +173,7 @@ private:
QVector<QueuedJob> graph_update_queue_;
QHash<Node*, Node*> copy_map_;
QHash<NodeGraph*, NodeGraph*> graph_map_;
ViewerOutput* copied_viewer_node_;
ColorManager* copied_color_manager_;
QVector<Node*> created_nodes_;
+3 -1
View File
@@ -96,7 +96,9 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time
if (output_color_transform) {
// Yes color transform, blit color managed
render_ctx_->BlitColorManaged(output_color_transform, texture, Renderer::kAlphaAssociated, blit_tex.get(), true, matrix);
render_ctx_->BlitColorManaged(output_color_transform, texture,
Config::Current()[QStringLiteral("ReassocLinToNonLin")].toBool() ? Renderer::kAlphaAssociated : Renderer::kAlphaNone,
blit_tex.get(), true, matrix);
} else {
// No color transform, just blit
ShaderJob job;
+7
View File
@@ -40,4 +40,11 @@ void Texture::Upload(void *data, int linesize)
}
}
void Texture::Download(void *data, int linesize)
{
if (renderer_) {
renderer_->DownloadFromTexture(this, data, linesize);
}
}
}
+2
View File
@@ -80,6 +80,8 @@ public:
void Upload(void* data, int linesize);
void Download(void* data, int linesize);
bool IsDummy() const
{
return !renderer_;
+6 -5
View File
@@ -36,6 +36,8 @@ ExportTask::ExportTask(ViewerOutput *viewer_node,
// Adjust video params to have no divider
VideoParams vp = viewer_node->GetVideoParams();
vp.set_divider(1);
vp.set_time_base(params.video_params().time_base());
vp.set_frame_rate(params.video_params().frame_rate());
set_video_params(vp);
set_audio_params(viewer_node->GetAudioParams());
@@ -85,15 +87,14 @@ bool ExportTask::Run()
if (params_.video_enabled()) {
// If a transformation matrix is applied to this video, create it here
VideoParams vp = viewer()->GetVideoParams();
if (vp.width() != params_.video_params().width()
|| params_.video_params().height() != params_.video_params().height()) {
if (video_params().width() != params_.video_params().width()
|| video_params().height() != params_.video_params().height()) {
video_force_size = QSize(params_.video_params().width(), params_.video_params().height());
if (params_.video_scaling_method() != ExportParams::kStretch) {
video_force_matrix = ExportParams::GenerateMatrix(params_.video_scaling_method(),
vp.width(),
vp.height(),
video_params().width(),
video_params().height(),
params_.video_params().width(),
params_.video_params().height());
}
+9 -9
View File
@@ -60,7 +60,7 @@ bool LoadOTIOTask::Run()
auto root = OTIO::SerializableObjectWithMetadata::from_json_file(GetFilename().toStdString(), &es);
if (es != OTIO::ErrorStatus::OK) {
if (es.outcome != OTIO::ErrorStatus::Outcome::OK) {
SetError(tr("Failed to load OpenTimelineIO from file \"%1\"").arg(GetFilename()));
return false;
}
@@ -156,7 +156,7 @@ bool LoadOTIOTask::Run()
// Get clips from track
auto clip_map = otio_track->children();
if (es != OTIO::ErrorStatus::OK) {
if (es.outcome != OTIO::ErrorStatus::Outcome::OK) {
SetError(tr("Failed to load clip"));
return false;
}
@@ -233,7 +233,7 @@ bool LoadOTIOTask::Run()
block->setParent(sequence->parent());
// Position transition in its own context
sequence->parent()->SetNodePosition(block, block, QPointF(0, 0));
block->SetNodePositionInContext(block, QPointF(0, 0));
}
if (otio_block->schema_name() == "Gap") {
@@ -241,7 +241,7 @@ bool LoadOTIOTask::Run()
block->setParent(sequence->parent());
// Position transition in its own context
sequence->parent()->SetNodePosition(block, block, QPointF(0, 0));
block->SetNodePositionInContext(block, QPointF(0, 0));
}
// Update this after it's used but before any continue statements
@@ -269,7 +269,7 @@ bool LoadOTIOTask::Run()
QFileInfo info(probed_item->filename());
probed_item->SetLabel(info.fileName());
FolderAddChild add(sequence_footage, probed_item, true);
FolderAddChild add(sequence_footage, probed_item);
add.redo_now();
}
@@ -277,10 +277,10 @@ bool LoadOTIOTask::Run()
block->setParent(sequence->parent());
// Position clip in its own context
sequence->parent()->SetNodePosition(block, block, QPointF(0, 0));
block->SetNodePositionInContext(block, QPointF(0, 0));
// Position footage in its context
sequence->parent()->SetNodePosition(probed_item, block, QPointF(-2, 0));
block->SetNodePositionInContext(probed_item, QPointF(-2, 0));
if (track->type() == Track::kVideo) {
@@ -289,14 +289,14 @@ bool LoadOTIOTask::Run()
Node::ConnectEdge(probed_item, NodeInput(transform, TransformDistortNode::kTextureInput));
Node::ConnectEdge(transform, NodeInput(block, ClipBlock::kBufferIn));
sequence->parent()->SetNodePosition(transform, block, QPointF(-1, 0));
block->SetNodePositionInContext(transform, QPointF(-1, 0));
} else {
VolumeNode* volume_node = new VolumeNode();
volume_node->setParent(sequence->parent());
Node::ConnectEdge(probed_item, NodeInput(volume_node, VolumeNode::kSamplesInput));
Node::ConnectEdge(volume_node, NodeInput(block, ClipBlock::kBufferIn));
sequence->parent()->SetNodePosition(volume_node, block, QPointF(-1, 0));
block->SetNodePositionInContext(volume_node, QPointF(-1, 0));
}
}
}
+51 -11
View File
@@ -91,7 +91,7 @@ bool SaveOTIOTask::Run()
}
}
return (es == OTIO::ErrorStatus::OK);
return (es.outcome == OTIO::ErrorStatus::Outcome::OK);
}
OTIO::Timeline *SaveOTIOTask::SerializeTimeline(Sequence *sequence)
@@ -102,8 +102,13 @@ OTIO::Timeline *SaveOTIOTask::SerializeTimeline(Sequence *sequence)
// Suppress unused variable warning
Q_UNUSED(timeline_retainer);
if (!SerializeTrackList(sequence->track_list(Track::kVideo), otio_timeline)
|| !SerializeTrackList(sequence->track_list(Track::kAudio), otio_timeline)) {
double rate = sequence->GetVideoParams().frame_rate().toDouble();
if (qIsNaN(rate)) {
return nullptr;
}
if (!SerializeTrackList(sequence->track_list(Track::kVideo), otio_timeline, rate)
|| !SerializeTrackList(sequence->track_list(Track::kAudio), otio_timeline, rate)) {
otio_timeline->possibly_delete();
return nullptr;
}
@@ -111,7 +116,7 @@ OTIO::Timeline *SaveOTIOTask::SerializeTimeline(Sequence *sequence)
return otio_timeline;
}
OTIO::Track *SaveOTIOTask::SerializeTrack(Track *track)
OTIO::Track *SaveOTIOTask::SerializeTrack(Track *track, double sequence_rate, rational max_track_length)
{
auto otio_track = new OTIO::Track();
@@ -135,12 +140,26 @@ OTIO::Track *SaveOTIOTask::SerializeTrack(Track *track)
if (dynamic_cast<ClipBlock*>(block)) {
auto otio_clip = new OTIO::Clip(block->GetLabel().toStdString());
otio_clip->set_source_range(OTIO::TimeRange(block->in().toRationalTime(),
block->length().toRationalTime()));
otio_clip->set_source_range(OTIO::TimeRange(block->in().toRationalTime(sequence_rate),
block->length().toRationalTime(sequence_rate)));
QVector<Footage*> media_nodes = block->FindInputNodes<Footage>();
if (!media_nodes.isEmpty()) {
auto media_ref = new OTIO::ExternalReference(media_nodes.first()->filename().toStdString());
OTIO::TimeRange available_range;
if (otio_track->kind().compare("Video") == 0) {
// OTIO ExternalReference uses the source clips frame rate (or sample rate) as opposed to
// the sequences rate
double source_frame_rate = static_cast<ClipBlock*>(block)->connected_viewer()->GetVideoParams().frame_rate().toDouble();
available_range = OTIO::TimeRange(OTIO::RationalTime(0, source_frame_rate),
OTIO::RationalTime(media_nodes.first()->GetVideoParams().duration(),
source_frame_rate));
} else if (otio_track->kind().compare("Audio") == 0) {
available_range = OTIO::TimeRange(OTIO::RationalTime(0, media_nodes.first()->GetAudioParams().sample_rate()),
OTIO::RationalTime(media_nodes.first()->GetAudioParams().duration(),
media_nodes.first()->GetAudioParams().sample_rate()));
}
auto media_ref = new OTIO::ExternalReference(media_nodes.first()->filename().toStdString(), available_range);
otio_clip->set_media_reference(media_ref);
}
@@ -168,7 +187,20 @@ OTIO::Track *SaveOTIOTask::SerializeTrack(Track *track)
otio_track->append_child(otio_block, &es);
if (es != OTIO::ErrorStatus::OK) {
if (es.outcome != OTIO::ErrorStatus::Outcome::OK) {
goto fail;
}
}
// All OTIO tracks must have the same duration so we add a Gap to fill the remaining time
if (otio_track->duration(&es).to_seconds() < max_track_length.toDouble()) {
double time_left = max_track_length.toDouble() - otio_track->duration(&es).to_seconds();
OTIO::Gap* gap = new OTIO::Gap(OTIO::TimeRange(otio_track->duration(&es),
OTIO::RationalTime(time_left, 1.0)));
otio_track->append_child(gap, &es);
if (es.outcome != OTIO::ErrorStatus::Outcome::OK) {
goto fail;
}
}
@@ -181,12 +213,20 @@ fail:
return nullptr;
}
bool SaveOTIOTask::SerializeTrackList(TrackList *list, OTIO::Timeline* otio_timeline)
bool SaveOTIOTask::SerializeTrackList(TrackList *list, OTIO::Timeline* otio_timeline, double sequence_rate)
{
OTIO::ErrorStatus es;
rational max_track_length = RATIONAL_MIN;
foreach (Track* track, list->GetTracks()) {
auto otio_track = SerializeTrack(track);
if (track->track_length() > max_track_length) {
max_track_length = track->track_length();
}
}
foreach (Track* track, list->GetTracks()) {
auto otio_track = SerializeTrack(track, sequence_rate, max_track_length);
if (!otio_track) {
return false;
@@ -194,7 +234,7 @@ bool SaveOTIOTask::SerializeTrackList(TrackList *list, OTIO::Timeline* otio_time
otio_timeline->tracks()->append_child(otio_track, &es);
if (es != OTIO::ErrorStatus::OK) {
if (es.outcome != OTIO::ErrorStatus::Outcome::OK) {
otio_track->possibly_delete();
return false;
}
+2 -2
View File
@@ -44,9 +44,9 @@ protected:
private:
OTIO::Timeline* SerializeTimeline(Sequence* sequence);
OTIO::Track* SerializeTrack(Track* track);
OTIO::Track* SerializeTrack(Track* track, double sequence_rate, rational max_track_length);
bool SerializeTrackList(TrackList* list, OTIO::Timeline *otio_timeline);
bool SerializeTrackList(TrackList* list, OTIO::Timeline *otio_timeline, double sequence_rate);
Project* project_;
+735 -267
View File
File diff suppressed because it is too large Load Diff
+2901 -2888
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -76,6 +76,7 @@ QIcon icon::Clock;
QIcon icon::Diamond;
QIcon icon::Plus;
QIcon icon::Minus;
QIcon icon::AddEffect;
void icon::LoadAll(const QString& theme)
{
@@ -129,6 +130,7 @@ void icon::LoadAll(const QString& theme)
Diamond = Create(theme, "diamond");
Plus = Create(theme, "plus");
Minus = Create(theme, "minus");
AddEffect = Create(theme, "add-effect");
}
QIcon icon::Create(const QString& theme, const QString &name)
+1
View File
@@ -86,6 +86,7 @@ extern QIcon Clock;
extern QIcon Diamond;
extern QIcon Plus;
extern QIcon Minus;
extern QIcon AddEffect;
/**
* @brief Create an icon object loaded from file
+17 -21
View File
@@ -24,34 +24,24 @@
namespace olive {
MultiUndoCommand::MultiUndoCommand() :
done_(false)
{
}
void MultiUndoCommand::redo()
{
if (!done_) {
for (auto it=children_.cbegin(); it!=children_.cend(); it++) {
(*it)->redo_and_set_modified();
}
done_ = true;
for (auto it=children_.cbegin(); it!=children_.cend(); it++) {
(*it)->redo_and_set_modified();
}
}
void MultiUndoCommand::undo()
{
if (done_) {
for (auto it=children_.crbegin(); it!=children_.crend(); it++) {
(*it)->undo_and_set_modified();
}
done_ = false;
for (auto it=children_.crbegin(); it!=children_.crend(); it++) {
(*it)->undo_and_set_modified();
}
}
UndoCommand::UndoCommand()
{
prepared_ = false;
done_ = false;
}
void UndoCommand::redo_and_set_modified()
@@ -76,17 +66,23 @@ void UndoCommand::undo_and_set_modified()
void UndoCommand::redo_now()
{
if (!prepared_) {
prepare();
prepared_ = true;
}
if (!done_) {
if (!prepared_) {
prepare();
prepared_ = true;
}
redo();
redo();
done_ = true;
}
}
void UndoCommand::undo_now()
{
undo();
if (done_) {
undo();
done_ = false;
}
}
}
+3 -3
View File
@@ -75,12 +75,14 @@ private:
bool prepared_;
bool done_;
};
class MultiUndoCommand : public UndoCommand
{
public:
MultiUndoCommand();
MultiUndoCommand() = default;
virtual Project* GetRelevantProject() const override
{
@@ -109,8 +111,6 @@ protected:
private:
std::vector<UndoCommand*> children_;
bool done_;
};
}
-1
View File
@@ -51,7 +51,6 @@ add_subdirectory(timelinewidget)
add_subdirectory(timeruler)
add_subdirectory(timetarget)
add_subdirectory(toolbar)
add_subdirectory(videoparamedit)
add_subdirectory(viewer)
set(OLIVE_SOURCES
+1 -1
View File
@@ -178,7 +178,7 @@ void ColorValuesWidget::ColorPickedBtnToggled(bool e)
qApp->removeEventFilter(this);
// Restore original color (or use overridden color from eventFilter)
SetColor(picker_end_color_);
SetReferenceColor(picker_end_color_);
emit ColorChanged(picker_end_color_);
}
}
+2 -4
View File
@@ -16,11 +16,9 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/curvewidget/beziercontrolpointitem.h
widget/curvewidget/beziercontrolpointitem.cpp
widget/curvewidget/curveview.h
widget/curvewidget/curveview.cpp
widget/curvewidget/curvewidget.h
widget/curvewidget/curveview.h
widget/curvewidget/curvewidget.cpp
widget/curvewidget/curvewidget.h
PARENT_SCOPE
)
@@ -1,106 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "beziercontrolpointitem.h"
#include <QApplication>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QWidget>
#include "common/qtutils.h"
namespace olive {
BezierControlPointItem::BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) :
QGraphicsRectItem(parent),
key_(key),
mode_(mode),
x_scale_(1.0),
y_scale_(1.0)
{
setFlag(QGraphicsItem::ItemIsMovable);
connect(key, &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos);
if (mode_ == NodeKeyframe::kInHandle) {
connect(key, &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos);
} else {
connect(key, &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos);
}
int control_point_size = QtUtils::QFontMetricsWidth(qApp->fontMetrics(), "o");
int half_sz = control_point_size / 2;
setRect(-half_sz, -half_sz, control_point_size, control_point_size);
}
void BezierControlPointItem::SetXScale(double scale)
{
x_scale_ = scale;
UpdatePos();
}
void BezierControlPointItem::SetYScale(double scale)
{
y_scale_ = scale;
UpdatePos();
}
NodeKeyframe* BezierControlPointItem::key() const
{
return key_;
}
const NodeKeyframe::BezierType &BezierControlPointItem::mode() const
{
return mode_;
}
QPointF BezierControlPointItem::GetCorrespondingKeyframeHandle() const
{
return key_->bezier_control(mode_);
}
void BezierControlPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
if (option->state & QStyle::State_Selected) {
painter->setPen(widget->palette().highlight().color());
} else {
painter->setPen(widget->palette().text().color());
}
painter->drawEllipse(rect());
}
void BezierControlPointItem::UpdatePos()
{
QPointF handle_offset = GetCorrespondingKeyframeHandle();
// Scale handle offset
handle_offset.setX(handle_offset.x() * x_scale_);
// Flip the Y coordinate because bezier curves are drawn bottom to top
handle_offset.setY(-handle_offset.y() * y_scale_);
setPos(handle_offset - rect().center());
}
}
@@ -1,68 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 BEZIERCONTROLPOINTITEM_H
#define BEZIERCONTROLPOINTITEM_H
#include <QGraphicsRectItem>
#include "node/keyframe.h"
namespace olive {
class BezierControlPointItem : public QObject, public QGraphicsRectItem
{
public:
BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr);
void SetXScale(double scale);
void SetYScale(double scale);
NodeKeyframe* key() const;
const NodeKeyframe::BezierType& mode() const;
QPointF GetCorrespondingKeyframeHandle() const;
void SetCorrespondingKeyframeHandle(const QPointF& handle);
void SetOpposingKeyframeHandle(const QPointF& handle);
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
private:
NodeKeyframe* key_;
NodeKeyframe::BezierType mode_;
double x_scale_;
double y_scale_;
private slots:
void UpdatePos();
};
}
#endif // BEZIERCONTROLPOINTITEM_H

Some files were not shown because too many files have changed in this diff Show More