cleaned source tree

This commit is contained in:
itsmattkc
2019-06-20 11:36:43 -07:00
parent 1fec3fb282
commit df93b9f242
478 changed files with 25 additions and 114765 deletions

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Before

Width:  |  Height:  |  Size: 293 KiB

After

Width:  |  Height:  |  Size: 293 KiB

+2 -2
View File
@@ -1,5 +1,5 @@
#ifndef PROJECT_H
#define PROJECT_H
#ifndef PROJECT_PANEL_H
#define PROJECT_PANEL_H
#include "widget/panel/panel.h"
#include "widget/projectexplorer/projectexplorer.h"
+6
View File
@@ -14,3 +14,9 @@
# 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}
project/project.h
project/project.cpp
PARENT_SCOPE
)
+6
View File
@@ -0,0 +1,6 @@
#include "project.h"
Project::Project()
{
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef PROJECT_H
#define PROJECT_H
class Project
{
public:
Project();
};
#endif // PROJECT_H
-5
View File
@@ -1,5 +0,0 @@
olive-editor (201902160026-0ac9dd9+805~ubuntu19.04.1) disco; urgency=low
* Auto build.
-- Launchpad Package Builder <noreply@launchpad.net> Sat, 16 Feb 2019 00:26:45 +0000
-1
View File
@@ -1 +0,0 @@
9
-13
View File
@@ -1,13 +0,0 @@
Source: olive-editor
Section: video
Priority: optional
Maintainer: Olive Team <itsmattkc@gmail.com>
Build-Depends: debhelper (>=9), build-essential, qt5-default, qtmultimedia5-dev, libqt5opengl5-dev, libqt5svg5-dev, libqt5multimedia5-plugins, libavformat-dev, libavcodec-dev, libavutil-dev, libswscale-dev, libswresample-dev, libavfilter-dev, libpostproc-dev, git, frei0r-plugins-dev, qttools5-dev-tools, cmake, qttools5-dev
Standards-Version: 3.9.6
Homepage: https://olivevideoeditor.org/
Package: olive-editor
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}, libqt5multimedia5-plugins
Description: Nonlinear video editor focused on performance and simplicity
-29
View File
@@ -1,29 +0,0 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: olive-editor
Source: https://olivevideoeditor.org/
Files: *
Copyright: 2018 Olive Team <itsmattkc@gmail.com>
License: GPL-3.0+
Files: debian/*
Copyright: 2014 Olive Team <itsmattkc@gmail.com>
License: GPL-3.0+
License: GPL-3.0+
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 package 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/>.
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
-3
View File
@@ -1,3 +0,0 @@
# A simple script to extract the Git hash from an auto-generated debian/changelog
grep -Po '(?<=-)(([a-z0-9])\w+)(?=\+)' -m 1 $1
-9
View File
@@ -1,9 +0,0 @@
#!/usr/bin/make -f
export QT_SELECT := qt5
%:
dh $@
override_dh_auto_build:
lrelease olive.pro
dh_auto_build
-1
View File
@@ -1 +0,0 @@
3.0 (quilt)
-6
View File
@@ -1,6 +0,0 @@
#include "ffmpegaudiodecoder.h"
FFmpegAudioDecoder::FFmpegAudioDecoder()
{
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef FFMPEGAUDIODECODER_H
#define FFMPEGAUDIODECODER_H
#include "decoders/ffmpegdecoder.h"
/**
* @brief The FFmpegAudioDecoder class
*
* The role of an audio decoder is to simply convert audio to
*/
class FFmpegAudioDecoder : public FFmpegDecoder
{
public:
FFmpegAudioDecoder();
};
#endif // FFMPEGAUDIODECODER_H
-28
View File
@@ -1,28 +0,0 @@
#include "decoder.h"
Decoder::Decoder() :
open_(false)
{
}
Decoder::Decoder(FootageStream *fs) :
open_(false),
stream_(fs)
{
}
Decoder::~Decoder()
{
}
FootageStream *Decoder::stream()
{
return stream_;
}
void Decoder::set_stream(FootageStream *fs)
{
Close();
stream_ = fs;
}
-106
View File
@@ -1,106 +0,0 @@
#ifndef DECODER_H
#define DECODER_H
#include <stdint.h>
#include "global/rational.h"
#include "project/footage.h"
#include "frame.h"
/**
* @brief The Decoder class
*
* A decoder's is the main class for bringing external media into Olive. Its responsibilities are to serve as
* abstraction from codecs/decoders and provide complete frames. These frames can be video or audio data and are
* provided as Frame objects in shared pointers to alleviate the responsibility of memory handling.
*
* The main function in a decoder is Retrieve() which should return complete image/audio data. A decoder should
* alleviate all the complexities of codec compression from the rest of the application (i.e. a decoder should never
* return a partial frame or require other parts of the system to interface directly with the codec). Often this will
* necessitate pre-emptively caching, indexing, or even fully transcoding media before using it which can be implemented
* through the Analyze() function.
*
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelFormatConverter
* (olive::pix_fmt_conv) to be utilized in the rest of the rendering pipeline.
*/
class Decoder : public QObject
{
Q_OBJECT
public:
Decoder();
Decoder(FootageStream* fs);
virtual ~Decoder();
FootageStream* stream();
void set_stream(FootageStream* fs);
/**
* @brief Open media/allocate memory
*
* Any file handles or memory allocation that needs to be done before this instance of a Decoder can return data
* should be done here.
*
* @return
*
* TRUE if successful and ready to return data, FALSE if failed to open and unable to retrieve data. If the function
* fails, any memory allocated should be free'd before returning FALSE, possibly by calling Close().
*/
virtual bool Open() = 0;
/**
* @brief Retrieve frame/data
*
* The main function for retrieving data from the Decoder. This function should always provide complete frame data
* (i.e. no partial frames or missing samples) at the timecode provided. The Decoder should perform any steps
* required to retrieve a complete frame separate from the rest of the program, using any form of caching/indexing
* to keep this as performant as possible.
*
* It's acceptable for this function to check whether the Decoder is open, and call Open() if not. If Open() returns
* false, this function should return nullptr.
*
* @param timecode
*
* The timecode (a rational in seconds) to retrieve the data at.
*
* @param length
*
* Audio only - ignored for video decoders. The total length of audio data to retrieve (a rational in seconds).
*
* @return
*
* A FramePtr of valid data at this timecode (of the requested length if this is audio media), or nullptr if there
* was nothing to retrieve at the provided timecode or the media could not be opened.
*/
virtual FramePtr Retrieve(const rational& timecode, const rational& length = 0) = 0;
/**
* @brief Close media/deallocate memory
*
* Any file handles or memory allocations opened in Open() should be cleaned up here.
*
* As the main memory freeing function, it's good practice to call this in Open() if there's an error that prevents
* correct function before Open() returns. As such, Close() should be prepared for not all memory/file handles to
* have been opened successfully.
*/
virtual void Close() = 0;
/**
* @brief Prepare footage for use by a Decoder later
*
* Needs fleshing out. This functions purpose will be to perform initial analyses of a video file. Any caching,
* indexing, or transcoding to help make this media performant and reliable should be done here.
*/
virtual void Analyze();// = 0;
protected:
bool open_;
private:
FootageStream* stream_;
};
using DecoderPtr = std::shared_ptr<Decoder>;
#endif // DECODER_H
-144
View File
@@ -1,144 +0,0 @@
#include "ffmpegdecoder.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
#include <QStatusBar>
#include <QString>
#include <QtMath>
#include <QDebug>
#include "ui/mainwindow.h"
FFmpegDecoder::FFmpegDecoder() :
fmt_ctx_(nullptr),
codec_ctx_(nullptr),
opts_(nullptr)
{
}
bool FFmpegDecoder::Open()
{
if (open_) {
return true;
}
int error_code;
qDebug() << "shid";
qDebug() << stream()->footage;
qDebug() << stream()->footage->url;
QByteArray ba = stream()->footage->url.toUtf8();
const char* filename = ba.constData();
// Open file in a format context
error_code = avformat_open_input(&fmt_ctx_, filename, nullptr, nullptr);
// Handle format context error
if (error_code != 0) {
FFmpegErr(error_code);
return false;
}
// Get stream information from format
error_code = avformat_find_stream_info(fmt_ctx_, nullptr);
// Handle get stream information error
if (error_code < 0) {
FFmpegErr(error_code);
return false;
}
// Dump format information
av_dump_format(fmt_ctx_, stream()->file_index, filename, 0);
// Get reference to correct AVStream
avstream_ = fmt_ctx_->streams[stream()->file_index];
// Find decoder
AVCodec* codec = avcodec_find_decoder(avstream_->codecpar->codec_id);
// Handle failure to find decoder
if (codec == nullptr) {
Error(tr("Failed to find appropriate decoder for this codec (%1 :: %2)")
.arg(stream()->footage->url, avstream_->codecpar->codec_id));
return false;
}
// Allocate context for the decoder
codec_ctx_ = avcodec_alloc_context3(codec);
if (codec_ctx_ == nullptr) {
Error(tr("Failed to allocate codec context (%1 :: %2)").arg(stream()->footage->url, stream()->file_index));
return false;
}
// Copy parameters from the AVStream to the AVCodecContext
error_code = avcodec_parameters_to_context(codec_ctx_, avstream_->codecpar);
// Handle failure to copy parameters
if (error_code < 0) {
FFmpegErr(error_code);
return false;
}
// enable multithreading on decoding
error_code = av_dict_set(&opts_, "threads", "auto", 0);
// Handle failure to set multithreaded decoding
if (error_code < 0) {
FFmpegErr(error_code);
return false;
}
// Open codec
error_code = avcodec_open2(codec_ctx_, codec, &opts_);
if (error_code < 0) {
FFmpegErr(error_code);
return false;
}
open_ = true;
return true;
}
void FFmpegDecoder::Close()
{
if (opts_ != nullptr) {
av_dict_free(&opts_);
opts_ = nullptr;
}
if (codec_ctx_ != nullptr) {
avcodec_free_context(&codec_ctx_);
codec_ctx_ = nullptr;
}
if (fmt_ctx_ != nullptr) {
avformat_close_input(&fmt_ctx_);
fmt_ctx_ = nullptr;
}
open_ = false;
}
void FFmpegDecoder::FFmpegErr(int error_code)
{
char err[1024];
av_strerror(error_code, err, 1024);
Error(tr("Error decoding %1 - %2 %3").arg(stream()->footage->url,
QString::number(error_code),
err));
}
void FFmpegDecoder::Error(const QString &s)
{
qWarning() << s;
olive::MainWindow->statusBar()->showMessage(s);
Close();
}
-29
View File
@@ -1,29 +0,0 @@
#ifndef FFMPEGDECODER_H
#define FFMPEGDECODER_H
#include "decoder.h"
class FFmpegDecoder : public Decoder
{
public:
FFmpegDecoder();
virtual bool Open() override;
virtual void Close() override;
protected:
void FFmpegErr(int error_code);
void Error(const QString& s);
AVFormatContext* fmt_ctx_;
AVCodecContext* codec_ctx_;
AVStream* avstream_;
AVPacket* pkt_;
AVFrame* frame_;
AVDictionary* opts_;
QVector<int64_t> frame_index_;
};
#endif // FFMPEGDECODER_H
-66
View File
@@ -1,66 +0,0 @@
#include "ffmpegvideodecoder.h"
#include <QDebug>
FFmpegVideoDecoder::FFmpegVideoDecoder()
{
}
FramePtr FFmpegVideoDecoder::Retrieve(const rational &timecode, const rational &length)
{
if (!open_ && !Open()) {
qWarning() << "Failed to open FFmpeg stream";
return nullptr;
}
// TODO index frames
int receive_ret;
// Allocate new frame
AVFrame* frame = av_frame_alloc();
AVPacket* pkt = av_packet_alloc();
while ((receive_ret = avcodec_receive_frame(codec_ctx_, frame)) == AVERROR(EAGAIN)) {
int read_ret = 0;
do {
// Make sure any previous packet buffers are cleared before reading new ones
av_packet_unref(pkt);
// Read next packet from file
read_ret = av_read_frame(fmt_ctx_, pkt);
} while (read_ret >= 0 && pkt->stream_index != stream()->file_index);
if (read_ret >= 0) {
int send_ret = avcodec_send_packet(codec_ctx_, pkt);
if (send_ret < 0) {
qCritical() << "Failed to send packet to decoder." << send_ret;
return nullptr;
}
} else {
if (read_ret == AVERROR_EOF) {
int send_ret = avcodec_send_packet(codec_ctx_, nullptr);
if (send_ret < 0) {
qCritical() << "Failed to send packet to decoder." << send_ret;
return nullptr;
}
} else {
qCritical() << "Could not read frame." << read_ret;
return nullptr;
}
}
}
if (receive_ret < 0) {
if (receive_ret != AVERROR_EOF) {
qCritical() << "Failed to receive packet from decoder." << receive_ret;
}
}
// Free AVPacket
av_packet_free(&pkt);
// AVFrame is wrapped in Frame object and shared ptr to automatically clear it
return std::make_shared<Frame>(frame);
}
-16
View File
@@ -1,16 +0,0 @@
#ifndef FFMPEGVIDEODECODER_H
#define FFMPEGVIDEODECODER_H
#include "ffmpegdecoder.h"
class FFmpegVideoDecoder : public FFmpegDecoder
{
public:
FFmpegVideoDecoder();
virtual FramePtr Retrieve(const rational& timecode, const rational& length = 0);
private:
};
#endif // FFMPEGVIDEODECODER_H
-89
View File
@@ -1,89 +0,0 @@
#include "frame.h"
Frame::Frame() :
frame_(nullptr)
{
}
Frame::Frame(AVFrame *f) :
frame_(f)
{
}
Frame::Frame(const Frame &f) :
frame_(nullptr)
{
}
Frame::Frame(Frame &&f) :
frame_(f.frame_)
{
f.frame_ = nullptr;
}
Frame &Frame::operator=(const Frame &f)
{
frame_ = nullptr;
return *this;
}
Frame &Frame::operator=(Frame &&f)
{
if (&f != this) {
frame_ = f.frame_;
f.frame_ = nullptr;
}
return *this;
}
Frame::~Frame()
{
FreeChild();
}
void Frame::SetAVFrame(AVFrame *f, AVRational timebase)
{
FreeChild();
f = frame_;
timestamp_ = rational(timebase.num*f->pts, timebase.den);
}
const int &Frame::width()
{
return frame_->width;
}
const int &Frame::height()
{
return frame_->height;
}
const rational &Frame::timestamp()
{
return timestamp_;
}
const int &Frame::format()
{
return frame_->format;
}
uint8_t **Frame::data()
{
return frame_->data;
}
int *Frame::linesize()
{
return frame_->linesize;
}
void Frame::FreeChild()
{
if (frame_ != nullptr) {
av_frame_free(&frame_);
frame_ = nullptr;
}
}
-96
View File
@@ -1,96 +0,0 @@
#ifndef FRAME_H
#define FRAME_H
#include <memory>
#include "global/rational.h"
/**
* @brief The Frame class
*
* Abstraction from AVFrame. Currently a simple AVFrame wrapper.
*
* This class does not support copying at this time.
*/
class Frame
{
public:
// Normal constructor
Frame();
// AVFrame constructor
Frame(AVFrame* f);
// Copy constructor
Frame(const Frame& f);
// Move constructor
Frame(Frame&& f);
// Copy assignment operator
Frame& operator=(const Frame& f);
// Move assignment operator
Frame& operator=(Frame&& f);
// Destructor
~Frame();
/**
* @brief Set frame child
*
* This class currently primarily functions as a wrapper for AVFrame for use outside of the Decoder classes.
* The internal AVFrame is set here. This class will also take ownership of the AVFrame and automatically
* clear it when deconstructed.
*
* @param f
*/
void SetAVFrame(AVFrame* f, AVRational timebase);
/**
* @brief Get frame's width in pixels
*/
const int& width();
/**
* @brief Get frame's height in pixels
*/
const int& height();
/**
* @brief Get frame's timestamp.
*
* This timestamp is always a rational that will equate to the time in seconds.
*/
const rational& timestamp();
/**
* @brief Get frame's format
*
* @return
*
* Currently this will either be an AVPixelFormat (video) or an AVSampleFormat (audio).
*/
const int& format();
/**
* @brief Get the data buffer of this frame
*/
uint8_t** data();
/**
* @brief Get the linesize information for this frame
*/
int* linesize();
private:
void FreeChild();
AVFrame* frame_;
rational timestamp_;
};
using FramePtr = std::shared_ptr<Frame>;
#endif // FRAME_H
-241
View File
@@ -1,241 +0,0 @@
#include "pixelformatconverter.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}
#include <QtMath>
#include <QDebug>
PixelFormatConverter* olive::pix_fmt_conv;
PixelFormatConverter::PixelFormatConverter()
{
threads_.resize(qMax(1, QThread::idealThreadCount()-1));
for (int i=0;i<threads_.size();i++) {
threads_[i] = new PixFmtConvertThread();
threads_[i]->start(QThread::HighPriority);
}
}
PixelFormatConverter::~PixelFormatConverter()
{
for (int i=0;i<threads_.size();i++) {
delete threads_[i];
}
}
void PixelFormatConverter::AVFrameToPipeline(uint8_t **input_buffer,
int *input_linesize,
const int& width,
const int& height,
AVPixelFormat input_fmt,
void *output_buffer,
olive::PixelFormat output_fmt)
{
// TODO: Support all AVPixelFormats. There are many and this will be an ongoing process (though many will likely
// not need much effort (or any support at all). In the time-being this function will continue to use swscale
// (functioning as abstraction from the rest of the code) while remaining support is added.
// Currently we don't natively support anything other than RGBA and RGBA64 so if it isn't this, we'll need to
// swscale it to one of those for the timebeing
uint8_t *converted_buffer = nullptr;
int converted_linesize[AV_NUM_DATA_POINTERS];
if (input_fmt != AV_PIX_FMT_RGBA && input_fmt != AV_PIX_FMT_RGBA64) {
converted_buffer = new uint8_t[input_linesize[0] * height * 4];
// Determine whether this is an 8-bit image or higher
AVPixelFormat possible_pix_fmts[] = {
AV_PIX_FMT_RGBA,
AV_PIX_FMT_RGBA64,
AV_PIX_FMT_NONE
};
AVPixelFormat pix_fmt = avcodec_find_best_pix_fmt_of_list(possible_pix_fmts,
input_fmt,
1,
nullptr);
SwsContext* sws_ctx = sws_getContext(width,
height,
input_fmt,
width,
height,
pix_fmt,
0,
nullptr,
nullptr,
nullptr);
switch (pix_fmt) {
case AV_PIX_FMT_RGBA:
converted_linesize[0] = width * 4;
break;
case AV_PIX_FMT_RGBA64:
converted_linesize[0] = width * 8;
break;
default:
// We shouldn't really ever get here, but we may as well handle it
qWarning() << "Invalid destination pixel format";
}
sws_scale(sws_ctx, input_buffer, input_linesize, 0, height, &converted_buffer, converted_linesize);
sws_freeContext(sws_ctx);
input_fmt = pix_fmt;
input_buffer = &converted_buffer;
}
// Wait til other frame conversions are done
mutex_.lock();
// Split job between threads
int lines_per_thread = qCeil(qreal(height)/qreal(threads_.size()));
// Send conversion job to threads
for (int i=0;i<threads_.size();i++) {
int line_start = lines_per_thread*i;
threads_.at(i)->Convert(input_buffer,
input_linesize,
width,
line_start,
qMin(lines_per_thread, height - line_start),
input_fmt,
output_buffer,
output_fmt);
}
// Wait for each thread to complete
for (int i=0;i<threads_.size();i++) {
threads_.at(i)->WaitUntilComplete();
}
mutex_.unlock();
delete [] converted_buffer;
}
int PixelFormatConverter::GetBufferSize(olive::PixelFormat format, const int &width, const int &height)
{
int rgba_channels = 4;
switch (format) {
case olive::PIX_FMT_RGBA8:
return width * height * rgba_channels;
case olive::PIX_FMT_RGBA16:
case olive::PIX_FMT_RGBA16F:
return 2 * width * height * rgba_channels;
case olive::PIX_FMT_RGBA32F:
return 4 * width * height * rgba_channels;
default:
return 0;
}
}
PixFmtConvertThread::PixFmtConvertThread() :
cancelled_(false)
{
}
void PixFmtConvertThread::run()
{
mutex_.lock();
while (!cancelled_) {
wait_cond_.wait(&mutex_);
if (cancelled_) break;
Process();
}
mutex_.unlock();
}
void PixFmtConvertThread::Convert(uint8_t **input_buffer,
int *input_linesize,
const int &width,
const int &line_start,
const int &line_count,
AVPixelFormat input_fmt,
void *output_buffer,
olive::PixelFormat output_fmt)
{
mutex2_.lock();
mutex_.lock();
input_buffer_ = input_buffer;
input_linesize_ = input_linesize;
width_ = width;
line_start_ = line_start;
line_count_ = line_count;
input_fmt_ = input_fmt;
output_buffer_ = output_buffer;
output_fmt_ = output_fmt;
wait_cond_.wakeAll();
mutex_.unlock();
}
void PixFmtConvertThread::WaitUntilComplete()
{
mutex2_.lock();
mutex_.lock();
mutex2_.unlock();
mutex_.unlock();
}
void PixFmtConvertThread::Cancel()
{
cancelled_ = true;
wait_cond_.wakeAll();
wait();
}
void PixFmtConvertThread::Process()
{
mutex2_.unlock();
switch (input_fmt_) {
case AV_PIX_FMT_RGBA:
{
int channels = 4; // FIXME RGBA magic number
int byte_linesize = input_linesize_[0]*channels;
int input_start = byte_linesize*line_start_;
int output_start = width_*channels*line_start_;
for (int i=0;i<line_count_;i++) {
int input_line_start = input_start + byte_linesize*i;
int input_line_end = width_*channels;
int output_line_start = output_start + width_*channels*i;
for (int j=0;j<input_line_end;j++) {
// Convert 8-bit integer to float and store in output buffer
static_cast<float*>(output_buffer_)[output_line_start+j] = input_buffer_[0][input_line_start+j] / 255.0f;
}
}
break;
}
default:
qWarning() << "PixFmtConvertThread doesn't yet know how to convert pixel format" << input_fmt_;
}
}
-144
View File
@@ -1,144 +0,0 @@
#ifndef PIXELFORMATCONVERTER_H
#define PIXELFORMATCONVERTER_H
extern "C" {
#include <libavutil/pixfmt.h>
}
#include <QMutex>
#include <QThread>
#include <QWaitCondition>
#include "rendering/pixelformats.h"
class PixFmtConvertThread : public QThread {
public:
PixFmtConvertThread();
virtual void run() override;
void Convert(uint8_t** input_buffer,
int* input_linesize,
const int &width,
const int &line_start,
const int &line_count,
AVPixelFormat input_fmt,
void* output_buffer,
olive::PixelFormat output_fmt);
void WaitUntilComplete();
void Cancel();
private:
// Main processing function
void Process();
// Threading variables
QWaitCondition wait_cond_;
QMutex mutex_;
QMutex mutex2_;
bool cancelled_;
// Input variables for conversion
uint8_t** input_buffer_;
int* input_linesize_;
int width_;
int line_start_;
int line_count_;
AVPixelFormat input_fmt_;
// Output variables for conversion
void* output_buffer_;
olive::PixelFormat output_fmt_;
};
/**
* @brief The PixelFormatConverter class
*
* A background process for optimized conversion of frames to Olive's internal pipeline format. It is designed as a
* replacement of FFmpeg's swscale for all pixel format conversions. It's main advantages over swscale are:
*
* * Support for floating-point pixel formats
* * Better control over the conversion from YCbCr to RGB
* * Multithreaded by design
*
* In the old system, frame pixel formats were converted twice - once to RGBA32 or RGBA64 by swscale, then a second
* time to RGBA16F or RGBA32F by OpenGL. In addition to the aforementioned advantages, a future goal of this class is to
* reduce those two steps to one.
*/
class PixelFormatConverter
{
public:
PixelFormatConverter();
~PixelFormatConverter();
/**
* @brief Converts data from FFmpeg (or equivalent data) to data ready for Olive's buffer
*
* @param input_buffer
*
* The buffer of data to convert. This can be fed AVFrame->data directly.
*
* @param input_linesize
*
* The linesize of the data to convert. This can be fed AVFrame->linesize directly.
*
* @param width
*
* Width of the frame. This will be the same in the output buffer.
*
* @param height
*
* Height of the frame. This will be the same in the output buffer.
*
* @param input_fmt
*
* Expects a value from the AVPixelFormat enum.
*
* @param output_buffer
*
* The buffer to place the converted data. This function does NOT allocate data, this must be done beforehand.
* The helper function GetBufferSize() can be used to determine the minimum size (in bytes) that this buffer
* needs to have allocated for this function to be successful.
*
* @param output_fmt
*
* The format to convert to. Must be a member of the olive::PixelFormat enum.
*/
void AVFrameToPipeline(uint8_t** input_buffer,
int* input_linesize,
const int &width,
const int &height,
AVPixelFormat input_fmt,
void* output_buffer,
olive::PixelFormat output_fmt);
/**
* @brief Returns the minimum buffer size (in bytes) necessary for a given format, width, and height.
*
* @param format
*
* The format of the data the buffer should contain. Must be a member of the olive::PixelFormat enum.
*
* @param width
*
* The width (in pixels) of the buffer.
*
* @param height
*
* The height (in pixels) of the buffer.
*/
static int GetBufferSize(olive::PixelFormat format, const int& width, const int& height);
private:
QMutex mutex_;
QVector<PixFmtConvertThread*> threads_;
};
namespace olive {
extern PixelFormatConverter* pix_fmt_conv;
}
#endif // PIXELFORMATCONVERTER_H
-66
View File
@@ -1,66 +0,0 @@
/***
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 "aboutdialog.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QDialogButtonBox>
#include "global/global.h"
AboutDialog::AboutDialog(QWidget *parent) :
QDialog(parent)
{
setWindowTitle("About Olive");
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setSpacing(20);
// Construct About text
QLabel* label =
new QLabel(QString("<html><head/><body>"
"<p><img src=\":/icons/olive-splash.png\"/></p>"
"<p><a href=\"https://www.olivevideoeditor.org/\">"
"<span style=\" text-decoration: underline; color:#007af4;\">"
"https://www.olivevideoeditor.org/"
"</span></a></p>"
"<p><b>%1</b></p>" // AppName (version identifier)
"<p>%2</p>" // First statement
"<p>%3</p>" // Second statement
"</body></html>").arg(olive::AppName,
tr("Olive is a non-linear video editor. This software is free and "
"protected by the GNU GPL."),
tr("Olive Team is obliged to inform users that Olive source code is "
"available for download from its website.")), this);
// Set text formatting
label->setAlignment(Qt::AlignCenter);
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
label->setCursor(Qt::IBeamCursor);
label->setWordWrap(true);
layout->addWidget(label);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
buttons->setCenterButtons(true);
layout->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
}
-48
View File
@@ -1,48 +0,0 @@
/***
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 ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
/**
* @brief The AboutDialog class
*
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from
* anywhere
*/
class AboutDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief AboutDialog Constructor
*
* Creates About dialog.
*
* @param parent
*
* QWidget parent object. Usually this will be MainWindow.
*/
explicit AboutDialog(QWidget *parent = nullptr);
};
#endif // ABOUTDIALOG_H
-244
View File
@@ -1,244 +0,0 @@
/***
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 "actionsearch.h"
#include <QVBoxLayout>
#include <QKeyEvent>
#include <QMenuBar>
#include <QLabel>
#include "ui/mainwindow.h"
ActionSearch::ActionSearch(QWidget *parent) :
QDialog(parent)
{
// ActionSearch requires a parent widget
Q_ASSERT(parent != nullptr);
// Set styling (object name is required for CSS specific to this object)
setObjectName("ASDiag");
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
// Size proportionally to the parent (usually MainWindow).
resize(parent->width()/3, parent->height()/3);
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
setWindowFlags(Qt::Popup);
QVBoxLayout* layout = new QVBoxLayout(this);
// Construct the main entry text field.
ActionSearchEntry* entry_field = new ActionSearchEntry(this);
// Set the main entry field font size to 1.2x its standard font size.
QFont entry_field_font = entry_field->font();
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2));
entry_field->setFont(entry_field_font);
// Set placeholder text for the main entry field
entry_field->setPlaceholderText(tr("Search for action..."));
// Connect signals/slots
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &)));
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
// We override it here to select the upper or lower item in the list.
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up()));
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down()));
layout->addWidget(entry_field);
// Construct list of actions
list_widget = new ActionSearchList(this);
// Set list's font to 1.2x its standard font size
QFont list_widget_font = list_widget->font();
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2));
list_widget->setFont(list_widget_font);
layout->addWidget(list_widget);
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
// shortcut for example).
entry_field->setFocus();
}
void ActionSearch::search_update(const QString &s, const QString &p, QMenu *parent) {
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
// submenus).
if (parent == nullptr) {
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
// (and their submenus).
// We'll clear all the current items in the list since if we're here, we're just starting.
list_widget->clear();
QList<QAction*> menus = olive::MainWindow->menuBar()->actions();
// Loop through all menus from the menubar and run this function on each one.
for (int i=0;i<menus.size();i++) {
QMenu* menu = menus.at(i)->menu();
search_update(s, p, menu);
}
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
// keyboard-exclusive functionality.
if (list_widget->count() > 0) {
list_widget->item(0)->setSelected(true);
}
} else {
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
// adding the current menu's text to the existing hierarchy (passed in `p`).
QString menu_text;
if (!p.isEmpty()) menu_text += p + " > ";
menu_text += parent->title().replace("&", ""); // Strip out any &s used in menu action names
// Loop over the menu's actions
QList<QAction*> actions = parent->actions();
for (int i=0;i<actions.size();i++) {
QAction* a = actions.at(i);
// Ignore separator actions
if (!a->isSeparator()) {
if (a->menu() != nullptr) {
// If the action is a menu, run this function recursively on it
search_update(s, menu_text, a->menu());
} else {
// This is a valid non-separator non-menu action, so check it against the currently entered string.
// Strip out all &s from the action's name
QString comp = a->text().replace("&", "");
// See if the action's name contains any of the currently entered string
if (comp.contains(s, Qt::CaseInsensitive)) {
// If so, we add it to the list widget.
QListWidgetItem* item = new QListWidgetItem(QString("%1\n(%2)").arg(comp, menu_text), list_widget);
// Add a pointer to the original QAction in the item's data
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
list_widget->addItem(item);
}
}
}
}
}
}
void ActionSearch::perform_action() {
// Loop over all the items in the list and if we find one that's selected, we trigger it.
QList<QListWidgetItem*> selected_items = list_widget->selectedItems();
if (list_widget->count() > 0 && selected_items.size() > 0) {
QListWidgetItem* item = selected_items.at(0);
// Get QAction pointer from item's data
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
a->trigger();
}
// Close this popup
accept();
}
void ActionSearch::move_selection_up() {
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
// bottom item).
int lim = list_widget->count();
for (int i=1;i<lim;i++) {
if (list_widget->item(i)->isSelected()) {
list_widget->item(i-1)->setSelected(true);
list_widget->scrollToItem(list_widget->item(i-1));
break;
}
}
}
void ActionSearch::move_selection_down() {
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
// bottom item).
int lim = list_widget->count()-1;
for (int i=0;i<lim;i++) {
if (list_widget->item(i)->isSelected()) {
list_widget->item(i+1)->setSelected(true);
list_widget->scrollToItem(list_widget->item(i+1));
break;
}
}
}
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {}
void ActionSearchEntry::keyPressEvent(QKeyEvent * event) {
// Listen for up/down, otherwise pass the key event to the base class.
switch (event->key()) {
case Qt::Key_Up:
emit moveSelectionUp();
break;
case Qt::Key_Down:
emit moveSelectionDown();
break;
default:
QLineEdit::keyPressEvent(event);
}
}
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {}
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) {
// Indiscriminately emit a signal on any double click
emit dbl_click();
}
-170
View File
@@ -1,170 +0,0 @@
/***
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 ACTIONSEARCH_H
#define ACTIONSEARCH_H
#include <QDialog>
#include <QLineEdit>
#include <QListWidget>
#include <QMenu>
class ActionSearchList;
/**
* @brief The ActionSearch class
*
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
*/
class ActionSearch : public QDialog
{
Q_OBJECT
public:
/**
* @brief ActionSearch Constructor
*
* Create ActionSearch popup.
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
ActionSearch(QWidget* parent);
private slots:
/**
* @brief Update the list of actions according to a search query
*
* This function adds/removes actions in the action list according to a given search query entered by the user.
*
* To loop over the menubar and all of its menus and submenus, this function will call itself recursively. As such
* some of its parameters do not need to be set externally, as these will be set by the function itself as it calls
* itself.
*
* @param s
*
* The search text. This is the only parameter that should be set externally.
*
* @param p
*
* The current parent hierarchy. In most cases, this should be left as nullptr when called externally.
* search_update() will fill this automatically as it needs while calling itself recursively.
*
* @param parent
*
* The current menu to loop over. In most cases, this should be left as nullptr when called externally.
* search_update() will fill this automatically as it needs while calling itself recursively.
*/
void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr);
/**
* @brief Perform the currently selected action
*
* Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently
* highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is
* triggered and the popup closes anyway.
*/
void perform_action();
/**
* @brief Move selection up
*
* A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the
* selection is already at the top of the list, this is a no-op.
*/
void move_selection_up();
/**
* @brief Move selection down
*
* A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the
* selection is already at the bottom of the list, this is a no-op.
*/
void move_selection_down();
private:
/**
* @brief Main widget that shows the list of commands
*/
ActionSearchList* list_widget;
};
/**
* @brief The ActionSearchList class
*
* Simple wrapper around QListWidget that emits a signal when an item is double clicked that ActionSearch connects
* to a slot that triggers the currently selected action.
*/
class ActionSearchList : public QListWidget {
Q_OBJECT
public:
/**
* @brief ActionSearchList Constructor
* @param parent
*
* Usually ActionSearch.
*/
ActionSearchList(QWidget* parent);
protected:
/**
* @brief Override of QListWidget's double click event that emits a signal.
*/
void mouseDoubleClickEvent(QMouseEvent *);
signals:
/**
* @brief Signal emitted when a QListWidget item is double clicked.
*/
void dbl_click();
};
/**
* @brief The ActionSearchEntry class
*
* Simple wrapper around QLineEdit that emits signals when the up or down arrow keys are pressed so that ActionSearch
* can connect them to moving the current selection up or down.
*/
class ActionSearchEntry : public QLineEdit {
Q_OBJECT
public:
/**
* @brief ActionSearchEntry
* @param parent
*
* Usually ActionSearch.
*/
ActionSearchEntry(QWidget* parent);
protected:
/**
* @brief Override of QLineEdit's key press event that listens for up/down key presses.
* @param event
*/
void keyPressEvent(QKeyEvent * event);
signals:
/**
* @brief Emitted when the user presses the up arrow key.
*/
void moveSelectionUp();
/**
* @brief Emitted when the user presses the down arrow key.
*/
void moveSelectionDown();
};
#endif // ACTIONSEARCH_H
-107
View File
@@ -1,107 +0,0 @@
/***
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 "advancedvideodialog.h"
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QLabel>
#include <QComboBox>
#include <QDebug>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/pixdesc.h>
}
AdvancedVideoDialog::AdvancedVideoDialog(QWidget *parent,
AVCodecID encoding_codec,
VideoCodecParams &iparams) :
QDialog(parent),
params_(iparams)
{
setWindowTitle(tr("Advanced Video Settings"));
// use variable for row to assist adding new fields to the grid layout
int row = 0;
// get encoder information for this codec from FFmpeg
AVCodec* codec_info = avcodec_find_encoder(static_cast<AVCodecID>(encoding_codec));
// set up grid layout for dialog
QGridLayout* layout = new QGridLayout(this);
// create row for codec pixel formats
layout->addWidget(new QLabel(tr("Pixel Format:")), row, 0);
pix_fmt_combo_ = new QComboBox();
// loop through available pixel formats for this codec
int pix_fmt_index = 0;
while (codec_info->pix_fmts[pix_fmt_index] != -1) { // AVCodec->pix_fmts is terminated by "-1"
// get the name of the pixel format and add it to the combobox (with the pixel format constant)
pix_fmt_combo_->addItem(av_get_pix_fmt_name(codec_info->pix_fmts[pix_fmt_index]),
codec_info->pix_fmts[pix_fmt_index]);
// if the user has already selected a pixel format, set the combobox to it as well
if (codec_info->pix_fmts[pix_fmt_index] == params_.pix_fmt) {
pix_fmt_combo_->setCurrentIndex(pix_fmt_combo_->count()-1);
}
pix_fmt_index++;
}
layout->addWidget(pix_fmt_combo_, row, 1);
row++;
// create row for multithreading thread count
layout->addWidget(new QLabel(tr("Threads:")), row, 0);
thread_spinbox_ = new QSpinBox();
// with the thread count, "0" is considered automatics
thread_spinbox_->setMinimum(0);
thread_spinbox_->setSpecialValueText("Auto");
// load current thread value
thread_spinbox_->setValue(params_.threads);
layout->addWidget(thread_spinbox_);
row++;
// buttons
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttons->setCenterButtons(true);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
layout->addWidget(buttons, row, 0, 1, 2);
}
void AdvancedVideoDialog::accept() {
// store settings back into struct
params_.pix_fmt = pix_fmt_combo_->currentData().toInt();
params_.threads = thread_spinbox_->value();
QDialog::accept();
}
-80
View File
@@ -1,80 +0,0 @@
/***
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 ADVANCEDVIDEODIALOG_H
#define ADVANCEDVIDEODIALOG_H
#include <QDialog>
#include <QComboBox>
#include <QSpinBox>
#include "rendering/exportthread.h"
/**
* @brief The AdvancedVideoDialog class
*
* A dialog for interfacing with VideoCodecParams, a struct for more advanced video settings sometimes specific to
* one codec. Primarily a companion to ExportDialog which will provide the VideoCodecParams reference,
*/
class AdvancedVideoDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief AdvancedVideoDialog Constructor
*
* @param parent
*
* QWidget parent. Usually ExportDialog.
*
* @param encoding_codec
*
* The AVCodecID of the selected export codec.
*
* @param iparams
*
* A VideoCodecParams struct containing the extra codec data.
*/
AdvancedVideoDialog(QWidget* parent,
AVCodecID encoding_codec,
VideoCodecParams& iparams);
public slots:
/**
* @brief Overridden accept for saving the UI data into the provided VideoCodecParams struct.
*/
virtual void accept() override;
private:
/**
* @brief Internal reference to VideoCodecParams struct provided by ExportDialog.
*/
VideoCodecParams& params_;
/**
* @brief ComboBox to show available pixel formats for this codec
*/
QComboBox* pix_fmt_combo_;
/**
* @brief SpinBox for multithreading settings
*/
QSpinBox* thread_spinbox_;
};
#endif // ADVANCEDVIDEODIALOG_H
-215
View File
@@ -1,215 +0,0 @@
/***
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 "autocutsilencedialog.h"
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QDialogButtonBox>
#include "timeline/sequence.h"
#include "rendering/renderfunctions.h"
#include "panels/panels.h"
#include "panels/timeline.h"
AutoCutSilenceDialog::AutoCutSilenceDialog(QWidget *parent, QVector<Clip*> clips) :
QDialog(parent),
clips_(clips)
{
setWindowTitle(tr("Cut Silence"));
QVBoxLayout* main_layout = new QVBoxLayout(this);
QGridLayout* grid = new QGridLayout();
grid->setSpacing(6);
grid->addWidget(new QLabel(tr("Attack Threshold:"), this), 0, 0);
attack_threshold = new LabelSlider(this);
attack_threshold->SetDecimalPlaces(0);
grid->addWidget(attack_threshold, 0, 1);
grid->addWidget(new QLabel(tr("Attack Time:"), this), 1, 0);
attack_time = new LabelSlider(this);
attack_time->SetDecimalPlaces(0);
grid->addWidget(attack_time, 1, 1);
grid->addWidget(new QLabel(tr("Release Threshold:"), this), 2, 0);
release_threshold = new LabelSlider(this);
release_threshold->SetDecimalPlaces(0);
grid->addWidget(release_threshold, 2, 1);
grid->addWidget(new QLabel(tr("Release Time:"), this), 3, 0);
release_time = new LabelSlider(this);
release_time->SetDecimalPlaces(0);
grid->addWidget(release_time, 3, 1);
main_layout->addLayout(grid);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttonBox->setCenterButtons(true);
main_layout->addWidget(buttonBox);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
}
int AutoCutSilenceDialog::exec()
{
default_attack_threshold = 5;
current_attack_threshold = 5;
default_attack_time = 2;
current_attack_time = 2;
default_release_threshold = 2;
current_release_threshold = 2;
default_release_time = 5;
current_release_time = 5;
attack_threshold->SetMinimum(1);
attack_threshold->setEnabled(true);
attack_threshold->SetDefault(default_attack_threshold);
attack_threshold->SetValue(current_attack_threshold);
attack_time->SetMinimum(1);
attack_time->setEnabled(true);
attack_time->SetDefault(default_attack_time);
attack_time->SetValue(current_attack_time);
release_threshold->SetMinimum(1);
release_threshold->setEnabled(true);
release_threshold->SetDefault(default_release_threshold);
release_threshold->SetValue(current_release_threshold);
release_time->SetMinimum(1);
release_time->setEnabled(true);
release_time->SetDefault(default_release_time);
release_time->SetValue(current_release_time);
return QDialog::exec();
}
void AutoCutSilenceDialog::accept() {
current_attack_threshold = attack_threshold->value();
current_attack_time = attack_time->value();
current_release_threshold = release_threshold->value();
current_release_time = release_time->value();
cut_silence();
update_ui(true);
QDialog::accept();
}
void AutoCutSilenceDialog::cut_silence() {
ComboAction* ca = new ComboAction();
// Loop over clips provided to this dialog
for (int j=0;j<clips_.size();j++) {
Clip* clip = clips_.at(j);
// Check if this clip is an audio footage clip
if (clip->type() == olive::kTypeAudio
&& clip->media() != nullptr
&& clip->media_stream()->preview_done) { // TODO provide warning for preview not being done
QVector<long> split_positions;
long clip_start = clip->timeline_in();
const FootageStream* ms = clip->media_stream();
long media_length = clip->media_length();
int preview_size = ms->audio_preview.length();
float chunk_size = (float)preview_size/media_length; // how many audio samples to read for each fotogram
int sample_size = qMax(current_attack_time, current_release_time)+1;
bool attack = false; // status flags
bool release = false;
QVector<qint8> vols;
vols.resize(sample_size);
vols.fill(0);
// loop through the entire sequence
for (long i=clip_start;i<media_length+clip_start;i++) {
long start = ((i-clip_start)*chunk_size); //audio samples are read relative to the clip, not absolute to the timeline
int circular_index = i%sample_size;
// read the current sample into the circular array
qint8 tmp = 0;
for (long k=start; k<start+chunk_size; k++){
tmp = qMax(tmp, qint8(qRound(double(ms->audio_preview.at(k)))));
}
vols[circular_index] = tmp;
//for debug:
//qInfo() << "i:" << i <<" - "<< i/30 <<":"<< i%30 << " - volume:" << vols[circular_index] <<"\n";
int overthreshold = 0;
int cut_idx = 0; //how much to cut (backwards)
// if current volume value is above threshold
if (vols[circular_index] >= current_attack_threshold && !attack){ // if we get one sample over the threshold
for(int k=0; k<sample_size; k++){ // count how many times this happened before within sample_size range (avoid false activations)
int back_idx = (((circular_index-k)%sample_size)+sample_size)%sample_size; // positive modulus
if(vols[back_idx] > current_attack_threshold){
overthreshold++;
cut_idx = k+1;
}
}
// if we reached threshold over the set tolerance
if(overthreshold >= current_attack_time){
split_positions.append(i-cut_idx);
attack = true;
release = false;
//qInfo() << "\n\n Current vol: "<<vols[circular_index]<<" attack at " << i-cut_idx << "\n\n";
}
overthreshold = 0;
cut_idx = 0;
}else if (vols[circular_index] < current_release_threshold && !release){ // if we get one sample under the threshold
for(int k=0; k<sample_size; k++){ // count how many times this happened before within sample_size range
int back_idx = (((circular_index-k)%sample_size)+sample_size)%sample_size; // positive modulus
if(vols[back_idx] < current_release_threshold)
overthreshold++;
}
// if we reached threshold over the set tolerance
if(overthreshold >= current_release_time){ // must be <= sample_size
attack = false;
release = true;
split_positions.append(i);
//qInfo() << "\n\n Current vol: "<<vols[circular_index]<<" release at " << i << "\n\n";
}
overthreshold = 0;
}
}
clip->track()->sequence()->SplitClipAtPositions(ca, clips_.at(j), split_positions);
}
}
if (ca->hasActions()) {
olive::undo_stack.push(ca);
} else {
delete ca;
}
}
-59
View File
@@ -1,59 +0,0 @@
/***
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 SILENCEDIALOG_H
#define SILENCEDIALOG_H
#include <QDialog>
#include <QCheckBox>
#include "timeline/clip.h"
#include "ui/labelslider.h"
class AutoCutSilenceDialog : public QDialog
{
Q_OBJECT
public:
AutoCutSilenceDialog(QWidget* parent, QVector<Clip*> clips);
public slots:
virtual int exec() override;
private slots:
virtual void accept() override;
private:
void cut_silence();
QVector<Clip*> clips_;
LabelSlider* attack_threshold;
LabelSlider* release_threshold;
LabelSlider* attack_time;
LabelSlider* release_time;
int default_attack_threshold;
int current_attack_threshold;
int default_release_threshold;
int current_release_threshold;
int default_attack_time;
int current_attack_time;
int default_release_time;
int current_release_time;
};
#endif // SILENCEDIALOG_H
-145
View File
@@ -1,145 +0,0 @@
/***
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 "clippropertiesdialog.h"
#include <QGridLayout>
#include <QLabel>
#include <QDialogButtonBox>
#include "panels/panels.h"
#include "undo/undo.h"
ClipPropertiesDialog::ClipPropertiesDialog(QWidget *parent, QVector<Clip *> clips) :
QDialog(parent)
{
setWindowTitle((clips.size() == 1) ?
tr("\"%1\" Properties").arg(clips.at(0)->name()) :
tr("Multiple Clip Properties"));
clips_ = clips;
QGridLayout* layout = new QGridLayout(this);
int row = 0;
// Clip Name field
layout->addWidget(new QLabel(tr("Name:")), row, 0);
clip_name_field_ = new QLineEdit();
layout->addWidget(clip_name_field_, row, 1);
row++;
// Clip Duration field
layout->addWidget(new QLabel(tr("Duration:")), row, 0);
duration_field_ = new LabelSlider();
duration_field_->SetDisplayType(LabelSlider::FrameNumber);
duration_field_->SetMinimum(1);
layout->addWidget(duration_field_, row, 1);
row++;
// Dialog buttons (OK and Cancel)
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttons->setCenterButtons(true);
layout->addWidget(buttons, row, 0, 1, 2);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
// analyze list of clips for default values
bool all_clips_have_same_name = true;
bool all_clips_have_same_duration = true;
for (int i=1;i<clips.size();i++) {
if (clips.at(i-1)->name() != clips.at(i)->name()) {
all_clips_have_same_name = false;
}
if (clips.at(i-1)->length() != clips.at(i)->length()) {
all_clips_have_same_duration = false;
}
}
Clip* first_clip = clips_.first();
if (all_clips_have_same_name) {
// if there's only one clip selected, set all defaults to that clip's properties
clip_name_field_->setText(first_clip->name());
} else {
// if there are multiple clips, use different properties
clip_name_field_->setPlaceholderText(tr("(multiple)"));
}
// it's assumed all the clips come from the same sequence
duration_field_->SetFrameRate(first_clip->track()->sequence()->frame_rate());
if (all_clips_have_same_duration) {
duration_field_->SetDefault(first_clip->length());
duration_field_->SetValue(first_clip->length());
duration_field_->SetMaximum(first_clip->media_length());
} else {
duration_field_->SetDefault(qSNaN());
duration_field_->SetValue(qSNaN());
}
}
void ClipPropertiesDialog::accept()
{
const QString& clip_name = clip_name_field_->text();
double clip_duration = duration_field_->value();
ComboAction* ca = new ComboAction();
for (int i=0;i<clips_.size();i++) {
Clip* clip = clips_.at(i);
// If the user entered a Clip name and the name is different from the Clip's name, create a rename command
if (!clip_name.isEmpty() && clip_name != clip->name()) {
ca->append(new RenameClipCommand(clip, clip_name));
}
// If the user entered a clip duration (and the duration has changed), create a "clip move" command
if (!qIsNaN(clip_duration)) {
long clip_duration_rounded = qRound(clip_duration);
if (clip->length() != clip_duration_rounded) {
clip->Move(ca,
clip->timeline_in(),
clip->timeline_in() + clip_duration_rounded,
clip->clip_in(),
clip->track());
}
}
}
if (ca->hasActions()) {
olive::undo_stack.push(ca);
update_ui(false);
} else {
delete ca;
}
QDialog::accept();
}
-72
View File
@@ -1,72 +0,0 @@
/***
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 CLIPPROPERTIESDIALOG_H
#define CLIPPROPERTIESDIALOG_H
#include <QDialog>
#include <QLineEdit>
#include "timeline/clip.h"
#include "ui/labelslider.h"
/**
* @brief The ClipPropertiesDialog class
*
* A dialog for setting Clip properties, accessible by right clicking a Clip and clicking "Properties". This can be
* run from anywhere provided it's given a valid array of Clip objects.
*/
class ClipPropertiesDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief ClipPropertiesDialog Constructor
* @param parent
*
* Parent widget.
*
* @param clips
*
* Array of Clip objects to set the properties of.
*/
ClipPropertiesDialog(QWidget* parent, QVector<Clip*> clips);
protected:
/**
* @brief Accept override. Saves the current properties to the array of Clips.
*/
virtual void accept() override;
private:
/**
* @brief Internal clip array (set in the constructor)
*/
QVector<Clip*> clips_;
/**
* @brief Widget for setting the Clip names
*/
QLineEdit* clip_name_field_;
/**
* @brief Widget for setting the Clip durations
*/
LabelSlider* duration_field_;
};
#endif // CLIPPROPERTIESDIALOG_H
-46
View File
@@ -1,46 +0,0 @@
#include "crashdialog.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QDialogButtonBox>
#include <QScrollBar>
CrashDialog* olive::crash_dialog;
CrashDialog::CrashDialog()
{
resize(480, 640);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(tr("We're very sorry, Olive has crashed. "
"Please send the following data to developers:")));
text_edit = new QTextEdit();
text_edit->setReadOnly(true);
layout->addWidget(text_edit);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
buttons->setCenterButtons(true);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
layout->addWidget(buttons);
// Set some default data for the crash report so we do as little as possible when we actually crash
#ifdef GITHASH
text_edit->append(QString("Version: %1").arg(GITHASH));
#else
text_edit->append(QString("Version: (unknown)"));
#endif
text_edit->append(QString("Operating System: %1 %2").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture()));
text_edit->append(QString("Build ABI: %1").arg(QSysInfo::buildAbi()));
text_edit->append(QString());
}
void CrashDialog::SetData(int signal, const QStringList& strings)
{
text_edit->append(QString("Signal: %1").arg(signal));
text_edit->append(QString());
for (int i=0;i<strings.size();i++) {
text_edit->append(strings.at(i));
}
}
-22
View File
@@ -1,22 +0,0 @@
#ifndef CRASHDIALOG_H
#define CRASHDIALOG_H
#include <QDialog>
#include <QTextEdit>
class CrashDialog : public QDialog
{
public:
CrashDialog();
void SetData(int signal, const QStringList &strings);
private:
QTextEdit* text_edit;
};
namespace olive {
extern CrashDialog* crash_dialog;
}
#endif // CRASHDIALOG_H
-63
View File
@@ -1,63 +0,0 @@
/***
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 "debugdialog.h"
#include <QTextEdit>
#include <QVBoxLayout>
#include <QScrollBar>
#include <QEvent>
#include "global/debug.h"
DebugDialog* olive::DebugDialog = nullptr;
DebugDialog::DebugDialog(QWidget *parent) : QDialog(parent) {
QVBoxLayout* layout = new QVBoxLayout(this);
textEdit = new QTextEdit(this);
textEdit->setWordWrapMode(QTextOption::NoWrap);
layout->addWidget(textEdit);
Retranslate();
}
void DebugDialog::Retranslate()
{
setWindowTitle(tr("Debug Log"));
}
void DebugDialog::update_log() {
textEdit->setHtml(get_debug_str());
textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum());
}
void DebugDialog::changeEvent(QEvent *e)
{
if (e->type() == QEvent::LanguageChange) {
Retranslate();
} else {
QDialog::changeEvent(e);
}
}
void DebugDialog::showEvent(QShowEvent *) {
update_log();
}
-79
View File
@@ -1,79 +0,0 @@
/***
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 DEBUGDIALOG_H
#define DEBUGDIALOG_H
#include <QDialog>
#include <QTextEdit>
/**
* @brief The DebugDialog class
*
* A dialog to display the current debug output. This dialog is omnipresent and shown and hidden when the user wants
* to see it. For efficiency, it will not update if it's hidden.
*/
class DebugDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief DebugDialog Constructor
* @param parent
*
* Parent widget. Usually MainWindow.
*/
DebugDialog(QWidget* parent = nullptr);
/**
* @brief Retranslate window title
*
* Sets title based on the current translation.
*/
void Retranslate();
public slots:
/**
* @brief Update the visual log with the debug text from get_debug_str()
*/
void update_log();
protected:
/**
* @brief Overrides change event to trigger Retranslate() on a LanguageChange event.
*/
virtual void changeEvent(QEvent* e) override;
/**
* @brief Overrides show event to trigger an update of the visual log (the visual log does not update while the
* debug dialog is hidden).
*/
virtual void showEvent(QShowEvent* event) override;
private:
/**
* @brief Display widget for the debug dialog.
*/
QTextEdit* textEdit;
};
namespace olive {
/**
* @brief Omnipresent instance of DebugDialog to be shown or hidden as the user wants
*/
extern DebugDialog* DebugDialog;
}
#endif // DEBUGDIALOG_H
-62
View File
@@ -1,62 +0,0 @@
/***
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 "demonotice.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QDialogButtonBox>
DemoNotice::DemoNotice(QWidget *parent) :
QDialog(parent)
{
setWindowTitle(tr("Welcome to Olive!"));
QVBoxLayout* vlayout = new QVBoxLayout(this);
QHBoxLayout* layout = new QHBoxLayout();
layout->setMargin(10);
layout->setSpacing(20);
QLabel* icon = new QLabel("<html><head/><body>"
"<p><img src=\":/icons/olive-splash.png\"/></p>"
"</body></html>", this);
layout->addWidget(icon);
QLabel* text = new QLabel("<html><head/><body><p>"
"<span style=\" font-size:14pt;\">"
+ tr("Welcome to Olive!")
+ "</span></p><p>"
+ tr("Olive is a free open-source video editor released under the GNU GPL. If you have paid for this software, you have been scammed.")
+ "</p><p>"
+ tr("This software is currently in ALPHA which means it is unstable and very likely to crash, have bugs, and have missing features. We offer no warranty so use at your own risk. Please report any bugs or feature requests at %1").arg("<a href=\"https://olivevideoeditor.org/\"><span style=\" text-decoration: underline; color:#007af4;\">www.olivevideoeditor.org</span></a>")
+ "</p><p>"
+ tr("Thank you for trying Olive and we hope you enjoy it!")
+ "</p></body></html>", this);
text->setWordWrap(true);
layout->addWidget(text);
vlayout->addLayout(layout);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
buttons->setCenterButtons(true);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
vlayout->addWidget(buttons);
}
-47
View File
@@ -1,47 +0,0 @@
/***
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 DEMONOTICE_H
#define DEMONOTICE_H
#include <QDialog>
/**
* @brief The DemoNotice class
*
* Simple dialog shown on startup to introduce Olive as alpha software (in release builds). Can be run from anywhere,
* but there should be no reason to create it outside of the application launch.
*
* To be phased out as Olive gains maturity.
*/
class DemoNotice : public QDialog
{
Q_OBJECT
public:
/**
* @brief DemoNotice Constructor
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
explicit DemoNotice(QWidget *parent = nullptr);
};
#endif // DEMONOTICE_H
-807
View File
@@ -1,807 +0,0 @@
/***
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 "exportdialog.h"
extern "C" {
#include <libavformat/avformat.h>
}
#include <QOpenGLWidget>
#include <QFileDialog>
#include <QThread>
#include <QMessageBox>
#include <QOpenGLContext>
#include <QtMath>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include "global/global.h"
#include "dialogs/advancedvideodialog.h"
#include "panels/panels.h"
#include "ui/viewerwidget.h"
#include "rendering/renderfunctions.h"
#include "rendering/audio.h"
#include "rendering/exportthread.h"
#include "ui/mainwindow.h"
enum ExportFormats {
FORMAT_3GPP,
FORMAT_AIFF,
FORMAT_APNG,
FORMAT_AVI,
FORMAT_DNXHD,
FORMAT_AC3,
FORMAT_FLV,
FORMAT_GIF,
FORMAT_IMG,
FORMAT_MP2,
FORMAT_MP3,
FORMAT_MPEG1,
FORMAT_MPEG2,
FORMAT_MPEG4,
FORMAT_MPEGTS,
FORMAT_MKV,
FORMAT_OGG,
FORMAT_MOV,
FORMAT_WAV,
FORMAT_WEBM,
FORMAT_WMV,
FORMAT_SIZE
};
ExportDialog::ExportDialog(QWidget *parent, Sequence* sequence) :
QDialog(parent),
sequence_(sequence)
{
setWindowTitle(tr("Export \"%1\"").arg(sequence->name()));
setup_ui();
rangeCombobox->setCurrentIndex(0);
if (sequence->using_workarea) {
rangeCombobox->setEnabled(true);
rangeCombobox->setCurrentIndex(1);
}
format_strings.resize(FORMAT_SIZE);
format_strings[FORMAT_3GPP] = "3GPP";
format_strings[FORMAT_AIFF] = "AIFF";
format_strings[FORMAT_APNG] = "Animated PNG";
format_strings[FORMAT_AVI] = "AVI";
format_strings[FORMAT_DNXHD] = "DNxHD";
format_strings[FORMAT_AC3] = "Dolby Digital (AC3)";
format_strings[FORMAT_FLV] = "FLV";
format_strings[FORMAT_GIF] = "GIF";
format_strings[FORMAT_IMG] = "Image Sequence";
format_strings[FORMAT_MP2] = "MP2 Audio";
format_strings[FORMAT_MP3] = "MP3 Audio";
format_strings[FORMAT_MPEG1] = "MPEG-1 Video";
format_strings[FORMAT_MPEG2] = "MPEG-2 Video";
format_strings[FORMAT_MPEG4] = "MPEG-4 Video";
format_strings[FORMAT_MPEGTS] = "MPEG-TS";
format_strings[FORMAT_MKV] = "Matroska MKV";
format_strings[FORMAT_OGG] = "Ogg";
format_strings[FORMAT_MOV] = "QuickTime MOV";
format_strings[FORMAT_WAV] = "WAVE Audio";
format_strings[FORMAT_WEBM] = "WebM";
format_strings[FORMAT_WMV] = "Windows Media";
for (int i=0;i<FORMAT_SIZE;i++) {
formatCombobox->addItem(format_strings[i]);
}
formatCombobox->setCurrentIndex(FORMAT_MPEG4);
// default to sequence's native dimensions
widthSpinbox->setValue(sequence->width());
heightSpinbox->setValue(sequence->height());
samplingRateSpinbox->setValue(sequence->audio_frequency());
framerateSpinbox->setValue(sequence->frame_rate());
// set some advanced defaults
vcodec_params.threads = 0;
}
void ExportDialog::add_codec_to_combobox(QComboBox* box, enum AVCodecID codec) {
QString codec_name;
AVCodec* codec_info = avcodec_find_encoder(codec);
if (codec_info == nullptr) {
codec_name = tr("Unknown codec name %1").arg(static_cast<int>(codec));
} else {
codec_name = codec_info->long_name;
}
box->addItem(codec_name, codec);
}
void ExportDialog::format_changed(int index) {
vcodecCombobox->clear();
acodecCombobox->clear();
int default_vcodec = 0;
int default_acodec = 0;
switch (index) {
case FORMAT_3GPP:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H265);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC);
default_vcodec = 1;
break;
case FORMAT_AIFF:
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE);
break;
case FORMAT_APNG:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_APNG);
break;
case FORMAT_AVI:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MJPEG);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MSVIDEO1);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_RAWVIDEO);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_HUFFYUV);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_DVVIDEO);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_FLAC);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE);
default_vcodec = 3;
default_acodec = 5;
break;
case FORMAT_DNXHD:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_DNXHD);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE);
break;
case FORMAT_AC3:
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_EAC3);
break;
case FORMAT_FLV:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_FLV1);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
break;
case FORMAT_GIF:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_GIF);
break;
case FORMAT_IMG:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_BMP);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MJPEG);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_JPEG2000);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_PSD);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_PNG);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_TIFF);
default_vcodec = 4;
break;
case FORMAT_MP2:
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2);
break;
case FORMAT_MP3:
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
break;
case FORMAT_MPEG1:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG1VIDEO);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE);
default_acodec = 1;
break;
case FORMAT_MPEG2:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG2VIDEO);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE);
default_acodec = 1;
break;
case FORMAT_MPEG4:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H265);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
default_vcodec = 1;
break;
case FORMAT_MPEGTS:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG2VIDEO);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
default_acodec = 2;
break;
case FORMAT_MKV:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H265);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_EAC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_FLAC);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_OPUS);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_VORBIS);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WAVPACK);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WMAV1);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WMAV2);
default_vcodec = 1;
break;
case FORMAT_OGG:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_THEORA);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_OPUS);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_VORBIS);
default_acodec = 1;
break;
case FORMAT_MOV:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_QTRLE);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H265);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MJPEG);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_PRORES);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE);
default_vcodec = 2;
break;
case FORMAT_WAV:
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE);
break;
case FORMAT_WEBM:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_VP8);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_VP9);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_OPUS);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_VORBIS);
default_vcodec = 1;
break;
case FORMAT_WMV:
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_WMV1);
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_WMV2);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WMAV1);
add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WMAV2);
default_vcodec = 1;
default_acodec = 1;
break;
default:
qCritical() << "Invalid format selection - this is a bug, please inform the developers";
}
vcodecCombobox->setCurrentIndex(default_vcodec);
acodecCombobox->setCurrentIndex(default_acodec);
bool video_enabled = vcodecCombobox->count() != 0;
bool audio_enabled = acodecCombobox->count() != 0;
videoGroupbox->setChecked(video_enabled);
audioGroupbox->setChecked(audio_enabled);
videoGroupbox->setEnabled(video_enabled);
audioGroupbox->setEnabled(audio_enabled);
}
void ExportDialog::export_thread_finished() {
// Determine if the export succeeded
bool succeeded = (progressBar->value() == 100);
// If it failed and we didn't cancel it, it must have errored out. Show an error message.
if (!succeeded && !export_thread_->WasInterrupted()) {
QMessageBox::critical(
this,
tr("Export Failed"),
tr("Export failed - %1").arg(export_thread_->GetError()),
QMessageBox::Ok
);
}
// Clear audio buffer
clear_audio_ibuffer();
// Re-enable/disable UI widgets based on the rendering state
prep_ui_for_render(false);
// Move OpenGL context back to the sequence viewer
panel_sequence_viewer->viewer_widget()->makeCurrent();
panel_sequence_viewer->viewer_widget()->initializeGL();
// Update the application UI
update_ui(false);
// Disconnect cancel button from export thread
disconnect(renderCancel, SIGNAL(clicked(bool)), export_thread_, SLOT(Interrupt()));
// Free the export thread
export_thread_->deleteLater();
// If the export succeeded, close the dialog
if (succeeded) {
accept();
}
}
void ExportDialog::prep_ui_for_render(bool r) {
export_button->setEnabled(!r);
cancel_button->setEnabled(!r);
videoGroupbox->setEnabled(!r);
audioGroupbox->setEnabled(!r);
renderCancel->setEnabled(r);
}
void ExportDialog::StartExport() {
if (widthSpinbox->value()%2 == 1 || heightSpinbox->value()%2 == 1) {
QMessageBox::critical(
this,
tr("Invalid dimensions"),
tr("Export width and height must both be even numbers/divisible by 2."),
QMessageBox::Ok
);
return;
}
QString ext;
switch (formatCombobox->currentIndex()) {
case FORMAT_3GPP:
ext = "3gp";
break;
case FORMAT_AIFF:
ext = "aiff";
break;
case FORMAT_APNG:
ext = "apng";
break;
case FORMAT_AVI:
ext = "avi";
break;
case FORMAT_DNXHD:
ext = "mxf";
break;
case FORMAT_AC3:
ext = "ac3";
break;
case FORMAT_FLV:
ext = "flv";
break;
case FORMAT_GIF:
ext = "gif";
break;
case FORMAT_IMG:
switch (vcodecCombobox->currentData().toInt()) {
case AV_CODEC_ID_BMP:
ext = "bmp";
break;
case AV_CODEC_ID_MJPEG:
ext = "jpg";
break;
case AV_CODEC_ID_JPEG2000:
ext = "jp2";
break;
case AV_CODEC_ID_PSD:
ext = "psd";
break;
case AV_CODEC_ID_PNG:
ext = "png";
break;
case AV_CODEC_ID_TIFF:
ext = "tif";
break;
default:
qCritical() << "Invalid codec selection for an image sequence";
QMessageBox::critical(
this,
tr("Invalid codec"),
tr("Couldn't determine output parameters for the selected codec. This is a bug, please contact the developers."),
QMessageBox::Ok
);
return;
}
break;
case FORMAT_MP3:
ext = "mp3";
break;
case FORMAT_MPEG1:
if (videoGroupbox->isChecked() && !audioGroupbox->isChecked()) {
ext = "m1v";
} else if (!videoGroupbox->isChecked() && audioGroupbox->isChecked()) {
ext = "m1a";
} else {
ext = "mpg";
}
break;
case FORMAT_MPEG2:
if (videoGroupbox->isChecked() && !audioGroupbox->isChecked()) {
ext = "m2v";
} else if (!videoGroupbox->isChecked() && audioGroupbox->isChecked()) {
ext = "m2a";
} else {
ext = "mpg";
}
break;
case FORMAT_MPEG4:
if (videoGroupbox->isChecked() && !audioGroupbox->isChecked()) {
ext = "m4v";
} else if (!videoGroupbox->isChecked() && audioGroupbox->isChecked()) {
ext = "m4a";
} else {
ext = "mp4";
}
break;
case FORMAT_MPEGTS:
ext = "ts";
break;
case FORMAT_MKV:
if (!videoGroupbox->isChecked()) {
ext = "mka";
} else {
ext = "mkv";
}
break;
case FORMAT_OGG:
ext = "ogg";
break;
case FORMAT_MOV:
ext = "mov";
break;
case FORMAT_WAV:
ext = "wav";
break;
case FORMAT_WEBM:
ext = "webm";
break;
case FORMAT_WMV:
if (videoGroupbox->isChecked()) {
ext = "wmv";
} else {
ext = "wma";
}
break;
default:
qCritical() << "Invalid format - this is a bug, please inform the developers";
QMessageBox::critical(
this,
tr("Invalid format"),
tr("Couldn't determine output format. This is a bug, please contact the developers."),
QMessageBox::Ok
);
return;
}
QString filename = QFileDialog::getSaveFileName(
this,
tr("Export Media"),
"",
format_strings[formatCombobox->currentIndex()] + " (*." + ext + ")"
);
if (!filename.isEmpty()) {
if (!filename.endsWith("." + ext, Qt::CaseInsensitive)) {
filename += "." + ext;
}
if (formatCombobox->currentIndex() == FORMAT_IMG) {
int ext_location = filename.lastIndexOf('.');
if (ext_location > filename.lastIndexOf('/')) {
filename.insert(ext_location, 'd');
filename.insert(ext_location, '5');
filename.insert(ext_location, '0');
filename.insert(ext_location, '%');
}
}
// Set up export parameters to send to the ExportThread
ExportParams params;
params.sequence = sequence_;
params.filename = filename;
params.video_enabled = videoGroupbox->isChecked();
if (params.video_enabled) {
params.video_codec = vcodecCombobox->currentData().toInt();
params.video_width = widthSpinbox->value();
params.video_height = heightSpinbox->value();
params.video_frame_rate = framerateSpinbox->value();
params.video_compression_type = compressionTypeCombobox->currentData().toInt();
params.video_bitrate = videobitrateSpinbox->value();
}
params.audio_enabled = audioGroupbox->isChecked();
if (params.audio_enabled) {
params.audio_codec = acodecCombobox->currentData().toInt();
params.audio_sampling_rate = samplingRateSpinbox->value();
params.audio_bitrate = audiobitrateSpinbox->value();
}
params.start_frame = 0;
params.end_frame = sequence_->GetEndFrame(); // entire sequence
if (rangeCombobox->currentIndex() == 1) {
params.start_frame = qMax(sequence_->workarea_in, params.start_frame);
params.end_frame = qMin(sequence_->workarea_out, params.end_frame);
}
// Create export thread
export_thread_ = new ExportThread(params, vcodec_params, this);
// Connect export thread signals/slots
connect(export_thread_, SIGNAL(finished()), this, SLOT(export_thread_finished()));
connect(export_thread_, SIGNAL(ProgressChanged(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
connect(renderCancel, SIGNAL(clicked(bool)), export_thread_, SLOT(Interrupt()));
// Close all effects in effect controls (prevents UI threading issues)
panel_effect_controls->Clear();
// Close all currently open clips
sequence_->Close();
olive::Global->set_export_state(true);
olive::Global->save_autorecovery_file();
prep_ui_for_render(true);
total_export_time_start = QDateTime::currentMSecsSinceEpoch();
export_thread_->start();
}
}
void ExportDialog::update_progress_bar(int value, qint64 remaining_ms) {
if (value == 100) {
// if value is 100%, show total render time rather than remaining
remaining_ms = QDateTime::currentMSecsSinceEpoch() - total_export_time_start;
}
// convert ms to H:MM:SS
int seconds = qFloor(remaining_ms*0.001)%60;
int minutes = qFloor(remaining_ms/60000)%60;
int hours = qFloor(remaining_ms/3600000);
if (value == 100) {
// show value as "total"
progressBar->setFormat(tr("%p% (Total: %1:%2:%3)").arg(QString::number(hours),
QString::number(minutes).rightJustified(2, '0'),
QString::number(seconds).rightJustified(2, '0')));
} else {
// show value as "remaining"
progressBar->setFormat(tr("%p% (ETA: %1:%2:%3)").arg(QString::number(hours),
QString::number(minutes).rightJustified(2, '0'),
QString::number(seconds).rightJustified(2, '0')));
}
progressBar->setValue(value);
}
void ExportDialog::vcodec_changed(int index) {
compressionTypeCombobox->clear();
if (vcodecCombobox->count() > 0) {
if (vcodecCombobox->itemData(index) == AV_CODEC_ID_H264
|| vcodecCombobox->itemData(index) == AV_CODEC_ID_H265) {
compressionTypeCombobox->setEnabled(true);
compressionTypeCombobox->addItem(tr("Quality-based (Constant Rate Factor)"), COMPRESSION_TYPE_CFR);
// compressionTypeCombobox->addItem("File size-based (Two-Pass)", COMPRESSION_TYPE_TARGETSIZE);
// compressionTypeCombobox->addItem("Average bitrate (Two-Pass)", COMPRESSION_TYPE_TARGETBR);
} else {
compressionTypeCombobox->addItem(tr("Constant Bitrate"), COMPRESSION_TYPE_CBR);
compressionTypeCombobox->setCurrentIndex(0);
compressionTypeCombobox->setEnabled(false);
}
// set default pix_fmt for this codec
AVCodec* codec_info = avcodec_find_encoder(static_cast<AVCodecID>(vcodecCombobox->itemData(index).toInt()));
if (codec_info == nullptr) {
QMessageBox::critical(this,
tr("Invalid Codec"),
tr("Failed to find a suitable encoder for this codec. Export will likely fail."));
} else {
vcodec_params.pix_fmt = codec_info->pix_fmts[0];
if (vcodec_params.pix_fmt == -1) {
QMessageBox::critical(this,
tr("Invalid Codec"),
tr("Failed to find pixel format for this encoder. Export will likely fail."));
}
}
}
}
void ExportDialog::comp_type_changed(int) {
videobitrateSpinbox->setToolTip("");
videobitrateSpinbox->setMinimum(0);
videobitrateSpinbox->setMaximum(99.99);
switch (compressionTypeCombobox->currentData().toInt()) {
case COMPRESSION_TYPE_CBR:
case COMPRESSION_TYPE_TARGETBR:
videoBitrateLabel->setText(tr("Bitrate (Mbps):"));
videobitrateSpinbox->setValue(qMax(0.5, (double) qRound((0.01528 * sequence_->height()) - 4.5)));
break;
case COMPRESSION_TYPE_CFR:
videoBitrateLabel->setText(tr("Quality (CRF):"));
videobitrateSpinbox->setValue(23);
videobitrateSpinbox->setMaximum(51);
videobitrateSpinbox->setToolTip(tr("Quality Factor:\n\n0 = lossless\n17-18 = visually lossless (compressed, but unnoticeable)\n23 = high quality\n51 = lowest quality possible"));
break;
case COMPRESSION_TYPE_TARGETSIZE:
videoBitrateLabel->setText(tr("Target File Size (MB):"));
videobitrateSpinbox->setValue(100);
break;
}
}
void ExportDialog::open_advanced_video_dialog() {
AdvancedVideoDialog avd(this, static_cast<AVCodecID>(vcodecCombobox->currentData().toInt()), vcodec_params);
avd.exec();
}
void ExportDialog::setup_ui() {
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
QHBoxLayout* format_layout = new QHBoxLayout();
format_layout->addWidget(new QLabel(tr("Format:"), this));
formatCombobox = new QComboBox();
format_layout->addWidget(formatCombobox);
verticalLayout->addLayout(format_layout);
QHBoxLayout* range_layout = new QHBoxLayout();
range_layout->addWidget(new QLabel(tr("Range:"), this));
rangeCombobox = new QComboBox(this);
rangeCombobox->addItem(tr("Entire Sequence"));
rangeCombobox->addItem(tr("In to Out"));
range_layout->addWidget(rangeCombobox);
verticalLayout->addLayout(range_layout);
videoGroupbox = new QGroupBox(this);
videoGroupbox->setTitle(tr("Video"));
videoGroupbox->setFlat(false);
videoGroupbox->setCheckable(true);
QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox);
videoGridLayout->addWidget(new QLabel(tr("Codec:"), this), 0, 0, 1, 1);
vcodecCombobox = new QComboBox(videoGroupbox);
videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1);
videoGridLayout->addWidget(new QLabel(tr("Width:"), this), 1, 0, 1, 1);
widthSpinbox = new QSpinBox(videoGroupbox);
widthSpinbox->setMaximum(16777216);
videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1);
videoGridLayout->addWidget(new QLabel(tr("Height:"), this), 2, 0, 1, 1);
heightSpinbox = new QSpinBox(videoGroupbox);
heightSpinbox->setMaximum(16777216);
videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1);
videoGridLayout->addWidget(new QLabel(tr("Frame Rate:"), this), 3, 0, 1, 1);
framerateSpinbox = new QDoubleSpinBox(videoGroupbox);
framerateSpinbox->setMaximum(60);
framerateSpinbox->setValue(0);
videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1);
videoGridLayout->addWidget(new QLabel(tr("Compression Type:"), this), 4, 0, 1, 1);
compressionTypeCombobox = new QComboBox(videoGroupbox);
videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1);
videoBitrateLabel = new QLabel(videoGroupbox);
videoGridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1);
videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox);
videobitrateSpinbox->setMaximum(100);
videobitrateSpinbox->setValue(2);
videoGridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1);
QPushButton* advanced_video_button = new QPushButton(tr("Advanced"));
connect(advanced_video_button, SIGNAL(clicked(bool)), this, SLOT(open_advanced_video_dialog()));
videoGridLayout->addWidget(advanced_video_button, 6, 1);
verticalLayout->addWidget(videoGroupbox);
audioGroupbox = new QGroupBox(this);
audioGroupbox->setTitle(tr("Audio"));
audioGroupbox->setCheckable(true);
QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox);
audioGridLayout->addWidget(new QLabel(tr("Codec:"), this), 0, 0, 1, 1);
acodecCombobox = new QComboBox(audioGroupbox);
audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1);
audioGridLayout->addWidget(new QLabel(tr("Sampling Rate:"), this), 1, 0, 1, 1);
samplingRateSpinbox = new QSpinBox(audioGroupbox);
samplingRateSpinbox->setMaximum(96000);
samplingRateSpinbox->setValue(0);
audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1);
audioGridLayout->addWidget(new QLabel(tr("Bitrate (Kbps/CBR):"), this), 3, 0, 1, 1);
audiobitrateSpinbox = new QSpinBox(audioGroupbox);
audiobitrateSpinbox->setMaximum(320);
audiobitrateSpinbox->setValue(256);
audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1);
verticalLayout->addWidget(audioGroupbox);
QHBoxLayout* progressLayout = new QHBoxLayout();
progressBar = new QProgressBar(this);
progressBar->setFormat("%p% (ETA: 0:00:00)");
progressBar->setEnabled(false);
progressBar->setValue(0);
progressLayout->addWidget(progressBar);
renderCancel = new QPushButton(this);
renderCancel->setIcon(QIcon(":/icons/error.svg"));
renderCancel->setEnabled(false);
progressLayout->addWidget(renderCancel);
verticalLayout->addLayout(progressLayout);
QHBoxLayout* buttonLayout = new QHBoxLayout();
buttonLayout->addStretch();
export_button = new QPushButton(this);
export_button->setText("Export");
connect(export_button, SIGNAL(clicked(bool)), this, SLOT(StartExport()));
buttonLayout->addWidget(export_button);
cancel_button = new QPushButton(this);
cancel_button->setText("Cancel");
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject()));
buttonLayout->addWidget(cancel_button);
buttonLayout->addStretch();
verticalLayout->addLayout(buttonLayout);
connect(formatCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(format_changed(int)));
connect(compressionTypeCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(comp_type_changed(int)));
connect(vcodecCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(vcodec_changed(int)));
}
-277
View File
@@ -1,277 +0,0 @@
/***
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 EXPORTDIALOG_H
#define EXPORTDIALOG_H
#include <QDialog>
#include <QComboBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QLabel>
#include <QProgressBar>
#include <QGroupBox>
#include "timeline/sequence.h"
#include "rendering/exportthread.h"
/**
* @brief The ExportDialog class
*
* The dialog to initiate an export. Requires a valid Sequence to be set in olive::ActiveSequence or the result is
* defined (most likely a crash), so you should always do a `nullptr` check on olive::ActiveSequence before constructing
* this dialog.
*/
class ExportDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief ExportDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
explicit ExportDialog(QWidget *parent, Sequence *sequence);
private slots:
/**
* @brief Slot for when the user changes the format
*
* Used to populate the available codecs list for this format.
*
* @param index
*
* Current format index (corresponding to enum ExportFormats)
*/
void format_changed(int index);
/**
* @brief Slot for when the user clicks the Export button
*
* Asks the user for the file to save to.
*/
void StartExport();
/**
* @brief Slot for the export thread to update the progress bar's value
*
* @param value
*
* An value between 0 - 100. A percentage of the Sequence that has been exported so far.
*
* @param remaining_ms
*
* The estimated time in milliseconds that it will take to complete the rest of the Sequence.
*/
void update_progress_bar(int value, qint64 remaining_ms);
/**
* @brief Slot for the export thread completing (both succeeding and failing)
*
* Runs whenever the thread has finished. Determines whether the thread succeeded or not (and shows an error message
* if not), cleans up the ExportThread object, sets the UI state back to normal.
*
* Connect to ExportThread::finished().
*/
void export_thread_finished();
/**
* @brief Slot for when the video codec changes
*
* Some video codecs require different settings. In the case of that, this function sorts through those.
*
* @param index
*
* Current vcodecCombobox index - its item data contains the AVCodecID.
*/
void vcodec_changed(int index);
/**
* @brief Slot for when the compression type changes
*
* Different UI objects should be displayed for different compression types.
*
* @param index
*
* Unused.
*/
void comp_type_changed(int index);
/**
* @brief Slot to open the Advanced Video Dialog
*
* Opens a dialog for setting more advanced video settings and passes a reference to vcodec_params to it.
*/
void open_advanced_video_dialog();
private:
/**
* @brief Function to create UI objects.
*/
void setup_ui();
/**
* @brief Enables/disables certain UI objects based on the exporting state.
*
* Some UI controls don't need to be set while exporting. This function enables/disables them appropriately.
*
* @param r
*
* TRUE if we're exporting, FALSE if we finished.
*/
void prep_ui_for_render(bool r);
/**
* @brief Retrieves the human-readable name of an AVCodecID and adds it to a QComboBox
*
* Also sets that item's data to the AVCodecID so it can be retrieved directly from the QComboBox.
*
* @param box
*
* The QComboBox to add the item to.
*
* @param codec
*
* The codec to add to the QComboBox.
*/
void add_codec_to_combobox(QComboBox* box, enum AVCodecID codec);
/**
* @brief Internal array of human-readable names corresponding to enum ExportFormats
*/
QVector<QString> format_strings;
/**
* @brief Pointer to an ExportThread
*
* Set when exporting starts, and deleted by export_thread_finished() when the thread is complete.
*/
ExportThread* export_thread_;
/**
* @brief Struct for advanced video codec parameters.
*
* More advanced video encoding parameters to be sent to the ExportThread. These variables are not directly editable
* in this dialog, instead calling open_advanced_video_dialog() will open an AdvancedVideoDialog for setting these
* values directly. vcodec_changed() should also set these to the defaults for that codec where appropriate.
*/
VideoCodecParams vcodec_params;
/**
* @brief ComboBox for selecting the time range of the Sequence to export
*/
QComboBox* rangeCombobox;
/**
* @brief SpinBox for the exported video's width
*/
QSpinBox* widthSpinbox;
/**
* @brief SpinBox for the exported video's bitrate
*/
QDoubleSpinBox* videobitrateSpinbox;
/**
* @brief Label for the exported video's bitrate - changes depending on the compression type
*/
QLabel* videoBitrateLabel;
/**
* @brief SpinBox for the exported video's frame rate
*/
QDoubleSpinBox* framerateSpinbox;
/**
* @brief ComboBox for the exported video codec
*/
QComboBox* vcodecCombobox;
/**
* @brief ComboBox for the exported audio's codec
*/
QComboBox* acodecCombobox;
/**
* @brief SpinBox for the exported audio's sample rate
*/
QSpinBox* samplingRateSpinbox;
/**
* @brief SpinBox for the exported audio's bitrate
*/
QSpinBox* audiobitrateSpinbox;
/**
* @brief Progress bar for visually showing the export progress
*/
QProgressBar* progressBar;
/**
* @brief ComboBox for the exported video's format
*/
QComboBox* formatCombobox;
/**
* @brief SpinBox for the exported video's height
*/
QSpinBox* heightSpinbox;
/**
* @brief Export button to trigger the start of an export
*/
QPushButton* export_button;
/**
* @brief Dialog cancel button to close this dialog
*/
QPushButton* cancel_button;
/**
* @brief Cancel button to abort the export before completion
*/
QPushButton* renderCancel;
/**
* @brief GroupBox containing all video-related UI objects
*/
QGroupBox* videoGroupbox;
/**
* @brief GroupBox containing all audio-related UI objects
*/
QGroupBox* audioGroupbox;
/**
* @brief ComboBox for the exported video compression type
*/
QComboBox* compressionTypeCombobox;
/**
* @brief Time value set when exporting begins to determine the total duration of the export
*/
qint64 total_export_time_start;
Sequence* sequence_;
};
#endif // EXPORTDIALOG_H
-63
View File
@@ -1,63 +0,0 @@
/***
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 "loaddialog.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include "global/global.h"
#include "panels/panels.h"
#include "ui/sourcetable.h"
#include "ui/mainwindow.h"
LoadDialog::LoadDialog(QWidget *parent) :
QDialog(parent)
{
setWindowTitle(tr("Loading..."));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(tr("Loading '%1'...").arg(olive::ActiveProjectFilename.mid(olive::ActiveProjectFilename.lastIndexOf('/')+1)), this));
bar = new QProgressBar(this);
bar->setValue(0);
layout->addWidget(bar);
QPushButton* cancel_button = new QPushButton(tr("Cancel"), this);
connect(cancel_button, SIGNAL(clicked(bool)), this, SIGNAL(cancel()));
// Wrap cancel button in a horizontal layout so it can be centered
QHBoxLayout* hboxLayout = new QHBoxLayout();
hboxLayout->addStretch();
hboxLayout->addWidget(cancel_button);
hboxLayout->addStretch();
layout->addLayout(hboxLayout);
}
void LoadDialog::setValue(int i)
{
bar->setValue(i);
}
-76
View File
@@ -1,76 +0,0 @@
/***
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 LOADDIALOG_H
#define LOADDIALOG_H
#include <QDialog>
#include <QProgressBar>
#include <QHBoxLayout>
#include "project/projectelements.h"
#include "project/loadthread.h"
/**
* @brief The LoadDialog class
*
* Shows a modal dialog for loading a project. Designed to be connected to a LoadThread object. This dialog should
* generally not be created directly, use OliveGlobal::LoadProject (or its variants) to correctly set up a LoadDialog
* and LoadThread and connect them to each other.
*/
class LoadDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief LoadDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
LoadDialog(QWidget* parent);
public slots:
/**
* @brief Set the progress bar value
*
* Ideally, connect this to LoadThread::report_progress().
*
* @param i
*
* Should be a value between 0-100.
*/
void setValue(int i);
signals:
/**
* @brief Signal emitted when the cancel button is clicked.
*
* Ideally, connect this to LoadThread::cancel();
*/
void cancel();
private:
/**
* @brief Progress bar widget
*/
QProgressBar* bar;
};
#endif // LOADDIALOG_H
-238
View File
@@ -1,238 +0,0 @@
/***
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 "mediapropertiesdialog.h"
#include <QGridLayout>
#include <QLabel>
#include <QComboBox>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QTreeWidgetItem>
#include <QGroupBox>
#include <QListWidget>
#include <QCheckBox>
#include <QSpinBox>
#include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE::v1;
#include "project/footage.h"
#include "project/media.h"
#include "panels/project.h"
#include "undo/undo.h"
#include "undo/undostack.h"
MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) :
QDialog(parent),
item(i)
{
setWindowTitle(tr("\"%1\" Properties").arg(i->get_name()));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QGridLayout* grid = new QGridLayout(this);
int row = 0;
Footage* f = item->to_footage();
grid->addWidget(new QLabel(tr("Tracks:"), this), row, 0, 1, 2);
row++;
track_list = new QListWidget(this);
for (int i=0;i<f->video_tracks.size();i++) {
const FootageStream& fs = f->video_tracks.at(i);
QListWidgetItem* item = new QListWidgetItem(
tr("Video %1: %2x%3 %4FPS").arg(
QString::number(fs.file_index),
QString::number(fs.video_width),
QString::number(fs.video_height),
QString::number(fs.video_frame_rate)
),
track_list
);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole+1, fs.file_index);
track_list->addItem(item);
}
for (int i=0;i<f->audio_tracks.size();i++) {
const FootageStream& fs = f->audio_tracks.at(i);
QListWidgetItem* item = new QListWidgetItem(
tr("Audio %1: %2Hz %3").arg(
QString::number(fs.file_index),
QString::number(fs.audio_frequency),
tr("%n channel(s)", "", fs.audio_channels)
),
track_list
);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole+1, fs.file_index);
track_list->addItem(item);
}
grid->addWidget(track_list, row, 0, 1, 2);
row++;
if (f->video_tracks.size() > 0) {
// frame conforming
if (!f->video_tracks.at(0).infinite_length) {
grid->addWidget(new QLabel(tr("Conform to Frame Rate:"), this), row, 0);
conform_fr = new QDoubleSpinBox(this);
conform_fr->setMinimum(0.01);
conform_fr->setValue(f->video_tracks.at(0).video_frame_rate * f->speed);
grid->addWidget(conform_fr, row, 1);
}
row++;
// premultiplied alpha mode
premultiply_alpha_setting = new QCheckBox(tr("Alpha is Premultiplied"), this);
premultiply_alpha_setting->setChecked(f->alpha_is_associated);
grid->addWidget(premultiply_alpha_setting, row, 0);
row++;
// deinterlacing mode
interlacing_box = new QComboBox(this);
interlacing_box->addItem(
tr("Auto (%1)").arg(
Footage::get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing)
)
);
interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_PROGRESSIVE));
interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_TOP_FIELD_FIRST));
interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST));
interlacing_box->setCurrentIndex(
(f->video_tracks.at(0).video_auto_interlacing == f->video_tracks.at(0).video_interlacing)
? 0
: f->video_tracks.at(0).video_interlacing + 1);
grid->addWidget(new QLabel(tr("Interlacing:"), this), row, 0);
grid->addWidget(interlacing_box, row, 1);
row++;
input_color_space = new QComboBox(this);
OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
QString footage_colorspace = f->Colorspace();
for (int i=0;i<config->getNumColorSpaces();i++) {
QString colorspace = config->getColorSpaceNameByIndex(i);
input_color_space->addItem(colorspace);
if (colorspace == footage_colorspace) {
input_color_space->setCurrentIndex(i);
}
}
grid->addWidget(new QLabel(tr("Color Space:")), row, 0);
grid->addWidget(input_color_space, row, 1);
row++;
}
name_box = new QLineEdit(item->get_name(), this);
grid->addWidget(new QLabel(tr("Name:"), this), row, 0);
grid->addWidget(name_box, row, 1);
row++;
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttons->setCenterButtons(true);
grid->addWidget(buttons, row, 0, 1, 2);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
}
void MediaPropertiesDialog::accept() {
Footage* f = item->to_footage();
ComboAction* ca = new ComboAction();
// set track enable
for (int i=0;i<track_list->count();i++) {
QListWidgetItem* item = track_list->item(i);
const QVariant& data = item->data(Qt::UserRole+1);
if (!data.isNull()) {
int index = data.toInt();
bool found = false;
for (int j=0;j<f->video_tracks.size();j++) {
if (f->video_tracks.at(j).file_index == index) {
f->video_tracks[j].enabled = (item->checkState() == Qt::Checked);
found = true;
break;
}
}
if (!found) {
for (int j=0;j<f->audio_tracks.size();j++) {
if (f->audio_tracks.at(j).file_index == index) {
f->audio_tracks[j].enabled = (item->checkState() == Qt::Checked);
break;
}
}
}
}
}
bool refresh_clips = false;
// set interlacing
if (f->video_tracks.size() > 0) {
if (interlacing_box->currentIndex() > 0) {
ca->append(new SetInt(&f->video_tracks[0].video_interlacing, interlacing_box->currentIndex() - 1));
} else {
ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing));
}
// set frame rate conform
if (!f->video_tracks.at(0).infinite_length) {
if (!qFuzzyCompare(conform_fr->value(), f->video_tracks.at(0).video_frame_rate)) {
ca->append(new SetDouble(&f->speed, f->speed, conform_fr->value()/f->video_tracks.at(0).video_frame_rate));
refresh_clips = true;
}
}
// set premultiplied alpha
f->alpha_is_associated = premultiply_alpha_setting->isChecked();
}
f->SetColorspace(input_color_space->currentText());
// set name
MediaRename* mr = new MediaRename(item, name_box->text());
ca->append(mr);
ca->appendPost(new CloseAllClipsCommand());
ca->appendPost(new UpdateFootageTooltip(item));
if (refresh_clips) {
ca->appendPost(new RefreshClips(item));
}
ca->appendPost(new UpdateViewer());
olive::undo_stack.push(ca);
QDialog::accept();
}
-97
View File
@@ -1,97 +0,0 @@
/***
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 MEDIAPROPERTIESDIALOG_H
#define MEDIAPROPERTIESDIALOG_H
#include <QDialog>
#include <QComboBox>
#include <QLineEdit>
#include <QListWidget>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include "project/footage.h"
#include "project/media.h"
/**
* @brief The MediaPropertiesDialog class
*
* A dialog for setting properties on Media. This can be loaded from any part of the application provided it's given
* a valid Media object.
*/
class MediaPropertiesDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief MediaPropertiesDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow or Project panel.
*
* @param i
*
* Media object to set properties for.
*/
MediaPropertiesDialog(QWidget *parent, Media* i);
private:
/**
* @brief ComboBox for interlacing setting
*/
QComboBox* interlacing_box;
/**
* @brief Media name text field
*/
QLineEdit* name_box;
/**
* @brief Internal pointer to Media object (set in constructor)
*/
Media* item;
/**
* @brief A list widget for listing the tracks in Media
*/
QListWidget* track_list;
/**
* @brief Frame rate to conform to
*/
QDoubleSpinBox* conform_fr;
/**
* @brief Setting for associated/premultiplied alpha
*/
QCheckBox* premultiply_alpha_setting;
/**
* @brief Setting for this media's color space
*/
QComboBox* input_color_space;
private slots:
/**
* @brief Overridden accept function for saving the properties back to the Media class
*/
void accept();
};
#endif // MEDIAPROPERTIESDIALOG_H
-323
View File
@@ -1,323 +0,0 @@
/***
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 "newsequencedialog.h"
#include <QVariant>
#include <QVBoxLayout>
#include <QComboBox>
#include <QGroupBox>
#include <QGridLayout>
#include <QSpinBox>
#include <QLabel>
#include <QLineEdit>
#include <QDialogButtonBox>
#include "panels/panels.h"
#include "panels/project.h"
#include "timeline/sequence.h"
#include "undo/undostack.h"
#include "undo/undo.h"
#include "timeline/clip.h"
#include "panels/timeline.h"
#include "project/media.h"
#include "rendering/audio.h"
#include "global/config.h"
// FIXME: TEST CODE
#include "nodes/nodes/nodemedia.h"
// END TEST CODE
extern "C" {
#include <libavcodec/avcodec.h>
}
NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing, Sequence* iexisting_sequence) :
QDialog(parent),
existing_item(existing),
existing_sequence(iexisting_sequence)
{
Q_ASSERT(!(existing != nullptr && iexisting_sequence != nullptr));
setup_ui();
if (existing != nullptr) {
existing_sequence = existing->to_sequence().get();
}
if (existing_sequence != nullptr) {
setWindowTitle(tr("Editing \"%1\"").arg(existing_sequence->name()));
width_numeric->setValue(existing_sequence->width());
height_numeric->setValue(existing_sequence->height());
int comp_rate = qRound(existing_sequence->frame_rate()*100);
for (int i=0;i<frame_rate_combobox->count();i++) {
if (qRound(frame_rate_combobox->itemData(i).toDouble()*100) == comp_rate) {
frame_rate_combobox->setCurrentIndex(i);
break;
}
}
sequence_name_edit->setText(existing_sequence->name());
for (int i=0;i<audio_frequency_combobox->count();i++) {
if (audio_frequency_combobox->itemData(i) == existing_sequence->audio_frequency()) {
audio_frequency_combobox->setCurrentIndex(i);
break;
}
}
} else {
existing_sequence = nullptr;
setWindowTitle(tr("New Sequence"));
}
}
void NewSequenceDialog::set_sequence_name(const QString& s) {
sequence_name_edit->setText(s);
}
void NewSequenceDialog::SetNameEditable(bool enabled)
{
sequence_name_edit->setVisible(enabled);
sequence_name_label->setVisible(enabled);
}
void NewSequenceDialog::accept() {
if (existing_sequence == nullptr) {
// The dialog wasn't given an existing Sequence object, so we'll make a new one
SequencePtr s = std::make_shared<Sequence>();
s->set_name(sequence_name_edit->text());
s->set_width(width_numeric->value());
s->set_height(height_numeric->value());
s->set_frame_rate(frame_rate_combobox->currentData().toDouble());
s->set_audio_frequency(audio_frequency_combobox->currentData().toInt());
s->set_audio_layout(AV_CH_LAYOUT_STEREO);
ComboAction* ca = new ComboAction();
olive::project_model.CreateSequence(ca, s, true, nullptr);
olive::undo_stack.push(ca);
} else if (existing_item != nullptr) {
// The dialog was given an existing Sequence object, so we'll apply the changes to it
ComboAction* ca = new ComboAction();
double multiplier = frame_rate_combobox->currentData().toDouble() / existing_sequence->frame_rate();
EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_item->to_sequence());
esc->name = sequence_name_edit->text();
esc->width = width_numeric->value();
esc->height = height_numeric->value();
esc->frame_rate = frame_rate_combobox->currentData().toDouble();
esc->audio_frequency = audio_frequency_combobox->currentData().toInt();
esc->audio_layout = AV_CH_LAYOUT_STEREO;
ca->append(esc);
QVector<Clip*> existing_sequence_clips = existing_sequence->GetAllClips();
for (int i=0;i<existing_sequence_clips.size();i++) {
existing_sequence_clips.at(i)->refactor_frame_rate(ca, multiplier, true);
}
olive::undo_stack.push(ca);
} else if (existing_sequence != nullptr) {
// This dialog was given an existing Sequence without a Media wrapper - therefore just directly apply the settings
existing_sequence->set_name(sequence_name_edit->text());
existing_sequence->set_width(width_numeric->value());
existing_sequence->set_height(height_numeric->value());
existing_sequence->set_frame_rate(frame_rate_combobox->currentData().toDouble());
existing_sequence->set_audio_frequency(audio_frequency_combobox->currentData().toInt());
existing_sequence->set_audio_layout(AV_CH_LAYOUT_STEREO);
}
QDialog::accept();
}
void NewSequenceDialog::preset_changed(int index) {
switch (index) {
case 0: // FILM 4K
width_numeric->setValue(4096);
height_numeric->setValue(2160);
break;
case 1: // TV 4K
width_numeric->setValue(3840);
height_numeric->setValue(2160);
break;
case 2: // 1080p
width_numeric->setValue(1920);
height_numeric->setValue(1080);
break;
case 3: // 720p
width_numeric->setValue(1280);
height_numeric->setValue(720);
break;
case 4: // 480p
width_numeric->setValue(640);
height_numeric->setValue(480);
break;
case 5: // 360p
width_numeric->setValue(640);
height_numeric->setValue(360);
break;
case 6: // 240p
width_numeric->setValue(320);
height_numeric->setValue(240);
break;
case 7: // 144p
width_numeric->setValue(192);
height_numeric->setValue(144);
break;
case 8: // NTSC (480i)
width_numeric->setValue(720);
height_numeric->setValue(480);
break;
case 9: // PAL (576i)
width_numeric->setValue(720);
height_numeric->setValue(576);
break;
}
}
void NewSequenceDialog::setup_ui() {
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
QWidget* widget = new QWidget(this);
QHBoxLayout* preset_layout = new QHBoxLayout(widget);
preset_layout->setContentsMargins(0, 0, 0, 0);
preset_layout->addWidget(new QLabel(tr("Preset:"), this));
preset_combobox = new QComboBox(widget);
preset_combobox->addItem(tr("Film 4K"));
preset_combobox->addItem(tr("TV 4K (Ultra HD/2160p)"));
preset_combobox->addItem(tr("1080p"));
preset_combobox->addItem(tr("720p"));
preset_combobox->addItem(tr("480p"));
preset_combobox->addItem(tr("360p"));
preset_combobox->addItem(tr("240p"));
preset_combobox->addItem(tr("144p"));
preset_combobox->addItem(tr("NTSC (480i)"));
preset_combobox->addItem(tr("PAL (576i)"));
preset_combobox->addItem(tr("Custom"));
preset_combobox->setCurrentIndex(2);
preset_layout->addWidget(preset_combobox);
verticalLayout->addWidget(widget);
QGroupBox* videoGroupBox = new QGroupBox(this);
videoGroupBox->setTitle(tr("Video"));
QGridLayout* videoLayout = new QGridLayout(videoGroupBox);
videoLayout->addWidget(new QLabel(tr("Width:"), this), 0, 0, 1, 1);
width_numeric = new QSpinBox(videoGroupBox);
width_numeric->setMaximum(9999);
width_numeric->setValue(olive::config.default_sequence_width);
videoLayout->addWidget(width_numeric, 0, 2, 1, 2);
videoLayout->addWidget(new QLabel(tr("Height:"), this), 1, 0, 1, 2);
height_numeric = new QSpinBox(videoGroupBox);
height_numeric->setMaximum(9999);
height_numeric->setValue(olive::config.default_sequence_height);
videoLayout->addWidget(height_numeric, 1, 2, 1, 2);
videoLayout->addWidget(new QLabel(tr("Frame Rate:"), this), 2, 0, 1, 1);
frame_rate_combobox = new QComboBox(videoGroupBox);
frame_rate_combobox->addItem("10 FPS", 10.0);
frame_rate_combobox->addItem("12.5 FPS", 12.5);
frame_rate_combobox->addItem("15 FPS", 15.0);
frame_rate_combobox->addItem("23.976 FPS", 23.976);
frame_rate_combobox->addItem("24 FPS", 24.0);
frame_rate_combobox->addItem("25 FPS", 25.0);
frame_rate_combobox->addItem("29.97 FPS", 29.97);
frame_rate_combobox->addItem("30 FPS", 30.0);
frame_rate_combobox->addItem("50 FPS", 50.0);
frame_rate_combobox->addItem("59.94 FPS", 59.94);
frame_rate_combobox->addItem("60 FPS", 60.0);
for (int i=0;i<frame_rate_combobox->count();i++) {
if (qFuzzyCompare(frame_rate_combobox->itemData(i).toDouble(), olive::config.default_sequence_framerate)) {
frame_rate_combobox->setCurrentIndex(i);
}
}
videoLayout->addWidget(frame_rate_combobox, 2, 2, 1, 2);
videoLayout->addWidget(new QLabel(tr("Pixel Aspect Ratio:"), this), 4, 0, 1, 1);
par_combobox = new QComboBox(videoGroupBox);
par_combobox->addItem(tr("Square Pixels (1.0)"));
videoLayout->addWidget(par_combobox, 4, 2, 1, 2);
videoLayout->addWidget(new QLabel(tr("Interlacing:"), this), 6, 0, 1, 1);
interlacing_combobox = new QComboBox(videoGroupBox);
interlacing_combobox->addItem(tr("None (Progressive)"));
videoLayout->addWidget(interlacing_combobox, 6, 2, 1, 2);
verticalLayout->addWidget(videoGroupBox);
QGroupBox* audioGroupBox = new QGroupBox(this);
audioGroupBox->setTitle(tr("Audio"));
QGridLayout* audioLayout = new QGridLayout(audioGroupBox);
audioLayout->addWidget(new QLabel(tr("Sample Rate: "), this), 0, 0, 1, 1);
audio_frequency_combobox = new QComboBox(audioGroupBox);
combobox_audio_sample_rates(audio_frequency_combobox);
for (int i=0;i<audio_frequency_combobox->count();i++) {
if (audio_frequency_combobox->itemData(i) == olive::config.default_sequence_audio_frequency) {
audio_frequency_combobox->setCurrentIndex(i);
}
}
audioLayout->addWidget(audio_frequency_combobox, 0, 1, 1, 1);
verticalLayout->addWidget(audioGroupBox);
QWidget* nameWidget = new QWidget(this);
QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget);
nameLayout->setContentsMargins(0, 0, 0, 0);
sequence_name_label = new QLabel(tr("Name:"));
nameLayout->addWidget(sequence_name_label);
sequence_name_edit = new QLineEdit(nameWidget);
nameLayout->addWidget(sequence_name_edit);
verticalLayout->addWidget(nameWidget);
QDialogButtonBox* buttonBox = new QDialogButtonBox(this);
buttonBox->setOrientation(Qt::Horizontal);
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
buttonBox->setCenterButtons(true);
verticalLayout->addWidget(buttonBox);
connect(preset_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(preset_changed(int)));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
-168
View File
@@ -1,168 +0,0 @@
/***
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 NEWSEQUENCEDIALOG_H
#define NEWSEQUENCEDIALOG_H
#include <QDialog>
#include <QComboBox>
#include <QSpinBox>
#include <QLineEdit>
#include "panels/project.h"
#include "project/media.h"
#include "timeline/sequence.h"
/**
* @brief The NewSequenceDialog class
*
* A dialog that creates a new (or edits an existing) Sequence object. Can be run from any part of the application.
*/
class NewSequenceDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief NewSequenceDialog constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*
* @param existing
*
* Set this to a Sequence object (wrapped in a Media object) to edit an existing Sequence,
* or leave as nullptr to create a new one.
*
* @param existing_sequence
*
* If your Sequence object is not wrapped in a Media object, use this to reference a raw Sequence pointer. You must
* not use both existing_sequence AND existing - one must be nullptr.
*/
explicit NewSequenceDialog(QWidget *parent = nullptr, Media* existing = nullptr, Sequence* iexisting_sequence = nullptr);
/**
* @brief Set the name for the new Sequence
*
* If creating a new Sequence, use this function before calling exec() to set what the new Sequence's
* name will be.
*
* The primary use of this is to set a unique default name (i.e. one that doesn't exist
* in the Sequence already) which is done by Project panel. This is usually "Sequence" followed by a number.
*
* @param s
*
* The name to set the new Sequence.
*/
void set_sequence_name(const QString& s);
/**
* @brief Set whether the Sequence's name can be edited
*
* This defaults to TRUE.
*
* @param enabled
*
* TRUE to allow the user to edit the Sequence's name. FALSE if not.
*/
void SetNameEditable(bool enabled);
private slots:
/**
* @brief Override accept function to create/edit a Sequence
*/
virtual void accept() override;
/**
* @brief Slot when the user changes the preset
*
* Sets all values according to the preset chosen.
*
* @param index
*
* Currently selected index of preset_combobox;
*/
void preset_changed(int index);
private:
/**
* @brief Internal reference to an existing Media wrapper (if one was provided to the constructor)
*/
Media* existing_item;
/**
* @brief Internal reference to an existing Sequence (if one was provided to the constructor)
*/
Sequence* existing_sequence;
/**
* @brief Internal function to create the dialog's UI
*/
void setup_ui();
/**
* @brief ComboBox to set the preset
*/
QComboBox* preset_combobox;
/**
* @brief SpinBox to set the Sequence height
*/
QSpinBox* height_numeric;
/**
* @brief SpinBox to set the Sequence width
*/
QSpinBox* width_numeric;
/**
* @brief ComboBox to set the pixel aspect ratio
*/
QComboBox* par_combobox;
/**
* @brief ComboBox to set the interlacing mode
*/
QComboBox* interlacing_combobox;
/**
* @brief ComboBox to set the frame rate
*/
QComboBox* frame_rate_combobox;
/**
* @brief ComboBox to set the audio frequence
*/
QComboBox* audio_frequency_combobox;
/**
* @brief Label marker for setting the Sequence's name
*
* Primarily a persistent class reference so it can be hidden with SetNameEditable() alongside sequence_name_edit.
*/
QLabel* sequence_name_label;
/**
* @brief Line edit to set the Sequence's name
*/
QLineEdit* sequence_name_edit;
};
#endif // NEWSEQUENCEDIALOG_H
File diff suppressed because it is too large Load Diff
-432
View File
@@ -1,432 +0,0 @@
/***
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 PREFERENCESDIALOG_H
#define PREFERENCESDIALOG_H
#include <QDialog>
#include <QKeySequenceEdit>
#include <QMenuBar>
#include <QLineEdit>
#include <QComboBox>
#include <QRadioButton>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QMenu>
#include <QCheckBox>
#include <QDoubleSpinBox>
#include <QSpinBox>
#include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE::v1;
#include "timeline/sequence.h"
class KeySequenceEditor;
/**
* @brief The PreferencesDialog class
*
* A dialog for the global application settings. Mostly an interface for Config. Can be loaded from any part of the
* application.
*/
class PreferencesDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief PreferencesDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
explicit PreferencesDialog(QWidget *parent = nullptr);
private slots:
/**
* @brief Override of accept to save preferences to Config.
*/
virtual void accept() override;
/**
* @brief Reset all selected shortcuts in keyboard_tree to their defaults
*/
void reset_default_shortcut();
/**
* @brief Reset all shortcuts indiscriminately to their defaults
*
* This is safe to call directly as it'll ask the user if they wish to do so before it resets.
*/
void reset_all_shortcuts();
/**
* @brief Shows/hides shortcut entries according to a shortcut query.
*
* This function can be directly connected to QLineEdit::textChanged() for simplicity.
*
* @param s
*
* The search query to compare shortcut names to.
*
* @param parent
*
* This is used as the function calls itself recursively to traverse the menu item hierarchy. This should be left as
* nullptr when called externally.
*
* @return
*
* Value used as function calls itself recursively to determine if a menu parent has any children that are not hidden.
* If so, TRUE is returned so the parent is shown too (even if it doesn't match the search query). If not, FALSE is
* returned so the parent is hidden.
*/
bool refine_shortcut_list(const QString &s, QTreeWidgetItem* parent = nullptr);
/**
* @brief Show a file dialog to load an external shortcut preset from file
*/
void load_shortcut_file();
/**
* @brief Show a file dialog to save an external shortcut preset from file
*/
void save_shortcut_file();
/**
* @brief Delete all previews (waveform and thumbnail cache)
*/
void delete_all_previews();
// Browse for file functionns
/**
* @brief Show a file dialog to browse for an external CSS file to load for styling the application.
*/
void browse_css_file();
void browse_ocio_config();
// OCIO function
void update_ocio_view_menu();
void update_ocio_view_menu(OCIO::ConstConfigRcPtr config);
void update_ocio_config(const QString&);
/**
* @brief Shows a NewSequenceDialog attached to default_sequence
*/
void edit_default_sequence_settings();
private:
/**
* @brief Create and arrange all UI widgets
*/
void setup_ui();
/**
* @brief Populate keyboard shortcut panel with keyboard shortcuts from the menu bar
*
* @param menu
*
* A reference to the main application's menu bar. Usually MainWindow::menuBar().
*/
void setup_kbd_shortcuts(QMenuBar* menu);
/**
* @brief Internal function called by setup_kbd_shortcuts() to traverse down the menu bar's hierarchy and populate the
* shortcut panel.
*
* This function will call itself recursively as it finds submenus belong to the menu provided. It will also create
* QTreeWidgetItems as children of the parent item provided, either using them as parents themselves for submenus
* or attaching a KeySequenceEditor to them for shortcut editing.
*
* @param menu
*
* The current menu to traverse down.
*
* @param parent
*
* The parent item to add QTreeWidgetItems to.
*/
void setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* parent);
enum PreviewDeleteTypes {
DELETE_NONE,
DELETE_THUMBNAILS,
DELETE_WAVEFORMS,
DELETE_BOTH
};
// used to delete previews
// type can be: 't' for thumbnails, 'w' for waveforms, or 1 for all
/**
* @brief Delete disk cached preview files (thumbnails, waveforms, etc.)
*
* @param type
*
* The types of previews to delete.
*/
void delete_previews(PreviewDeleteTypes type);
void populate_ocio_menus(OCIO::ConstConfigRcPtr config);
/**
* @brief UI widget for editing the CSS filename
*/
QLineEdit* custom_css_fn;
/**
* @brief UI widget for editing the list of extensions to detect image sequences from
*/
QLineEdit* imgSeqFormatEdit;
/**
* @brief UI widget for editing the recording channels
*/
QComboBox* recordingComboBox;
/**
* @brief UI widget for editing keyboard shortcuts
*/
QTreeWidget* keyboard_tree;
/**
* @brief UI widget for editing the upcoming queue size
*/
QDoubleSpinBox* upcoming_queue_spinbox;
/**
* @brief UI widget for editing the upcoming queue type
*/
QComboBox* upcoming_queue_type;
/**
* @brief UI widget for editing the previous queue size
*/
QDoubleSpinBox* previous_queue_spinbox;
/**
* @brief UI widget for editing the previous queue type
*/
QComboBox* previous_queue_type;
/**
* @brief UI widget for editing the size of textboxes in the EffectControls panel
*/
QSpinBox* effect_textbox_lines_field;
/**
* @brief UI widget for selecting the output audio device
*/
QComboBox* audio_output_devices;
/**
* @brief UI widget for selecting the input audio device
*/
QComboBox* audio_input_devices;
/**
* @brief UI widget for selecting the audio sampling rates
*/
QComboBox* audio_sample_rate;
/**
* @brief UI widget for selecting the UI language
*/
QComboBox* language_combobox;
/**
* @brief UI widget for selecting the resolution of the thumbnails to generate
*/
QSpinBox* thumbnail_res_spinbox;
/**
* @brief UI widget for selecting the resolution of the waveforms to generate
*/
QSpinBox* waveform_res_spinbox;
QCheckBox* enable_color_management;
QLineEdit* ocio_config_file;
QComboBox* ocio_default_input;
QComboBox* ocio_display;
QComboBox* ocio_view;
QComboBox* ocio_look;
QComboBox* playback_bit_depth;
QComboBox* export_bit_depth;
/**
* @brief UI widget for selecting the current UI style
*/
QComboBox* ui_style;
/**
* @brief Stored default Sequence object
*
* Default Sequence settings are loaded into an actual Sequence object that can be loaded into NewSequenceDialog
* for the sake of familiarity with the user.
*/
Sequence default_sequence;
/**
* @brief List of keyboard shortcut actions that can be triggered (links with key_shortcut_items and
* key_shortcut_fields)
*/
QVector<QAction*> key_shortcut_actions;
/**
* @brief List of keyboard shortcut items in keyboard_tree corresponding to existing actions (links with
* key_shortcut_actions and key_shortcut_fields)
*/
QVector<QTreeWidgetItem*> key_shortcut_items;
/**
* @brief List of keyboard shortcut editing fields in keyboard_tree corresponding to existing actions (links with
* key_shortcut_actions and key_shortcut_fields)
*/
QVector<KeySequenceEditor*> key_shortcut_fields;
/**
* @brief Tests an OpenColorIO configuration file to determine whether it's valid and throws a messagebox if not
*
* @param url
*
* URL to the OpenColorIO configuration file.
*
* @return
*
* A OCIO::ConstConfigRcPtr config pointer if the configuration file is valid, nullptr if not.
*/
OCIO::ConstConfigRcPtr TestOCIOConfig(const QString& url);
/**
* @brief Add an automated QCheckBox+boolean value pair
*
* Many preferences are simple true/false (or on/off) options. Rather than adding a QCheckBox for each one and
* manually setting its checked value to the configuration setting (and vice versa when saving), this convenience
* function will add it to an automated set of checkboxes, automatically setting the checked state to the current
* setting, and then saving the new checked state back to the setting when the user accepts the changes (clicks OK).
*
* @param ui
*
* A valid QCheckBox item. This function does not take ownership of the QWidget or place it in a layout anywhere.
*
* @param value
*
* A pointer to the Boolean value this QCheckBox should be shared with. The QCheckBox widget's checked state will be
* set to the value of this pointer.
*
* @param restart_required
*
* Defaults to FALSE, set this to TRUE if changing this setting should prompt the user for a restart of Olive before
* the setting change takes effect.
*/
void AddBoolPair(QCheckBox* ui, bool* value, bool restart_required = false);
/**
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
*/
QVector<QCheckBox*> bool_ui;
/**
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
*/
QVector<bool*> bool_value;
/**
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
*/
QVector<bool> bool_restart_required;
};
/**
* @brief The KeySequenceEditor class
*
* Simple derived class of QKeySequenceEdit that attaches to a QAction and provides functions for transferring
* keyboard shortcuts to and from it.
*/
class KeySequenceEditor : public QKeySequenceEdit {
Q_OBJECT
public:
/**
* @brief KeySequenceEditor Constructor
*
* @param parent
*
* QWidget parent.
*
* @param a
*
* The QAction to link to. This cannot be changed throughout the lifetime of a KeySequenceEditor.
*/
KeySequenceEditor(QWidget *parent, QAction* a);
/**
* @brief Sets the attached QAction's shortcut to the shortcut entered in this field.
*
* This is not done automatically in case the user cancels out of the Preferences dialog, in which case the
* expectation is that the changes made will not be saved. Therefore, this needs to be triggered manually when
* PreferencesDialog saves.
*/
void set_action_shortcut();
/**
* @brief Set this shortcut back to the QAction's default shortcut
*
* Each QAction contains the default shortcut in its `property("default")` and can be used to restore the default
* "hard-coded" shortcut with this function.
*
* This function does not save the default shortcut back into the QAction, it simply loads the default shortcut from
* the QAction into this edit field. To save it into the QAction, it's necessary to call set_action_shortcut() after
* calling this function.
*/
void reset_to_default();
/**
* @brief Return attached QAction's unique ID
*
* Each of Olive's menu actions has a unique string ID (that, unlike the text, is not translated) for matching with
* an external shortcut configuration file. The ID is stored in the QAction's `property("id")`. This function returns
* that ID.
*
* @return
*
* The QAction's unique ID.
*/
QString action_name();
/**
* @brief Serialize this shortcut entry into a string that can be saved to a file
*
* @return
*
* A string serialization of this shortcut. The format is "[ID]\t[SEQUENCE]" where [ID] is the attached QAction's
* unique identifier and [SEQUENCE] is the current keyboard shortcut in the field (NOT necessarily the shortcut in
* the QAction). If the entered shortcut is the same as the QAction's default shortcut, the return value is empty
* because a default shortcut does not need to be saved to a file.
*/
QString export_shortcut();
private:
/**
* @brief Internal reference to the linked QAction
*/
QAction* action;
};
#endif // PREFERENCESDIALOG_H
-184
View File
@@ -1,184 +0,0 @@
/***
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 "proxydialog.h"
#include <QGridLayout>
#include <QLabel>
#include <QDialogButtonBox>
#include <QComboBox>
#include <QFileDialog>
#include <QMessageBox>
#include <QDebug>
#include "project/proxygenerator.h"
#include "project/footage.h"
#include "ui/mainwindow.h"
#include "global/global.h"
ProxyDialog::ProxyDialog(QWidget *parent, const QVector<Media*> &media) :
QDialog(parent),
selected_media(media)
{
// set dialog title
setWindowTitle(tr("Create Proxy"));
// set proxy folder name to "Proxy", depending on the user's language
proxy_folder_name = tr("Proxy");
// set up dialog's layout
QGridLayout* layout = new QGridLayout(this);
// set the video dimensions of the proxy
layout->addWidget(new QLabel(tr("Dimensions:"), this), 0, 0);
size_combobox = new QComboBox(this);
size_combobox->addItem(tr("Same Size as Source"), 1.0);
size_combobox->addItem(tr("Half Resolution (1/2)"), 0.5);
size_combobox->addItem(tr("Quarter Resolution (1/4)"), 0.25);
size_combobox->addItem(tr("Eighth Resolution (1/8)"), 0.125);
size_combobox->addItem(tr("Sixteenth Resolution (1/16)"), 0.0625);
layout->addWidget(size_combobox, 0, 1);
// set the desired format of the proxy to create
layout->addWidget(new QLabel(tr("Format:"), this), 1, 0);
format_combobox = new QComboBox(this);
format_combobox->addItem(tr("ProRes HQ"));
// format_combobox->addItem(tr("ProRes SQ"));
// format_combobox->addItem(tr("ProRes LT"));
// format_combobox->addItem(tr("DNxHD"));
// format_combobox->addItem(tr("H.264"));
layout->addWidget(format_combobox, 1, 1);
// set the location to place the proxies
layout->addWidget(new QLabel(tr("Location:"), this), 2, 0);
location_combobox = new QComboBox(this);
location_combobox->addItem(tr("Same as Source (in \"%1\" folder)").arg(proxy_folder_name));
location_combobox->addItem("");
connect(location_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(location_changed(int)));
layout->addWidget(location_combobox, 2, 1);
// location_changed will set the default "location" items
location_changed(0);
// set up dialog buttons
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttons->setCenterButtons(true);
layout->addWidget(buttons, 3, 0, 1, 2);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
}
void ProxyDialog::accept() {
QVector<ProxyInfo> info_list;
// set to TRUE if any existing proxies exist and the user chooses to overwrite all of them
bool overwrite_all_existing = false;
for (int i=0;i<selected_media.size();i++) {
// loop through selected footage and send info to the proxy queue
ProxyInfo info;
// fill info struct based on user input
info.media = selected_media.at(i);
info.codec_type = 0;
info.size_multiplier = size_combobox->currentData().toDouble();
Footage* footage = selected_media.at(i)->to_footage();
QString base_footage_fn = QFileInfo(footage->url).baseName();
// TEMPORARILY hardcoded proxy format
base_footage_fn.append(".mov");
// determine path from input
if (custom_location.isEmpty()) {
// use same as source (proxy subfolder)
// generate full path from footage path and proxy_folder_name's translated "Proxy"
info.path = QDir(QFileInfo(footage->url).dir().filePath(proxy_folder_name)).filePath(base_footage_fn);
} else {
// use existing location
info.path = QDir(custom_location).filePath(base_footage_fn);
}
// if the proposed proxy file already exists & user didn't select YesToAll box
if (QFileInfo::exists(info.path) && !overwrite_all_existing){
int rtn = QMessageBox::warning( this,
tr("Proxy file exists"),
tr("The file \"%1\" already exists. Do you wish to replace it?").arg(info.path),
QMessageBox::YesToAll | QMessageBox::Yes | QMessageBox::No);
switch (rtn){
case QMessageBox::Yes:
// continue as normal, proxy generator will automatically overwrite this file
break;
case QMessageBox::YesToAll:
// continue as normal, also set variable so that above messagebox is not shown again
overwrite_all_existing = true;
break;
case QMessageBox::No:
// return to dialog without closing or starting any proxy generation
return;
}
}
// send to proxy generator thread
info_list.append(info);
}
// all proxy info checks out, queue it with the proxy generator
for (int i=0;i<info_list.size();i++) {
Footage* footage = info_list.at(i).media->to_footage();
footage->proxy = true;
footage->proxy_path.clear();
olive::proxy_generator.queue(info_list.at(i));
}
olive::Global->set_modified(true);
QDialog::accept();
}
void ProxyDialog::location_changed(int i) {
// clear any custom location - either the user picked a new one or chose not a custom location
custom_location.clear();
if (i == 1) {
// if the user picked a custom location, ask which directory
QString s = QFileDialog::getExistingDirectory(this);
if (s.isEmpty()) {
// if the user didn't input anything, set the combobox back to default
location_combobox->setCurrentIndex(0);
} else {
// if the user chose a custom location, set the combobox text to it and custom_location for future usage
location_combobox->setItemText(1, s);
custom_location = s;
}
} else {
// if the user doesn't picks something other than custom location, set this string back to default for clearer UX
location_combobox->setItemText(1, tr("Custom Location"));
}
}
-107
View File
@@ -1,107 +0,0 @@
/***
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 PROXYDIALOG_H
#define PROXYDIALOG_H
#include <QDialog>
#include <QVector>
#include <QComboBox>
#include "project/media.h"
/**
* @brief The ProxyDialog class
*
* Dialog to set up proxy generation of footage. This dialog can be called from anywhere provided it's given a valid
* array of Media and will start all proxy generation.
*/
class ProxyDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief ProxyDialog Constructor
* @param parent
*
* Parent widget to become modal to.
*
* @param footage
*
* List of Footage items to process.
*/
ProxyDialog(QWidget* parent, const QVector<Media *> &media);
public slots:
/**
* @brief Accept changes
*
* Called when the user clicks OK on the dialog. Verifies all proxies, asking the user whether they want to overwrite
* existing proxies if necessary, and if everything is valid, queues the footage with ProxyGenerator.
*/
virtual void accept() override;
private:
/**
* @brief User's desired dimensions
*
* Always a fraction of the original video size (e.g. 1/2, 1/4, 1/8, etc.)
*/
QComboBox* size_combobox;
/**
* @brief User's desired proxy format
*
* e.g. ProRes, DNxHD, etc.
*/
QComboBox* format_combobox;
/**
* @brief Allows users to set the directory to store proxies in
*/
QComboBox* location_combobox;
/**
* @brief Stores the custom location to store proxies if the user sets a custom location
*/
QString custom_location;
/**
* @brief Stores the default subdirectory to be made next to the source (dependent on the user's language)
*
* "Proxy" in en-US.
*/
QString proxy_folder_name;
/**
* @brief Stored list of footage to make proxies for
*/
QVector<Media*> selected_media;
private slots:
/**
* @brief Slot when the user changes the location
*
* Triggered when the user changes the index in the location combobox.
*
* @param i
*
* location_combobox's new selected index
*/
void location_changed(int i);
};
#endif // PROXYDIALOG_H
-130
View File
@@ -1,130 +0,0 @@
/***
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 "replaceclipmediadialog.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QMessageBox>
#include "panels/panels.h"
#include "undo/undostack.h"
#include "rendering/cacher.h"
#include "undo/undo.h"
ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media* old_media) :
QDialog(parent),
media(old_media)
{
setWindowTitle(tr("Replace clips using \"%1\"").arg(old_media->get_name()));
resize(300, 400);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(tr("Select which media you want to replace this media's clips with:"), this));
tree = new QTreeView(this);
layout->addWidget(tree);
use_same_media_in_points = new QCheckBox(tr("Keep the same media in-points"), this);
use_same_media_in_points->setChecked(true);
layout->addWidget(use_same_media_in_points);
QHBoxLayout* buttons = new QHBoxLayout();
buttons->addStretch();
QPushButton* replace_button = new QPushButton(tr("Replace"), this);
connect(replace_button, SIGNAL(clicked(bool)), this, SLOT(accept()));
buttons->addWidget(replace_button);
QPushButton* cancel_button = new QPushButton(tr("Cancel"), this);
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject()));
buttons->addWidget(cancel_button);
buttons->addStretch();
layout->addLayout(buttons);
tree->setModel(&olive::project_model);
}
void ReplaceClipMediaDialog::accept() {
QModelIndexList selected_items = tree->selectionModel()->selectedRows();
if (selected_items.size() != 1) {
QMessageBox::critical(
this,
tr("No media selected"),
tr("Please select a media to replace with or click 'Cancel'."),
QMessageBox::Ok
);
} else {
Media* new_item = static_cast<Media*>(selected_items.at(0).internalPointer());
if (media == new_item) {
QMessageBox::critical(
this,
tr("Same media selected"),
tr("You selected the same media that you're replacing. Please select a different one or click 'Cancel'."),
QMessageBox::Ok
);
} else if (new_item->get_type() == MEDIA_TYPE_FOLDER) {
QMessageBox::critical(
this,
tr("Folder selected"),
tr("You cannot replace footage with a folder."),
QMessageBox::Ok
);
} else {
SequencePtr top_sequence = Timeline::GetTopSequence();
if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && top_sequence == new_item->to_sequence()) {
QMessageBox::critical(
this,
tr("Active sequence selected"),
tr("You cannot insert a sequence into itself."),
QMessageBox::Ok
);
} else {
ReplaceClipMediaCommand* rcmc = new ReplaceClipMediaCommand(
media,
new_item,
use_same_media_in_points->isChecked()
);
QVector<Clip*> all_clips = top_sequence->GetAllClips();
for (int i=0;i<all_clips.size();i++) {
Clip* c = all_clips.at(i);
if (c->media() == media) {
rcmc->clips.append(c);
}
}
olive::undo_stack.push(rcmc);
QDialog::accept();
}
}
}
}
-82
View File
@@ -1,82 +0,0 @@
/***
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 REPLACECLIPMEDIADIALOG_H
#define REPLACECLIPMEDIADIALOG_H
#include <QDialog>
#include <QTreeView>
#include <QCheckBox>
#include "ui/sourcetable.h"
#include "project/projectelements.h"
/**
* @brief The ReplaceClipMediaDialog class
*
* A dialog to replace all Clips using a certain Media with a different Media. This dialog can be run from anywhere
* provided it's given a valid Media object.
*/
class ReplaceClipMediaDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief ReplaceClipMediaDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow or Project panel.
*
* @param old_media
*
* A valid Media object which will be used to scan the currently active Sequence for Clips using it.
*/
ReplaceClipMediaDialog(QWidget* parent, Media* old_media);
private slots:
/**
* @brief Overridden accept for when the user clicks "Replace"
*
* Checks whether the requested replace is valid using the following criteria:
* * Any Media is selected
* * The selected Media is not the same Media that the user is trying to replace
* * The Media is not a folder
* * The Media is not the currently active Sequence
*/
virtual void accept() override;
private:
/**
* @brief Internal pointer to the Media we're replacing
*/
Media* media;
/**
* @brief Tree widget to show Project's media
*/
QTreeView* tree;
/**
* @brief CheckBox for using the same media in points
*
* When the starting point of a Clip is trimmed (i.e. the Clip no longer starts at 0),
*/
QCheckBox* use_same_media_in_points;
};
#endif // REPLACECLIPMEDIADIALOG_H
-461
View File
@@ -1,461 +0,0 @@
/***
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 "speeddialog.h"
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QDialogButtonBox>
#include "timeline/sequence.h"
#include "project/footage.h"
#include "rendering/renderfunctions.h"
#include "panels/panels.h"
#include "panels/timeline.h"
#include "undo/undo.h"
#include "undo/undostack.h"
#include "nodes/oldeffectnode.h"
#include "project/media.h"
SpeedDialog::SpeedDialog(QWidget *parent, QVector<Clip*> clips) : QDialog(parent) {
setWindowTitle(tr("Speed/Duration"));
clips_ = clips;
QVBoxLayout* main_layout = new QVBoxLayout(this);
QGridLayout* grid = new QGridLayout();
grid->setSpacing(6);
grid->addWidget(new QLabel(tr("Speed:"), this), 0, 0);
percent = new LabelSlider(this);
percent->SetDecimalPlaces(2);
percent->SetDisplayType(LabelSlider::Percent);
percent->SetDefault(1);
grid->addWidget(percent, 0, 1);
grid->addWidget(new QLabel(tr("Frame Rate:"), this), 1, 0);
frame_rate = new LabelSlider(this);
frame_rate->SetDecimalPlaces(3);
grid->addWidget(frame_rate, 1, 1);
grid->addWidget(new QLabel(tr("Duration:"), this), 2, 0);
duration = new LabelSlider(this);
duration->SetDisplayType(LabelSlider::FrameNumber);
duration->SetFrameRate(clips_.first()->track()->sequence()->frame_rate());
grid->addWidget(duration, 2, 1);
main_layout->addLayout(grid);
reverse = new QCheckBox(tr("Reverse"), this);
maintain_pitch = new QCheckBox(tr("Maintain Audio Pitch"), this);
ripple = new QCheckBox(tr("Ripple Changes"), this);
main_layout->addWidget(reverse);
main_layout->addWidget(maintain_pitch);
main_layout->addWidget(ripple);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttonBox->setCenterButtons(true);
main_layout->addWidget(buttonBox);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(percent, SIGNAL(valueChanged(double)), this, SLOT(percent_update()));
connect(frame_rate, SIGNAL(valueChanged(double)), this, SLOT(frame_rate_update()));
connect(duration, SIGNAL(valueChanged(double)), this, SLOT(duration_update()));
}
int SpeedDialog::exec() {
bool enable_frame_rate = false;
bool multiple_audio = false;
maintain_pitch->setEnabled(false);
double default_frame_rate = qSNaN();
double current_frame_rate = qSNaN();
double current_percent = qSNaN();
long default_length = -1;
long current_length = -1;
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
double clip_percent;
// get default frame rate/percentage
clip_percent = c->speed().value;
if (c->type() == olive::kTypeVideo) {
bool process_video = true;
if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) {
FootageStream* ms = c->media_stream();
if (ms != nullptr && ms->infinite_length) {
process_video = false;
}
}
if (process_video) {
double media_frame_rate = c->media_frame_rate();
// get "default" frame rate"
if (enable_frame_rate) {
// check if frame rate is equal to default
if (!qIsNaN(default_frame_rate) && !qFuzzyCompare(media_frame_rate, default_frame_rate)) {
default_frame_rate = qSNaN();
}
if (!qIsNaN(current_frame_rate) && !qFuzzyCompare(media_frame_rate*c->speed().value, current_frame_rate)) {
current_frame_rate = qSNaN();
}
} else {
default_frame_rate = media_frame_rate;
current_frame_rate = media_frame_rate*c->speed().value;
}
enable_frame_rate = true;
}
} else if (c->type() == olive::kTypeAudio) {
maintain_pitch->setEnabled(true);
if (!multiple_audio) {
maintain_pitch->setChecked(c->speed().maintain_audio_pitch);
multiple_audio = true;
} else if (!maintain_pitch->isTristate() && maintain_pitch->isChecked() != c->speed().maintain_audio_pitch) {
maintain_pitch->setCheckState(Qt::PartiallyChecked);
maintain_pitch->setTristate(true);
}
}
if (i == 0) {
reverse->setChecked(c->reversed());
} else if (c->reversed() != reverse->isChecked()) {
reverse->setTristate(true);
reverse->setCheckState(Qt::PartiallyChecked);
}
// get default length
long clip_default_length = qRound(c->length() * clip_percent);
if (i == 0) {
current_length = c->length();
default_length = clip_default_length;
current_percent = clip_percent;
} else {
if (current_length != -1 && c->length() != current_length) {
current_length = -1;
}
if (default_length != -1 && clip_default_length != default_length) {
default_length = -1;
}
if (!qIsNaN(current_percent) && !qFuzzyCompare(clip_percent, current_percent)) {
current_percent = qSNaN();
}
}
}
frame_rate->SetMinimum(1);
percent->SetMinimum(0.0001);
duration->SetMinimum(1);
frame_rate->setEnabled(enable_frame_rate);
frame_rate->SetDefault(default_frame_rate);
frame_rate->SetValue(current_frame_rate);
percent->SetValue(current_percent);
duration->SetDefault(default_length);
duration->SetValue((current_length == -1) ? qSNaN() : current_length);
return QDialog::exec();
}
void SpeedDialog::percent_update() {
bool got_fr = false;
double fr_val = qSNaN();
long len_val = -1;
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
// get frame rate
if (frame_rate->isEnabled() && c->type() == olive::kTypeVideo) {
double clip_fr = c->media_frame_rate() * percent->value();
if (got_fr) {
if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) {
fr_val = qSNaN();
}
} else {
fr_val = clip_fr;
got_fr = true;
}
}
// get duration
long clip_default_length = qRound(c->length() * c->speed().value);
long new_clip_length = qRound(clip_default_length / percent->value());
if (i == 0) {
len_val = new_clip_length;
} else if (len_val > -1 && len_val != new_clip_length) {
len_val = -1;
}
}
frame_rate->SetValue(fr_val);
duration->SetValue((len_val == -1) ? qSNaN() : len_val);
}
void SpeedDialog::duration_update() {
double pc_val = qSNaN();
bool got_fr = false;
double fr_val = qSNaN();
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
// get percent
long clip_default_length = qRound(c->length() * c->speed().value);
double clip_pc = clip_default_length / duration->value();
if (i == 0) {
pc_val = clip_pc;
} else if (!qIsNaN(pc_val) && !qFuzzyCompare(clip_pc, pc_val)) {
pc_val = qSNaN();
}
// get frame rate
if (frame_rate->isEnabled() && c->type() == olive::kTypeVideo) {
double clip_fr = c->media_frame_rate() * clip_pc;
if (got_fr) {
if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) {
fr_val = qSNaN();
}
} else {
fr_val = clip_fr;
got_fr = true;
}
}
}
frame_rate->SetValue(fr_val);
percent->SetValue(pc_val);
}
void SpeedDialog::frame_rate_update() {
/*double fr = (frame_rate->value());
double pc = (fr / default_frame_rate);
percent->set_value(pc, false);
duration->set_value(default_length / pc, false);*/
double old_pc_val = qSNaN();
bool got_pc_val = false;
double pc_val = qSNaN();
bool got_len_val = false;
long len_val = -1;
// analyze video clips
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
// check if all selected clips are currently the same speed
if (i == 0) {
old_pc_val = c->speed().value;
} else if (!qIsNaN(old_pc_val) && !qFuzzyCompare(c->speed().value, old_pc_val)) {
old_pc_val = qSNaN();
}
if (c->type() == olive::kTypeVideo) {
// what would the new speed be based on this frame rate
double new_clip_speed = frame_rate->value() / c->media_frame_rate();
if (!got_pc_val) {
pc_val = new_clip_speed;
got_pc_val = true;
} else if (!qIsNaN(pc_val) && !qFuzzyCompare(pc_val, new_clip_speed)) {
pc_val = qSNaN();
}
// what would be the new length based on this speed
long new_clip_len = (c->length() * c->speed().value) / new_clip_speed;
if (!got_len_val) {
len_val = new_clip_len;
got_len_val = true;
} else if (len_val > -1 && new_clip_len != len_val) {
len_val = -1;
}
}
}
// analyze audio clips
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
if (c->type() == olive::kTypeAudio) {
long new_clip_len = (qIsNaN(old_pc_val) || qIsNaN(pc_val)) ?
c->length() : qRound((c->length() * c->speed().value) / pc_val);
if (len_val > -1 && new_clip_len != len_val) {
len_val = -1;
break;
}
}
}
percent->SetValue(pc_val);
duration->SetValue((len_val == -1) ? qSNaN() : len_val);
}
void set_speed(ComboAction* ca, Clip* c, double speed, bool ripple, long& ep, long& lr) {
c->track()->DeselectArea(c->timeline_in(), c->timeline_out());
long proposed_out = c->timeline_out();
double multiplier = (c->speed().value / speed);
proposed_out = qRound(c->timeline_in() + (c->length() * multiplier));
ca->append(new SetSpeedAction(c, speed));
if (!ripple && proposed_out > c->timeline_out()) {
QVector<Clip*> all_clips = c->track()->sequence()->GetAllClips();
for (int i=0;i<all_clips.size();i++) {
Clip* compare = all_clips.at(i);
if (compare != nullptr
&& compare->track() == c->track()
&& compare->timeline_in() >= c->timeline_out() && compare->timeline_in() < proposed_out) {
proposed_out = compare->timeline_in();
}
}
}
ep = qMin(ep, c->timeline_out());
lr = qMax(lr, proposed_out - c->timeline_out());
c->track()->sequence()->MoveClip(c,
ca,
c->timeline_in(),
proposed_out,
qRound(c->clip_in() * multiplier),
c->track());
c->refactor_frame_rate(ca, multiplier, false);
c->track()->SelectArea(c->timeline_in(), proposed_out);
}
void SpeedDialog::accept() {
ComboAction* ca = new ComboAction();
// undoable action for setting "maintain audio pitch"
SetClipProperty* audio_pitch_action = new SetClipProperty(kSetClipPropertyMaintainAudioPitch);
// undoable action for setting "reversed"
SetClipProperty* reversed_action = new SetClipProperty(kSetClipPropertyReversed);
// undoable action for restoring clip selections
Sequence* sequence = clips_.first()->track()->sequence();
QVector<Selection> old_selections = sequence->Selections();
// variables used to calculate ripples
long earliest_point = LONG_MAX;
long longest_ripple = LONG_MIN;
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
// make sure the clip is closed while we're making changes
if (c->IsOpen()) {
c->Close(true);
}
// set maintain audio pitch if the user made a selection
if (c->type() == olive::kTypeAudio
&& maintain_pitch->checkState() != Qt::PartiallyChecked
&& c->speed().maintain_audio_pitch != maintain_pitch->isChecked()) {
audio_pitch_action->AddSetting(c, maintain_pitch->isChecked());
}
// set reverse setting if the user made a selection
if (reverse->checkState() != Qt::PartiallyChecked && c->reversed() != reverse->isChecked()) {
long new_clip_in = (c->media_length() - (c->length() + c->clip_in()));
c->track()->sequence()->MoveClip(c,
ca,
c->timeline_in(),
c->timeline_out(),
new_clip_in,
c->track());
c->set_clip_in(new_clip_in);
reversed_action->AddSetting(c, reverse->isChecked());
}
}
// setting the actual speed
if (!qIsNaN(percent->value())) {
// if we have a percentage value, use that on all the clips
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
set_speed(ca, c, percent->value(), ripple->isChecked(), earliest_point, longest_ripple);
}
} else if (!qIsNaN(frame_rate->value())) {
// if the user changed the speed by changing the frame rate,
bool can_change_all = true;
double cached_speed = clips_.first()->speed().value;
double cached_fr = qSNaN();
// see if we can use the frame rate to change all the speeds
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
if (i > 0 && !qFuzzyCompare(cached_speed, c->speed().value)) {
can_change_all = false;
}
if (c->type() == olive::kTypeVideo) {
if (qIsNaN(cached_fr)) {
cached_fr = c->media_frame_rate();
} else if (!qFuzzyCompare(cached_fr, c->media_frame_rate())) {
can_change_all = false;
break;
}
}
}
// make changes
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
if (c->type() == olive::kTypeVideo) {
set_speed(ca, c, frame_rate->value() / c->media_frame_rate(), ripple->isChecked(), earliest_point, longest_ripple);
} else if (can_change_all) {
set_speed(ca, c, frame_rate->value() / cached_fr, ripple->isChecked(), earliest_point, longest_ripple);
}
}
} else if (!qIsNaN(duration->value())) {
// simply set duration
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
set_speed(ca, c, (c->length() * c->speed().value) / duration->value(), ripple->isChecked(), earliest_point, longest_ripple);
}
}
if (ripple->isChecked()) {
sequence->Ripple(ca, earliest_point, longest_ripple);
}
ca->append(new SetSelectionsCommand(sequence, old_selections, sequence->Selections()));
ca->append(reversed_action);
ca->append(audio_pitch_action);
olive::undo_stack.push(ca);
update_ui(true);
QDialog::accept();
}
-132
View File
@@ -1,132 +0,0 @@
/***
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 SPEEDDIALOG_H
#define SPEEDDIALOG_H
#include <QDialog>
#include <QCheckBox>
#include "timeline/clip.h"
#include "ui/labelslider.h"
/**
* @brief The SpeedDialog class
*
* A dialog for setting the speed of one or more Clips. This can be run from anywhere provided it's given a valid
* array of Clips.
*
* It's preferable to
*/
class SpeedDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief SpeedDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow or Timeline panel.
*
* @param clips
*
* A valid array of Clips to change the speed of.
*/
SpeedDialog(QWidget* parent, QVector<Clip*> clips);
public slots:
/**
* @brief Override of exec() to set up current Clip speed data just before opening
*
* @return
*
* The result of QDialog::exec(), a DialogCode result.
*/
virtual int exec() override;
private slots:
/**
* @brief Override of accept() to perform the selected changes on the Clips
*/
virtual void accept() override;
/**
* @brief Slot when the speed percentage field is changed by the user
*
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
* same speed multipler.
*/
void percent_update();
/**
* @brief Slot when the duration field is changed by the user
*
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
* same speed multipler.
*/
void duration_update();
/**
* @brief Slot when the frame rate field is changed by the user
*
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
* same speed multipler.
*/
void frame_rate_update();
private:
/**
* @brief Internal array of Clip objects
*/
QVector<Clip*> clips_;
/**
* @brief Speed percentage field
*/
LabelSlider* percent;
/**
* @brief Duration field
*/
LabelSlider* duration;
/**
* @brief Frame rate field
*/
LabelSlider* frame_rate;
/**
* @brief UI widget for setting the Clip's reverse value
*/
QCheckBox* reverse;
/**
* @brief UI widget for setting the Clip's maintain pitch value
*/
QCheckBox* maintain_pitch;
/**
* @brief UI widget for setting whether to ripple Clips around these changes or not
*/
QCheckBox* ripple;
};
#endif // SPEEDDIALOG_H
-213
View File
@@ -1,213 +0,0 @@
/***
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 "texteditdialog.h"
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QTimer>
#include <QDebug>
#include "ui/icons.h"
TextEditDialog::TextEditDialog(QWidget *parent, const QString &s, bool rich_text) :
QDialog(parent),
rich_text_(rich_text)
{
setWindowTitle(tr("Edit Text"));
QVBoxLayout* layout = new QVBoxLayout(this);
// Create central text editor object
textEdit = new QTextEdit(this);
textEdit->setUndoRedoEnabled(true);
// Upper toolbar
if (rich_text) {
QHBoxLayout* toolbar = new QHBoxLayout();
// Italic Button
italic_button = new QPushButton();
italic_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/italic.svg", false));
italic_button->setCheckable(true);
connect(italic_button, SIGNAL(clicked(bool)), textEdit, SLOT(setFontItalic(bool)));
toolbar->addWidget(italic_button);
// Underline Button
underline_button = new QPushButton();
underline_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/underline.svg", false));
underline_button->setCheckable(true);
connect(underline_button, SIGNAL(clicked(bool)), textEdit, SLOT(setFontUnderline(bool)));
toolbar->addWidget(underline_button);
// Font Name
font_list = new QFontComboBox();
connect(font_list, SIGNAL(currentIndexChanged(const QString&)), textEdit, SLOT(setFontFamily(const QString&)));
toolbar->addWidget(font_list);
// Font Weight
font_weight = new QComboBox();
font_weight->addItem(tr("Thin"), QFont::Thin);
font_weight->addItem(tr("Extra Light"), QFont::ExtraLight);
font_weight->addItem(tr("Light"), QFont::Light);
font_weight->addItem(tr("Normal"), QFont::Normal);
font_weight->addItem(tr("Medium"), QFont::Medium);
font_weight->addItem(tr("Demi Bold"), QFont::DemiBold);
font_weight->addItem(tr("Bold"), QFont::Bold);
font_weight->addItem(tr("Extra Bold"), QFont::ExtraBold);
font_weight->addItem(tr("Black"), QFont::Black);
connect(font_weight, SIGNAL(currentIndexChanged(int)), this, SLOT(SetFontWeight(int)));
toolbar->addWidget(font_weight);
// Font Size
font_size = new LabelSlider();
connect(font_size, SIGNAL(valueChanged(double)), textEdit, SLOT(setFontPointSize(qreal)));
toolbar->addWidget(font_size);
// Font Color
font_color = new ColorButton();
connect(font_color, SIGNAL(color_changed(const QColor&)), textEdit, SLOT(setTextColor(const QColor &)));
toolbar->addWidget(font_color);
toolbar->addStretch();
// Left Align
left_align_button = new QPushButton();
left_align_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/align-left.svg", false));
left_align_button->setCheckable(true);
left_align_button->setProperty("a", Qt::AlignLeft);
connect(left_align_button, SIGNAL(clicked(bool)), this, SLOT(SetAlignmentFromProperty()));
toolbar->addWidget(left_align_button);
// Center Align
center_align_button = new QPushButton();
center_align_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/align-center.svg", false));
center_align_button->setCheckable(true);
center_align_button->setProperty("a", Qt::AlignCenter);
connect(center_align_button, SIGNAL(clicked(bool)), this, SLOT(SetAlignmentFromProperty()));
toolbar->addWidget(center_align_button);
// Right Align
right_align_button = new QPushButton();
right_align_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/align-right.svg", false));
right_align_button->setCheckable(true);
right_align_button->setProperty("a", Qt::AlignRight);
connect(right_align_button, SIGNAL(clicked(bool)), this, SLOT(SetAlignmentFromProperty()));
toolbar->addWidget(right_align_button);
// Justify Align
justify_align_button = new QPushButton();
justify_align_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/justify-center.svg", false));
justify_align_button->setCheckable(true);
justify_align_button->setProperty("a", Qt::AlignJustify);
connect(justify_align_button, SIGNAL(clicked(bool)), this, SLOT(SetAlignmentFromProperty()));
toolbar->addWidget(justify_align_button);
layout->addLayout(toolbar);
}
layout->addWidget(textEdit);
// Lower toolbar
/*
if (rich_text) {
QHBoxLayout* lower_toolbar = new QHBoxLayout();
lower_toolbar->addWidget(new QLabel(tr("Letter Spacing:")));
letter_spacing = new LabelSlider();
connect(letter_spacing, SIGNAL(valueChanged(double)), this, SLOT(SetLetterSpacing(qreal)));
lower_toolbar->addWidget(letter_spacing);
lower_toolbar->addStretch();
layout->addLayout(lower_toolbar);
}
*/
// Create dialog buttons at the bottom
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttons->setCenterButtons(true);
layout->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
// Connect the cursor position changing to the rich text toolbar buttons updating (so for example, when italic text
// is selected, the italic button will be pressed)
connect(textEdit, SIGNAL(cursorPositionChanged()), this, SLOT(UpdateUIFromTextCursor()));
// Set the widget's text based on the rich text mode
if (rich_text_) {
textEdit->setHtml(s);
} else {
textEdit->setPlainText(s);
}
// Helps ensure the UI elements update correctly at the beginning - when the cursor is at the start, the UI elements
// show up blank. Setting it to the end is probably more expected behavior anyway.
textEdit->moveCursor(QTextCursor::End);
}
const QString& TextEditDialog::get_string() {
return result_str;
}
void TextEditDialog::accept() {
result_str = rich_text_ ? textEdit->toHtml() : textEdit->toPlainText();
QDialog::accept();
}
void TextEditDialog::SetFontWeight(int i)
{
textEdit->setFontWeight(font_weight->itemData(i).toInt());
}
void TextEditDialog::SetAlignmentFromProperty()
{
textEdit->setAlignment(static_cast<Qt::Alignment>(sender()->property("a").toInt()));
UpdateUIFromTextCursor();
}
void TextEditDialog::UpdateUIFromTextCursor()
{
if (rich_text_) {
italic_button->setChecked(textEdit->fontItalic());
underline_button->setChecked(textEdit->fontUnderline());
font_list->setCurrentText(textEdit->fontFamily());
font_size->SetValue(textEdit->fontPointSize());
font_color->set_color(textEdit->textColor());
for (int i=0;i<font_weight->count();i++) {
if (font_weight->itemData(i).toInt() == textEdit->fontWeight()) {
font_weight->blockSignals(true);
font_weight->setCurrentIndex(i);
font_weight->blockSignals(false);
break;
}
}
Qt::Alignment align = textEdit->alignment();
left_align_button->setChecked(align == Qt::AlignLeft);
center_align_button->setChecked(align == Qt::AlignCenter);
right_align_button->setChecked(align == Qt::AlignRight);
justify_align_button->setChecked(align == Qt::AlignJustify);
}
}
-179
View File
@@ -1,179 +0,0 @@
/***
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 TEXTEDITDIALOG_H
#define TEXTEDITDIALOG_H
#include <QDialog>
#include <QPlainTextEdit>
#include <QFontComboBox>
#include "ui/labelslider.h"
#include "ui/colorbutton.h"
/**
* @brief The TextEditDialog class
*
* A separate window for editing text. This window can be resized arbitrarily and also provides a toolbar for rich text
* editing (if rich text is enabled). This dialog can be run from anywhere. Once the dialog has closed (i.e. returned
* from exec() ), the text entered into it can be retrieved using get_string().
*
* TODO: Add a live signal for updating the calling function.
*/
class TextEditDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief TextEditDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*
* @param s
*
* The starting string when the dialog opens. It'll be read as rich text HTML or plain text based on the `rich_text`
* parameter (which defaults to rich text HTML). It can also be left empty to start blank.
*
* @param rich_text
*
* Set the editing mode of the editor. If TRUE, the dialog will interpret the string in `s` as rich text HTML and also
* return rich text HTML through get_string(). It'll also show a toolbar with rich text options (i.e. font, italic,
* underline, size, etc.) If FALSE, the dialog will run in plain text mode interpreting the string in `s` as plain
* text and returning plain text through get_string(). It also will not show the rich text editing toolbar.
*/
TextEditDialog(QWidget* parent = nullptr, const QString& s = nullptr, bool rich_text = true);
/**
* @brief Retrieve the current text in the dialog
*
* This function can be called after the user has accepted the dialog (i.e. made changes and clicked OK).
* This will return either plain text or rich text (HTML) depending on the mode it's running in (rich/plain text mode
* is set in the constructor). The value this returns only gets updated when the user clicks OK so it cannot be
* used to retrieve live text updates from the dialog.
*
* @return
*
* The text entered once the user accepted this dialog.
*/
const QString& get_string();
private slots:
/**
* @brief Override of accept() to store the entered text string so it can be retrieved by get_string().
*/
virtual void accept() override;
/**
* @brief Slot for the font_weight combobox to set the font weight based on its data value
*
* @param i
*
* Index of the font_weight to retrieve the desired font weight from
*/
void SetFontWeight(int i);
/**
* @brief Slot for text alignment buttons to set alignment based on their properties
*
* Intended slot for left_align_button, center_align_button, right_align_button, and justify_align_button. Pulls
* from their property("a") value which should be a member of the Qt::Alignment enum.
*/
void SetAlignmentFromProperty();
/**
* @brief Slot for when the text edit widget's cursor moves so the rich text toolbar can stay up to date
*
* In rich text mode, different parts of a text document can be formatted in different ways. As the user moves
* around the text, the UI buttons should be consistent with whatever text is currently selected. This slot should
* therefore be connected to QTextEdit::cursorPositionChanged() and will change the "checked" state of the formatting
* buttons and current index of the comboboxes to match the currently selected text.
*/
void UpdateUIFromTextCursor();
private:
/**
* @brief Internal rich text mode value
*
* This is set in the constructor and cannot be changed during the lifetime of this dialog.
*/
bool rich_text_;
/**
* @brief Internal storage of text entered, saved when the user clicks OK
*/
QString result_str;
/**
* @brief Main text editing widget
*/
QTextEdit* textEdit;
/**
* @brief Toggle button for setting the italic state of the currently selected text
*/
QPushButton* italic_button;
/**
* @brief Toggle button for setting the underlined state of the currently selected text
*/
QPushButton* underline_button;
/**
* @brief ComboBox for the list of font families that the selected text can be set to
*/
QFontComboBox* font_list;
/**
* @brief ComboBox for the list of font weights that the selected text can be set to
*/
QComboBox* font_weight;
/**
* @brief A slider to set the current font size
*/
LabelSlider* font_size;
/**
* @brief A color selector for setting the current text color
*/
ColorButton* font_color;
/**
* @brief Button for setting the current text row(s) to left alignment
*/
QPushButton* left_align_button;
/**
* @brief Button for setting the current text row(s) to center alignment
*/
QPushButton* center_align_button;
/**
* @brief Button for setting the current text row(s) to right alignment
*/
QPushButton* right_align_button;
/**
* @brief Button for setting the current text row(s) to justified alignment
*/
QPushButton* justify_align_button;
};
#endif // TEXTEDITDIALOG_H
-344
View File
@@ -1,344 +0,0 @@
/***
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 "effectfield.h"
#include <QDateTime>
#include <QtMath>
#include <cfloat>
#include "rendering/renderfunctions.h"
#include "global/config.h"
#include "global/timing.h"
#include "nodes/nodeio.h"
#include "nodes/oldeffectnode.h"
#include "undo/undo.h"
#include "timeline/clip.h"
#include "timeline/sequence.h"
#include "global/math.h"
#include "global/debug.h"
EffectField::EffectField(NodeIO* parent, EffectFieldType t) :
QObject(parent),
type_(t),
enabled_(true)
{
// EffectField MUST be created with a parent.
Q_ASSERT(parent != nullptr);
// Set a very base default value
SetValueAt(0, 0);
// Connect this field to the effect's changed function
connect(this, SIGNAL(Changed()), parent->ParentNode(), SLOT(FieldChanged()));
}
NodeIO *EffectField::GetParentRow()
{
return static_cast<NodeIO*>(parent());
}
QVariant EffectField::ConvertStringToValue(const QString &s)
{
return s;
}
QString EffectField::ConvertValueToString(const QVariant &v)
{
return v.toString();
}
void EffectField::UpdateWidgetValue(QWidget *, double) {}
QVariant EffectField::GetValueAt(double timecode)
{
if (HasKeyframes()) {
int before_keyframe;
int after_keyframe;
double progress;
GetKeyframeData(timecode, before_keyframe, after_keyframe, progress);
const QVariant& before_data = keyframes.at(before_keyframe).data;
switch (type_) {
case EFFECT_FIELD_DOUBLE:
{
double value;
if (before_keyframe == after_keyframe) {
value = keyframes.at(before_keyframe).data.toDouble();
} else {
const EffectKeyframe& before_key = keyframes.at(before_keyframe);
const EffectKeyframe& after_key = keyframes.at(after_keyframe);
double before_dbl = before_key.data.toDouble();
double after_dbl = after_key.data.toDouble();
if (before_key.type == EFFECT_KEYFRAME_HOLD) {
// Hold keyframes will always return the previous keyframe with no interpolation
value = before_dbl;
} else if (before_key.type == EFFECT_KEYFRAME_BEZIER || after_key.type == EFFECT_KEYFRAME_BEZIER) {
// bezier interpolation
if (before_key.type == EFFECT_KEYFRAME_BEZIER && after_key.type == EFFECT_KEYFRAME_BEZIER) {
// cubic bezier
double t = cubic_t_from_x(timecode,
before_key.time,
before_key.time+GetValidKeyframeHandlePosition(before_keyframe, true),
after_key.time+GetValidKeyframeHandlePosition(after_keyframe, false),
after_key.time);
value = cubic_from_t(before_dbl,
before_dbl+before_key.post_handle.y(),
after_dbl+after_key.pre_handle.y(),
after_dbl,
t);
} else if (after_key.type == EFFECT_KEYFRAME_LINEAR) { // quadratic bezier
// last keyframe is the bezier one
double t = quad_t_from_x(timecode,
before_key.time,
before_key.time+GetValidKeyframeHandlePosition(before_keyframe, true),
after_key.time);
value = quad_from_t(before_dbl,
before_dbl+before_key.post_handle.y(),
after_dbl,
t);
} else {
// this keyframe is the bezier one
double t = quad_t_from_x(timecode,
before_key.time,
after_key.time+GetValidKeyframeHandlePosition(after_keyframe, false),
after_key.time);
value = quad_from_t(before_dbl,
after_dbl+after_key.pre_handle.y(),
after_dbl,
t);
}
} else {
// Linear interpolation (default)
value = double_lerp(before_dbl, after_dbl, progress);
}
}
persistent_data_ = value;
break;
}
case EFFECT_FIELD_COLOR:
{
QColor value;
if (before_keyframe == after_keyframe) {
value = keyframes.at(before_keyframe).data.value<QColor>();
} else {
QColor before_data = keyframes.at(before_keyframe).data.value<QColor>();
QColor after_data = keyframes.at(after_keyframe).data.value<QColor>();
value = QColor(lerp(before_data.red(), after_data.red(), progress),
lerp(before_data.green(), after_data.green(), progress),
lerp(before_data.blue(), after_data.blue(), progress));
}
persistent_data_ = value;
break;
}
case EFFECT_FIELD_STRING:
case EFFECT_FIELD_BOOL:
case EFFECT_FIELD_COMBO:
case EFFECT_FIELD_FONT:
case EFFECT_FIELD_FILE:
persistent_data_ = before_data;
break;
default:
break;
}
}
return persistent_data_;
}
void EffectField::SetValueAt(double time, const QVariant &value)
{
if (HasKeyframes()) {
// Create keyframe here
// Check array if a keyframe at this time already exists
int keyframe_index = -1;
for (int i=0;i<keyframes.size();i++) {
if (qFuzzyCompare(keyframes.at(i).time, time)) {
keyframe_index = i;
break;
}
}
// If keyframe doesn't exist, make it
if (keyframe_index == -1) {
EffectKeyframe key;
key.time = time;
key.data = value;
key.type = (keyframes.isEmpty()) ? EFFECT_KEYFRAME_LINEAR : keyframes.last().type;
keyframes.append(key);
} else {
EffectKeyframe& key = keyframes[keyframe_index];
key.data = value;
}
} else {
persistent_data_ = value;
}
emit Changed();
}
void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca)
{
if (enabled) {
// Create keyframe from perpetual data
EffectKeyframe key;
key.time = GetParentRow()->ParentNode()->Time();
key.data = persistent_data_;
key.type = EFFECT_KEYFRAME_LINEAR;
keyframes.append(key);
ca->append(new KeyframeAdd(this, keyframes.size()-1));
} else {
// Convert keyframes to one "perpetual" keyframe
// Set first keyframe to whatever the data is now
ca->append(new SetQVariant(&persistent_data_, persistent_data_, GetValueAt(GetParentRow()->ParentNode()->Time())));
// Delete all keyframes
for (int i=0;i<keyframes.size();i++) {
ca->append(new KeyframeDelete(this, 0));
}
}
}
const EffectField::EffectFieldType &EffectField::type()
{
return type_;
}
double EffectField::GetValidKeyframeHandlePosition(int key, bool post) {
int comp_key = -1;
// find keyframe before or after this one
for (int i=0;i<keyframes.size();i++) {
if (i != key
&& ((keyframes.at(i).time > keyframes.at(key).time) == post)
&& (comp_key == -1
|| ((keyframes.at(i).time < keyframes.at(comp_key).time) == post))) {
// compare with next keyframe for post or previous frame for pre
comp_key = i;
}
}
double adjusted_key = post ? keyframes.at(key).post_handle.x() : keyframes.at(key).pre_handle.x();
// if this is the earliest/latest keyframe, no validation is required
if (comp_key == -1) {
return adjusted_key;
}
double comp = keyframes.at(comp_key).time - keyframes.at(key).time;
// if comp keyframe is bezier, validate with its accompanying handle
if (keyframes.at(comp_key).type == EFFECT_KEYFRAME_BEZIER) {
double relative_comp_handle = comp + (post ? keyframes.at(comp_key).pre_handle.x() : keyframes.at(comp_key).post_handle.x());
// return an average
if ((post && keyframes.at(key).post_handle.x() > relative_comp_handle)
|| (!post && keyframes.at(key).pre_handle.x() < relative_comp_handle)) {
adjusted_key = (adjusted_key + relative_comp_handle)*0.5;
}
}
// don't let handle go beyond the compare keyframe's time
if (post == (adjusted_key > comp)) {
return comp;
}
if (post == (adjusted_key < 0)) {
return 0;
}
// original value is valid
return adjusted_key;
}
void EffectField::GetKeyframeData(double timecode, int &before, int &after, double &progress) {
int before_keyframe_index = -1;
int after_keyframe_index = -1;
double before_keyframe_time = DBL_MIN;
double after_keyframe_time = DBL_MAX;
for (int i=0;i<keyframes.size();i++) {
double eval_keyframe_time = keyframes.at(i).time;
if (qFuzzyCompare(eval_keyframe_time, timecode)) {
before = i;
after = i;
return;
} else if (eval_keyframe_time < timecode && eval_keyframe_time > before_keyframe_time) {
before_keyframe_index = i;
before_keyframe_time = eval_keyframe_time;
} else if (eval_keyframe_time > timecode && eval_keyframe_time < after_keyframe_time) {
after_keyframe_index = i;
after_keyframe_time = eval_keyframe_time;
}
}
if ((type_ == EFFECT_FIELD_DOUBLE || type_ == EFFECT_FIELD_COLOR)
&& (before_keyframe_index > -1 && after_keyframe_index > -1)) {
// interpolate
before = before_keyframe_index;
after = after_keyframe_index;
progress = (timecode-before_keyframe_time)/(after_keyframe_time-before_keyframe_time);
} else if (before_keyframe_index > -1) {
before = before_keyframe_index;
after = before_keyframe_index;
} else {
before = after_keyframe_index;
after = after_keyframe_index;
}
}
bool EffectField::HasKeyframes() {
return (GetParentRow()->IsKeyframing() && !keyframes.isEmpty());
}
bool EffectField::IsEnabled() {
return enabled_;
}
void EffectField::SetEnabled(bool e) {
enabled_ = e;
emit EnabledChanged(enabled_);
}
-420
View File
@@ -1,420 +0,0 @@
/***
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 EFFECTFIELD_H
#define EFFECTFIELD_H
#include <QObject>
#include <QVariant>
#include <QVector>
#include "effects/keyframe.h"
#include "undo/undostack.h"
#include "nodes/nodedatatypes.h"
class NodeIO;
class ComboAction;
/**
* @brief The EffectField class
*
* Any user-interactive element of an Effect. Usually a parameter that modifies the effect output, but sometimes just
* a UI object that performs some other function (e.g. LabelField and ButtonField).
*
* EffectField provides a largely abstract interface for Effect classes to pull information from. The class itself
* handles keyframing between linear, bezier, and hold interpolation accessible through GetValueAt(). This class
* is abstract, and therefore never intended to be used on its own. Instead you should always use a derived class.
*
* EffectField objects are *not* UI objects on their own. Instead, they're largely a system of values that can change
* over time. For a widget that the user can use to edit/modify these values, use CreateWidget().
*
* Derived classes are expected to override at least CreateWidget() to create a visual interactive widget corresponding
* to the field. If this
* field is a value used in the Effect (as most will be), UpdateWidgetValue() should also be overridden to display the
* correct value for this field as the user moves around the Timeline.
*
* If the field is intended to be saved and loaded from Olive project files (as most will be), ConvertStringToValue()
* and ConvertValueToString() may also have to be overridden depending on how the derived class's data works.
*/
class EffectField : public QObject {
Q_OBJECT
public:
/**
* @brief The EffectFieldType enum
*
* Predetermined types of fields. Used throughout Olive to identify what kind of data to expect from GetValueAt().
*
* This enum is also currently used to match an external XML effect's fields with the correct derived class (e.g.
* EFFECT_FIELD_DOUBLE matches to DoubleField).
*/
enum EffectFieldType {
/** Values are doubles. Also corresponds to DoubleField. */
EFFECT_FIELD_DOUBLE,
/** Values are colors. Also corresponds to ColorField. */
EFFECT_FIELD_COLOR,
/** Values are strings. Also corresponds to StringField. */
EFFECT_FIELD_STRING,
/** Values are booleans. Also corresponds to BoolField. */
EFFECT_FIELD_BOOL,
/** Values are arbitrary data. Also corresponds to ComboField. */
EFFECT_FIELD_COMBO,
/** Values are font family names (in string). Also corresponds to FontField. */
EFFECT_FIELD_FONT,
/** Values are filenames (in string). Also corresponds to FileField. */
EFFECT_FIELD_FILE,
/** Values is a UI object with no data. Corresponds to nothing. */
EFFECT_FIELD_UI
};
/**
* @brief EffectField Constructor
*
* Creates a new EffectField object.
*
* @param parent
*
* The EffectRow to add this field to. This must be a valid EffectRow. The EffectRow takes ownership of the field
* using the QObject parent/child system to automate memory management. EffectFields are never expected
* to change parent during their lifetime.
*
* @param t
*
* The type of data contained within this field. This is expected to be filled by a derived class.
*/
EffectField(NodeIO* parent, EffectFieldType t);
/**
* @brief Get the EffectRow that this field is a member of.
*
* Equivalent to `static_cast<EffectRow*>(EffectField::parent())`
*
* @return
*
* The EffectRow that this field is a member of.
*/
NodeIO* GetParentRow();
/**
* @brief Get the type of data to expect from this field
*
* @return
*
* A member of the EffectFieldType enum.
*/
const EffectFieldType& type();
/**
* @brief Get the value of this field at a given timecode
*
* EffectFields are designed to be keyframable, meaning the user can make the values change over the course of the
* Sequence. This is the main function used through Olive to retrieve what value this field will be at a given time.
*
* A common use case for this function would be EffectField::GetValueAt(EffectField::Now()), which will automatically
* retrieve the timecode at the current playhead.
*
* If the parent EffectRow is NOT keyframing, this function will simply return persistent_data_. If it IS keyframing,
* this will use the values in `keyframes` to determine what value should be specifically at this time.
* (bezier or linear interpolating it between values if necessary). Therefore this function should almost always be
* used to retrieve data from this field as the value will always be correct for the given time.
*
* @param timecode
*
* The time to retrieve the value in clip/media seconds (e.g. 0.0 is the very start of the media, 1.0 is one second
* into the media).
*
* @return
*
* A QVariant representation of the value at the given timecode.
*/
QVariant GetValueAt(double timecode);
/**
* @brief Set the value of this field at a given timecode
*
* EffectFields are designed to be keyframable, meaning the user can make the values change over the course of the
* Sequence. This is the main function used through Olive to set what value this field will be at a given time.
*
* If the parent EffectRow is keyframing, this function will determine whether a keyframe exists at this time already.
* If it does, it will change the value at that keyframe to `value`. Otherwise, it'll create a new keyframe at the
* specified `time` with the specified `value`.
*
* If the parent EffectRow is not keyframing, the data is simply stored in `persistent_data_`.
*
* When constructing an Effect
*
* @param time
*
* The time to retrieve the value at in clip/media seconds (e.g. 0.0 is the very start of the media, 1.0 is one
* second into the media).
*
* @param value
*
* The QVariant value to set at this time.
*/
void SetValueAt(double time, const QVariant& value);
/**
* @brief Set up keyframing on this field
*
* This should always be called if the user is enabling/disabling keyframing on the parent row. This function will
* move data between persistent_data_ and keyframes depending on whether keyframing is being enabled or disabled.
*
* If keyframing is getting ENABLED, this function will create the first keyframe automatically at the current time
* using the current value in persistent_data_.
*
* If keyframing is getting DISABLED, persistent_data_ is set to the current value at this time (GetValueAt(Now()))
* and delete all current keyframes.
*
* @param enabled
*
* TRUE if keyframing is getting enabled.
*
* @param ca
*
* A valid ComboAction object. It's expected that this function will be part of a larger action to enable/disable
* keyframing on the parent EffectRow, so this function will add commands to this ComboAction.
*/
void PrepareDataForKeyframing(bool enabled, ComboAction* ca);
/**
* @brief Convert a value from this field to a string
*
* When saving effect data to a project file, the data needs to be converted to a string format for saving in XML.
* The needs of this string representation may differ depending on the needs of the derived class, therefore you
* derived classes may need to override it.
*
* Default behavior is a simple QVariant <-> QString conversion, which should suffice in most cases.
*
* @param v
*
* The QVariant data (retrieved from this field) to convert to string
*
* @return
*
* A string representation of the QVariant data provided.
*/
virtual QString ConvertValueToString(const QVariant& v);
/**
* @brief Convert a string to a value appropriate for this field
*
* This function is the inverse of ConvertValueToString(), converting a string back to field data.
*
* @param s
*
* The string to convert to data.
*
* @return
*
* QVariant data converted from the provided string.
*/
virtual QVariant ConvertStringToValue(const QString& s);
/**
* @brief Create a widget for the user to interact with this field
*
* EffectField objects are *not* UI objects on their own. Instead, they're largely a system of values that can change
* over time. This function creates a QWidget object that can be placed somewhere in the UI so the user can
* interact with and change the data in this field.
*
* This function must be overridden by derived classes in order to create a widget that appropriate for that field's
* data. The derived class is also responsible for
* connecting signals like EnabledChanged(), Clicked(), and any other data that needs to be transferred between the
* widget and the field (setting up the signals and slots to do so). The field does NOT retain ownership (or any
* reference for that matter) to widgets it creates,
* so keeping the widget and field up to date with each other relies solely on setting up signals and slots.
* Infinite widgets can be created from a single field and used throughout Olive this way.
*
* Ownership is passed to the caller, and therefore the caller is responsible for freeing it.
*
* @param existing
*
* Olive allows multiple effects to attach to one UI layout. Pass a QWidget to this parameter (instead of nullptr)
* to attach this field additionally to the widget's signals/slots without creating a new one. The QWidget must be a
* widget previously created from the same derived class type or the result is undefined.
*
* @return
*
* A new QWidget object for this EffectField, or the same QWidget passed to `existing` if one was specified.
*/
virtual QWidget* CreateWidget(QWidget* existing = nullptr) = 0;
/**
* @brief Update a widget created by CreateWidget() using the value at a given time
*
* Use this function to update a QWidget (obtained from CreateWidget()) with the correct value from the field at a
* given time.
*
* Since only the derived classes know what type of QWidget it created in CreateWidget() and how to work with them,
* derived classes are also expected to override this function if the field is an active value used in the Effect
* that should visually update as the user moves around the Timeline. However if the field does NOT need to update
* live (e.g. the field is just a UI wrapper like LabelField or ButtonField), this function does not need to be
* overridden as the default behavior (to do nothing) will suffice in those cases.
*
* @param widget
*
* The QWidget to set the value of (must be a QWidget obtained from CreateWidget() or the behavior is undefined).
*
* @param timecode
*
* The time in clip/media seconds to retrieve data from.
*/
virtual void UpdateWidgetValue(QWidget* widget, double timecode);
/**
* @brief Get the correct X position/time value of a bezier keyframe's handles
*
* Retrieves the X value (time value) of a bezier keyframe's handles. Internally, the handles' X values are allowed
* to be arbitrary values. This however can lead to inadvertently creating impossible bezier curves (ones that, for
* example, mathematically loop over each other, but obviously a field can't have two values at the same time).
*
* This function returns the keyframe handles' X values adjusted to prevent this from happening. All calculations
* are consistent (i.e. the post handle of one keyframe will be adjusted the same way as the pre handle of the
* keyframe before it). It's recommended to always use this function to retrieve keyframe handle X values.
*
* @param key
*
* Index of the keyframe (in `keyframes`) to retrieve the handle position from.
*
* @param post
*
* FALSE to retrieve the "pre" handle (handle to the left of the keyframe), TRUE to retrieve the "post" handle
* (handle to the right of the keyframe).
*
* @return
*
* The adjusted X value of that keyframe handle.
*/
double GetValidKeyframeHandlePosition(int key, bool post);
/**
* @brief Return whether this field is enabled or not
*
* @return
*
* TRUE if this field is enabled.
*/
bool IsEnabled();
/**
* @brief Set the enabled state of this field
* @param e
*
* TRUE to enable this field, FALSE to disable it.
*/
void SetEnabled(bool e);
/**
* @brief Persistent data object
*
* If the parent EffectRow is not keyframing, all field data is stored and retrieved here. If the row IS keyframing,
* this variable goes basically unused unless `keyframes` is empty.
*
* NOTE: It is NOT recommended to access this variable directly. Use GetValueAt() instead.
*/
QVariant persistent_data_;
/**
* @brief Keyframe array
*
* Contains all data about this field's keyframes, from the keyframe times, to their data, to their type (linear,
* bezier, or hold), to the bezier handles (if using bezier). If the row is not keyframing, this array is never used
* (`persistent_data_` is used instead). If it is, this array will always be used unless the array is empty, in which
* case `persistent_data_` will be used again.
*/
QVector<EffectKeyframe> keyframes;
signals:
/**
* @brief Changed signal
*
* Emitted whenever SetValueAt() is called in order to trigger a UI update and Viewer repaint. Note this is NOT
* triggered as the value changes from keyframing. Only when the user themselves triggers a change.
*/
void Changed();
/**
* @brief Clicked signal
*
* Emitted when the user clicks on a QWidget attached to this field. Derived classes should connect this to the
* clicked signal of any QWidget's created (or attached) in CreateWidget().
*/
void Clicked();
/**
* @brief Enable change state signal
*
* Emitted when the field's enabled state is changed through SetEnabled(). Derived classes should connect this to the
* setEnabled() slot of any QWidget's created (or attached) in CreateWidget().
*/
void EnabledChanged(bool);
private:
/**
* @brief Internal type variable set in the constructor. Access with type().
*/
EffectFieldType type_;
/**
* @brief Used by GetValueAt() to determine whether to use keyframe data or persistent data
* @return
*
* TRUE if this keyframe data should be retrieved, FALSE if persistent data should be retrieved
*/
bool HasKeyframes();
/**
* @brief Internal function for determining where we are between the available keyframes
*
* @param timecode
*
* Timecode to get keyframe data at
*
* @param before
*
* The index (in the keyframes array) in the keyframe prior to this timecode.
*
* @param after
*
* The index (in the keyframes array) in the keyframe after this timecode.
*
* @param d
*
* The progress between the `before` keyframe and `after` keyframe from 0.0 to 1.0.
*/
void GetKeyframeData(double timecode, int& before, int& after, double& d);
/**
* @brief Internal enabled value
*/
bool enabled_;
};
#endif // EFFECTFIELD_H
-40
View File
@@ -1,40 +0,0 @@
/***
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 EFFECTFIELDS_H
#define EFFECTFIELDS_H
/**
A simple convenience header for including all the available EffectField derivations.
*/
#include "fields/boolfield.h"
#include "fields/buttonfield.h"
#include "fields/colorfield.h"
#include "fields/combofield.h"
#include "fields/doublefield.h"
#include "fields/filefield.h"
#include "fields/fontfield.h"
#include "fields/labelfield.h"
#include "fields/stringfield.h"
#endif // EFFECTFIELDS_H
-63
View File
@@ -1,63 +0,0 @@
/***
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 "effectgizmo.h"
#include "ui/labelslider.h"
#include "effects/fields/doublefield.h"
#include "nodes/oldeffectnode.h"
EffectGizmo::EffectGizmo(OldEffectNode *parent, int type) :
QObject(parent),
x_field1(nullptr),
x_field_multi1(1.0),
y_field1(nullptr),
y_field_multi1(1.0),
x_field2(nullptr),
x_field_multi2(1.0),
y_field2(nullptr),
y_field_multi2(1.0),
type(type),
cursor(-1)
{
Q_ASSERT(parent != nullptr);
int point_count = (type == GIZMO_TYPE_POLY) ? 4 : 1;
world_pos.resize(point_count);
screen_pos.resize(point_count);
color = Qt::white;
}
int EffectGizmo::get_point_count() {
return world_pos.size();
}
int EffectGizmo::get_type() {
return type;
}
int EffectGizmo::get_cursor() {
return cursor;
}
void EffectGizmo::set_cursor(int c) {
cursor = c;
}
-73
View File
@@ -1,73 +0,0 @@
/***
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 EFFECTGIZMO_H
#define EFFECTGIZMO_H
enum GizmoType {
GIZMO_TYPE_DOT,
GIZMO_TYPE_POLY,
GIZMO_TYPE_TARGET
};
#define GIZMO_DOT_SIZE 2.5f
#define GIZMO_TARGET_SIZE 5.0f
#include <QObject>
#include <QString>
#include <QRect>
#include <QPoint>
#include <QVector>
#include <QVector3D>
#include <QColor>
class DoubleField;
class OldEffectNode;
class EffectGizmo : public QObject {
Q_OBJECT
public:
EffectGizmo(OldEffectNode* parent, int type);
QVector<QVector3D> world_pos;
QVector<QPoint> screen_pos;
DoubleField* x_field1;
double x_field_multi1;
DoubleField* y_field1;
double y_field_multi1;
DoubleField* x_field2;
double x_field_multi2;
DoubleField* y_field2;
double y_field_multi2;
QColor color;
int get_point_count();
int get_type();
int get_cursor();
void set_cursor(int c);
private:
int type;
int cursor;
};
#endif // EFFECTGIZMO_H
-170
View File
@@ -1,170 +0,0 @@
/***
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 "effectloaders.h"
#include <QDir>
#include <QXmlStreamReader>
#include <QDebug>
#include "nodes/oldeffectnode.h"
#include "effects/transition.h"
#include "global/path.h"
#include "panels/panels.h"
#include "panels/effectcontrols.h"
#include "global/config.h"
#include "effects/internal/transformeffect.h"
#include "effects/internal/texteffect.h"
#include "effects/internal/timecodeeffect.h"
#include "effects/internal/solideffect.h"
#include "effects/internal/audionoiseeffect.h"
#include "effects/internal/toneeffect.h"
#include "effects/internal/volumeeffect.h"
#include "effects/internal/paneffect.h"
#include "effects/internal/shakeeffect.h"
#include "effects/internal/cornerpineffect.h"
#include "effects/internal/vsthost.h"
#include "effects/internal/fillleftrighteffect.h"
#include "effects/internal/richtexteffect.h"
#include "effects/internal/crossdissolvetransition.h"
#include "effects/internal/linearfadetransition.h"
#include "effects/internal/logarithmicfadetransition.h"
#include "effects/internal/exponentialfadetransition.h"
#include "nodes/nodes/nodemedia.h"
#include "nodes/nodes/nodetexturepassthru.h"
#include "nodes/nodes/nodeshader.h"
QMutex olive::effects_loaded;
void load_internal_effects() {
if (!olive::runtime_config.shaders_are_enabled) {
qWarning() << "Shaders are disabled, some effects may be nonfunctional";
}
olive::node_library.resize(kInvalidNode);
olive::node_library.fill(nullptr);
olive::node_library[kTransformEffect] = std::make_shared<TransformEffect>(nullptr);
olive::node_library[kTextInput] = std::make_shared<TextEffect>(nullptr);
olive::node_library[kSolidInput] = std::make_shared<SolidEffect>(nullptr);
olive::node_library[kNoiseInput] = std::make_shared<AudioNoiseEffect>(nullptr);
olive::node_library[kVolumeEffect] = std::make_shared<VolumeEffect>(nullptr);
olive::node_library[kPanEffect] = std::make_shared<PanEffect>(nullptr);
olive::node_library[kToneInput] = std::make_shared<ToneEffect>(nullptr);
olive::node_library[kShakeEffect] = std::make_shared<ShakeEffect>(nullptr);
olive::node_library[kTimecodeEffect] = std::make_shared<TimecodeEffect>(nullptr);
olive::node_library[kFillLeftRightEffect] = std::make_shared<FillLeftRightEffect>(nullptr);
olive::node_library[kVstEffect] = std::make_shared<VSTHost>(nullptr);
olive::node_library[kCornerPinEffect] = std::make_shared<CornerPinEffect>(nullptr);
olive::node_library[kRichTextInput] = std::make_shared<RichTextEffect>(nullptr);
//olive::node_library[kMediaInput] = std::make_shared<NodeMedia>(nullptr);
//olive::node_library[kImageOutput] = std::make_shared<NodeTexturePassthru>(nullptr);
olive::node_library[kCrossDissolveTransition] = std::make_shared<CrossDissolveTransition>(nullptr);
olive::node_library[kLinearFadeTransition] = std::make_shared<LinearFadeTransition>(nullptr);
olive::node_library[kExponentialFadeTransition] = std::make_shared<ExponentialFadeTransition>(nullptr);
olive::node_library[kLogarithmicFadeTransition] = std::make_shared<LogarithmicFadeTransition>(nullptr);
}
void load_shader_effects_worker(const QString& effects_path) {
QDir effects_dir(effects_path);
if (effects_dir.exists()) {
QList<QString> entries = effects_dir.entryList({"*.xml"},
QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
for (int i=0;i<entries.size();i++) {
QString entry_path = effects_dir.filePath(entries.at(i));
if (QFileInfo(entry_path).isDir()) {
load_shader_effects_worker(entry_path);
} else {
QString file_url = QDir(effects_path).filePath(entries.at(i));
QFile file(file_url);
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open" << entries.at(i);
return;
}
QXmlStreamReader reader(&file);
while (!reader.atEnd()) {
if (reader.name() == "effect") {
QString effect_name;
QString effect_cat;
QString effect_id;
const QXmlStreamAttributes attr = reader.attributes();
for (int j=0;j<attr.size();j++) {
if (attr.at(j).name() == "name") {
effect_name = attr.at(j).value().toString();
} else if (attr.at(j).name() == "category") {
effect_cat = attr.at(j).value().toString();
} else if (attr.at(j).name() == "id") {
effect_id = attr.at(j).value().toString();
}
}
if (!effect_name.isEmpty() && !effect_id.isEmpty()) {
olive::node_library.append(std::make_shared<NodeShader>(nullptr,
effect_name,
effect_id,
effect_cat,
file_url));
} else {
qCritical() << "Invalid effect found in" << entries.at(i);
}
break;
}
reader.readNext();
}
file.close();
}
}
}
}
void load_shader_effects() {
QList<QString> effects_paths = get_effects_paths();
for (int h=0;h<effects_paths.size();h++) {
const QString& effects_path = effects_paths.at(h);
load_shader_effects_worker(effects_path);
}
}
void EffectInit::StartLoading() {
EffectInit* init_thread = new EffectInit();
QObject::connect(init_thread, SIGNAL(finished()), init_thread, SLOT(deleteLater()));
init_thread->start();
}
EffectInit::EffectInit() {
olive::effects_loaded.lock();
}
void EffectInit::run() {
qInfo() << "Initializing effects...";
load_internal_effects();
load_shader_effects();
olive::effects_loaded.unlock();
qInfo() << "Finished initializing effects";
}
-55
View File
@@ -1,55 +0,0 @@
/***
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 EFFECTLOADERS_H
#define EFFECTLOADERS_H
#include <QList>
#include <QThread>
#include <QMutex>
namespace olive {
extern QMutex effects_loaded;
}
/**
* @brief The EffectInit class
*
* A separate thread for loading effects in the background while the rest of the program's initiation takes place.
* The program can even run before the effects have finished loading, but any point the software needs to access
* effects, it will have to wait for this thread to finish before it can. Fortunately this thread is usually very
* quick and is over before the MainWindow shows.
*/
class EffectInit : public QThread {
public:
EffectInit();
/**
* @brief A static convenience function to set up the EffectInit thread, start it, and free itself when complete.
*/
static void StartLoading();
protected:
/**
* @brief Function that runs in the other thread.
*/
void run();
};
#endif // EFFECTLOADERS_H
-99
View File
@@ -1,99 +0,0 @@
/***
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 "boolfield.h"
#include <QCheckBox>
#include "nodes/node.h"
#include "undo/undo.h"
BoolField::BoolField(NodeIO *parent) :
EffectField(parent, EffectField::EFFECT_FIELD_BOOL)
{}
bool BoolField::GetBoolAt(double timecode)
{
return GetValueAt(timecode).toBool();
}
QWidget *BoolField::CreateWidget(QWidget *existing)
{
QCheckBox* cb;
if (existing == nullptr) {
cb = new QCheckBox();
cb->setEnabled(IsEnabled());
} else {
cb = static_cast<QCheckBox*>(existing);
}
connect(cb, SIGNAL(toggled(bool)), this, SLOT(UpdateFromWidget(bool)));
connect(this, SIGNAL(EnabledChanged(bool)), cb, SLOT(setEnabled(bool)));
connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(Toggled(bool)));
return cb;
}
void BoolField::UpdateWidgetValue(QWidget *widget, double timecode)
{
QCheckBox* cb = static_cast<QCheckBox*>(widget);
// Setting the checked state on the checkbox below normally triggers a change() signal that will then trickle back
// to setting a value on this field. Therefore we block its signals while we're setting this.
cb->blockSignals(true);
if (qIsNaN(timecode)) {
cb->setTristate(true);
cb->setCheckState(Qt::PartiallyChecked);
} else {
cb->setTristate(false);
cb->setChecked(GetBoolAt(timecode));
}
cb->blockSignals(false);
emit Toggled(cb->isChecked());
}
QVariant BoolField::ConvertStringToValue(const QString &s)
{
return (s == "1");
}
QString BoolField::ConvertValueToString(const QVariant &v)
{
return QString::number(v.toBool());
}
void BoolField::UpdateFromWidget(bool b)
{
KeyframeDataChange* kdc = new KeyframeDataChange(this);
SetValueAt(GetParentRow()->ParentNode()->Time(), b);
kdc->SetNewKeyframes();
olive::undo_stack.push(kdc);
}
-101
View File
@@ -1,101 +0,0 @@
/***
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 BOOLFIELD_H
#define BOOLFIELD_H
#include "../effectfield.h"
/**
* @brief The BoolField class
*
* An EffectField derivative the produces boolean values (true or false) and uses a checkbox as its visual representation.
*/
class BoolField : public EffectField
{
Q_OBJECT
public:
/**
* @brief Reimplementation of EffectField::EffectField().
*/
BoolField(NodeIO* parent);
/**
* @brief Get the boolean value at a given timecode
*
* A convenience function, equivalent to GetValueAt(timecode).toBool()
*
* @param timecode
*
* The timecode to retrieve the value at
*
* @return
*
* The boolean value at this timecode
*/
bool GetBoolAt(double timecode);
/**
* @brief Reimplementation of EffectField::CreateWidget()
*
* Creates and connects to a QCheckBox.
*/
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
/**
* @brief Reimplementation of EffectField::UpdateWidgetValue()
*/
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
/**
* @brief Reimplementation of EffectField::ConvertStringToValue()
*/
virtual QVariant ConvertStringToValue(const QString& s) override;
/**
* @brief Reimplementation of EffectField::ConvertValueToString()
*/
virtual QString ConvertValueToString(const QVariant& v) override;
signals:
/**
* @brief Emitted whenever the UI widget's boolean value has changed
*
* For any QCheckBox created through this field's CreateWidget() function, this signal is emitted any time the
* checkbox value changes (either through user intervention or keyframing). It is mostly useful for
* enabling/disabling/changing other UI elements based on the checked
* state of this field's value (e.g. enabling other fields if this field is checked).
*
* It is NOT a reliable signal that the value has changed at all, as it is only emitted if a widget (created
* from CreateWidget() ) is currently active.
*/
void Toggled(bool);
private slots:
/**
* @brief Internal function connected to any QWidget made from CreateWidget() to update the value based on user input
*
* @param b
*
* The current checked state of the QWidget (QCheckBox in this case). Automatically set when this slot is connected
* to the QCheckBox::toggled() signal.
*/
void UpdateFromWidget(bool b);
};
#endif // BOOLFIELD_H
-66
View File
@@ -1,66 +0,0 @@
/***
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 "buttonfield.h"
#include <QPushButton>
ButtonField::ButtonField(NodeIO *parent, const QString &string) :
EffectField(parent, EffectField::EFFECT_FIELD_UI),
button_text_(string)
{}
void ButtonField::SetCheckable(bool c)
{
checkable_ = c;
}
void ButtonField::SetChecked(bool c)
{
checked_ = c;
emit CheckedChanged(c);
}
QWidget *ButtonField::CreateWidget(QWidget *existing)
{
QPushButton* button;
if (existing == nullptr) {
button = new QPushButton();
button->setCheckable(checkable_);
button->setEnabled(IsEnabled());
button->setText(button_text_);
} else {
button = static_cast<QPushButton*>(existing);
}
connect(this, SIGNAL(CheckedChanged(bool)), button, SLOT(setChecked(bool)));
connect(this, SIGNAL(EnabledChanged(bool)), button, SLOT(setEnabled(bool)));
connect(button, SIGNAL(clicked(bool)), this, SIGNAL(Clicked()));
connect(button, SIGNAL(toggled(bool)), this, SLOT(SetChecked(bool)));
connect(button, SIGNAL(toggled(bool)), this, SIGNAL(Toggled(bool)));
return button;
}
-111
View File
@@ -1,111 +0,0 @@
/***
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 BUTTONFIELD_H
#define BUTTONFIELD_H
#include "../effectfield.h"
/**
* @brief The ButtonField class
*
* A UI-type EffectField. This field is largely an EffectField wrapper around a QPushButton and provides no data that's
* usable in the Effect. It's primarily useful for other UI functions (e.g. showing/hiding a dialog or other UI
* elements). This field is not exposed to the external shader API as it requires raw C++ code to connect it to other
* elements.
*
* As with all widgets created from EffectField::CreateWidget(), you should never interface with the resulting widget
* directly (apart from adding it to a layout and deleting it when it's unnecessary). All signals/slots should pass
* through ButtonField instead to keep consistency with every layer involved.
*/
class ButtonField : public EffectField
{
Q_OBJECT
public:
/**
* @brief Reimplementation of EffectField::EffectField().
*/
ButtonField(NodeIO* parent, const QString& string);
/**
* @brief Set whether this pushbutton is checkable
*
* This function is mainly a wrapper around QPushButton::setCheckable().
*
* "Checkable" means the button can be toggled between a state of being "normal" and being "pressed". In checkable
* mode this field still cannot be used as a value in an Effect. Instead use BoolField (which uses a QCheckBox
* representation) for passing values to the Effect that can only be true or false.
*
* @param c
*
* TRUE if this button should be checkable or not.
*/
void SetCheckable(bool c);
/**
* @brief Reimplementation of EffectField::CreateWidget()
*
* Creates and connects to a QPushButton.
*/
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
public slots:
/**
* @brief A slot for when a widget's (created and connected from CreateWidget() ) checked state is changed
*
* @param c
*
* The current checked state (automatically filled by the QPushButton::toggled() signal)
*/
void SetChecked(bool c);
signals:
/**
* @brief A signal emitted whenever the field's internal checked state is changed
*
* Primarily used to set any connected widget's checked state to be consistent with the field's.
*/
void CheckedChanged(bool);
/**
* @brief A signal emitted whenever the checked state of a connected widget changes
*
* Any widgets associated with this field will emit this signal when their checked state changes.
*/
void Toggled(bool);
private:
/**
* @brief Internal button text string passed to widgets created by CreateWidget()
*/
bool checkable_;
/**
* @brief Internal checked value passed to and from widgets created by CreateWidget()
*/
bool checked_;
/**
* @brief Internal button text string passed to widgets created by CreateWidget()
*/
QString button_text_;
};
#endif // BUTTONFIELD_H
-73
View File
@@ -1,73 +0,0 @@
/***
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 "colorfield.h"
#include <QColor>
#include "ui/colorbutton.h"
#include "nodes/node.h"
#include "undo/undo.h"
ColorField::ColorField(NodeIO* parent) :
EffectField(parent, EffectField::EFFECT_FIELD_COLOR)
{}
QColor ColorField::GetColorAt(double timecode)
{
return GetValueAt(timecode).value<QColor>();
}
QWidget *ColorField::CreateWidget(QWidget *existing)
{
ColorButton* cb = (existing != nullptr) ? static_cast<ColorButton*>(existing) : new ColorButton();
connect(cb, SIGNAL(color_changed(const QColor &)), this, SLOT(UpdateFromWidget(const QColor &)));
connect(this, SIGNAL(EnabledChanged(bool)), cb, SLOT(setEnabled(bool)));
return cb;
}
void ColorField::UpdateWidgetValue(QWidget *widget, double timecode)
{
ColorButton* cb = static_cast<ColorButton*>(widget);
cb->set_color(GetColorAt(timecode));
}
QVariant ColorField::ConvertStringToValue(const QString &s)
{
return QColor(s);
}
QString ColorField::ConvertValueToString(const QVariant &v)
{
return v.value<QColor>().name();
}
void ColorField::UpdateFromWidget(const QColor& c)
{
KeyframeDataChange* kdc = new KeyframeDataChange(this);
SetValueAt(GetParentRow()->ParentNode()->Time(), c);
kdc->SetNewKeyframes();
olive::undo_stack.push(kdc);
}
-88
View File
@@ -1,88 +0,0 @@
/***
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 COLORFIELD_H
#define COLORFIELD_H
#include "../effectfield.h"
/**
* @brief The ColorField class
*
* An EffectField derivative that produces color values and uses a ColorButton as its UI representative.
*/
class ColorField : public EffectField
{
Q_OBJECT
public:
/**
* @brief Reimplementation of EffectField::EffectField().
*/
ColorField(NodeIO* parent);
/**
* @brief Get the color value at a given timecode
*
* A convenience function, equivalent to GetValueAt(timecode).value<QColor>().
*
* @param timecode
*
* The timecode to retrieve the color at
*
* @return
*
* The color value at this timecode
*/
QColor GetColorAt(double timecode);
/**
* @brief CreateWidget
*
* Creates and connects to a ColorButton.
*/
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
/**
* @brief Reimplementation of EffectField::UpdateWidgetValue()
*/
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
/**
* @brief Reimplementation of EffectField::ConvertStringToValue()
*/
virtual QVariant ConvertStringToValue(const QString& s) override;
/**
* @brief Reimplementation of EffectField::ConvertValueToString()
*/
virtual QString ConvertValueToString(const QVariant& v) override;
private slots:
/**
* @brief Internal function connected to any QWidget made from CreateWidget() to update the value based on user input
*
* @param b
*
* The current color selected by the QWidget (ColorButton in this case). Automatically triggered when this slot is
* connected to the ColorButton::color_changed() signal.
*/
void UpdateFromWidget(const QColor &c);
};
#endif // COLORFIELD_H
-93
View File
@@ -1,93 +0,0 @@
/***
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 "combofield.h"
#include <QDebug>
#include "nodes/node.h"
#include "ui/comboboxex.h"
#include "undo/undo.h"
ComboField::ComboField(NodeIO* parent) :
EffectField(parent, EffectField::EFFECT_FIELD_COMBO)
{}
void ComboField::AddItem(const QString &text, const QVariant &data)
{
ComboFieldItem item;
item.name = text;
item.data = data;
items_.append(item);
}
QWidget *ComboField::CreateWidget(QWidget *existing)
{
ComboBoxEx* cb;
if (existing == nullptr) {
cb = new ComboBoxEx();
cb->setScrollingEnabled(false);
for (int i=0;i<items_.size();i++) {
cb->addItem(items_.at(i).name);
}
} else {
cb = static_cast<ComboBoxEx*>(existing);
}
connect(cb, SIGNAL(activated(int)), this, SLOT(UpdateFromWidget(int)));
connect(this, SIGNAL(EnabledChanged(bool)), cb, SLOT(setEnabled(bool)));
return cb;
}
void ComboField::UpdateWidgetValue(QWidget *widget, double timecode)
{
QVariant data = GetValueAt(timecode);
ComboBoxEx* cb = static_cast<ComboBoxEx*>(widget);
for (int i=0;i<items_.size();i++) {
if (items_.at(i).data == data) {
cb->blockSignals(true);
cb->setCurrentIndex(i);
cb->blockSignals(false);
emit DataChanged(data);
return;
}
}
qWarning() << "Failed to set ComboField value from data";
}
void ComboField::UpdateFromWidget(int index)
{
KeyframeDataChange* kdc = new KeyframeDataChange(this);
SetValueAt(GetParentRow()->ParentNode()->Time(), items_.at(index).data);
kdc->SetNewKeyframes();
olive::undo_stack.push(kdc);
}
-113
View File
@@ -1,113 +0,0 @@
/***
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 COMBOFIELD_H
#define COMBOFIELD_H
#include "../effectfield.h"
/**
* @brief The ComboFieldItem struct
*
* An internal string+value pair used to represent a combobox item. name is used for the UI
* representation of the choices and the data is what can be retrieved by code.
*
* \see ComboField::AddItem.
*/
struct ComboFieldItem {
QString name;
QVariant data;
};
/**
* @brief The ComboField class
*
* An EffectField derivative to produce arbitrary data based on a fixed selection of items.
*/
class ComboField : public EffectField
{
Q_OBJECT
public:
/**
* @brief Reimplementation of EffectField::EffectField().
*/
ComboField(NodeIO* parent);
/**
* @brief Add an item to this ComboField
*
* Adds a choice that the user can choose from this ComboField. All choices need text (for the on-screen
* choice) and data, which gets read on the backend. The selected data is what gets saved and loaded from
* project files, and therefore the data should be unique to this item. In case more items get added to
* this ComboField later, old project files will still open correctly. This is also why simple selected
* indices are not available. The text is only shown on the UI so it can be safely translated during runtime.
*
* @param text
*
* The text to show at this index.
*
* @param data
*
* The data to be retrieved at this index.
*/
void AddItem(const QString& text, const QVariant& data);
/**
* @brief Reimplementation of EffectField::CreateWidget()
*
* Creates and connects to a QComboBox with the set of items added in AddItem().
*/
virtual QWidget *CreateWidget(QWidget *existing = nullptr) override;
/**
* @brief Reimplementation of EffectField::UpdateWidgetValue()
*/
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
signals:
/**
* @brief Signal emitted whenever a connected widget's data gets changed
*
* Useful for UI events that need to occur with the change of this ComboField's value.
*/
void DataChanged(const QVariant&);
private:
/**
* @brief Internal array of string+value pair items.
*
* \see ComboFieldItem
*/
QVector<ComboFieldItem> items_;
private slots:
/**
* @brief Internal function connected to any QWidget made from CreateWidget() to update the value based on user input
*
* @param b
*
* The current index of the QWidget (QComboBox in this case). Automatically set when this slot is connected
* to the QComboBox::currentIndexChanged() signal. This is the only time ComboFields deal with indices since the
* QComboBox's indices will match precisely to the items_ array. Outside of this function, QVariant data is preferred.
*/
void UpdateFromWidget(int index);
};
#endif // COMBOFIELD_H
-151
View File
@@ -1,151 +0,0 @@
/***
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 "doublefield.h"
#include "nodes/node.h"
#include "undo/undo.h"
DoubleField::DoubleField(NodeIO* parent) :
EffectField(parent, EffectField::EFFECT_FIELD_DOUBLE),
min_(qSNaN()),
max_(qSNaN()),
default_(0),
display_type_(LabelSlider::Normal),
frame_rate_(30),
value_set_(false),
kdc_(nullptr)
{
connect(this, SIGNAL(Changed()), this, SLOT(ValueHasBeenSet()), Qt::DirectConnection);
}
double DoubleField::GetDoubleAt(double timecode)
{
return GetValueAt(timecode).toDouble();
}
void DoubleField::SetMinimum(double minimum)
{
min_ = minimum;
emit MinimumChanged(min_);
}
void DoubleField::SetMaximum(double maximum)
{
max_ = maximum;
emit MaximumChanged(max_);
}
void DoubleField::SetDefault(double d)
{
default_ = d;
if (!value_set_) {
SetValueAt(0, d);
}
}
void DoubleField::SetDisplayType(LabelSlider::DisplayType type)
{
display_type_ = type;
}
void DoubleField::SetFrameRate(const double &rate)
{
frame_rate_ = rate;
}
QVariant DoubleField::ConvertStringToValue(const QString &s)
{
return s.toDouble();
}
QString DoubleField::ConvertValueToString(const QVariant &v)
{
return QString::number(v.toDouble());
}
QWidget *DoubleField::CreateWidget(QWidget *existing)
{
LabelSlider* ls;
if (existing == nullptr) {
ls = new LabelSlider();
if (!qIsNaN(min_)) {
ls->SetMinimum(min_);
}
ls->SetDefault(default_);
if (!qIsNaN(max_)) {
ls->SetMaximum(max_);
}
ls->SetDisplayType(display_type_);
ls->SetFrameRate(frame_rate_);
ls->setEnabled(IsEnabled());
} else {
ls = static_cast<LabelSlider*>(existing);
}
connect(ls, SIGNAL(valueChanged(double)), this, SLOT(UpdateFromWidget(double)));
connect(ls, SIGNAL(clicked()), this, SIGNAL(Clicked()));
connect(this, SIGNAL(EnabledChanged(bool)), ls, SLOT(setEnabled(bool)));
connect(this, SIGNAL(MaximumChanged(double)), ls, SLOT(SetMaximum(double)));
connect(this, SIGNAL(MinimumChanged(double)), ls, SLOT(SetMinimum(double)));
return ls;
}
void DoubleField::UpdateWidgetValue(QWidget *widget, double timecode)
{
if (qIsNaN(timecode)) {
static_cast<LabelSlider*>(widget)->SetValue(qSNaN());
} else {
static_cast<LabelSlider*>(widget)->SetValue(GetDoubleAt(timecode));
}
}
void DoubleField::ValueHasBeenSet()
{
value_set_ = true;
}
void DoubleField::UpdateFromWidget(double d)
{
LabelSlider* ls = static_cast<LabelSlider*>(sender());
if (ls->IsDragging() && kdc_ == nullptr) {
kdc_ = new KeyframeDataChange(this);
}
SetValueAt(GetParentRow()->ParentNode()->Time(), d);
if (!ls->IsDragging() && kdc_ != nullptr) {
kdc_->SetNewKeyframes();
olive::undo_stack.push(kdc_);
kdc_ = nullptr;
}
}

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