diff --git a/app/cli/cliexport/cliexportmanager.cpp b/app/cli/cliexport/cliexportmanager.cpp
new file mode 100644
index 000000000..de0145729
--- /dev/null
+++ b/app/cli/cliexport/cliexportmanager.cpp
@@ -0,0 +1,30 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#include "cliexportmanager.h"
+
+OLIVE_NAMESPACE_ENTER
+
+CLIExportManager::CLIExportManager()
+{
+
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/cli/cliexport/cliexportmanager.h b/app/cli/cliexport/cliexportmanager.h
new file mode 100644
index 000000000..6d3fc346b
--- /dev/null
+++ b/app/cli/cliexport/cliexportmanager.h
@@ -0,0 +1,36 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#ifndef CLIEXPORTMANAGER_H
+#define CLIEXPORTMANAGER_H
+
+#include "task/export/export.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class CLIExportManager : public QObject
+{
+public:
+ CLIExportManager();
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // CLIEXPORTMANAGER_H
diff --git a/app/cli/cliprogress/cliprogressdialog.cpp b/app/cli/cliprogress/cliprogressdialog.cpp
index fa846c1ff..88c0cfd57 100644
--- a/app/cli/cliprogress/cliprogressdialog.cpp
+++ b/app/cli/cliprogress/cliprogressdialog.cpp
@@ -27,9 +27,10 @@ OLIVE_NAMESPACE_ENTER
CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) :
QObject(parent),
title_(title),
- progress_(0),
+ progress_(-1),
drawn_(false)
{
+ SetProgress(0);
}
void CLIProgressDialog::Update()
@@ -68,7 +69,7 @@ void CLIProgressDialog::Update()
std::cout << "[";
// Get UI bar progress
- int bar_prog = qRound(progress_ * 0.01 * progress_bar_columns);
+ int bar_prog = qRound(progress_ * progress_bar_columns);
// Draw filled in bar
for (int i=0;iGetTitle(), parent)
+ CLIProgressDialog(task->GetTitle(), parent),
+ task_(task)
{
- // FIXME: Still developing this, don't try to use
+ connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
+}
+
+bool CLITaskDialog::Run()
+{
+ return task_->Start();
}
OLIVE_NAMESPACE_EXIT
diff --git a/app/cli/clitask/clitaskdialog.h b/app/cli/clitask/clitaskdialog.h
index 6408b238e..c574b6011 100644
--- a/app/cli/clitask/clitaskdialog.h
+++ b/app/cli/clitask/clitaskdialog.h
@@ -28,9 +28,15 @@ OLIVE_NAMESPACE_ENTER
class CLITaskDialog : public CLIProgressDialog
{
+ Q_OBJECT
public:
CLITaskDialog(Task *task, QObject* parent = nullptr);
+ bool Run();
+
+private:
+ Task* task_;
+
};
OLIVE_NAMESPACE_EXIT
diff --git a/app/codec/encoder.cpp b/app/codec/encoder.cpp
index 43e0ca1a6..388278525 100644
--- a/app/codec/encoder.cpp
+++ b/app/codec/encoder.cpp
@@ -158,6 +158,63 @@ void EncodingParams::SetExportLength(const rational &export_length)
export_length_ = export_length;
}
+void EncodingParams::Save(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement(QStringLiteral("encode"));
+
+ writer->writeTextElement(QStringLiteral("filename"), filename_);
+
+ writer->writeStartElement(QStringLiteral("video"));
+
+ writer->writeAttribute(QStringLiteral("enabled"), QString::number(video_enabled_));
+
+ if (video_enabled_) {
+ writer->writeTextElement(QStringLiteral("codec"), QString::number(video_codec_));
+ writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width()));
+ writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height()));
+ writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format()));
+ writer->writeTextElement(QStringLiteral("timebase"), video_params_.time_base().toString());
+ writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params_.divider()));
+ writer->writeTextElement(QStringLiteral("bitrate"), QString::number(video_bit_rate_));
+ writer->writeTextElement(QStringLiteral("maxbitrate"), QString::number(video_max_bit_rate_));
+ writer->writeTextElement(QStringLiteral("bufsize"), QString::number(video_buffer_size_));
+ writer->writeTextElement(QStringLiteral("threads"), QString::number(video_threads_));
+
+ if (!video_opts_.isEmpty()) {
+ writer->writeStartElement(QStringLiteral("opts"));
+
+ QHash::const_iterator i;
+ for (i=video_opts_.constBegin(); i!=video_opts_.constEnd(); i++) {
+ writer->writeStartElement(QStringLiteral("entry"));
+
+ writer->writeTextElement(QStringLiteral("key"), i.key());
+ writer->writeTextElement(QStringLiteral("value"), i.value());
+
+ writer->writeEndElement(); // entry
+ }
+
+ writer->writeEndElement(); // opts
+ }
+ }
+
+ writer->writeEndElement(); // video
+
+ writer->writeStartElement(QStringLiteral("audio"));
+
+ writer->writeAttribute(QStringLiteral("enabled"), QString::number(audio_enabled_));
+
+ if (audio_enabled_) {
+ writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_));
+ writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate()));
+ writer->writeTextElement(QStringLiteral("channellayout"), QString::number(audio_params_.channel_layout()));
+ writer->writeTextElement(QStringLiteral("format"), QString::number(audio_params_.format()));
+ }
+
+ writer->writeEndElement(); // audio
+
+ writer->writeEndElement(); // encode
+}
+
Encoder* Encoder::CreateFromID(const QString &id, const EncodingParams& params)
{
Q_UNUSED(id)
diff --git a/app/codec/encoder.h b/app/codec/encoder.h
index 8a60a75cc..260c735a3 100644
--- a/app/codec/encoder.h
+++ b/app/codec/encoder.h
@@ -23,6 +23,7 @@
#include
#include
+#include
#include "codec/exportcodec.h"
#include "codec/exportformat.h"
@@ -69,6 +70,8 @@ public:
const rational& GetExportLength() const;
void SetExportLength(const rational& GetExportLength);
+ virtual void Save(QXmlStreamWriter* writer) const;
+
private:
QString filename_;
diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp
index 4d2430e30..3b54fcdb2 100644
--- a/app/codec/ffmpeg/ffmpegdecoder.cpp
+++ b/app/codec/ffmpeg/ffmpegdecoder.cpp
@@ -458,6 +458,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
video_stream->set_width(avstream->codecpar->width);
video_stream->set_height(avstream->codecpar->height);
+ video_stream->set_format(GetNativePixelFormat(FFmpegCommon::GetCompatiblePixelFormat(static_cast(avstream->codecpar->format))));
video_stream->set_frame_rate(av_guess_frame_rate(fmt_ctx, avstream, nullptr));
video_stream->set_start_time(avstream->start_time);
diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp
index e5f7b74c8..5a7650e89 100644
--- a/app/codec/oiio/oiiodecoder.cpp
+++ b/app/codec/oiio/oiiodecoder.cpp
@@ -117,6 +117,7 @@ bool OIIODecoder::Probe(Footage *f, const QAtomicInt *cancelled)
image_stream->set_width(in->spec().width);
image_stream->set_height(in->spec().height);
+ image_stream->set_format(GetFormatFromOIIOBasetype(in->spec()));
// Images will always have just one stream
image_stream->set_index(0);
@@ -278,6 +279,23 @@ void OIIODecoder::BufferToFrame(OIIO::ImageBuf *buf, FramePtr frame)
#endif
}
+PixelFormat::Format OIIODecoder::GetFormatFromOIIOBasetype(const OIIO::ImageSpec& spec)
+{
+ bool has_alpha = (spec.nchannels == kRGBAChannels);
+
+ if (spec.format == OIIO::TypeDesc::UINT8) {
+ return has_alpha ? PixelFormat::PIX_FMT_RGBA8 : PixelFormat::PIX_FMT_RGB8;
+ } else if (spec.format == OIIO::TypeDesc::UINT16) {
+ return has_alpha ? PixelFormat::PIX_FMT_RGBA16U : PixelFormat::PIX_FMT_RGB16U;
+ } else if (spec.format == OIIO::TypeDesc::HALF) {
+ return has_alpha ? PixelFormat::PIX_FMT_RGBA16F : PixelFormat::PIX_FMT_RGB16F;
+ } else if (spec.format == OIIO::TypeDesc::FLOAT) {
+ return has_alpha ? PixelFormat::PIX_FMT_RGBA32F : PixelFormat::PIX_FMT_RGB32F;
+ } else {
+ return PixelFormat::PIX_FMT_INVALID;
+ }
+}
+
bool OIIODecoder::FileTypeIsSupported(const QString& fn)
{
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
@@ -361,16 +379,9 @@ bool OIIODecoder::OpenImageHandler(const QString &fn)
is_rgba_ = (spec.nchannels == kRGBAChannels);
- // Weirdly, switch statement doesn't work correctly here
- if (spec.format == OIIO::TypeDesc::UINT8) {
- pix_fmt_ = is_rgba_ ? PixelFormat::PIX_FMT_RGBA8 : PixelFormat::PIX_FMT_RGB8;
- } else if (spec.format == OIIO::TypeDesc::UINT16) {
- pix_fmt_ = is_rgba_ ? PixelFormat::PIX_FMT_RGBA16U : PixelFormat::PIX_FMT_RGB16U;
- } else if (spec.format == OIIO::TypeDesc::HALF) {
- pix_fmt_ = is_rgba_ ? PixelFormat::PIX_FMT_RGBA16F : PixelFormat::PIX_FMT_RGB16F;
- } else if (spec.format == OIIO::TypeDesc::FLOAT) {
- pix_fmt_ = is_rgba_ ? PixelFormat::PIX_FMT_RGBA32F : PixelFormat::PIX_FMT_RGB32F;
- } else {
+ pix_fmt_ = GetFormatFromOIIOBasetype(spec);
+
+ if (pix_fmt_ == PixelFormat::PIX_FMT_INVALID) {
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format";
return false;
}
diff --git a/app/codec/oiio/oiiodecoder.h b/app/codec/oiio/oiiodecoder.h
index f481481b1..990fc3aff 100644
--- a/app/codec/oiio/oiiodecoder.h
+++ b/app/codec/oiio/oiiodecoder.h
@@ -57,6 +57,8 @@ public:
static void BufferToFrame(OIIO::ImageBuf* buf, FramePtr frame);
+ static PixelFormat::Format GetFormatFromOIIOBasetype(const OIIO::ImageSpec& spec);
+
private:
#if OIIO_VERSION < 10903
OIIO::ImageInput* image_;
diff --git a/app/core.cpp b/app/core.cpp
index 70fb71f6c..d737a0563 100644
--- a/app/core.cpp
+++ b/app/core.cpp
@@ -81,18 +81,17 @@ Core *Core::instance()
return &instance_;
}
-bool Core::Start()
+int Core::execute(QCoreApplication* a)
{
- // Reset config (Config sets to default on construction already, but we do it again here as a workaround that fixes
- // the fact that some of the config paths set by default rely on the app name having been set (in main())
- Config::Current().SetDefaults();
+ int exit_code = 1;
+
+ // Start core
+ OLIVE_NAMESPACE::Core::instance()->Start();
//
// Parse command line arguments
//
- QCoreApplication* app = QCoreApplication::instance();
-
QCommandLineParser parser;
parser.addHelpOption();
parser.addVersionOption();
@@ -111,7 +110,7 @@ bool Core::Start()
parser.addOption(headless_export_option);
// Parse options
- parser.process(*app);
+ parser.process(*a);
QStringList args = parser.positionalArguments();
@@ -120,6 +119,44 @@ bool Core::Start()
startup_project_ = args.first();
}
+ gui_active_ = !parser.isSet(headless_export_option);
+
+ if (gui_active_) {
+
+ // Start GUI
+ StartGUI(parser.isSet(fullscreen_option));
+
+ // If we have a startup
+ QMetaObject::invokeMethod(this, "OpenStartupProject", Qt::QueuedConnection);
+
+ // Run application loop and receive exit code
+ exit_code = a->exec();
+
+ } else {
+
+ if (parser.isSet(headless_export_option)) {
+ // Start a headless export
+ if (StartHeadlessExport()) {
+ exit_code = 0;
+ }
+ }
+
+ }
+
+
+
+ // Clear core memory
+ OLIVE_NAMESPACE::Core::instance()->Stop();
+
+ return exit_code;
+}
+
+void Core::Start()
+{
+ // Reset config (Config sets to default on construction already, but we do it again here as a workaround that fixes
+ // the fact that some of the config paths set by default rely on the app name having been set (in main())
+ Config::Current().SetDefaults();
+
// Declare custom types for Qt signal/slot system
DeclareTypesForQt();
@@ -141,53 +178,6 @@ bool Core::Start()
//
qInfo() << "Using Qt version:" << qVersion();
-
- gui_active_ = !parser.isSet(headless_export_option);
-
- if (gui_active_) {
-
- // Start GUI
- StartGUI(parser.isSet(fullscreen_option));
-
- // Load startup project
- if (!startup_project_.isEmpty() && !QFileInfo::exists(startup_project_)) {
- QMessageBox::warning(main_window(),
- tr("Failed to open startup file"),
- tr("The project \"%1\" doesn't exist. A new project will be started instead.").arg(startup_project_),
- QMessageBox::Ok);
-
- startup_project_.clear();
- }
-
- if (startup_project_.isEmpty()) {
- // If no load project is set, create a new one on open
- CreateNewProject();
- } else {
- OpenProjectInternal(startup_project_);
- }
-
- return true;
-
- } else {
-
- if (parser.isSet(headless_export_option)) {
-
- if (startup_project_.isEmpty()) {
- qCritical().noquote() << tr("You must specify a project file to export");
- } else {
- OpenProjectInternal(startup_project_);
-
- qDebug() << "Ready for exporting!";
-
- return true;
- }
-
- }
-
- // Error fallback
- return false;
-
- }
}
void Core::Stop()
@@ -539,6 +529,106 @@ void Core::ProjectWasModified(bool e)
}
}
+bool Core::StartHeadlessExport()
+{
+ if (startup_project_.isEmpty()) {
+ qCritical().noquote() << tr("You must specify a project file to export");
+ return false;
+ }
+
+ if (!QFileInfo::exists(startup_project_)) {
+ qCritical().noquote() << tr("Specified project does not exist");
+ return false;
+ }
+
+ // Start a load task and try running it
+ ProjectLoadTask plm(startup_project_);
+ CLITaskDialog task_dialog(&plm);
+
+ if (task_dialog.Run()) {
+ ProjectPtr p = plm.GetLoadedProjects().first();
+ QList items = p->get_items_of_type(Item::kSequence);
+
+ // Check if this project contains sequences
+ if (items.isEmpty()) {
+ qCritical().noquote() << tr("Project contains no sequences, nothing to export");
+ return false;
+ }
+
+ SequencePtr sequence = nullptr;
+
+ // Check if this project contains multiple sequences
+ if (items.size() > 1) {
+ qInfo().noquote() << tr("This project has multiple sequences. Which do you wish to export?");
+ for (int i=0;iname().toStdString();
+ }
+
+ QTextStream stream(stdin);
+ QString sequence_read;
+ int sequence_index = -1;
+ QString quit_code = QStringLiteral("q");
+ std::string prompt = tr("Enter number (or %1 to cancel): ").arg(quit_code).toStdString();
+ forever {
+ std::cout << prompt;
+
+ stream.readLineInto(&sequence_read);
+
+ if (!QString::compare(sequence_read, quit_code, Qt::CaseInsensitive)) {
+ return false;
+ }
+
+ bool ok;
+ sequence_index = sequence_read.toInt(&ok);
+
+ if (ok && sequence_index >= 0 && sequence_index < items.size()) {
+ break;
+ } else {
+ qCritical().noquote() << tr("Invalid sequence number");
+ }
+ }
+
+ sequence = std::static_pointer_cast(items.at(sequence_index));
+ } else {
+ sequence = std::static_pointer_cast(items.first());
+ }
+
+ ExportParams params;
+ ExportTask export_task(sequence->viewer_output(), p->color_manager(), params);
+ CLITaskDialog export_dialog(&export_task);
+ if (export_dialog.Run()) {
+ qInfo().noquote() << tr("Export succeeded");
+ return true;
+ } else {
+ qInfo().noquote() << tr("Export failed: %1").arg(export_task.GetError());
+ return false;
+ }
+ } else {
+ qCritical().noquote() << tr("Project failed to load: %1").arg(plm.GetError());
+ return false;
+ }
+}
+
+void Core::OpenStartupProject()
+{
+ // Load startup project
+ if (!startup_project_.isEmpty() && !QFileInfo::exists(startup_project_)) {
+ QMessageBox::warning(main_window_,
+ tr("Failed to open startup file"),
+ tr("The project \"%1\" doesn't exist. A new project will be started instead.").arg(startup_project_),
+ QMessageBox::Ok);
+
+ startup_project_.clear();
+ }
+
+ if (startup_project_.isEmpty()) {
+ // If no load project is set, create a new one on open
+ CreateNewProject();
+ } else {
+ OpenProjectInternal(startup_project_);
+ }
+}
+
void Core::DeclareTypesForQt()
{
qRegisterMetaType();
@@ -559,6 +649,7 @@ void Core::DeclareTypesForQt()
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
+ qRegisterMetaType();
}
void Core::StartGUI(bool full_screen)
@@ -877,6 +968,11 @@ bool Core::SaveProjectAs(ProjectPtr p)
GetProjectFilter());
if (!fn.isEmpty()) {
+ QString extension(QStringLiteral(".ove"));
+ if (!fn.endsWith(extension, Qt::CaseInsensitive)) {
+ fn.append(extension);
+ }
+
p->set_filename(fn);
SaveProjectInternal(p);
@@ -927,7 +1023,7 @@ void Core::OpenProjectInternal(const QString &filename)
//connect(plm, &ProjectLoadManager::ProjectLoaded, this, &Core::AddOpenProject);
- CLITaskDialog task_dialog(plm);
+
}
}
diff --git a/app/core.h b/app/core.h
index 07aeccb08..9b3d25728 100644
--- a/app/core.h
+++ b/app/core.h
@@ -66,12 +66,14 @@ public:
*/
static Core* instance();
+ int execute(QCoreApplication *a);
+
/**
* @brief Start Olive Core
*
* Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode).
*/
- bool Start();
+ void Start();
/**
* @brief Stop Olive Core
@@ -405,11 +407,6 @@ private:
*/
void PushRecentlyOpenedProject(const QString &s);
- /**
- * @brief Internal project open
- */
- void OpenProjectInternal(const QString& filename);
-
/**
* @brief Declare custom types/classes for Qt's signal/slot system
*
@@ -507,6 +504,15 @@ private slots:
void ProjectWasModified(bool e);
+ bool StartHeadlessExport();
+
+ void OpenStartupProject();
+
+ /**
+ * @brief Internal project open
+ */
+ void OpenProjectInternal(const QString& filename);
+
};
OLIVE_NAMESPACE_EXIT
diff --git a/app/main.cpp b/app/main.cpp
index e87f1fd34..0026395f0 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -86,23 +86,5 @@ int main(int argc, char *argv[]) {
avfilter_register_all();
#endif
- int exit_code;
-
- // Start core
- if (OLIVE_NAMESPACE::Core::instance()->Start()) {
-
- // Run application loop and receive exit code
- exit_code = a.exec();
-
- } else {
-
- // Core failed to start, exit now
- exit_code = 1;
-
- }
-
- // Clear core memory
- OLIVE_NAMESPACE::Core::instance()->Stop();
-
- return exit_code;
+ return OLIVE_NAMESPACE::Core::instance()->execute(&a);
}
diff --git a/app/node/param.cpp b/app/node/param.cpp
index ff8e72b21..ec1eb8b65 100644
--- a/app/node/param.cpp
+++ b/app/node/param.cpp
@@ -185,6 +185,58 @@ NodeEdgePtr NodeParam::DisconnectForNewOutput(NodeInput *input)
return nullptr;
}
+QString NodeParam::GetPrettyDataTypeName(const NodeParam::DataType &type)
+{
+ switch (type) {
+ case kNone:
+ return tr("None");
+ case kInt:
+ case kCombo:
+ return tr("Integer");
+ case kFloat:
+ return tr("Float");
+ case kRational:
+ return tr("Rational");
+ case kBoolean:
+ return tr("Boolean");
+ case kColor:
+ return tr("Color");
+ case kMatrix:
+ return tr("Matrix");
+ case kText:
+ return tr("Text");
+ case kFont:
+ return tr("Font");
+ case kFile:
+ return tr("File");
+ case kTexture:
+ return tr("Texture");
+ case kSamples:
+ return tr("Samples");
+ case kFootage:
+ return tr("Footage");
+ case kVec2:
+ return tr("Vector 2D");
+ case kVec3:
+ return tr("Vector 3D");
+ case kVec4:
+ return tr("Vector 4D");
+
+ case kDecimal:
+ case kNumber:
+ case kString:
+ case kBuffer:
+ case kVector:
+ case kShaderJob:
+ case kSampleJob:
+ case kGenerateJob:
+ case kAny:
+ break;
+ }
+
+ return tr("Unknown");
+}
+
QByteArray NodeParam::ValueToBytes(const NodeParam::DataType &type, const QVariant &value)
{
switch (type) {
@@ -222,39 +274,6 @@ QByteArray NodeParam::ValueToBytes(const NodeParam::DataType &type, const QVaria
return QByteArray();
}
-NodeParam::DataType NodeParam::StringToDataType(const QString &s)
-{
- QString type_id = s.toLower();
-
- if (type_id == QStringLiteral("float")) {
- return kFloat;
- } else if (type_id == QStringLiteral("int")) {
- return kInt;
- } else if (type_id == QStringLiteral("rational")) {
- return kRational;
- } else if (type_id == QStringLiteral("bool")) {
- return kBoolean;
- } else if (type_id == QStringLiteral("color")) {
- return kColor;
- } else if (type_id == QStringLiteral("matrix")) {
- return kMatrix;
- } else if (type_id == QStringLiteral("text")) {
- return kText;
- } else if (type_id == QStringLiteral("texture")) {
- return kTexture;
- } else if (type_id == QStringLiteral("vec2")) {
- return kVec2;
- } else if (type_id == QStringLiteral("vec3")) {
- return kVec3;
- } else if (type_id == QStringLiteral("vec4")) {
- return kVec4;
- } else if (type_id == QStringLiteral("combo")) {
- return kCombo;
- }
-
- return kAny;
-}
-
template
QByteArray NodeParam::ValueToBytesInternal(const QVariant &v)
{
diff --git a/app/node/param.h b/app/node/param.h
index 40e5a9716..03e667936 100644
--- a/app/node/param.h
+++ b/app/node/param.h
@@ -378,18 +378,13 @@ public:
/**
* @brief Get a human-readable translated name for a certain data type
*/
- static QString GetDefaultDataTypeName(const DataType &type);
+ static QString GetPrettyDataTypeName(const DataType &type);
/**
* @brief Convert a value from a NodeParam into bytes
*/
static QByteArray ValueToBytes(const DataType &type, const QVariant& value);
- /**
- * @brief Convert a string to a data type
- */
- static DataType StringToDataType(const QString& s);
-
signals:
/**
* @brief Signal emitted when an edge is added to this parameter
diff --git a/app/node/value.cpp b/app/node/value.cpp
index d487f0a3d..e2f9913c5 100644
--- a/app/node/value.cpp
+++ b/app/node/value.cpp
@@ -61,26 +61,11 @@ NodeValue::NodeValue(const NodeParam::DataType &type, const QVariant &data, cons
{
}
-const NodeParam::DataType &NodeValue::type() const
-{
- return type_;
-}
-
-const QString &NodeValue::tag() const
-{
- return tag_;
-}
-
bool NodeValue::operator==(const NodeValue &rhs) const
{
return type_ == rhs.type_ && tag_ == rhs.tag_ && data_ == rhs.data_;
}
-const QVariant &NodeValue::data() const
-{
- return data_;
-}
-
QVariant NodeValueTable::Get(const NodeParam::DataType &type, const QString &tag) const
{
return GetWithMeta(type, tag).data();
diff --git a/app/node/value.h b/app/node/value.h
index 387117993..7efb8592a 100644
--- a/app/node/value.h
+++ b/app/node/value.h
@@ -33,9 +33,25 @@ public:
NodeValue();
NodeValue(const NodeParam::DataType& type, const QVariant& data, const Node* from, const QString& tag = QString());
- const NodeParam::DataType& type() const;
- const QVariant& data() const;
- const QString& tag() const;
+ const NodeParam::DataType& type() const
+ {
+ return type_;
+ }
+
+ const QVariant& data() const
+ {
+ return data_;
+ }
+
+ const QString& tag() const
+ {
+ return tag_;
+ }
+
+ const Node* source() const
+ {
+ return from_;
+ }
bool operator==(const NodeValue& rhs) const;
@@ -90,6 +106,16 @@ public:
NodeValueTable Merge() const;
+ using const_iterator = QHash::const_iterator;
+
+ inline QHash::const_iterator begin() const {
+ return tables_.cbegin();
+ }
+
+ inline QHash::const_iterator end() const {
+ return tables_.cend();
+ }
+
private:
QHash tables_;
diff --git a/app/panel/CMakeLists.txt b/app/panel/CMakeLists.txt
index 0c6423398..75c1c7738 100644
--- a/app/panel/CMakeLists.txt
+++ b/app/panel/CMakeLists.txt
@@ -23,6 +23,7 @@ add_subdirectory(pixelsampler)
add_subdirectory(project)
add_subdirectory(scope)
add_subdirectory(sequenceviewer)
+add_subdirectory(table)
add_subdirectory(taskmanager)
add_subdirectory(timebased)
add_subdirectory(timeline)
diff --git a/app/panel/table/CMakeLists.txt b/app/panel/table/CMakeLists.txt
new file mode 100644
index 000000000..f332aa646
--- /dev/null
+++ b/app/panel/table/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2019 Olive Team
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+set(OLIVE_SOURCES
+ ${OLIVE_SOURCES}
+ panel/table/table.h
+ panel/table/table.cpp
+ PARENT_SCOPE
+)
diff --git a/app/panel/table/table.cpp b/app/panel/table/table.cpp
new file mode 100644
index 000000000..7183e9c23
--- /dev/null
+++ b/app/panel/table/table.cpp
@@ -0,0 +1,44 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#include "table.h"
+
+OLIVE_NAMESPACE_ENTER
+
+NodeTablePanel::NodeTablePanel(QWidget* parent) :
+ TimeBasedPanel(QStringLiteral("NodeTablePanel"), parent)
+{
+ view_ = new NodeTableWidget();
+ SetTimeBasedWidget(view_);
+
+ Retranslate();
+}
+
+void NodeTablePanel::SetNodes(const QList &nodes)
+{
+ view_->SetNodes(nodes);
+}
+
+void NodeTablePanel::Retranslate()
+{
+ SetTitle(tr("Table View"));
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/panel/table/table.h b/app/panel/table/table.h
new file mode 100644
index 000000000..0587cf51b
--- /dev/null
+++ b/app/panel/table/table.h
@@ -0,0 +1,45 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#ifndef NODETABLEPANEL_H
+#define NODETABLEPANEL_H
+
+#include "panel/timebased/timebased.h"
+#include "widget/nodetableview/nodetablewidget.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class NodeTablePanel : public TimeBasedPanel
+{
+public:
+ NodeTablePanel(QWidget* parent);
+
+public slots:
+ void SetNodes(const QList& nodes);
+
+private:
+ virtual void Retranslate() override;
+
+ NodeTableWidget* view_;
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // NODETABLEPANEL_H
diff --git a/app/project/item/footage/imagestream.cpp b/app/project/item/footage/imagestream.cpp
index beb5f3b3b..41a9d0d23 100644
--- a/app/project/item/footage/imagestream.cpp
+++ b/app/project/item/footage/imagestream.cpp
@@ -72,26 +72,6 @@ QString ImageStream::description() const
QString::number(height()));
}
-const int &ImageStream::width() const
-{
- return width_;
-}
-
-void ImageStream::set_width(const int &width)
-{
- width_ = width;
-}
-
-const int &ImageStream::height() const
-{
- return height_;
-}
-
-void ImageStream::set_height(const int &height)
-{
- height_ = height;
-}
-
bool ImageStream::premultiplied_alpha() const
{
return premultiplied_alpha_;
diff --git a/app/project/item/footage/imagestream.h b/app/project/item/footage/imagestream.h
index 9e3b6f72b..30fc712a2 100644
--- a/app/project/item/footage/imagestream.h
+++ b/app/project/item/footage/imagestream.h
@@ -21,6 +21,7 @@
#ifndef IMAGESTREAM_H
#define IMAGESTREAM_H
+#include "render/pixelformat.h"
#include "stream.h"
OLIVE_NAMESPACE_ENTER
@@ -36,11 +37,35 @@ public:
virtual QString description() const override;
- const int& width() const;
- void set_width(const int& width);
+ const int& width() const
+ {
+ return width_;
+ }
- const int& height() const;
- void set_height(const int& height);
+ void set_width(const int& width)
+ {
+ width_ = width;
+ }
+
+ const int& height() const
+ {
+ return height_;
+ }
+
+ void set_height(const int& height)
+ {
+ height_ = height;
+ }
+
+ const PixelFormat::Format& format() const
+ {
+ return format_;
+ }
+
+ void set_format(const PixelFormat::Format& format)
+ {
+ format_ = format;
+ }
bool premultiplied_alpha() const;
void set_premultiplied_alpha(bool e);
@@ -63,6 +88,8 @@ private:
bool premultiplied_alpha_;
QString colorspace_;
+ PixelFormat::Format format_;
+
private slots:
void ColorConfigChanged();
diff --git a/app/render/backend/renderworker.cpp b/app/render/backend/renderworker.cpp
index 7142eae07..1ff861658 100644
--- a/app/render/backend/renderworker.cpp
+++ b/app/render/backend/renderworker.cpp
@@ -244,14 +244,16 @@ QVariant RenderWorker::GetCachedFrame(const Node* node, const rational& time)
if (QFileInfo::exists(fn)) {
FramePtr f = FrameHashCache::LoadCacheFrame(hash);
- // The cached frame won't load with the correct divider by default, so we enforce it here
- f->set_video_params(VideoParams(f->width() * video_params_.divider(),
- f->height() * video_params_.divider(),
- f->video_params().time_base(),
- f->video_params().format(),
- video_params_.divider()));
+ if (f) {
+ // The cached frame won't load with the correct divider by default, so we enforce it here
+ f->set_video_params(VideoParams(f->width() * video_params_.divider(),
+ f->height() * video_params_.divider(),
+ f->video_params().time_base(),
+ f->video_params().format(),
+ video_params_.divider()));
- return CachedFrameToTexture(f);
+ return CachedFrameToTexture(f);
+ }
}
}
@@ -316,9 +318,7 @@ QVariant RenderWorker::ProcessVideoFootage(StreamPtr stream, const rational &inp
// Return a texture from the derived class
value = FootageFrameToTexture(stream, frame);
- if (value.isNull()) {
- qDebug() << "Texture from derivative was blank";
- } else {
+ if (!value.isNull()) {
// Put this into the image cache instead
still_image_cache_.insert(stream.get(), {value,
colorspace_match,
@@ -326,8 +326,6 @@ QVariant RenderWorker::ProcessVideoFootage(StreamPtr stream, const rational &inp
video_params_.divider(),
time_match});
}
- } else {
- qDebug() << "Frame from decoder was blank";
}
}
diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp
index 0bde0581a..58f18a957 100644
--- a/app/render/framehashcache.cpp
+++ b/app/render/framehashcache.cpp
@@ -206,34 +206,51 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn)
FramePtr frame = nullptr;
if (!fn.isEmpty() && QFileInfo::exists(fn)) {
- auto input = OIIO::ImageInput::open(fn.toStdString());
+ Imf::InputFile file(fn.toUtf8(), 0);
- if (input) {
-
- PixelFormat::Format image_format = PixelFormat::OIIOFormatToOliveFormat(input->spec().format,
- input->spec().nchannels == kRGBAChannels);
-
- frame = Frame::Create();
- frame->set_video_params(VideoParams(input->spec().width,
- input->spec().height,
- image_format));
-
- frame->allocate();
-
- input->read_image(input->spec().format,
- frame->data(),
- OIIO::AutoStride,
- frame->linesize_bytes());
-
- input->close();
-
-#if OIIO_VERSION < 10903
- OIIO::ImageInput::destroy(input);
-#endif
+ Imath::Box2i dw = file.header().dataWindow();
+ Imf::PixelType pix_type = file.header().channels().begin().channel().type;
+ int width = dw.max.x - dw.min.x + 1;
+ int height = dw.max.y - dw.min.y + 1;
+ bool has_alpha = file.header().channels().findChannel("A");
+ PixelFormat::Format image_format;
+ if (pix_type == Imf::HALF) {
+ if (has_alpha) {
+ image_format = PixelFormat::PIX_FMT_RGBA16F;
+ } else {
+ image_format = PixelFormat::PIX_FMT_RGB16F;
+ }
} else {
- qWarning() << "OIIO Error:" << OIIO::geterror().c_str();
+ if (has_alpha) {
+ image_format = PixelFormat::PIX_FMT_RGBA32F;
+ } else {
+ image_format = PixelFormat::PIX_FMT_RGB32F;
+ }
}
+
+ frame = Frame::Create();
+ frame->set_video_params(VideoParams(width,
+ height,
+ image_format));
+
+ frame->allocate();
+
+ int bpc = PixelFormat::BytesPerChannel(image_format);
+
+ size_t xs = PixelFormat::ChannelCount(image_format) * bpc;
+ size_t ys = frame->linesize_bytes();
+
+ Imf::FrameBuffer framebuffer;
+ framebuffer.insert("R", Imf::Slice(pix_type, frame->data(), xs, ys));
+ framebuffer.insert("G", Imf::Slice(pix_type, frame->data() + bpc, xs, ys));
+ framebuffer.insert("B", Imf::Slice(pix_type, frame->data() + 2*bpc, xs, ys));
+ if (has_alpha) {
+ framebuffer.insert("A", Imf::Slice(pix_type, frame->data() + 3*bpc, xs, ys));
+ }
+
+ file.setFrameBuffer(framebuffer);
+ file.readPixels(dw.min.y, dw.max.y);
}
return frame;
diff --git a/app/render/videoparams.h b/app/render/videoparams.h
index b108874d5..56534ed3c 100644
--- a/app/render/videoparams.h
+++ b/app/render/videoparams.h
@@ -91,4 +91,6 @@ private:
OLIVE_NAMESPACE_EXIT
+Q_DECLARE_METATYPE(OLIVE_NAMESPACE::VideoParams)
+
#endif // VIDEOPARAMS_H
diff --git a/app/task/export/exportparams.cpp b/app/task/export/exportparams.cpp
index a603735ee..299c1fca3 100644
--- a/app/task/export/exportparams.cpp
+++ b/app/task/export/exportparams.cpp
@@ -100,4 +100,26 @@ QMatrix4x4 ExportParams::GenerateMatrix(ExportParams::VideoScalingMethod method,
return preview_matrix;
}
+void ExportParams::Save(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement(QStringLiteral("export"));
+
+ writer->writeTextElement(QStringLiteral("encoder"), encoder_id_);
+
+ writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
+
+ writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_));
+
+ writer->writeTextElement(QStringLiteral("customrangein"), custom_range_.in().toString());
+
+ writer->writeTextElement(QStringLiteral("customrangeout"), custom_range_.out().toString());
+
+ // FIXME: Change this when color chains are implemented
+ writer->writeTextElement(QStringLiteral("color"), color_transform_.output());
+
+ EncodingParams::Save(writer);
+
+ writer->writeEndElement(); // export
+}
+
OLIVE_NAMESPACE_EXIT
diff --git a/app/task/export/exportparams.h b/app/task/export/exportparams.h
index a74ea3c5d..ed6106d67 100644
--- a/app/task/export/exportparams.h
+++ b/app/task/export/exportparams.h
@@ -56,6 +56,8 @@ public:
int source_width, int source_height,
int dest_width, int dest_height);
+ virtual void Save(QXmlStreamWriter* writer) const override;
+
private:
QString encoder_id_;
diff --git a/app/task/project/load/load.cpp b/app/task/project/load/load.cpp
index 20d05ce7d..932a94c74 100644
--- a/app/task/project/load/load.cpp
+++ b/app/task/project/load/load.cpp
@@ -70,6 +70,8 @@ bool ProjectLoadTask::Run()
project_file.close();
+ emit ProgressChanged(1);
+
if (reader.hasError()) {
SetError(reader.errorString());
return false;
diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt
index dc068e82b..67c20349e 100644
--- a/app/widget/CMakeLists.txt
+++ b/app/widget/CMakeLists.txt
@@ -31,6 +31,7 @@ add_subdirectory(nodecombobox)
add_subdirectory(nodecopypaste)
add_subdirectory(nodeview)
add_subdirectory(nodeparamview)
+add_subdirectory(nodetableview)
add_subdirectory(panel)
add_subdirectory(pixelsampler)
add_subdirectory(playbackcontrols)
diff --git a/app/widget/clickablelabel/clickablelabel.cpp b/app/widget/clickablelabel/clickablelabel.cpp
index 0ab4dd631..a267a1019 100644
--- a/app/widget/clickablelabel/clickablelabel.cpp
+++ b/app/widget/clickablelabel/clickablelabel.cpp
@@ -20,6 +20,8 @@
#include "clickablelabel.h"
+#include
+
OLIVE_NAMESPACE_ENTER
ClickableLabel::ClickableLabel(const QString &text, QWidget *parent) :
@@ -32,16 +34,18 @@ ClickableLabel::ClickableLabel(QWidget *parent) :
{
}
-void ClickableLabel::mouseReleaseEvent(QMouseEvent *)
+void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
{
- if (underMouse()) {
+ if (event->button() == Qt::LeftButton && underMouse()) {
emit MouseClicked();
}
}
-void ClickableLabel::mouseDoubleClickEvent(QMouseEvent *)
+void ClickableLabel::mouseDoubleClickEvent(QMouseEvent *event)
{
- emit MouseDoubleClicked();
+ if (event->button() == Qt::LeftButton) {
+ emit MouseDoubleClicked();
+ }
}
OLIVE_NAMESPACE_EXIT
diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp
index 612b14f81..43507cac6 100644
--- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp
+++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp
@@ -23,7 +23,10 @@
#include
#include "common/qtutils.h"
+#include "core.h"
#include "node/node.h"
+#include "widget/menu/menu.h"
+#include "widget/nodeview/nodeviewundo.h"
OLIVE_NAMESPACE_ENTER
@@ -39,7 +42,9 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidg
connected_to_lbl_ = new ClickableLabel();
connected_to_lbl_->setCursor(Qt::PointingHandCursor);
+ connected_to_lbl_->setContextMenuPolicy(Qt::CustomContextMenu);
connect(connected_to_lbl_, &ClickableLabel::MouseClicked, this, &NodeParamViewConnectedLabel::ConnectionClicked);
+ connect(connected_to_lbl_, &ClickableLabel::customContextMenuRequested, this, &NodeParamViewConnectedLabel::ShowLabelContextMenu);
layout->addWidget(connected_to_lbl_);
layout->addStretch();
@@ -69,4 +74,16 @@ void NodeParamViewConnectedLabel::UpdateConnected()
connected_to_lbl_->setText(connection_str);
}
+void NodeParamViewConnectedLabel::ShowLabelContextMenu()
+{
+ Menu m(this);
+
+ QAction* disconnect_action = m.addAction(tr("Disconnect"));
+ connect(disconnect_action, &QAction::triggered, this, [this](){
+ Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(input_->get_connected_output(), input_));
+ });
+
+ m.exec(QCursor::pos());
+}
+
OLIVE_NAMESPACE_EXIT
diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h
index 161bf2a0d..04103cae1 100644
--- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h
+++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h
@@ -37,6 +37,8 @@ signals:
private slots:
void UpdateConnected();
+ void ShowLabelContextMenu();
+
private:
ClickableLabel* connected_to_lbl_;
diff --git a/app/widget/nodetableview/CMakeLists.txt b/app/widget/nodetableview/CMakeLists.txt
new file mode 100644
index 000000000..f12dff040
--- /dev/null
+++ b/app/widget/nodetableview/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2019 Olive Team
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+set(OLIVE_SOURCES
+ ${OLIVE_SOURCES}
+ widget/nodetableview/nodetabletraverser.h
+ widget/nodetableview/nodetabletraverser.cpp
+ widget/nodetableview/nodetableview.h
+ widget/nodetableview/nodetableview.cpp
+ widget/nodetableview/nodetablewidget.h
+ widget/nodetableview/nodetablewidget.cpp
+ PARENT_SCOPE
+)
diff --git a/app/widget/nodetableview/nodetabletraverser.cpp b/app/widget/nodetableview/nodetabletraverser.cpp
new file mode 100644
index 000000000..1ca0e1080
--- /dev/null
+++ b/app/widget/nodetableview/nodetabletraverser.cpp
@@ -0,0 +1,44 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#include "nodetabletraverser.h"
+
+OLIVE_NAMESPACE_ENTER
+
+QVariant NodeTableTraverser::ProcessVideoFootage(StreamPtr stream, const rational &input_time)
+{
+ ImageStreamPtr video_stream = std::static_pointer_cast(stream);
+
+ return QVariant::fromValue(VideoParams(video_stream->width(),
+ video_stream->height(),
+ video_stream->timebase(),
+ video_stream->format()));
+}
+
+QVariant NodeTableTraverser::ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time)
+{
+ AudioStreamPtr audio_stream = std::static_pointer_cast(stream);
+
+ return QVariant::fromValue(AudioParams(audio_stream->sample_rate(),
+ audio_stream->channel_layout(),
+ SampleFormat::kInternalFormat));
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/widget/nodetableview/nodetabletraverser.h b/app/widget/nodetableview/nodetabletraverser.h
new file mode 100644
index 000000000..20dae8e2e
--- /dev/null
+++ b/app/widget/nodetableview/nodetabletraverser.h
@@ -0,0 +1,42 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#ifndef NODETABLETRAVERSER_H
+#define NODETABLETRAVERSER_H
+
+#include "node/traverser.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class NodeTableTraverser : public NodeTraverser
+{
+public:
+ NodeTableTraverser() = default;
+
+protected:
+ virtual QVariant ProcessVideoFootage(StreamPtr stream, const rational &input_time);
+
+ virtual QVariant ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time);
+
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // NODETABLETRAVERSER_H
diff --git a/app/widget/nodetableview/nodetableview.cpp b/app/widget/nodetableview/nodetableview.cpp
new file mode 100644
index 000000000..8e9f5aab0
--- /dev/null
+++ b/app/widget/nodetableview/nodetableview.cpp
@@ -0,0 +1,113 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#include "nodetableview.h"
+
+#include
+
+#include "node/param.h"
+#include "nodetabletraverser.h"
+
+OLIVE_NAMESPACE_ENTER
+
+NodeTableView::NodeTableView(QWidget* parent) :
+ QTreeWidget(parent)
+{
+ setColumnCount(3);
+ setHeaderLabels({tr("Type"), tr("Value"), tr("Source")});
+}
+
+void NodeTableView::SetNode(Node *n, const rational &time)
+{
+ clear();
+
+ NodeTableTraverser traverser;
+ NodeValueDatabase db = traverser.GenerateDatabase(n, TimeRange(time, time));
+
+ NodeValueDatabase::const_iterator i;
+
+ for (i=db.begin(); i!=db.end(); i++) {
+ const NodeValueTable& table = i.value();
+
+ NodeInput* input = n->GetInputWithID(i.key());
+ if (!input) {
+ // Filters out table entries that aren't inputs (like "global")
+ continue;
+ }
+
+ QTreeWidgetItem* top_item = new QTreeWidgetItem();
+ top_item->setText(0, input->name());
+ top_item->setFirstColumnSpanned(true);
+ this->addTopLevelItem(top_item);
+
+ for (int j=table.Count()-1; j>=0; j--) {
+ const NodeValue& value = table.at(j);
+
+ QString value_str = NodeInput::ValueToString(value.type(), value.data());
+
+ QString source_name;
+ if (value.source()) {
+ source_name = value.source()->Name();
+ } else {
+ source_name = tr("(unknown)");
+ }
+
+ QTreeWidgetItem* sub_item = new QTreeWidgetItem();
+ sub_item->setText(0, NodeParam::GetPrettyDataTypeName(value.type()));
+ sub_item->setText(1, value_str);
+ sub_item->setText(2, source_name);
+ top_item->addChild(sub_item);
+
+ // Special cases
+ if (value.type() == NodeParam::kTexture) {
+ // NodeTableTraverser converts footage to VideoParams
+ QTreeWidgetItem* red_channel = new QTreeWidgetItem();
+ red_channel->setText(0, tr("Red"));
+ sub_item->addChild(red_channel);
+
+ QTreeWidgetItem* green_channel = new QTreeWidgetItem();
+ green_channel->setText(0, tr("Green"));
+ sub_item->addChild(green_channel);
+
+ QTreeWidgetItem* blue_channel = new QTreeWidgetItem();
+ blue_channel->setText(0, tr("Blue"));
+ sub_item->addChild(blue_channel);
+
+ if (PixelFormat::FormatHasAlphaChannel(value.data().value().format())) {
+ QTreeWidgetItem* alpha_channel = new QTreeWidgetItem();
+ alpha_channel->setText(0, tr("Alpha"));
+ sub_item->addChild(alpha_channel);
+ }
+ }
+ }
+ }
+}
+
+void NodeTableView::SetMultipleNodeMessage()
+{
+ this->clear();
+
+ QTreeWidgetItem* item = new QTreeWidgetItem();
+ item->setText(0, tr("Multiple nodes selected"));
+ item->setFirstColumnSpanned(true);
+ this->addTopLevelItem(item);
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/widget/nodetableview/nodetableview.h b/app/widget/nodetableview/nodetableview.h
new file mode 100644
index 000000000..e453903b7
--- /dev/null
+++ b/app/widget/nodetableview/nodetableview.h
@@ -0,0 +1,43 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#ifndef NODETABLEVIEW_H
+#define NODETABLEVIEW_H
+
+#include
+
+#include "node/node.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class NodeTableView : public QTreeWidget
+{
+public:
+ NodeTableView(QWidget* parent = nullptr);
+
+ void SetNode(Node* n, const rational& time);
+
+ void SetMultipleNodeMessage();
+
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // NODETABLEVIEW_H
diff --git a/app/widget/nodetableview/nodetablewidget.cpp b/app/widget/nodetableview/nodetablewidget.cpp
new file mode 100644
index 000000000..20db058c2
--- /dev/null
+++ b/app/widget/nodetableview/nodetablewidget.cpp
@@ -0,0 +1,49 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#include "nodetablewidget.h"
+
+#include
+
+OLIVE_NAMESPACE_ENTER
+
+NodeTableWidget::NodeTableWidget(QWidget* parent) :
+ TimeBasedWidget(parent)
+{
+ QVBoxLayout* layout = new QVBoxLayout(this);
+ layout->setSpacing(0);
+ layout->setMargin(0);
+
+ view_ = new NodeTableView();
+ layout->addWidget(view_);
+}
+
+void NodeTableWidget::SetNodes(const QList &nodes)
+{
+ if (nodes.isEmpty()) {
+ view_->clear();
+ } else if (nodes.size() == 1) {
+ view_->SetNode(nodes.first(), rational());
+ } else {
+ view_->SetMultipleNodeMessage();
+ }
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/widget/nodetableview/nodetablewidget.h b/app/widget/nodetableview/nodetablewidget.h
new file mode 100644
index 000000000..ae0c82cc4
--- /dev/null
+++ b/app/widget/nodetableview/nodetablewidget.h
@@ -0,0 +1,43 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#ifndef NODETABLEWIDGET_H
+#define NODETABLEWIDGET_H
+
+#include "nodetableview.h"
+#include "widget/timebased/timebased.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class NodeTableWidget : public TimeBasedWidget
+{
+public:
+ NodeTableWidget(QWidget* parent = nullptr);
+
+ void SetNodes(const QList& nodes);
+
+private:
+ NodeTableView* view_;
+
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // NODETABLEWIDGET_H
diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp
index c011fa558..97f186cca 100644
--- a/app/widget/timelinewidget/timelinewidget.cpp
+++ b/app/widget/timelinewidget/timelinewidget.cpp
@@ -1241,6 +1241,13 @@ void TimelineWidget::UpdateViewTimebases()
}
}
+void TimelineWidget::SetViewBeamCursor(const TimelineCoordinate &coord)
+{
+ foreach (TimelineAndTrackView* tview, views_) {
+ tview->view()->SetBeamCursor(coord);
+ }
+}
+
void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected)
{
TimelineViewBlockItem* link_item;
diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h
index d8a6c0dc5..fad1da588 100644
--- a/app/widget/timelinewidget/timelinewidget.h
+++ b/app/widget/timelinewidget/timelinewidget.h
@@ -209,6 +209,15 @@ private:
};
+ class BeamTool : public Tool
+ {
+ public:
+ BeamTool(TimelineWidget *parent);
+
+ virtual void HoverMove(TimelineViewMouseEvent *event) override;
+
+ };
+
class PointerTool : public Tool
{
public:
@@ -327,7 +336,7 @@ private:
};
- class EditTool : public Tool
+ class EditTool : public BeamTool
{
public:
EditTool(TimelineWidget* parent);
@@ -337,7 +346,7 @@ private:
virtual void MouseRelease(TimelineViewMouseEvent *event) override;
};
- class RazorTool : public Tool
+ class RazorTool : public BeamTool
{
public:
RazorTool(TimelineWidget* parent);
@@ -498,6 +507,8 @@ private:
void UpdateViewTimebases();
+ void SetViewBeamCursor(const TimelineCoordinate& coord);
+
private slots:
void ViewMousePressed(TimelineViewMouseEvent* event);
void ViewMouseMoved(TimelineViewMouseEvent* event);
diff --git a/app/widget/timelinewidget/tool/CMakeLists.txt b/app/widget/timelinewidget/tool/CMakeLists.txt
index 53b862a84..f14d4a8de 100644
--- a/app/widget/timelinewidget/tool/CMakeLists.txt
+++ b/app/widget/timelinewidget/tool/CMakeLists.txt
@@ -17,6 +17,7 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/timelinewidget/tool/add.cpp
+ widget/timelinewidget/tool/beam.cpp
widget/timelinewidget/tool/edit.cpp
widget/timelinewidget/tool/import.cpp
widget/timelinewidget/tool/pointer.cpp
diff --git a/app/widget/timelinewidget/tool/beam.cpp b/app/widget/timelinewidget/tool/beam.cpp
new file mode 100644
index 000000000..a04d14b9e
--- /dev/null
+++ b/app/widget/timelinewidget/tool/beam.cpp
@@ -0,0 +1,35 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#include "widget/timelinewidget/timelinewidget.h"
+
+OLIVE_NAMESPACE_ENTER
+
+TimelineWidget::BeamTool::BeamTool(TimelineWidget *parent) :
+ Tool(parent)
+{
+}
+
+void TimelineWidget::BeamTool::HoverMove(TimelineViewMouseEvent *event)
+{
+ parent()->SetViewBeamCursor(event->GetCoordinates(true));
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/widget/timelinewidget/tool/edit.cpp b/app/widget/timelinewidget/tool/edit.cpp
index d7940f0c4..cb804bebc 100644
--- a/app/widget/timelinewidget/tool/edit.cpp
+++ b/app/widget/timelinewidget/tool/edit.cpp
@@ -23,7 +23,7 @@
OLIVE_NAMESPACE_ENTER
TimelineWidget::EditTool::EditTool(TimelineWidget* parent) :
- Tool(parent)
+ BeamTool(parent)
{
}
diff --git a/app/widget/timelinewidget/tool/razor.cpp b/app/widget/timelinewidget/tool/razor.cpp
index d89e9125d..298a36b3b 100644
--- a/app/widget/timelinewidget/tool/razor.cpp
+++ b/app/widget/timelinewidget/tool/razor.cpp
@@ -23,7 +23,7 @@
OLIVE_NAMESPACE_ENTER
TimelineWidget::RazorTool::RazorTool(TimelineWidget* parent) :
- Tool(parent)
+ BeamTool(parent)
{
}
diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp
index 91409bf94..2d3cc4511 100644
--- a/app/widget/timelinewidget/view/timelineview.cpp
+++ b/app/widget/timelinewidget/view/timelineview.cpp
@@ -141,21 +141,21 @@ void TimelineView::wheelEvent(QWheelEvent *event)
}
QWheelEvent e(
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- event->position(),
- event->globalPosition(),
-#else
- event->pos(),
- event->globalPos(),
-#endif
- event->pixelDelta(),
- angle_delta,
- event->buttons(),
- event->modifiers(),
- event->phase(),
- event->inverted(),
- event->source()
- );
+ #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ event->position(),
+ event->globalPosition(),
+ #else
+ event->pos(),
+ event->globalPos(),
+ #endif
+ event->pixelDelta(),
+ angle_delta,
+ event->buttons(),
+ event->modifiers(),
+ event->phase(),
+ event->inverted(),
+ event->source()
+ );
#else
@@ -166,15 +166,15 @@ void TimelineView::wheelEvent(QWheelEvent *event)
}
QWheelEvent e(
- event->pos(),
- event->globalPos(),
- event->pixelDelta(),
- event->angleDelta(),
- event->delta(),
- orientation,
- event->buttons(),
- event->modifiers()
- );
+ event->pos(),
+ event->globalPos(),
+ event->pixelDelta(),
+ event->angleDelta(),
+ event->delta(),
+ orientation,
+ event->buttons(),
+ event->modifiers()
+ );
#endif
QGraphicsView::wheelEvent(&e);
@@ -244,6 +244,26 @@ void TimelineView::drawBackground(QPainter *painter, const QRectF &rect)
}
}
+void TimelineView::drawForeground(QPainter *painter, const QRectF &rect)
+{
+ TimelineViewBase::drawForeground(painter, rect);
+
+ if (show_beam_cursor_
+ && connected_track_list_
+ && cursor_coord_.GetTrack().type() == connected_track_list_->type()
+ && cursor_coord_.GetTrack().index() < connected_track_list_->GetTrackCount()) {
+ painter->setPen(Qt::gray);
+
+ double cursor_x = TimeToScene(cursor_coord_.GetFrame());
+ int track_index = cursor_coord_.GetTrack().index();
+
+ painter->drawLine(cursor_x,
+ GetTrackY(track_index),
+ cursor_x,
+ GetTrackHeight(track_index));
+ }
+}
+
void TimelineView::ToolChangedEvent(Tool::Item tool)
{
switch (tool) {
@@ -261,6 +281,12 @@ void TimelineView::ToolChangedEvent(Tool::Item tool)
default:
unsetCursor();
}
+
+ // Hide/show cursor if necessary
+ if (show_beam_cursor_) {
+ show_beam_cursor_ = false;
+ viewport()->update();
+ }
}
void TimelineView::SceneRectUpdateEvent(QRectF &rect)
@@ -399,6 +425,20 @@ void TimelineView::ConnectTrackList(TrackList *list)
}
}
+void TimelineView::SetBeamCursor(const TimelineCoordinate &coord)
+{
+ bool update_required = true;/*(coord.GetTrack().type() == connected_track_list_->type()
+ || cursor_coord_.GetTrack().type() == connected_track_list_->type()
+ || !show_beam_cursor_);*/
+
+ show_beam_cursor_ = true;
+ cursor_coord_ = coord;
+
+ if (update_required) {
+ viewport()->update();
+ }
+}
+
int TimelineView::SceneToTrack(double y)
{
int track = -1;
diff --git a/app/widget/timelinewidget/view/timelineview.h b/app/widget/timelinewidget/view/timelineview.h
index 53daceca0..4bacad401 100644
--- a/app/widget/timelinewidget/view/timelineview.h
+++ b/app/widget/timelinewidget/view/timelineview.h
@@ -61,6 +61,8 @@ public:
void ConnectTrackList(TrackList* list);
+ void SetBeamCursor(const TimelineCoordinate& coord);
+
signals:
void MousePressed(TimelineViewMouseEvent* event);
void MouseMoved(TimelineViewMouseEvent* event);
@@ -88,6 +90,7 @@ protected:
virtual void dropEvent(QDropEvent *event) override;
virtual void drawBackground(QPainter *painter, const QRectF &rect) override;
+ virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
virtual void ToolChangedEvent(Tool::Item tool) override;
@@ -111,6 +114,10 @@ private:
void UpdatePlayheadRect();
+ bool show_beam_cursor_;
+
+ TimelineCoordinate cursor_coord_;
+
TrackList* connected_track_list_;
};
diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp
index c3eaf7e75..a917143f6 100644
--- a/app/window/mainwindow/mainwindow.cpp
+++ b/app/window/mainwindow/mainwindow.cpp
@@ -72,6 +72,7 @@ MainWindow::MainWindow(QWidget *parent) :
node_panel_ = PanelManager::instance()->CreatePanel(this);
footage_viewer_panel_ = PanelManager::instance()->CreatePanel(this);
param_panel_ = PanelManager::instance()->CreatePanel(this);
+ table_panel_ = PanelManager::instance()->CreatePanel(this);
sequence_viewer_panel_ = PanelManager::instance()->CreatePanel(this);
pixel_sampler_panel_ = PanelManager::instance()->CreatePanel(this);
AppendProjectPanel();
@@ -82,6 +83,7 @@ MainWindow::MainWindow(QWidget *parent) :
// Make connections to sequence viewer
connect(node_panel_, &NodePanel::SelectionChanged, param_panel_, &ParamPanel::SetNodes);
+ connect(node_panel_, &NodePanel::SelectionChanged, table_panel_, &NodeTablePanel::SetNodes);
connect(param_panel_, &ParamPanel::RequestSelectNode, node_panel_, &NodePanel::Select);
connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp);
connect(param_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp);
@@ -602,6 +604,10 @@ void MainWindow::SetDefaultLayout()
tabifyDockWidget(footage_viewer_panel_, param_panel_);
footage_viewer_panel_->raise();
+ table_panel_->hide();
+ table_panel_->setFloating(true);
+ addDockWidget(Qt::TopDockWidgetArea, table_panel_);
+
sequence_viewer_panel_->show();
addDockWidget(Qt::TopDockWidgetArea, sequence_viewer_panel_);
diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h
index f01f0524a..c9f349051 100644
--- a/app/window/mainwindow/mainwindow.h
+++ b/app/window/mainwindow/mainwindow.h
@@ -30,6 +30,7 @@
#include "panel/param/param.h"
#include "panel/project/project.h"
#include "panel/scope/scope.h"
+#include "panel/table/table.h"
#include "panel/taskmanager/taskmanager.h"
#include "panel/timeline/timeline.h"
#include "panel/tool/tool.h"
@@ -147,6 +148,7 @@ private:
QList curve_panels_;
PixelSamplerPanel* pixel_sampler_panel_;
QList scope_panels_;
+ NodeTablePanel* table_panel_;
#ifdef Q_OS_WINDOWS
unsigned int taskbar_btn_id_;