Merge branch 'master' of https://github.com/olive-editor/olive
This commit is contained in:
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "cliexportmanager.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
CLIExportManager::CLIExportManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#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
|
||||
@@ -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;i<bar_prog;i++) {
|
||||
@@ -90,10 +91,10 @@ void CLIProgressDialog::Update()
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
std::cout << progress_ << "% " << std::flush;
|
||||
std::cout << qRound(progress_ * 100.0) << "% " << std::endl << std::flush;
|
||||
}
|
||||
|
||||
void CLIProgressDialog::SetProgress(int p)
|
||||
void CLIProgressDialog::SetProgress(double p)
|
||||
{
|
||||
if (progress_ != p) {
|
||||
progress_ = p;
|
||||
|
||||
@@ -34,14 +34,14 @@ public:
|
||||
CLIProgressDialog(const QString &title, QObject* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void SetProgress(int p);
|
||||
void SetProgress(double p);
|
||||
|
||||
private:
|
||||
void Update();
|
||||
|
||||
QString title_;
|
||||
|
||||
int progress_;
|
||||
double progress_;
|
||||
|
||||
bool drawn_;
|
||||
|
||||
|
||||
@@ -23,9 +23,15 @@
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) :
|
||||
CLIProgressDialog(task->GetTitle(), 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<QString, QString>::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)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <QString>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#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_;
|
||||
|
||||
|
||||
@@ -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<AVPixelFormat>(avstream->codecpar->format))));
|
||||
video_stream->set_frame_rate(av_guess_frame_rate(fmt_ctx, avstream, nullptr));
|
||||
video_stream->set_start_time(avstream->start_time);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
+151
-55
@@ -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<ItemPtr> 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;i<items.size();i++) {
|
||||
std::cout << "[" << i << "] " << items.at(i)->name().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<Sequence>(items.at(sequence_index));
|
||||
} else {
|
||||
sequence = std::static_pointer_cast<Sequence>(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<rational>();
|
||||
@@ -559,6 +649,7 @@ void Core::DeclareTypesForQt()
|
||||
qRegisterMetaType<OLIVE_NAMESPACE::SampleJob>();
|
||||
qRegisterMetaType<OLIVE_NAMESPACE::ShaderJob>();
|
||||
qRegisterMetaType<OLIVE_NAMESPACE::GenerateJob>();
|
||||
qRegisterMetaType<OLIVE_NAMESPACE::VideoParams>();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+12
-6
@@ -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
|
||||
|
||||
+1
-19
@@ -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);
|
||||
}
|
||||
|
||||
+52
-33
@@ -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<typename T>
|
||||
QByteArray NodeParam::ValueToBytesInternal(const QVariant &v)
|
||||
{
|
||||
|
||||
+1
-6
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
+29
-3
@@ -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<QString, NodeValueTable>::const_iterator;
|
||||
|
||||
inline QHash<QString, NodeValueTable>::const_iterator begin() const {
|
||||
return tables_.cbegin();
|
||||
}
|
||||
|
||||
inline QHash<QString, NodeValueTable>::const_iterator end() const {
|
||||
return tables_.cend();
|
||||
}
|
||||
|
||||
private:
|
||||
QHash<QString, NodeValueTable> tables_;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
panel/table/table.h
|
||||
panel/table/table.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#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<Node *> &nodes)
|
||||
{
|
||||
view_->SetNodes(nodes);
|
||||
}
|
||||
|
||||
void NodeTablePanel::Retranslate()
|
||||
{
|
||||
SetTitle(tr("Table View"));
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#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<Node*>& nodes);
|
||||
|
||||
private:
|
||||
virtual void Retranslate() override;
|
||||
|
||||
NodeTableWidget* view_;
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // NODETABLEPANEL_H
|
||||
@@ -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_;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -91,4 +91,6 @@ private:
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::VideoParams)
|
||||
|
||||
#endif // VIDEOPARAMS_H
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ bool ProjectLoadTask::Run()
|
||||
|
||||
project_file.close();
|
||||
|
||||
emit ProgressChanged(1);
|
||||
|
||||
if (reader.hasError()) {
|
||||
SetError(reader.errorString());
|
||||
return false;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "clickablelabel.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
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
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#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
|
||||
|
||||
@@ -37,6 +37,8 @@ signals:
|
||||
private slots:
|
||||
void UpdateConnected();
|
||||
|
||||
void ShowLabelContextMenu();
|
||||
|
||||
private:
|
||||
ClickableLabel* connected_to_lbl_;
|
||||
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
)
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "nodetabletraverser.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
QVariant NodeTableTraverser::ProcessVideoFootage(StreamPtr stream, const rational &input_time)
|
||||
{
|
||||
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(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<AudioStream>(stream);
|
||||
|
||||
return QVariant::fromValue(AudioParams(audio_stream->sample_rate(),
|
||||
audio_stream->channel_layout(),
|
||||
SampleFormat::kInternalFormat));
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#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
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "nodetableview.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
|
||||
#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<VideoParams>().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
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef NODETABLEVIEW_H
|
||||
#define NODETABLEVIEW_H
|
||||
|
||||
#include <QTreeWidget>
|
||||
|
||||
#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
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "nodetablewidget.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
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<Node *> &nodes)
|
||||
{
|
||||
if (nodes.isEmpty()) {
|
||||
view_->clear();
|
||||
} else if (nodes.size() == 1) {
|
||||
view_->SetNode(nodes.first(), rational());
|
||||
} else {
|
||||
view_->SetMultipleNodeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#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<Node*>& nodes);
|
||||
|
||||
private:
|
||||
NodeTableView* view_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // NODETABLEWIDGET_H
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#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
|
||||
@@ -23,7 +23,7 @@
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
TimelineWidget::EditTool::EditTool(TimelineWidget* parent) :
|
||||
Tool(parent)
|
||||
BeamTool(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
TimelineWidget::RazorTool::RazorTool(TimelineWidget* parent) :
|
||||
Tool(parent)
|
||||
BeamTool(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
@@ -72,6 +72,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
node_panel_ = PanelManager::instance()->CreatePanel<NodePanel>(this);
|
||||
footage_viewer_panel_ = PanelManager::instance()->CreatePanel<FootageViewerPanel>(this);
|
||||
param_panel_ = PanelManager::instance()->CreatePanel<ParamPanel>(this);
|
||||
table_panel_ = PanelManager::instance()->CreatePanel<NodeTablePanel>(this);
|
||||
sequence_viewer_panel_ = PanelManager::instance()->CreatePanel<SequenceViewerPanel>(this);
|
||||
pixel_sampler_panel_ = PanelManager::instance()->CreatePanel<PixelSamplerPanel>(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_);
|
||||
|
||||
|
||||
@@ -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<CurvePanel*> curve_panels_;
|
||||
PixelSamplerPanel* pixel_sampler_panel_;
|
||||
QList<ScopePanel*> scope_panels_;
|
||||
NodeTablePanel* table_panel_;
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
unsigned int taskbar_btn_id_;
|
||||
|
||||
Reference in New Issue
Block a user