Merge branch 'master' into OTIO_load
This commit is contained in:
+13
-16
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
+5
-3
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -47,6 +47,8 @@ Block::Block() :
|
||||
IgnoreHashingFrom(kLengthInput);
|
||||
|
||||
AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
SetFlags(kDontShowInParamView);
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> Block::Category() const
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
)
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
* @brief Methods of interpolation to use with this keyframe
|
||||
*/
|
||||
enum Type {
|
||||
kInvalid = -1,
|
||||
kLinear,
|
||||
kHold,
|
||||
kBezier
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ public:
|
||||
|
||||
void Upload(void* data, int linesize);
|
||||
|
||||
void Download(void* data, int linesize);
|
||||
|
||||
bool IsDummy() const
|
||||
{
|
||||
return !renderer_;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+2901
-2888
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user