app: migrate all engine access to the C ABI facade (nm U _ZN5olive = 0)
Every app module now reaches liboakengine exclusively through oakengine_* C calls, EngineEventBridge subscriptions and app-side handle headers (cliphandle/keyframehandle/nodevaluehandle/oakvaluehelper). Direct C++ command construction, engine signal connect()s, and engine type usage in MOC-visible signatures are gone: 557 -> 0 undefined olive:: symbols in oak-editor.
This commit is contained in:
@@ -19,6 +19,33 @@
|
|||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
core.h
|
core.h
|
||||||
core.cpp
|
core.cpp
|
||||||
|
engineeventbridge.h
|
||||||
|
engineeventbridge.cpp
|
||||||
|
common/htmlapp.cpp
|
||||||
|
common/filefunctionsapp.cpp
|
||||||
|
common/colorcodingapp.h
|
||||||
|
common/colorcodingapp.cpp
|
||||||
|
common/xmlutilsapp.cpp
|
||||||
|
common/hashstreamapp.cpp
|
||||||
|
common/qtutilsapp.cpp
|
||||||
|
common/nodevaluehandle.h
|
||||||
|
)
|
||||||
|
|
||||||
|
# The app-side *app.cpp helpers define symbols whose qualified names also
|
||||||
|
# exist in liboakengine.so (B10 moved utilities). Build them with hidden
|
||||||
|
# visibility so the executable's definitions are not ELF-interposable: the
|
||||||
|
# engine library keeps binding to its own copies and static data members
|
||||||
|
# (ColorCoding::colors, Html::k_block_tags) are not double-initialized and
|
||||||
|
# double-destroyed at process exit (was the exit-time heap corruption in
|
||||||
|
# timeline-tests / olive-gtest).
|
||||||
|
set_source_files_properties(
|
||||||
|
common/htmlapp.cpp
|
||||||
|
common/filefunctionsapp.cpp
|
||||||
|
common/colorcodingapp.cpp
|
||||||
|
common/xmlutilsapp.cpp
|
||||||
|
common/hashstreamapp.cpp
|
||||||
|
common/qtutilsapp.cpp
|
||||||
|
PROPERTIES COMPILE_OPTIONS "-fvisibility=hidden"
|
||||||
)
|
)
|
||||||
|
|
||||||
#set(OLIVE_RESOURCES)
|
#set(OLIVE_RESOURCES)
|
||||||
@@ -27,6 +54,7 @@ set(OLIVE_SOURCES
|
|||||||
add_subdirectory(dialog)
|
add_subdirectory(dialog)
|
||||||
add_subdirectory(packaging)
|
add_subdirectory(packaging)
|
||||||
add_subdirectory(panel)
|
add_subdirectory(panel)
|
||||||
|
add_subdirectory(timeline)
|
||||||
add_subdirectory(ts)
|
add_subdirectory(ts)
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
add_subdirectory(widget)
|
add_subdirectory(widget)
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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 "common/colorcodingapp.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
QVector<Color> ColorCoding::colors = {
|
||||||
|
Color(0.545f, 0.255f, 0.255f), Color(0.412f, 0.188f, 0.259f),
|
||||||
|
Color(0.561f, 0.427f, 0.239f), Color(0.486f, 0.306f, 0.235f),
|
||||||
|
Color(0.631f, 0.612f, 0.212f), Color(0.404f, 0.478f, 0.243f),
|
||||||
|
Color(0.349f, 0.576f, 0.275f), Color(0.224f, 0.459f, 0.251f),
|
||||||
|
Color(0.259f, 0.471f, 0.541f), Color(0.184f, 0.376f, 0.329f),
|
||||||
|
Color(0.259f, 0.365f, 0.541f), Color(0.196f, 0.216f, 0.412f),
|
||||||
|
Color(0.612f, 0.294f, 0.502f), Color(0.404f, 0.220f, 0.459f),
|
||||||
|
Color(0.800f, 0.800f, 0.800f), Color(0.502f, 0.502f, 0.502f)
|
||||||
|
};
|
||||||
|
|
||||||
|
const QVector<Color> &ColorCoding::standard_colors()
|
||||||
|
{
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ColorCoding::get_color_name(int c)
|
||||||
|
{
|
||||||
|
switch (c) {
|
||||||
|
case k_red: return QObject::tr("Red");
|
||||||
|
case k_maroon: return QObject::tr("Maroon");
|
||||||
|
case k_orange: return QObject::tr("Orange");
|
||||||
|
case k_brown: return QObject::tr("Brown");
|
||||||
|
case k_yellow: return QObject::tr("Yellow");
|
||||||
|
case k_olive: return QObject::tr("Olive");
|
||||||
|
case k_lime: return QObject::tr("Lime");
|
||||||
|
case k_green: return QObject::tr("Green");
|
||||||
|
case k_cyan: return QObject::tr("Cyan");
|
||||||
|
case k_teal: return QObject::tr("Teal");
|
||||||
|
case k_blue: return QObject::tr("Blue");
|
||||||
|
case k_navy: return QObject::tr("Navy");
|
||||||
|
case k_pink: return QObject::tr("Pink");
|
||||||
|
case k_purple: return QObject::tr("Purple");
|
||||||
|
case k_silver: return QObject::tr("Silver");
|
||||||
|
case k_gray: return QObject::tr("Gray");
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color ColorCoding::get_color(int c)
|
||||||
|
{
|
||||||
|
return colors.at(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::GlobalColor ColorCoding::get_ui_selector_color(const Color &c)
|
||||||
|
{
|
||||||
|
if (c.get_rough_luminance() > 0.40f) {
|
||||||
|
return Qt::black;
|
||||||
|
} else {
|
||||||
|
return Qt::white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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 OAK_COLORCODINGAPP_H
|
||||||
|
#define OAK_COLORCODINGAPP_H
|
||||||
|
|
||||||
|
#include <olive/core/core.h>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief App-side ColorCoding (moved from engine/ui/colorcoding.h)
|
||||||
|
*
|
||||||
|
* Provides the same static color-label mapping as the engine version but
|
||||||
|
* without QObject inheritance (no moc symbols). Only the static methods
|
||||||
|
* used by app code are included.
|
||||||
|
*/
|
||||||
|
class ColorCoding {
|
||||||
|
public:
|
||||||
|
enum Code {
|
||||||
|
k_red,
|
||||||
|
k_maroon,
|
||||||
|
k_orange,
|
||||||
|
k_brown,
|
||||||
|
k_yellow,
|
||||||
|
k_olive,
|
||||||
|
k_lime,
|
||||||
|
k_green,
|
||||||
|
k_cyan,
|
||||||
|
k_teal,
|
||||||
|
k_blue,
|
||||||
|
k_navy,
|
||||||
|
k_pink,
|
||||||
|
k_purple,
|
||||||
|
k_silver,
|
||||||
|
k_gray
|
||||||
|
};
|
||||||
|
|
||||||
|
static QString get_color_name(int c);
|
||||||
|
|
||||||
|
static Color get_color(int c);
|
||||||
|
|
||||||
|
static Qt::GlobalColor get_ui_selector_color(const Color &c);
|
||||||
|
|
||||||
|
static const QVector<Color> &standard_colors();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QVector<Color> colors;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace olive
|
||||||
|
|
||||||
|
#endif // OAK_COLORCODINGAPP_H
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2022 Olive Team
|
||||||
|
Modifications Copyright (C) 2025 mikesolar
|
||||||
|
|
||||||
|
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 OAK_CONFIGWRAPPER_H
|
||||||
|
#define OAK_CONFIGWRAPPER_H
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "olive/core/util/rational.h"
|
||||||
|
#include "oakengine/config.h"
|
||||||
|
|
||||||
|
// Facade migration B9b: replace the engine's OAK_CONFIG macro (which
|
||||||
|
// references olive::Config::current()/operator[] and brings C++ symbols into
|
||||||
|
// the editor binary) with a thin header-only wrapper around the C ABI.
|
||||||
|
//
|
||||||
|
// Include this header instead of "config/config.h" in app code. It undefines
|
||||||
|
// the engine macros and redefines them to return an inline OakConfigValue that
|
||||||
|
// forwards reads/writes to oakengine_config_*().
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
class OakConfigValue {
|
||||||
|
public:
|
||||||
|
explicit OakConfigValue(const QString &key) : key_(key) {}
|
||||||
|
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return oakengine_config_get_int(key_utf8(), 0) != 0;
|
||||||
|
}
|
||||||
|
operator int() const
|
||||||
|
{
|
||||||
|
return static_cast<int>(oakengine_config_get_int(key_utf8(), 0));
|
||||||
|
}
|
||||||
|
operator qint64() const
|
||||||
|
{
|
||||||
|
return static_cast<qint64>(oakengine_config_get_int(key_utf8(), 0));
|
||||||
|
}
|
||||||
|
operator quint64() const
|
||||||
|
{
|
||||||
|
return static_cast<quint64>(oakengine_config_get_int(key_utf8(), 0));
|
||||||
|
}
|
||||||
|
operator int64_t() const
|
||||||
|
{
|
||||||
|
return oakengine_config_get_int(key_utf8(), 0);
|
||||||
|
}
|
||||||
|
operator uint64_t() const
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(oakengine_config_get_int(key_utf8(), 0));
|
||||||
|
}
|
||||||
|
operator QString() const
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
const int len = oakengine_config_get_string(key_utf8(), buf,
|
||||||
|
sizeof(buf));
|
||||||
|
return QString::fromUtf8(buf, len);
|
||||||
|
}
|
||||||
|
operator QVariant() const
|
||||||
|
{
|
||||||
|
return QVariant(static_cast<QString>(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
OakConfigValue &operator=(bool v)
|
||||||
|
{
|
||||||
|
oakengine_config_set_int(key_utf8(), v ? 1 : 0);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(int v)
|
||||||
|
{
|
||||||
|
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(uint v)
|
||||||
|
{
|
||||||
|
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(qint64 v)
|
||||||
|
{
|
||||||
|
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(quint64 v)
|
||||||
|
{
|
||||||
|
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(int64_t v)
|
||||||
|
{
|
||||||
|
oakengine_config_set_int(key_utf8(), v);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(uint64_t v)
|
||||||
|
{
|
||||||
|
oakengine_config_set_int(key_utf8(), static_cast<int64_t>(v));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(const QString &v)
|
||||||
|
{
|
||||||
|
const QByteArray utf8 = v.toUtf8();
|
||||||
|
oakengine_config_set_string(key_utf8(), utf8.constData());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(const char *v)
|
||||||
|
{
|
||||||
|
oakengine_config_set_string(key_utf8(), v ? v : "");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OakConfigValue &operator=(const QVariant &v)
|
||||||
|
{
|
||||||
|
switch (v.typeId()) {
|
||||||
|
case QMetaType::Bool:
|
||||||
|
*this = v.toBool();
|
||||||
|
break;
|
||||||
|
case QMetaType::Int:
|
||||||
|
case QMetaType::UInt:
|
||||||
|
case QMetaType::LongLong:
|
||||||
|
case QMetaType::ULongLong:
|
||||||
|
case QMetaType::Long:
|
||||||
|
case QMetaType::Short:
|
||||||
|
case QMetaType::Char:
|
||||||
|
case QMetaType::ULong:
|
||||||
|
case QMetaType::UShort:
|
||||||
|
case QMetaType::UChar:
|
||||||
|
*this = v.toLongLong();
|
||||||
|
break;
|
||||||
|
case QMetaType::Double:
|
||||||
|
case QMetaType::Float:
|
||||||
|
*this = static_cast<int64_t>(v.toDouble());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*this = v.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool toBool() const { return static_cast<bool>(*this); }
|
||||||
|
int toInt() const { return static_cast<int>(*this); }
|
||||||
|
qint64 toLongLong() const { return static_cast<qint64>(*this); }
|
||||||
|
quint64 toULongLong() const { return static_cast<quint64>(*this); }
|
||||||
|
QString toString() const { return static_cast<QString>(*this); }
|
||||||
|
|
||||||
|
bool operator==(int rhs) const { return toInt() == rhs; }
|
||||||
|
bool operator!=(int rhs) const { return toInt() != rhs; }
|
||||||
|
bool operator==(qint64 rhs) const { return toLongLong() == rhs; }
|
||||||
|
bool operator!=(qint64 rhs) const { return toLongLong() != rhs; }
|
||||||
|
bool operator==(const QString &rhs) const { return toString() == rhs; }
|
||||||
|
bool operator!=(const QString &rhs) const { return toString() != rhs; }
|
||||||
|
bool operator==(const char *rhs) const { return toString() == QString::fromUtf8(rhs); }
|
||||||
|
bool operator!=(const char *rhs) const { return toString() != QString::fromUtf8(rhs); }
|
||||||
|
|
||||||
|
template <typename T> T value() const
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, olive::core::Rational>) {
|
||||||
|
const QString s = static_cast<QString>(*this);
|
||||||
|
const QByteArray utf8 = s.toUtf8();
|
||||||
|
return olive::core::Rational::from_string(
|
||||||
|
std::string(utf8.constData(), size_t(utf8.size())));
|
||||||
|
} else {
|
||||||
|
return static_cast<T>(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char *key_utf8() const
|
||||||
|
{
|
||||||
|
key_utf8_ = key_.toUtf8();
|
||||||
|
return key_utf8_.constData();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString key_;
|
||||||
|
mutable QByteArray key_utf8_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace olive
|
||||||
|
|
||||||
|
#ifdef OAK_CONFIG
|
||||||
|
#undef OAK_CONFIG
|
||||||
|
#endif
|
||||||
|
#ifdef OAK_CONFIG_STR
|
||||||
|
#undef OAK_CONFIG_STR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define OAK_CONFIG(x) olive::OakConfigValue(QStringLiteral(x))
|
||||||
|
#define OAK_CONFIG_STR(x) olive::OakConfigValue(x)
|
||||||
|
|
||||||
|
#endif // OAK_CONFIGWRAPPER_H
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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 OAK_DEBUGAPP_H
|
||||||
|
#define OAK_DEBUGAPP_H
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief App-side debug handler (moved from engine/common/debug.cpp)
|
||||||
|
*
|
||||||
|
* Replaces engine's olive::debug_handler so oak-editor doesn't import
|
||||||
|
* that symbol. Only used in main.cpp's qInstallMessageHandler.
|
||||||
|
*/
|
||||||
|
static void debug_handler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
|
{
|
||||||
|
// Suppress noisy warnings from Qt's QXcbIntegration
|
||||||
|
if (type == QtWarningMsg && msg.contains("QXcbIntegration")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suppress all Qt warnings during automated testing
|
||||||
|
static const bool is_testing = qEnvironmentVariableIsSet("OAK_TESTING");
|
||||||
|
if (is_testing && type == QtWarningMsg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString log_line;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case QtDebugMsg:
|
||||||
|
log_line = QStringLiteral("Debug: %1 (%2:%3, %4)\n");
|
||||||
|
break;
|
||||||
|
case QtInfoMsg:
|
||||||
|
log_line = QStringLiteral("Info: %1 (%2:%3, %4)\n");
|
||||||
|
break;
|
||||||
|
case QtWarningMsg:
|
||||||
|
log_line = QStringLiteral("Warning: %1 (%2:%3, %4)\n");
|
||||||
|
break;
|
||||||
|
case QtCriticalMsg:
|
||||||
|
log_line = QStringLiteral("Critical: %1 (%2:%3, %4)\n");
|
||||||
|
break;
|
||||||
|
case QtFatalMsg:
|
||||||
|
log_line = QStringLiteral("Fatal: %1 (%2:%3, %4)\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_line = log_line.arg(msg, context.file != nullptr ? context.file : "<null>",
|
||||||
|
QString::number(context.line), context.function != nullptr ?
|
||||||
|
context.function : "<null>");
|
||||||
|
|
||||||
|
std::cerr << log_line.toUtf8().constData();
|
||||||
|
|
||||||
|
if (type == QtFatalMsg) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace olive
|
||||||
|
|
||||||
|
#endif // OAK_DEBUGAPP_H
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
// App-side implementations of FileFunctions methods that would otherwise
|
||||||
|
// be imported from liboakengine. The declarations live in the engine header
|
||||||
|
// (common/filefunctions.h) which is on the public include path; these
|
||||||
|
// definitions resolve the symbols locally in the app binary.
|
||||||
|
|
||||||
|
#include "common/filefunctions.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
bool FileFunctions::directory_is_valid(const QDir &d,
|
||||||
|
bool try_to_create_if_not_exists)
|
||||||
|
{
|
||||||
|
return d.exists() ||
|
||||||
|
(try_to_create_if_not_exists && d.mkpath(QStringLiteral(".")));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileFunctions::read_file_as_string(const QString &filename)
|
||||||
|
{
|
||||||
|
QFile f(filename);
|
||||||
|
QString file_data;
|
||||||
|
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
QTextStream text_stream(&f);
|
||||||
|
file_data = text_stream.readAll();
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
return file_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileFunctions::get_auto_recovery_root()
|
||||||
|
{
|
||||||
|
return QDir(QStandardPaths::writableLocation(
|
||||||
|
QStandardPaths::AppLocalDataLocation))
|
||||||
|
.filePath(QStringLiteral("autorecovery"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileFunctions::ensure_filename_extension(QString fn,
|
||||||
|
const QString &extension)
|
||||||
|
{
|
||||||
|
if (!fn.isEmpty() && !extension.isEmpty()) {
|
||||||
|
QString extension_with_dot;
|
||||||
|
extension_with_dot.append('.');
|
||||||
|
extension_with_dot.append(extension);
|
||||||
|
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
||||||
|
fn.append(extension_with_dot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileFunctions::get_configuration_location()
|
||||||
|
{
|
||||||
|
if (is_portable()) {
|
||||||
|
return get_application_path();
|
||||||
|
} else {
|
||||||
|
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
|
QDir(s).mkpath(".");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileFunctions::is_portable()
|
||||||
|
{
|
||||||
|
return QFileInfo::exists(QDir(get_application_path()).filePath("portable"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileFunctions::get_application_path()
|
||||||
|
{
|
||||||
|
return QCoreApplication::applicationDirPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
// App-side implementations of qHash overloads and stream operators
|
||||||
|
// used by QHash containers and QDataStream serialization in app code.
|
||||||
|
// Provides local definitions so the app doesn't import these from liboakengine.
|
||||||
|
|
||||||
|
#include "node/param.h"
|
||||||
|
#include "node/output/track/track.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
uint qHash(const NodeInput &i)
|
||||||
|
{
|
||||||
|
return qHash(i.node()) ^ qHash(i.input()) ^ ::qHash(i.element());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const NodeInputPair &i)
|
||||||
|
{
|
||||||
|
return qHash(i.node) ^ qHash(i.input);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const NodeKeyframeTrackReference &i)
|
||||||
|
{
|
||||||
|
return qHash(i.input()) ^ ::qHash(i.track());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const Track::Reference &r, uint seed)
|
||||||
|
{
|
||||||
|
return ::qHash(QStringLiteral("%1:%2").arg(QString::number(r.type()),
|
||||||
|
QString::number(r.index())),
|
||||||
|
seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream &operator<<(QDataStream &out, const Track::Reference &ref)
|
||||||
|
{
|
||||||
|
out << static_cast<int>(ref.type()) << ref.index();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream &operator>>(QDataStream &in, Track::Reference &ref)
|
||||||
|
{
|
||||||
|
int type, index;
|
||||||
|
in >> type >> index;
|
||||||
|
ref = Track::Reference(static_cast<Track::Type>(type), index);
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,507 @@
|
|||||||
|
/*
|
||||||
|
* Oak Video Editor - Non-Linear Video Editor
|
||||||
|
* Copyright (C) 2025 Olive CE 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 "htmlapp.h"
|
||||||
|
|
||||||
|
#include <QAbstractTextDocumentLayout>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <QTextBlockFormat>
|
||||||
|
#include <QTextCharFormat>
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QTextFragment>
|
||||||
|
#include <QTextList>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
|
#include "common/xmlutils.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QTextBlock>
|
||||||
|
|
||||||
|
#include "common/xmlutils.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
const QVector<QString> Html::k_block_tags = { QStringLiteral("p"),
|
||||||
|
QStringLiteral("div") };
|
||||||
|
|
||||||
|
inline bool str_equals(const QStringView &a, const QStringView &b)
|
||||||
|
{
|
||||||
|
return !a.compare(b, Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Html::doc_to_html(const QTextDocument *doc)
|
||||||
|
{
|
||||||
|
QString html;
|
||||||
|
QXmlStreamWriter writer(&html);
|
||||||
|
|
||||||
|
//writer.setAutoFormatting(true);
|
||||||
|
|
||||||
|
for (auto it = doc->begin(); it != doc->end(); it = it.next()) {
|
||||||
|
write_block(&writer, it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HtmlNode {
|
||||||
|
QString tag;
|
||||||
|
QTextCharFormat format;
|
||||||
|
};
|
||||||
|
|
||||||
|
QTextCharFormat merge_html_formats(const QVector<HtmlNode> &stack)
|
||||||
|
{
|
||||||
|
QTextCharFormat f;
|
||||||
|
|
||||||
|
for (int i = 0; i < stack.size(); i++) {
|
||||||
|
f.merge(stack.at(i).format);
|
||||||
|
}
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Html::html_to_doc(QTextDocument *doc, const QString &html)
|
||||||
|
{
|
||||||
|
// Empty doc
|
||||||
|
doc->clear();
|
||||||
|
bool inside_block = true;
|
||||||
|
|
||||||
|
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
||||||
|
QTextCursor c(doc);
|
||||||
|
|
||||||
|
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
||||||
|
QXmlStreamReader reader(wrapped);
|
||||||
|
|
||||||
|
QVector<HtmlNode> fmt_stack;
|
||||||
|
|
||||||
|
QTextCharFormat default_fmt;
|
||||||
|
default_fmt.setFontWeight(QFont::Normal);
|
||||||
|
fmt_stack.append({ QStringLiteral("html"), default_fmt });
|
||||||
|
|
||||||
|
QTextCharFormat current_fmt;
|
||||||
|
|
||||||
|
while (!reader.atEnd()) {
|
||||||
|
reader.readNext();
|
||||||
|
|
||||||
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
|
QString tag = reader.name().toString().toLower();
|
||||||
|
|
||||||
|
fmt_stack.append({ tag, read_char_format(reader.attributes()) });
|
||||||
|
current_fmt = merge_html_formats(fmt_stack);
|
||||||
|
|
||||||
|
if (k_block_tags.contains(tag)) {
|
||||||
|
QTextBlockFormat block_fmt =
|
||||||
|
read_block_format(reader.attributes());
|
||||||
|
if (inside_block) {
|
||||||
|
c.setBlockFormat(block_fmt);
|
||||||
|
c.setBlockCharFormat(current_fmt);
|
||||||
|
} else {
|
||||||
|
c.insertBlock(block_fmt, current_fmt);
|
||||||
|
inside_block = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||||
|
QString characters = reader.text().toString();
|
||||||
|
c.insertText(characters, current_fmt);
|
||||||
|
|
||||||
|
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||||
|
QString tag = reader.name().toString().toLower();
|
||||||
|
|
||||||
|
for (int i = fmt_stack.size() - 1; i >= 0; i--) {
|
||||||
|
if (fmt_stack.at(i).tag == tag) {
|
||||||
|
fmt_stack.removeAt(i);
|
||||||
|
current_fmt = merge_html_formats(fmt_stack);
|
||||||
|
|
||||||
|
if (k_block_tags.contains(tag)) {
|
||||||
|
inside_block = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.error()) {
|
||||||
|
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Html::write_block(QXmlStreamWriter *writer, const QTextBlock &block)
|
||||||
|
{
|
||||||
|
writer->writeStartElement(QStringLiteral("p"));
|
||||||
|
|
||||||
|
const QTextBlockFormat &fmt = block.blockFormat();
|
||||||
|
|
||||||
|
// Write block alignment
|
||||||
|
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
||||||
|
if (fmt.alignment() & Qt::AlignRight) {
|
||||||
|
writer->writeAttribute(QStringLiteral("align"),
|
||||||
|
QStringLiteral("right"));
|
||||||
|
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
||||||
|
writer->writeAttribute(QStringLiteral("align"),
|
||||||
|
QStringLiteral("center"));
|
||||||
|
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||||
|
writer->writeAttribute(QStringLiteral("align"),
|
||||||
|
QStringLiteral("justify"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTL support
|
||||||
|
if (block.textDirection() == Qt::RightToLeft) {
|
||||||
|
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write CSS attributes
|
||||||
|
QString style;
|
||||||
|
|
||||||
|
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
||||||
|
write_css_property(&style, QStringLiteral("line-height"),
|
||||||
|
QStringLiteral("%1%").arg(fmt.lineHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
write_char_format(&style, block.charFormat());
|
||||||
|
|
||||||
|
if (!style.isEmpty()) {
|
||||||
|
writer->writeAttribute(QStringLiteral("style"), style);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = block.begin();
|
||||||
|
|
||||||
|
if (it != block.end()) {
|
||||||
|
for (; it != block.end(); it++) {
|
||||||
|
write_fragment(writer, it.fragment());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer->writeEndElement(); // p
|
||||||
|
}
|
||||||
|
|
||||||
|
void Html::write_fragment(QXmlStreamWriter *writer,
|
||||||
|
const QTextFragment &fragment)
|
||||||
|
{
|
||||||
|
const QTextCharFormat &fmt = fragment.charFormat();
|
||||||
|
|
||||||
|
writer->writeStartElement(QStringLiteral("span"));
|
||||||
|
|
||||||
|
// Write CSS attributes
|
||||||
|
QString style;
|
||||||
|
|
||||||
|
write_char_format(&style, fmt);
|
||||||
|
|
||||||
|
if (!style.isEmpty()) {
|
||||||
|
writer->writeAttribute(QStringLiteral("style"), style);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
||||||
|
bool first_line = true;
|
||||||
|
foreach (const QString &l, lines) {
|
||||||
|
if (first_line) {
|
||||||
|
first_line = false;
|
||||||
|
} else {
|
||||||
|
writer->writeEmptyElement(QStringLiteral("br"));
|
||||||
|
}
|
||||||
|
writer->writeCharacters(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer->writeEndElement(); // span
|
||||||
|
}
|
||||||
|
|
||||||
|
void Html::write_css_property(QString *style, const QString &key,
|
||||||
|
const QStringList &values)
|
||||||
|
{
|
||||||
|
QString value;
|
||||||
|
foreach (QString v, values) {
|
||||||
|
if (v.contains(' ')) {
|
||||||
|
v = QStringLiteral("'%1'").arg(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
append_string_auto_space(&value, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
append_string_auto_space(style, QStringLiteral("%1: %2;").arg(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Html::write_char_format(QString *style, const QTextCharFormat &fmt)
|
||||||
|
{
|
||||||
|
QStringList families = fmt.fontFamilies().toStringList();
|
||||||
|
if (!families.isEmpty()) {
|
||||||
|
write_css_property(style, QStringLiteral("font-family"),
|
||||||
|
families.first());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
||||||
|
write_css_property(
|
||||||
|
style, QStringLiteral("font-size"),
|
||||||
|
QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
||||||
|
write_css_property(style, QStringLiteral("font-weight"),
|
||||||
|
QString::number(fmt.fontWeight() * 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
||||||
|
write_css_property(style, QStringLiteral("font-style"),
|
||||||
|
fmt.fontItalic() ? QStringLiteral("italic") :
|
||||||
|
QStringLiteral("normal"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
||||||
|
write_css_property(style, QStringLiteral("-ove-font-style"),
|
||||||
|
fmt.fontStyleName().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList deco;
|
||||||
|
|
||||||
|
if (fmt.fontUnderline()) {
|
||||||
|
deco.append(QStringLiteral("underline"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.fontStrikeOut()) {
|
||||||
|
deco.append(QStringLiteral("line-through"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.fontOverline()) {
|
||||||
|
deco.append(QStringLiteral("overline"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deco.isEmpty()) {
|
||||||
|
write_css_property(style, QStringLiteral("text-decoration"), deco);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.foreground().style() != Qt::NoBrush) {
|
||||||
|
const QColor &color = fmt.foreground().color();
|
||||||
|
QString cs;
|
||||||
|
|
||||||
|
if (color.alpha() == 255) {
|
||||||
|
cs = color.name();
|
||||||
|
} else if (color.alpha()) {
|
||||||
|
cs = QStringLiteral("rgba(%1, %2, %3, %4)")
|
||||||
|
.arg(QString::number(color.red()),
|
||||||
|
QString::number(color.green()),
|
||||||
|
QString::number(color.blue()),
|
||||||
|
QString::number(color.alphaF()));
|
||||||
|
}
|
||||||
|
|
||||||
|
write_css_property(style, QStringLiteral("color"), cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
||||||
|
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
||||||
|
write_css_property(style, QStringLiteral("font-variant"),
|
||||||
|
QStringLiteral("small-caps"));
|
||||||
|
// TODO: Add others
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.fontLetterSpacing() != 0.0) {
|
||||||
|
write_css_property(style, QStringLiteral("letter-spacing"),
|
||||||
|
QStringLiteral("%1%").arg(
|
||||||
|
QString::number(fmt.fontLetterSpacing())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt.fontStretch() != 0) {
|
||||||
|
write_css_property(
|
||||||
|
style, QStringLiteral("font-stretch"),
|
||||||
|
QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCharFormat Html::read_char_format(const QXmlStreamAttributes &attributes)
|
||||||
|
{
|
||||||
|
QTextCharFormat fmt;
|
||||||
|
|
||||||
|
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||||
|
if (str_equals(attr.name(), QStringLiteral("style"))) {
|
||||||
|
auto css = get_css_from_style(attr.value().toString());
|
||||||
|
|
||||||
|
for (auto it = css.begin(); it != css.end(); it++) {
|
||||||
|
const QString &first_val = it.value().first();
|
||||||
|
|
||||||
|
if (it.key() == QStringLiteral("font-family")) {
|
||||||
|
fmt.setFontFamilies({ first_val });
|
||||||
|
} else if (it.key() == QStringLiteral("font-size")) {
|
||||||
|
if (first_val.endsWith(QStringLiteral("pt"),
|
||||||
|
Qt::CaseInsensitive)) {
|
||||||
|
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||||
|
}
|
||||||
|
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||||
|
fmt.setFontWeight(first_val.toInt() / 8);
|
||||||
|
} else if (it.key() == QStringLiteral("font-style")) {
|
||||||
|
fmt.setFontItalic(
|
||||||
|
str_equals(first_val, QStringLiteral("italic")));
|
||||||
|
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||||
|
foreach (const QString &v, it.value()) {
|
||||||
|
if (str_equals(v, QStringLiteral("underline"))) {
|
||||||
|
fmt.setFontUnderline(true);
|
||||||
|
} else if (str_equals(v,
|
||||||
|
QStringLiteral("line-through"))) {
|
||||||
|
fmt.setFontStrikeOut(true);
|
||||||
|
} else if (str_equals(v, QStringLiteral("overline"))) {
|
||||||
|
fmt.setFontOverline(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (it.key() == QStringLiteral("color")) {
|
||||||
|
if (first_val.startsWith(QStringLiteral("rgba"),
|
||||||
|
Qt::CaseInsensitive)) {
|
||||||
|
QString vals_only = first_val;
|
||||||
|
vals_only.remove(QStringLiteral("rgba"));
|
||||||
|
vals_only.remove(QStringLiteral("("));
|
||||||
|
vals_only.remove(QStringLiteral(")"));
|
||||||
|
QStringList rgba = vals_only.split(',');
|
||||||
|
if (rgba.size() == 4) {
|
||||||
|
QColor c;
|
||||||
|
c.setRed(rgba.at(0).toInt()); // Writer emits 0-255 RGB (CSS rgba() convention)
|
||||||
|
c.setGreen(rgba.at(1).toInt());
|
||||||
|
c.setBlue(rgba.at(2).toInt());
|
||||||
|
c.setAlphaF(rgba.at(3).toDouble());
|
||||||
|
fmt.setForeground(c);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.setForeground(QColor(first_val));
|
||||||
|
}
|
||||||
|
} else if (it.key() == QStringLiteral("font-variant")) {
|
||||||
|
if (str_equals(first_val, QStringLiteral("small-caps"))) {
|
||||||
|
fmt.setFontCapitalization(QFont::SmallCaps);
|
||||||
|
}
|
||||||
|
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||||
|
if (first_val.contains(QChar('%'))) {
|
||||||
|
fmt.setFontLetterSpacing(
|
||||||
|
first_val.chopped(1).toDouble());
|
||||||
|
}
|
||||||
|
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||||
|
if (first_val.contains(QChar('%'))) {
|
||||||
|
fmt.setFontStretch(first_val.chopped(1).toInt());
|
||||||
|
}
|
||||||
|
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
||||||
|
fmt.setFontStyleName(first_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextBlockFormat Html::read_block_format(const QXmlStreamAttributes &attributes)
|
||||||
|
{
|
||||||
|
QTextBlockFormat block_fmt;
|
||||||
|
|
||||||
|
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||||
|
if (str_equals(attr.name(), QStringLiteral("align"))) {
|
||||||
|
if (str_equals(attr.value(), QStringLiteral("right"))) {
|
||||||
|
block_fmt.setAlignment(Qt::AlignRight);
|
||||||
|
} else if (str_equals(attr.value(), QStringLiteral("center"))) {
|
||||||
|
block_fmt.setAlignment(Qt::AlignHCenter);
|
||||||
|
} else if (str_equals(attr.value(), QStringLiteral("justify"))) {
|
||||||
|
block_fmt.setAlignment(Qt::AlignJustify);
|
||||||
|
}
|
||||||
|
} else if (str_equals(attr.name(), QStringLiteral("dir"))) {
|
||||||
|
if (str_equals(attr.value(), QStringLiteral("rtl"))) {
|
||||||
|
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
||||||
|
}
|
||||||
|
} else if (str_equals(attr.name(), QStringLiteral("style"))) {
|
||||||
|
auto css = get_css_from_style(attr.value().toString());
|
||||||
|
|
||||||
|
for (auto it = css.begin(); it != css.end(); it++) {
|
||||||
|
if (it.key() == QStringLiteral("line-height")) {
|
||||||
|
const QString &first_val = it.value().constFirst();
|
||||||
|
if (first_val.contains(QChar('%'))) {
|
||||||
|
block_fmt.setLineHeight(
|
||||||
|
first_val.chopped(1).toDouble(),
|
||||||
|
QTextBlockFormat::ProportionalHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return block_fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Html::append_string_auto_space(QString *s, const QString &append)
|
||||||
|
{
|
||||||
|
if (!s->isEmpty()) {
|
||||||
|
s->append(QChar(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
s->append(append);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QStringList> Html::get_css_from_style(const QString &s)
|
||||||
|
{
|
||||||
|
QMap<QString, QStringList> map;
|
||||||
|
|
||||||
|
QStringList list = s.split(QChar(';'));
|
||||||
|
|
||||||
|
foreach (const QString &a, list) {
|
||||||
|
QStringList kv = a.split(QChar(':'));
|
||||||
|
|
||||||
|
if (kv.size() != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
||||||
|
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
||||||
|
// match. Also commas should be filtered out.
|
||||||
|
QStringList values;
|
||||||
|
const QString &val = kv.at(1);
|
||||||
|
QChar in_quote(0);
|
||||||
|
QString current_str;
|
||||||
|
for (int i = 0; i < val.size(); i++) {
|
||||||
|
const QChar ¤t_char = val.at(i);
|
||||||
|
|
||||||
|
if (!in_quote.isNull()) {
|
||||||
|
// If inside quotes and character isn't quote, indiscriminately append char
|
||||||
|
if (current_char == in_quote) {
|
||||||
|
in_quote = QChar(0);
|
||||||
|
} else {
|
||||||
|
current_str.append(current_char);
|
||||||
|
}
|
||||||
|
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
||||||
|
// Dump current
|
||||||
|
if (!current_str.isEmpty()) {
|
||||||
|
values.append(current_str);
|
||||||
|
current_str.clear();
|
||||||
|
}
|
||||||
|
} else if (in_quote.isNull() && (current_char == QChar('\'') ||
|
||||||
|
current_char == QChar('"'))) {
|
||||||
|
in_quote = current_char;
|
||||||
|
} else {
|
||||||
|
current_str.append(current_char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current_str.isEmpty()) {
|
||||||
|
values.append(current_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
values.append(QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
map[kv.at(0).trimmed().toLower()] = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Oak Video Editor - Non-Linear Video Editor
|
||||||
|
* Copyright (C) 2025 Olive CE 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 OAK_HTMLAPP_H
|
||||||
|
#define OAK_HTML_H
|
||||||
|
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QTextFragment>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Functions for converting HTML to QTextDocument and vice versa
|
||||||
|
*
|
||||||
|
* Qt does contain its own functions for this, however they have some limitations. Some things that
|
||||||
|
* we want to support (e.g. kerning/spacing and font stretch) are not implemented in Qt's
|
||||||
|
* QTextHtmlExporter and QTextHtmlParser. Additionally, since these functions are not part of Qt's
|
||||||
|
* public API, and make many references to other parts of Qt that are not part of the public API,
|
||||||
|
* there is no way to subclass or extend their functionality without forking Qt as a whole.
|
||||||
|
*
|
||||||
|
* Therefore, it became necessary to write a custom class for the conversion so that we can
|
||||||
|
* ensure support for the features we need.
|
||||||
|
*
|
||||||
|
* If someone wishes to extend this class for more feature support, feel free to open a pull
|
||||||
|
* request. But this is NOT intended to be an exhaustive HTML implementation, and is primarily
|
||||||
|
* designed to store rich text in a standard format for the purpose of text formatting for video.
|
||||||
|
*/
|
||||||
|
class Html {
|
||||||
|
public:
|
||||||
|
static QString doc_to_html(const QTextDocument *doc);
|
||||||
|
|
||||||
|
static void html_to_doc(QTextDocument *doc, const QString &html);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void write_block(QXmlStreamWriter *writer, const QTextBlock &block);
|
||||||
|
|
||||||
|
static void write_fragment(QXmlStreamWriter *writer,
|
||||||
|
const QTextFragment &fragment);
|
||||||
|
|
||||||
|
static void write_css_property(QString *style, const QString &key,
|
||||||
|
const QStringList &value);
|
||||||
|
static void write_css_property(QString *style, const QString &key,
|
||||||
|
const QString &value)
|
||||||
|
{
|
||||||
|
write_css_property(style, key, QStringList({ value }));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_char_format(QString *style, const QTextCharFormat &fmt);
|
||||||
|
|
||||||
|
static QTextCharFormat
|
||||||
|
read_char_format(const QXmlStreamAttributes &attributes);
|
||||||
|
|
||||||
|
static QTextBlockFormat
|
||||||
|
read_block_format(const QXmlStreamAttributes &attributes);
|
||||||
|
|
||||||
|
static void append_string_auto_space(QString *s, const QString &append);
|
||||||
|
|
||||||
|
static QMap<QString, QStringList> get_css_from_style(const QString &s);
|
||||||
|
|
||||||
|
static const QVector<QString> k_block_tags;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OAK_HTML_H
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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 OAK_NODEVALUEHANDLE_H
|
||||||
|
#define OAK_NODEVALUEHANDLE_H
|
||||||
|
|
||||||
|
#include "node/value.h"
|
||||||
|
#include "oakengine/node.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert engine NodeValue::Type to oak_node_value_type (app-side).
|
||||||
|
*
|
||||||
|
* The two enums do NOT share ordinals (e.g. k_boolean=4 vs BOOL=3), so a
|
||||||
|
* plain int cast is a bug. Mirrors from_c_type() in
|
||||||
|
* engine/src/capi/node.cpp. Lives in an app header, NOT in the public
|
||||||
|
* facade headers — the C ABI surface stays pure C (see
|
||||||
|
* docs/zh/r6-cleanup-plan.md red line 3 context). Returns -1 for types the
|
||||||
|
* facade cannot represent (caller falls back to the input's declared type).
|
||||||
|
*/
|
||||||
|
inline int node_value_type_to_c(NodeValue::Type t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
case NodeValue::k_int: return OAK_NODE_VALUE_INT;
|
||||||
|
case NodeValue::k_float: return OAK_NODE_VALUE_FLOAT;
|
||||||
|
case NodeValue::k_boolean: return OAK_NODE_VALUE_BOOL;
|
||||||
|
case NodeValue::k_rational: return OAK_NODE_VALUE_RATIONAL;
|
||||||
|
case NodeValue::k_color: return OAK_NODE_VALUE_COLOR;
|
||||||
|
case NodeValue::k_vec2: return OAK_NODE_VALUE_VEC2;
|
||||||
|
case NodeValue::k_vec3: return OAK_NODE_VALUE_VEC3;
|
||||||
|
case NodeValue::k_vec4: return OAK_NODE_VALUE_VEC4;
|
||||||
|
case NodeValue::k_combo: return OAK_NODE_VALUE_COMBO;
|
||||||
|
case NodeValue::k_file: return OAK_NODE_VALUE_STRING;
|
||||||
|
case NodeValue::k_text: return OAK_NODE_VALUE_TEXT;
|
||||||
|
case NodeValue::k_font: return OAK_NODE_VALUE_FONT;
|
||||||
|
case NodeValue::k_str_combo: return OAK_NODE_VALUE_STR_COMBO;
|
||||||
|
case NodeValue::k_binary: return OAK_NODE_VALUE_BINARY;
|
||||||
|
case NodeValue::k_bezier: return OAK_NODE_VALUE_BEZIER;
|
||||||
|
case NodeValue::k_texture: return OAK_NODE_VALUE_TEXTURE;
|
||||||
|
case NodeValue::k_samples: return OAK_NODE_VALUE_SAMPLES;
|
||||||
|
case NodeValue::k_video_params: return OAK_NODE_VALUE_VIDEO_PARAMS;
|
||||||
|
case NodeValue::k_audio_params: return OAK_NODE_VALUE_AUDIO_PARAMS;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace olive
|
||||||
|
|
||||||
|
#endif // OAK_NODEVALUEHANDLE_H
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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 OAKVALUEHELPER_H
|
||||||
|
#define OAKVALUEHELPER_H
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QVector2D>
|
||||||
|
#include <QVector3D>
|
||||||
|
#include <QVector4D>
|
||||||
|
|
||||||
|
#include "node/keyframe.h"
|
||||||
|
#include "node/value.h"
|
||||||
|
#include "oakengine/node.h"
|
||||||
|
#include "olive/core/util/color.h"
|
||||||
|
|
||||||
|
namespace olive {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a per-track component QVariant into the C ABI oak_node_value POD.
|
||||||
|
*
|
||||||
|
* `type` is the declared input data type (e.g. k_float/k_color). For split-track
|
||||||
|
* types the component is the track-0 scalar (float for k_color's red channel, etc.).
|
||||||
|
* Returns false for types that have no POD representation.
|
||||||
|
*/
|
||||||
|
static inline bool QVariantToOakNodeValue(NodeValue::Type type, const QVariant &v,
|
||||||
|
oak_node_value *out)
|
||||||
|
{
|
||||||
|
memset(out, 0, sizeof(*out));
|
||||||
|
switch (type) {
|
||||||
|
case NodeValue::k_int:
|
||||||
|
case NodeValue::k_combo:
|
||||||
|
out->type = (type == NodeValue::k_combo) ? OAK_NODE_VALUE_COMBO
|
||||||
|
: OAK_NODE_VALUE_INT;
|
||||||
|
out->num = v.toLongLong();
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_float:
|
||||||
|
out->type = OAK_NODE_VALUE_FLOAT;
|
||||||
|
out->f[0] = v.toDouble();
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_boolean:
|
||||||
|
out->type = OAK_NODE_VALUE_BOOL;
|
||||||
|
out->num = v.toBool() ? 1 : 0;
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_rational:
|
||||||
|
out->type = OAK_NODE_VALUE_RATIONAL;
|
||||||
|
{
|
||||||
|
const Rational r = v.value<Rational>();
|
||||||
|
out->num = r.numerator();
|
||||||
|
out->den = r.denominator();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_color:
|
||||||
|
out->type = OAK_NODE_VALUE_COLOR;
|
||||||
|
{
|
||||||
|
const core::Color c = v.value<core::Color>();
|
||||||
|
out->f[0] = c.red();
|
||||||
|
out->f[1] = c.green();
|
||||||
|
out->f[2] = c.blue();
|
||||||
|
out->f[3] = c.alpha();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_vec2:
|
||||||
|
out->type = OAK_NODE_VALUE_VEC2;
|
||||||
|
{
|
||||||
|
const QVector2D vec = v.value<QVector2D>();
|
||||||
|
out->f[0] = vec.x();
|
||||||
|
out->f[1] = vec.y();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_vec3:
|
||||||
|
out->type = OAK_NODE_VALUE_VEC3;
|
||||||
|
{
|
||||||
|
const QVector3D vec = v.value<QVector3D>();
|
||||||
|
out->f[0] = vec.x();
|
||||||
|
out->f[1] = vec.y();
|
||||||
|
out->f[2] = vec.z();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_vec4:
|
||||||
|
out->type = OAK_NODE_VALUE_VEC4;
|
||||||
|
{
|
||||||
|
const QVector4D vec = v.value<QVector4D>();
|
||||||
|
out->f[0] = vec.x();
|
||||||
|
out->f[1] = vec.y();
|
||||||
|
out->f[2] = vec.z();
|
||||||
|
out->f[3] = vec.w();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a per-track component QVariant into the C ABI oak_node_value POD.
|
||||||
|
*
|
||||||
|
* Unlike QVariantToOakNodeValue() which takes a full normal value, this takes a
|
||||||
|
* single track's component (e.g. one float for a k_color channel). The resulting
|
||||||
|
* POD has the input's declared type with the component in f[0]/num, exactly what
|
||||||
|
* the per-track facade commands expect.
|
||||||
|
*/
|
||||||
|
static inline bool NodeTrackComponentToOakNodeValue(NodeValue::Type type,
|
||||||
|
const QVariant &v,
|
||||||
|
oak_node_value *out)
|
||||||
|
{
|
||||||
|
memset(out, 0, sizeof(*out));
|
||||||
|
switch (type) {
|
||||||
|
case NodeValue::k_int:
|
||||||
|
case NodeValue::k_combo:
|
||||||
|
out->type = (type == NodeValue::k_combo) ? OAK_NODE_VALUE_COMBO
|
||||||
|
: OAK_NODE_VALUE_INT;
|
||||||
|
out->num = v.toLongLong();
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_float:
|
||||||
|
case NodeValue::k_bezier:
|
||||||
|
out->type = OAK_NODE_VALUE_FLOAT;
|
||||||
|
out->f[0] = v.toDouble();
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_boolean:
|
||||||
|
out->type = OAK_NODE_VALUE_BOOL;
|
||||||
|
out->num = v.toBool() ? 1 : 0;
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_rational:
|
||||||
|
out->type = OAK_NODE_VALUE_RATIONAL;
|
||||||
|
{
|
||||||
|
const Rational r = v.value<Rational>();
|
||||||
|
out->num = r.numerator();
|
||||||
|
out->den = r.denominator();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_color:
|
||||||
|
out->type = OAK_NODE_VALUE_COLOR;
|
||||||
|
out->f[0] = v.toFloat();
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_vec2:
|
||||||
|
out->type = OAK_NODE_VALUE_VEC2;
|
||||||
|
out->f[0] = v.toFloat();
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_vec3:
|
||||||
|
out->type = OAK_NODE_VALUE_VEC3;
|
||||||
|
out->f[0] = v.toFloat();
|
||||||
|
return true;
|
||||||
|
case NodeValue::k_vec4:
|
||||||
|
out->type = OAK_NODE_VALUE_VEC4;
|
||||||
|
out->f[0] = v.toFloat();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a full C ABI oak_node_value POD back into a QVariant.
|
||||||
|
*
|
||||||
|
* Mirrors QVariantToOakNodeValue(). String/binary/bezier are not represented
|
||||||
|
* in the POD and return an invalid QVariant; use the dedicated string/binary/
|
||||||
|
* bezier facade getters for those.
|
||||||
|
*/
|
||||||
|
static inline QVariant OakNodeValueToQVariant(const oak_node_value &v)
|
||||||
|
{
|
||||||
|
switch (v.type) {
|
||||||
|
case OAK_NODE_VALUE_INT:
|
||||||
|
return QVariant::fromValue<qlonglong>(v.num);
|
||||||
|
case OAK_NODE_VALUE_FLOAT:
|
||||||
|
return QVariant::fromValue(v.f[0]);
|
||||||
|
case OAK_NODE_VALUE_BOOL:
|
||||||
|
return QVariant::fromValue(v.num != 0);
|
||||||
|
case OAK_NODE_VALUE_RATIONAL:
|
||||||
|
return QVariant::fromValue(
|
||||||
|
Rational(int(v.num), int(v.den)));
|
||||||
|
case OAK_NODE_VALUE_COLOR:
|
||||||
|
return QVariant::fromValue(core::Color(
|
||||||
|
float(v.f[0]), float(v.f[1]), float(v.f[2]), float(v.f[3])));
|
||||||
|
case OAK_NODE_VALUE_VEC2:
|
||||||
|
return QVariant::fromValue(
|
||||||
|
QVector2D(float(v.f[0]), float(v.f[1])));
|
||||||
|
case OAK_NODE_VALUE_VEC3:
|
||||||
|
return QVariant::fromValue(
|
||||||
|
QVector3D(float(v.f[0]), float(v.f[1]), float(v.f[2])));
|
||||||
|
case OAK_NODE_VALUE_VEC4:
|
||||||
|
return QVariant::fromValue(
|
||||||
|
QVector4D(float(v.f[0]), float(v.f[1]), float(v.f[2]), float(v.f[3])));
|
||||||
|
case OAK_NODE_VALUE_COMBO:
|
||||||
|
return QVariant::fromValue<int>(int(v.num));
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Map an engine NodeKeyframe::Type to the facade easing type.
|
||||||
|
*/
|
||||||
|
static inline int NodeKeyframeTypeToFacade(NodeKeyframe::Type type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case NodeKeyframe::k_bezier:
|
||||||
|
return 1;
|
||||||
|
case NodeKeyframe::k_hold:
|
||||||
|
return 2;
|
||||||
|
case NodeKeyframe::k_linear:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace olive
|
||||||
|
|
||||||
|
#endif // OAKVALUEHELPER_H
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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 "common/qtutils.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
int QtUtils::q_font_metrics_width(QFontMetrics fm, const QString &s)
|
||||||
|
{
|
||||||
|
return fm.horizontalAdvance(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFrame *QtUtils::create_horizontal_line()
|
||||||
|
{
|
||||||
|
QFrame *horizontal_line = new QFrame();
|
||||||
|
horizontal_line->setFrameShape(QFrame::HLine);
|
||||||
|
horizontal_line->setFrameShadow(QFrame::Sunken);
|
||||||
|
return horizontal_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFrame *QtUtils::create_vertical_line()
|
||||||
|
{
|
||||||
|
QFrame *l = create_horizontal_line();
|
||||||
|
l->setFrameShape(QFrame::VLine);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QtUtils::get_formatted_date_time(const QDateTime &dt)
|
||||||
|
{
|
||||||
|
return dt.toString(Qt::TextDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList QtUtils::word_wrap_string(const QString &s, const QFontMetrics &fm,
|
||||||
|
int bounding_width)
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
QStringList lines = s.split('\n');
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
QString this_line = lines.at(i);
|
||||||
|
while (this_line.size() > 1 &&
|
||||||
|
q_font_metrics_width(fm, this_line) >= bounding_width) {
|
||||||
|
int old_size = this_line.size();
|
||||||
|
int hard_break = -1;
|
||||||
|
for (int j = this_line.size() - 1; j >= 0; j--) {
|
||||||
|
const QChar &char_test = this_line.at(j);
|
||||||
|
if (char_test.isSpace() || char_test == '-') {
|
||||||
|
if (q_font_metrics_width(fm, this_line.left(j)) <
|
||||||
|
bounding_width) {
|
||||||
|
if (!char_test.isSpace()) j++;
|
||||||
|
list.append(this_line.left(j));
|
||||||
|
while (j < this_line.size() &&
|
||||||
|
this_line.at(j).isSpace()) j++;
|
||||||
|
this_line.remove(0, j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (hard_break == -1 &&
|
||||||
|
q_font_metrics_width(fm, this_line.left(j)) <
|
||||||
|
bounding_width) {
|
||||||
|
hard_break = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (old_size == this_line.size()) {
|
||||||
|
if (hard_break != -1) {
|
||||||
|
list.append(this_line.left(hard_break));
|
||||||
|
this_line.remove(0, hard_break);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this_line.isEmpty()) {
|
||||||
|
list.append(this_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::KeyboardModifiers
|
||||||
|
QtUtils::flip_control_and_shift_modifiers(Qt::KeyboardModifiers e)
|
||||||
|
{
|
||||||
|
if (e & Qt::ControlModifier & Qt::ShiftModifier) return e;
|
||||||
|
if (e & Qt::ShiftModifier) {
|
||||||
|
e |= Qt::ControlModifier;
|
||||||
|
e &= ~Qt::ShiftModifier;
|
||||||
|
} else if (e & Qt::ControlModifier) {
|
||||||
|
e |= Qt::ShiftModifier;
|
||||||
|
e &= ~Qt::ControlModifier;
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtUtils::set_combo_box_data(QComboBox *cb, int data)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cb->count(); i++) {
|
||||||
|
if (cb->itemData(i).toInt() == data) {
|
||||||
|
cb->setCurrentIndex(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtUtils::set_combo_box_data(QComboBox *cb, const QString &data)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cb->count(); i++) {
|
||||||
|
if (cb->itemData(i).toString() == data) {
|
||||||
|
cb->setCurrentIndex(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor QtUtils::to_q_color(const core::Color &i)
|
||||||
|
{
|
||||||
|
QColor c;
|
||||||
|
|
||||||
|
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
|
||||||
|
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
|
||||||
|
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
|
||||||
|
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
|
||||||
|
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2022 Olive Team
|
||||||
|
Modifications Copyright (C) 2025 mikesolar
|
||||||
|
|
||||||
|
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 OAK_UNDOWRAPPER_H
|
||||||
|
#define OAK_UNDOWRAPPER_H
|
||||||
|
|
||||||
|
#include "oakengine/undo.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap an app-side undo command object in the facade custom-command API.
|
||||||
|
*
|
||||||
|
* `Cmd` must provide public `redo()` and `undo()` methods. Ownership of `cmd`
|
||||||
|
* is transferred to the returned opaque command pointer; the wrapper deletes
|
||||||
|
* `cmd` when the engine command is destroyed.
|
||||||
|
*
|
||||||
|
* This helper lets app code keep small app-state undo commands (selections,
|
||||||
|
* splitter sizes, etc.) without defining new subclasses of olive::UndoCommand,
|
||||||
|
* which would keep olive::UndoCommand symbols in the editor binary.
|
||||||
|
*/
|
||||||
|
template <typename Cmd>
|
||||||
|
void *wrap_app_undo_command(const char *name, Cmd *cmd)
|
||||||
|
{
|
||||||
|
return oakengine_undo_command_create(
|
||||||
|
name,
|
||||||
|
[](void *userdata) {
|
||||||
|
static_cast<Cmd *>(userdata)->redo();
|
||||||
|
},
|
||||||
|
[](void *userdata) {
|
||||||
|
static_cast<Cmd *>(userdata)->undo();
|
||||||
|
},
|
||||||
|
[](void *userdata) {
|
||||||
|
delete static_cast<Cmd *>(userdata);
|
||||||
|
},
|
||||||
|
cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace olive
|
||||||
|
|
||||||
|
#endif // OAK_UNDOWRAPPER_H
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Oak - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2026 Oak 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/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
// App-side implementation of xml_read_next_start_element
|
||||||
|
// Provides a local definition so the app doesn't import this from liboakengine.
|
||||||
|
|
||||||
|
#include "common/xmlutils.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
bool xml_read_next_start_element(QXmlStreamReader *reader,
|
||||||
|
CancelAtom *cancel_atom)
|
||||||
|
{
|
||||||
|
QXmlStreamReader::TokenType token;
|
||||||
|
|
||||||
|
while ((token = reader->readNext()) != QXmlStreamReader::Invalid &&
|
||||||
|
token != QXmlStreamReader::EndDocument &&
|
||||||
|
(!cancel_atom || !cancel_atom->is_cancelled())) {
|
||||||
|
if (reader->isEndElement()) {
|
||||||
|
return false;
|
||||||
|
} else if (reader->isStartElement()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+571
-180
File diff suppressed because it is too large
Load Diff
+115
-25
@@ -23,6 +23,11 @@
|
|||||||
#define OAK_CORE_H
|
#define OAK_CORE_H
|
||||||
|
|
||||||
#include "coreengine.h"
|
#include "coreengine.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include "oakengine/app.h"
|
||||||
|
#include "oakengine/undo.h"
|
||||||
|
#include "oakengine/init.h"
|
||||||
|
#include "oakengine/task.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -32,35 +37,40 @@ class MainWindow;
|
|||||||
/**
|
/**
|
||||||
* @brief The main central Olive application instance_
|
* @brief The main central Olive application instance_
|
||||||
*
|
*
|
||||||
* This is the UI-facing derivation of EngineCore. It runs both in GUI and
|
* This is the UI-facing application controller. It holds an EngineCore
|
||||||
* CLI modes (and handles what to init based on that). All UI-independent
|
* member for UI-independent engine state and adds the main window, dialogs
|
||||||
* engine state lives in the base class EngineCore; this class adds the main
|
* and other user interaction on top of it.
|
||||||
* window, dialogs and other user interaction on top of it.
|
*
|
||||||
|
* EngineCore is NOT a base class — it is a member, so the MOC-generated
|
||||||
|
* code for Core does not pull in EngineCore's Q_OBJECT symbols.
|
||||||
*
|
*
|
||||||
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
|
* The "public slots" are usually user-triggered actions and can be connected to UI elements (e.g. creating a folder,
|
||||||
* opening the import dialog, etc.)
|
* opening the import dialog, etc.)
|
||||||
*/
|
*/
|
||||||
class Core : public EngineCore {
|
class Core : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Core Constructor
|
* @brief Core Constructor
|
||||||
*
|
*
|
||||||
* Registers the UI handlers that EngineCore uses to request user
|
* Creates the EngineCore engine instance and registers the UI handlers
|
||||||
* interaction.
|
* that the engine uses to request user interaction.
|
||||||
*/
|
*/
|
||||||
Core(const CoreParams ¶ms);
|
Core(const OakEngineAppParams *params = nullptr);
|
||||||
|
|
||||||
|
~Core()
|
||||||
|
{
|
||||||
|
instance_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Core object accessible from anywhere in the code
|
* @brief Core object accessible from anywhere in the code
|
||||||
*
|
*
|
||||||
* Use this to access Core functions. This is simply EngineCore::instance()
|
* Returns the application Core singleton (no EngineCore::instance() call).
|
||||||
* cast to Core, which is safe because the application entry point (main())
|
|
||||||
* always constructs a Core.
|
|
||||||
*/
|
*/
|
||||||
static Core *instance()
|
static Core *instance()
|
||||||
{
|
{
|
||||||
return static_cast<Core *>(EngineCore::instance());
|
return instance_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,7 +123,7 @@ public:
|
|||||||
* @brief Show a dialog to the user to rename a set of nodes
|
* @brief Show a dialog to the user to rename a set of nodes
|
||||||
*/
|
*/
|
||||||
bool label_nodes(const QVector<Node *> &nodes,
|
bool label_nodes(const QVector<Node *> &nodes,
|
||||||
MultiUndoCommand *parent = nullptr);
|
void *parent = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Opens a project from the recently opened list
|
* @brief Opens a project from the recently opened list
|
||||||
@@ -137,6 +147,9 @@ public:
|
|||||||
void open_export_dialog_for_viewer(ViewerOutput *viewer,
|
void open_export_dialog_for_viewer(ViewerOutput *viewer,
|
||||||
bool start_still_image);
|
bool start_still_image);
|
||||||
|
|
||||||
|
bool add_open_project_from_task(OakEngineTask *task, bool add_to_recents);
|
||||||
|
bool add_recovery_project_from_task(OakEngineTask *task);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* @brief Starts an open file dialog to load a project from file
|
* @brief Starts an open file dialog to load a project from file
|
||||||
@@ -180,13 +193,6 @@ public slots:
|
|||||||
*/
|
*/
|
||||||
void dialog_export_show();
|
void dialog_export_show();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Show OTIO import dialog
|
|
||||||
*/
|
|
||||||
#ifdef USE_OTIO
|
|
||||||
bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new folder in the currently active project
|
* @brief Create a new folder in the currently active project
|
||||||
*/
|
*/
|
||||||
@@ -201,6 +207,85 @@ public slots:
|
|||||||
|
|
||||||
void browse_auto_recoveries();
|
void browse_auto_recoveries();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// The following methods are ordinary member functions, NOT slots. They are
|
||||||
|
// deliberately kept out of the `public slots:` section because their
|
||||||
|
// signatures reference engine C++ types (Project*, Sequence*, UndoStack*).
|
||||||
|
// If MOC processed them as slots it would instantiate QMetaType for those
|
||||||
|
// types and pull their staticMetaObject symbols across the ABI boundary.
|
||||||
|
// None of them are connect() targets: every connection involving Core uses
|
||||||
|
// the new-style member-function syntax, which works with plain methods.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show OTIO import dialog
|
||||||
|
*/
|
||||||
|
#ifdef USE_OTIO
|
||||||
|
bool DialogImportOTIOShow(const QList<Sequence *> &sequences);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ---- Facade-wrapping methods (shadow EngineCore to avoid symbol refs) ----
|
||||||
|
|
||||||
|
UndoStack *undo_stack() const;
|
||||||
|
|
||||||
|
Tool::Item tool() const;
|
||||||
|
void set_tool(const Tool::Item &tool);
|
||||||
|
|
||||||
|
bool snapping() const;
|
||||||
|
void set_snapping(const bool &b);
|
||||||
|
|
||||||
|
Timecode::Display get_timecode_display() const;
|
||||||
|
void set_timecode_display(Timecode::Display d);
|
||||||
|
|
||||||
|
void show_status_bar_message(const QString &s, int timeout = 0);
|
||||||
|
void clear_status_bar_message();
|
||||||
|
|
||||||
|
static QString footage_file_dialog_filter();
|
||||||
|
static bool is_footage_extension_allowed(const QString &path);
|
||||||
|
|
||||||
|
void create_new_project();
|
||||||
|
Sequence *create_new_sequence_for_project(const QString &format,
|
||||||
|
Project *project);
|
||||||
|
static Sequence *create_new_sequence_for_project(Project *project);
|
||||||
|
|
||||||
|
void clear_open_recent_list();
|
||||||
|
void set_use_proxy_media(bool enabled);
|
||||||
|
|
||||||
|
void request_pixel_sampling_in_viewers(bool e);
|
||||||
|
|
||||||
|
Tool::AddableObject get_selected_addable_object() const;
|
||||||
|
void set_selected_addable_object(const Tool::AddableObject &obj);
|
||||||
|
void set_selected_transition_object(const QString &obj);
|
||||||
|
|
||||||
|
static void copy_string_to_clipboard(const QString &s);
|
||||||
|
|
||||||
|
void set_magic(bool e);
|
||||||
|
|
||||||
|
// Recent project list accessors (replaces EngineCore::get_recent_projects())
|
||||||
|
int get_recent_project_count() const;
|
||||||
|
QString get_recent_project_at(int index) const;
|
||||||
|
|
||||||
|
// Facade-wrapping methods (delegate through the C ABI)
|
||||||
|
|
||||||
|
bool set_language(const QString &locale);
|
||||||
|
void set_autorecovery_interval(int minutes);
|
||||||
|
|
||||||
|
void on_project_saved(Project *p);
|
||||||
|
static QString get_auto_recovery_index_filename();
|
||||||
|
void add_open_project(olive::Project *p, bool add_to_recents = false);
|
||||||
|
void remove_recently_opened_project(int index);
|
||||||
|
void set_active_project(Project *p);
|
||||||
|
QString get_selected_transition() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Forwarding signals (shadow EngineCore signals so connect() resolves here)
|
||||||
|
void tool_changed(const Tool::Item &tool);
|
||||||
|
void addable_object_changed(Tool::AddableObject o);
|
||||||
|
void snapping_changed(const bool &b);
|
||||||
|
void timecode_display_changed(Timecode::Display d);
|
||||||
|
void open_recent_list_changed();
|
||||||
|
void color_picker_enabled(bool e);
|
||||||
|
void color_picker_color_emitted(const Color &reference, const Color &display);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
||||||
@@ -242,15 +327,20 @@ private:
|
|||||||
*/
|
*/
|
||||||
MainWindow *main_window_;
|
MainWindow *main_window_;
|
||||||
|
|
||||||
private slots:
|
/**
|
||||||
void project_save_succeeded(Task *task);
|
* @brief Cached Core* singleton
|
||||||
|
*/
|
||||||
|
static Core *instance_;
|
||||||
|
|
||||||
bool add_open_project_from_task_and_add_to_recents(Task *task)
|
private slots:
|
||||||
|
void project_save_succeeded(OakEngineTask *task);
|
||||||
|
|
||||||
|
bool add_open_project_from_task_and_add_to_recents(OakEngineTask *task)
|
||||||
{
|
{
|
||||||
return add_open_project_from_task(task, true);
|
return instance()->add_open_project_from_task(task, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void import_task_complete(Task *task);
|
void import_task_complete(OakEngineTask *task);
|
||||||
|
|
||||||
bool confirm_image_sequence(const QString &filename);
|
bool confirm_image_sequence(const QString &filename);
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "common/configwrapper.h"
|
||||||
#include "patreon.h"
|
#include "patreon.h"
|
||||||
#include "scrollinglabel.h"
|
#include "scrollinglabel.h"
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
ColorDialog::ColorDialog(ColorManager *color_manager, const ManagedColor &start,
|
ColorDialog::ColorDialog(OakEngineColorManager *color_manager, const ManagedColor &start,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, color_manager_(color_manager)
|
, color_manager_(color_manager)
|
||||||
@@ -142,11 +142,23 @@ void ColorDialog::set_color(const ManagedColor &start)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Convert reference color to the input space
|
// Convert reference color to the input space
|
||||||
ColorProcessorPtr linear_to_input = ColorProcessor::create(
|
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
|
||||||
color_manager_, color_manager_->get_reference_color_space(),
|
return oakengine_color_manager_reference_color_space(
|
||||||
start.color_input());
|
color_manager_, buf, size);
|
||||||
|
}).toUtf8();
|
||||||
|
QByteArray in_cs = start.color_input().toUtf8();
|
||||||
|
oak_color_transform in_pod;
|
||||||
|
in_pod.is_display = 0;
|
||||||
|
in_pod.output = in_cs.constData();
|
||||||
|
in_pod.view = nullptr;
|
||||||
|
in_pod.look = nullptr;
|
||||||
|
ColorProcessorHandlePtr linear_to_input(
|
||||||
|
oakengine_color_processor_create(color_manager_, ref_cs.constData(),
|
||||||
|
&in_pod,
|
||||||
|
OAKENGINE_COLOR_PROCESSOR_NORMAL),
|
||||||
|
ColorProcessorHandleDeleter());
|
||||||
|
|
||||||
managed_start = linear_to_input->convert_color(start);
|
managed_start = oak_convert_color(linear_to_input, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
color_wheel_->set_selected_color(managed_start);
|
color_wheel_->set_selected_color(managed_start);
|
||||||
@@ -161,7 +173,7 @@ ManagedColor ColorDialog::get_selected_color() const
|
|||||||
|
|
||||||
// Convert to linear and return a linear color
|
// Convert to linear and return a linear color
|
||||||
if (input_to_ref_processor_) {
|
if (input_to_ref_processor_) {
|
||||||
selected = input_to_ref_processor_->convert_color(selected);
|
selected = oak_convert_color(input_to_ref_processor_, selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
selected.set_color_input(get_color_space_input());
|
selected.set_color_input(get_color_space_input());
|
||||||
@@ -183,22 +195,50 @@ ColorTransform ColorDialog::get_color_space_output() const
|
|||||||
void ColorDialog::color_space_changed(const QString &input,
|
void ColorDialog::color_space_changed(const QString &input,
|
||||||
const ColorTransform &output)
|
const ColorTransform &output)
|
||||||
{
|
{
|
||||||
input_to_ref_processor_ = ColorProcessor::create(
|
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
|
||||||
color_manager_, input, color_manager_->get_reference_color_space());
|
return oakengine_color_manager_reference_color_space(
|
||||||
|
color_manager_, buf, size);
|
||||||
|
}).toUtf8();
|
||||||
|
QByteArray in = input.toUtf8();
|
||||||
|
QByteArray o, v, l;
|
||||||
|
oak_color_transform out_pod = oak_to_transform(output, &o, &v, &l);
|
||||||
|
|
||||||
ColorProcessorPtr ref_to_display = ColorProcessor::create(
|
auto make_proc = [&](const char *input_cs, const oak_color_transform *dest,
|
||||||
color_manager_, color_manager_->get_reference_color_space(), output);
|
int dir) -> ColorProcessorHandlePtr {
|
||||||
|
return ColorProcessorHandlePtr(
|
||||||
|
oakengine_color_processor_create(color_manager_, input_cs, dest,
|
||||||
|
dir),
|
||||||
|
ColorProcessorHandleDeleter());
|
||||||
|
};
|
||||||
|
|
||||||
ColorProcessorPtr ref_to_input = ColorProcessor::create(
|
input_to_ref_processor_ = make_proc(in.constData(), &out_pod,
|
||||||
color_manager_, color_manager_->get_reference_color_space(), input);
|
OAKENGINE_COLOR_PROCESSOR_NORMAL);
|
||||||
|
|
||||||
|
oak_color_transform ref_display_pod;
|
||||||
|
ref_display_pod.is_display = out_pod.is_display;
|
||||||
|
ref_display_pod.output = out_pod.output;
|
||||||
|
ref_display_pod.view = out_pod.view;
|
||||||
|
ref_display_pod.look = out_pod.look;
|
||||||
|
ColorProcessorHandlePtr ref_to_display = make_proc(
|
||||||
|
ref_cs.constData(), &ref_display_pod,
|
||||||
|
OAKENGINE_COLOR_PROCESSOR_NORMAL);
|
||||||
|
|
||||||
|
oak_color_transform ref_input_pod;
|
||||||
|
ref_input_pod.is_display = 0;
|
||||||
|
ref_input_pod.output = in.constData();
|
||||||
|
ref_input_pod.view = nullptr;
|
||||||
|
ref_input_pod.look = nullptr;
|
||||||
|
ColorProcessorHandlePtr ref_to_input = make_proc(
|
||||||
|
ref_cs.constData(), &ref_input_pod,
|
||||||
|
OAKENGINE_COLOR_PROCESSOR_NORMAL);
|
||||||
|
|
||||||
// Display -> reference is the inverse of the display transform. Older OCIO
|
// Display -> reference is the inverse of the display transform. Older OCIO
|
||||||
// versions crashed on TRANSFORM_DIR_INVERSE; guard by requiring a valid
|
// versions crashed on TRANSFORM_DIR_INVERSE; guard by requiring a valid
|
||||||
// processor and fall back to disabling the display tab if creation fails.
|
// processor and fall back to disabling the display tab if creation fails.
|
||||||
ColorProcessorPtr display_to_ref = ColorProcessor::create(
|
ColorProcessorHandlePtr display_to_ref = make_proc(
|
||||||
color_manager_, color_manager_->get_reference_color_space(), output,
|
ref_cs.constData(), &ref_display_pod,
|
||||||
ColorProcessor::k_inverse);
|
OAKENGINE_COLOR_PROCESSOR_INVERSE);
|
||||||
if (display_to_ref && !display_to_ref->get_processor()) {
|
if (display_to_ref && !oakengine_color_processor_is_valid(display_to_ref.get())) {
|
||||||
display_to_ref = nullptr;
|
display_to_ref = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
#include "node/color/colormanager/colormanager.h"
|
#include "oakengine/color.h"
|
||||||
#include "render/managedcolor.h"
|
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||||
#include "widget/colorwheel/colorgradientwidget.h"
|
#include "widget/colorwheel/colorgradientwidget.h"
|
||||||
#include "widget/colorwheel/colorspacechooser.h"
|
#include "widget/colorwheel/colorspacechooser.h"
|
||||||
#include "widget/colorwheel/colorswatchchooser.h"
|
#include "widget/colorwheel/colorswatchchooser.h"
|
||||||
@@ -57,7 +57,7 @@ public:
|
|||||||
*
|
*
|
||||||
* QWidget parent.
|
* QWidget parent.
|
||||||
*/
|
*/
|
||||||
ColorDialog(ColorManager *color_manager,
|
ColorDialog(OakEngineColorManager *color_manager,
|
||||||
const ManagedColor &start = Color(1.0f, 1.0f, 1.0f),
|
const ManagedColor &start = Color(1.0f, 1.0f, 1.0f),
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ public slots:
|
|||||||
void set_color(const ManagedColor &c);
|
void set_color(const ManagedColor &c);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ColorManager *color_manager_;
|
OakEngineColorManager *color_manager_;
|
||||||
|
|
||||||
ColorWheelWidget *color_wheel_;
|
ColorWheelWidget *color_wheel_;
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ private:
|
|||||||
|
|
||||||
ColorGradientWidget *hsv_value_gradient_;
|
ColorGradientWidget *hsv_value_gradient_;
|
||||||
|
|
||||||
ColorProcessorPtr input_to_ref_processor_;
|
ColorProcessorHandlePtr input_to_ref_processor_;
|
||||||
|
|
||||||
ColorSpaceChooser *chooser_;
|
ColorSpaceChooser *chooser_;
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
|
#include "oakengine/undo.h"
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -70,13 +71,13 @@ void ConfigDialogBase::accept()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiUndoCommand *command = new MultiUndoCommand();
|
void *command = oakengine_undo_command_create_multi();
|
||||||
|
|
||||||
foreach (ConfigDialogBaseTab *tab, tabs_) {
|
foreach (ConfigDialogBaseTab *tab, tabs_) {
|
||||||
tab->accept(command);
|
tab->accept(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
|
oakengine_undo_push(command, tr("Set Configuration").toUtf8().constData());
|
||||||
|
|
||||||
AcceptEvent();
|
AcceptEvent();
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "common/configwrapper.h"
|
||||||
#include "undo/undocommand.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -36,7 +35,7 @@ public:
|
|||||||
|
|
||||||
virtual bool validate();
|
virtual bool validate();
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *parent) = 0;
|
virtual void accept(void *parent) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "oakengine/disk.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ void DiskCacheDialog::clear_disk_cache(const QString &path, QWidget *parent,
|
|||||||
if (clear_btn)
|
if (clear_btn)
|
||||||
clear_btn->setEnabled(false);
|
clear_btn->setEnabled(false);
|
||||||
|
|
||||||
if (DiskManager::instance()->clear_disk_cache(path)) {
|
if (oakengine_disk_clear_cache(path.toUtf8().constData())) {
|
||||||
if (clear_btn)
|
if (clear_btn)
|
||||||
clear_btn->setText(tr("Disk Cache Cleared"));
|
clear_btn->setText(tr("Disk Cache Cleared"));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -89,19 +89,21 @@ AV1Section::AV1Section(int default_crf, QWidget *parent)
|
|||||||
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AV1Section::add_opts(EncodingParams *params)
|
void AV1Section::add_opts(OakEngineEncodingParams *params)
|
||||||
{
|
{
|
||||||
CompressionMethod method = static_cast<CompressionMethod>(
|
CompressionMethod method = static_cast<CompressionMethod>(
|
||||||
compression_method_stack_->currentIndex());
|
compression_method_stack_->currentIndex());
|
||||||
|
|
||||||
if (method == k_constant_rate_factor) {
|
if (method == k_constant_rate_factor) {
|
||||||
// Set Quantizer value
|
// Set Quantizer value
|
||||||
params->set_video_option(QStringLiteral("qp"),
|
oakengine_encoding_params_set_video_option(
|
||||||
QString::number(crf_section_->get_value()));
|
params, "qp",
|
||||||
|
QByteArray::number(crf_section_->get_value()).constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
params->set_video_option(QStringLiteral("preset"),
|
oakengine_encoding_params_set_video_option(
|
||||||
QString::number(preset_combobox_->currentIndex()));
|
params, "preset",
|
||||||
|
QByteArray::number(preset_combobox_->currentIndex()).constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
|
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public:
|
|||||||
AV1Section(QWidget *parent = nullptr);
|
AV1Section(QWidget *parent = nullptr);
|
||||||
AV1Section(int default_crf, QWidget *parent);
|
AV1Section(int default_crf, QWidget *parent);
|
||||||
|
|
||||||
virtual void add_opts(EncodingParams *params) override;
|
virtual void add_opts(OakEngineEncodingParams *params) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStackedWidget *compression_method_stack_;
|
QStackedWidget *compression_method_stack_;
|
||||||
|
|||||||
@@ -79,17 +79,21 @@ CineformSection::CineformSection(QWidget *parent)
|
|||||||
layout->addWidget(quality_combobox_, row, 1);
|
layout->addWidget(quality_combobox_, row, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CineformSection::add_opts(EncodingParams *params)
|
void CineformSection::add_opts(OakEngineEncodingParams *params)
|
||||||
{
|
{
|
||||||
params->set_video_option(
|
oakengine_encoding_params_set_video_option(
|
||||||
QStringLiteral("quality"),
|
params, "quality",
|
||||||
QString::number(quality_combobox_->currentIndex()));
|
QByteArray::number(quality_combobox_->currentIndex()).constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CineformSection::set_opts(const EncodingParams *p)
|
void CineformSection::set_opts(const OakEngineEncodingParams *p)
|
||||||
{
|
{
|
||||||
quality_combobox_->setCurrentIndex(
|
char buf[64];
|
||||||
p->video_option(QStringLiteral("quality")).toInt());
|
const int ret = oakengine_encoding_params_video_option(
|
||||||
|
p, "quality", buf, static_cast<int>(sizeof(buf)));
|
||||||
|
if (ret > 0) {
|
||||||
|
quality_combobox_->setCurrentIndex(QString::fromUtf8(buf).toInt());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ class CineformSection : public CodecSection {
|
|||||||
public:
|
public:
|
||||||
CineformSection(QWidget *parent = nullptr);
|
CineformSection(QWidget *parent = nullptr);
|
||||||
|
|
||||||
virtual void add_opts(EncodingParams *params) override;
|
virtual void add_opts(OakEngineEncodingParams *params) override;
|
||||||
|
|
||||||
virtual void set_opts(const EncodingParams *p) override;
|
virtual void set_opts(const OakEngineEncodingParams *p) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QComboBox *quality_combobox_;
|
QComboBox *quality_combobox_;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "oakengine/encoding.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -34,12 +34,12 @@ class CodecSection : public QWidget {
|
|||||||
public:
|
public:
|
||||||
CodecSection(QWidget *parent = nullptr);
|
CodecSection(QWidget *parent = nullptr);
|
||||||
|
|
||||||
virtual void add_opts(EncodingParams *params)
|
virtual void add_opts(OakEngineEncodingParams *params)
|
||||||
{
|
{
|
||||||
Q_UNUSED(params)
|
Q_UNUSED(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void set_opts(const EncodingParams *p)
|
virtual void set_opts(const OakEngineEncodingParams *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p)
|
Q_UNUSED(p)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ H264Section::H264Section(int default_crf, QWidget *parent)
|
|||||||
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
compression_method_stack_, &QStackedWidget::setCurrentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void H264Section::add_opts(EncodingParams *params)
|
void H264Section::add_opts(OakEngineEncodingParams *params)
|
||||||
{
|
{
|
||||||
// FIXME: Implement two-pass
|
// FIXME: Implement two-pass
|
||||||
|
|
||||||
@@ -110,13 +110,15 @@ void H264Section::add_opts(EncodingParams *params)
|
|||||||
|
|
||||||
// This option is not used by the encoder (nor is anything with the ove_ prefix), it's to help us
|
// This option is not used by the encoder (nor is anything with the ove_ prefix), it's to help us
|
||||||
// identify which option was chosen when params are restored
|
// identify which option was chosen when params are restored
|
||||||
params->set_video_option(QStringLiteral("ove_compressionmethod"),
|
oakengine_encoding_params_set_video_option(
|
||||||
QString::number(method));
|
params, "ove_compressionmethod",
|
||||||
|
QByteArray::number(method).constData());
|
||||||
|
|
||||||
if (method == k_constant_rate_factor) {
|
if (method == k_constant_rate_factor) {
|
||||||
// Simply set CRF value
|
// Simply set CRF value
|
||||||
params->set_video_option(QStringLiteral("crf"),
|
oakengine_encoding_params_set_video_option(
|
||||||
QString::number(crf_section_->get_value()));
|
params, "crf",
|
||||||
|
QByteArray::number(crf_section_->get_value()).constData());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
int64_t target_rate, max_rate, min_rate;
|
int64_t target_rate, max_rate, min_rate;
|
||||||
@@ -129,40 +131,58 @@ void H264Section::add_opts(EncodingParams *params)
|
|||||||
} else {
|
} else {
|
||||||
// Calculate the bit rate from the file size divided by the sequence length in seconds (bits per second)
|
// Calculate the bit rate from the file size divided by the sequence length in seconds (bits per second)
|
||||||
int64_t target_fs = filesize_section_->get_file_size();
|
int64_t target_fs = filesize_section_->get_file_size();
|
||||||
target_rate = qRound64(static_cast<double>(target_fs) /
|
int export_len_num = 0, export_len_den = 1;
|
||||||
params->get_export_length().to_double());
|
oakengine_encoding_params_get_export_length(
|
||||||
|
params, &export_len_num, &export_len_den);
|
||||||
|
const double export_len_sec =
|
||||||
|
(export_len_den > 0)
|
||||||
|
? static_cast<double>(export_len_num)
|
||||||
|
/ static_cast<double>(export_len_den)
|
||||||
|
: 1.0;
|
||||||
|
target_rate = qRound64(static_cast<double>(target_fs) / export_len_sec);
|
||||||
min_rate = target_rate;
|
min_rate = target_rate;
|
||||||
max_rate = target_rate;
|
max_rate = target_rate;
|
||||||
|
|
||||||
params->set_video_option(QStringLiteral("ove_targetfilesize"),
|
oakengine_encoding_params_set_video_option(
|
||||||
QString::number(target_fs));
|
params, "ove_targetfilesize",
|
||||||
|
QByteArray::number(target_fs).constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable CRF encoding
|
// Disable CRF encoding
|
||||||
params->set_video_option(QStringLiteral("crf"), QStringLiteral("-1"));
|
oakengine_encoding_params_set_video_option(params, "crf", "-1");
|
||||||
|
|
||||||
params->set_video_bit_rate(target_rate);
|
oakengine_encoding_params_set_video_bit_rate(params, target_rate);
|
||||||
params->set_video_min_bit_rate(min_rate);
|
oakengine_encoding_params_set_video_min_bit_rate(params, min_rate);
|
||||||
params->set_video_max_bit_rate(max_rate);
|
oakengine_encoding_params_set_video_max_bit_rate(params, max_rate);
|
||||||
params->set_video_buffer_size(2000000);
|
oakengine_encoding_params_set_video_buffer_size(params, 2000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
params->set_video_option(QStringLiteral("preset"),
|
oakengine_encoding_params_set_video_option(
|
||||||
QString::number(preset_combobox_->currentIndex()));
|
params, "preset",
|
||||||
|
QByteArray::number(preset_combobox_->currentIndex()).constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
void H264Section::set_opts(const EncodingParams *p)
|
void H264Section::set_opts(const OakEngineEncodingParams *p)
|
||||||
{
|
{
|
||||||
CompressionMethod method = static_cast<CompressionMethod>(
|
char buf[64];
|
||||||
p->video_option(QStringLiteral("ove_compressionmethod")).toInt());
|
|
||||||
|
CompressionMethod method = k_constant_rate_factor;
|
||||||
|
if (oakengine_encoding_params_video_option(
|
||||||
|
p, "ove_compressionmethod", buf,
|
||||||
|
static_cast<int>(sizeof(buf))) > 0) {
|
||||||
|
method = static_cast<CompressionMethod>(QString::fromUtf8(buf).toInt());
|
||||||
|
}
|
||||||
|
|
||||||
compression_method_stack_->setCurrentIndex(method);
|
compression_method_stack_->setCurrentIndex(method);
|
||||||
|
|
||||||
if (method == k_constant_rate_factor) {
|
if (method == k_constant_rate_factor) {
|
||||||
crf_section_->set_value(p->video_option(QStringLiteral("crf")).toInt());
|
if (oakengine_encoding_params_video_option(
|
||||||
|
p, "crf", buf, static_cast<int>(sizeof(buf))) > 0) {
|
||||||
|
crf_section_->set_value(QString::fromUtf8(buf).toInt());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
int64_t target_rate = p->video_bit_rate();
|
int64_t target_rate = oakengine_encoding_params_video_bit_rate(p);
|
||||||
int64_t max_rate = p->video_max_bit_rate();
|
int64_t max_rate = oakengine_encoding_params_video_max_bit_rate(p);
|
||||||
|
|
||||||
if (method == k_target_bit_rate) {
|
if (method == k_target_bit_rate) {
|
||||||
// Use user-supplied values for the bit rate
|
// Use user-supplied values for the bit rate
|
||||||
@@ -170,9 +190,12 @@ void H264Section::set_opts(const EncodingParams *p)
|
|||||||
bitrate_section_->set_maximum_bit_rate(max_rate);
|
bitrate_section_->set_maximum_bit_rate(max_rate);
|
||||||
} else {
|
} else {
|
||||||
// Calculate the bit rate from the file size divided by the sequence length in seconds (bits per second)
|
// Calculate the bit rate from the file size divided by the sequence length in seconds (bits per second)
|
||||||
|
if (oakengine_encoding_params_video_option(
|
||||||
|
p, "ove_targetfilesize", buf,
|
||||||
|
static_cast<int>(sizeof(buf))) > 0) {
|
||||||
filesize_section_->set_file_size(
|
filesize_section_->set_file_size(
|
||||||
p->video_option(QStringLiteral("ove_targetfilesize"))
|
QString::fromUtf8(buf).toLongLong());
|
||||||
.toLongLong());
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,9 +100,9 @@ public:
|
|||||||
H264Section(QWidget *parent = nullptr);
|
H264Section(QWidget *parent = nullptr);
|
||||||
H264Section(int default_crf, QWidget *parent);
|
H264Section(int default_crf, QWidget *parent);
|
||||||
|
|
||||||
virtual void add_opts(EncodingParams *params) override;
|
virtual void add_opts(OakEngineEncodingParams *params) override;
|
||||||
|
|
||||||
virtual void set_opts(const EncodingParams *p) override;
|
virtual void set_opts(const OakEngineEncodingParams *p) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStackedWidget *compression_method_stack_;
|
QStackedWidget *compression_method_stack_;
|
||||||
|
|||||||
+348
-242
@@ -33,16 +33,24 @@
|
|||||||
|
|
||||||
#include "common/digit.h"
|
#include "common/digit.h"
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "codec/ffmpeg/ffmpegencoder.h"
|
#include "codec/exportcodec.h"
|
||||||
|
#include "codec/exportformat.h"
|
||||||
#include "dialog/msgbox.h"
|
#include "dialog/msgbox.h"
|
||||||
#include "dialog/task/task.h"
|
#include "dialog/task/task.h"
|
||||||
#include "exportsavepresetdialog.h"
|
#include "exportsavepresetdialog.h"
|
||||||
#include "node/project.h"
|
#include "node/project.h"
|
||||||
#include "node/project/sequence/sequence.h"
|
#include "node/project/sequence/sequence.h"
|
||||||
|
#include "oakengine/events.h"
|
||||||
|
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||||
|
#include "widget/viewer/vieweroutpututils.h"
|
||||||
#include "oakengine/exporter.h"
|
#include "oakengine/exporter.h"
|
||||||
#include "task/taskmanager.h"
|
#include "oakengine/project.h"
|
||||||
|
#include "oakengine/task.h"
|
||||||
|
#include "oakengine/encoding.h"
|
||||||
|
#include "oakengine/viewer.h"
|
||||||
#include "ui/icons/icons.h"
|
#include "ui/icons/icons.h"
|
||||||
#include "widget/timeruler/timeruler.h"
|
#include "widget/timeruler/timeruler.h"
|
||||||
|
#include "common/configwrapper.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -54,57 +62,96 @@ namespace
|
|||||||
|
|
||||||
// pix_fmt string (e.g. "yuv420p") to its index in the codec's supported
|
// pix_fmt string (e.g. "yuv420p") to its index in the codec's supported
|
||||||
// list; 0 (the codec's preferred format) when absent.
|
// list; 0 (the codec's preferred format) when absent.
|
||||||
int pix_fmt_index(ExportCodec::Codec codec, const QString &pix_fmt)
|
int pix_fmt_index(int codec, const QString &pix_fmt)
|
||||||
{
|
{
|
||||||
if (pix_fmt.isEmpty()) {
|
if (pix_fmt.isEmpty()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
FFmpegEncoder probe{ EncodingParams() };
|
return oakengine_encoding_pix_fmt_index(codec, pix_fmt.toUtf8().constData());
|
||||||
const int index = probe.get_pixel_formats_for_codec(codec).indexOf(pix_fmt);
|
|
||||||
return index >= 0 ? index : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodingParams (assembled by the dialog) -> facade POD. One-to-one with
|
// OakEngineEncodingParams (assembled by the dialog) -> facade POD. One-to-one with
|
||||||
// oak_export_options_ex; see oakengine/exporter.h for the field docs.
|
// oak_export_options_ex; see oakengine/exporter.h for the field docs.
|
||||||
oak_export_options_ex params_to_ex(const EncodingParams &p)
|
oak_export_options_ex params_to_ex(const OakEngineEncodingParams *p)
|
||||||
{
|
{
|
||||||
oak_export_options_ex o = {};
|
oak_export_options_ex o = {};
|
||||||
|
|
||||||
const VideoParams &vp = p.video_params();
|
int64_t vbrate = 0, abrate = 0;
|
||||||
const Rational tb = vp.frame_rate().flipped();
|
int asample_rate = 0;
|
||||||
|
uint64_t ach_layout = 0;
|
||||||
|
int asample_fmt = 0;
|
||||||
|
int vthreads = 0;
|
||||||
|
int scaling = 0;
|
||||||
|
int is_img_seq = 0;
|
||||||
|
|
||||||
if (p.has_custom_range()) {
|
oak_video_params vp = {};
|
||||||
|
oakengine_encoding_params_get_video_params(p, &vp);
|
||||||
|
|
||||||
|
vbrate = oakengine_encoding_params_video_bit_rate(p);
|
||||||
|
abrate = oakengine_encoding_params_audio_bit_rate(p);
|
||||||
|
vthreads = oakengine_encoding_params_video_threads(p);
|
||||||
|
scaling = oakengine_encoding_params_video_scaling_method(p);
|
||||||
|
is_img_seq = oakengine_encoding_params_video_is_image_sequence(p);
|
||||||
|
|
||||||
|
if (oakengine_encoding_params_has_custom_range(p)) {
|
||||||
o.range_mode = OAKENGINE_EXPORT_RANGE_CUSTOM;
|
o.range_mode = OAKENGINE_EXPORT_RANGE_CUSTOM;
|
||||||
o.range_in_ts = Timecode::time_to_timestamp(p.custom_range().in(), tb);
|
int64_t r_in_num = 0, r_in_den = 1, r_out_num = 0, r_out_den = 1;
|
||||||
|
oakengine_encoding_params_get_custom_range(
|
||||||
|
p, &r_in_num, &r_in_den, &r_out_num, &r_out_den);
|
||||||
|
o.range_in_ts =
|
||||||
|
Timecode::time_to_timestamp(
|
||||||
|
Rational(r_in_num, r_in_den),
|
||||||
|
Rational(vp.time_base_num, vp.time_base_den));
|
||||||
o.range_out_ts =
|
o.range_out_ts =
|
||||||
Timecode::time_to_timestamp(p.custom_range().out(), tb);
|
Timecode::time_to_timestamp(
|
||||||
|
Rational(r_out_num, r_out_den),
|
||||||
|
Rational(vp.time_base_num, vp.time_base_den));
|
||||||
} else {
|
} else {
|
||||||
o.range_mode = OAKENGINE_EXPORT_RANGE_ENTIRE;
|
o.range_mode = OAKENGINE_EXPORT_RANGE_ENTIRE;
|
||||||
}
|
}
|
||||||
|
|
||||||
o.format = int(p.format());
|
o.format = oakengine_encoding_params_format(p);
|
||||||
o.video_enabled = p.video_enabled() ? 1 : 0;
|
o.video_enabled = oakengine_encoding_params_video_enabled(p) ? 1 : 0;
|
||||||
o.video_codec = int(p.video_codec());
|
o.video_codec = oakengine_encoding_params_video_codec(p);
|
||||||
o.audio_enabled = p.audio_enabled() ? 1 : 0;
|
o.audio_enabled = oakengine_encoding_params_audio_enabled(p) ? 1 : 0;
|
||||||
o.audio_codec = int(p.audio_codec());
|
o.audio_codec = oakengine_encoding_params_audio_codec(p);
|
||||||
o.subtitles_enabled = p.subtitles_enabled() ? 1 : 0;
|
o.subtitles_enabled = oakengine_encoding_params_subtitles_enabled(p) ? 1 : 0;
|
||||||
o.subtitles_sidecar = p.subtitles_are_sidecar() ? 1 : 0;
|
o.subtitles_sidecar = oakengine_encoding_params_subtitles_are_sidecar(p) ? 1 : 0;
|
||||||
o.subtitles_format =
|
o.subtitles_format =
|
||||||
p.subtitles_are_sidecar() ? int(p.subtitle_sidecar_fmt()) : 0;
|
oakengine_encoding_params_subtitles_are_sidecar(p)
|
||||||
o.subtitles_codec = p.subtitles_enabled() ? int(p.subtitles_codec()) : 0;
|
? oakengine_encoding_params_subtitles_sidecar_format(p)
|
||||||
|
: 0;
|
||||||
|
o.subtitles_codec = oakengine_encoding_params_subtitles_enabled(p)
|
||||||
|
? oakengine_encoding_params_subtitles_codec(p)
|
||||||
|
: 0;
|
||||||
|
|
||||||
o.video_bit_rate = p.video_bit_rate();
|
o.video_bit_rate = vbrate;
|
||||||
o.audio_bit_rate = p.audio_bit_rate();
|
o.audio_bit_rate = abrate;
|
||||||
o.video_pix_fmt = pix_fmt_index(p.video_codec(), p.video_pix_fmt());
|
|
||||||
|
|
||||||
o.audio_sample_rate = p.audio_params().sample_rate();
|
char pix_fmt_buf[64];
|
||||||
o.audio_channel_layout = p.audio_params().channel_layout();
|
if (oakengine_encoding_params_video_pix_fmt(
|
||||||
o.audio_sample_format = int(p.audio_params().format());
|
p, pix_fmt_buf, static_cast<int>(sizeof(pix_fmt_buf))) > 0) {
|
||||||
|
o.video_pix_fmt = oakengine_encoding_pix_fmt_index(
|
||||||
|
o.video_codec, pix_fmt_buf);
|
||||||
|
} else {
|
||||||
|
o.video_pix_fmt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const QString ct = p.color_transform().output();
|
if (oakengine_encoding_params_get_audio_params(
|
||||||
if (ct.isEmpty()) {
|
p, &asample_rate, &ach_layout, &asample_fmt) == OAKENGINE_OK) {
|
||||||
|
o.audio_sample_rate = asample_rate;
|
||||||
|
o.audio_channel_layout = ach_layout;
|
||||||
|
o.audio_sample_format = asample_fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
char ct_buf[128];
|
||||||
|
const int ct_ret = oakengine_encoding_params_color_transform_output(
|
||||||
|
p, ct_buf, static_cast<int>(sizeof(ct_buf)));
|
||||||
|
if (ct_ret <= 0 || ct_buf[0] == '\0') {
|
||||||
o.color_transform = OAKENGINE_EXPORT_COLOR_REFERENCE;
|
o.color_transform = OAKENGINE_EXPORT_COLOR_REFERENCE;
|
||||||
} else if (ct == QStringLiteral("sRGB OETF")) {
|
} else {
|
||||||
|
const QString ct = QString::fromUtf8(ct_buf);
|
||||||
|
if (ct == QStringLiteral("sRGB OETF")) {
|
||||||
o.color_transform = OAKENGINE_EXPORT_COLOR_SRGB_OETF;
|
o.color_transform = OAKENGINE_EXPORT_COLOR_SRGB_OETF;
|
||||||
} else if (ct == QStringLiteral("Rec.709 OETF")) {
|
} else if (ct == QStringLiteral("Rec.709 OETF")) {
|
||||||
o.color_transform = OAKENGINE_EXPORT_COLOR_REC709_OETF;
|
o.color_transform = OAKENGINE_EXPORT_COLOR_REC709_OETF;
|
||||||
@@ -112,101 +159,29 @@ oak_export_options_ex params_to_ex(const EncodingParams &p)
|
|||||||
o.color_transform = OAKENGINE_EXPORT_COLOR_BT1886_EOTF;
|
o.color_transform = OAKENGINE_EXPORT_COLOR_BT1886_EOTF;
|
||||||
} else {
|
} else {
|
||||||
o.color_transform = OAKENGINE_EXPORT_COLOR_CUSTOM;
|
o.color_transform = OAKENGINE_EXPORT_COLOR_CUSTOM;
|
||||||
const QByteArray utf = ct.toUtf8();
|
|
||||||
snprintf(o.color_transform_name, sizeof(o.color_transform_name),
|
snprintf(o.color_transform_name, sizeof(o.color_transform_name),
|
||||||
"%s", utf.constData());
|
"%s", ct_buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
o.video_width = vp.width();
|
o.video_width = vp.width;
|
||||||
o.video_height = vp.height();
|
o.video_height = vp.height;
|
||||||
o.frame_rate_num = vp.frame_rate().numerator();
|
o.frame_rate_num = vp.time_base_den; // time_base is frame duration, so rate = den/num
|
||||||
o.frame_rate_den = vp.frame_rate().denominator();
|
o.frame_rate_den = vp.time_base_num;
|
||||||
o.pixel_aspect_num = vp.pixel_aspect_ratio().numerator();
|
o.pixel_aspect_num = vp.pixel_aspect_num;
|
||||||
o.pixel_aspect_den = vp.pixel_aspect_ratio().denominator();
|
o.pixel_aspect_den = vp.pixel_aspect_den;
|
||||||
o.interlacing = int(vp.interlacing());
|
o.interlacing = vp.interlacing;
|
||||||
o.pixel_format = int(vp.format());
|
o.pixel_format = vp.format;
|
||||||
o.scaling_method = int(p.video_scaling_method());
|
o.scaling_method = scaling;
|
||||||
o.color_range = int(vp.color_range());
|
o.color_range = vp.color_range;
|
||||||
o.video_threads = p.video_threads();
|
o.video_threads = vthreads;
|
||||||
o.is_image_sequence = p.video_is_image_sequence() ? 1 : 0;
|
o.is_image_sequence = is_img_seq;
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief ExportTask replacement driven by the liboakengine C ABI facade
|
|
||||||
*
|
|
||||||
* Same Task contract as the engine's ExportTask (progress via
|
|
||||||
* progress_changed, cancel via CancelEvent), but the actual
|
|
||||||
* render+encode goes through oakengine_export_render_ex(): the facade
|
|
||||||
* owns the ExportTask instance, its event-loop drive and the conform
|
|
||||||
* prewarm. Cancellation is forwarded to the facade
|
|
||||||
* (oakengine_export_cancel()), which reports OAKENGINE_E_CANCELLED back.
|
|
||||||
*/
|
|
||||||
class FacadeExportTask : public Task {
|
|
||||||
public:
|
|
||||||
FacadeExportTask(ViewerOutput *viewer_node, const EncodingParams ¶ms)
|
|
||||||
: sequence_(reinterpret_cast<OakEngineSequence *>(viewer_node))
|
|
||||||
, params_(params)
|
|
||||||
{
|
|
||||||
set_title(tr("Exporting \"%1\"").arg(viewer_node->get_label()));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool run() override
|
|
||||||
{
|
|
||||||
oak_export_options_ex o = params_to_ex(params_);
|
|
||||||
// Pass the codec section's encoder-specific options through.
|
|
||||||
for (auto it = params_.video_opts().cbegin();
|
|
||||||
it != params_.video_opts().cend(); ++it) {
|
|
||||||
oakengine_export_set_video_option(it.key().toUtf8().constData(),
|
|
||||||
it.value().toUtf8().constData());
|
|
||||||
}
|
|
||||||
oakengine_export_set_progress_callback(
|
|
||||||
&FacadeExportTask::forward_progress, this);
|
|
||||||
const int rc = oakengine_export_render_ex(
|
|
||||||
sequence_, params_.filename().toUtf8().constData(), &o);
|
|
||||||
oakengine_export_set_progress_callback(nullptr, nullptr);
|
|
||||||
oakengine_export_set_video_option("", nullptr);
|
|
||||||
|
|
||||||
if (rc == OAKENGINE_E_CANCELLED) {
|
|
||||||
// Mirror the engine task's cancelled state for TaskDialog.
|
|
||||||
cancel();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (rc != OAKENGINE_OK) {
|
|
||||||
char err[1024];
|
|
||||||
err[0] = '\0';
|
|
||||||
oakengine_export_last_error(err, sizeof(err));
|
|
||||||
set_error(err[0] ? QString::fromUtf8(err) :
|
|
||||||
QStringLiteral("Export failed"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CancelEvent() override
|
|
||||||
{
|
|
||||||
oakengine_export_cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void forward_progress(double fraction, void *userdata)
|
|
||||||
{
|
|
||||||
static_cast<FacadeExportTask *>(userdata)->emit_progress(fraction);
|
|
||||||
}
|
|
||||||
|
|
||||||
void emit_progress(double fraction)
|
|
||||||
{
|
|
||||||
emit progress_changed(fraction);
|
|
||||||
}
|
|
||||||
|
|
||||||
OakEngineSequence *sequence_;
|
|
||||||
EncodingParams params_;
|
|
||||||
};
|
|
||||||
|
|
||||||
ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: super(parent)
|
: super(parent)
|
||||||
@@ -312,16 +287,32 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
|||||||
|
|
||||||
preferences_tabs_ = new QTabWidget();
|
preferences_tabs_ = new QTabWidget();
|
||||||
|
|
||||||
color_manager_ = viewer_node_->project()->color_manager();
|
color_manager_ = oak_color_manager(viewer_node_->project()->color_manager());
|
||||||
video_tab_ = new ExportVideoTab(color_manager_);
|
video_tab_ = new ExportVideoTab(color_manager_);
|
||||||
add_preferences_tab(video_tab_, tr("Video"));
|
add_preferences_tab(video_tab_, tr("Video"));
|
||||||
|
|
||||||
// Set video tab time and make connections
|
// Set video tab time and make connections
|
||||||
connect(viewer_node, &ViewerOutput::playhead_changed, video_tab_,
|
viewer_sub_ = oakengine_event_subscribe(
|
||||||
&ExportVideoTab::set_time);
|
reinterpret_cast<OakEngineNode *>(viewer_node),
|
||||||
connect(video_tab_, &ExportVideoTab::time_changed, viewer_node,
|
OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED,
|
||||||
&ViewerOutput::set_playhead);
|
[](const oakengine_event *event, void *userdata) {
|
||||||
video_tab_->set_time(viewer_node->get_playhead());
|
auto *dlg = static_cast<ExportDialog *>(userdata);
|
||||||
|
auto *tab = dlg->video_tab_;
|
||||||
|
tab->set_time(Rational(event->a, event->b));
|
||||||
|
},
|
||||||
|
this);
|
||||||
|
connect(video_tab_, &ExportVideoTab::time_changed, this,
|
||||||
|
[viewer_node](const Rational &time) {
|
||||||
|
oakengine_viewer_set_playhead(
|
||||||
|
reinterpret_cast<OakEngineNode *>(viewer_node),
|
||||||
|
time.numerator(), time.denominator());
|
||||||
|
});
|
||||||
|
{
|
||||||
|
int64_t pn, pd;
|
||||||
|
oakengine_viewer_get_playhead(
|
||||||
|
reinterpret_cast<OakEngineNode *>(viewer_node), &pn, &pd);
|
||||||
|
video_tab_->set_time(Rational(pn, pd));
|
||||||
|
}
|
||||||
|
|
||||||
audio_tab_ = new ExportAudioTab();
|
audio_tab_ = new ExportAudioTab();
|
||||||
add_preferences_tab(audio_tab_, tr("Audio"));
|
add_preferences_tab(audio_tab_, tr("Audio"));
|
||||||
@@ -394,11 +385,11 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
|||||||
set_default_filename();
|
set_default_filename();
|
||||||
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
previously_selected_format_ = ExportFormat::k_format_mpe_g4_video;
|
previously_selected_format_ = OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO;
|
||||||
connect(format_combobox_, &ExportFormatComboBox::format_changed, this,
|
connect(format_combobox_, &ExportFormatComboBox::format_changed, this,
|
||||||
&ExportDialog::format_changed);
|
&ExportDialog::format_changed);
|
||||||
|
|
||||||
VideoParams vp = viewer_node_->get_video_params();
|
VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||||
video_aspect_ratio_ =
|
video_aspect_ratio_ =
|
||||||
static_cast<double>(vp.width()) / static_cast<double>(vp.height());
|
static_cast<double>(vp.width()) / static_cast<double>(vp.height());
|
||||||
|
|
||||||
@@ -430,7 +421,8 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
|||||||
|
|
||||||
// If the viewer already has cached params, use them
|
// If the viewer already has cached params, use them
|
||||||
if (!stills_only_mode_ &&
|
if (!stills_only_mode_ &&
|
||||||
viewer_node_->get_last_used_encoding_params().is_valid()) {
|
oakengine_encoding_params_get_last_used(
|
||||||
|
reinterpret_cast<OakEngineSequence *>(viewer_node_)) != nullptr) {
|
||||||
// This will automatically set the param data
|
// This will automatically set the param data
|
||||||
QtUtils::set_combo_box_data(preset_combobox_, k_preset_last_used);
|
QtUtils::set_combo_box_data(preset_combobox_, k_preset_last_used);
|
||||||
} else {
|
} else {
|
||||||
@@ -477,8 +469,9 @@ void ExportDialog::start_export()
|
|||||||
|
|
||||||
// Validate if the entered filename contains the correct extension (the extension is necessary
|
// Validate if the entered filename contains the correct extension (the extension is necessary
|
||||||
// for both FFmpeg and OIIO to determine the output format)
|
// for both FFmpeg and OIIO to determine the output format)
|
||||||
QString necessary_ext = QStringLiteral(".%1").arg(
|
char ext_buf[64];
|
||||||
ExportFormat::get_extension(format_combobox_->get_format()));
|
int ext_len = oakengine_encoding_format_extension(format_combobox_->get_format(), ext_buf, sizeof(ext_buf));
|
||||||
|
QString necessary_ext = QStringLiteral(".%1").arg(QString::fromUtf8(ext_buf, ext_len));
|
||||||
QString proposed_filename = filename_edit_->text().trimmed();
|
QString proposed_filename = filename_edit_->text().trimmed();
|
||||||
|
|
||||||
// If it doesn't, see if the user wants to append it automatically. If not, we don't abort the export.
|
// If it doesn't, see if the user wants to append it automatically. If not, we don't abort the export.
|
||||||
@@ -513,7 +506,7 @@ void ExportDialog::start_export()
|
|||||||
// Validate if this is an image sequence and if the filename contains enough digits
|
// Validate if this is an image sequence and if the filename contains enough digits
|
||||||
if (video_tab_->is_image_sequence_set()) {
|
if (video_tab_->is_image_sequence_set()) {
|
||||||
// Ensure filename contains digits
|
// Ensure filename contains digits
|
||||||
if (!Encoder::filename_contains_digit_placeholder(proposed_filename)) {
|
if (!oakengine_encoding_filename_contains_digit_placeholder(proposed_filename.toUtf8().constData())) {
|
||||||
msg_box(
|
msg_box(
|
||||||
this, QMessageBox::Critical, tr("Invalid filename"),
|
this, QMessageBox::Critical, tr("Invalid filename"),
|
||||||
tr("Export is set to an image sequence, but the filename does not have a section for digits "
|
tr("Export is set to an image sequence, but the filename does not have a section for digits "
|
||||||
@@ -524,7 +517,7 @@ void ExportDialog::start_export()
|
|||||||
int64_t frame_count = get_export_length_in_timebase_units();
|
int64_t frame_count = get_export_length_in_timebase_units();
|
||||||
int64_t needed_digit_count = get_digit_count(frame_count);
|
int64_t needed_digit_count = get_digit_count(frame_count);
|
||||||
int current_digit_count =
|
int current_digit_count =
|
||||||
Encoder::get_image_sequence_placeholder_digit_count(proposed_filename);
|
oakengine_encoding_image_sequence_digit_count(proposed_filename.toUtf8().constData());
|
||||||
if (current_digit_count < needed_digit_count) {
|
if (current_digit_count < needed_digit_count) {
|
||||||
msg_box(
|
msg_box(
|
||||||
this, QMessageBox::Critical, tr("Invalid filename"),
|
this, QMessageBox::Critical, tr("Invalid filename"),
|
||||||
@@ -549,8 +542,8 @@ void ExportDialog::start_export()
|
|||||||
|
|
||||||
// Validate video resolution
|
// Validate video resolution
|
||||||
if (video_enabled_->isChecked() &&
|
if (video_enabled_->isChecked() &&
|
||||||
(video_tab_->get_selected_codec() == ExportCodec::k_codec_h264 ||
|
(video_tab_->get_selected_codec() == OAKENGINE_ENCODING_CODEC_H264 ||
|
||||||
video_tab_->get_selected_codec() == ExportCodec::k_codec_h265) &&
|
video_tab_->get_selected_codec() == OAKENGINE_ENCODING_CODEC_H265) &&
|
||||||
(video_tab_->width_slider()->get_value() % 2 != 0 ||
|
(video_tab_->width_slider()->get_value() % 2 != 0 ||
|
||||||
video_tab_->height_slider()->get_value() % 2 != 0)) {
|
video_tab_->height_slider()->get_value() % 2 != 0)) {
|
||||||
msg_box(this, QMessageBox::Critical, tr("Invalid Parameters"),
|
msg_box(this, QMessageBox::Critical, tr("Invalid Parameters"),
|
||||||
@@ -558,12 +551,13 @@ void ExportDialog::start_export()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FacadeExportTask *task =
|
OakEngineTask *task = oakengine_task_create_export(
|
||||||
new FacadeExportTask(viewer_node_, generate_params());
|
reinterpret_cast<OakEngineSequence *>(viewer_node_),
|
||||||
|
generate_params());
|
||||||
|
|
||||||
if (export_bkg_box_->isChecked()) {
|
if (export_bkg_box_->isChecked()) {
|
||||||
// Send to TaskManager to export in background
|
// Send to TaskManager to export in background
|
||||||
TaskManager::instance()->add_task(task);
|
oakengine_task_manager_add(task);
|
||||||
this->accept();
|
this->accept();
|
||||||
} else {
|
} else {
|
||||||
// Use modal dialog box
|
// Use modal dialog box
|
||||||
@@ -578,7 +572,7 @@ void ExportDialog::export_finished()
|
|||||||
{
|
{
|
||||||
TaskDialog *td = static_cast<TaskDialog *>(sender());
|
TaskDialog *td = static_cast<TaskDialog *>(sender());
|
||||||
|
|
||||||
if (td->get_task()->is_cancelled()) {
|
if (oakengine_task_is_cancelled(td->get_task())) {
|
||||||
// If this task was cancelled, we stay open so the user can potentially queue another export
|
// If this task was cancelled, we stay open so the user can potentially queue another export
|
||||||
} else {
|
} else {
|
||||||
// Accept this dialog and close
|
// Accept this dialog and close
|
||||||
@@ -600,11 +594,14 @@ void ExportDialog::image_sequence_check_box_changed(bool e)
|
|||||||
QString suffix = current_fileinfo.suffix();
|
QString suffix = current_fileinfo.suffix();
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
if (!Encoder::filename_contains_digit_placeholder(basename)) {
|
if (!oakengine_encoding_filename_contains_digit_placeholder(basename.toUtf8().constData())) {
|
||||||
basename.append(QStringLiteral("_[#####]"));
|
basename.append(QStringLiteral("_[#####]"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
basename = Encoder::filename_remove_digit_placeholder(basename);
|
char buf[1024];
|
||||||
|
oakengine_encoding_filename_remove_digit_placeholder(
|
||||||
|
basename.toUtf8().constData(), buf, sizeof(buf));
|
||||||
|
basename = QString::fromUtf8(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set filename
|
// Set filename
|
||||||
@@ -636,7 +633,14 @@ void ExportDialog::preset_combo_box_changed()
|
|||||||
if (preset_number == k_preset_default) {
|
if (preset_number == k_preset_default) {
|
||||||
set_defaults();
|
set_defaults();
|
||||||
} else if (preset_number == k_preset_last_used) {
|
} else if (preset_number == k_preset_last_used) {
|
||||||
set_params(viewer_node_->get_last_used_encoding_params());
|
OakEngineEncodingParams *last =
|
||||||
|
oakengine_encoding_params_get_last_used(
|
||||||
|
reinterpret_cast<OakEngineSequence *>(viewer_node_));
|
||||||
|
if (last) {
|
||||||
|
set_params(last);
|
||||||
|
} else {
|
||||||
|
set_defaults();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
set_params(presets_.at(preset_number));
|
set_params(presets_.at(preset_number));
|
||||||
}
|
}
|
||||||
@@ -653,12 +657,17 @@ void ExportDialog::add_preferences_tab(QWidget *inner_widget,
|
|||||||
|
|
||||||
void ExportDialog::browse_filename()
|
void ExportDialog::browse_filename()
|
||||||
{
|
{
|
||||||
ExportFormat::Format f = format_combobox_->get_format();
|
int f = format_combobox_->get_format();
|
||||||
|
|
||||||
|
char name_buf[256];
|
||||||
|
char ext_buf[64];
|
||||||
|
oakengine_encoding_format_name(f, name_buf, sizeof(name_buf));
|
||||||
|
oakengine_encoding_format_extension(f, ext_buf, sizeof(ext_buf));
|
||||||
|
|
||||||
QString browsed_fn = QFileDialog::getSaveFileName(
|
QString browsed_fn = QFileDialog::getSaveFileName(
|
||||||
this, "", filename_edit_->text().trimmed(),
|
this, "", filename_edit_->text().trimmed(),
|
||||||
QStringLiteral("%1 (*.%2)")
|
QStringLiteral("%1 (*.%2)")
|
||||||
.arg(ExportFormat::get_name(f), ExportFormat::get_extension(f)),
|
.arg(QString::fromUtf8(name_buf), QString::fromUtf8(ext_buf)),
|
||||||
nullptr,
|
nullptr,
|
||||||
|
|
||||||
// We don't confirm overwrite here because we do it later
|
// We don't confirm overwrite here because we do it later
|
||||||
@@ -669,12 +678,14 @@ void ExportDialog::browse_filename()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::format_changed(ExportFormat::Format current_format)
|
void ExportDialog::format_changed(int current_format)
|
||||||
{
|
{
|
||||||
QString current_filename = filename_edit_->text().trimmed();
|
QString current_filename = filename_edit_->text().trimmed();
|
||||||
QString previously_selected_ext =
|
char ext_buf[64];
|
||||||
ExportFormat::get_extension(previously_selected_format_);
|
oakengine_encoding_format_extension(previously_selected_format_, ext_buf, sizeof(ext_buf));
|
||||||
QString currently_selected_ext = ExportFormat::get_extension(current_format);
|
QString previously_selected_ext = QString::fromUtf8(ext_buf);
|
||||||
|
oakengine_encoding_format_extension(current_format, ext_buf, sizeof(ext_buf));
|
||||||
|
QString currently_selected_ext = QString::fromUtf8(ext_buf);
|
||||||
|
|
||||||
// If the previous extension was added, remove it
|
// If the previous extension was added, remove it
|
||||||
if (current_filename.endsWith(previously_selected_ext,
|
if (current_filename.endsWith(previously_selected_ext,
|
||||||
@@ -742,25 +753,45 @@ void ExportDialog::load_presets()
|
|||||||
|
|
||||||
preset_combobox_->addItem(tr("Default"), k_preset_default);
|
preset_combobox_->addItem(tr("Default"), k_preset_default);
|
||||||
|
|
||||||
if (viewer_node_->get_last_used_encoding_params().is_valid()) {
|
if (oakengine_encoding_params_get_last_used(
|
||||||
|
reinterpret_cast<OakEngineSequence *>(viewer_node_)) != nullptr) {
|
||||||
preset_combobox_->addItem(tr("Last Used"), k_preset_last_used);
|
preset_combobox_->addItem(tr("Last Used"), k_preset_last_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
preset_combobox_->insertSeparator(preset_combobox_->count());
|
preset_combobox_->insertSeparator(preset_combobox_->count());
|
||||||
|
|
||||||
QStringList l = EncodingParams::get_list_of_presets();
|
QStringList l;
|
||||||
|
{
|
||||||
|
const int n = oakengine_encoding_preset_count();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
char name_buf[256];
|
||||||
|
if (oakengine_encoding_preset_name(
|
||||||
|
i, name_buf, static_cast<int>(sizeof(name_buf))) > 0) {
|
||||||
|
l.append(QString::fromUtf8(name_buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
presets_.reserve(l.size());
|
presets_.reserve(l.size());
|
||||||
|
|
||||||
for (const QString &preset : l) {
|
for (const QString &preset : l) {
|
||||||
EncodingParams p;
|
OakEngineEncodingParams *p = oakengine_encoding_params_create();
|
||||||
|
|
||||||
QFile f(EncodingParams::get_preset_path().filePath(preset));
|
char preset_path_buf[1024];
|
||||||
if (f.open(QFile::ReadOnly)) {
|
preset_path_buf[0] = '\0';
|
||||||
if (p.load(&f)) {
|
oakengine_encoding_preset_path(
|
||||||
|
preset_path_buf, static_cast<int>(sizeof(preset_path_buf)));
|
||||||
|
|
||||||
|
const QByteArray preset_path_utf =
|
||||||
|
QDir(QString::fromUtf8(preset_path_buf))
|
||||||
|
.filePath(preset)
|
||||||
|
.toUtf8();
|
||||||
|
const int rc = oakengine_encoding_params_load_file(
|
||||||
|
p, preset_path_utf.constData());
|
||||||
|
if (rc == OAKENGINE_OK) {
|
||||||
preset_combobox_->addItem(preset, int(presets_.size()));
|
preset_combobox_->addItem(preset, int(presets_.size()));
|
||||||
presets_.push_back(p);
|
presets_.push_back(p);
|
||||||
}
|
} else {
|
||||||
f.close();
|
oakengine_encoding_params_destroy(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,13 +802,17 @@ void ExportDialog::set_default_filename()
|
|||||||
{
|
{
|
||||||
Project *p = viewer_node_->project();
|
Project *p = viewer_node_->project();
|
||||||
|
|
||||||
|
char fn_buf[512];
|
||||||
|
oakengine_project_filename(
|
||||||
|
reinterpret_cast<OakEngineProject *>(p),
|
||||||
|
fn_buf, sizeof(fn_buf));
|
||||||
QDir doc_location;
|
QDir doc_location;
|
||||||
|
|
||||||
if (p->filename().isEmpty()) {
|
if (fn_buf[0] == '\0') {
|
||||||
doc_location.setPath(QStandardPaths::writableLocation(
|
doc_location.setPath(QStandardPaths::writableLocation(
|
||||||
QStandardPaths::DocumentsLocation));
|
QStandardPaths::DocumentsLocation));
|
||||||
} else {
|
} else {
|
||||||
doc_location = QFileInfo(p->filename()).dir();
|
doc_location = QFileInfo(fn_buf).dir();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString file_location = doc_location.filePath(viewer_node_->get_label());
|
QString file_location = doc_location.filePath(viewer_node_->get_label());
|
||||||
@@ -801,14 +836,14 @@ bool ExportDialog::sequence_has_subtitles() const
|
|||||||
void ExportDialog::set_defaults()
|
void ExportDialog::set_defaults()
|
||||||
{
|
{
|
||||||
if (!stills_only_mode_) {
|
if (!stills_only_mode_) {
|
||||||
format_combobox_->set_format(ExportFormat::k_format_mpe_g4_video);
|
format_combobox_->set_format(OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO);
|
||||||
} else {
|
} else {
|
||||||
format_combobox_->set_format(ExportFormat::k_format_png);
|
format_combobox_->set_format(OAKENGINE_ENCODING_FORMAT_PNG);
|
||||||
}
|
}
|
||||||
format_changed(format_combobox_->get_format());
|
format_changed(format_combobox_->get_format());
|
||||||
|
|
||||||
VideoParams vp = viewer_node_->get_video_params();
|
VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||||
AudioParams ap = viewer_node_->get_audio_params();
|
AudioParams ap = viewer_output_audio_params(viewer_node_);
|
||||||
|
|
||||||
video_tab_->width_slider()->set_value(vp.width());
|
video_tab_->width_slider()->set_value(vp.width());
|
||||||
video_tab_->width_slider()->SetDefaultValue(vp.width());
|
video_tab_->width_slider()->SetDefaultValue(vp.width());
|
||||||
@@ -826,82 +861,117 @@ void ExportDialog::set_defaults()
|
|||||||
audio_tab_->channel_layout_combobox()->set_channel_layout(
|
audio_tab_->channel_layout_combobox()->set_channel_layout(
|
||||||
ap.channel_layout());
|
ap.channel_layout());
|
||||||
subtitles_enabled_->setChecked(sequence_has_subtitles());
|
subtitles_enabled_->setChecked(sequence_has_subtitles());
|
||||||
subtitle_tab_->set_sidecar_format(ExportFormat::k_format_srt);
|
subtitle_tab_->set_sidecar_format(OAKENGINE_ENCODING_FORMAT_SRT);
|
||||||
}
|
}
|
||||||
|
|
||||||
EncodingParams ExportDialog::generate_params() const
|
OakEngineEncodingParams *ExportDialog::generate_params() const
|
||||||
{
|
{
|
||||||
VideoParams video_render_params(
|
OakEngineEncodingParams *params = oakengine_encoding_params_create();
|
||||||
static_cast<int>(video_tab_->width_slider()->get_value()),
|
|
||||||
static_cast<int>(video_tab_->height_slider()->get_value()),
|
|
||||||
get_selected_timebase(),
|
|
||||||
video_tab_->pixel_format_field()->get_pixel_format(),
|
|
||||||
VideoParams::k_internal_channel_count,
|
|
||||||
video_tab_->pixel_aspect_combobox()->get_pixel_aspect_ratio(),
|
|
||||||
video_tab_->interlaced_combobox()->get_interlace_mode(), 1);
|
|
||||||
|
|
||||||
AudioParams audio_render_params(
|
oakengine_encoding_params_set_format(
|
||||||
audio_tab_->sample_rate_combobox()->get_sample_rate(),
|
params, format_combobox_->get_format());
|
||||||
audio_tab_->channel_layout_combobox()->get_channel_layout(),
|
oakengine_encoding_params_set_filename(
|
||||||
audio_tab_->sample_format_combobox()->get_sample_format());
|
params, filename_edit_->text().trimmed().toUtf8().constData());
|
||||||
|
|
||||||
EncodingParams params;
|
const Rational export_len = viewer_node_->get_length();
|
||||||
params.set_format(format_combobox_->get_format());
|
oakengine_encoding_params_set_export_length(
|
||||||
params.set_filename(filename_edit_->text().trimmed());
|
params, export_len.numerator(), export_len.denominator());
|
||||||
params.set_export_length(viewer_node_->get_length());
|
|
||||||
|
|
||||||
if (ExportCodec::is_codec_a_still_image(video_tab_->get_selected_codec()) &&
|
if (oakengine_encoding_codec_is_still_image(video_tab_->get_selected_codec()) &&
|
||||||
!video_tab_->is_image_sequence_set()) {
|
!video_tab_->is_image_sequence_set()) {
|
||||||
// Exporting as image without exporting image sequence, only export one frame
|
// Exporting as image without exporting image sequence, only export one frame
|
||||||
Rational export_time = video_tab_->get_still_image_time();
|
Rational export_time = video_tab_->get_still_image_time();
|
||||||
params.set_custom_range(
|
const Rational tb = get_selected_timebase();
|
||||||
TimeRange(export_time, export_time + get_selected_timebase()));
|
oakengine_encoding_params_set_custom_range(
|
||||||
|
params, export_time.numerator(), export_time.denominator(),
|
||||||
|
(export_time + tb).numerator(),
|
||||||
|
(export_time + tb).denominator());
|
||||||
} else if (range_combobox_->currentIndex() == k_range_in_to_out) {
|
} else if (range_combobox_->currentIndex() == k_range_in_to_out) {
|
||||||
// Assume if this combobox is enabled, workarea is enabled - a check that we make in this dialog's constructor
|
const TimeRange &r = viewer_node_->get_work_area()->range();
|
||||||
params.set_custom_range(viewer_node_->get_work_area()->range());
|
oakengine_encoding_params_set_custom_range(
|
||||||
|
params, r.in().numerator(), r.in().denominator(),
|
||||||
|
r.out().numerator(), r.out().denominator());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video_tab_->scaling_method_combobox()->isEnabled()) {
|
if (video_tab_->scaling_method_combobox()->isEnabled()) {
|
||||||
params.set_video_scaling_method(
|
oakengine_encoding_params_set_video_scaling_method(
|
||||||
static_cast<EncodingParams::VideoScalingMethod>(
|
params,
|
||||||
video_tab_->scaling_method_combobox()->currentData().toInt()));
|
video_tab_->scaling_method_combobox()->currentData().toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video_enabled_->isChecked()) {
|
if (video_enabled_->isChecked()) {
|
||||||
ExportCodec::Codec video_codec = video_tab_->get_selected_codec();
|
const int video_codec = video_tab_->get_selected_codec();
|
||||||
|
|
||||||
video_render_params.set_color_range(video_tab_->color_range());
|
// Build video params from the tab
|
||||||
|
const int vw = static_cast<int>(video_tab_->width_slider()->get_value());
|
||||||
|
const int vh = static_cast<int>(video_tab_->height_slider()->get_value());
|
||||||
|
const Rational tb = get_selected_timebase();
|
||||||
|
const int pix_fmt = video_tab_->pixel_format_field()->get_pixel_format();
|
||||||
|
const int ch_count = oakengine_video_params_internal_channel_count();
|
||||||
|
const Rational par = video_tab_->pixel_aspect_combobox()->get_pixel_aspect_ratio();
|
||||||
|
const int interlace = video_tab_->interlaced_combobox()->get_interlace_mode();
|
||||||
|
|
||||||
params.enable_video(video_render_params, video_codec);
|
oak_video_params vp = {};
|
||||||
|
vp.width = vw;
|
||||||
|
vp.height = vh;
|
||||||
|
vp.time_base_num = tb.numerator();
|
||||||
|
vp.time_base_den = tb.denominator();
|
||||||
|
vp.format = pix_fmt;
|
||||||
|
vp.pixel_aspect_num = par.numerator();
|
||||||
|
vp.pixel_aspect_den = par.denominator();
|
||||||
|
vp.interlacing = interlace;
|
||||||
|
vp.color_range = video_tab_->color_range();
|
||||||
|
|
||||||
params.set_video_threads(video_tab_->threads());
|
oakengine_encoding_params_enable_video(params, &vp, video_codec);
|
||||||
|
|
||||||
|
oakengine_encoding_params_set_video_threads(
|
||||||
|
params, video_tab_->threads());
|
||||||
|
|
||||||
if (video_tab_->isVisible()) {
|
if (video_tab_->isVisible()) {
|
||||||
video_tab_->get_codec_section()->add_opts(¶ms);
|
video_tab_->get_codec_section()->add_opts(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
params.set_color_transform(video_tab_->current_ocio_color_space());
|
{
|
||||||
|
const QString ct = video_tab_->current_ocio_color_space();
|
||||||
|
oakengine_encoding_params_set_color_transform(
|
||||||
|
params, ct.isEmpty() ? nullptr : ct.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
params.set_video_pix_fmt(video_tab_->pix_fmt());
|
{
|
||||||
|
const QString pix_fmt_name = video_tab_->pix_fmt();
|
||||||
|
oakengine_encoding_params_set_video_pix_fmt(
|
||||||
|
params,
|
||||||
|
pix_fmt_name.isEmpty() ? nullptr
|
||||||
|
: pix_fmt_name.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
params.set_video_is_image_sequence(video_tab_->is_image_sequence_set());
|
oakengine_encoding_params_set_video_is_image_sequence(
|
||||||
|
params, video_tab_->is_image_sequence_set() ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio_enabled_->isChecked()) {
|
if (audio_enabled_->isChecked()) {
|
||||||
ExportCodec::Codec audio_codec = audio_tab_->get_codec();
|
const int audio_codec = audio_tab_->get_codec();
|
||||||
params.enable_audio(audio_render_params, audio_codec);
|
const int sample_rate = audio_tab_->sample_rate_combobox()->get_sample_rate();
|
||||||
|
const uint64_t ch_layout = audio_tab_->channel_layout_combobox()->get_channel_layout();
|
||||||
|
const int sample_fmt = audio_tab_->sample_format_combobox()->get_sample_format();
|
||||||
|
|
||||||
params.set_audio_bit_rate(audio_tab_->bit_rate_slider()->get_value() *
|
oakengine_encoding_params_enable_audio(
|
||||||
1000);
|
params, sample_rate, ch_layout, sample_fmt, audio_codec);
|
||||||
|
|
||||||
|
oakengine_encoding_params_set_audio_bit_rate(
|
||||||
|
params,
|
||||||
|
audio_tab_->bit_rate_slider()->get_value() * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subtitles_enabled_->isEnabled() && subtitles_enabled_->isChecked()) {
|
if (subtitles_enabled_->isEnabled() && subtitles_enabled_->isChecked()) {
|
||||||
if (!subtitle_tab_->get_sidecar_enabled()) {
|
if (!subtitle_tab_->get_sidecar_enabled()) {
|
||||||
// Export subtitles embedded in container
|
// Export subtitles embedded in container
|
||||||
params.enable_subtitles(subtitle_tab_->get_subtitle_codec());
|
oakengine_encoding_params_enable_subtitles(
|
||||||
|
params, subtitle_tab_->get_subtitle_codec());
|
||||||
} else {
|
} else {
|
||||||
// Export subtitles to a sidecar file
|
// Export subtitles to a sidecar file
|
||||||
params.enable_sidecar_subtitles(subtitle_tab_->get_sidecar_format(),
|
oakengine_encoding_params_enable_sidecar_subtitles(
|
||||||
|
params, subtitle_tab_->get_sidecar_format(),
|
||||||
subtitle_tab_->get_subtitle_codec());
|
subtitle_tab_->get_subtitle_codec());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -909,68 +979,99 @@ EncodingParams ExportDialog::generate_params() const
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::set_params(const EncodingParams &e)
|
void ExportDialog::set_params(const OakEngineEncodingParams *e)
|
||||||
{
|
{
|
||||||
format_combobox_->set_format(e.format());
|
format_combobox_->set_format(oakengine_encoding_params_format(e));
|
||||||
format_changed(format_combobox_->get_format());
|
format_changed(format_combobox_->get_format());
|
||||||
|
|
||||||
if (e.has_custom_range() && viewer_node_->get_work_area()->enabled()) {
|
if (oakengine_encoding_params_has_custom_range(e) &&
|
||||||
|
viewer_node_->get_work_area()->enabled()) {
|
||||||
range_combobox_->setCurrentIndex(k_range_in_to_out);
|
range_combobox_->setCurrentIndex(k_range_in_to_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
QtUtils::set_combo_box_data(video_tab_->scaling_method_combobox(),
|
QtUtils::set_combo_box_data(video_tab_->scaling_method_combobox(),
|
||||||
e.video_scaling_method());
|
oakengine_encoding_params_video_scaling_method(e));
|
||||||
|
|
||||||
video_enabled_->setChecked(e.video_enabled());
|
const int video_enabled = oakengine_encoding_params_video_enabled(e);
|
||||||
if (e.video_enabled()) {
|
video_enabled_->setChecked(video_enabled);
|
||||||
video_tab_->width_slider()->set_value(e.video_params().width());
|
if (video_enabled) {
|
||||||
video_tab_->height_slider()->set_value(e.video_params().height());
|
oak_video_params vp = {};
|
||||||
set_selected_timebase(e.video_params().time_base());
|
oakengine_encoding_params_get_video_params(e, &vp);
|
||||||
|
|
||||||
|
video_tab_->width_slider()->set_value(vp.width);
|
||||||
|
video_tab_->height_slider()->set_value(vp.height);
|
||||||
|
set_selected_timebase(Rational(vp.time_base_num, vp.time_base_den));
|
||||||
video_tab_->pixel_format_field()->set_pixel_format(
|
video_tab_->pixel_format_field()->set_pixel_format(
|
||||||
e.video_params().format());
|
static_cast<olive::core::PixelFormat::Format>(vp.format));
|
||||||
video_tab_->pixel_aspect_combobox()->set_pixel_aspect_ratio(
|
video_tab_->pixel_aspect_combobox()->set_pixel_aspect_ratio(
|
||||||
e.video_params().pixel_aspect_ratio());
|
Rational(vp.pixel_aspect_num, vp.pixel_aspect_den));
|
||||||
video_tab_->interlaced_combobox()->set_interlace_mode(
|
video_tab_->interlaced_combobox()->set_interlace_mode(vp.interlacing);
|
||||||
e.video_params().interlacing());
|
|
||||||
|
|
||||||
video_tab_->set_selected_codec(e.video_codec());
|
video_tab_->set_selected_codec(oakengine_encoding_params_video_codec(e));
|
||||||
|
|
||||||
video_tab_->set_color_range(e.video_params().color_range());
|
video_tab_->set_color_range(vp.color_range);
|
||||||
|
|
||||||
video_tab_->set_threads(e.video_threads());
|
video_tab_->set_threads(oakengine_encoding_params_video_threads(e));
|
||||||
|
|
||||||
if (video_tab_->isVisible()) {
|
if (video_tab_->isVisible()) {
|
||||||
video_tab_->get_codec_section()->set_opts(&e);
|
video_tab_->get_codec_section()->set_opts(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
video_tab_->set_ocio_color_space(e.color_transform().output());
|
{
|
||||||
|
char ct_buf[128];
|
||||||
video_tab_->set_pix_fmt(e.video_pix_fmt());
|
if (oakengine_encoding_params_color_transform_output(
|
||||||
|
e, ct_buf, static_cast<int>(sizeof(ct_buf))) > 0) {
|
||||||
video_tab_->set_image_sequence(e.video_is_image_sequence());
|
video_tab_->set_ocio_color_space(QString::fromUtf8(ct_buf));
|
||||||
|
} else {
|
||||||
|
video_tab_->set_ocio_color_space(QString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_enabled_->setChecked(e.audio_enabled());
|
{
|
||||||
if (e.audio_enabled()) {
|
char pix_fmt_buf[64];
|
||||||
audio_tab_->sample_rate_combobox()->set_sample_rate(
|
if (oakengine_encoding_params_video_pix_fmt(
|
||||||
e.audio_params().sample_rate());
|
e, pix_fmt_buf, static_cast<int>(sizeof(pix_fmt_buf))) > 0) {
|
||||||
audio_tab_->channel_layout_combobox()->set_channel_layout(
|
video_tab_->set_pix_fmt(QString::fromUtf8(pix_fmt_buf));
|
||||||
e.audio_params().channel_layout());
|
} else {
|
||||||
|
video_tab_->set_pix_fmt(QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
video_tab_->set_image_sequence(
|
||||||
|
oakengine_encoding_params_video_is_image_sequence(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
const int audio_enabled = oakengine_encoding_params_audio_enabled(e);
|
||||||
|
audio_enabled_->setChecked(audio_enabled);
|
||||||
|
if (audio_enabled) {
|
||||||
|
int asample_rate = 0;
|
||||||
|
uint64_t ach_layout = 0;
|
||||||
|
int asample_fmt = 0;
|
||||||
|
oakengine_encoding_params_get_audio_params(
|
||||||
|
e, &asample_rate, &ach_layout, &asample_fmt);
|
||||||
|
|
||||||
|
audio_tab_->sample_rate_combobox()->set_sample_rate(asample_rate);
|
||||||
|
audio_tab_->channel_layout_combobox()->set_channel_layout(ach_layout);
|
||||||
audio_tab_->sample_format_combobox()->set_sample_format(
|
audio_tab_->sample_format_combobox()->set_sample_format(
|
||||||
e.audio_params().format());
|
static_cast<olive::core::SampleFormat::Format>(asample_fmt));
|
||||||
|
|
||||||
audio_tab_->set_codec(e.audio_codec());
|
audio_tab_->set_codec(oakengine_encoding_params_audio_codec(e));
|
||||||
|
|
||||||
audio_tab_->bit_rate_slider()->set_value(e.audio_bit_rate() / 1000);
|
audio_tab_->bit_rate_slider()->set_value(
|
||||||
|
oakengine_encoding_params_audio_bit_rate(e) / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subtitles_enabled_->isEnabled()) {
|
if (subtitles_enabled_->isEnabled()) {
|
||||||
subtitles_enabled_->setChecked(e.subtitles_enabled());
|
const int subs_enabled = oakengine_encoding_params_subtitles_enabled(e);
|
||||||
subtitle_tab_->set_sidecar_enabled(e.subtitles_are_sidecar());
|
subtitles_enabled_->setChecked(subs_enabled);
|
||||||
if (e.subtitles_enabled()) {
|
subtitle_tab_->set_sidecar_enabled(
|
||||||
subtitle_tab_->set_subtitle_codec(e.subtitles_codec());
|
oakengine_encoding_params_subtitles_are_sidecar(e));
|
||||||
if (e.subtitles_are_sidecar()) {
|
if (subs_enabled) {
|
||||||
subtitle_tab_->set_sidecar_format(e.subtitle_sidecar_fmt());
|
subtitle_tab_->set_subtitle_codec(
|
||||||
|
oakengine_encoding_params_subtitles_codec(e));
|
||||||
|
if (oakengine_encoding_params_subtitles_are_sidecar(e)) {
|
||||||
|
subtitle_tab_->set_sidecar_format(
|
||||||
|
oakengine_encoding_params_subtitles_sidecar_format(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -997,7 +1098,10 @@ void ExportDialog::done(int r)
|
|||||||
preview_viewer_->connect_viewer_node(nullptr);
|
preview_viewer_->connect_viewer_node(nullptr);
|
||||||
|
|
||||||
if (!stills_only_mode_) {
|
if (!stills_only_mode_) {
|
||||||
viewer_node_->set_last_used_encoding_params(generate_params());
|
OakEngineEncodingParams *p = generate_params();
|
||||||
|
oakengine_encoding_params_set_last_used(
|
||||||
|
reinterpret_cast<OakEngineSequence *>(viewer_node_), p);
|
||||||
|
oakengine_encoding_params_destroy(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
super::done(r);
|
super::done(r);
|
||||||
@@ -1024,14 +1128,16 @@ void ExportDialog::update_viewer_dimensions()
|
|||||||
static_cast<int>(video_tab_->width_slider()->get_value()),
|
static_cast<int>(video_tab_->width_slider()->get_value()),
|
||||||
static_cast<int>(video_tab_->height_slider()->get_value()));
|
static_cast<int>(video_tab_->height_slider()->get_value()));
|
||||||
|
|
||||||
VideoParams vp = viewer_node_->get_video_params();
|
VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||||
|
|
||||||
QMatrix4x4 transform = EncodingParams::generate_matrix(
|
float mat16[16];
|
||||||
static_cast<EncodingParams::VideoScalingMethod>(
|
oakengine_encoding_generate_matrix(
|
||||||
video_tab_->scaling_method_combobox()->currentData().toInt()),
|
video_tab_->scaling_method_combobox()->currentData().toInt(),
|
||||||
vp.width(), vp.height(),
|
vp.width(), vp.height(),
|
||||||
static_cast<int>(video_tab_->width_slider()->get_value()),
|
static_cast<int>(video_tab_->width_slider()->get_value()),
|
||||||
static_cast<int>(video_tab_->height_slider()->get_value()));
|
static_cast<int>(video_tab_->height_slider()->get_value()),
|
||||||
|
mat16);
|
||||||
|
QMatrix4x4 transform(mat16);
|
||||||
|
|
||||||
preview_viewer_->set_matrix(transform);
|
preview_viewer_->set_matrix(transform);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,17 @@
|
|||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <cstdint>
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "codec/encoder.h"
|
||||||
#include "codec/exportcodec.h"
|
|
||||||
#include "codec/exportformat.h"
|
|
||||||
#include "dialog/export/exportformatcombobox.h"
|
#include "dialog/export/exportformatcombobox.h"
|
||||||
#include "exportaudiotab.h"
|
#include "exportaudiotab.h"
|
||||||
#include "exportsubtitlestab.h"
|
#include "exportsubtitlestab.h"
|
||||||
#include "exportvideotab.h"
|
#include "exportvideotab.h"
|
||||||
|
#include "oakengine/encoding.h"
|
||||||
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
|
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
|
||||||
#include "widget/viewer/viewer.h"
|
#include "widget/viewer/viewer.h"
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@ public:
|
|||||||
Rational get_selected_timebase() const;
|
Rational get_selected_timebase() const;
|
||||||
void set_selected_timebase(const Rational &r);
|
void set_selected_timebase(const Rational &r);
|
||||||
|
|
||||||
EncodingParams generate_params() const;
|
OakEngineEncodingParams *generate_params() const;
|
||||||
void set_params(const EncodingParams &e);
|
void set_params(const OakEngineEncodingParams *e);
|
||||||
|
|
||||||
virtual bool eventFilter(QObject *o, QEvent *e) override;
|
virtual bool eventFilter(QObject *o, QEvent *e) override;
|
||||||
|
|
||||||
@@ -77,7 +77,9 @@ private:
|
|||||||
|
|
||||||
ViewerOutput *viewer_node_;
|
ViewerOutput *viewer_node_;
|
||||||
|
|
||||||
ExportFormat::Format previously_selected_format_;
|
int64_t viewer_sub_ = 0;
|
||||||
|
|
||||||
|
int previously_selected_format_;
|
||||||
|
|
||||||
Rational get_export_length() const;
|
Rational get_export_length() const;
|
||||||
int64_t get_export_length_in_timebase_units() const;
|
int64_t get_export_length_in_timebase_units() const;
|
||||||
@@ -93,7 +95,7 @@ private:
|
|||||||
|
|
||||||
QComboBox *preset_combobox_;
|
QComboBox *preset_combobox_;
|
||||||
QComboBox *range_combobox_;
|
QComboBox *range_combobox_;
|
||||||
std::vector<EncodingParams> presets_;
|
std::vector<OakEngineEncodingParams *> presets_;
|
||||||
|
|
||||||
QCheckBox *video_enabled_;
|
QCheckBox *video_enabled_;
|
||||||
QCheckBox *audio_enabled_;
|
QCheckBox *audio_enabled_;
|
||||||
@@ -109,7 +111,7 @@ private:
|
|||||||
|
|
||||||
double video_aspect_ratio_;
|
double video_aspect_ratio_;
|
||||||
|
|
||||||
ColorManager *color_manager_;
|
OakEngineColorManager *color_manager_;
|
||||||
|
|
||||||
QWidget *preferences_area_;
|
QWidget *preferences_area_;
|
||||||
QCheckBox *export_bkg_box_;
|
QCheckBox *export_bkg_box_;
|
||||||
@@ -122,7 +124,7 @@ private:
|
|||||||
private slots:
|
private slots:
|
||||||
void browse_filename();
|
void browse_filename();
|
||||||
|
|
||||||
void format_changed(ExportFormat::Format current_format);
|
void format_changed(int current_format);
|
||||||
|
|
||||||
void resolution_changed();
|
void resolution_changed();
|
||||||
|
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ public:
|
|||||||
pixel_format_combobox_->setCurrentText(s);
|
pixel_format_combobox_->setCurrentText(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoParams::ColorRange yuv_range() const
|
int yuv_range() const
|
||||||
{
|
{
|
||||||
return static_cast<VideoParams::ColorRange>(
|
return static_cast<int>(
|
||||||
yuv_color_range_combobox_->currentIndex());
|
yuv_color_range_combobox_->currentIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_yuv_range(VideoParams::ColorRange i)
|
void set_yuv_range(int i)
|
||||||
{
|
{
|
||||||
yuv_color_range_combobox_->setCurrentIndex(i);
|
yuv_color_range_combobox_->setCurrentIndex(i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include <olive/core/core.h>
|
||||||
|
#include "oakengine/encoding.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -87,14 +90,17 @@ ExportAudioTab::ExportAudioTab(QWidget *parent)
|
|||||||
outer_layout->addStretch();
|
outer_layout->addStretch();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExportAudioTab::set_format(ExportFormat::Format format)
|
int ExportAudioTab::set_format(int format)
|
||||||
{
|
{
|
||||||
QList<ExportCodec::Codec> acodecs = ExportFormat::get_audio_codecs(format);
|
const int acodec_count = oakengine_encoding_format_audio_codec_count(format);
|
||||||
setEnabled(!acodecs.isEmpty());
|
setEnabled(acodec_count > 0);
|
||||||
codec_combobox_->blockSignals(true);
|
codec_combobox_->blockSignals(true);
|
||||||
codec_combobox_->clear();
|
codec_combobox_->clear();
|
||||||
foreach (ExportCodec::Codec acodec, acodecs) {
|
for (int i = 0; i < acodec_count; i++) {
|
||||||
codec_combobox_->addItem(ExportCodec::get_codec_name(acodec), acodec);
|
int codec = oakengine_encoding_format_audio_codec_at(format, i);
|
||||||
|
char buf[256];
|
||||||
|
oakengine_encoding_codec_name(codec, buf, sizeof(buf));
|
||||||
|
codec_combobox_->addItem(QString::fromUtf8(buf), codec);
|
||||||
}
|
}
|
||||||
codec_combobox_->blockSignals(false);
|
codec_combobox_->blockSignals(false);
|
||||||
fmt_ = format;
|
fmt_ = format;
|
||||||
@@ -102,18 +108,25 @@ int ExportAudioTab::set_format(ExportFormat::Format format)
|
|||||||
update_sample_formats();
|
update_sample_formats();
|
||||||
update_bit_rate_enabled();
|
update_bit_rate_enabled();
|
||||||
|
|
||||||
return acodecs.size();
|
return acodec_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportAudioTab::update_sample_formats()
|
void ExportAudioTab::update_sample_formats()
|
||||||
{
|
{
|
||||||
auto fmts = ExportFormat::get_sample_formats_for_codec(fmt_, get_codec());
|
// Use oakengine to get sample format values and build the vector
|
||||||
|
const int count = oakengine_encoding_sample_format_count(fmt_, get_codec());
|
||||||
|
std::vector<olive::core::SampleFormat> fmts;
|
||||||
|
fmts.reserve(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int val = oakengine_encoding_sample_format_at(fmt_, get_codec(), i);
|
||||||
|
fmts.push_back(olive::core::SampleFormat(static_cast<olive::core::SampleFormat::Format>(val)));
|
||||||
|
}
|
||||||
sample_format_combobox_->set_available_formats(fmts);
|
sample_format_combobox_->set_available_formats(fmts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportAudioTab::update_bit_rate_enabled()
|
void ExportAudioTab::update_bit_rate_enabled()
|
||||||
{
|
{
|
||||||
bool uses_bitrate = !ExportCodec::is_codec_lossless(get_codec());
|
bool uses_bitrate = !oakengine_encoding_codec_is_lossless(get_codec());
|
||||||
bit_rate_slider_->setEnabled(uses_bitrate);
|
bit_rate_slider_->setEnabled(uses_bitrate);
|
||||||
|
|
||||||
if (!uses_bitrate) {
|
if (!uses_bitrate) {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "codec/exportformat.h"
|
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
#include "widget/standardcombos/standardcombos.h"
|
#include "widget/standardcombos/standardcombos.h"
|
||||||
|
|
||||||
@@ -38,13 +37,12 @@ class ExportAudioTab : public QWidget {
|
|||||||
public:
|
public:
|
||||||
ExportAudioTab(QWidget *parent = nullptr);
|
ExportAudioTab(QWidget *parent = nullptr);
|
||||||
|
|
||||||
ExportCodec::Codec get_codec() const
|
int get_codec() const
|
||||||
{
|
{
|
||||||
return static_cast<ExportCodec::Codec>(
|
return codec_combobox_->currentData().toInt();
|
||||||
codec_combobox_->currentData().toInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_codec(ExportCodec::Codec c)
|
void set_codec(int c)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < codec_combobox_->count(); i++) {
|
for (int i = 0; i < codec_combobox_->count(); i++) {
|
||||||
if (codec_combobox_->itemData(i) == c) {
|
if (codec_combobox_->itemData(i) == c) {
|
||||||
@@ -75,10 +73,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
int set_format(ExportFormat::Format format);
|
int set_format(int format);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExportFormat::Format fmt_;
|
int fmt_;
|
||||||
QComboBox *codec_combobox_;
|
QComboBox *codec_combobox_;
|
||||||
SampleRateComboBox *sample_rate_combobox_;
|
SampleRateComboBox *sample_rate_combobox_;
|
||||||
ChannelLayoutComboBox *channel_layout_combobox_;
|
ChannelLayoutComboBox *channel_layout_combobox_;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include "oakengine/encoding.h"
|
||||||
#include "ui/icons/icons.h"
|
#include "ui/icons/icons.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
@@ -32,6 +33,10 @@ namespace olive
|
|||||||
ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent)
|
ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent)
|
||||||
: QComboBox(parent)
|
: QComboBox(parent)
|
||||||
{
|
{
|
||||||
|
// The invalid placeholder format is the format count itself
|
||||||
|
// (ExportFormat::k_format_count), not -1.
|
||||||
|
current_ = oakengine_encoding_format_count();
|
||||||
|
|
||||||
custom_menu_ = new Menu(this);
|
custom_menu_ = new Menu(this);
|
||||||
|
|
||||||
// Populate combobox formats
|
// Populate combobox formats
|
||||||
@@ -69,43 +74,45 @@ void ExportFormatComboBox::showPopup()
|
|||||||
custom_menu_->exec(mapToGlobal(QPoint(0, 0)));
|
custom_menu_->exec(mapToGlobal(QPoint(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportFormatComboBox::set_format(ExportFormat::Format fmt)
|
void ExportFormatComboBox::set_format(int fmt)
|
||||||
{
|
{
|
||||||
current_ = fmt;
|
current_ = fmt;
|
||||||
clear();
|
clear();
|
||||||
addItem(ExportFormat::get_name(current_));
|
char buf[256];
|
||||||
|
oakengine_encoding_format_name(fmt, buf, sizeof(buf));
|
||||||
|
addItem(QString::fromUtf8(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportFormatComboBox::handle_index_change(QAction *a)
|
void ExportFormatComboBox::handle_index_change(QAction *a)
|
||||||
{
|
{
|
||||||
ExportFormat::Format f =
|
int f = a->data().toInt();
|
||||||
static_cast<ExportFormat::Format>(a->data().toInt());
|
|
||||||
set_format(f);
|
set_format(f);
|
||||||
emit format_changed(f);
|
emit format_changed(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportFormatComboBox::populate_type(Track::Type type)
|
void ExportFormatComboBox::populate_type(Track::Type type)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ExportFormat::k_format_count; i++) {
|
const int fmt_count = oakengine_encoding_format_count();
|
||||||
ExportFormat::Format f = static_cast<ExportFormat::Format>(i);
|
for (int i = 0; i < fmt_count; i++) {
|
||||||
|
int f = i;
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
if (type == Track::k_video &&
|
bool has_video = oakengine_encoding_format_video_codec_count(f) > 0;
|
||||||
!ExportFormat::get_video_codecs(f).isEmpty()) {
|
bool has_audio = oakengine_encoding_format_audio_codec_count(f) > 0;
|
||||||
|
bool has_sub = oakengine_encoding_format_subtitle_codec_count(f) > 0;
|
||||||
|
|
||||||
|
if (type == Track::k_video && has_video) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else if (type == Track::k_audio &&
|
} else if (type == Track::k_audio && !has_video && has_audio) {
|
||||||
ExportFormat::get_video_codecs(f).isEmpty() &&
|
|
||||||
!ExportFormat::get_audio_codecs(f).isEmpty()) {
|
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else if (type == Track::k_subtitle &&
|
} else if (type == Track::k_subtitle && !has_video && !has_audio && has_sub) {
|
||||||
ExportFormat::get_video_codecs(f).isEmpty() &&
|
|
||||||
ExportFormat::get_audio_codecs(f).isEmpty() &&
|
|
||||||
!ExportFormat::get_subtitle_codecs(f).isEmpty()) {
|
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString format_name = ExportFormat::get_name(f);
|
oakengine_encoding_format_name(f, buf, sizeof(buf));
|
||||||
|
QString format_name = QString::fromUtf8(buf);
|
||||||
|
|
||||||
QAction *a = custom_menu_->addAction(format_name);
|
QAction *a = custom_menu_->addAction(format_name);
|
||||||
a->setData(i);
|
a->setData(i);
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QWidgetAction>
|
#include <QWidgetAction>
|
||||||
|
|
||||||
#include "codec/exportformat.h"
|
|
||||||
#include "node/output/track/track.h"
|
#include "node/output/track/track.h"
|
||||||
#include "widget/menu/menu.h"
|
#include "widget/menu/menu.h"
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportFormat::Format get_format() const
|
int get_format() const
|
||||||
{
|
{
|
||||||
return current_;
|
return current_;
|
||||||
}
|
}
|
||||||
@@ -56,10 +55,10 @@ public:
|
|||||||
void showPopup();
|
void showPopup();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void format_changed(ExportFormat::Format fmt);
|
void format_changed(int fmt);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void set_format(ExportFormat::Format fmt);
|
void set_format(int fmt);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handle_index_change(QAction *a);
|
void handle_index_change(QAction *a);
|
||||||
@@ -71,7 +70,7 @@ private:
|
|||||||
|
|
||||||
Menu *custom_menu_;
|
Menu *custom_menu_;
|
||||||
|
|
||||||
ExportFormat::Format current_ = ExportFormat::k_format_count;
|
int current_ = -1; // was ExportFormat::k_format_count
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "exportsavepresetdialog.h"
|
#include "exportsavepresetdialog.h"
|
||||||
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
|
#include <QDir>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
ExportSavePresetDialog::ExportSavePresetDialog(const EncodingParams &p,
|
ExportSavePresetDialog::ExportSavePresetDialog(const OakEngineEncodingParams *p,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, params_(p)
|
, params_(p)
|
||||||
@@ -39,7 +40,17 @@ ExportSavePresetDialog::ExportSavePresetDialog(const EncodingParams &p,
|
|||||||
name_edit_ = new QLineEdit();
|
name_edit_ = new QLineEdit();
|
||||||
|
|
||||||
// Populate existing list
|
// Populate existing list
|
||||||
QStringList l = EncodingParams::get_list_of_presets();
|
QStringList l;
|
||||||
|
{
|
||||||
|
const int n = oakengine_encoding_preset_count();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
char name_buf[256];
|
||||||
|
if (oakengine_encoding_preset_name(
|
||||||
|
i, name_buf, static_cast<int>(sizeof(name_buf))) > 0) {
|
||||||
|
l.append(QString::fromUtf8(name_buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!l.empty()) {
|
if (!l.empty()) {
|
||||||
auto list_widget = new QListWidget();
|
auto list_widget = new QListWidget();
|
||||||
for (const QString &f : l) {
|
for (const QString &f : l) {
|
||||||
@@ -78,13 +89,17 @@ void ExportSavePresetDialog::accept()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir d(EncodingParams::get_preset_path());
|
char preset_path_buf[1024];
|
||||||
|
preset_path_buf[0] = '\0';
|
||||||
|
oakengine_encoding_preset_path(
|
||||||
|
preset_path_buf, static_cast<int>(sizeof(preset_path_buf)));
|
||||||
|
QDir d(QString::fromUtf8(preset_path_buf));
|
||||||
|
|
||||||
if (!d.exists()) {
|
if (!d.exists()) {
|
||||||
d.mkpath(QStringLiteral("."));
|
d.mkpath(QStringLiteral("."));
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile f(d.filePath(name_edit_->text()));
|
if (d.exists(name_edit_->text())) {
|
||||||
if (f.exists()) {
|
|
||||||
if (QMessageBox::question(
|
if (QMessageBox::question(
|
||||||
this, tr("Overwrite Preset"),
|
this, tr("Overwrite Preset"),
|
||||||
tr("A preset with the name \"%1\" already exists. Do you wish to overwrite it?")
|
tr("A preset with the name \"%1\" already exists. Do you wish to overwrite it?")
|
||||||
@@ -94,17 +109,18 @@ void ExportSavePresetDialog::accept()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!f.open(QFile::WriteOnly)) {
|
const QByteArray full_path =
|
||||||
|
d.filePath(name_edit_->text()).toUtf8();
|
||||||
|
const int rc = oakengine_encoding_params_save_file(
|
||||||
|
params_, full_path.constData());
|
||||||
|
if (rc != OAKENGINE_OK) {
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
this, tr("Write Error"),
|
this, tr("Write Error"),
|
||||||
tr("Failed to open file \"%1\" for writing.").arg(f.fileName()));
|
tr("Failed to save preset to \"%1\".").arg(
|
||||||
|
QString::fromUtf8(full_path)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
params_.save(&f);
|
|
||||||
|
|
||||||
f.close();
|
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "oakengine/encoding.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -34,7 +34,7 @@ namespace olive
|
|||||||
class ExportSavePresetDialog : public QDialog {
|
class ExportSavePresetDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ExportSavePresetDialog(const EncodingParams &p, QWidget *parent = nullptr);
|
ExportSavePresetDialog(const OakEngineEncodingParams *p, QWidget *parent = nullptr);
|
||||||
|
|
||||||
QString get_selected_preset_name() const
|
QString get_selected_preset_name() const
|
||||||
{
|
{
|
||||||
@@ -47,7 +47,7 @@ public slots:
|
|||||||
private:
|
private:
|
||||||
QLineEdit *name_edit_;
|
QLineEdit *name_edit_;
|
||||||
|
|
||||||
EncodingParams params_;
|
const OakEngineEncodingParams *params_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,9 @@
|
|||||||
/*
|
|
||||||
* Oak Video Editor - Non-Linear Video Editor
|
|
||||||
* Copyright (C) 2025 Olive CE 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 "exportsubtitlestab.h"
|
#include "exportsubtitlestab.h"
|
||||||
|
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
|
|
||||||
|
#include "oakengine/encoding.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -62,32 +46,35 @@ ExportSubtitlesTab::ExportSubtitlesTab(QWidget *parent)
|
|||||||
&QWidget::setVisible);
|
&QWidget::setVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExportSubtitlesTab::set_format(ExportFormat::Format format)
|
int ExportSubtitlesTab::set_format(int format)
|
||||||
{
|
{
|
||||||
auto vcodecs = ExportFormat::get_video_codecs(format);
|
const bool has_video = oakengine_encoding_format_video_codec_count(format) > 0;
|
||||||
auto acodecs = ExportFormat::get_audio_codecs(format);
|
const bool has_audio = oakengine_encoding_format_audio_codec_count(format) > 0;
|
||||||
|
int scodec_count = oakengine_encoding_format_subtitle_codec_count(format);
|
||||||
|
|
||||||
auto scodecs = ExportFormat::get_subtitle_codecs(format);
|
if (scodec_count > 0 && !has_video && !has_audio) {
|
||||||
|
|
||||||
if (!scodecs.empty() && vcodecs.empty() && acodecs.empty()) {
|
|
||||||
// If format supports ONLY scodecs, default this to off and disable it
|
// If format supports ONLY scodecs, default this to off and disable it
|
||||||
sidecar_checkbox_->setChecked(false);
|
sidecar_checkbox_->setChecked(false);
|
||||||
sidecar_checkbox_->setEnabled(false);
|
sidecar_checkbox_->setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
// If format does not support scodecs, default this to checked and disable it
|
// If format does not support scodecs, default this to checked and disable it
|
||||||
sidecar_checkbox_->setChecked(scodecs.empty());
|
sidecar_checkbox_->setChecked(scodec_count == 0);
|
||||||
sidecar_checkbox_->setEnabled(!scodecs.empty());
|
sidecar_checkbox_->setEnabled(scodec_count > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
scodecs =
|
// Refresh for sidecar format
|
||||||
ExportFormat::get_subtitle_codecs(sidecar_format_combobox_->get_format());
|
int sidecar_fmt = sidecar_format_combobox_->get_format();
|
||||||
|
scodec_count = oakengine_encoding_format_subtitle_codec_count(sidecar_fmt);
|
||||||
|
|
||||||
codec_combobox_->clear();
|
codec_combobox_->clear();
|
||||||
foreach (ExportCodec::Codec scodec, scodecs) {
|
for (int i = 0; i < scodec_count; i++) {
|
||||||
codec_combobox_->addItem(ExportCodec::get_codec_name(scodec), scodec);
|
int scodec = oakengine_encoding_format_subtitle_codec_at(sidecar_fmt, i);
|
||||||
|
char buf[256];
|
||||||
|
oakengine_encoding_codec_name(scodec, buf, sizeof(buf));
|
||||||
|
codec_combobox_->addItem(QString::fromUtf8(buf), scodec);
|
||||||
}
|
}
|
||||||
|
|
||||||
return scodecs.size();
|
return scodec_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
#include "codec/exportformat.h"
|
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "dialog/export/exportformatcombobox.h"
|
#include "dialog/export/exportformatcombobox.h"
|
||||||
|
|
||||||
@@ -47,24 +46,23 @@ public:
|
|||||||
sidecar_checkbox_->setChecked(e);
|
sidecar_checkbox_->setChecked(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportFormat::Format get_sidecar_format() const
|
int get_sidecar_format() const
|
||||||
{
|
{
|
||||||
return sidecar_format_combobox_->get_format();
|
return sidecar_format_combobox_->get_format();
|
||||||
}
|
}
|
||||||
void set_sidecar_format(ExportFormat::Format f)
|
void set_sidecar_format(int f)
|
||||||
{
|
{
|
||||||
sidecar_format_combobox_->set_format(f);
|
sidecar_format_combobox_->set_format(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
int set_format(ExportFormat::Format format);
|
int set_format(int format);
|
||||||
|
|
||||||
ExportCodec::Codec get_subtitle_codec()
|
int get_subtitle_codec()
|
||||||
{
|
{
|
||||||
return static_cast<ExportCodec::Codec>(
|
return codec_combobox_->currentData().toInt();
|
||||||
codec_combobox_->currentData().toInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_subtitle_codec(ExportCodec::Codec c)
|
void set_subtitle_codec(int c)
|
||||||
{
|
{
|
||||||
QtUtils::set_combo_box_data(codec_combobox_, c);
|
QtUtils::set_combo_box_data(codec_combobox_, c);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,15 +29,16 @@
|
|||||||
|
|
||||||
#include "exportadvancedvideodialog.h"
|
#include "exportadvancedvideodialog.h"
|
||||||
#include "node/color/colormanager/colormanager.h"
|
#include "node/color/colormanager/colormanager.h"
|
||||||
|
#include "oakengine/encoding.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
ExportVideoTab::ExportVideoTab(ColorManager *color_manager, QWidget *parent)
|
ExportVideoTab::ExportVideoTab(OakEngineColorManager *color_manager, QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, color_manager_(color_manager)
|
, color_manager_(color_manager)
|
||||||
, threads_(0)
|
, threads_(0)
|
||||||
, color_range_(VideoParams::k_color_range_default)
|
, color_range_(0) // k_color_range_default
|
||||||
{
|
{
|
||||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
@@ -50,17 +51,20 @@ ExportVideoTab::ExportVideoTab(ColorManager *color_manager, QWidget *parent)
|
|||||||
outer_layout->addStretch();
|
outer_layout->addStretch();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExportVideoTab::set_format(ExportFormat::Format format)
|
int ExportVideoTab::set_format(int format)
|
||||||
{
|
{
|
||||||
format_ = format;
|
format_ = format;
|
||||||
|
|
||||||
QList<ExportCodec::Codec> vcodecs = ExportFormat::get_video_codecs(format);
|
const int vcodec_count = oakengine_encoding_format_video_codec_count(format);
|
||||||
setEnabled(!vcodecs.isEmpty());
|
setEnabled(vcodec_count > 0);
|
||||||
codec_combobox()->clear();
|
codec_combobox()->clear();
|
||||||
foreach (ExportCodec::Codec vcodec, vcodecs) {
|
for (int i = 0; i < vcodec_count; i++) {
|
||||||
codec_combobox()->addItem(ExportCodec::get_codec_name(vcodec), vcodec);
|
int vcodec = oakengine_encoding_format_video_codec_at(format, i);
|
||||||
|
char buf[256];
|
||||||
|
oakengine_encoding_codec_name(vcodec, buf, sizeof(buf));
|
||||||
|
codec_combobox()->addItem(QString::fromUtf8(buf), vcodec);
|
||||||
}
|
}
|
||||||
return vcodecs.size();
|
return vcodec_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportVideoTab::is_image_sequence_set() const
|
bool ExportVideoTab::is_image_sequence_set() const
|
||||||
@@ -116,9 +120,9 @@ QWidget *ExportVideoTab::setup_resolution_section()
|
|||||||
|
|
||||||
scaling_method_combobox_ = new QComboBox();
|
scaling_method_combobox_ = new QComboBox();
|
||||||
scaling_method_combobox_->setEnabled(false);
|
scaling_method_combobox_->setEnabled(false);
|
||||||
scaling_method_combobox_->addItem(tr("Fit"), EncodingParams::k_fit);
|
scaling_method_combobox_->addItem(tr("Fit"), OAKENGINE_ENCODING_SCALING_FIT);
|
||||||
scaling_method_combobox_->addItem(tr("Stretch"), EncodingParams::k_stretch);
|
scaling_method_combobox_->addItem(tr("Stretch"), OAKENGINE_ENCODING_SCALING_STRETCH);
|
||||||
scaling_method_combobox_->addItem(tr("Crop"), EncodingParams::k_crop);
|
scaling_method_combobox_->addItem(tr("Crop"), OAKENGINE_ENCODING_SCALING_CROP);
|
||||||
layout->addWidget(scaling_method_combobox_, row, 1);
|
layout->addWidget(scaling_method_combobox_, row, 1);
|
||||||
|
|
||||||
// Automatically enable/disable the scaling method depending on maintain aspect ratio
|
// Automatically enable/disable the scaling method depending on maintain aspect ratio
|
||||||
@@ -223,9 +227,14 @@ void ExportVideoTab::maintain_aspect_ratio_changed(bool val)
|
|||||||
|
|
||||||
void ExportVideoTab::open_advanced_dialog()
|
void ExportVideoTab::open_advanced_dialog()
|
||||||
{
|
{
|
||||||
// Find export formats compatible with this encoder
|
// Find pixel formats compatible with this encoder
|
||||||
QStringList pixel_formats =
|
QStringList pixel_formats;
|
||||||
ExportFormat::get_pixel_formats_for_codec(format_, get_selected_codec());
|
const int pix_count = oakengine_encoding_pix_fmt_count(format_, get_selected_codec());
|
||||||
|
for (int i = 0; i < pix_count; i++) {
|
||||||
|
char buf[64];
|
||||||
|
oakengine_encoding_pix_fmt_at(format_, get_selected_codec(), i, buf, sizeof(buf));
|
||||||
|
pixel_formats.append(QString::fromUtf8(buf));
|
||||||
|
}
|
||||||
|
|
||||||
ExportAdvancedVideoDialog d(pixel_formats, this);
|
ExportAdvancedVideoDialog d(pixel_formats, this);
|
||||||
|
|
||||||
@@ -256,30 +265,35 @@ void ExportVideoTab::update_frame_rate(Rational r)
|
|||||||
|
|
||||||
void ExportVideoTab::video_codec_changed()
|
void ExportVideoTab::video_codec_changed()
|
||||||
{
|
{
|
||||||
ExportCodec::Codec codec = get_selected_codec();
|
int codec = get_selected_codec();
|
||||||
|
|
||||||
switch (codec) {
|
switch (codec) {
|
||||||
case ExportCodec::k_codec_h264:
|
case OAKENGINE_ENCODING_CODEC_H264:
|
||||||
case ExportCodec::k_codec_h264rgb:
|
case OAKENGINE_ENCODING_CODEC_H264RGB:
|
||||||
set_codec_section(h264_section_);
|
set_codec_section(h264_section_);
|
||||||
break;
|
break;
|
||||||
case ExportCodec::k_codec_h265:
|
case OAKENGINE_ENCODING_CODEC_H265:
|
||||||
set_codec_section(h265_section_);
|
set_codec_section(h265_section_);
|
||||||
break;
|
break;
|
||||||
case ExportCodec::k_codec_a_v1:
|
case OAKENGINE_ENCODING_CODEC_AV1:
|
||||||
set_codec_section(av1_section_);
|
set_codec_section(av1_section_);
|
||||||
break;
|
break;
|
||||||
case ExportCodec::k_codec_cineform:
|
case OAKENGINE_ENCODING_CODEC_CINEFORM:
|
||||||
set_codec_section(cineform_section_);
|
set_codec_section(cineform_section_);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
set_codec_section(
|
set_codec_section(
|
||||||
ExportCodec::is_codec_a_still_image(codec) ? image_section_ : nullptr);
|
oakengine_encoding_codec_is_still_image(codec) ? image_section_ : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default pixel format
|
// Set default pixel format
|
||||||
QStringList pix_fmts =
|
QStringList pix_fmts;
|
||||||
ExportFormat::get_pixel_formats_for_codec(format_, codec);
|
const int pix_count = oakengine_encoding_pix_fmt_count(format_, codec);
|
||||||
|
for (int i = 0; i < pix_count; i++) {
|
||||||
|
char buf[64];
|
||||||
|
oakengine_encoding_pix_fmt_at(format_, codec, i, buf, sizeof(buf));
|
||||||
|
pix_fmts.append(QString::fromUtf8(buf));
|
||||||
|
}
|
||||||
if (!pix_fmts.isEmpty()) {
|
if (!pix_fmts.isEmpty()) {
|
||||||
pix_fmt_ = pix_fmts.first();
|
pix_fmt_ = pix_fmts.first();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -32,8 +32,9 @@
|
|||||||
#include "dialog/export/codec/codecstack.h"
|
#include "dialog/export/codec/codecstack.h"
|
||||||
#include "dialog/export/codec/h264section.h"
|
#include "dialog/export/codec/h264section.h"
|
||||||
#include "dialog/export/codec/imagesection.h"
|
#include "dialog/export/codec/imagesection.h"
|
||||||
#include "node/color/colormanager/colormanager.h"
|
#include "oakengine/color.h"
|
||||||
#include "widget/colorwheel/colorspacechooser.h"
|
#include "widget/colorwheel/colorspacechooser.h"
|
||||||
|
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
#include "widget/standardcombos/standardcombos.h"
|
#include "widget/standardcombos/standardcombos.h"
|
||||||
|
|
||||||
@@ -43,9 +44,9 @@ namespace olive
|
|||||||
class ExportVideoTab : public QWidget {
|
class ExportVideoTab : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ExportVideoTab(ColorManager *color_manager, QWidget *parent = nullptr);
|
ExportVideoTab(OakEngineColorManager *color_manager, QWidget *parent = nullptr);
|
||||||
|
|
||||||
int set_format(ExportFormat::Format format);
|
int set_format(int format);
|
||||||
|
|
||||||
bool is_image_sequence_set() const;
|
bool is_image_sequence_set() const;
|
||||||
void set_image_sequence(bool e) const;
|
void set_image_sequence(bool e) const;
|
||||||
@@ -55,13 +56,12 @@ public:
|
|||||||
return image_section_->get_time();
|
return image_section_->get_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportCodec::Codec get_selected_codec() const
|
int get_selected_codec() const
|
||||||
{
|
{
|
||||||
return static_cast<ExportCodec::Codec>(
|
return codec_combobox()->currentData().toInt();
|
||||||
codec_combobox()->currentData().toInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_selected_codec(ExportCodec::Codec c)
|
void set_selected_codec(int c)
|
||||||
{
|
{
|
||||||
QtUtils::set_combo_box_data(codec_combobox(), c);
|
QtUtils::set_combo_box_data(codec_combobox(), c);
|
||||||
}
|
}
|
||||||
@@ -161,11 +161,11 @@ public:
|
|||||||
pix_fmt_ = s;
|
pix_fmt_ = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoParams::ColorRange color_range() const
|
int color_range() const
|
||||||
{
|
{
|
||||||
return color_range_;
|
return color_range_;
|
||||||
}
|
}
|
||||||
void set_color_range(VideoParams::ColorRange c)
|
void set_color_range(int c)
|
||||||
{
|
{
|
||||||
color_range_ = c;
|
color_range_ = c;
|
||||||
}
|
}
|
||||||
@@ -204,7 +204,7 @@ private:
|
|||||||
IntegerSlider *width_slider_;
|
IntegerSlider *width_slider_;
|
||||||
IntegerSlider *height_slider_;
|
IntegerSlider *height_slider_;
|
||||||
|
|
||||||
ColorManager *color_manager_;
|
OakEngineColorManager *color_manager_;
|
||||||
|
|
||||||
InterlacedComboBox *interlaced_combobox_;
|
InterlacedComboBox *interlaced_combobox_;
|
||||||
PixelAspectRatioComboBox *pixel_aspect_combobox_;
|
PixelAspectRatioComboBox *pixel_aspect_combobox_;
|
||||||
@@ -213,9 +213,9 @@ private:
|
|||||||
int threads_;
|
int threads_;
|
||||||
|
|
||||||
QString pix_fmt_;
|
QString pix_fmt_;
|
||||||
VideoParams::ColorRange color_range_;
|
int color_range_;
|
||||||
|
|
||||||
ExportFormat::Format format_;
|
int format_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void maintain_aspect_ratio_changed(bool val);
|
void maintain_aspect_ratio_changed(bool val);
|
||||||
|
|||||||
@@ -34,11 +34,13 @@
|
|||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "node/nodeundo.h"
|
|
||||||
#include "oakengine/footage.h"
|
#include "oakengine/footage.h"
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
|
#include "oakengine/timeline.h"
|
||||||
|
#include "oakengine/undo.h"
|
||||||
#include "streamproperties/audiostreamproperties.h"
|
#include "streamproperties/audiostreamproperties.h"
|
||||||
#include "streamproperties/videostreamproperties.h"
|
#include "streamproperties/videostreamproperties.h"
|
||||||
|
#include "widget/viewer/vieweroutpututils.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -131,28 +133,43 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
|||||||
QString description;
|
QString description;
|
||||||
bool is_enabled = false;
|
bool is_enabled = false;
|
||||||
|
|
||||||
|
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
||||||
|
reinterpret_cast<OakEngineNode *>(footage_));
|
||||||
|
|
||||||
switch (reference.type()) {
|
switch (reference.type()) {
|
||||||
case Track::k_video: {
|
case Track::k_video: {
|
||||||
stacked_widget_->addWidget(
|
stacked_widget_->addWidget(
|
||||||
new VideoStreamProperties(footage_, reference.index()));
|
new VideoStreamProperties(footage_, reference.index()));
|
||||||
|
|
||||||
VideoParams vp = footage_->get_video_params(reference.index());
|
VideoParams vp = viewer_output_video_params(footage_, reference.index());
|
||||||
is_enabled = vp.enabled();
|
is_enabled = vp.enabled();
|
||||||
description = Footage::describe_video_stream(vp);
|
{
|
||||||
|
char desc_buf[256];
|
||||||
|
oakengine_footage_describe_video_stream(
|
||||||
|
facade_handle, reference.index(), desc_buf,
|
||||||
|
sizeof(desc_buf));
|
||||||
|
description = QString::fromUtf8(desc_buf);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Track::k_audio: {
|
case Track::k_audio: {
|
||||||
stacked_widget_->addWidget(
|
stacked_widget_->addWidget(
|
||||||
new AudioStreamProperties(footage_, reference.index()));
|
new AudioStreamProperties(footage_, reference.index()));
|
||||||
|
|
||||||
AudioParams ap = footage_->get_audio_params(reference.index());
|
AudioParams ap = viewer_output_audio_params(footage_, reference.index());
|
||||||
is_enabled = ap.enabled();
|
is_enabled = ap.enabled();
|
||||||
description = Footage::describe_audio_stream(ap);
|
{
|
||||||
|
char desc_buf[256];
|
||||||
|
oakengine_footage_describe_audio_stream(
|
||||||
|
facade_handle, reference.index(), desc_buf,
|
||||||
|
sizeof(desc_buf));
|
||||||
|
description = QString::fromUtf8(desc_buf);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Track::k_subtitle: {
|
case Track::k_subtitle: {
|
||||||
SubtitleParams sp = footage_->get_subtitle_params(reference.index());
|
is_enabled = oakengine_footage_get_stream_enabled(
|
||||||
is_enabled = sp.enabled();
|
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference.index());
|
||||||
|
|
||||||
// FIXME: Language?
|
// FIXME: Language?
|
||||||
description = tr("Subtitles");
|
description = tr("Subtitles");
|
||||||
@@ -164,6 +181,8 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oakengine_footage_free(facade_handle);
|
||||||
|
|
||||||
QListWidgetItem *item = new QListWidgetItem(description, track_list_);
|
QListWidgetItem *item = new QListWidgetItem(description, track_list_);
|
||||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||||
item->setCheckState(is_enabled ? Qt::Checked : Qt::Unchecked);
|
item->setCheckState(is_enabled ? Qt::Checked : Qt::Unchecked);
|
||||||
@@ -244,16 +263,16 @@ void FootagePropertiesDialog::accept()
|
|||||||
|
|
||||||
switch (reference.type()) {
|
switch (reference.type()) {
|
||||||
case Track::k_video:
|
case Track::k_video:
|
||||||
old_stream_enabled =
|
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||||
footage_->get_video_params(reference.index()).enabled();
|
facade_handle, OAKENGINE_TRACK_TYPE_VIDEO, reference.index());
|
||||||
break;
|
break;
|
||||||
case Track::k_audio:
|
case Track::k_audio:
|
||||||
old_stream_enabled =
|
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||||
footage_->get_audio_params(reference.index()).enabled();
|
facade_handle, OAKENGINE_TRACK_TYPE_AUDIO, reference.index());
|
||||||
break;
|
break;
|
||||||
case Track::k_subtitle:
|
case Track::k_subtitle:
|
||||||
old_stream_enabled =
|
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||||
footage_->get_subtitle_params(reference.index()).enabled();
|
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference.index());
|
||||||
break;
|
break;
|
||||||
case Track::k_none:
|
case Track::k_none:
|
||||||
case Track::k_count:
|
case Track::k_count:
|
||||||
@@ -269,12 +288,12 @@ void FootagePropertiesDialog::accept()
|
|||||||
|
|
||||||
oakengine_footage_free(facade_handle);
|
oakengine_footage_free(facade_handle);
|
||||||
|
|
||||||
MultiUndoCommand *command = new MultiUndoCommand();
|
void *command = oakengine_undo_command_create_multi();
|
||||||
for (int i = 0; i < stacked_widget_->count(); i++) {
|
for (int i = 0; i < stacked_widget_->count(); i++) {
|
||||||
static_cast<StreamProperties *>(stacked_widget_->widget(i))
|
static_cast<StreamProperties *>(stacked_widget_->widget(i))
|
||||||
->accept(command);
|
->accept(command);
|
||||||
}
|
}
|
||||||
delete command; // stream pages write through the facade directly
|
oakengine_undo_command_free(command); // stream pages write through the facade directly
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
|
|
||||||
#include "node/project/footage/footage.h"
|
#include "node/project/footage/footage.h"
|
||||||
#include "undo/undocommand.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ AudioStreamProperties::AudioStreamProperties(Footage *footage, int audio_index)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamProperties::accept(MultiUndoCommand *)
|
void AudioStreamProperties::accept(void *)
|
||||||
{
|
{
|
||||||
Q_UNUSED(footage_)
|
Q_UNUSED(footage_)
|
||||||
Q_UNUSED(audio_index_)
|
Q_UNUSED(audio_index_)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class AudioStreamProperties : public StreamProperties {
|
|||||||
public:
|
public:
|
||||||
AudioStreamProperties(Footage *footage, int audio_index);
|
AudioStreamProperties(Footage *footage, int audio_index);
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *parent) override;
|
virtual void accept(void *parent) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Footage *footage_;
|
Footage *footage_;
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "undo/undocommand.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -34,7 +33,7 @@ class StreamProperties : public QWidget {
|
|||||||
public:
|
public:
|
||||||
StreamProperties(QWidget *parent = nullptr);
|
StreamProperties(QWidget *parent = nullptr);
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *)
|
virtual void accept(void *)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,12 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "node/project.h"
|
#include "node/project.h"
|
||||||
|
#include "oakengine/color.h"
|
||||||
|
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||||
#include "oakengine/footage.h"
|
#include "oakengine/footage.h"
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
|
#include "oakengine/viewer.h"
|
||||||
|
#include "oakengine/videoparams.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -46,7 +50,10 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
|||||||
|
|
||||||
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
|
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
|
||||||
|
|
||||||
VideoParams vp = footage_->get_video_params(video_index_);
|
oak_video_params vpod;
|
||||||
|
oakengine_viewer_get_video_params(
|
||||||
|
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
||||||
|
&vpod);
|
||||||
|
|
||||||
// Stream override values come through the liboakengine C ABI facade;
|
// Stream override values come through the liboakengine C ABI facade;
|
||||||
// layout-only conditions (channel count, video type) stay direct reads.
|
// layout-only conditions (channel count, video type) stay direct reads.
|
||||||
@@ -85,10 +92,13 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
|||||||
|
|
||||||
// The dropdown's color space list comes through the facade (same list
|
// The dropdown's color space list comes through the facade (same list
|
||||||
// the engine's color config reports).
|
// the engine's color config reports).
|
||||||
|
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||||
|
reinterpret_cast<OakEngineProject *>(footage_->project()));
|
||||||
video_color_space_->addItem(tr("Default (%1)")
|
video_color_space_->addItem(tr("Default (%1)")
|
||||||
.arg(footage_->project()
|
.arg(oak_query_string([cm](char *buf, int size) {
|
||||||
->color_manager()
|
return oakengine_color_manager_default_input_color_space(
|
||||||
->get_default_input_color_space()));
|
cm, buf, size);
|
||||||
|
})));
|
||||||
|
|
||||||
const int colorspace_count =
|
const int colorspace_count =
|
||||||
oakengine_footage_colorspace_count(facade_handle);
|
oakengine_footage_colorspace_count(facade_handle);
|
||||||
@@ -110,14 +120,14 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
|||||||
|
|
||||||
color_range_combo_ = new QComboBox();
|
color_range_combo_ = new QComboBox();
|
||||||
color_range_combo_->addItem(tr("Limited (16-235)"),
|
color_range_combo_->addItem(tr("Limited (16-235)"),
|
||||||
VideoParams::k_color_range_limited);
|
0);
|
||||||
color_range_combo_->addItem(tr("Full (0-255)"),
|
color_range_combo_->addItem(tr("Full (0-255)"),
|
||||||
VideoParams::k_color_range_full);
|
1);
|
||||||
color_range_combo_->setCurrentIndex(color_range);
|
color_range_combo_->setCurrentIndex(color_range);
|
||||||
|
|
||||||
video_layout->addWidget(color_range_combo_, row, 1);
|
video_layout->addWidget(color_range_combo_, row, 1);
|
||||||
|
|
||||||
if (vp.channel_count() == VideoParams::k_rgba_channel_count) {
|
if (oakengine_video_params_internal_channel_count() == 4) {
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha"));
|
video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha"));
|
||||||
@@ -127,7 +137,7 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
|||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
if (vp.video_type() == VideoParams::k_video_type_image_sequence) {
|
if (vpod.video_type == 2) {
|
||||||
QGroupBox *imgseq_group = new QGroupBox(tr("Image Sequence"));
|
QGroupBox *imgseq_group = new QGroupBox(tr("Image Sequence"));
|
||||||
QGridLayout *imgseq_layout = new QGridLayout(imgseq_group);
|
QGridLayout *imgseq_layout = new QGridLayout(imgseq_group);
|
||||||
|
|
||||||
@@ -169,7 +179,7 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
|||||||
oakengine_footage_free(facade_handle);
|
oakengine_footage_free(facade_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoStreamProperties::accept(MultiUndoCommand *parent)
|
void VideoStreamProperties::accept(void *parent)
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent)
|
Q_UNUSED(parent)
|
||||||
|
|
||||||
@@ -182,17 +192,40 @@ void VideoStreamProperties::accept(MultiUndoCommand *parent)
|
|||||||
set_colorspace = video_color_space_->currentText();
|
set_colorspace = video_color_space_->currentText();
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoParams vp = footage_->get_video_params(video_index_);
|
// Fetch current values through the facade (avoids the inline
|
||||||
|
// ViewerOutput::get_video_params() which references k_video_params_input).
|
||||||
|
char vp_colorspace[256];
|
||||||
|
vp_colorspace[0] = '\0';
|
||||||
|
int vp_color_range = 0, vp_interlacing = 0, vp_premultiplied = 0;
|
||||||
|
oakengine_footage_get_video_stream_overrides(
|
||||||
|
facade_handle, video_index_, vp_colorspace, sizeof(vp_colorspace),
|
||||||
|
&vp_color_range, &vp_interlacing, &vp_premultiplied);
|
||||||
|
|
||||||
|
int vp_par_num = 1, vp_par_den = 1;
|
||||||
|
oakengine_footage_get_pixel_aspect(facade_handle, video_index_,
|
||||||
|
&vp_par_num, &vp_par_den);
|
||||||
|
|
||||||
|
oak_video_params vpod;
|
||||||
|
oakengine_viewer_get_video_params(
|
||||||
|
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
||||||
|
&vpod);
|
||||||
|
|
||||||
|
int64_t vp_start_time = 0, vp_duration = 0;
|
||||||
|
int vp_fr_num = 0, vp_fr_den = 1;
|
||||||
|
oakengine_footage_get_image_sequence_params(
|
||||||
|
facade_handle, video_index_, &vp_start_time, &vp_duration,
|
||||||
|
&vp_fr_num, &vp_fr_den);
|
||||||
|
|
||||||
// Write every override through the facade (each call is one undoable
|
// Write every override through the facade (each call is one undoable
|
||||||
// command on the shared undo stack, replacing this dialog's own undo
|
// command on the shared undo stack, replacing this dialog's own undo
|
||||||
// command classes with identical semantics).
|
// command classes with identical semantics).
|
||||||
if ((video_premultiply_alpha_ &&
|
if ((video_premultiply_alpha_ &&
|
||||||
video_premultiply_alpha_->isChecked() != vp.premultiplied_alpha()) ||
|
video_premultiply_alpha_->isChecked() != (vp_premultiplied != 0)) ||
|
||||||
set_colorspace != vp.colorspace() ||
|
set_colorspace != QString::fromUtf8(vp_colorspace) ||
|
||||||
static_cast<VideoParams::Interlacing>(
|
static_cast<VideoParams::Interlacing>(
|
||||||
video_interlace_combo_->currentIndex()) != vp.interlacing() ||
|
video_interlace_combo_->currentIndex()) !=
|
||||||
color_range_combo_->currentData().toInt() != vp.color_range()) {
|
static_cast<VideoParams::Interlacing>(vp_interlacing) ||
|
||||||
|
color_range_combo_->currentData().toInt() != vp_color_range) {
|
||||||
oakengine_footage_set_video_stream_overrides(
|
oakengine_footage_set_video_stream_overrides(
|
||||||
facade_handle, video_index_,
|
facade_handle, video_index_,
|
||||||
set_colorspace.toUtf8().constData(),
|
set_colorspace.toUtf8().constData(),
|
||||||
@@ -204,19 +237,19 @@ void VideoStreamProperties::accept(MultiUndoCommand *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Rational new_par = pixel_aspect_combo_->get_pixel_aspect_ratio();
|
const Rational new_par = pixel_aspect_combo_->get_pixel_aspect_ratio();
|
||||||
if (new_par != vp.pixel_aspect_ratio()) {
|
if (new_par != Rational(vp_par_num, vp_par_den)) {
|
||||||
oakengine_footage_set_pixel_aspect(facade_handle, video_index_,
|
oakengine_footage_set_pixel_aspect(facade_handle, video_index_,
|
||||||
new_par.numerator(),
|
new_par.numerator(),
|
||||||
new_par.denominator());
|
new_par.denominator());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vp.video_type() == VideoParams::k_video_type_image_sequence) {
|
if (vpod.video_type == 2) {
|
||||||
int64_t new_dur =
|
int64_t new_dur =
|
||||||
imgseq_end_time_->get_value() - imgseq_start_time_->get_value() + 1;
|
imgseq_end_time_->get_value() - imgseq_start_time_->get_value() + 1;
|
||||||
|
|
||||||
if (vp.start_time() != imgseq_start_time_->get_value() ||
|
if (vp_start_time != imgseq_start_time_->get_value() ||
|
||||||
vp.duration() != new_dur ||
|
vp_duration != new_dur ||
|
||||||
vp.frame_rate() != imgseq_frame_rate_->get_frame_rate()) {
|
Rational(vp_fr_num, vp_fr_den) != imgseq_frame_rate_->get_frame_rate()) {
|
||||||
const Rational fr = imgseq_frame_rate_->get_frame_rate();
|
const Rational fr = imgseq_frame_rate_->get_frame_rate();
|
||||||
oakengine_footage_set_image_sequence_params(
|
oakengine_footage_set_image_sequence_params(
|
||||||
facade_handle, video_index_,
|
facade_handle, video_index_,
|
||||||
@@ -230,8 +263,11 @@ void VideoStreamProperties::accept(MultiUndoCommand *parent)
|
|||||||
|
|
||||||
bool VideoStreamProperties::sanity_check()
|
bool VideoStreamProperties::sanity_check()
|
||||||
{
|
{
|
||||||
if (footage_->get_video_params(video_index_).video_type() ==
|
oak_video_params vpod;
|
||||||
VideoParams::k_video_type_image_sequence) {
|
oakengine_viewer_get_video_params(
|
||||||
|
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
||||||
|
&vpod);
|
||||||
|
if (vpod.video_type == 2) {
|
||||||
if (imgseq_start_time_->get_value() >= imgseq_end_time_->get_value()) {
|
if (imgseq_start_time_->get_value() >= imgseq_end_time_->get_value()) {
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
this, tr("Invalid Configuration"),
|
this, tr("Invalid Configuration"),
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class VideoStreamProperties : public StreamProperties {
|
|||||||
public:
|
public:
|
||||||
VideoStreamProperties(Footage *footage, int video_index);
|
VideoStreamProperties(Footage *footage, int video_index);
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *parent) override;
|
virtual void accept(void *parent) override;
|
||||||
|
|
||||||
virtual bool sanity_check() override;
|
virtual bool sanity_check() override;
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,9 @@ void FootageRelinkDialog::browse_for_footage()
|
|||||||
new_dir.filePath(relative_to_original);
|
new_dir.filePath(relative_to_original);
|
||||||
|
|
||||||
if (QFileInfo::exists(absolute_to_new)) {
|
if (QFileInfo::exists(absolute_to_new)) {
|
||||||
other_footage->set_filename(absolute_to_new);
|
oakengine_footage_relink(
|
||||||
|
reinterpret_cast<OakEngineFootage *>(other_footage),
|
||||||
|
absolute_to_new.toUtf8().constData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
|
#include "oakengine/timeline.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -138,29 +140,29 @@ void MarkerPropertiesDialog::accept()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiUndoCommand *command = new MultiUndoCommand();
|
// Batch-set properties via facade (one undoable command)
|
||||||
|
{
|
||||||
int color = color_menu_->get_selected_color();
|
QVector<OakEngineMarker *> oak_markers;
|
||||||
|
|
||||||
foreach (TimelineMarker *m, markers_) {
|
foreach (TimelineMarker *m, markers_) {
|
||||||
if (color != -1) {
|
oak_markers.append(reinterpret_cast<OakEngineMarker *>(m));
|
||||||
command->add_child(new MarkerChangeColorCommand(m, color));
|
|
||||||
}
|
}
|
||||||
|
int color = color_menu_->get_selected_color();
|
||||||
|
QByteArray name_ba;
|
||||||
|
const char *name = nullptr;
|
||||||
if (label_edit_->placeholderText().isEmpty()) {
|
if (label_edit_->placeholderText().isEmpty()) {
|
||||||
command->add_child(
|
name_ba = label_edit_->text().toUtf8();
|
||||||
new MarkerChangeNameCommand(m, label_edit_->text()));
|
name = name_ba.constData();
|
||||||
}
|
}
|
||||||
|
oakengine_marker_set_properties(
|
||||||
|
oak_markers.data(), oak_markers.size(), color, name,
|
||||||
|
(markers_.size() == 1) ? 1 : 0,
|
||||||
|
in_slider_->get_value().numerator(),
|
||||||
|
in_slider_->get_value().denominator(),
|
||||||
|
out_slider_->get_value().numerator(),
|
||||||
|
out_slider_->get_value().denominator(),
|
||||||
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (markers_.size() == 1) {
|
|
||||||
command->add_child(new MarkerChangeTimeCommand(
|
|
||||||
markers_.front(),
|
|
||||||
TimeRange(in_slider_->get_value(), out_slider_->get_value())));
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::instance()->undo_stack()->push(command, tr("Set Marker Properties"));
|
|
||||||
|
|
||||||
super::accept();
|
super::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "oakengine/config.h"
|
||||||
#include "tabs/preferencesgeneraltab.h"
|
#include "tabs/preferencesgeneraltab.h"
|
||||||
#include "tabs/preferencesbehaviortab.h"
|
#include "tabs/preferencesbehaviortab.h"
|
||||||
#include "tabs/preferencesappearancetab.h"
|
#include "tabs/preferencesappearancetab.h"
|
||||||
@@ -69,7 +69,7 @@ PreferencesDialog::PreferencesDialog(MainWindow *main_window, int start_tab)
|
|||||||
|
|
||||||
void PreferencesDialog::AcceptEvent()
|
void PreferencesDialog::AcceptEvent()
|
||||||
{
|
{
|
||||||
Config::save();
|
oakengine_config_save();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
#include "node/node.h"
|
#include "node/node.h"
|
||||||
|
#include "oakengine/node.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -69,8 +70,9 @@ PreferencesAppearanceTab::PreferencesAppearanceTab()
|
|||||||
QGridLayout *color_layout = new QGridLayout(color_group);
|
QGridLayout *color_layout = new QGridLayout(color_group);
|
||||||
|
|
||||||
for (int i = 0; i < Node::k_category_count; i++) {
|
for (int i = 0; i < Node::k_category_count; i++) {
|
||||||
QString cat_name =
|
char cat_buf[256];
|
||||||
Node::get_category_name(static_cast<Node::CategoryID>(i));
|
oakengine_node_category_name(i, cat_buf, sizeof(cat_buf));
|
||||||
|
QString cat_name = QString::fromUtf8(cat_buf);
|
||||||
color_layout->addWidget(new QLabel(cat_name), i, 0);
|
color_layout->addWidget(new QLabel(cat_name), i, 0);
|
||||||
|
|
||||||
ColorCodingComboBox *ccc = new ColorCodingComboBox();
|
ColorCodingComboBox *ccc = new ColorCodingComboBox();
|
||||||
@@ -102,7 +104,7 @@ PreferencesAppearanceTab::PreferencesAppearanceTab()
|
|||||||
layout->addStretch();
|
layout->addStretch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesAppearanceTab::accept(MultiUndoCommand *command)
|
void PreferencesAppearanceTab::accept(void *command)
|
||||||
{
|
{
|
||||||
Q_UNUSED(command)
|
Q_UNUSED(command)
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class PreferencesAppearanceTab : public ConfigDialogBaseTab {
|
|||||||
public:
|
public:
|
||||||
PreferencesAppearanceTab();
|
PreferencesAppearanceTab();
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *command) override;
|
virtual void accept(void *command) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,8 +25,9 @@
|
|||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
#include "audio/audiomanager.h"
|
#include "oakengine/audio.h"
|
||||||
#include "config/config.h"
|
#include <portaudio.h>
|
||||||
|
#include "common/configwrapper.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -171,13 +172,13 @@ PreferencesAudioTab::PreferencesAudioTab()
|
|||||||
new ExportFormatComboBox(ExportFormatComboBox::k_show_audio_only);
|
new ExportFormatComboBox(ExportFormatComboBox::k_show_audio_only);
|
||||||
record_format_combo_->setSizePolicy(QSizePolicy::Expanding,
|
record_format_combo_->setSizePolicy(QSizePolicy::Expanding,
|
||||||
QSizePolicy::Expanding);
|
QSizePolicy::Expanding);
|
||||||
record_format_combo_->set_format(static_cast<ExportFormat::Format>(
|
record_format_combo_->set_format(static_cast<int>(
|
||||||
OAK_CONFIG("AudioRecordingFormat").toInt()));
|
OAK_CONFIG("AudioRecordingFormat").toInt()));
|
||||||
fmt_layout->addWidget(record_format_combo_);
|
fmt_layout->addWidget(record_format_combo_);
|
||||||
|
|
||||||
record_options_ = new ExportAudioTab();
|
record_options_ = new ExportAudioTab();
|
||||||
record_options_->set_format(record_format_combo_->get_format());
|
record_options_->set_format(record_format_combo_->get_format());
|
||||||
record_options_->set_codec(static_cast<ExportCodec::Codec>(
|
record_options_->set_codec(static_cast<int>(
|
||||||
OAK_CONFIG("AudioRecordingCodec").toInt()));
|
OAK_CONFIG("AudioRecordingCodec").toInt()));
|
||||||
record_options_->sample_rate_combobox()->set_sample_rate(
|
record_options_->sample_rate_combobox()->set_sample_rate(
|
||||||
OAK_CONFIG("AudioRecordingSampleRate").toInt());
|
OAK_CONFIG("AudioRecordingSampleRate").toInt());
|
||||||
@@ -213,7 +214,7 @@ PreferencesAudioTab::PreferencesAudioTab()
|
|||||||
refresh_backends();
|
refresh_backends();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesAudioTab::accept(MultiUndoCommand *command)
|
void PreferencesAudioTab::accept(void *command)
|
||||||
{
|
{
|
||||||
Q_UNUSED(command)
|
Q_UNUSED(command)
|
||||||
|
|
||||||
@@ -228,8 +229,8 @@ void PreferencesAudioTab::accept(MultiUndoCommand *command)
|
|||||||
OAK_CONFIG("AudioInput") = audio_input_devices_->currentText();
|
OAK_CONFIG("AudioInput") = audio_input_devices_->currentText();
|
||||||
|
|
||||||
// Set devices to be used from now on
|
// Set devices to be used from now on
|
||||||
AudioManager::instance()->set_output_device(output_device);
|
oakengine_audio_set_output_device(output_device);
|
||||||
AudioManager::instance()->set_input_device(input_device);
|
oakengine_audio_set_input_device(input_device);
|
||||||
|
|
||||||
OAK_CONFIG("AudioOutputSampleRate") = output_rate_combo_->get_sample_rate();
|
OAK_CONFIG("AudioOutputSampleRate") = output_rate_combo_->get_sample_rate();
|
||||||
OAK_CONFIG("AudioOutputChannelLayout") =
|
OAK_CONFIG("AudioOutputChannelLayout") =
|
||||||
@@ -251,7 +252,8 @@ void PreferencesAudioTab::accept(MultiUndoCommand *command)
|
|||||||
->get_sample_format()
|
->get_sample_format()
|
||||||
.to_string());
|
.to_string());
|
||||||
|
|
||||||
emit AudioManager::instance() -> output_params_changed();
|
// AudioManager output params changed is handled internally by the facade
|
||||||
|
// when oakengine_audio_set_output_device() is called.
|
||||||
|
|
||||||
OAK_CONFIG("AudioScrubbing") = audio_scrubbing_->isChecked();
|
OAK_CONFIG("AudioScrubbing") = audio_scrubbing_->isChecked();
|
||||||
}
|
}
|
||||||
@@ -299,7 +301,7 @@ void PreferencesAudioTab::refresh_devices()
|
|||||||
|
|
||||||
void PreferencesAudioTab::hard_refresh_backends()
|
void PreferencesAudioTab::hard_refresh_backends()
|
||||||
{
|
{
|
||||||
AudioManager::instance()->hard_reset();
|
oakengine_audio_hard_reset();
|
||||||
refresh_backends();
|
refresh_backends();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,9 +309,9 @@ void PreferencesAudioTab::attempt_to_set_devices_from_config()
|
|||||||
{
|
{
|
||||||
// Load with currently active devices
|
// Load with currently active devices
|
||||||
PaDeviceIndex current_output_index =
|
PaDeviceIndex current_output_index =
|
||||||
AudioManager::instance()->get_output_device();
|
static_cast<PaDeviceIndex>(oakengine_audio_get_output_device());
|
||||||
PaDeviceIndex current_input_index =
|
PaDeviceIndex current_input_index =
|
||||||
AudioManager::instance()->get_input_device();
|
static_cast<PaDeviceIndex>(oakengine_audio_get_input_device());
|
||||||
|
|
||||||
const PaDeviceInfo *current_output = nullptr, *current_input = nullptr;
|
const PaDeviceInfo *current_output = nullptr, *current_input = nullptr;
|
||||||
if (current_output_index != paNoDevice) {
|
if (current_output_index != paNoDevice) {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class PreferencesAudioTab : public ConfigDialogBaseTab {
|
|||||||
public:
|
public:
|
||||||
PreferencesAudioTab();
|
PreferencesAudioTab();
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *command) override;
|
virtual void accept(void *command) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QComboBox *audio_backend_combobox_;
|
QComboBox *audio_backend_combobox_;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "common/configwrapper.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -114,7 +114,7 @@ PreferencesBehaviorTab::PreferencesBehaviorTab(Category category)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesBehaviorTab::accept(MultiUndoCommand *command)
|
void PreferencesBehaviorTab::accept(void *command)
|
||||||
{
|
{
|
||||||
Q_UNUSED(command)
|
Q_UNUSED(command)
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public:
|
|||||||
|
|
||||||
PreferencesBehaviorTab(Category category);
|
PreferencesBehaviorTab(Category category);
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *command) override;
|
virtual void accept(void *command) override;
|
||||||
|
|
||||||
static QString behavior_pref_tr(const char *text)
|
static QString behavior_pref_tr(const char *text)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,16 +29,24 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "config/config.h"
|
#include "common/configwrapper.h"
|
||||||
|
#include "oakengine/disk.h"
|
||||||
|
#include "olive/core/core.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
PreferencesDiskTab::PreferencesDiskTab()
|
PreferencesDiskTab::PreferencesDiskTab()
|
||||||
{
|
{
|
||||||
// Get default disk cache folder
|
// Get default disk cache folder path
|
||||||
default_disk_cache_folder_ =
|
{
|
||||||
DiskManager::instance()->get_default_cache_folder();
|
int len = oakengine_disk_get_default_cache_path(nullptr, 0);
|
||||||
|
if (len > 0) {
|
||||||
|
QByteArray buf(len + 1, '\0');
|
||||||
|
oakengine_disk_get_default_cache_path(buf.data(), buf.size());
|
||||||
|
default_disk_cache_folder_ = QString::fromUtf8(buf.constData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
@@ -54,7 +62,7 @@ PreferencesDiskTab::PreferencesDiskTab()
|
|||||||
row, 0);
|
row, 0);
|
||||||
|
|
||||||
disk_cache_location_ =
|
disk_cache_location_ =
|
||||||
new PathWidget(default_disk_cache_folder_->get_path());
|
new PathWidget(default_disk_cache_folder_);
|
||||||
disk_management_layout->addWidget(disk_cache_location_, row, 1);
|
disk_management_layout->addWidget(disk_cache_location_, row, 1);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
@@ -62,8 +70,8 @@ PreferencesDiskTab::PreferencesDiskTab()
|
|||||||
QPushButton *disk_cache_settings_btn =
|
QPushButton *disk_cache_settings_btn =
|
||||||
new QPushButton(tr("Disk Cache Settings"));
|
new QPushButton(tr("Disk Cache Settings"));
|
||||||
connect(disk_cache_settings_btn, &QPushButton::clicked, this, [this]() {
|
connect(disk_cache_settings_btn, &QPushButton::clicked, this, [this]() {
|
||||||
DiskManager::instance()->show_disk_cache_settings_dialog(
|
oakengine_disk_show_settings_dialog(
|
||||||
disk_cache_location_->text(), this);
|
disk_cache_location_->text().toUtf8().constData(), this);
|
||||||
});
|
});
|
||||||
disk_management_layout->addWidget(disk_cache_settings_btn, row, 1);
|
disk_management_layout->addWidget(disk_cache_settings_btn, row, 1);
|
||||||
|
|
||||||
@@ -81,7 +89,7 @@ PreferencesDiskTab::PreferencesDiskTab()
|
|||||||
cache_ahead_slider_->set_format(tr("%1 seconds"));
|
cache_ahead_slider_->set_format(tr("%1 seconds"));
|
||||||
cache_ahead_slider_->set_minimum(0);
|
cache_ahead_slider_->set_minimum(0);
|
||||||
cache_ahead_slider_->set_value(
|
cache_ahead_slider_->set_value(
|
||||||
OAK_CONFIG("DiskCacheAhead").value<Rational>().to_double());
|
OAK_CONFIG("DiskCacheAhead").value<core::Rational>().to_double());
|
||||||
cache_behavior_layout->addWidget(cache_ahead_slider_, row, 1);
|
cache_behavior_layout->addWidget(cache_ahead_slider_, row, 1);
|
||||||
|
|
||||||
cache_behavior_layout->addWidget(new QLabel(tr("Cache Behind:")), row, 2);
|
cache_behavior_layout->addWidget(new QLabel(tr("Cache Behind:")), row, 2);
|
||||||
@@ -90,7 +98,7 @@ PreferencesDiskTab::PreferencesDiskTab()
|
|||||||
cache_behind_slider_->set_minimum(0);
|
cache_behind_slider_->set_minimum(0);
|
||||||
cache_behind_slider_->set_format(tr("%1 seconds"));
|
cache_behind_slider_->set_format(tr("%1 seconds"));
|
||||||
cache_behind_slider_->set_value(
|
cache_behind_slider_->set_value(
|
||||||
OAK_CONFIG("DiskCacheBehind").value<Rational>().to_double());
|
OAK_CONFIG("DiskCacheBehind").value<core::Rational>().to_double());
|
||||||
cache_behavior_layout->addWidget(cache_behind_slider_, row, 3);
|
cache_behavior_layout->addWidget(cache_behind_slider_, row, 3);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
@@ -171,11 +179,11 @@ PreferencesDiskTab::PreferencesDiskTab()
|
|||||||
|
|
||||||
bool PreferencesDiskTab::validate()
|
bool PreferencesDiskTab::validate()
|
||||||
{
|
{
|
||||||
if (disk_cache_location_->text() != default_disk_cache_folder_->get_path()) {
|
if (disk_cache_location_->text() != default_disk_cache_folder_) {
|
||||||
// Disk cache location is changing
|
// Disk cache location is changing
|
||||||
|
|
||||||
// Check if the user is okay with invalidating the current cache
|
// Check if the user is okay with invalidating the current cache
|
||||||
if (!DiskManager::show_disk_cache_change_confirmation_dialog(this)) {
|
if (!oakengine_disk_show_change_confirmation_dialog(this)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,18 +199,19 @@ bool PreferencesDiskTab::validate()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesDiskTab::accept(MultiUndoCommand *command)
|
void PreferencesDiskTab::accept(void *command)
|
||||||
{
|
{
|
||||||
Q_UNUSED(command)
|
Q_UNUSED(command)
|
||||||
|
|
||||||
if (disk_cache_location_->text() != default_disk_cache_folder_->get_path()) {
|
if (disk_cache_location_->text() != default_disk_cache_folder_) {
|
||||||
default_disk_cache_folder_->set_path(disk_cache_location_->text());
|
oakengine_disk_set_default_cache_path(
|
||||||
|
disk_cache_location_->text().toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
OAK_CONFIG("DiskCacheBehind") = QVariant::fromValue(
|
OAK_CONFIG("DiskCacheBehind") = QVariant::fromValue(
|
||||||
Rational::from_double(cache_behind_slider_->get_value()));
|
core::Rational::from_double(cache_behind_slider_->get_value()));
|
||||||
OAK_CONFIG("DiskCacheAhead") = QVariant::fromValue(
|
OAK_CONFIG("DiskCacheAhead") = QVariant::fromValue(
|
||||||
Rational::from_double(cache_ahead_slider_->get_value()));
|
core::Rational::from_double(cache_ahead_slider_->get_value()));
|
||||||
|
|
||||||
OAK_CONFIG("ProxyWidth") =
|
OAK_CONFIG("ProxyWidth") =
|
||||||
static_cast<int>(proxy_width_slider_->get_value());
|
static_cast<int>(proxy_width_slider_->get_value());
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include "dialog/configbase/configdialogbase.h"
|
#include "dialog/configbase/configdialogbase.h"
|
||||||
#include "render/diskmanager.h"
|
#include "oakengine/disk.h"
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
#include "widget/path/pathwidget.h"
|
#include "widget/path/pathwidget.h"
|
||||||
@@ -43,7 +43,7 @@ public:
|
|||||||
|
|
||||||
virtual bool validate() override;
|
virtual bool validate() override;
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *command) override;
|
virtual void accept(void *command) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PathWidget *disk_cache_location_;
|
PathWidget *disk_cache_location_;
|
||||||
@@ -52,7 +52,7 @@ private:
|
|||||||
|
|
||||||
FloatSlider *cache_behind_slider_;
|
FloatSlider *cache_behind_slider_;
|
||||||
|
|
||||||
DiskCacheFolder *default_disk_cache_folder_;
|
QString default_disk_cache_folder_;
|
||||||
|
|
||||||
IntegerSlider *proxy_width_slider_;
|
IntegerSlider *proxy_width_slider_;
|
||||||
IntegerSlider *proxy_height_slider_;
|
IntegerSlider *proxy_height_slider_;
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
|
|||||||
layout->addStretch();
|
layout->addStretch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesGeneralTab::accept(MultiUndoCommand *command)
|
void PreferencesGeneralTab::accept(void *command)
|
||||||
{
|
{
|
||||||
Q_UNUSED(command)
|
Q_UNUSED(command)
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class PreferencesGeneralTab : public ConfigDialogBaseTab {
|
|||||||
public:
|
public:
|
||||||
PreferencesGeneralTab();
|
PreferencesGeneralTab();
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *command) override;
|
virtual void accept(void *command) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void add_language(const QString &locale_name);
|
void add_language(const QString &locale_name);
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ PreferencesKeyboardTab::PreferencesKeyboardTab(MainWindow *main_window)
|
|||||||
setup_kbd_shortcuts(main_window_->menuBar());
|
setup_kbd_shortcuts(main_window_->menuBar());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesKeyboardTab::accept(MultiUndoCommand *command)
|
void PreferencesKeyboardTab::accept(void *command)
|
||||||
{
|
{
|
||||||
Q_UNUSED(command)
|
Q_UNUSED(command)
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class PreferencesKeyboardTab : public ConfigDialogBaseTab {
|
|||||||
public:
|
public:
|
||||||
PreferencesKeyboardTab(MainWindow *main_window);
|
PreferencesKeyboardTab(MainWindow *main_window);
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *command) override;
|
virtual void accept(void *command) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -26,8 +26,9 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "render/lutlibrary.h"
|
#include "oakengine/lut.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -46,7 +47,16 @@ PreferencesLutTab::PreferencesLutTab()
|
|||||||
"these locations when picking a LUT file.")));
|
"these locations when picking a LUT file.")));
|
||||||
|
|
||||||
library_dirs_list_ = new QListWidget();
|
library_dirs_list_ = new QListWidget();
|
||||||
library_dirs_list_->addItems(LUTLibrary::get_directories());
|
{
|
||||||
|
int dir_count = oakengine_lut_directory_count();
|
||||||
|
for (int i = 0; i < dir_count; i++) {
|
||||||
|
char buf[4096];
|
||||||
|
int len = oakengine_lut_directory_at(i, buf, sizeof(buf));
|
||||||
|
if (len > 0) {
|
||||||
|
library_dirs_list_->addItem(QString::fromUtf8(buf, len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
library_layout->addWidget(library_dirs_list_);
|
library_layout->addWidget(library_dirs_list_);
|
||||||
|
|
||||||
QHBoxLayout *button_layout = new QHBoxLayout();
|
QHBoxLayout *button_layout = new QHBoxLayout();
|
||||||
@@ -74,7 +84,7 @@ PreferencesLutTab::PreferencesLutTab()
|
|||||||
outer_layout->addStretch();
|
outer_layout->addStretch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesLutTab::accept(MultiUndoCommand *command)
|
void PreferencesLutTab::accept(void *command)
|
||||||
{
|
{
|
||||||
Q_UNUSED(command)
|
Q_UNUSED(command)
|
||||||
|
|
||||||
@@ -83,7 +93,14 @@ void PreferencesLutTab::accept(MultiUndoCommand *command)
|
|||||||
dirs.append(library_dirs_list_->item(i)->text());
|
dirs.append(library_dirs_list_->item(i)->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
LUTLibrary::set_directories(dirs);
|
std::vector<QByteArray> utf8_dirs;
|
||||||
|
std::vector<const char*> cstr_dirs;
|
||||||
|
for (int i = 0; i < dirs.size(); i++) {
|
||||||
|
utf8_dirs.push_back(dirs[i].toUtf8());
|
||||||
|
cstr_dirs.push_back(utf8_dirs.back().constData());
|
||||||
|
}
|
||||||
|
oakengine_lut_set_directories(cstr_dirs.data(),
|
||||||
|
static_cast<int>(cstr_dirs.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class PreferencesLutTab : public ConfigDialogBaseTab {
|
|||||||
public:
|
public:
|
||||||
PreferencesLutTab();
|
PreferencesLutTab();
|
||||||
|
|
||||||
virtual void accept(MultiUndoCommand *command) override;
|
virtual void accept(void *command) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QListWidget *library_dirs_list_;
|
QListWidget *library_dirs_list_;
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ PluginProgressDialogReporter::PluginProgressDialogReporter(
|
|||||||
: dialog_(new ProgressDialog(message, title, nullptr))
|
: dialog_(new ProgressDialog(message, title, nullptr))
|
||||||
{
|
{
|
||||||
dialog_->setAttribute(Qt::WA_DeleteOnClose);
|
dialog_->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
connect(dialog_, &ProgressDialog::cancelled, this,
|
QObject::connect(dialog_, &ProgressDialog::cancelled, dialog_, [this]() {
|
||||||
&PluginProgressReporter::cancelled);
|
cancelled_ = true;
|
||||||
|
set_cancelled();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginProgressDialogReporter::~PluginProgressDialogReporter()
|
PluginProgressDialogReporter::~PluginProgressDialogReporter()
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class ProgressDialog;
|
|||||||
* is destroyed by the engine with deleteLater().
|
* is destroyed by the engine with deleteLater().
|
||||||
*/
|
*/
|
||||||
class PluginProgressDialogReporter : public plugin::PluginProgressReporter {
|
class PluginProgressDialogReporter : public plugin::PluginProgressReporter {
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
PluginProgressDialogReporter(const QString &message, const QString &title);
|
PluginProgressDialogReporter(const QString &message, const QString &title);
|
||||||
|
|
||||||
@@ -52,8 +51,11 @@ public:
|
|||||||
|
|
||||||
virtual void close() override;
|
virtual void close() override;
|
||||||
|
|
||||||
|
bool was_cancelled() const { return cancelled_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<ProgressDialog> dialog_;
|
QPointer<ProgressDialog> dialog_;
|
||||||
|
bool cancelled_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,10 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include "common/filefunctions.h"
|
#include "common/filefunctions.h"
|
||||||
#include "node/color/colormanager/colormanager.h"
|
#include "oakengine/color.h"
|
||||||
#include "render/diskmanager.h"
|
#include "oakengine/disk.h"
|
||||||
|
#include "oakengine/project.h"
|
||||||
|
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -45,8 +47,12 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
{
|
{
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
char name_buf[256];
|
||||||
|
oakengine_project_name(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||||
|
name_buf, sizeof(name_buf));
|
||||||
setWindowTitle(
|
setWindowTitle(
|
||||||
tr("Project Properties for '%1'").arg(working_project_->name()));
|
tr("Project Properties for '%1'").arg(name_buf));
|
||||||
|
|
||||||
QTabWidget *tabs = new QTabWidget;
|
QTabWidget *tabs = new QTabWidget;
|
||||||
layout->addWidget(tabs);
|
layout->addWidget(tabs);
|
||||||
@@ -85,7 +91,14 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
reference_space_->addItem(tr("Scene Linear"), ocio::ROLE_SCENE_LINEAR);
|
reference_space_->addItem(tr("Scene Linear"), ocio::ROLE_SCENE_LINEAR);
|
||||||
reference_space_->addItem(tr("Compositing Log"),
|
reference_space_->addItem(tr("Compositing Log"),
|
||||||
ocio::ROLE_COMPOSITING_LOG);
|
ocio::ROLE_COMPOSITING_LOG);
|
||||||
QtUtils::set_combo_box_data(reference_space_, p->get_color_reference_space());
|
QtUtils::set_combo_box_data(reference_space_,
|
||||||
|
[p]() -> QString {
|
||||||
|
char buf[256];
|
||||||
|
oakengine_project_get_color_reference_space(
|
||||||
|
reinterpret_cast<OakEngineProject *>(p),
|
||||||
|
buf, sizeof(buf));
|
||||||
|
return QString::fromUtf8(buf);
|
||||||
|
}());
|
||||||
color_layout->addWidget(reference_space_, row, 1, 1, 2);
|
color_layout->addWidget(reference_space_, row, 1, 1, 2);
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
@@ -95,8 +108,11 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
connect(browse_btn, &QPushButton::clicked, this,
|
connect(browse_btn, &QPushButton::clicked, this,
|
||||||
&ProjectPropertiesDialog::browse_for_ocio_config);
|
&ProjectPropertiesDialog::browse_for_ocio_config);
|
||||||
|
|
||||||
ocio_filename_->setText(
|
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||||
working_project_->color_manager()->get_config_filename());
|
reinterpret_cast<OakEngineProject *>(working_project_));
|
||||||
|
ocio_filename_->setText(oak_query_string([cm](char *buf, int size) {
|
||||||
|
return oakengine_color_manager_get_config_filename(cm, buf, size);
|
||||||
|
}));
|
||||||
|
|
||||||
connect(ocio_filename_, &QLineEdit::textChanged, this,
|
connect(ocio_filename_, &QLineEdit::textChanged, this,
|
||||||
&ProjectPropertiesDialog::ocio_filename_updated);
|
&ProjectPropertiesDialog::ocio_filename_updated);
|
||||||
@@ -129,7 +145,14 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
|
|
||||||
// Create custom cache path widget
|
// Create custom cache path widget
|
||||||
custom_cache_path_ =
|
custom_cache_path_ =
|
||||||
new PathWidget(working_project_->get_custom_cache_path(), this);
|
new PathWidget(
|
||||||
|
[this]() -> QString {
|
||||||
|
char buf[4096];
|
||||||
|
oakengine_project_get_custom_cache_path(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||||
|
buf, sizeof(buf));
|
||||||
|
return QString::fromUtf8(buf);
|
||||||
|
}(), this);
|
||||||
custom_cache_path_->setEnabled(false);
|
custom_cache_path_->setEnabled(false);
|
||||||
cache_layout->addWidget(custom_cache_path_);
|
cache_layout->addWidget(custom_cache_path_);
|
||||||
|
|
||||||
@@ -139,7 +162,8 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
|||||||
&PathWidget::setEnabled);
|
&PathWidget::setEnabled);
|
||||||
|
|
||||||
// Check the radio button that should currently be active
|
// Check the radio button that should currently be active
|
||||||
disk_cache_radios_[working_project_->get_cache_location_setting()]
|
disk_cache_radios_[oakengine_project_get_cache_location_setting(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_))]
|
||||||
->setChecked(true);
|
->setChecked(true);
|
||||||
|
|
||||||
// Add disk cache settings button
|
// Add disk cache settings button
|
||||||
@@ -181,7 +205,13 @@ void ProjectPropertiesDialog::accept()
|
|||||||
->isChecked()) {
|
->isChecked()) {
|
||||||
// Ensure alongside project path is valid
|
// Ensure alongside project path is valid
|
||||||
if (!verify_path_and_warn_if_bad(
|
if (!verify_path_and_warn_if_bad(
|
||||||
working_project_->get_cache_alongside_project_path())) {
|
[this]() -> QString {
|
||||||
|
char buf[4096];
|
||||||
|
oakengine_project_cache_alongside_path(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||||
|
buf, sizeof(buf));
|
||||||
|
return QString::fromUtf8(buf);
|
||||||
|
}())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -191,33 +221,55 @@ void ProjectPropertiesDialog::accept()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (custom_cache_path_->text() != working_project_->get_custom_cache_path()) {
|
if (custom_cache_path_->text() !=
|
||||||
|
[this]() -> QString {
|
||||||
|
char buf[4096];
|
||||||
|
oakengine_project_get_custom_cache_path(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||||
|
buf, sizeof(buf));
|
||||||
|
return QString::fromUtf8(buf);
|
||||||
|
}()) {
|
||||||
// Check if the user is okay with invalidating the current cache
|
// Check if the user is okay with invalidating the current cache
|
||||||
if (!DiskManager::show_disk_cache_change_confirmation_dialog(this)) {
|
if (!oakengine_disk_show_change_confirmation_dialog(this)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
working_project_->set_custom_cache_path(custom_cache_path_->text());
|
oakengine_project_set_custom_cache_path(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||||
|
custom_cache_path_->text().toUtf8().constData());
|
||||||
|
|
||||||
emit DiskManager::instance() -> invalidate_project(working_project_);
|
oakengine_disk_invalidate_project(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should ripple changes throughout the graph/cache that the color config has changed, and
|
// This should ripple changes throughout the graph/cache that the color config has changed, and
|
||||||
// therefore should be done after the cache path is changed
|
// therefore should be done after the cache path is changed
|
||||||
if (working_project_->color_manager()->get_config_filename() !=
|
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||||
ocio_filename_->text()) {
|
reinterpret_cast<OakEngineProject *>(working_project_));
|
||||||
working_project_->color_manager()->set_config_filename(
|
QString old_config = oak_query_string([cm](char *buf, int size) {
|
||||||
ocio_filename_->text());
|
return oakengine_color_manager_get_config_filename(cm, buf, size);
|
||||||
|
});
|
||||||
|
QString old_input_cs = oak_query_string([cm](char *buf, int size) {
|
||||||
|
return oakengine_color_manager_default_input_color_space(cm, buf, size);
|
||||||
|
});
|
||||||
|
if (old_config != ocio_filename_->text()) {
|
||||||
|
oakengine_color_manager_set_config_filename(
|
||||||
|
cm, ocio_filename_->text().toUtf8().constData());
|
||||||
}
|
}
|
||||||
if (working_project_->color_manager()->get_default_input_color_space() !=
|
if (old_input_cs != default_input_colorspace_->currentText()) {
|
||||||
default_input_colorspace_->currentText()) {
|
oakengine_color_manager_set_default_input_color_space(
|
||||||
working_project_->color_manager()->set_default_input_color_space(
|
cm, default_input_colorspace_->currentText().toUtf8().constData());
|
||||||
default_input_colorspace_->currentText());
|
|
||||||
}
|
}
|
||||||
if (working_project_->get_color_reference_space() !=
|
if ([this]() -> QString {
|
||||||
reference_space_->currentData().toString()) {
|
char buf[256];
|
||||||
working_project_->set_color_reference_space(
|
oakengine_project_get_color_reference_space(
|
||||||
reference_space_->currentData().toString());
|
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||||
|
buf, sizeof(buf));
|
||||||
|
return QString::fromUtf8(buf);
|
||||||
|
}() != reference_space_->currentData().toString()) {
|
||||||
|
oakengine_project_set_color_reference_space(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||||
|
reference_space_->currentData().toString().toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
super::accept();
|
super::accept();
|
||||||
@@ -253,50 +305,69 @@ void ProjectPropertiesDialog::ocio_filename_updated()
|
|||||||
{
|
{
|
||||||
default_input_colorspace_->clear();
|
default_input_colorspace_->clear();
|
||||||
|
|
||||||
try {
|
OakEngineColorConfig *config = nullptr;
|
||||||
ocio::ConstConfigRcPtr c;
|
|
||||||
|
|
||||||
if (ocio_filename_->text().isEmpty()) {
|
if (ocio_filename_->text().isEmpty()) {
|
||||||
c = ColorManager::get_default_config();
|
config = oakengine_color_config_load_default();
|
||||||
} else {
|
} else {
|
||||||
c = ColorManager::create_config_from_file(ocio_filename_->text());
|
config = oakengine_color_config_load_file(
|
||||||
|
ocio_filename_->text().toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config) {
|
||||||
ocio_filename_->setStyleSheet(QString());
|
ocio_filename_->setStyleSheet(QString());
|
||||||
ocio_config_is_valid_ = true;
|
ocio_config_is_valid_ = true;
|
||||||
|
|
||||||
// List input color spaces
|
// List input color spaces
|
||||||
QStringList input_cs = ColorManager::list_available_colorspaces(c);
|
int cs_count = oakengine_color_config_colorspace_count(config);
|
||||||
|
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_));
|
||||||
|
QString default_cs = oak_query_string([cm](char *buf, int size) {
|
||||||
|
return oakengine_color_manager_default_input_color_space(cm, buf,
|
||||||
|
size);
|
||||||
|
});
|
||||||
|
|
||||||
foreach (QString cs, input_cs) {
|
for (int i = 0; i < cs_count; i++) {
|
||||||
|
QString cs = oak_query_string([config, i](char *buf, int size) {
|
||||||
|
return oakengine_color_config_colorspace_at(config, i, buf,
|
||||||
|
size);
|
||||||
|
});
|
||||||
default_input_colorspace_->addItem(cs);
|
default_input_colorspace_->addItem(cs);
|
||||||
|
|
||||||
if (cs ==
|
if (cs == default_cs) {
|
||||||
working_project_->color_manager()->get_default_input_color_space()) {
|
|
||||||
default_input_colorspace_->setCurrentIndex(
|
default_input_colorspace_->setCurrentIndex(
|
||||||
default_input_colorspace_->count() - 1);
|
default_input_colorspace_->count() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ocio::Exception &e) {
|
|
||||||
|
oakengine_color_config_free(config);
|
||||||
|
} else {
|
||||||
|
char err_buf[1024];
|
||||||
|
oakengine_color_last_error(err_buf, sizeof(err_buf));
|
||||||
ocio_config_is_valid_ = false;
|
ocio_config_is_valid_ = false;
|
||||||
ocio_filename_->setStyleSheet(
|
ocio_filename_->setStyleSheet(
|
||||||
QStringLiteral("QLineEdit {color: red;}"));
|
QStringLiteral("QLineEdit {color: red;}"));
|
||||||
ocio_config_error_ = e.what();
|
ocio_config_error_ = QString::fromUtf8(err_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectPropertiesDialog::open_disk_cache_settings()
|
void ProjectPropertiesDialog::open_disk_cache_settings()
|
||||||
{
|
{
|
||||||
if (disk_cache_radios_[Project::k_cache_use_default_location]->isChecked()) {
|
if (disk_cache_radios_[Project::k_cache_use_default_location]->isChecked()) {
|
||||||
DiskManager::instance()->show_disk_cache_settings_dialog(
|
oakengine_disk_show_settings_dialog(nullptr, this);
|
||||||
DiskManager::instance()->get_default_cache_folder(), this);
|
|
||||||
} else if (disk_cache_radios_[Project::k_cache_store_alongside_project]
|
} else if (disk_cache_radios_[Project::k_cache_store_alongside_project]
|
||||||
->isChecked()) {
|
->isChecked()) {
|
||||||
DiskManager::instance()->show_disk_cache_settings_dialog(
|
oakengine_disk_show_settings_dialog(
|
||||||
working_project_->get_cache_alongside_project_path(), this);
|
[this]() -> QString {
|
||||||
|
char buf[4096];
|
||||||
|
oakengine_project_cache_alongside_path(
|
||||||
|
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||||
|
buf, sizeof(buf));
|
||||||
|
return QString::fromUtf8(buf);
|
||||||
|
}().toUtf8().constData(), this);
|
||||||
} else {
|
} else {
|
||||||
DiskManager::instance()->show_disk_cache_settings_dialog(
|
oakengine_disk_show_settings_dialog(
|
||||||
custom_cache_path_->text(), this);
|
custom_cache_path_->text().toUtf8().constData(), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,11 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "common/configwrapper.h"
|
||||||
#include "node/project.h"
|
#include "node/project.h"
|
||||||
|
#include "oakengine/project.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -42,8 +44,8 @@ ProxyDialog::ProxyDialog(QWidget *parent, const QVector<Footage *> &footage)
|
|||||||
{
|
{
|
||||||
setWindowTitle(tr("Proxy Settings"));
|
setWindowTitle(tr("Proxy Settings"));
|
||||||
|
|
||||||
const ProxyManager::ProxyParams params =
|
oak_proxy_params params;
|
||||||
ProxyManager::proxy_params_from_config();
|
oakengine_proxy_params_from_config(¶ms);
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
@@ -186,9 +188,12 @@ void ProxyDialog::accept()
|
|||||||
if (!footage_.isEmpty()) {
|
if (!footage_.isEmpty()) {
|
||||||
for (Footage *item : footage_) {
|
for (Footage *item : footage_) {
|
||||||
if (custom_params_checkbox_->isChecked()) {
|
if (custom_params_checkbox_->isChecked()) {
|
||||||
item->set_custom_proxy_params(current_params());
|
oak_proxy_params p = current_params();
|
||||||
|
oakengine_footage_set_custom_proxy_params(
|
||||||
|
reinterpret_cast<OakEngineFootage *>(item), &p);
|
||||||
} else {
|
} else {
|
||||||
item->clear_custom_proxy_params();
|
oakengine_footage_clear_custom_proxy_params(
|
||||||
|
reinterpret_cast<OakEngineFootage *>(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,15 +274,18 @@ void ProxyDialog::set_f_fmpeg_path(const QString &path)
|
|||||||
ffmpeg_path_edit_->setText(path);
|
ffmpeg_path_edit_->setText(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxyManager::ProxyParams ProxyDialog::current_params() const
|
oak_proxy_params ProxyDialog::current_params() const
|
||||||
{
|
{
|
||||||
ProxyManager::ProxyParams params = ProxyManager::proxy_params_from_config();
|
oak_proxy_params params;
|
||||||
|
oakengine_proxy_params_from_config(¶ms);
|
||||||
params.width = static_cast<int>(width_slider_->get_value());
|
params.width = static_cast<int>(width_slider_->get_value());
|
||||||
params.height = static_cast<int>(height_slider_->get_value());
|
params.height = static_cast<int>(height_slider_->get_value());
|
||||||
params.divider = resolution_combo_->currentData().toInt();
|
params.divider = resolution_combo_->currentData().toInt();
|
||||||
params.crf = static_cast<int>(crf_slider_->get_value());
|
params.crf = static_cast<int>(crf_slider_->get_value());
|
||||||
params.preset = preset_combo_->currentText();
|
strncpy(params.preset, preset_combo_->currentText().toUtf8().constData(),
|
||||||
params.include_audio = include_audio_checkbox_->isChecked();
|
sizeof(params.preset) - 1);
|
||||||
|
params.preset[sizeof(params.preset) - 1] = '\0';
|
||||||
|
params.include_audio = include_audio_checkbox_->isChecked() ? 1 : 0;
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,40 +310,60 @@ void ProxyDialog::refresh_footage_list()
|
|||||||
for (const Footage *item : footage_) {
|
for (const Footage *item : footage_) {
|
||||||
QTreeWidgetItem *tree_item = new QTreeWidgetItem(footage_tree_);
|
QTreeWidgetItem *tree_item = new QTreeWidgetItem(footage_tree_);
|
||||||
tree_item->setText(0, item->filename());
|
tree_item->setText(0, item->filename());
|
||||||
QString state = ProxyManager::proxy_state_to_string(item->proxy_state());
|
{
|
||||||
|
char state_buf[256];
|
||||||
|
int state_len = oakengine_proxy_state_to_string(
|
||||||
|
item->proxy_state(), state_buf, sizeof(state_buf));
|
||||||
|
QString state = (state_len > 0)
|
||||||
|
? QString::fromUtf8(state_buf, state_len)
|
||||||
|
: QString();
|
||||||
if (item->has_custom_proxy_params()) {
|
if (item->has_custom_proxy_params()) {
|
||||||
state = tr("%1 (custom settings)").arg(state);
|
state = tr("%1 (custom settings)").arg(state);
|
||||||
}
|
}
|
||||||
tree_item->setText(1, state);
|
tree_item->setText(1, state);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyDialog::generate_proxies()
|
void ProxyDialog::generate_proxies()
|
||||||
{
|
{
|
||||||
if (!ProxyManager::instance()) {
|
|
||||||
qWarning() << "ProxyDialog::GenerateProxies: ProxyManager unavailable";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Footage *item : footage_) {
|
for (Footage *item : footage_) {
|
||||||
const VideoParams video = item->get_first_enabled_video_stream();
|
const VideoParams video = item->get_first_enabled_video_stream();
|
||||||
if (!video.is_valid()) {
|
oak_video_params _vp;
|
||||||
|
oakengine_viewer_get_first_enabled_video_stream(
|
||||||
|
reinterpret_cast<OakEngineNode *>(item), &_vp);
|
||||||
|
if (!oakengine_video_params_is_valid(&_vp)) {
|
||||||
qWarning()
|
qWarning()
|
||||||
<< "ProxyDialog::GenerateProxies: skipping item with no valid video stream"
|
<< "ProxyDialog::GenerateProxies: skipping item with no valid video stream"
|
||||||
<< item->filename();
|
<< item->filename();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProxyManager::ProxyParams params =
|
oak_proxy_params params;
|
||||||
custom_params_checkbox_->isChecked() ? current_params()
|
if (custom_params_checkbox_->isChecked()) {
|
||||||
: item->get_effective_proxy_params();
|
params = current_params();
|
||||||
const ProxyManager::Proxy proxy =
|
} else {
|
||||||
ProxyManager::instance()->get_or_start_proxy(
|
oakengine_footage_get_effective_proxy_params(
|
||||||
item->project()->cache_path(), item->filename(),
|
reinterpret_cast<OakEngineFootage *>(item), ¶ms);
|
||||||
video.stream_index(), params);
|
}
|
||||||
item->set_proxy(proxy.filename, proxy.state, video.stream_index(),
|
oak_proxy_result proxy;
|
||||||
params.version, true);
|
char cache_buf[512];
|
||||||
item->invalidate_all(Footage::k_filename_input);
|
oakengine_project_cache_path(
|
||||||
|
reinterpret_cast<OakEngineProject *>(item->project()),
|
||||||
|
cache_buf, sizeof(cache_buf));
|
||||||
|
int ret = oakengine_proxy_get_or_start(
|
||||||
|
cache_buf,
|
||||||
|
item->filename().toUtf8().constData(),
|
||||||
|
video.stream_index(), ¶ms, &proxy);
|
||||||
|
if (ret != 0) {
|
||||||
|
qWarning() << "ProxyDialog::GenerateProxies: failed to get/start proxy for"
|
||||||
|
<< item->filename();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
oakengine_footage_set_proxy(reinterpret_cast<OakEngineFootage *>(item),
|
||||||
|
proxy.filename, proxy.state,
|
||||||
|
video.stream_index(), 1, params.version);
|
||||||
|
oakengine_footage_invalidate(reinterpret_cast<OakEngineFootage *>(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh_footage_list();
|
refresh_footage_list();
|
||||||
@@ -349,9 +377,16 @@ void ProxyDialog::delete_proxies()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QFile::remove(item->proxy_path());
|
QFile::remove(item->proxy_path());
|
||||||
QFile::remove(ProxyManager::get_working_proxy_filename(item->proxy_path()));
|
{
|
||||||
item->clear_proxy();
|
char wbuf[4096];
|
||||||
item->invalidate_all(Footage::k_filename_input);
|
int wlen = oakengine_proxy_get_working_filename(
|
||||||
|
item->proxy_path().toUtf8().constData(), wbuf, sizeof(wbuf));
|
||||||
|
if (wlen > 0) {
|
||||||
|
QFile::remove(QString::fromUtf8(wbuf, wlen));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oakengine_footage_clear_proxy(reinterpret_cast<OakEngineFootage *>(item));
|
||||||
|
oakengine_footage_invalidate(reinterpret_cast<OakEngineFootage *>(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh_footage_list();
|
refresh_footage_list();
|
||||||
|
|||||||
@@ -25,7 +25,10 @@
|
|||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
|
|
||||||
#include "codec/proxymanager.h"
|
#include "oakengine/footage.h"
|
||||||
|
#include "oakengine/proxy.h"
|
||||||
|
#include "oakengine/videoparams.h"
|
||||||
|
#include "oakengine/viewer.h"
|
||||||
#include "node/project/footage/footage.h"
|
#include "node/project/footage/footage.h"
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
|
|
||||||
@@ -68,7 +71,7 @@ public:
|
|||||||
void set_f_fmpeg_path(const QString &path);
|
void set_f_fmpeg_path(const QString &path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ProxyManager::ProxyParams current_params() const;
|
oak_proxy_params current_params() const;
|
||||||
|
|
||||||
void save_global_settings();
|
void save_global_settings();
|
||||||
|
|
||||||
|
|||||||
@@ -30,11 +30,12 @@
|
|||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "common/configwrapper.h"
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "dialog/msgbox.h"
|
#include "dialog/msgbox.h"
|
||||||
#include "oakengine/node.h"
|
#include "oakengine/node.h"
|
||||||
#include "oakengine/timeline.h"
|
#include "oakengine/timeline.h"
|
||||||
|
#include "oakengine/videoparams.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -114,7 +115,7 @@ void SequenceDialog::accept()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!VideoParams::format_is_float(
|
if (!oakengine_video_params_format_is_float(
|
||||||
parameter_tab_->get_selected_preview_format()) &&
|
parameter_tab_->get_selected_preview_format()) &&
|
||||||
!OAK_CONFIG("PreviewNonFloatDontAskAgain").toBool()) {
|
!OAK_CONFIG("PreviewNonFloatDontAskAgain").toBool()) {
|
||||||
QMessageBox b(this);
|
QMessageBox b(this);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "oakengine/timeline.h"
|
#include "oakengine/timeline.h"
|
||||||
|
#include "oakengine/videoparams.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -122,8 +123,7 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence *sequence,
|
|||||||
height_slider_->set_value(height);
|
height_slider_->set_value(height);
|
||||||
framerate_combo_->set_frame_rate(Rational(fps_num, fps_den));
|
framerate_combo_->set_frame_rate(Rational(fps_num, fps_den));
|
||||||
pixelaspect_combo_->set_pixel_aspect_ratio(Rational(par_num, par_den));
|
pixelaspect_combo_->set_pixel_aspect_ratio(Rational(par_num, par_den));
|
||||||
interlacing_combo_->set_interlace_mode(
|
interlacing_combo_->set_interlace_mode(interlacing);
|
||||||
static_cast<VideoParams::Interlacing>(interlacing));
|
|
||||||
preview_resolution_field_->set_divider(divider);
|
preview_resolution_field_->set_divider(divider);
|
||||||
preview_format_field_->set_pixel_format(
|
preview_format_field_->set_pixel_format(
|
||||||
static_cast<PixelFormat::Format>(format));
|
static_cast<PixelFormat::Format>(format));
|
||||||
@@ -173,15 +173,14 @@ void SequenceDialogParameterTab::save_preset_clicked()
|
|||||||
|
|
||||||
void SequenceDialogParameterTab::update_preview_resolution_label()
|
void SequenceDialogParameterTab::update_preview_resolution_label()
|
||||||
{
|
{
|
||||||
VideoParams test_param(get_selected_video_width(), get_selected_video_height(),
|
int ew, eh;
|
||||||
PixelFormat::invalid,
|
oakengine_video_params_effective_size(
|
||||||
VideoParams::k_internal_channel_count, Rational(1),
|
get_selected_video_width(), get_selected_video_height(),
|
||||||
VideoParams::k_interlace_none,
|
preview_resolution_field_->currentData().toInt(), &ew, &eh);
|
||||||
preview_resolution_field_->currentData().toInt());
|
|
||||||
|
|
||||||
preview_resolution_label_->setText(
|
preview_resolution_label_->setText(
|
||||||
tr("(%1x%2)").arg(QString::number(test_param.effective_width()),
|
tr("(%1x%2)").arg(QString::number(ew),
|
||||||
QString::number(test_param.effective_height())));
|
QString::number(eh)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public:
|
|||||||
return pixelaspect_combo_->get_pixel_aspect_ratio();
|
return pixelaspect_combo_->get_pixel_aspect_ratio();
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoParams::Interlacing get_selected_video_interlacing_mode() const
|
int get_selected_video_interlacing_mode() const
|
||||||
{
|
{
|
||||||
return interlacing_combo_->get_interlace_mode();
|
return interlacing_combo_->get_interlace_mode();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
#include <QTreeWidgetItem>
|
#include <QTreeWidgetItem>
|
||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "common/configwrapper.h"
|
||||||
#include "render/videoparams.h"
|
#include "oakengine/videoparams.h"
|
||||||
#include "ui/icons/icons.h"
|
#include "ui/icons/icons.h"
|
||||||
#include "widget/menu/menu.h"
|
#include "widget/menu/menu.h"
|
||||||
|
|
||||||
@@ -75,12 +75,12 @@ SequenceDialogPresetTab::SequenceDialogPresetTab(QWidget *parent)
|
|||||||
|
|
||||||
preset_tree_->addTopLevelItem(
|
preset_tree_->addTopLevelItem(
|
||||||
create_sd_preset_folder(tr("NTSC"), 720, 480, Rational(30000, 1001),
|
create_sd_preset_folder(tr("NTSC"), 720, 480, Rational(30000, 1001),
|
||||||
VideoParams::k_pixel_aspect_ntsc_standard,
|
Rational(8, 9),
|
||||||
VideoParams::k_pixel_aspect_ntsc_widescreen, 1));
|
Rational(32, 27), 1));
|
||||||
preset_tree_->addTopLevelItem(
|
preset_tree_->addTopLevelItem(
|
||||||
create_sd_preset_folder(tr("PAL"), 720, 576, Rational(25, 1),
|
create_sd_preset_folder(tr("PAL"), 720, 576, Rational(25, 1),
|
||||||
VideoParams::k_pixel_aspect_pal_standard,
|
Rational(16, 15),
|
||||||
VideoParams::k_pixel_aspect_pal_widescreen, 1));
|
Rational(64, 45), 1));
|
||||||
|
|
||||||
// Load custom presets
|
// Load custom presets
|
||||||
for (int i = 0; i < get_number_of_presets(); i++) {
|
for (int i = 0; i < get_number_of_presets(); i++) {
|
||||||
@@ -118,32 +118,42 @@ SequenceDialogPresetTab::create_hd_preset_folder(const QString &name, int width,
|
|||||||
add_standard_item(parent,
|
add_standard_item(parent,
|
||||||
std::make_shared<SequencePreset>(
|
std::make_shared<SequencePreset>(
|
||||||
tr("%1 23.976 FPS").arg(name), width, height,
|
tr("%1 23.976 FPS").arg(name), width, height,
|
||||||
Rational(24000, 1001), VideoParams::k_pixel_aspect_square,
|
Rational(24000, 1001),
|
||||||
VideoParams::k_interlace_none, 48000, layout, divider,
|
Rational(1), // k_pixel_aspect_square
|
||||||
|
0, // k_interlace_none
|
||||||
|
48000, layout, divider,
|
||||||
default_format, default_autocache));
|
default_format, default_autocache));
|
||||||
add_standard_item(parent,
|
add_standard_item(parent,
|
||||||
std::make_shared<SequencePreset>(
|
std::make_shared<SequencePreset>(
|
||||||
tr("%1 25 FPS").arg(name), width, height,
|
tr("%1 25 FPS").arg(name), width, height,
|
||||||
Rational(25, 1), VideoParams::k_pixel_aspect_square,
|
Rational(25, 1),
|
||||||
VideoParams::k_interlace_none, 48000, layout, divider,
|
Rational(1), // k_pixel_aspect_square
|
||||||
|
0, // k_interlace_none
|
||||||
|
48000, layout, divider,
|
||||||
default_format, default_autocache));
|
default_format, default_autocache));
|
||||||
add_standard_item(parent,
|
add_standard_item(parent,
|
||||||
std::make_shared<SequencePreset>(
|
std::make_shared<SequencePreset>(
|
||||||
tr("%1 29.97 FPS").arg(name), width, height,
|
tr("%1 29.97 FPS").arg(name), width, height,
|
||||||
Rational(30000, 1001), VideoParams::k_pixel_aspect_square,
|
Rational(30000, 1001),
|
||||||
VideoParams::k_interlace_none, 48000, layout, divider,
|
Rational(1), // k_pixel_aspect_square
|
||||||
|
0, // k_interlace_none
|
||||||
|
48000, layout, divider,
|
||||||
default_format, default_autocache));
|
default_format, default_autocache));
|
||||||
add_standard_item(parent,
|
add_standard_item(parent,
|
||||||
std::make_shared<SequencePreset>(
|
std::make_shared<SequencePreset>(
|
||||||
tr("%1 50 FPS").arg(name), width, height,
|
tr("%1 50 FPS").arg(name), width, height,
|
||||||
Rational(50, 1), VideoParams::k_pixel_aspect_square,
|
Rational(50, 1),
|
||||||
VideoParams::k_interlace_none, 48000, layout, divider,
|
Rational(1), // k_pixel_aspect_square
|
||||||
|
0, // k_interlace_none
|
||||||
|
48000, layout, divider,
|
||||||
default_format, default_autocache));
|
default_format, default_autocache));
|
||||||
add_standard_item(parent,
|
add_standard_item(parent,
|
||||||
std::make_shared<SequencePreset>(
|
std::make_shared<SequencePreset>(
|
||||||
tr("%1 59.94 FPS").arg(name), width, height,
|
tr("%1 59.94 FPS").arg(name), width, height,
|
||||||
Rational(60000, 1001), VideoParams::k_pixel_aspect_square,
|
Rational(60000, 1001),
|
||||||
VideoParams::k_interlace_none, 48000, layout, divider,
|
Rational(1), // k_pixel_aspect_square
|
||||||
|
0, // k_interlace_none
|
||||||
|
48000, layout, divider,
|
||||||
default_format, default_autocache));
|
default_format, default_autocache));
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
@@ -161,12 +171,14 @@ QTreeWidgetItem *SequenceDialogPresetTab::create_sd_preset_folder(
|
|||||||
add_standard_item(
|
add_standard_item(
|
||||||
parent, std::make_shared<SequencePreset>(
|
parent, std::make_shared<SequencePreset>(
|
||||||
tr("%1 Standard").arg(name), width, height, frame_rate,
|
tr("%1 Standard").arg(name), width, height, frame_rate,
|
||||||
standard_par, VideoParams::k_interlaced_bottom_first, 48000,
|
standard_par, 2, // k_interlaced_bottom_first
|
||||||
|
48000,
|
||||||
layout, divider, default_format, default_autocache));
|
layout, divider, default_format, default_autocache));
|
||||||
add_standard_item(
|
add_standard_item(
|
||||||
parent, std::make_shared<SequencePreset>(
|
parent, std::make_shared<SequencePreset>(
|
||||||
tr("%1 Widescreen").arg(name), width, height, frame_rate,
|
tr("%1 Widescreen").arg(name), width, height, frame_rate,
|
||||||
wide_par, VideoParams::k_interlaced_bottom_first, 48000,
|
wide_par, 2, // k_interlaced_bottom_first
|
||||||
|
48000,
|
||||||
layout, divider, default_format, default_autocache));
|
layout, divider, default_format, default_autocache));
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public:
|
|||||||
|
|
||||||
SequencePreset(const QString &name, int width, int height,
|
SequencePreset(const QString &name, int width, int height,
|
||||||
const Rational &frame_rate, const Rational &pixel_aspect,
|
const Rational &frame_rate, const Rational &pixel_aspect,
|
||||||
VideoParams::Interlacing interlacing, int sample_rate,
|
int interlacing, int sample_rate,
|
||||||
uint64_t channel_layout, int preview_divider,
|
uint64_t channel_layout, int preview_divider,
|
||||||
PixelFormat preview_format, bool preview_autocache)
|
PixelFormat preview_format, bool preview_autocache)
|
||||||
: width_(width)
|
: width_(width)
|
||||||
@@ -74,7 +74,7 @@ public:
|
|||||||
reader->name() == QStringLiteral("interlacing_")) {
|
reader->name() == QStringLiteral("interlacing_")) {
|
||||||
// "interlacing_" is the element name mistakenly written by
|
// "interlacing_" is the element name mistakenly written by
|
||||||
// older versions of Save(); accept it for backward compatibility
|
// older versions of Save(); accept it for backward compatibility
|
||||||
interlacing_ = static_cast<VideoParams::Interlacing>(
|
interlacing_ = static_cast<int>(
|
||||||
reader->readElementText().toInt());
|
reader->readElementText().toInt());
|
||||||
} else if (reader->name() == QStringLiteral("samplerate")) {
|
} else if (reader->name() == QStringLiteral("samplerate")) {
|
||||||
sample_rate_ = reader->readElementText().toInt();
|
sample_rate_ = reader->readElementText().toInt();
|
||||||
@@ -140,7 +140,7 @@ public:
|
|||||||
return pixel_aspect_;
|
return pixel_aspect_;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoParams::Interlacing interlacing() const
|
int interlacing() const
|
||||||
{
|
{
|
||||||
return interlacing_;
|
return interlacing_;
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ private:
|
|||||||
int height_;
|
int height_;
|
||||||
Rational frame_rate_;
|
Rational frame_rate_;
|
||||||
Rational pixel_aspect_;
|
Rational pixel_aspect_;
|
||||||
VideoParams::Interlacing interlacing_;
|
int interlacing_;
|
||||||
int sample_rate_;
|
int sample_rate_;
|
||||||
uint64_t channel_layout_;
|
uint64_t channel_layout_;
|
||||||
int preview_divider_;
|
int preview_divider_;
|
||||||
|
|||||||
@@ -27,8 +27,15 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "node/nodeundo.h"
|
#include "node/block/clip/clip.h"
|
||||||
|
#include "oakengine/timeline.h"
|
||||||
|
#include "oakengine/node.h"
|
||||||
|
#include "oakengine/undo.h"
|
||||||
|
#include "widget/timelinewidget/cliphandle.h"
|
||||||
#include "timeline/timelineundopointer.h"
|
#include "timeline/timelineundopointer.h"
|
||||||
|
#include "timeline/timelinecommon.h"
|
||||||
|
#include "timeline/timelineundopointer.h"
|
||||||
|
#include "timeline/timelineundoripple.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -122,15 +129,15 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
|||||||
layout->addWidget(btns);
|
layout->addWidget(btns);
|
||||||
|
|
||||||
// Determine which speed value to use
|
// Determine which speed value to use
|
||||||
start_speed_ = clips.first()->speed();
|
start_speed_ = clip_speed(clips.first());
|
||||||
start_duration_ = clips.first()->length();
|
start_duration_ = clips.first()->length();
|
||||||
start_reverse_ = clips.first()->reverse();
|
start_reverse_ = clip_is_reversed(clips.first());
|
||||||
start_maintain_audio_pitch_ = clips.first()->maintain_audio_pitch();
|
start_maintain_audio_pitch_ = clip_maintain_audio_pitch(clips.first());
|
||||||
start_loop_ = int(clips.first()->loop_mode());
|
start_loop_ = clip_loop_mode(clips.first());
|
||||||
for (int i = 1; i < clips.size(); i++) {
|
for (int i = 1; i < clips.size(); i++) {
|
||||||
ClipBlock *c = clips.at(i);
|
ClipBlock *c = clips.at(i);
|
||||||
|
|
||||||
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, c->speed())) {
|
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, clip_speed(c))) {
|
||||||
// Speed differs per clip
|
// Speed differs per clip
|
||||||
start_speed_ = qSNaN();
|
start_speed_ = qSNaN();
|
||||||
}
|
}
|
||||||
@@ -141,8 +148,8 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
|||||||
|
|
||||||
// Yes, in theory a bool should only ever be 0 or 1 anyway, but MSVC complained and it is
|
// Yes, in theory a bool should only ever be 0 or 1 anyway, but MSVC complained and it is
|
||||||
// *possible* that a bool could be something else, so this code is safer
|
// *possible* that a bool could be something else, so this code is safer
|
||||||
int clip_reverse = c->reverse() ? 1 : 0;
|
int clip_reverse = clip_is_reversed(c) ? 1 : 0;
|
||||||
int clip_maintain_pitch = c->maintain_audio_pitch() ? 1 : 0;
|
int clip_maintain_pitch = clip_maintain_audio_pitch(c) ? 1 : 0;
|
||||||
if (start_reverse_ != -1 && clip_reverse != start_reverse_) {
|
if (start_reverse_ != -1 && clip_reverse != start_reverse_) {
|
||||||
start_reverse_ = -1;
|
start_reverse_ = -1;
|
||||||
}
|
}
|
||||||
@@ -151,7 +158,7 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
|||||||
start_maintain_audio_pitch_ = -1;
|
start_maintain_audio_pitch_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start_loop_ != -1 && int(c->loop_mode()) != start_loop_) {
|
if (start_loop_ != -1 && clip_loop_mode(c) != start_loop_) {
|
||||||
start_loop_ = -1;
|
start_loop_ = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,9 +196,40 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
|||||||
|
|
||||||
void SpeedDurationDialog::accept()
|
void SpeedDurationDialog::accept()
|
||||||
{
|
{
|
||||||
MultiUndoCommand *command = new MultiUndoCommand();
|
// Collect all duration/speed changes into a single undo entry.
|
||||||
|
const QByteArray undo_name = tr("Speed/Duration").toUtf8();
|
||||||
|
oakengine_undo_group_begin(undo_name.constData());
|
||||||
|
|
||||||
// Set duration values
|
// Set speed values
|
||||||
|
if (speed_slider_->is_tristate()) {
|
||||||
|
if (link_box_->isChecked() && !dur_slider_->is_tristate()) {
|
||||||
|
// Automatically determine speed from duration
|
||||||
|
foreach (ClipBlock *c, clips_) {
|
||||||
|
double speed = get_speed_adjustment(clip_speed(c), c->length(),
|
||||||
|
dur_slider_->get_value());
|
||||||
|
oak_node_value val;
|
||||||
|
memset(&val, 0, sizeof(val));
|
||||||
|
val.type = OAK_NODE_VALUE_FLOAT;
|
||||||
|
val.f[0] = speed;
|
||||||
|
oakengine_node_set_input(
|
||||||
|
reinterpret_cast<OakEngineNode *>(c),
|
||||||
|
oakengine_clip_speed_input_id(), &val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set speeds to value of slider
|
||||||
|
foreach (ClipBlock *c, clips_) {
|
||||||
|
oak_node_value val;
|
||||||
|
memset(&val, 0, sizeof(val));
|
||||||
|
val.type = OAK_NODE_VALUE_FLOAT;
|
||||||
|
val.f[0] = speed_slider_->get_value();
|
||||||
|
oakengine_node_set_input(
|
||||||
|
reinterpret_cast<OakEngineNode *>(c),
|
||||||
|
oakengine_clip_speed_input_id(), &val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set duration values (undoable via facade)
|
||||||
TimelineRippleDeleteGapsAtRegionsCommand::RangeList ripple_ranges;
|
TimelineRippleDeleteGapsAtRegionsCommand::RangeList ripple_ranges;
|
||||||
|
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (ClipBlock *c, clips_) {
|
||||||
@@ -199,7 +237,7 @@ void SpeedDurationDialog::accept()
|
|||||||
|
|
||||||
if (dur_slider_->is_tristate()) {
|
if (dur_slider_->is_tristate()) {
|
||||||
if (link_box_->isChecked() && !speed_slider_->is_tristate()) {
|
if (link_box_->isChecked() && !speed_slider_->is_tristate()) {
|
||||||
proposed_length = get_length_adjustment(c->length(), c->speed(),
|
proposed_length = get_length_adjustment(c->length(), clip_speed(c),
|
||||||
speed_slider_->get_value(),
|
speed_slider_->get_value(),
|
||||||
timebase_);
|
timebase_);
|
||||||
}
|
}
|
||||||
@@ -219,8 +257,17 @@ void SpeedDurationDialog::accept()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (proposed_length != c->length()) {
|
if (proposed_length != c->length()) {
|
||||||
command->add_child(new BlockTrimCommand(
|
// Trim the clip's out-point to the new length (one undoable child
|
||||||
c->track(), c, proposed_length, Timeline::k_trim_out));
|
// inside the group, kept as a direct C++ command because the dialog
|
||||||
|
// already works in Rational time and has the track available).
|
||||||
|
oakengine_undo_push(
|
||||||
|
oakengine_block_trim_command(
|
||||||
|
reinterpret_cast<void *>(c->track()),
|
||||||
|
reinterpret_cast<void *>(c),
|
||||||
|
proposed_length.numerator(),
|
||||||
|
proposed_length.denominator(),
|
||||||
|
olive::Timeline::k_trim_out, 0),
|
||||||
|
tr("Trim Clip").toUtf8().constData());
|
||||||
ripple_ranges.append(
|
ripple_ranges.append(
|
||||||
{ c->track(),
|
{ c->track(),
|
||||||
TimeRange(c->in() + proposed_length, c->out()) });
|
TimeRange(c->in() + proposed_length, c->out()) });
|
||||||
@@ -228,70 +275,85 @@ void SpeedDurationDialog::accept()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ripple_box_->isChecked()) {
|
if (ripple_box_->isChecked() && !ripple_ranges.isEmpty()) {
|
||||||
command->add_child(new TimelineRippleDeleteGapsAtRegionsCommand(
|
Sequence *seq = reinterpret_cast<Sequence *>(
|
||||||
clips_.first()->track()->sequence(), ripple_ranges));
|
oakengine_clip_get_sequence(
|
||||||
|
reinterpret_cast<OakEngineClip *>(clips_.first())));
|
||||||
|
if (seq) {
|
||||||
|
QVector<int64_t> range_in_ts;
|
||||||
|
QVector<int64_t> range_out_ts;
|
||||||
|
QVector<int> range_track_types;
|
||||||
|
QVector<int> range_track_indexes;
|
||||||
|
range_in_ts.reserve(ripple_ranges.size());
|
||||||
|
range_out_ts.reserve(ripple_ranges.size());
|
||||||
|
range_track_types.reserve(ripple_ranges.size());
|
||||||
|
range_track_indexes.reserve(ripple_ranges.size());
|
||||||
|
int tbn = 0, tbd = 0;
|
||||||
|
oakengine_node_frame_time_base(
|
||||||
|
reinterpret_cast<OakEngineNode *>(seq), &tbn, &tbd);
|
||||||
|
for (const auto &range : ripple_ranges) {
|
||||||
|
range_track_types.append(range.first->type());
|
||||||
|
range_track_indexes.append(range.first->index());
|
||||||
|
range_in_ts.append(olive::core::Timecode::time_to_timestamp(
|
||||||
|
range.second.in(), olive::Rational(tbn, tbd),
|
||||||
|
olive::core::Timecode::k_round));
|
||||||
|
range_out_ts.append(olive::core::Timecode::time_to_timestamp(
|
||||||
|
range.second.out(), olive::Rational(tbn, tbd),
|
||||||
|
olive::core::Timecode::k_round));
|
||||||
}
|
}
|
||||||
|
oakengine_undo_push(
|
||||||
// Set speed values
|
oakengine_timeline_ripple_delete_gaps_command(
|
||||||
if (speed_slider_->is_tristate()) {
|
reinterpret_cast<void *>(seq),
|
||||||
if (link_box_->isChecked() && !dur_slider_->is_tristate()) {
|
range_in_ts.constData(), range_out_ts.constData(),
|
||||||
// Automatically determine speed from duration
|
range_track_types.constData(),
|
||||||
foreach (ClipBlock *c, clips_) {
|
range_track_indexes.constData(),
|
||||||
command->add_child(new NodeParamSetStandardValueCommand(
|
ripple_ranges.size()),
|
||||||
NodeKeyframeTrackReference(
|
tr("Ripple Delete Gaps").toUtf8().constData());
|
||||||
NodeInput(c, ClipBlock::k_speed_input)),
|
|
||||||
get_speed_adjustment(c->speed(), c->length(),
|
|
||||||
dur_slider_->get_value())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Set speeds to value of slider
|
|
||||||
foreach (ClipBlock *c, clips_) {
|
|
||||||
command->add_child(new NodeParamSetStandardValueCommand(
|
|
||||||
NodeKeyframeTrackReference(NodeInput(c, ClipBlock::k_speed_input)),
|
|
||||||
speed_slider_->get_value()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set reverse values
|
// Set reverse values
|
||||||
if (!reverse_box_->isTristate()) {
|
if (!reverse_box_->isTristate()) {
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (ClipBlock *c, clips_) {
|
||||||
command->add_child(new NodeParamSetStandardValueCommand(
|
oak_node_value val;
|
||||||
NodeKeyframeTrackReference(
|
memset(&val, 0, sizeof(val));
|
||||||
NodeInput(c, ClipBlock::k_reverse_input)),
|
val.type = OAK_NODE_VALUE_BOOL;
|
||||||
reverse_box_->isChecked()));
|
val.num = reverse_box_->isChecked() ? 1 : 0;
|
||||||
|
oakengine_node_set_input(
|
||||||
|
reinterpret_cast<OakEngineNode *>(c),
|
||||||
|
oakengine_clip_reverse_input_id(), &val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set reverse values
|
// Set maintain audio pitch values
|
||||||
if (!maintain_audio_pitch_box_->isTristate()) {
|
if (!maintain_audio_pitch_box_->isTristate()) {
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (ClipBlock *c, clips_) {
|
||||||
command->add_child(new NodeParamSetStandardValueCommand(
|
oak_node_value val;
|
||||||
NodeKeyframeTrackReference(
|
memset(&val, 0, sizeof(val));
|
||||||
NodeInput(c, ClipBlock::k_maintain_audio_pitch_input)),
|
val.type = OAK_NODE_VALUE_BOOL;
|
||||||
maintain_audio_pitch_box_->isChecked()));
|
val.num = maintain_audio_pitch_box_->isChecked() ? 1 : 0;
|
||||||
|
oakengine_node_set_input(
|
||||||
|
reinterpret_cast<OakEngineNode *>(c),
|
||||||
|
oakengine_clip_maintain_audio_pitch_input_id(), &val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loop_combo_->currentIndex() != -1) {
|
if (loop_combo_->currentIndex() != -1) {
|
||||||
foreach (ClipBlock *c, clips_) {
|
foreach (ClipBlock *c, clips_) {
|
||||||
command->add_child(new NodeParamSetStandardValueCommand(
|
oak_node_value val;
|
||||||
NodeKeyframeTrackReference(
|
memset(&val, 0, sizeof(val));
|
||||||
NodeInput(c, ClipBlock::k_loop_mode_input)),
|
val.type = OAK_NODE_VALUE_INT;
|
||||||
loop_combo_->currentData()));
|
val.num = loop_combo_->currentData().toInt();
|
||||||
|
oakengine_node_set_input(
|
||||||
|
reinterpret_cast<OakEngineNode *>(c),
|
||||||
|
oakengine_clip_loop_mode_input_id(), &val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString name = (clips_.size() > 1) ?
|
oakengine_undo_group_end();
|
||||||
tr("Set %1 Clip Properties").arg(clips_.size()) :
|
|
||||||
tr("Set Clip \"%1\" Properties")
|
|
||||||
.arg(clips_.first()->get_label_or_name());
|
|
||||||
Core::instance()->undo_stack()->push(command, name);
|
|
||||||
|
|
||||||
super::accept();
|
super::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rational SpeedDurationDialog::get_length_adjustment(
|
Rational SpeedDurationDialog::get_length_adjustment(
|
||||||
const Rational &original_length, double original_speed, double new_speed,
|
const Rational &original_length, double original_speed, double new_speed,
|
||||||
const Rational &timebase)
|
const Rational &timebase)
|
||||||
|
|||||||
@@ -26,14 +26,13 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
#include "node/block/clip/clip.h"
|
|
||||||
#include "node/block/gap/gap.h"
|
#include "node/block/gap/gap.h"
|
||||||
#include "undo/undocommand.h"
|
|
||||||
#include "widget/slider/floatslider.h"
|
#include "widget/slider/floatslider.h"
|
||||||
#include "widget/slider/rationalslider.h"
|
#include "widget/slider/rationalslider.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive {
|
||||||
{
|
|
||||||
|
class ClipBlock;
|
||||||
|
|
||||||
class SpeedDurationDialog : public QDialog {
|
class SpeedDurationDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|||||||
+32
-28
@@ -24,29 +24,42 @@
|
|||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
#include "oakengine/task.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
#define super ProgressDialog
|
#define super ProgressDialog
|
||||||
|
|
||||||
TaskDialog::TaskDialog(Task *task, const QString &title, QWidget *parent)
|
TaskDialog::TaskDialog(OakEngineTask *task, const QString &title, QWidget *parent)
|
||||||
: super(task->get_title(), title, parent)
|
: super([&]() {
|
||||||
|
char buf[512];
|
||||||
|
buf[0] = '\0';
|
||||||
|
oakengine_task_title(task, buf, sizeof(buf));
|
||||||
|
return QString::fromUtf8(buf);
|
||||||
|
}(), title, parent)
|
||||||
, task_(task)
|
, task_(task)
|
||||||
, destroy_on_close_(true)
|
, destroy_on_close_(true)
|
||||||
, already_shown_(false)
|
, already_shown_(false)
|
||||||
, task_finished_(false)
|
, task_finished_(false)
|
||||||
{
|
{
|
||||||
// Clear task when this dialog is destroyed
|
bridge_ = new EngineEventBridge(this);
|
||||||
task_->setParent(this);
|
bridge_->subscribe(task, OAKENGINE_EVENT_TASK_PROGRESS);
|
||||||
|
connect(bridge_, &EngineEventBridge::task_progress, this,
|
||||||
|
[this](OakEngineTask *, double progress) {
|
||||||
|
set_progress(progress);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
// Connect the save manager progress signal to the progress bar update on the dialog
|
connect(this, &TaskDialog::cancelled, this, [this]() {
|
||||||
connect(task_, &Task::progress_changed, this, &TaskDialog::set_progress,
|
oakengine_task_cancel(task_);
|
||||||
Qt::QueuedConnection);
|
}, Qt::DirectConnection);
|
||||||
|
}
|
||||||
|
|
||||||
// Connect cancel signal (must be a direct connection or it'll be queued after the task has
|
TaskDialog::~TaskDialog()
|
||||||
// already finished)
|
{
|
||||||
connect(this, &TaskDialog::cancelled, task_, &Task::Cancel,
|
if (task_) {
|
||||||
Qt::DirectConnection);
|
oakengine_task_free(task_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskDialog::showEvent(QShowEvent *e)
|
void TaskDialog::showEvent(QShowEvent *e)
|
||||||
@@ -54,20 +67,15 @@ void TaskDialog::showEvent(QShowEvent *e)
|
|||||||
super::showEvent(e);
|
super::showEvent(e);
|
||||||
|
|
||||||
if (!already_shown_) {
|
if (!already_shown_) {
|
||||||
// Create watcher for when the task finishes
|
|
||||||
QFutureWatcher<bool> *task_watcher = new QFutureWatcher<bool>();
|
QFutureWatcher<bool> *task_watcher = new QFutureWatcher<bool>();
|
||||||
|
|
||||||
// Listen for when the task finishes
|
|
||||||
connect(task_watcher, &QFutureWatcher<bool>::finished, this,
|
connect(task_watcher, &QFutureWatcher<bool>::finished, this,
|
||||||
&TaskDialog::task_finished, Qt::QueuedConnection);
|
&TaskDialog::task_finished, Qt::QueuedConnection);
|
||||||
|
|
||||||
// Run task in another thread with QtConcurrent
|
|
||||||
task_watcher->setFuture(
|
task_watcher->setFuture(
|
||||||
#if QT_VERSION_MAJOR >= 6
|
QtConcurrent::run([this]() -> bool {
|
||||||
QtConcurrent::run(&Task::start, task_)
|
return oakengine_task_start_sync(task_) == 1;
|
||||||
#else
|
})
|
||||||
QtConcurrent::run(task_, &Task::Start)
|
|
||||||
#endif
|
|
||||||
);
|
);
|
||||||
|
|
||||||
already_shown_ = true;
|
already_shown_ = true;
|
||||||
@@ -76,19 +84,12 @@ void TaskDialog::showEvent(QShowEvent *e)
|
|||||||
|
|
||||||
void TaskDialog::closeEvent(QCloseEvent *e)
|
void TaskDialog::closeEvent(QCloseEvent *e)
|
||||||
{
|
{
|
||||||
// Cancel task if it is running
|
oakengine_task_cancel(task_);
|
||||||
task_->Cancel();
|
|
||||||
|
|
||||||
// Standard close function
|
|
||||||
super::closeEvent(e);
|
super::closeEvent(e);
|
||||||
|
|
||||||
// Reset shown
|
|
||||||
already_shown_ = false;
|
already_shown_ = false;
|
||||||
|
|
||||||
// Clean up this task and dialog, but only if the task has actually finished.
|
|
||||||
// If the user closes the window while the task is still running, deleting now
|
|
||||||
// would destroy the Task object out from under the worker thread and crash
|
|
||||||
// when the task later touches its own members (e.g. ExportTask::encoder_).
|
|
||||||
if (destroy_on_close_ && task_finished_) {
|
if (destroy_on_close_ && task_finished_) {
|
||||||
deleteLater();
|
deleteLater();
|
||||||
}
|
}
|
||||||
@@ -104,7 +105,10 @@ void TaskDialog::task_finished()
|
|||||||
if (task_watcher->result()) {
|
if (task_watcher->result()) {
|
||||||
emit task_succeeded(task_);
|
emit task_succeeded(task_);
|
||||||
} else {
|
} else {
|
||||||
show_error_message(tr("Task Failed"), task_->get_error());
|
char err[512];
|
||||||
|
err[0] = '\0';
|
||||||
|
oakengine_task_error(task_, err, sizeof(err));
|
||||||
|
show_error_message(tr("Task Failed"), QString::fromUtf8(err));
|
||||||
emit task_failed(task_);
|
emit task_failed(task_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-21
@@ -23,7 +23,8 @@
|
|||||||
#define OAK_TASKDIALOG_H
|
#define OAK_TASKDIALOG_H
|
||||||
|
|
||||||
#include "dialog/progress/progress.h"
|
#include "dialog/progress/progress.h"
|
||||||
#include "task/task.h"
|
#include "engineeventbridge.h"
|
||||||
|
#include "oakengine/task.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -31,29 +32,16 @@ namespace olive
|
|||||||
class TaskDialog : public ProgressDialog {
|
class TaskDialog : public ProgressDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/**
|
TaskDialog(OakEngineTask *task, const QString &title, QWidget *parent = nullptr);
|
||||||
* @brief TaskDialog Constructor
|
|
||||||
*
|
~TaskDialog() override;
|
||||||
* Creates a TaskDialog. The TaskDialog takes ownership of the Task and will destroy it on close.
|
|
||||||
* Connect to the Task::Succeeded() if you want to retrieve information from the task before it
|
|
||||||
* gets destroyed.
|
|
||||||
*/
|
|
||||||
TaskDialog(Task *task, const QString &title, QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set whether TaskDialog should destroy itself (and the task) when it's closed
|
|
||||||
*
|
|
||||||
* This is TRUE by default.
|
|
||||||
*/
|
|
||||||
void set_destroy_on_close(bool e)
|
void set_destroy_on_close(bool e)
|
||||||
{
|
{
|
||||||
destroy_on_close_ = e;
|
destroy_on_close_ = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
OakEngineTask *get_task() const
|
||||||
* @brief Returns this dialog's task
|
|
||||||
*/
|
|
||||||
Task *get_task() const
|
|
||||||
{
|
{
|
||||||
return task_;
|
return task_;
|
||||||
}
|
}
|
||||||
@@ -64,12 +52,14 @@ protected:
|
|||||||
virtual void closeEvent(QCloseEvent *e) override;
|
virtual void closeEvent(QCloseEvent *e) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void task_succeeded(Task *task);
|
void task_succeeded(OakEngineTask *task);
|
||||||
|
|
||||||
void task_failed(Task *task);
|
void task_failed(OakEngineTask *task);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Task *task_;
|
OakEngineTask *task_;
|
||||||
|
|
||||||
|
EngineEventBridge *bridge_ = nullptr;
|
||||||
|
|
||||||
bool destroy_on_close_;
|
bool destroy_on_close_;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,427 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2022 Olive Team
|
||||||
|
Modifications Copyright (C) 2026 Oak 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 "engineeventbridge.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "oakengine/node.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
EngineEventBridge::EngineEventBridge(QObject *parent) : QObject(parent) {}
|
||||||
|
|
||||||
|
EngineEventBridge::~EngineEventBridge()
|
||||||
|
{
|
||||||
|
foreach (int64_t id, subscriptions_) {
|
||||||
|
oakengine_event_unsubscribe(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t EngineEventBridge::subscribe(void *handle, int32_t event_id)
|
||||||
|
{
|
||||||
|
const int64_t id =
|
||||||
|
oakengine_event_subscribe(handle, event_id, &on_engine_event, this);
|
||||||
|
if (id > 0) {
|
||||||
|
subscriptions_.append(id);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineEventBridge::unsubscribe(int64_t id)
|
||||||
|
{
|
||||||
|
if (oakengine_event_unsubscribe(id) == OAKENGINE_OK) {
|
||||||
|
subscriptions_.removeAll(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineEventBridge::unsubscribe_all()
|
||||||
|
{
|
||||||
|
foreach (int64_t id, subscriptions_) {
|
||||||
|
oakengine_event_unsubscribe(id);
|
||||||
|
}
|
||||||
|
subscriptions_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineEventBridge::on_engine_event(const oakengine_event *event,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
static_cast<EngineEventBridge *>(userdata)->dispatch(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EngineEventBridge::dispatch(const oakengine_event *event)
|
||||||
|
{
|
||||||
|
switch (event->id) {
|
||||||
|
case OAKENGINE_EVENT_PROJECT_MODIFIED_CHANGED:
|
||||||
|
emit project_modified_changed(event->a != 0);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_PROJECT_NAME_CHANGED:
|
||||||
|
emit project_name_changed();
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_FOLDER_BEGIN_INSERT_ITEM:
|
||||||
|
emit folder_begin_insert_item(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_FOLDER_END_INSERT_ITEM:
|
||||||
|
emit folder_end_insert_item(static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_FOLDER_BEGIN_REMOVE_ITEM:
|
||||||
|
emit folder_begin_remove_item(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_FOLDER_END_REMOVE_ITEM:
|
||||||
|
emit folder_end_remove_item(static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_TRACK_ADDED:
|
||||||
|
emit sequence_track_added(static_cast<OakEngineTrack *>(event->handle),
|
||||||
|
int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_TRACK_REMOVED:
|
||||||
|
emit sequence_track_removed(
|
||||||
|
static_cast<OakEngineTrack *>(event->handle), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_TRACK_LIST_CHANGED:
|
||||||
|
emit sequence_track_list_changed(
|
||||||
|
static_cast<OakEngineSequence *>(event->source), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_TRACK_HEIGHT_CHANGED:
|
||||||
|
emit sequence_track_height_changed(
|
||||||
|
static_cast<OakEngineSequence *>(event->source),
|
||||||
|
static_cast<OakEngineTrack *>(event->handle), int(event->a),
|
||||||
|
int(event->b));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_SUBTITLES_CHANGED:
|
||||||
|
emit sequence_subtitles_changed(
|
||||||
|
static_cast<OakEngineSequence *>(event->source), event->a,
|
||||||
|
event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TRACK_INDEX_CHANGED:
|
||||||
|
emit track_index_changed(static_cast<OakEngineTrack *>(event->source),
|
||||||
|
int(event->a), int(event->b));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TRACK_HEIGHT_CHANGED: {
|
||||||
|
double h;
|
||||||
|
memcpy(&h, &event->a, sizeof(h));
|
||||||
|
emit track_height_changed(static_cast<OakEngineTrack *>(event->source),
|
||||||
|
h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OAKENGINE_EVENT_TRACK_BLOCKS_REFRESHED:
|
||||||
|
emit track_blocks_refreshed(
|
||||||
|
static_cast<OakEngineTrack *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TRACK_MUTED_CHANGED:
|
||||||
|
emit track_muted_changed(static_cast<OakEngineTrack *>(event->source),
|
||||||
|
event->a != 0);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_BLOCK_ENABLED_CHANGED:
|
||||||
|
emit block_enabled_changed(
|
||||||
|
static_cast<OakEngineBlock *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_BLOCK_PREVIEW_CHANGED:
|
||||||
|
emit block_preview_changed(
|
||||||
|
static_cast<OakEngineBlock *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_MARKER_LIST_MARKER_ADDED:
|
||||||
|
emit marker_list_marker_added(
|
||||||
|
static_cast<OakEngineMarkerList *>(event->source),
|
||||||
|
static_cast<OakEngineMarker *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_MARKER_LIST_MARKER_REMOVED:
|
||||||
|
emit marker_list_marker_removed(
|
||||||
|
static_cast<OakEngineMarkerList *>(event->source),
|
||||||
|
static_cast<OakEngineMarker *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_MARKER_LIST_MARKER_MODIFIED:
|
||||||
|
emit marker_list_marker_modified(
|
||||||
|
static_cast<OakEngineMarkerList *>(event->source),
|
||||||
|
static_cast<OakEngineMarker *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_WORKAREA_RANGE_CHANGED:
|
||||||
|
emit workarea_range_changed(
|
||||||
|
static_cast<OakEngineWorkarea *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_WORKAREA_ENABLED_CHANGED:
|
||||||
|
emit workarea_enabled_changed(
|
||||||
|
static_cast<OakEngineWorkarea *>(event->source), event->a != 0);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TRACK_BLOCK_ADDED:
|
||||||
|
emit track_block_added(static_cast<OakEngineBlock *>(event->handle),
|
||||||
|
event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TRACK_BLOCK_REMOVED:
|
||||||
|
emit track_block_removed(static_cast<OakEngineBlock *>(event->handle),
|
||||||
|
event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_MARKER_ADDED:
|
||||||
|
emit sequence_marker_added(event->a);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_MARKER_REMOVED:
|
||||||
|
emit sequence_marker_removed(event->a);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_MARKER_MODIFIED:
|
||||||
|
emit sequence_marker_modified(event->a);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_WORKAREA_RANGE_CHANGED:
|
||||||
|
emit sequence_workarea_range_changed(event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_SEQUENCE_WORKAREA_ENABLED_CHANGED:
|
||||||
|
emit sequence_workarea_enabled_changed(event->a != 0);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_LABEL_CHANGED:
|
||||||
|
emit node_label_changed(static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_VALUE_CHANGED:
|
||||||
|
emit node_input_value_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), int(event->a),
|
||||||
|
event->b, event->c);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_CONNECTED:
|
||||||
|
emit node_input_connected(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_DISCONNECTED:
|
||||||
|
emit node_input_disconnected(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_FLAGS_CHANGED:
|
||||||
|
emit node_input_flags_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), event->a);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_PROPERTY_CHANGED:
|
||||||
|
emit node_input_property_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_DATA_TYPE_CHANGED:
|
||||||
|
emit node_input_data_type_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_ARRAY_SIZE_CHANGED:
|
||||||
|
emit node_input_array_size_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), int(event->a),
|
||||||
|
int(event->b));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_KEYFRAME_ENABLE_CHANGED:
|
||||||
|
emit node_keyframe_enable_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), int(event->a),
|
||||||
|
event->b != 0);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_KEYFRAME_ADDED:
|
||||||
|
emit node_keyframe_added(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineKeyframe *>(event->handle),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), int(event->a),
|
||||||
|
int(event->b));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_KEYFRAME_REMOVED:
|
||||||
|
emit node_keyframe_removed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineKeyframe *>(event->handle),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""), int(event->a),
|
||||||
|
int(event->b));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_KEYFRAME_TIME_CHANGED:
|
||||||
|
emit node_keyframe_time_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineKeyframe *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_KEYFRAME_TYPE_CHANGED:
|
||||||
|
emit node_keyframe_type_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineKeyframe *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_KEYFRAME_VALUE_CHANGED:
|
||||||
|
emit node_keyframe_value_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineKeyframe *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_NODE_ADDED_TO_CONTEXT:
|
||||||
|
emit node_node_added_to_context(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_NODE_REMOVED_FROM_CONTEXT:
|
||||||
|
emit node_node_removed_from_context(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_MESSAGE_COUNT_CHANGED:
|
||||||
|
emit node_message_count_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_LINKS_CHANGED:
|
||||||
|
emit node_links_changed(static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_COLOR_CHANGED:
|
||||||
|
emit node_color_changed(static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_ADDED:
|
||||||
|
emit node_input_added(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_INPUT_REMOVED:
|
||||||
|
emit node_input_removed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_REMOVED_FROM_GRAPH:
|
||||||
|
emit node_removed_from_graph(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_GROUP_INPUT_PASSTHROUGH_ADDED:
|
||||||
|
emit group_input_passthrough_added(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""),
|
||||||
|
static_cast<int>(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_GROUP_INPUT_PASSTHROUGH_REMOVED:
|
||||||
|
emit group_input_passthrough_removed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""),
|
||||||
|
static_cast<int>(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_GROUP_OUTPUT_PASSTHROUGH_CHANGED:
|
||||||
|
emit group_output_passthrough_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_NODE_CONTEXT_POSITION_CHANGED: {
|
||||||
|
double x, y;
|
||||||
|
memcpy(&x, &event->a, sizeof(x));
|
||||||
|
memcpy(&y, &event->b, sizeof(y));
|
||||||
|
emit node_context_position_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source),
|
||||||
|
static_cast<OakEngineNode *>(event->handle), x, y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OAKENGINE_EVENT_VIEWER_LENGTH_CHANGED:
|
||||||
|
emit viewer_length_changed(static_cast<OakEngineNode *>(event->source),
|
||||||
|
event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED:
|
||||||
|
emit viewer_playhead_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source), event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_FRAME_RATE_CHANGED:
|
||||||
|
emit viewer_frame_rate_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source), event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_SIZE_CHANGED:
|
||||||
|
emit viewer_size_changed(static_cast<OakEngineNode *>(event->source),
|
||||||
|
int(event->a), int(event->b));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_PIXEL_ASPECT_CHANGED:
|
||||||
|
emit viewer_pixel_aspect_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source), event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_INTERLACING_CHANGED:
|
||||||
|
emit viewer_interlacing_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_VIDEO_PARAMS_CHANGED:
|
||||||
|
emit viewer_video_params_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_AUDIO_PARAMS_CHANGED:
|
||||||
|
emit viewer_audio_params_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_TEXTURE_INPUT_CHANGED:
|
||||||
|
emit viewer_texture_input_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_SAMPLE_RATE_CHANGED:
|
||||||
|
emit viewer_sample_rate_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source), int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_VIEWER_CONNECTED_WAVEFORM_CHANGED:
|
||||||
|
emit viewer_connected_waveform_changed(
|
||||||
|
static_cast<OakEngineNode *>(event->source));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TASK_MANAGER_TASK_ADDED:
|
||||||
|
emit task_manager_task_added(
|
||||||
|
static_cast<OakEngineTask *>(event->handle),
|
||||||
|
QString::fromUtf8(event->s ? event->s : ""));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TASK_MANAGER_TASK_REMOVED:
|
||||||
|
emit task_manager_task_removed(
|
||||||
|
static_cast<OakEngineTask *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TASK_MANAGER_TASK_FAILED:
|
||||||
|
emit task_manager_task_failed(
|
||||||
|
static_cast<OakEngineTask *>(event->handle));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TASK_MANAGER_LIST_CHANGED:
|
||||||
|
emit task_manager_list_changed();
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TASK_STARTED:
|
||||||
|
emit task_started(static_cast<OakEngineTask *>(event->source),
|
||||||
|
event->a);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_TASK_PROGRESS: {
|
||||||
|
double d;
|
||||||
|
memcpy(&d, &event->a, sizeof(d));
|
||||||
|
emit task_progress(static_cast<OakEngineTask *>(event->source), d);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OAKENGINE_EVENT_TASK_FINISHED:
|
||||||
|
emit task_finished(static_cast<OakEngineTask *>(event->source),
|
||||||
|
event->a != 0);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_UNDO_INDEX_CHANGED:
|
||||||
|
emit undo_index_changed(int(event->a));
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_PARAMS_CHANGED:
|
||||||
|
emit audio_output_params_changed();
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_PLAYBACK_CACHE_INVALIDATED:
|
||||||
|
emit playback_cache_invalidated(event->source, event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_PLAYBACK_CACHE_VALIDATED:
|
||||||
|
emit playback_cache_validated(event->source, event->a, event->b);
|
||||||
|
break;
|
||||||
|
case OAKENGINE_EVENT_FRAME_CACHE_INVALIDATED:
|
||||||
|
emit frame_cache_invalidated(event->source, event->a, event->b);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2022 Olive Team
|
||||||
|
Modifications Copyright (C) 2026 Oak 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 ENGINEEVENTBRIDGE_H
|
||||||
|
#define ENGINEEVENTBRIDGE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "oakengine/events.h"
|
||||||
|
#include "oakengine/task.h"
|
||||||
|
|
||||||
|
namespace olive
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Qt-signal adapter over the liboakengine event C ABI
|
||||||
|
* (oakengine/events.h).
|
||||||
|
*
|
||||||
|
* The facade delivers engine change notifications as C callbacks; the
|
||||||
|
* application consumes Qt signals. EngineEventBridge sits in between:
|
||||||
|
* subscribe() registers a C callback on an engine handle, and the bridge
|
||||||
|
* re-emits the event as the matching typed Qt signal. This is the standard
|
||||||
|
* replacement for connect(engineObject, &EngineClass::signal, ...) at the
|
||||||
|
* remaining app -> engine connection points (see
|
||||||
|
* docs/zh/facade-migration-roadmap.md).
|
||||||
|
*
|
||||||
|
* Semantics match the connections they replace: the engine invokes the C
|
||||||
|
* callback synchronously on the emitting thread (Qt::DirectConnection
|
||||||
|
* equivalent) and the bridge emits its Qt signals from that same callback,
|
||||||
|
* so receivers are still called synchronously on the engine object's
|
||||||
|
* thread (the GUI thread in practice).
|
||||||
|
*
|
||||||
|
* The bridge owns its subscriptions: destroying it unsubscribes
|
||||||
|
* everything. Subscriptions also die automatically with the observed
|
||||||
|
* engine object (the facade drops them on QObject::destroyed), so a
|
||||||
|
* project/sequence teardown never leaves a dangling callback into the app.
|
||||||
|
*/
|
||||||
|
class EngineEventBridge : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit EngineEventBridge(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
~EngineEventBridge() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subscribe to `event_id` (OAKENGINE_EVENT_*) on `handle` and
|
||||||
|
* return the subscription id (> 0), or 0 on failure.
|
||||||
|
*
|
||||||
|
* `handle` is a facade handle (an engine Node or Project reinterpreted
|
||||||
|
* as OakEngineNode / OakEngineProject etc., per the event table in
|
||||||
|
* oakengine/events.h). Each matching engine change is re-emitted as
|
||||||
|
* the corresponding Qt signal of this bridge.
|
||||||
|
*/
|
||||||
|
int64_t subscribe(void *handle, int32_t event_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cancel a subscription returned by subscribe(). Unknown or
|
||||||
|
* already-dead ids are ignored.
|
||||||
|
*/
|
||||||
|
void unsubscribe(int64_t id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cancel every live subscription (but keep the Qt signal
|
||||||
|
* connections). Use when the observed engine object set changes, e.g.
|
||||||
|
* switching sequences, so stale subscriptions don't pile up.
|
||||||
|
*/
|
||||||
|
void unsubscribe_all();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void project_modified_changed(bool modified);
|
||||||
|
void project_name_changed();
|
||||||
|
|
||||||
|
void folder_begin_insert_item(OakEngineNode *folder,
|
||||||
|
OakEngineNode *child, int index);
|
||||||
|
void folder_end_insert_item(OakEngineNode *folder);
|
||||||
|
void folder_begin_remove_item(OakEngineNode *folder,
|
||||||
|
OakEngineNode *child, int index);
|
||||||
|
void folder_end_remove_item(OakEngineNode *folder);
|
||||||
|
|
||||||
|
void sequence_track_added(OakEngineTrack *track, int track_type);
|
||||||
|
void sequence_track_removed(OakEngineTrack *track, int track_type);
|
||||||
|
void sequence_track_list_changed(OakEngineSequence *source, int track_type);
|
||||||
|
void sequence_track_height_changed(OakEngineSequence *source,
|
||||||
|
OakEngineTrack *track, int track_type,
|
||||||
|
int height_px);
|
||||||
|
void sequence_subtitles_changed(OakEngineSequence *source, qint64 in_ts,
|
||||||
|
qint64 out_ts);
|
||||||
|
|
||||||
|
void track_block_added(OakEngineBlock *block, qint64 in_ts,
|
||||||
|
qint64 out_ts);
|
||||||
|
void track_block_removed(OakEngineBlock *block, qint64 in_ts,
|
||||||
|
qint64 out_ts);
|
||||||
|
void track_index_changed(OakEngineTrack *source, int old_index,
|
||||||
|
int new_index);
|
||||||
|
void track_height_changed(OakEngineTrack *source, double height);
|
||||||
|
void track_blocks_refreshed(OakEngineTrack *source);
|
||||||
|
void track_muted_changed(OakEngineTrack *source, bool muted);
|
||||||
|
|
||||||
|
void block_enabled_changed(OakEngineBlock *source);
|
||||||
|
void block_preview_changed(OakEngineBlock *source);
|
||||||
|
|
||||||
|
void sequence_marker_added(qint64 time_ts);
|
||||||
|
void sequence_marker_removed(qint64 time_ts);
|
||||||
|
void sequence_marker_modified(qint64 time_ts);
|
||||||
|
|
||||||
|
void marker_list_marker_added(OakEngineMarkerList *source,
|
||||||
|
OakEngineMarker *marker);
|
||||||
|
void marker_list_marker_removed(OakEngineMarkerList *source,
|
||||||
|
OakEngineMarker *marker);
|
||||||
|
void marker_list_marker_modified(OakEngineMarkerList *source,
|
||||||
|
OakEngineMarker *marker);
|
||||||
|
|
||||||
|
void sequence_workarea_range_changed(qint64 in_ts, qint64 out_ts);
|
||||||
|
void sequence_workarea_enabled_changed(bool enabled);
|
||||||
|
|
||||||
|
void workarea_range_changed(OakEngineWorkarea *source);
|
||||||
|
void workarea_enabled_changed(OakEngineWorkarea *source, bool enabled);
|
||||||
|
|
||||||
|
/* Node family (source is the subscribed OakEngineNode*). Input ids are
|
||||||
|
* copied out of the event during the callback. */
|
||||||
|
void node_label_changed(OakEngineNode *source, const QString &label);
|
||||||
|
void node_input_value_changed(OakEngineNode *source, const QString &input,
|
||||||
|
int element, qint64 in_ts, qint64 out_ts);
|
||||||
|
void node_input_connected(OakEngineNode *source, OakEngineNode *output,
|
||||||
|
const QString &input, int element);
|
||||||
|
void node_input_disconnected(OakEngineNode *source, OakEngineNode *output,
|
||||||
|
const QString &input, int element);
|
||||||
|
void node_input_flags_changed(OakEngineNode *source, const QString &input,
|
||||||
|
qint64 flags);
|
||||||
|
void node_input_property_changed(OakEngineNode *source,
|
||||||
|
const QString &input);
|
||||||
|
void node_input_data_type_changed(OakEngineNode *source,
|
||||||
|
const QString &input, int type);
|
||||||
|
void node_input_array_size_changed(OakEngineNode *source,
|
||||||
|
const QString &input, int old_size,
|
||||||
|
int new_size);
|
||||||
|
void node_keyframe_enable_changed(OakEngineNode *source,
|
||||||
|
const QString &input, int element,
|
||||||
|
bool enabled);
|
||||||
|
void node_keyframe_added(OakEngineNode *source, OakEngineKeyframe *key,
|
||||||
|
const QString &input, int element, int track);
|
||||||
|
void node_keyframe_removed(OakEngineNode *source, OakEngineKeyframe *key,
|
||||||
|
const QString &input, int element, int track);
|
||||||
|
void node_keyframe_time_changed(OakEngineNode *source,
|
||||||
|
OakEngineKeyframe *key);
|
||||||
|
void node_keyframe_type_changed(OakEngineNode *source,
|
||||||
|
OakEngineKeyframe *key);
|
||||||
|
void node_keyframe_value_changed(OakEngineNode *source,
|
||||||
|
OakEngineKeyframe *key);
|
||||||
|
void node_node_added_to_context(OakEngineNode *source,
|
||||||
|
OakEngineNode *node);
|
||||||
|
void node_node_removed_from_context(OakEngineNode *source,
|
||||||
|
OakEngineNode *node);
|
||||||
|
void node_message_count_changed(OakEngineNode *source);
|
||||||
|
void node_links_changed(OakEngineNode *source);
|
||||||
|
void node_color_changed(OakEngineNode *source);
|
||||||
|
void node_input_added(OakEngineNode *source, const QString &input_id);
|
||||||
|
void node_input_removed(OakEngineNode *source, const QString &input_id);
|
||||||
|
void node_removed_from_graph(OakEngineNode *source,
|
||||||
|
OakEngineNode *project);
|
||||||
|
|
||||||
|
/* Group family (source is the group node). */
|
||||||
|
void group_input_passthrough_added(OakEngineNode *source,
|
||||||
|
OakEngineNode *node,
|
||||||
|
const QString &input, int element);
|
||||||
|
void group_input_passthrough_removed(OakEngineNode *source,
|
||||||
|
OakEngineNode *node,
|
||||||
|
const QString &input, int element);
|
||||||
|
void group_output_passthrough_changed(OakEngineNode *source,
|
||||||
|
OakEngineNode *output);
|
||||||
|
|
||||||
|
/* Context position (source is the context node). */
|
||||||
|
void node_context_position_changed(OakEngineNode *source,
|
||||||
|
OakEngineNode *node, double x,
|
||||||
|
double y);
|
||||||
|
|
||||||
|
/* Viewer family (source is the subscribed viewer OakEngineNode*).
|
||||||
|
* Rational payloads (seconds) are delivered as num/den pairs. */
|
||||||
|
void viewer_length_changed(OakEngineNode *source, qint64 num, qint64 den);
|
||||||
|
void viewer_playhead_changed(OakEngineNode *source, qint64 num,
|
||||||
|
qint64 den);
|
||||||
|
void viewer_frame_rate_changed(OakEngineNode *source, qint64 num,
|
||||||
|
qint64 den);
|
||||||
|
void viewer_size_changed(OakEngineNode *source, int w, int h);
|
||||||
|
void viewer_pixel_aspect_changed(OakEngineNode *source, qint64 num,
|
||||||
|
qint64 den);
|
||||||
|
void viewer_interlacing_changed(OakEngineNode *source, int mode);
|
||||||
|
void viewer_video_params_changed(OakEngineNode *source);
|
||||||
|
void viewer_audio_params_changed(OakEngineNode *source);
|
||||||
|
void viewer_texture_input_changed(OakEngineNode *source);
|
||||||
|
void viewer_sample_rate_changed(OakEngineNode *source, int sr);
|
||||||
|
void viewer_connected_waveform_changed(OakEngineNode *source);
|
||||||
|
|
||||||
|
/* Task manager family (title is copied out of the event). */
|
||||||
|
void task_manager_task_added(OakEngineTask *task, const QString &title);
|
||||||
|
void task_manager_task_removed(OakEngineTask *task);
|
||||||
|
void task_manager_task_failed(OakEngineTask *task);
|
||||||
|
void task_manager_list_changed();
|
||||||
|
|
||||||
|
/* Task family (source is the subscribed OakEngineTask*). */
|
||||||
|
void task_started(OakEngineTask *source, qint64 start_time);
|
||||||
|
void task_progress(OakEngineTask *source, double progress);
|
||||||
|
void task_finished(OakEngineTask *source, bool succeeded);
|
||||||
|
|
||||||
|
/* Undo stack family. */
|
||||||
|
void undo_index_changed(int index);
|
||||||
|
|
||||||
|
/* AudioManager family. */
|
||||||
|
void audio_output_params_changed();
|
||||||
|
|
||||||
|
/* Playback cache / frame cache family (B9c). */
|
||||||
|
void playback_cache_invalidated(void *cache, qint64 a, qint64 b);
|
||||||
|
void playback_cache_validated(void *cache, qint64 a, qint64 b);
|
||||||
|
void frame_cache_invalidated(void *cache, qint64 a, qint64 b);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// C callback entry point; `userdata` is the EngineEventBridge.
|
||||||
|
static void on_engine_event(const oakengine_event *event,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
void dispatch(const oakengine_event *event);
|
||||||
|
|
||||||
|
QVector<int64_t> subscriptions_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ENGINEEVENTBRIDGE_H
|
||||||
+41
-24
@@ -27,6 +27,7 @@
|
|||||||
* Use the navigation above to find documentation on classes or source files.
|
* Use the navigation above to find documentation on classes or source files.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "oakengine/plugin.h"
|
||||||
#include "pluginSupport/olivehost.h"
|
#include "pluginSupport/olivehost.h"
|
||||||
|
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
@@ -37,11 +38,12 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QSurfaceFormat>
|
#include <QSurfaceFormat>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include <oakengine/config.h>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "common/commandlineparser.h"
|
#include "common/commandlineparser.h"
|
||||||
#include "common/debug.h"
|
#include "common/debugapp.h"
|
||||||
#include "node/project/serializer/serializer.h"
|
#include <oakengine/serializer.h>
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "window/mainwindow/mainwindow.h"
|
#include "window/mainwindow/mainwindow.h"
|
||||||
|
|
||||||
@@ -55,6 +57,17 @@
|
|||||||
#ifdef USE_CRASHPAD
|
#ifdef USE_CRASHPAD
|
||||||
#include "common/crashpadinterface.h"
|
#include "common/crashpadinterface.h"
|
||||||
#endif // USE_CRASHPAD
|
#endif // USE_CRASHPAD
|
||||||
|
|
||||||
|
static void config_error_handler(const char *title, const char *message,
|
||||||
|
void *)
|
||||||
|
{
|
||||||
|
QWidget *parent = olive::Core::instance() ?
|
||||||
|
olive::Core::instance()->main_window() :
|
||||||
|
nullptr;
|
||||||
|
QMessageBox::critical(parent, QString::fromUtf8(title),
|
||||||
|
QString::fromUtf8(message), QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
int decompress_project(const QString &project)
|
int decompress_project(const QString &project)
|
||||||
{
|
{
|
||||||
if (project.isEmpty()) {
|
if (project.isEmpty()) {
|
||||||
@@ -80,7 +93,7 @@ int decompress_project(const QString &project)
|
|||||||
.toUtf8()
|
.toUtf8()
|
||||||
.constData());
|
.constData());
|
||||||
|
|
||||||
if (!olive::ProjectSerializer::check_compressed_id(&project_file)) {
|
if (!oakengine_serializer_check_compressed(project.toUtf8().constData())) {
|
||||||
printf("%s\n",
|
printf("%s\n",
|
||||||
QCoreApplication::translate(
|
QCoreApplication::translate(
|
||||||
"main", "Failed to decompress, project may be corrupt")
|
"main", "Failed to decompress, project may be corrupt")
|
||||||
@@ -182,7 +195,9 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
olive::Core::CoreParams startup_params;
|
OakEngineAppParams startup_params;
|
||||||
|
memset(&startup_params, 0, sizeof(startup_params));
|
||||||
|
startup_params.run_mode = OAKENGINE_APP_RUN_NORMAL;
|
||||||
|
|
||||||
CommandLineParser parser;
|
CommandLineParser parser;
|
||||||
|
|
||||||
@@ -282,26 +297,32 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (export_option->is_set()) {
|
if (export_option->is_set()) {
|
||||||
startup_params.set_run_mode(olive::Core::CoreParams::k_headless_export);
|
startup_params.run_mode = OAKENGINE_APP_RUN_HEADLESS_EXPORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts_option->is_set()) {
|
if (ts_option->is_set()) {
|
||||||
if (ts_option->get_setting().isEmpty()) {
|
if (ts_option->get_setting().isEmpty()) {
|
||||||
qWarning() << "--ts was set but no translation file was provided";
|
qWarning() << "--ts was set but no translation file was provided";
|
||||||
} else {
|
} else {
|
||||||
startup_params.set_startup_language(ts_option->get_setting());
|
QByteArray sl_utf = ts_option->get_setting().toUtf8();
|
||||||
|
startup_params.startup_language = sl_utf.constData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool load_plugins = !no_plugin->is_set();
|
const bool load_plugins = !no_plugin->is_set();
|
||||||
|
|
||||||
if (crash_option->is_set()) {
|
if (crash_option->is_set()) {
|
||||||
startup_params.set_crash_on_startup(true);
|
startup_params.crash_on_startup = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
startup_params.set_fullscreen(fullscreen_option->is_set());
|
startup_params.fullscreen = fullscreen_option->is_set() ? 1 : 0;
|
||||||
|
|
||||||
startup_params.set_startup_project(project_argument->get_setting());
|
{
|
||||||
|
QByteArray sp_utf = project_argument->get_setting().toUtf8();
|
||||||
|
if (!sp_utf.isEmpty()) {
|
||||||
|
startup_params.startup_project = sp_utf.constData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set OpenGL display profile. Oak's render pipeline still uses OpenGL
|
// Set OpenGL display profile. Oak's render pipeline still uses OpenGL
|
||||||
// internally even when Vulkan is requested as the Qt graphics backend.
|
// internally even when Vulkan is requested as the Qt graphics backend.
|
||||||
@@ -328,7 +349,7 @@ int main(int argc, char *argv[])
|
|||||||
// Create application instance
|
// Create application instance
|
||||||
std::unique_ptr<QCoreApplication> a;
|
std::unique_ptr<QCoreApplication> a;
|
||||||
|
|
||||||
if (startup_params.run_mode() == olive::Core::CoreParams::k_run_normal) {
|
if (startup_params.run_mode == OAKENGINE_APP_RUN_NORMAL) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Since Oak Video Editor is linked with the console subsystem (for better POSIX compatibility), a console
|
// Since Oak Video Editor is linked with the console subsystem (for better POSIX compatibility), a console
|
||||||
// is created by default. If the user didn't request one, we free it here.
|
// is created by default. If the user didn't request one, we free it here.
|
||||||
@@ -344,19 +365,15 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// Configuration errors are reported through a UI handler so the engine
|
// Configuration errors are reported through a UI handler so the engine
|
||||||
// layer (config) never has to know about dialogs
|
// layer (config) never has to know about dialogs
|
||||||
olive::Config::set_error_handler(
|
oakengine_config_set_error_handler(config_error_handler, NULL);
|
||||||
[](const QString &title, const QString &message) {
|
|
||||||
QWidget *parent =
|
|
||||||
olive::Core::instance() ? olive::Core::instance()->main_window() :
|
|
||||||
nullptr;
|
|
||||||
QMessageBox::critical(parent, title, message, QMessageBox::Ok);
|
|
||||||
});
|
|
||||||
|
|
||||||
olive::Config::load();
|
oakengine_config_load();
|
||||||
|
char backend_buf[64];
|
||||||
|
int backend_len = oakengine_config_get_string(
|
||||||
|
"GraphicsBackend", backend_buf, sizeof(backend_buf));
|
||||||
const QString graphics_backend =
|
const QString graphics_backend =
|
||||||
olive::Config::current()[QStringLiteral("GraphicsBackend")]
|
backend_len > 0 ? QString::fromUtf8(backend_buf).toLower() :
|
||||||
.toString()
|
QStringLiteral("opengl");
|
||||||
.toLower();
|
|
||||||
qputenv("QSG_RHI_BACKEND", graphics_backend == QStringLiteral("vulkan") ?
|
qputenv("QSG_RHI_BACKEND", graphics_backend == QStringLiteral("vulkan") ?
|
||||||
QByteArrayLiteral("vulkan") :
|
QByteArrayLiteral("vulkan") :
|
||||||
QByteArrayLiteral("opengl"));
|
QByteArrayLiteral("opengl"));
|
||||||
@@ -367,7 +384,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (load_plugins) {
|
if (load_plugins) {
|
||||||
olive::plugin::load_plugins("plugins");
|
oakengine_plugin_load_plugins("plugins");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -421,7 +438,7 @@ int main(int argc, char *argv[])
|
|||||||
#endif // USE_CRASHPAD
|
#endif // USE_CRASHPAD
|
||||||
|
|
||||||
// Start core
|
// Start core
|
||||||
olive::Core c(startup_params);
|
olive::Core c(&startup_params);
|
||||||
|
|
||||||
c.start();
|
c.start();
|
||||||
|
|
||||||
|
|||||||
@@ -40,20 +40,25 @@ public:
|
|||||||
virtual void deselect_all() override;
|
virtual void deselect_all() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void set_node(Node *node)
|
void set_node(OakEngineNode *node)
|
||||||
{
|
{
|
||||||
// Convert single pointer to either an empty vector or a vector of one
|
// Convert single pointer to either an empty vector or a vector of one
|
||||||
QVector<Node *> nodes;
|
QVector<Node *> nodes;
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
nodes.append(node);
|
nodes.append(reinterpret_cast<Node *>(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
set_nodes(nodes);
|
set_nodes(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Not a slot: signature uses the engine C++ type Node*, which must not be
|
||||||
|
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
||||||
|
// boundary). All connections use new-style member-function syntax.
|
||||||
void set_nodes(const QVector<Node *> &nodes);
|
void set_nodes(const QVector<Node *> &nodes);
|
||||||
|
|
||||||
|
public slots:
|
||||||
virtual void increase_track_height() override;
|
virtual void increase_track_height() override;
|
||||||
|
|
||||||
virtual void decrease_track_height() override;
|
virtual void decrease_track_height() override;
|
||||||
|
|||||||
@@ -45,12 +45,12 @@ void FootageViewerPanel::override_work_area(const TimeRange &r)
|
|||||||
get_footage_viewer_widget()->override_work_area(r);
|
get_footage_viewer_widget()->override_work_area(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<ViewerOutput *> FootageViewerPanel::get_selected_footage() const
|
QVector<OakEngineNode *> FootageViewerPanel::get_selected_footage() const
|
||||||
{
|
{
|
||||||
QVector<ViewerOutput *> list;
|
QVector<OakEngineNode *> list;
|
||||||
|
|
||||||
if (get_connected_viewer()) {
|
if (get_connected_viewer()) {
|
||||||
list.append(get_connected_viewer());
|
list.append(reinterpret_cast<OakEngineNode *>(get_connected_viewer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public:
|
|||||||
return static_cast<FootageViewerWidget *>(get_time_based_widget());
|
return static_cast<FootageViewerWidget *>(get_time_based_widget());
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual QVector<ViewerOutput *> get_selected_footage() const override;
|
virtual QVector<OakEngineNode *> get_selected_footage() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void retranslate() override;
|
virtual void retranslate() override;
|
||||||
|
|||||||
+10
-7
@@ -25,6 +25,8 @@
|
|||||||
#include "panel/panel.h"
|
#include "panel/panel.h"
|
||||||
#include "widget/nodeview/nodewidget.h"
|
#include "widget/nodeview/nodewidget.h"
|
||||||
|
|
||||||
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -117,21 +119,22 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void select(const QVector<Node::ContextPair> &p)
|
void select(
|
||||||
|
const QVector<QPair<OakEngineNode *, OakEngineNode *>> &p)
|
||||||
{
|
{
|
||||||
node_widget_->view()->select(p, true);
|
node_widget_->view()->select(p, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nodes_selected(const QVector<Node *> &nodes);
|
void nodes_selected(const QVector<OakEngineNode *> &nodes);
|
||||||
|
|
||||||
void nodes_deselected(const QVector<Node *> &nodes);
|
void nodes_deselected(const QVector<OakEngineNode *> &nodes);
|
||||||
|
|
||||||
void node_selection_changed(const QVector<Node *> &nodes);
|
void node_selection_changed(const QVector<OakEngineNode *> &nodes);
|
||||||
void
|
void node_selection_changed_with_contexts(
|
||||||
node_selection_changed_with_contexts(const QVector<Node::ContextPair> &nodes);
|
const QVector<QPair<OakEngineNode *, OakEngineNode *>> &nodes);
|
||||||
|
|
||||||
void node_group_opened(NodeGroup *group);
|
void node_group_opened(OakEngineNode *group);
|
||||||
|
|
||||||
void node_group_closed();
|
void node_group_closed();
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include "panelmanager.h"
|
#include "panelmanager.h"
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "common/configwrapper.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void set_selected_nodes(const QVector<Node::ContextPair> &nodes)
|
void set_selected_nodes(
|
||||||
|
const QVector<QPair<OakEngineNode *, OakEngineNode *>> &nodes)
|
||||||
{
|
{
|
||||||
get_param_view()->set_selected_nodes(nodes, false);
|
get_param_view()->set_selected_nodes(nodes, false);
|
||||||
}
|
}
|
||||||
@@ -61,12 +62,17 @@ public slots:
|
|||||||
|
|
||||||
virtual void deselect_all() override;
|
virtual void deselect_all() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Not a slot: signature uses the engine C++ type Node*, which must not be
|
||||||
|
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
||||||
|
// boundary). All connections use new-style member-function syntax.
|
||||||
void set_contexts(const QVector<Node *> &contexts);
|
void set_contexts(const QVector<Node *> &contexts);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void focused_node_changed(Node *n);
|
void focused_node_changed(OakEngineNode *n);
|
||||||
|
|
||||||
void selected_nodes_changed(const QVector<Node::ContextPair> &nodes);
|
void selected_nodes_changed(
|
||||||
|
const QVector<QPair<OakEngineNode *, OakEngineNode *>> &nodes);
|
||||||
|
|
||||||
void request_viewer_to_start_editing_text();
|
void request_viewer_to_start_editing_text();
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,14 @@
|
|||||||
|
|
||||||
#include "node/project/footage/footage.h"
|
#include "node/project/footage/footage.h"
|
||||||
|
|
||||||
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
class FootageManagementPanel {
|
class FootageManagementPanel {
|
||||||
public:
|
public:
|
||||||
virtual QVector<ViewerOutput *> get_selected_footage() const = 0;
|
virtual QVector<OakEngineNode *> get_selected_footage() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "oakengine/events.h"
|
||||||
|
#include "oakengine/project.h"
|
||||||
#include "node/project/sequence/sequence.h"
|
#include "node/project/sequence/sequence.h"
|
||||||
#include "panel/footageviewer/footageviewer.h"
|
#include "panel/footageviewer/footageviewer.h"
|
||||||
#include "panel/timeline/timeline.h"
|
#include "panel/timeline/timeline.h"
|
||||||
@@ -36,6 +38,14 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
ProjectPanel::~ProjectPanel()
|
||||||
|
{
|
||||||
|
if (project_name_sub_ > 0) {
|
||||||
|
oakengine_event_unsubscribe(project_name_sub_);
|
||||||
|
project_name_sub_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ProjectPanel::ProjectPanel(const QString &unique_name)
|
ProjectPanel::ProjectPanel(const QString &unique_name)
|
||||||
: PanelWidget(unique_name)
|
: PanelWidget(unique_name)
|
||||||
{
|
{
|
||||||
@@ -87,19 +97,24 @@ Project *ProjectPanel::project() const
|
|||||||
void ProjectPanel::set_project(Project *p)
|
void ProjectPanel::set_project(Project *p)
|
||||||
{
|
{
|
||||||
if (project()) {
|
if (project()) {
|
||||||
disconnect(project(), &Project::name_changed, this,
|
if (project_name_sub_ > 0) {
|
||||||
&ProjectPanel::update_subtitle);
|
oakengine_event_unsubscribe(project_name_sub_);
|
||||||
disconnect(project(), &Project::name_changed, this,
|
project_name_sub_ = 0;
|
||||||
&ProjectPanel::project_name_changed);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
explorer_->set_project(p);
|
explorer_->set_project(p);
|
||||||
|
|
||||||
if (project()) {
|
if (project()) {
|
||||||
connect(project(), &Project::name_changed, this,
|
auto *ph = reinterpret_cast<OakEngineProject *>(project());
|
||||||
&ProjectPanel::update_subtitle);
|
project_name_sub_ = oakengine_event_subscribe(
|
||||||
connect(project(), &Project::name_changed, this,
|
ph, OAKENGINE_EVENT_PROJECT_NAME_CHANGED,
|
||||||
&ProjectPanel::project_name_changed);
|
[](const oakengine_event *, void *userdata) {
|
||||||
|
auto *s = static_cast<ProjectPanel *>(userdata);
|
||||||
|
s->update_subtitle();
|
||||||
|
s->project_name_changed();
|
||||||
|
},
|
||||||
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_subtitle();
|
update_subtitle();
|
||||||
@@ -154,7 +169,7 @@ void ProjectPanel::rename_selected()
|
|||||||
explorer_->rename_selected_item();
|
explorer_->rename_selected_item();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectPanel::edit(Node *item)
|
void ProjectPanel::edit(OakEngineNode *item)
|
||||||
{
|
{
|
||||||
explorer_->edit(item);
|
explorer_->edit(item);
|
||||||
}
|
}
|
||||||
@@ -170,8 +185,9 @@ void ProjectPanel::retranslate()
|
|||||||
update_subtitle();
|
update_subtitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectPanel::item_double_click_slot(Node *item)
|
void ProjectPanel::item_double_click_slot(OakEngineNode *item_handle)
|
||||||
{
|
{
|
||||||
|
Node *item = reinterpret_cast<Node *>(item_handle);
|
||||||
if (item == nullptr) {
|
if (item == nullptr) {
|
||||||
// If the user double clicks on empty space, show the import dialog
|
// If the user double clicks on empty space, show the import dialog
|
||||||
Core::instance()->dialog_import_show();
|
Core::instance()->dialog_import_show();
|
||||||
@@ -201,7 +217,11 @@ void ProjectPanel::show_new_menu()
|
|||||||
void ProjectPanel::update_subtitle()
|
void ProjectPanel::update_subtitle()
|
||||||
{
|
{
|
||||||
if (project()) {
|
if (project()) {
|
||||||
QString project_title = QStringLiteral("%1").arg(project()->name());
|
char name_buf[256];
|
||||||
|
oakengine_project_name(
|
||||||
|
reinterpret_cast<OakEngineProject *>(project()),
|
||||||
|
name_buf, sizeof(name_buf));
|
||||||
|
QString project_title = QString::fromUtf8(name_buf);
|
||||||
|
|
||||||
if (explorer_->get_root() != project()->root()) {
|
if (explorer_->get_root() != project()->root()) {
|
||||||
QString folder_path;
|
QString folder_path;
|
||||||
@@ -229,14 +249,14 @@ void ProjectPanel::save_connected_project()
|
|||||||
Core::instance()->save_project();
|
Core::instance()->save_project();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<ViewerOutput *> ProjectPanel::get_selected_footage() const
|
QVector<OakEngineNode *> ProjectPanel::get_selected_footage() const
|
||||||
{
|
{
|
||||||
QVector<Node *> items = selected_items();
|
QVector<Node *> items = selected_items();
|
||||||
QVector<ViewerOutput *> footage;
|
QVector<OakEngineNode *> footage;
|
||||||
|
|
||||||
foreach (Node *i, items) {
|
foreach (Node *i, items) {
|
||||||
if (dynamic_cast<ViewerOutput *>(i)) {
|
if (dynamic_cast<ViewerOutput *>(i)) {
|
||||||
footage.append(static_cast<ViewerOutput *>(i));
|
footage.append(reinterpret_cast<OakEngineNode *>(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
#include "panel/panel.h"
|
#include "panel/panel.h"
|
||||||
#include "widget/projectexplorer/projectexplorer.h"
|
#include "widget/projectexplorer/projectexplorer.h"
|
||||||
|
|
||||||
|
struct OakEngineNode;
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ class ProjectPanel : public PanelWidget, public FootageManagementPanel {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ProjectPanel(const QString &unique_name);
|
ProjectPanel(const QString &unique_name);
|
||||||
|
~ProjectPanel() override;
|
||||||
|
|
||||||
Project *project() const;
|
Project *project() const;
|
||||||
void set_project(Project *p);
|
void set_project(Project *p);
|
||||||
@@ -49,7 +52,7 @@ public:
|
|||||||
|
|
||||||
Folder *get_selected_folder() const;
|
Folder *get_selected_folder() const;
|
||||||
|
|
||||||
virtual QVector<ViewerOutput *> get_selected_footage() const override;
|
virtual QVector<OakEngineNode *> get_selected_footage() const override;
|
||||||
|
|
||||||
ProjectViewModel *model() const;
|
ProjectViewModel *model() const;
|
||||||
|
|
||||||
@@ -66,20 +69,23 @@ public:
|
|||||||
virtual void rename_selected() override;
|
virtual void rename_selected() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void edit(Node *item);
|
void edit(OakEngineNode *item);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void project_name_changed();
|
void project_name_changed();
|
||||||
|
|
||||||
void selection_changed(const QVector<Node *> &selected);
|
void selection_changed(const QVector<OakEngineNode *> &selected);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void retranslate() override;
|
virtual void retranslate() override;
|
||||||
|
|
||||||
ProjectExplorer *explorer_;
|
ProjectExplorer *explorer_;
|
||||||
|
|
||||||
|
// Event subscription IDs (replaces connect to Project signals)
|
||||||
|
int64_t project_name_sub_ = 0;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void item_double_click_slot(Node *item);
|
void item_double_click_slot(OakEngineNode *item);
|
||||||
|
|
||||||
void show_new_menu();
|
void show_new_menu();
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ void ScopePanel::set_reference_buffer(TexturePtr frame)
|
|||||||
waveform_view_->set_buffer(frame);
|
waveform_view_->set_buffer(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopePanel::set_color_manager(ColorManager *manager)
|
void ScopePanel::set_color_manager(OakEngineColorManager *manager)
|
||||||
{
|
{
|
||||||
histogram_->connect_color_manager(manager);
|
histogram_->connect_color_manager(manager);
|
||||||
vectorscope_->connect_color_manager(manager);
|
vectorscope_->connect_color_manager(manager);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void set_reference_buffer(TexturePtr frame);
|
void set_reference_buffer(TexturePtr frame);
|
||||||
|
|
||||||
void set_color_manager(ColorManager *manager);
|
void set_color_manager(OakEngineColorManager *manager);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void retranslate() override;
|
virtual void retranslate() override;
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ class NodeTablePanel : public TimeBasedPanel {
|
|||||||
public:
|
public:
|
||||||
NodeTablePanel();
|
NodeTablePanel();
|
||||||
|
|
||||||
public slots:
|
public:
|
||||||
|
// Not slots: signatures use the engine C++ type Node*, which must not be
|
||||||
|
// exposed to MOC (it would pull Node::staticMetaObject across the ABI
|
||||||
|
// boundary). All connections use new-style member-function syntax.
|
||||||
void select_nodes(const QVector<Node *> &nodes)
|
void select_nodes(const QVector<Node *> &nodes)
|
||||||
{
|
{
|
||||||
static_cast<NodeTableWidget *>(get_time_based_widget())->select_nodes(nodes);
|
static_cast<NodeTableWidget *>(get_time_based_widget())->select_nodes(nodes);
|
||||||
|
|||||||
@@ -21,13 +21,15 @@
|
|||||||
|
|
||||||
#include "taskmanager.h"
|
#include "taskmanager.h"
|
||||||
|
|
||||||
#include "task/taskmanager.h"
|
#include "engineeventbridge.h"
|
||||||
|
#include "oakengine/task.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
TaskManagerPanel::TaskManagerPanel()
|
TaskManagerPanel::TaskManagerPanel()
|
||||||
: PanelWidget(QStringLiteral("TaskManagerPanel"))
|
: PanelWidget(QStringLiteral("TaskManagerPanel"))
|
||||||
|
, bridge_(new EngineEventBridge(this))
|
||||||
{
|
{
|
||||||
// Create task view
|
// Create task view
|
||||||
view_ = new TaskView(this);
|
view_ = new TaskView(this);
|
||||||
@@ -35,15 +37,30 @@ TaskManagerPanel::TaskManagerPanel()
|
|||||||
// Set it as the main widget
|
// Set it as the main widget
|
||||||
setWidget(view_);
|
setWidget(view_);
|
||||||
|
|
||||||
// Connect task view to the task manager
|
// Connect task view to the task manager via EngineEventBridge
|
||||||
connect(TaskManager::instance(), &TaskManager::task_added, view_,
|
bridge_->subscribe(oakengine_task_manager_handle(),
|
||||||
&TaskView::add_task);
|
OAKENGINE_EVENT_TASK_MANAGER_TASK_ADDED);
|
||||||
connect(TaskManager::instance(), &TaskManager::task_removed, view_,
|
bridge_->subscribe(oakengine_task_manager_handle(),
|
||||||
&TaskView::remove_task);
|
OAKENGINE_EVENT_TASK_MANAGER_TASK_REMOVED);
|
||||||
connect(TaskManager::instance(), &TaskManager::task_failed, view_,
|
bridge_->subscribe(oakengine_task_manager_handle(),
|
||||||
&TaskView::task_failed);
|
OAKENGINE_EVENT_TASK_MANAGER_TASK_FAILED);
|
||||||
connect(view_, &TaskView::task_cancelled, TaskManager::instance(),
|
|
||||||
&TaskManager::cancel_task);
|
connect(bridge_, &EngineEventBridge::task_manager_task_added, this,
|
||||||
|
[this](OakEngineTask *task, const QString &) {
|
||||||
|
view_->add_task(task);
|
||||||
|
});
|
||||||
|
connect(bridge_, &EngineEventBridge::task_manager_task_removed, this,
|
||||||
|
[this](OakEngineTask *task) {
|
||||||
|
view_->remove_task(task);
|
||||||
|
});
|
||||||
|
connect(bridge_, &EngineEventBridge::task_manager_task_failed, this,
|
||||||
|
[this](OakEngineTask *task) {
|
||||||
|
view_->task_failed(task);
|
||||||
|
});
|
||||||
|
connect(view_, &TaskView::task_cancelled, this,
|
||||||
|
[](OakEngineTask *t) {
|
||||||
|
oakengine_task_manager_cancel(t);
|
||||||
|
});
|
||||||
|
|
||||||
// Set strings
|
// Set strings
|
||||||
retranslate();
|
retranslate();
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class EngineEventBridge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A PanelWidget wrapper around a TaskView widget
|
* @brief A PanelWidget wrapper around a TaskView widget
|
||||||
*/
|
*/
|
||||||
@@ -40,6 +42,8 @@ private:
|
|||||||
virtual void retranslate() override;
|
virtual void retranslate() override;
|
||||||
|
|
||||||
TaskView *view_;
|
TaskView *view_;
|
||||||
|
|
||||||
|
EngineEventBridge *bridge_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ TimeBasedPanel::TimeBasedPanel(const QString &object_name)
|
|||||||
, widget_(nullptr)
|
, widget_(nullptr)
|
||||||
, show_and_raise_on_connect_(false)
|
, show_and_raise_on_connect_(false)
|
||||||
{
|
{
|
||||||
|
bridge_ = new EngineEventBridge(this);
|
||||||
|
connect(bridge_, &EngineEventBridge::node_label_changed, this,
|
||||||
|
[this](OakEngineNode *, const QString &label) {
|
||||||
|
set_subtitle(label);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeBasedPanel::~TimeBasedPanel()
|
TimeBasedPanel::~TimeBasedPanel()
|
||||||
@@ -142,16 +147,19 @@ void TimeBasedPanel::retranslate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeBasedPanel::connected_node_changed(ViewerOutput *old, ViewerOutput *now)
|
void TimeBasedPanel::connected_node_changed(OakEngineNode *old, OakEngineNode *now)
|
||||||
{
|
{
|
||||||
if (old) {
|
if (old) {
|
||||||
disconnect(old, &ViewerOutput::label_changed, this,
|
if (label_sub_) {
|
||||||
&TimeBasedPanel::set_subtitle);
|
bridge_->unsubscribe(label_sub_);
|
||||||
|
label_sub_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (now) {
|
if (now) {
|
||||||
connect(now, &ViewerOutput::label_changed, this,
|
label_sub_ = bridge_->subscribe(
|
||||||
&TimeBasedPanel::set_subtitle);
|
reinterpret_cast<void *>(now),
|
||||||
|
OAKENGINE_EVENT_NODE_LABEL_CHANGED);
|
||||||
|
|
||||||
if (show_and_raise_on_connect_) {
|
if (show_and_raise_on_connect_) {
|
||||||
this->show();
|
this->show();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "panel/panel.h"
|
#include "panel/panel.h"
|
||||||
#include "widget/timebased/timebasedwidget.h"
|
#include "widget/timebased/timebasedwidget.h"
|
||||||
|
#include "engineeventbridge.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -141,8 +142,11 @@ private:
|
|||||||
|
|
||||||
bool show_and_raise_on_connect_;
|
bool show_and_raise_on_connect_;
|
||||||
|
|
||||||
|
EngineEventBridge *bridge_ = nullptr;
|
||||||
|
int64_t label_sub_ = 0;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void connected_node_changed(ViewerOutput *old, ViewerOutput *now);
|
void connected_node_changed(OakEngineNode *old, OakEngineNode *now);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,13 +171,13 @@ void TimelinePanel::rename_selected()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TimelinePanel::insert_footage_at_playhead(
|
void TimelinePanel::insert_footage_at_playhead(
|
||||||
const QVector<ViewerOutput *> &footage)
|
const QVector<OakEngineNode *> &footage)
|
||||||
{
|
{
|
||||||
timeline_widget()->insert_footage_at_playhead(footage);
|
timeline_widget()->insert_footage_at_playhead(footage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimelinePanel::overwrite_footage_at_playhead(
|
void TimelinePanel::overwrite_footage_at_playhead(
|
||||||
const QVector<ViewerOutput *> &footage)
|
const QVector<OakEngineNode *> &footage)
|
||||||
{
|
{
|
||||||
timeline_widget()->overwrite_footage_at_playhead(footage);
|
timeline_widget()->overwrite_footage_at_playhead(footage);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user