Merge branch 'master' into transition-offsets
@@ -33,9 +33,5 @@ set(OLIVE_SOURCES
|
||||
codec/frame.h
|
||||
codec/samplebuffer.cpp
|
||||
codec/samplebuffer.h
|
||||
codec/waveinput.cpp
|
||||
codec/waveinput.h
|
||||
codec/waveoutput.cpp
|
||||
codec/waveoutput.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -57,19 +57,13 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i
|
||||
|
||||
QString ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams ¶ms)
|
||||
{
|
||||
QString index_fn = QStringLiteral("%1.%2:%3").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
|
||||
QString::number(stream.stream()));
|
||||
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
|
||||
QString::number(stream.stream()),
|
||||
QString::number(params.sample_rate()),
|
||||
QString::number(params.format()),
|
||||
QString::number(params.channel_layout()));
|
||||
|
||||
index_fn = QDir(cache_path).filePath(index_fn);
|
||||
|
||||
index_fn.append('.');
|
||||
index_fn.append(QString::number(params.sample_rate()));
|
||||
index_fn.append('.');
|
||||
index_fn.append(QString::number(params.format()));
|
||||
index_fn.append('.');
|
||||
index_fn.append(QString::number(params.channel_layout()));
|
||||
|
||||
return index_fn;
|
||||
return QDir(cache_path).filePath(index_fn);
|
||||
}
|
||||
|
||||
void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
#include "codec/ffmpeg/ffmpegdecoder.h"
|
||||
#include "codec/oiio/oiiodecoder.h"
|
||||
#include "codec/waveinput.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "common/ffmpegutils.h"
|
||||
#include "common/filefunctions.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
@@ -129,7 +127,7 @@ Decoder::RetrieveAudioData Decoder::RetrieveAudio(const TimeRange &range, const
|
||||
}
|
||||
|
||||
// See if we got the conform
|
||||
SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filename, range, loop_mode);
|
||||
SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filename, range, loop_mode, params);
|
||||
|
||||
return {kOK, out_buffer, nullptr};
|
||||
}
|
||||
@@ -265,13 +263,11 @@ bool Decoder::ConformAudioInternal(const QString& filename, const AudioParams &p
|
||||
return false;
|
||||
}
|
||||
|
||||
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range, Footage::LoopMode loop_mode)
|
||||
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
|
||||
{
|
||||
WaveInput input(conform_filename);
|
||||
|
||||
if (input.open()) {
|
||||
const AudioParams& input_params = input.params();
|
||||
QFile input(conform_filename);
|
||||
|
||||
if (input.open(QFile::ReadOnly)) {
|
||||
QByteArray packed_data(input_params.time_to_bytes(range.length()), Qt::Uninitialized);
|
||||
|
||||
qint64 read_index = input_params.time_to_bytes(range.in());
|
||||
@@ -279,12 +275,12 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam
|
||||
|
||||
while (write_index < packed_data.size()) {
|
||||
if (loop_mode == Footage::kLoopModeLoop) {
|
||||
while (read_index >= input.data_length()) {
|
||||
read_index -= input.data_length();
|
||||
while (read_index >= input.size()) {
|
||||
read_index -= input.size();
|
||||
}
|
||||
|
||||
while (read_index < 0) {
|
||||
read_index += input.data_length();
|
||||
read_index += input.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,13 +290,14 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam
|
||||
// Reading before 0, write silence here until audio data would actually start
|
||||
write_count = qMin(-read_index, qint64(packed_data.size()));
|
||||
memset(packed_data.data() + write_index, 0, write_count);
|
||||
} else if (read_index >= input.data_length()) {
|
||||
} else if (read_index >= input.size()) {
|
||||
// Reading after data length, write silence until the end of the buffer
|
||||
write_count = packed_data.size() - write_index;
|
||||
memset(packed_data.data() + write_index, 0, write_count);
|
||||
} else {
|
||||
write_count = qMin(input.data_length() - read_index, packed_data.size() - write_index);
|
||||
input.read(read_index, packed_data.data() + write_index, write_count);
|
||||
write_count = qMin(input.size() - read_index, packed_data.size() - write_index);
|
||||
input.seek(read_index);
|
||||
input.read(packed_data.data() + write_index, write_count);
|
||||
}
|
||||
|
||||
read_index += write_count;
|
||||
|
||||
@@ -33,7 +33,6 @@ extern "C" {
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "codec/samplebuffer.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "common/rational.h"
|
||||
#include "node/project/footage/footage.h"
|
||||
#include "node/project/footage/footagedescription.h"
|
||||
@@ -313,7 +312,7 @@ signals:
|
||||
private:
|
||||
void UpdateLastAccessed();
|
||||
|
||||
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range, Footage::LoopMode loop_mode);
|
||||
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams ¶ms);
|
||||
|
||||
CodecStream stream_;
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ extern "C" {
|
||||
#include <QThread>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "codec/waveinput.h"
|
||||
#include "common/define.h"
|
||||
#include "common/ffmpegutils.h"
|
||||
#include "common/filefunctions.h"
|
||||
@@ -465,7 +464,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
|
||||
|
||||
swr_init(resampler);
|
||||
|
||||
WaveOutput wave_out(filename, params);
|
||||
QFile wave_out(filename);
|
||||
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
@@ -473,7 +472,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
|
||||
|
||||
bool success = false;
|
||||
|
||||
if (wave_out.open()) {
|
||||
if (wave_out.open(QFile::WriteOnly)) {
|
||||
while (true) {
|
||||
// Check if we have a `cancelled` ptr and its value
|
||||
if (cancelled && *cancelled) {
|
||||
|
||||
@@ -37,7 +37,6 @@ extern "C" {
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "codec/decoder.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "ffmpegframepool.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -322,7 +322,7 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio)
|
||||
|
||||
if (audio_frame_offset_ == audio_max_samples_ || (i == converted && !input_data)) {
|
||||
// Got all the samples we needed, write the frame
|
||||
audio_frame_->pts = audio_write_count_;
|
||||
audio_frame_->pts = av_rescale_q(audio_write_count_, {1, audio_codec_ctx_->sample_rate}, audio_codec_ctx_->time_base);
|
||||
|
||||
WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_);
|
||||
audio_write_count_ += audio_frame_offset_;
|
||||
@@ -336,7 +336,7 @@ bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio)
|
||||
|
||||
if (!input_data && audio_frame_offset_ > 0) {
|
||||
audio_frame_->nb_samples = audio_frame_offset_;
|
||||
audio_frame_->pts = audio_write_count_;
|
||||
audio_frame_->pts = av_rescale_q(audio_write_count_, {1, audio_codec_ctx_->sample_rate}, audio_codec_ctx_->time_base);
|
||||
WriteAVFrame(audio_frame_, audio_codec_ctx_, audio_stream_);
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
|
||||
subtitle.num_rects = 1;
|
||||
subtitle.rects = &rect_array;
|
||||
|
||||
subtitle.pts = Timecode::time_to_timestamp(sub_block->in(), av_get_time_base_q(), Timecode::kFloor);
|
||||
subtitle.pts = Timecode::time_to_timestamp(sub_block->in(), subtitle_codec_ctx_->time_base, Timecode::kFloor);
|
||||
subtitle.end_display_time = qRound64(sub_block->length().toDouble() * 1000);
|
||||
|
||||
QVector<uint8_t> out_buf(1024 * 1024);
|
||||
@@ -412,9 +412,9 @@ bool FFmpegEncoder::WriteSubtitle(const SubtitleBlock *sub_block)
|
||||
pkt->data = out_buf.data();
|
||||
pkt->size = sub_sz;
|
||||
pkt->pts = subtitle.pts;
|
||||
pkt->duration = subtitle.end_display_time;
|
||||
pkt->duration = av_rescale_q(subtitle.end_display_time, {1, 1000}, subtitle_codec_ctx_->time_base);
|
||||
pkt->dts = pkt->pts;
|
||||
av_packet_rescale_ts(pkt, av_get_time_base_q(), subtitle_stream_->time_base);
|
||||
av_packet_rescale_ts(pkt, subtitle_codec_ctx_->time_base, subtitle_stream_->time_base);
|
||||
|
||||
int err = av_interleaved_write_frame(fmt_ctx_, pkt);
|
||||
bool ret = true;
|
||||
@@ -694,6 +694,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
|
||||
codec_ctx->height = params().video_params().height();
|
||||
codec_ctx->sample_aspect_ratio = params().video_params().pixel_aspect_ratio().toAVRational();
|
||||
codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational();
|
||||
codec_ctx->framerate = params().video_params().frame_rate().toAVRational();
|
||||
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
|
||||
|
||||
if (params().video_params().interlacing() != VideoParams::kInterlaceNone) {
|
||||
@@ -818,6 +819,10 @@ bool FFmpegEncoder::SetupCodecContext(AVStream* stream, AVCodecContext* codec_ct
|
||||
return false;
|
||||
}
|
||||
|
||||
if (codec->type == AVMEDIA_TYPE_VIDEO) {
|
||||
stream->avg_frame_rate = codec_ctx->framerate;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "waveinput.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QtMath>
|
||||
|
||||
namespace olive {
|
||||
|
||||
WaveInput::WaveInput(const QString &f) :
|
||||
file_(f)
|
||||
{
|
||||
}
|
||||
|
||||
WaveInput::~WaveInput()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool WaveInput::open()
|
||||
{
|
||||
if (!file_.open(QFile::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_.read(4) != "RIFF") {
|
||||
close();
|
||||
qCritical() << "No RIFF found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip filesize bytes
|
||||
file_.seek(file_.pos() + 4);
|
||||
|
||||
if (file_.read(4) != "WAVE") {
|
||||
close();
|
||||
qCritical() << "No WAVE found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find fmt_ section
|
||||
if (!find_str(&file_, "fmt ")) {
|
||||
close();
|
||||
qCritical() << "No fmt found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip fmt_ section size
|
||||
file_.seek(file_.pos()+4);
|
||||
|
||||
// Create data stream for reading bytes into types
|
||||
QDataStream data_stream(&file_);
|
||||
data_stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
// Read data type
|
||||
uint16_t data_type;
|
||||
data_stream >> data_type;
|
||||
|
||||
bool data_is_float;
|
||||
switch (data_type) {
|
||||
case 1: // PCM Integer
|
||||
data_is_float = false;
|
||||
break;
|
||||
case 3:
|
||||
data_is_float = true;
|
||||
break;
|
||||
default:
|
||||
// If it's neither float nor int, we can't work with this file
|
||||
close();
|
||||
qCritical() << "Invalid WAV type" << data_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read number of channels
|
||||
uint16_t channel_count;
|
||||
data_stream >> channel_count;
|
||||
|
||||
uint64_t channel_layout = static_cast<uint64_t>(av_get_default_channel_layout(channel_count));
|
||||
|
||||
int32_t sample_rate;
|
||||
data_stream >> sample_rate;
|
||||
|
||||
// Skip bytes per second value and bytes per sample value
|
||||
file_.seek(file_.pos() + 6);
|
||||
|
||||
uint16_t bits_per_sample;
|
||||
data_stream >> bits_per_sample;
|
||||
|
||||
AudioParams::Format format;
|
||||
|
||||
switch (bits_per_sample) {
|
||||
case 8:
|
||||
format = AudioParams::kFormatUnsigned8;
|
||||
break;
|
||||
case 16:
|
||||
format = AudioParams::kFormatSigned16;
|
||||
break;
|
||||
case 32:
|
||||
if (data_is_float) {
|
||||
format = AudioParams::kFormatFloat32;
|
||||
} else {
|
||||
format = AudioParams::kFormatSigned32;
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
if (data_is_float) {
|
||||
format = AudioParams::kFormatFloat64;
|
||||
} else {
|
||||
format = AudioParams::kFormatSigned64;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// We don't know this format...
|
||||
close();
|
||||
qCritical() << "Invalid format found" << bits_per_sample;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're good to go!
|
||||
params_ = AudioParams(sample_rate, channel_layout, format);
|
||||
|
||||
if (!find_str(&file_, "data")) {
|
||||
close();
|
||||
qCritical() << "No data tag found";
|
||||
return false;
|
||||
}
|
||||
|
||||
data_stream >> data_size_;
|
||||
data_position_ = file_.pos();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaveInput::is_open() const
|
||||
{
|
||||
return file_.isOpen();
|
||||
}
|
||||
|
||||
QByteArray WaveInput::read(qint64 length)
|
||||
{
|
||||
if (!is_open()) {
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
return file_.read(qMin(calculate_max_read(), length));
|
||||
}
|
||||
|
||||
QByteArray WaveInput::read(qint64 offset, qint64 length)
|
||||
{
|
||||
if (!is_open()) {
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
seek(offset);
|
||||
return file_.read(qMin(calculate_max_read(), length));
|
||||
}
|
||||
|
||||
qint64 WaveInput::read(qint64 offset, char *buffer, qint64 length)
|
||||
{
|
||||
if (!is_open()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Q_ASSERT(length > 0);
|
||||
|
||||
seek(offset);
|
||||
return file_.read(buffer, qMin(calculate_max_read(), length));
|
||||
}
|
||||
|
||||
bool WaveInput::seek(qint64 pos)
|
||||
{
|
||||
return file_.seek(data_position_ + qMin(pos, qint64(data_size_)));
|
||||
}
|
||||
|
||||
bool WaveInput::at_end() const
|
||||
{
|
||||
return file_.pos() == (data_position_ + data_size_);
|
||||
}
|
||||
|
||||
const AudioParams &WaveInput::params() const
|
||||
{
|
||||
return params_;
|
||||
}
|
||||
|
||||
void WaveInput::close()
|
||||
{
|
||||
if (file_.isOpen()) {
|
||||
file_.close();
|
||||
}
|
||||
}
|
||||
|
||||
const quint32 &WaveInput::data_length() const
|
||||
{
|
||||
return data_size_;
|
||||
}
|
||||
|
||||
int WaveInput::sample_count() const
|
||||
{
|
||||
return params_.bytes_to_samples(data_size_);
|
||||
}
|
||||
|
||||
bool WaveInput::find_str(QFile *f, const char *str)
|
||||
{
|
||||
qint64 pos = f->pos();
|
||||
while (f->read(4) != str) {
|
||||
if (f->atEnd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pos++;
|
||||
f->seek(pos);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 WaveInput::calculate_max_read() const
|
||||
{
|
||||
return data_size_ - (file_.pos() - data_position_ );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "waveoutput.h"
|
||||
|
||||
#include "render/audioparams.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const int16_t kWAVIntegerFormat = 1;
|
||||
const int16_t kWAVFloatFormat = 3;
|
||||
|
||||
WaveOutput::WaveOutput(const QString &f,
|
||||
const AudioParams& params) :
|
||||
file_(f),
|
||||
params_(params)
|
||||
{
|
||||
Q_ASSERT(params_.is_valid());
|
||||
}
|
||||
|
||||
WaveOutput::~WaveOutput()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool WaveOutput::open()
|
||||
{
|
||||
data_length_ = 0;
|
||||
|
||||
if (file_.open(QFile::WriteOnly)) {
|
||||
// RIFF header
|
||||
file_.write("RIFF");
|
||||
|
||||
// Total file size minus RIFF and this integer (minus 8 bytes, filled in later)
|
||||
write_int<int32_t>(&file_, 0);
|
||||
|
||||
// File type header
|
||||
file_.write("WAVE");
|
||||
|
||||
// Begin format descriptor chunk
|
||||
file_.write("fmt ");
|
||||
|
||||
// Format chunk size
|
||||
write_int<int32_t>(&file_, 16);
|
||||
|
||||
// Type of format
|
||||
switch (params_.format()) {
|
||||
case AudioParams::kFormatUnsigned8:
|
||||
case AudioParams::kFormatSigned16:
|
||||
case AudioParams::kFormatSigned32:
|
||||
case AudioParams::kFormatSigned64:
|
||||
write_int<int16_t>(&file_, kWAVIntegerFormat);
|
||||
break;
|
||||
case AudioParams::kFormatFloat32:
|
||||
case AudioParams::kFormatFloat64:
|
||||
write_int<int16_t>(&file_, kWAVFloatFormat);
|
||||
break;
|
||||
case AudioParams::kFormatInvalid:
|
||||
case AudioParams::kFormatCount:
|
||||
qWarning() << "Invalid sample format for WAVE audio";
|
||||
file_.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Number of channels
|
||||
write_int<int16_t>(&file_, static_cast<int16_t>(params_.channel_count()));
|
||||
|
||||
// Sample rate
|
||||
write_int<int32_t>(&file_, params_.sample_rate());
|
||||
|
||||
// Bytes per second
|
||||
write_int<int32_t>(&file_, params_.samples_to_bytes(params_.sample_rate()));
|
||||
|
||||
// Bytes per sample
|
||||
write_int<int16_t>(&file_, static_cast<int16_t>(params_.samples_to_bytes(1)));
|
||||
|
||||
// Bits per sample per channel
|
||||
write_int<int16_t>(&file_, static_cast<int16_t>(params_.bits_per_sample()));
|
||||
|
||||
// Data chunk header
|
||||
file_.write("data");
|
||||
|
||||
// Size of data chunk (filled in later)
|
||||
write_int<int32_t>(&file_, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WaveOutput::write(const QByteArray &bytes)
|
||||
{
|
||||
if (file_.isOpen()) {
|
||||
file_.write(bytes);
|
||||
|
||||
data_length_ += bytes.size();
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOutput::write(const char *bytes, int length)
|
||||
{
|
||||
if (file_.isOpen()) {
|
||||
file_.write(bytes, length);
|
||||
|
||||
data_length_ += length;
|
||||
}
|
||||
}
|
||||
|
||||
void WaveOutput::close()
|
||||
{
|
||||
if (file_.isOpen()) {
|
||||
|
||||
// Write file sizes
|
||||
file_.seek(4);
|
||||
write_int<int32_t>(&file_, data_length_ + 36);
|
||||
|
||||
file_.seek(40);
|
||||
write_int<int32_t>(&file_, data_length_);
|
||||
|
||||
file_.close();
|
||||
}
|
||||
}
|
||||
|
||||
const int& WaveOutput::data_length() const
|
||||
{
|
||||
return data_length_;
|
||||
}
|
||||
|
||||
const AudioParams &WaveOutput::params() const
|
||||
{
|
||||
return params_;
|
||||
}
|
||||
|
||||
void WaveOutput::switch_endianness(QByteArray& array)
|
||||
{
|
||||
int half_sz = array.size()/2;
|
||||
|
||||
for (int i=0;i<half_sz;i++) {
|
||||
int oppose_index = array.size() - i - 1;
|
||||
|
||||
char temp = array[i];
|
||||
array[i] = array[oppose_index];
|
||||
array[oppose_index] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void WaveOutput::write_int(QFile *file, T integer)
|
||||
{
|
||||
QByteArray bytes;
|
||||
bytes.resize(sizeof(T));
|
||||
memcpy(bytes.data(), &integer, static_cast<size_t>(bytes.size()));
|
||||
|
||||
// WAV expects little-endian, so if the integer is big endian we need to switch
|
||||
if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
|
||||
switch_endianness(bytes);
|
||||
}
|
||||
|
||||
file->write(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -833,6 +833,42 @@ void Core::SaveUnrecoveredList()
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::RevertProjectInternal(Project *p, bool by_opening_existing)
|
||||
{
|
||||
if (p->filename().isEmpty()) {
|
||||
QMessageBox::critical(main_window_, tr("Revert"),
|
||||
tr("This project has not yet been saved, therefore there is no last saved state to revert to."));
|
||||
} else {
|
||||
QString msg;
|
||||
|
||||
if (by_opening_existing) {
|
||||
msg = tr("The project \"%1\" is already open. By re-opening it, the project will revert to "
|
||||
"its last saved state. Any unsaved changes will be lost. Do you wish to continue?").arg(p->filename());
|
||||
} else {
|
||||
msg = tr("This will revert the project \"%1\" back to its last saved state. "
|
||||
"All unsaved changes will be lost. Do you wish to continue?").arg(p->name());
|
||||
}
|
||||
|
||||
if (QMessageBox::question(main_window_, tr("Revert"), msg, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
|
||||
// Copy filename because CloseProject is going to delete `p`
|
||||
QString filename = p->filename();
|
||||
|
||||
// Close project without prompting to save it
|
||||
CloseProjectBehavior b = kCloseProjectDontSave;
|
||||
CloseProject(p, false, b);
|
||||
|
||||
// NOTE: `p` will be deleted now, so don't try accessing it
|
||||
|
||||
// Re-open project at the filename
|
||||
OpenProjectInternal(filename);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Core::SaveAutorecovery()
|
||||
{
|
||||
if (Config::Current()[QStringLiteral("AutorecoveryEnabled")].toBool()) {
|
||||
@@ -1009,6 +1045,15 @@ bool Core::SaveAllProjects()
|
||||
return true;
|
||||
}
|
||||
|
||||
void Core::RevertActiveProject()
|
||||
{
|
||||
Project *p = GetActiveProject();
|
||||
|
||||
if (p) {
|
||||
RevertProjectInternal(p, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::CloseActiveProject()
|
||||
{
|
||||
return CloseProject(GetActiveProject(), true);
|
||||
@@ -1194,9 +1239,18 @@ void Core::OpenProjectInternal(const QString &filename, bool recovery_project)
|
||||
{
|
||||
// See if this project is open already
|
||||
foreach (Project* p, open_projects_) {
|
||||
if (p->filename() == filename) {
|
||||
// Comparing QFileInfos will handle case insensitivity and both slash directions on platforms
|
||||
// where this is necessary (not naming any names *cough* Windows)
|
||||
if (QFileInfo(p->filename()) == QFileInfo(filename)) {
|
||||
// This project is already open
|
||||
AddOpenProject(p);
|
||||
bool reverted = RevertProjectInternal(p, true);
|
||||
|
||||
if (!reverted) {
|
||||
// Calling this will focus attention to the project that the user just tried to re-open
|
||||
AddOpenProject(p);
|
||||
}
|
||||
|
||||
// Don't do anything else
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +329,11 @@ public slots:
|
||||
*/
|
||||
bool SaveAllProjects();
|
||||
|
||||
/**
|
||||
* @brief Revert project to last saved state (basically close and open it)
|
||||
*/
|
||||
void RevertActiveProject();
|
||||
|
||||
/**
|
||||
* @brief Closes the active project
|
||||
*
|
||||
@@ -506,6 +511,8 @@ private:
|
||||
|
||||
void SaveUnrecoveredList();
|
||||
|
||||
bool RevertProjectInternal(Project *p, bool by_opening_existing);
|
||||
|
||||
/**
|
||||
* @brief Internal main window object
|
||||
*/
|
||||
|
||||
@@ -45,9 +45,9 @@ QString KeySequenceEditor::action_name() {
|
||||
}
|
||||
|
||||
QString KeySequenceEditor::export_shortcut() {
|
||||
QString ks = keySequence().toString();
|
||||
if (ks != action->property("keydefault")) {
|
||||
return action->property("id").toString() + "\t" + ks;
|
||||
QKeySequence ks = keySequence();
|
||||
if (ks != action->property("keydefault").value<QKeySequence>()) {
|
||||
return action->property("id").toString() + "\t" + ks.toString();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -150,6 +150,9 @@ bool PreferencesKeyboardTab::refine_shortcut_list(const QString &s, QTreeWidgetI
|
||||
for (int i=0;i<keyboard_tree_->topLevelItemCount();i++) {
|
||||
refine_shortcut_list(s, keyboard_tree_->topLevelItem(i));
|
||||
}
|
||||
|
||||
// Return value is `all_children_are_hidden` which doesn't matter at the top level
|
||||
return false;
|
||||
} else {
|
||||
parent->setExpanded(!s.isEmpty());
|
||||
|
||||
@@ -158,7 +161,9 @@ bool PreferencesKeyboardTab::refine_shortcut_list(const QString &s, QTreeWidgetI
|
||||
for (int i=0;i<parent->childCount();i++) {
|
||||
QTreeWidgetItem* item = parent->child(i);
|
||||
if (item->childCount() > 0) {
|
||||
all_children_are_hidden = refine_shortcut_list(s, item);
|
||||
if (!refine_shortcut_list(s, item)) {
|
||||
all_children_are_hidden = false;
|
||||
}
|
||||
} else {
|
||||
item->setHidden(false);
|
||||
if (s.isEmpty()) {
|
||||
@@ -183,7 +188,6 @@ bool PreferencesKeyboardTab::refine_shortcut_list(const QString &s, QTreeWidgetI
|
||||
|
||||
return all_children_are_hidden;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PreferencesKeyboardTab::load_shortcut_file() {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "distort/transform/transformdistortnode.h"
|
||||
#include "generator/matrix/matrix.h"
|
||||
#include "generator/polygon/polygon.h"
|
||||
#include "generator/shape/shapenode.h"
|
||||
#include "generator/solid/solid.h"
|
||||
#include "generator/text/text.h"
|
||||
#include "filter/blur/blur.h"
|
||||
@@ -239,13 +240,14 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
||||
return new TimeRemapNode();
|
||||
case kSubtitleBlock:
|
||||
return new SubtitleBlock();
|
||||
case kShapeGenerator:
|
||||
return new ShapeNode();
|
||||
|
||||
case kInternalNodeCount:
|
||||
break;
|
||||
}
|
||||
|
||||
// We should never get here
|
||||
abort();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
kValueNode,
|
||||
kTimeRemapNode,
|
||||
kSubtitleBlock,
|
||||
kShapeGenerator,
|
||||
|
||||
// Count value
|
||||
kInternalNodeCount
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
add_subdirectory(matrix)
|
||||
add_subdirectory(polygon)
|
||||
add_subdirectory(shape)
|
||||
add_subdirectory(solid)
|
||||
add_subdirectory(text)
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/generator/shape/shapenode.cpp
|
||||
node/generator/shape/shapenode.h
|
||||
node/generator/shape/shapenodebase.cpp
|
||||
node/generator/shape/shapenodebase.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,89 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "shapenode.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super ShapeNodeBase
|
||||
|
||||
QString ShapeNode::kTypeInput = QStringLiteral("type_in");
|
||||
|
||||
ShapeNode::ShapeNode()
|
||||
{
|
||||
PrependInput(kTypeInput, NodeValue::kCombo);
|
||||
}
|
||||
|
||||
QString ShapeNode::Name() const
|
||||
{
|
||||
return tr("Shape");
|
||||
}
|
||||
|
||||
QString ShapeNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.shape");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> ShapeNode::Category() const
|
||||
{
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
|
||||
QString ShapeNode::Description() const
|
||||
{
|
||||
return tr("Generate a 2D primitive shape.");
|
||||
}
|
||||
|
||||
void ShapeNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTypeInput, tr("Type"));
|
||||
|
||||
// Coordinate with Type enum
|
||||
SetComboBoxStrings(kTypeInput, {tr("Rectangle"), tr("Ellipse")});
|
||||
}
|
||||
|
||||
ShaderCode ShapeNode::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/shape.frag")));
|
||||
}
|
||||
|
||||
NodeValueTable ShapeNode::Value(const QString &output, NodeValueDatabase &value) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(this, kTypeInput, value);
|
||||
job.InsertValue(this, kPositionInput, value);
|
||||
job.InsertValue(this, kSizeInput, value);
|
||||
job.InsertValue(this, kColorInput, value);
|
||||
job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution")));
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
return table;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,58 +18,44 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef WAVEINPUT_H
|
||||
#define WAVEINPUT_H
|
||||
#ifndef SHAPENODE_H
|
||||
#define SHAPENODE_H
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "render/audioparams.h"
|
||||
#include "shapenodebase.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class WaveInput
|
||||
class ShapeNode : public ShapeNodeBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaveInput(const QString& f);
|
||||
ShapeNode();
|
||||
|
||||
~WaveInput();
|
||||
enum Type {
|
||||
kRectangle,
|
||||
kEllipse
|
||||
};
|
||||
|
||||
DISABLE_COPY_MOVE(WaveInput)
|
||||
NODE_DEFAULT_DESTRUCTOR(ShapeNode)
|
||||
NODE_COPY_FUNCTION(ShapeNode)
|
||||
|
||||
bool open();
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
bool is_open() const;
|
||||
virtual void Retranslate() override;
|
||||
|
||||
QByteArray read(qint64 length);
|
||||
QByteArray read(qint64 offset, qint64 length);
|
||||
qint64 read(qint64 offset, char *buffer, qint64 length);
|
||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
||||
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
|
||||
|
||||
bool seek(qint64 pos);
|
||||
|
||||
bool at_end() const;
|
||||
|
||||
const AudioParams& params() const;
|
||||
|
||||
void close();
|
||||
|
||||
const quint32& data_length() const;
|
||||
|
||||
int sample_count() const;
|
||||
static QString kTypeInput;
|
||||
|
||||
private:
|
||||
bool find_str(QFile* f, const char* str);
|
||||
|
||||
qint64 calculate_max_read() const;
|
||||
|
||||
AudioParams params_;
|
||||
|
||||
QFile file_;
|
||||
|
||||
qint64 data_position_;
|
||||
|
||||
quint32 data_size_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WAVEINPUT_H
|
||||
#endif // SHAPENODE_H
|
||||
@@ -0,0 +1,49 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "shapenodebase.h"
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super Node
|
||||
|
||||
QString ShapeNodeBase::kPositionInput = QStringLiteral("pos_in");
|
||||
QString ShapeNodeBase::kSizeInput = QStringLiteral("size_in");
|
||||
QString ShapeNodeBase::kColorInput = QStringLiteral("color_in");
|
||||
|
||||
ShapeNodeBase::ShapeNodeBase()
|
||||
{
|
||||
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(10, 10));
|
||||
AddInput(kSizeInput, NodeValue::kVec2, QVector2D(100, 100));
|
||||
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 0.0, 0.0, 1.0)));
|
||||
}
|
||||
|
||||
void ShapeNodeBase::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kPositionInput, tr("Position"));
|
||||
SetInputName(kSizeInput, tr("Size"));
|
||||
SetInputName(kColorInput, tr("Color"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,51 +18,29 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef WAVEAUDIO_H
|
||||
#define WAVEAUDIO_H
|
||||
#ifndef SHAPENODEBASE_H
|
||||
#define SHAPENODEBASE_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
|
||||
#include "render/audioparams.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class WaveOutput
|
||||
class ShapeNodeBase : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaveOutput(const QString& f,
|
||||
const AudioParams& params);
|
||||
ShapeNodeBase();
|
||||
|
||||
~WaveOutput();
|
||||
NODE_DEFAULT_DESTRUCTOR(ShapeNodeBase)
|
||||
|
||||
DISABLE_COPY_MOVE(WaveOutput)
|
||||
virtual void Retranslate() override;
|
||||
|
||||
bool open();
|
||||
|
||||
void write(const QByteArray& bytes);
|
||||
void write(const char* bytes, int length);
|
||||
|
||||
void close();
|
||||
|
||||
const int& data_length() const;
|
||||
|
||||
const AudioParams& params() const;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
void write_int(QFile* file, T integer);
|
||||
|
||||
void switch_endianness(QByteArray &array);
|
||||
|
||||
QFile file_;
|
||||
|
||||
AudioParams params_;
|
||||
|
||||
int data_length_;
|
||||
static QString kPositionInput;
|
||||
static QString kSizeInput;
|
||||
static QString kColorInput;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WAVEAUDIO_H
|
||||
#endif // SHAPENODEBASE_H
|
||||
@@ -46,7 +46,11 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define NODE_DEFAULT_DESTRUCTOR(x) virtual ~x() override {DisconnectAll();}
|
||||
#define NODE_DEFAULT_DESTRUCTOR(x) \
|
||||
virtual ~x() override {DisconnectAll();}
|
||||
|
||||
#define NODE_COPY_FUNCTION(x) \
|
||||
virtual Node *copy() const override {return new x();}
|
||||
|
||||
class NodeGraph;
|
||||
class Folder;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QUuid>
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -390,7 +391,14 @@ void AudioPlaybackCache::UpdateOffsetsFrom(int index)
|
||||
|
||||
AudioPlaybackCache::PlaybackDevice *AudioPlaybackCache::CreatePlaybackDevice(QObject* parent) const
|
||||
{
|
||||
return new PlaybackDevice(playlist_, params_.bytes_per_sample_per_channel(), parent);
|
||||
PlaybackDevice *d = new PlaybackDevice(playlist_, params_.bytes_per_sample_per_channel(), parent);
|
||||
|
||||
// If we're child of a viewer, set the data limit so audio doesn't play beyond the length
|
||||
if (ViewerOutput *viewer = viewer_parent()) {
|
||||
d->SetDataLimit(params_.time_to_bytes_per_channel(viewer->GetAudioLength()));
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
AudioPlaybackCache::Segment::Segment(qint64 size)
|
||||
@@ -403,7 +411,8 @@ AudioPlaybackCache::PlaybackDevice::PlaybackDevice(const AudioPlaybackCache::Pla
|
||||
playlist_(playlist),
|
||||
current_segment_(0),
|
||||
segment_read_index_(0),
|
||||
sample_size_(sample_sz)
|
||||
sample_size_(sample_sz),
|
||||
limit_(INT64_MAX)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -437,10 +446,15 @@ qint64 AudioPlaybackCache::PlaybackDevice::readData(char *data, qint64 maxSize)
|
||||
|
||||
while (read_size < maxSize
|
||||
&& current_segment_ >= 0
|
||||
&& current_segment_ < playlist_.size()) {
|
||||
&& current_segment_ < playlist_.size()
|
||||
&& playlist_.at(current_segment_).offset() + segment_read_index_ < limit_) {
|
||||
const Segment& cs = playlist_.at(current_segment_);
|
||||
qint64 current_segment_sz = cs.size();
|
||||
|
||||
if (cs.offset() + current_segment_sz > limit_) {
|
||||
current_segment_sz = limit_ - cs.offset();
|
||||
}
|
||||
|
||||
QVector<QFile*> segment_files(cs.channels());
|
||||
segment_files.fill(nullptr);
|
||||
|
||||
@@ -480,13 +494,13 @@ qint64 AudioPlaybackCache::PlaybackDevice::readData(char *data, qint64 maxSize)
|
||||
|
||||
// Add to the read index
|
||||
segment_read_index_ += sample_size_;
|
||||
}
|
||||
|
||||
// If we've reached the end of this segment, tick the counter over to the next segment
|
||||
if (segment_read_index_ == current_segment_sz) {
|
||||
// Jump to the next file
|
||||
segment_read_index_ = 0;
|
||||
current_segment_++;
|
||||
}
|
||||
// If we've reached the end of this segment, tick the counter over to the next segment
|
||||
if (segment_read_index_ == current_segment_sz) {
|
||||
// Jump to the next file
|
||||
segment_read_index_ = 0;
|
||||
current_segment_++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -145,6 +145,11 @@ public:
|
||||
public:
|
||||
PlaybackDevice(const Playlist& playlist, int sample_sz, QObject* parent = nullptr);
|
||||
|
||||
void SetDataLimit(qint64 limit)
|
||||
{
|
||||
limit_ = limit;
|
||||
}
|
||||
|
||||
virtual ~PlaybackDevice() override;
|
||||
|
||||
virtual bool isSequential() const override
|
||||
@@ -178,6 +183,8 @@ public:
|
||||
|
||||
int sample_size_;
|
||||
|
||||
qint64 limit_;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Input texture coordinate
|
||||
varying vec2 ove_texcoord;
|
||||
|
||||
// Match with ShapeNode::Type
|
||||
#define SHAPE_RECTANGLE 0
|
||||
#define SHAPE_ELLIPSE 1
|
||||
|
||||
uniform vec2 pos_in;
|
||||
uniform vec2 size_in;
|
||||
uniform int type_in;
|
||||
uniform vec2 resolution_in;
|
||||
uniform vec4 color_in;
|
||||
|
||||
void main() {
|
||||
vec2 real_position = pos_in/resolution_in;
|
||||
vec2 real_size = size_in/resolution_in;
|
||||
|
||||
vec4 col = vec4(0.0);
|
||||
|
||||
if (type_in == SHAPE_RECTANGLE) {
|
||||
if (ove_texcoord.x >= real_position.x && ove_texcoord.y >= real_position.y
|
||||
&& ove_texcoord.x < real_position.x+real_size.x && ove_texcoord.y < real_position.y+real_size.y) {
|
||||
col = color_in;
|
||||
}
|
||||
} else if (type_in == SHAPE_ELLIPSE) {
|
||||
vec2 center = pos_in+size_in*0.5;
|
||||
float radius = size_in.y*0.5;
|
||||
float aspect_ratio = size_in.x/size_in.y;
|
||||
|
||||
vec2 offset = ove_texcoord*resolution_in - center;
|
||||
offset.x /= aspect_ratio;
|
||||
float d = length(offset)-radius;
|
||||
float t = clamp(d, 0.0, 1.0);
|
||||
|
||||
col = color_in * (1.0-t);
|
||||
}
|
||||
|
||||
gl_FragColor = col;
|
||||
}
|
||||
@@ -60,6 +60,7 @@ QIcon icon::Sequence;
|
||||
QIcon icon::Video;
|
||||
QIcon icon::Audio;
|
||||
QIcon icon::Image;
|
||||
QIcon icon::MiniMap;
|
||||
QIcon icon::TriUp;
|
||||
QIcon icon::TriLeft;
|
||||
QIcon icon::TriDown;
|
||||
@@ -110,6 +111,8 @@ void icon::LoadAll(const QString& theme)
|
||||
Audio = Create(theme, "audiosource");
|
||||
Image = Create(theme, "imagesource");
|
||||
|
||||
MiniMap = Create(theme, "map");
|
||||
|
||||
TriUp = Create(theme, "tri-up");
|
||||
TriLeft = Create(theme, "tri-left");
|
||||
TriDown = Create(theme, "tri-down");
|
||||
|
||||
@@ -65,6 +65,9 @@ extern QIcon Video;
|
||||
extern QIcon Audio;
|
||||
extern QIcon Image;
|
||||
|
||||
// Node Icons
|
||||
extern QIcon MiniMap;
|
||||
|
||||
// Triangle Arrows
|
||||
extern QIcon TriUp;
|
||||
extern QIcon TriLeft;
|
||||
|
||||
@@ -110,6 +110,10 @@
|
||||
<file>png/magnet.32.png</file>
|
||||
<file>png/magnet.64.png</file>
|
||||
<file>png/magnet.128.png</file>
|
||||
<file>png/map.16.png</file>
|
||||
<file>png/map.32.png</file>
|
||||
<file>png/map.64.png</file>
|
||||
<file>png/map.128.png</file>
|
||||
<file>png/minus.16.png</file>
|
||||
<file>png/minus.32.png</file>
|
||||
<file>png/minus.64.png</file>
|
||||
|
||||
|
After Width: | Height: | Size: 934 B |
|
After Width: | Height: | Size: 356 B |
|
After Width: | Height: | Size: 417 B |
|
After Width: | Height: | Size: 569 B |
@@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
sodipodi:docname="map5.svg"
|
||||
inkscape:version="1.2-dev (217ad3d, 2021-07-13)"
|
||||
sodipodi:version="0.32"
|
||||
id="svg2"
|
||||
height="64"
|
||||
width="64"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
inkscape:export-filename="/Users/pablo/olive_pablo/icons/iconview.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs14" />
|
||||
<sodipodi:namedview
|
||||
stroke="#3465a4"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-x="47"
|
||||
inkscape:window-height="927"
|
||||
inkscape:window-width="1633"
|
||||
inkscape:showpageshadow="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
showgrid="true"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:cy="32"
|
||||
inkscape:cx="31.957503"
|
||||
inkscape:zoom="11.765625"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1"
|
||||
bordercolor="#989898"
|
||||
pagecolor="#000000"
|
||||
id="base"
|
||||
fill="#729fcf"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:lockguides="true"
|
||||
guidecolor="#ff0000"
|
||||
guideopacity="0.30196078">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid_pixels"
|
||||
color="#ffffff"
|
||||
opacity="0.05098039"
|
||||
empcolor="#ffffff"
|
||||
empopacity="0.10196078"
|
||||
dotted="false"
|
||||
empspacing="8"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
visible="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
enabled="true" />
|
||||
<sodipodi:guide
|
||||
position="2,64"
|
||||
orientation="1,0"
|
||||
id="guide6912"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="62,64"
|
||||
orientation="1,0"
|
||||
id="guide6914"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="0,62"
|
||||
orientation="0,-1"
|
||||
id="guide6916"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="0,2"
|
||||
orientation="0,-1"
|
||||
id="guide6918"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="32,32"
|
||||
orientation="0,-1"
|
||||
id="guide6920"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="32,32"
|
||||
orientation="1,0"
|
||||
id="guide6922"
|
||||
inkscape:locked="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Martin Ruskov</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:source>http://commons.wikimedia.org/wiki/Tango_icon</dc:source>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Design"
|
||||
id="layer1"
|
||||
transform="translate(0,16)">
|
||||
<path
|
||||
style="font-variation-settings:normal;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.86;stop-color:#ffffff"
|
||||
d="M 5,37 V -5 h 54 v 42 z"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
id="path4087" />
|
||||
<path
|
||||
style="color:#ffffff;fill:#ffffff;fill-opacity:0.48;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||
d="m 32,13 c -1.656786,1.66e-4 -2.999834,1.343214 -3,3 v 11 c 1.66e-4,1.656786 1.343214,2.999834 3,3 h 17 c 1.656786,-1.66e-4 2.999834,-1.343214 3,-3 V 16 c -1.66e-4,-1.656786 -1.343214,-2.999834 -3,-3 z"
|
||||
id="path1776"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -110,6 +110,10 @@
|
||||
<file>png/magnet.32.png</file>
|
||||
<file>png/magnet.64.png</file>
|
||||
<file>png/magnet.128.png</file>
|
||||
<file>png/map.16.png</file>
|
||||
<file>png/map.32.png</file>
|
||||
<file>png/map.64.png</file>
|
||||
<file>png/map.128.png</file>
|
||||
<file>png/minus.16.png</file>
|
||||
<file>png/minus.32.png</file>
|
||||
<file>png/minus.64.png</file>
|
||||
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 460 B |
|
After Width: | Height: | Size: 629 B |
@@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
sodipodi:docname="map5.svg"
|
||||
inkscape:version="1.2-dev (217ad3d, 2021-07-13)"
|
||||
sodipodi:version="0.32"
|
||||
id="svg2"
|
||||
height="64"
|
||||
width="64"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
inkscape:export-filename="/Users/pablo/olive_pablo/icons/iconview.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs14" />
|
||||
<sodipodi:namedview
|
||||
stroke="#3465a4"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-x="47"
|
||||
inkscape:window-height="927"
|
||||
inkscape:window-width="1633"
|
||||
inkscape:showpageshadow="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
showgrid="true"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:cy="32"
|
||||
inkscape:cx="31.957503"
|
||||
inkscape:zoom="11.765625"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1"
|
||||
bordercolor="#202020"
|
||||
pagecolor="#ffffff"
|
||||
id="base"
|
||||
fill="#729fcf"
|
||||
inkscape:window-maximized="0"
|
||||
borderlayer="true"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:lockguides="true"
|
||||
guidecolor="#ff0000"
|
||||
guideopacity="0.30196078">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid_pixels"
|
||||
color="#000000"
|
||||
opacity="0.05098039"
|
||||
empcolor="#000000"
|
||||
empopacity="0.10196078"
|
||||
dotted="false"
|
||||
empspacing="8"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
visible="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
enabled="true" />
|
||||
<sodipodi:guide
|
||||
position="2,64"
|
||||
orientation="1,0"
|
||||
id="guide6912"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="62,64"
|
||||
orientation="1,0"
|
||||
id="guide6914"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="0,62"
|
||||
orientation="0,-1"
|
||||
id="guide6916"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="0,2"
|
||||
orientation="0,-1"
|
||||
id="guide6918"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="32,32"
|
||||
orientation="0,-1"
|
||||
id="guide6920"
|
||||
inkscape:locked="true" />
|
||||
<sodipodi:guide
|
||||
position="32,32"
|
||||
orientation="1,0"
|
||||
id="guide6922"
|
||||
inkscape:locked="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Martin Ruskov</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:source>http://commons.wikimedia.org/wiki/Tango_icon</dc:source>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Design"
|
||||
id="layer1"
|
||||
transform="translate(0,16)">
|
||||
<path
|
||||
style="font-variation-settings:normal;vector-effect:none;fill:none;fill-opacity:1;stroke:#212121;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.86;stop-color:#000000"
|
||||
d="M 5,37 V -5 h 54 v 42 z"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
id="path4087" />
|
||||
<path
|
||||
style="color:#000000;fill:#212121;fill-opacity:0.48;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||
d="m 32,13 c -1.656786,1.66e-4 -2.999834,1.343214 -3,3 v 11 c 1.66e-4,1.656786 1.343214,2.999834 3,3 h 17 c 1.656786,-1.66e-4 2.999834,-1.343214 3,-3 V 16 c -1.66e-4,-1.656786 -1.343214,-2.999834 -3,-3 z"
|
||||
id="path1776"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -87,7 +87,7 @@ void Menu::InsertAlphabetically(Menu *menu)
|
||||
InsertAlphabetically(menu->menuAction());
|
||||
}
|
||||
|
||||
void Menu::ConformItem(QAction *a, const QString &id, const QString &key)
|
||||
void Menu::ConformItem(QAction *a, const QString &id, const QKeySequence &key)
|
||||
{
|
||||
a->setProperty("id", id);
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
QAction* AddItem(const QString& id,
|
||||
const typename QtPrivate::FunctionPointer<Func>::Object *receiver,
|
||||
Func member,
|
||||
const QString &key = QString())
|
||||
const QKeySequence &key = QKeySequence())
|
||||
{
|
||||
QAction* a = CreateItem(this, id, receiver, member, key);
|
||||
|
||||
@@ -171,7 +171,7 @@ public:
|
||||
const QString& id,
|
||||
const typename QtPrivate::FunctionPointer<Func>::Object *receiver,
|
||||
Func member,
|
||||
const QString& key = QString())
|
||||
const QKeySequence &key = QKeySequence())
|
||||
{
|
||||
QAction* a = new QAction(parent);
|
||||
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
const QString& id,
|
||||
const typename QtPrivate::FunctionPointer<Func>::Object *receiver,
|
||||
Func member,
|
||||
const QString& key = QString())
|
||||
const QKeySequence &key = QKeySequence())
|
||||
{
|
||||
ConformItem(a, id, key);
|
||||
|
||||
@@ -224,7 +224,7 @@ public:
|
||||
|
||||
static void ConformItem(QAction *a,
|
||||
const QString& id,
|
||||
const QString& key = QString());
|
||||
const QKeySequence &key = QKeySequence());
|
||||
|
||||
private:
|
||||
void Init();
|
||||
|
||||
@@ -33,32 +33,32 @@ MenuShared* MenuShared::instance_ = nullptr;
|
||||
MenuShared::MenuShared()
|
||||
{
|
||||
// "New" menu shared items
|
||||
new_project_item_ = Menu::CreateItem(this, "newproj", Core::instance(), &Core::CreateNewProject, "Ctrl+N");
|
||||
new_sequence_item_ = Menu::CreateItem(this, "newseq", Core::instance(), &Core::CreateNewSequence, "Ctrl+Shift+N");
|
||||
new_project_item_ = Menu::CreateItem(this, "newproj", Core::instance(), &Core::CreateNewProject, tr("Ctrl+N"));
|
||||
new_sequence_item_ = Menu::CreateItem(this, "newseq", Core::instance(), &Core::CreateNewSequence, tr("Ctrl+Shift+N"));
|
||||
new_folder_item_ = Menu::CreateItem(this, "newfolder", Core::instance(), &Core::CreateNewFolder);
|
||||
|
||||
// "Edit" menu shared items
|
||||
edit_cut_item_ = Menu::CreateItem(this, "cut", this, &MenuShared::CutTriggered, "Ctrl+X");
|
||||
edit_copy_item_ = Menu::CreateItem(this, "copy", this, &MenuShared::CopyTriggered, "Ctrl+C");
|
||||
edit_paste_item_ = Menu::CreateItem(this, "paste", this, &MenuShared::PasteTriggered, "Ctrl+V");
|
||||
edit_paste_insert_item_ = Menu::CreateItem(this, "pasteinsert", this, &MenuShared::PasteInsertTriggered, "Ctrl+Shift+V");
|
||||
edit_duplicate_item_ = Menu::CreateItem(this, "duplicate", this, &MenuShared::DuplicateTriggered, "Ctrl+D");
|
||||
edit_delete_item_ = Menu::CreateItem(this, "delete", this, &MenuShared::DeleteSelectedTriggered, "Del");
|
||||
edit_ripple_delete_item_ = Menu::CreateItem(this, "rippledelete", this, &MenuShared::RippleDeleteTriggered, "Shift+Del");
|
||||
edit_split_item_ = Menu::CreateItem(this, "split", this, &MenuShared::SplitAtPlayheadTriggered, "Ctrl+K");
|
||||
edit_speedduration_item_ = Menu::CreateItem(this, "speeddur", this, &MenuShared::SpeedDurationTriggered, "Ctrl+R");
|
||||
edit_cut_item_ = Menu::CreateItem(this, "cut", this, &MenuShared::CutTriggered, tr("Ctrl+X"));
|
||||
edit_copy_item_ = Menu::CreateItem(this, "copy", this, &MenuShared::CopyTriggered, tr("Ctrl+C"));
|
||||
edit_paste_item_ = Menu::CreateItem(this, "paste", this, &MenuShared::PasteTriggered, tr("Ctrl+V"));
|
||||
edit_paste_insert_item_ = Menu::CreateItem(this, "pasteinsert", this, &MenuShared::PasteInsertTriggered, tr("Ctrl+Shift+V"));
|
||||
edit_duplicate_item_ = Menu::CreateItem(this, "duplicate", this, &MenuShared::DuplicateTriggered, tr("Ctrl+D"));
|
||||
edit_delete_item_ = Menu::CreateItem(this, "delete", this, &MenuShared::DeleteSelectedTriggered, tr("Del"));
|
||||
edit_ripple_delete_item_ = Menu::CreateItem(this, "rippledelete", this, &MenuShared::RippleDeleteTriggered, tr("Shift+Del"));
|
||||
edit_split_item_ = Menu::CreateItem(this, "split", this, &MenuShared::SplitAtPlayheadTriggered, tr("Ctrl+K"));
|
||||
edit_speedduration_item_ = Menu::CreateItem(this, "speeddur", this, &MenuShared::SpeedDurationTriggered, tr("Ctrl+R"));
|
||||
|
||||
// "In/Out" menu shared items
|
||||
inout_set_in_item_ = Menu::CreateItem(this, "setinpoint", this, &MenuShared::SetInTriggered, "I");
|
||||
inout_set_out_item_ = Menu::CreateItem(this, "setoutpoint", this, &MenuShared::SetOutTriggered, "O");
|
||||
inout_set_in_item_ = Menu::CreateItem(this, "setinpoint", this, &MenuShared::SetInTriggered, tr("I"));
|
||||
inout_set_out_item_ = Menu::CreateItem(this, "setoutpoint", this, &MenuShared::SetOutTriggered, tr("O"));
|
||||
inout_reset_in_item_ = Menu::CreateItem(this, "resetin", this, &MenuShared::ResetInTriggered);
|
||||
inout_reset_out_item_ = Menu::CreateItem(this, "resetout", this, &MenuShared::ResetOutTriggered);
|
||||
inout_clear_inout_item_ = Menu::CreateItem(this, "clearinout", this, &MenuShared::ClearInOutTriggered, "G");
|
||||
inout_clear_inout_item_ = Menu::CreateItem(this, "clearinout", this, &MenuShared::ClearInOutTriggered, tr("G"));
|
||||
|
||||
// "Clip Edit" menu shared items
|
||||
clip_add_default_transition_item_ = Menu::CreateItem(this, "deftransition", this, &MenuShared::DefaultTransitionTriggered, "Ctrl+Shift+D");
|
||||
clip_link_unlink_item_ = Menu::CreateItem(this, "linkunlink", this, &MenuShared::ToggleLinksTriggered, "Ctrl+L");
|
||||
clip_enable_disable_item_ = Menu::CreateItem(this, "enabledisable", this, &MenuShared::EnableDisableTriggered, "Shift+E");
|
||||
clip_add_default_transition_item_ = Menu::CreateItem(this, "deftransition", this, &MenuShared::DefaultTransitionTriggered, tr("Ctrl+Shift+D"));
|
||||
clip_link_unlink_item_ = Menu::CreateItem(this, "linkunlink", this, &MenuShared::ToggleLinksTriggered, tr("Ctrl+L"));
|
||||
clip_enable_disable_item_ = Menu::CreateItem(this, "enabledisable", this, &MenuShared::EnableDisableTriggered, tr("Shift+E"));
|
||||
clip_nest_item_ = Menu::CreateItem(this, "nest", this, &MenuShared::NestTriggered);
|
||||
|
||||
// TimeRuler menu shared items
|
||||
|
||||
@@ -514,7 +514,17 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
{
|
||||
QComboBox* cb = static_cast<QComboBox*>(widgets_.first());
|
||||
cb->blockSignals(true);
|
||||
cb->setCurrentIndex(input_.GetValueAtTime(node_time).toInt());
|
||||
int index = input_.GetValueAtTime(node_time).toInt();
|
||||
int real_row = -1;
|
||||
for (int i=0; i<cb->count(); i++) {
|
||||
if (!cb->itemText(i).isEmpty()) {
|
||||
real_row++;
|
||||
}
|
||||
|
||||
if (real_row == index) {
|
||||
cb->setCurrentIndex(i);
|
||||
}
|
||||
}
|
||||
cb->blockSignals(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -363,9 +363,13 @@ void NodeView::Duplicate()
|
||||
|
||||
void NodeView::SetColorLabel(int index)
|
||||
{
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
for (Node* node : qAsConst(selected_nodes_)) {
|
||||
node->SetOverrideColor(index);
|
||||
command->add_child(new NodeOverrideColorCommand(node, index));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
|
||||
void NodeView::ZoomIn()
|
||||
|
||||
@@ -43,13 +43,13 @@ void NodeViewToolBar::changeEvent(QEvent *e)
|
||||
void NodeViewToolBar::Retranslate()
|
||||
{
|
||||
add_node_btn_->setToolTip(tr("Add Node"));
|
||||
minimap_btn_->setText(tr("Mini-Map"));
|
||||
minimap_btn_->setToolTip(tr("Toggle Mini-Map"));
|
||||
}
|
||||
|
||||
void NodeViewToolBar::UpdateIcons()
|
||||
{
|
||||
add_node_btn_->setIcon(icon::Add);
|
||||
minimap_btn_->setIcon(icon::MiniMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -171,4 +171,26 @@ Project *NodeRenameCommand::GetRelevantProject() const
|
||||
return nodes_.isEmpty() ? nullptr : nodes_.first()->project();
|
||||
}
|
||||
|
||||
NodeOverrideColorCommand::NodeOverrideColorCommand(Node *node, int index) :
|
||||
node_(node),
|
||||
new_index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
Project *NodeOverrideColorCommand::GetRelevantProject() const
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
void NodeOverrideColorCommand::redo()
|
||||
{
|
||||
old_index_ = node_->GetOverrideColor();
|
||||
node_->SetOverrideColor(new_index_);
|
||||
}
|
||||
|
||||
void NodeOverrideColorCommand::undo()
|
||||
{
|
||||
node_->SetOverrideColor(old_index_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -342,6 +342,27 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class NodeOverrideColorCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeOverrideColorCommand(Node *node, int index);
|
||||
|
||||
virtual Project * GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node *node_;
|
||||
|
||||
int old_index_;
|
||||
|
||||
int new_index_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEVIEWUNDO_H
|
||||
|
||||
@@ -217,6 +217,8 @@ bool TimeBasedView::PlayheadMove(QMouseEvent *event)
|
||||
rational movement;
|
||||
|
||||
snap_service_->SnapPoint({mouse_time}, &movement, SnapService::kSnapAll & ~SnapService::kSnapToPlayhead);
|
||||
|
||||
mouse_time += movement;
|
||||
}
|
||||
|
||||
SetTime(mouse_time);
|
||||
|
||||
@@ -720,9 +720,13 @@ void TimelineWidget::ToggleSelectedEnabled()
|
||||
|
||||
void TimelineWidget::SetColorLabel(int index)
|
||||
{
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
b->SetOverrideColor(index);
|
||||
command->add_child(new NodeOverrideColorCommand(b, index));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
|
||||
void TimelineWidget::NudgeLeft()
|
||||
|
||||
@@ -426,9 +426,7 @@ void TrackPlaceBlockCommand::undo()
|
||||
}
|
||||
t->EndOperation();
|
||||
|
||||
if (ripple_remove_command_) {
|
||||
t->Node::InvalidateCache(insert_range, Track::kBlockInput);
|
||||
}
|
||||
t->Node::InvalidateCache(insert_range, Track::kBlockInput);
|
||||
|
||||
// Remove tracks if we added them
|
||||
for (int i=add_track_commands_.size()-1; i>=0; i--) {
|
||||
|
||||
@@ -151,6 +151,8 @@ void SeekableWidget::SeekToScreenPoint(int screen)
|
||||
snap_service_->SnapPoint({playhead_time},
|
||||
&movement,
|
||||
SnapService::kSnapAll & ~SnapService::kSnapToPlayhead);
|
||||
|
||||
playhead_time += movement;
|
||||
}
|
||||
|
||||
if (playhead_time != GetTime()) {
|
||||
|
||||
@@ -50,24 +50,26 @@ MainMenu::MainMenu(MainWindow *parent) :
|
||||
file_menu_ = new Menu(this, this, &MainMenu::FileMenuAboutToShow);
|
||||
file_new_menu_ = new Menu(file_menu_);
|
||||
MenuShared::instance()->AddItemsForNewMenu(file_new_menu_);
|
||||
file_open_item_ = file_menu_->AddItem("openproj", Core::instance(), &Core::OpenProject, "Ctrl+O");
|
||||
file_open_item_ = file_menu_->AddItem("openproj", Core::instance(), &Core::OpenProject, tr("Ctrl+O"));
|
||||
file_open_recent_menu_ = new Menu(file_menu_);
|
||||
file_open_recent_separator_ = file_open_recent_menu_->addSeparator();
|
||||
file_open_recent_clear_item_ = file_open_recent_menu_->AddItem("clearopenrecent", Core::instance(), &Core::ClearOpenRecentList);
|
||||
file_save_item_ = file_menu_->AddItem("saveproj", Core::instance(), &Core::SaveActiveProject, "Ctrl+S");
|
||||
file_save_as_item_ = file_menu_->AddItem("saveprojas", Core::instance(), &Core::SaveActiveProjectAs, "Ctrl+Shift+S");
|
||||
file_save_item_ = file_menu_->AddItem("saveproj", Core::instance(), &Core::SaveActiveProject, tr("Ctrl+S"));
|
||||
file_save_as_item_ = file_menu_->AddItem("saveprojas", Core::instance(), &Core::SaveActiveProjectAs, tr("Ctrl+Shift+S"));
|
||||
file_save_all_item_ = file_menu_->AddItem("saveallproj", Core::instance(), &Core::SaveAllProjects);
|
||||
file_menu_->addSeparator();
|
||||
file_import_item_ = file_menu_->AddItem("import", Core::instance(), &Core::DialogImportShow, "Ctrl+I");
|
||||
file_revert_item_ = file_menu_->AddItem("revert", Core::instance(), &Core::RevertActiveProject, tr("F12"));
|
||||
file_menu_->addSeparator();
|
||||
file_import_item_ = file_menu_->AddItem("import", Core::instance(), &Core::DialogImportShow, tr("Ctrl+I"));
|
||||
file_menu_->addSeparator();
|
||||
file_export_menu_ = new Menu(file_menu_);
|
||||
file_export_media_item_ = file_export_menu_->AddItem("export", Core::instance(), &Core::DialogExportShow, "Ctrl+M");
|
||||
file_export_media_item_ = file_export_menu_->AddItem("export", Core::instance(), &Core::DialogExportShow, tr("Ctrl+M"));
|
||||
file_menu_->addSeparator();
|
||||
file_close_project_item_ = file_menu_->AddItem("closeproj", Core::instance(), &Core::CloseActiveProject);
|
||||
file_close_all_projects_item_ = file_menu_->AddItem("closeallproj", Core::instance(), static_cast<bool(Core::*)()>(&Core::CloseAllProjects));
|
||||
file_close_all_except_item_ = file_menu_->AddItem("closeallexcept", Core::instance(), &Core::CloseAllExceptActiveProject);
|
||||
file_menu_->addSeparator();
|
||||
file_exit_item_ = file_menu_->AddItem("exit", parent, &MainWindow::close, "Ctrl+Q");
|
||||
file_exit_item_ = file_menu_->AddItem("exit", parent, &MainWindow::close);
|
||||
|
||||
//
|
||||
// EDIT MENU
|
||||
@@ -78,10 +80,10 @@ MainMenu::MainMenu(MainWindow *parent) :
|
||||
connect(edit_menu_, &Menu::aboutToHide, this, &MainMenu::EditMenuAboutToHide);
|
||||
|
||||
edit_undo_item_ = Core::instance()->undo_stack()->GetUndoAction();
|
||||
Menu::ConformItem(edit_undo_item_, "undo", "Ctrl+Z");
|
||||
Menu::ConformItem(edit_undo_item_, "undo", tr("Ctrl+Z"));
|
||||
edit_menu_->addAction(edit_undo_item_);
|
||||
edit_redo_item_ = Core::instance()->undo_stack()->GetRedoAction();
|
||||
Menu::ConformItem(edit_redo_item_, "redo", "Ctrl+Shift+Z");
|
||||
Menu::ConformItem(edit_redo_item_, "redo", tr("Ctrl+Shift+Z"));
|
||||
edit_menu_->addAction(edit_redo_item_);
|
||||
|
||||
edit_menu_->addSeparator();
|
||||
@@ -90,49 +92,49 @@ MainMenu::MainMenu(MainWindow *parent) :
|
||||
// Create "alternate delete" action so we can pick up backspace as well as delete while still
|
||||
// keeping them configurable
|
||||
edit_delete2_item_ = new QAction();
|
||||
Menu::ConformItem(edit_delete2_item_, "delete2", MenuShared::instance(), &MenuShared::DeleteSelectedTriggered, "Backspace");
|
||||
Menu::ConformItem(edit_delete2_item_, "delete2", MenuShared::instance(), &MenuShared::DeleteSelectedTriggered, tr("Backspace"));
|
||||
auto actions = edit_menu_->actions();
|
||||
edit_menu_->insertAction(actions.at(actions.indexOf(MenuShared::instance()->edit_delete_item()) + 1), edit_delete2_item_);
|
||||
}
|
||||
edit_menu_->addSeparator();
|
||||
edit_select_all_item_ = edit_menu_->AddItem("selectall", this, &MainMenu::SelectAllTriggered, "Ctrl+A");
|
||||
edit_deselect_all_item_ = edit_menu_->AddItem("deselectall", this, &MainMenu::DeselectAllTriggered, "Ctrl+Shift+A");
|
||||
edit_select_all_item_ = edit_menu_->AddItem("selectall", this, &MainMenu::SelectAllTriggered, tr("Ctrl+A"));
|
||||
edit_deselect_all_item_ = edit_menu_->AddItem("deselectall", this, &MainMenu::DeselectAllTriggered, tr("Ctrl+Shift+A"));
|
||||
edit_menu_->addSeparator();
|
||||
MenuShared::instance()->AddItemsForClipEditMenu(edit_menu_);
|
||||
edit_menu_->addSeparator();
|
||||
edit_insert_item_ = edit_menu_->AddItem("insert", this, &MainMenu::InsertTriggered, ",");
|
||||
edit_overwrite_item_ = edit_menu_->AddItem("overwrite", this, &MainMenu::OverwriteTriggered, ".");
|
||||
edit_insert_item_ = edit_menu_->AddItem("insert", this, &MainMenu::InsertTriggered, tr(","));
|
||||
edit_overwrite_item_ = edit_menu_->AddItem("overwrite", this, &MainMenu::OverwriteTriggered, tr("."));
|
||||
edit_menu_->addSeparator();
|
||||
edit_ripple_to_in_item_ = edit_menu_->AddItem("rippletoin", this, &MainMenu::RippleToInTriggered, "Q");
|
||||
edit_ripple_to_out_item_ = edit_menu_->AddItem("rippletoout", this, &MainMenu::RippleToOutTriggered, "W");
|
||||
edit_edit_to_in_item_ = edit_menu_->AddItem("edittoin", this, &MainMenu::EditToInTriggered, "Ctrl+Alt+Q");
|
||||
edit_edit_to_out_item_ = edit_menu_->AddItem("edittoout", this, &MainMenu::EditToOutTriggered, "Ctrl+Alt+W");
|
||||
edit_ripple_to_in_item_ = edit_menu_->AddItem("rippletoin", this, &MainMenu::RippleToInTriggered, tr("Q"));
|
||||
edit_ripple_to_out_item_ = edit_menu_->AddItem("rippletoout", this, &MainMenu::RippleToOutTriggered, tr("W"));
|
||||
edit_edit_to_in_item_ = edit_menu_->AddItem("edittoin", this, &MainMenu::EditToInTriggered, tr("Ctrl+Alt+Q"));
|
||||
edit_edit_to_out_item_ = edit_menu_->AddItem("edittoout", this, &MainMenu::EditToOutTriggered, tr("Ctrl+Alt+W"));
|
||||
edit_menu_->addSeparator();
|
||||
edit_nudge_left_item_ = edit_menu_->AddItem("nudgeleft", this, &MainMenu::NudgeLeftTriggered, "Alt+Left");
|
||||
edit_nudge_right_item_ = edit_menu_->AddItem("nudgeright", this, &MainMenu::NudgeRightTriggered, "Alt+Right");
|
||||
edit_move_in_to_playhead_item_ = edit_menu_->AddItem("moveintoplayhead", this, &MainMenu::MoveInToPlayheadTriggered, "[");
|
||||
edit_move_out_to_playhead_item_ = edit_menu_->AddItem("moveouttoplayhead", this, &MainMenu::MoveOutToPlayheadTriggered, "]");
|
||||
edit_nudge_left_item_ = edit_menu_->AddItem("nudgeleft", this, &MainMenu::NudgeLeftTriggered, tr("Alt+Left"));
|
||||
edit_nudge_right_item_ = edit_menu_->AddItem("nudgeright", this, &MainMenu::NudgeRightTriggered, tr("Alt+Right"));
|
||||
edit_move_in_to_playhead_item_ = edit_menu_->AddItem("moveintoplayhead", this, &MainMenu::MoveInToPlayheadTriggered, tr("["));
|
||||
edit_move_out_to_playhead_item_ = edit_menu_->AddItem("moveouttoplayhead", this, &MainMenu::MoveOutToPlayheadTriggered, tr("]"));
|
||||
edit_menu_->addSeparator();
|
||||
MenuShared::instance()->AddItemsForInOutMenu(edit_menu_);
|
||||
edit_delete_inout_item_ = edit_menu_->AddItem("deleteinout", this, &MainMenu::DeleteInOutTriggered, ";");
|
||||
edit_ripple_delete_inout_item_ = edit_menu_->AddItem("rippledeleteinout", this, &MainMenu::RippleDeleteInOutTriggered, "'");
|
||||
edit_delete_inout_item_ = edit_menu_->AddItem("deleteinout", this, &MainMenu::DeleteInOutTriggered, tr(";"));
|
||||
edit_ripple_delete_inout_item_ = edit_menu_->AddItem("rippledeleteinout", this, &MainMenu::RippleDeleteInOutTriggered, tr("'"));
|
||||
edit_menu_->addSeparator();
|
||||
edit_set_marker_item_ = edit_menu_->AddItem("marker", this, &MainMenu::SetMarkerTriggered, "M");
|
||||
edit_set_marker_item_ = edit_menu_->AddItem("marker", this, &MainMenu::SetMarkerTriggered, tr("M"));
|
||||
|
||||
//
|
||||
// VIEW MENU
|
||||
//
|
||||
view_menu_ = new Menu(this, this, &MainMenu::ViewMenuAboutToShow);
|
||||
view_zoom_in_item_ = view_menu_->AddItem("zoomin", this, &MainMenu::ZoomInTriggered, "=");
|
||||
view_zoom_out_item_ = view_menu_->AddItem("zoomout", this, &MainMenu::ZoomOutTriggered, "-");
|
||||
view_increase_track_height_item_ = view_menu_->AddItem("vzoomin", this, &MainMenu::IncreaseTrackHeightTriggered, "Ctrl+=");
|
||||
view_decrease_track_height_item_ = view_menu_->AddItem("vzoomout", this, &MainMenu::DecreaseTrackHeightTriggered, "Ctrl+-");
|
||||
view_show_all_item_ = view_menu_->AddItem("showall", this, &MainMenu::ToggleShowAllTriggered, "\\");
|
||||
view_zoom_in_item_ = view_menu_->AddItem("zoomin", this, &MainMenu::ZoomInTriggered, tr("="));
|
||||
view_zoom_out_item_ = view_menu_->AddItem("zoomout", this, &MainMenu::ZoomOutTriggered, tr("-"));
|
||||
view_increase_track_height_item_ = view_menu_->AddItem("vzoomin", this, &MainMenu::IncreaseTrackHeightTriggered, tr("Ctrl+="));
|
||||
view_decrease_track_height_item_ = view_menu_->AddItem("vzoomout", this, &MainMenu::DecreaseTrackHeightTriggered, tr("Ctrl+-"));
|
||||
view_show_all_item_ = view_menu_->AddItem("showall", this, &MainMenu::ToggleShowAllTriggered, tr("\\"));
|
||||
view_show_all_item_->setCheckable(true);
|
||||
|
||||
view_menu_->addSeparator();
|
||||
|
||||
view_full_screen_item_ = view_menu_->AddItem("fullscreen", parent, &MainWindow::SetFullscreen, "F11");
|
||||
view_full_screen_item_ = view_menu_->AddItem("fullscreen", parent, &MainWindow::SetFullscreen, tr("F11"));
|
||||
view_full_screen_item_->setCheckable(true);
|
||||
|
||||
view_full_screen_viewer_item_ = view_menu_->AddItem("fullscreenviewer", this, &MainMenu::FullScreenViewerTriggered);
|
||||
@@ -141,28 +143,28 @@ MainMenu::MainMenu(MainWindow *parent) :
|
||||
// PLAYBACK MENU
|
||||
//
|
||||
playback_menu_ = new Menu(this, this, &MainMenu::PlaybackMenuAboutToShow);
|
||||
playback_gotostart_item_ = playback_menu_->AddItem("gotostart", this, &MainMenu::GoToStartTriggered, "Home");
|
||||
playback_prevframe_item_ = playback_menu_->AddItem("prevframe", this, &MainMenu::PrevFrameTriggered, "Left");
|
||||
playback_playpause_item_ = playback_menu_->AddItem("playpause", this, &MainMenu::PlayPauseTriggered, "Space");
|
||||
playback_playinout_item_ = playback_menu_->AddItem("playintoout", this, &MainMenu::PlayInToOutTriggered, "Shift+Space");
|
||||
playback_nextframe_item_ = playback_menu_->AddItem("nextframe", this, &MainMenu::NextFrameTriggered, "Right");
|
||||
playback_gotoend_item_ = playback_menu_->AddItem("gotoend", this, &MainMenu::GoToEndTriggered, "End");
|
||||
playback_gotostart_item_ = playback_menu_->AddItem("gotostart", this, &MainMenu::GoToStartTriggered, tr("Home"));
|
||||
playback_prevframe_item_ = playback_menu_->AddItem("prevframe", this, &MainMenu::PrevFrameTriggered, tr("Left"));
|
||||
playback_playpause_item_ = playback_menu_->AddItem("playpause", this, &MainMenu::PlayPauseTriggered, tr("Space"));
|
||||
playback_playinout_item_ = playback_menu_->AddItem("playintoout", this, &MainMenu::PlayInToOutTriggered, tr("Shift+Space"));
|
||||
playback_nextframe_item_ = playback_menu_->AddItem("nextframe", this, &MainMenu::NextFrameTriggered, tr("Right"));
|
||||
playback_gotoend_item_ = playback_menu_->AddItem("gotoend", this, &MainMenu::GoToEndTriggered, tr("End"));
|
||||
|
||||
playback_menu_->addSeparator();
|
||||
|
||||
playback_prevcut_item_ = playback_menu_->AddItem("prevcut", this, &MainMenu::GoToPrevCutTriggered, "Up");
|
||||
playback_nextcut_item_ = playback_menu_->AddItem("nextcut", this, &MainMenu::GoToNextCutTriggered, "Down");
|
||||
playback_prevcut_item_ = playback_menu_->AddItem("prevcut", this, &MainMenu::GoToPrevCutTriggered, tr("Up"));
|
||||
playback_nextcut_item_ = playback_menu_->AddItem("nextcut", this, &MainMenu::GoToNextCutTriggered, tr("Down"));
|
||||
|
||||
playback_menu_->addSeparator();
|
||||
|
||||
playback_gotoin_item_ = playback_menu_->AddItem("gotoin", this, &MainMenu::GoToInTriggered, "Shift+I");
|
||||
playback_gotoout_item_ = playback_menu_->AddItem("gotoout", this, &MainMenu::GoToOutTriggered, "Shift+O");
|
||||
playback_gotoin_item_ = playback_menu_->AddItem("gotoin", this, &MainMenu::GoToInTriggered, tr("Shift+I"));
|
||||
playback_gotoout_item_ = playback_menu_->AddItem("gotoout", this, &MainMenu::GoToOutTriggered, tr("Shift+O"));
|
||||
|
||||
playback_menu_->addSeparator();
|
||||
|
||||
playback_shuttleleft_item_ = playback_menu_->AddItem("decspeed", this, &MainMenu::ShuttleLeftTriggered, "J");
|
||||
playback_shuttlestop_item_ = playback_menu_->AddItem("pause", this, &MainMenu::ShuttleStopTriggered, "K");
|
||||
playback_shuttleright_item_ = playback_menu_->AddItem("incspeed", this, &MainMenu::ShuttleRightTriggered, "L");
|
||||
playback_shuttleleft_item_ = playback_menu_->AddItem("decspeed", this, &MainMenu::ShuttleLeftTriggered, tr("J"));
|
||||
playback_shuttlestop_item_ = playback_menu_->AddItem("pause", this, &MainMenu::ShuttleStopTriggered, tr("K"));
|
||||
playback_shuttleright_item_ = playback_menu_->AddItem("incspeed", this, &MainMenu::ShuttleRightTriggered, tr("L"));
|
||||
|
||||
playback_menu_->addSeparator();
|
||||
|
||||
@@ -186,7 +188,7 @@ MainMenu::MainMenu(MainWindow *parent) :
|
||||
//
|
||||
window_menu_ = new Menu(this, this, &MainMenu::WindowMenuAboutToShow);
|
||||
window_menu_separator_ = window_menu_->addSeparator();
|
||||
window_maximize_panel_item_ = window_menu_->AddItem("maximizepanel", parent, &MainWindow::ToggleMaximizedPanel, "`");
|
||||
window_maximize_panel_item_ = window_menu_->AddItem("maximizepanel", parent, &MainWindow::ToggleMaximizedPanel, tr("`"));
|
||||
window_lock_layout_item_ = window_menu_->AddItem("lockpanels", PanelManager::instance(), &PanelManager::SetPanelsLocked);
|
||||
window_lock_layout_item_->setCheckable(true);
|
||||
window_menu_->addSeparator();
|
||||
@@ -200,71 +202,81 @@ MainMenu::MainMenu(MainWindow *parent) :
|
||||
|
||||
tools_group_ = new QActionGroup(this);
|
||||
|
||||
tools_pointer_item_ = tools_menu_->AddItem("pointertool", this, &MainMenu::ToolItemTriggered, "V");
|
||||
tools_pointer_item_ = tools_menu_->AddItem("pointertool", this, &MainMenu::ToolItemTriggered, tr("V"));
|
||||
tools_pointer_item_->setCheckable(true);
|
||||
tools_pointer_item_->setData(Tool::kPointer);
|
||||
tools_group_->addAction(tools_pointer_item_);
|
||||
|
||||
tools_edit_item_ = tools_menu_->AddItem("edittool", this, &MainMenu::ToolItemTriggered, "X");
|
||||
tools_edit_item_ = tools_menu_->AddItem("edittool", this, &MainMenu::ToolItemTriggered, tr("X"));
|
||||
tools_edit_item_->setCheckable(true);
|
||||
tools_edit_item_->setData(Tool::kEdit);
|
||||
tools_group_->addAction(tools_edit_item_);
|
||||
|
||||
tools_ripple_item_ = tools_menu_->AddItem("rippletool", this, &MainMenu::ToolItemTriggered, "B");
|
||||
tools_ripple_item_ = tools_menu_->AddItem("rippletool", this, &MainMenu::ToolItemTriggered, tr("B"));
|
||||
tools_ripple_item_->setCheckable(true);
|
||||
tools_ripple_item_->setData(Tool::kRipple);
|
||||
tools_group_->addAction(tools_ripple_item_);
|
||||
|
||||
tools_rolling_item_ = tools_menu_->AddItem("rollingtool", this, &MainMenu::ToolItemTriggered, "N");
|
||||
tools_rolling_item_ = tools_menu_->AddItem("rollingtool", this, &MainMenu::ToolItemTriggered, tr("N"));
|
||||
tools_rolling_item_->setCheckable(true);
|
||||
tools_rolling_item_->setData(Tool::kRolling);
|
||||
tools_group_->addAction(tools_rolling_item_);
|
||||
|
||||
tools_razor_item_ = tools_menu_->AddItem("razortool", this, &MainMenu::ToolItemTriggered, "C");
|
||||
tools_razor_item_ = tools_menu_->AddItem("razortool", this, &MainMenu::ToolItemTriggered, tr("C"));
|
||||
tools_razor_item_->setCheckable(true);
|
||||
tools_razor_item_->setData(Tool::kRazor);
|
||||
tools_group_->addAction(tools_razor_item_);
|
||||
|
||||
tools_slip_item_ = tools_menu_->AddItem("sliptool", this, &MainMenu::ToolItemTriggered, "Y");
|
||||
tools_slip_item_ = tools_menu_->AddItem("sliptool", this, &MainMenu::ToolItemTriggered, tr("Y"));
|
||||
tools_slip_item_->setCheckable(true);
|
||||
tools_slip_item_->setData(Tool::kSlip);
|
||||
tools_group_->addAction(tools_slip_item_);
|
||||
|
||||
tools_slide_item_ = tools_menu_->AddItem("slidetool", this, &MainMenu::ToolItemTriggered, "U");
|
||||
tools_slide_item_ = tools_menu_->AddItem("slidetool", this, &MainMenu::ToolItemTriggered, tr("U"));
|
||||
tools_slide_item_->setCheckable(true);
|
||||
tools_slide_item_->setData(Tool::kSlide);
|
||||
tools_group_->addAction(tools_slide_item_);
|
||||
|
||||
tools_hand_item_ = tools_menu_->AddItem("handtool", this, &MainMenu::ToolItemTriggered, "H");
|
||||
tools_hand_item_ = tools_menu_->AddItem("handtool", this, &MainMenu::ToolItemTriggered, tr("H"));
|
||||
tools_hand_item_->setCheckable(true);
|
||||
tools_hand_item_->setData(Tool::kHand);
|
||||
tools_group_->addAction(tools_hand_item_);
|
||||
|
||||
tools_zoom_item_ = tools_menu_->AddItem("zoomtool", this, &MainMenu::ToolItemTriggered, "Z");
|
||||
tools_zoom_item_ = tools_menu_->AddItem("zoomtool", this, &MainMenu::ToolItemTriggered, tr("Z"));
|
||||
tools_zoom_item_->setCheckable(true);
|
||||
tools_zoom_item_->setData(Tool::kZoom);
|
||||
tools_group_->addAction(tools_zoom_item_);
|
||||
|
||||
tools_transition_item_ = tools_menu_->AddItem("transitiontool", this, &MainMenu::ToolItemTriggered, "T");
|
||||
tools_transition_item_ = tools_menu_->AddItem("transitiontool", this, &MainMenu::ToolItemTriggered, tr("T"));
|
||||
tools_transition_item_->setCheckable(true);
|
||||
tools_transition_item_->setData(Tool::kTransition);
|
||||
tools_group_->addAction(tools_transition_item_);
|
||||
|
||||
tools_add_item_ = tools_menu_->AddItem("addtool", this, &MainMenu::ToolItemTriggered, tr("A"));
|
||||
tools_add_item_->setCheckable(true);
|
||||
tools_add_item_->setData(Tool::kAdd);
|
||||
tools_group_->addAction(tools_add_item_);
|
||||
|
||||
tools_record_item_ = tools_menu_->AddItem("recordtool", this, &MainMenu::ToolItemTriggered, tr("R"));
|
||||
tools_record_item_->setCheckable(true);
|
||||
tools_record_item_->setData(Tool::kRecord);
|
||||
tools_group_->addAction(tools_record_item_);
|
||||
|
||||
tools_menu_->addSeparator();
|
||||
|
||||
tools_snapping_item_ = tools_menu_->AddItem("snapping", Core::instance(), &Core::SetSnapping, "S");
|
||||
tools_snapping_item_ = tools_menu_->AddItem("snapping", Core::instance(), &Core::SetSnapping, tr("S"));
|
||||
tools_snapping_item_->setCheckable(true);
|
||||
tools_snapping_item_->setChecked(Core::instance()->snapping());
|
||||
|
||||
tools_menu_->addSeparator();
|
||||
|
||||
tools_preferences_item_ = tools_menu_->AddItem("prefs", Core::instance(), &Core::DialogPreferencesShow, "Ctrl+,");
|
||||
tools_preferences_item_ = tools_menu_->AddItem("prefs", Core::instance(), &Core::DialogPreferencesShow, tr("Ctrl+,"));
|
||||
|
||||
//
|
||||
// HELP MENU
|
||||
//
|
||||
help_menu_ = new Menu(this);
|
||||
help_action_search_item_ = help_menu_->AddItem("actionsearch", this, &MainMenu::ActionSearchTriggered, "/");
|
||||
help_action_search_item_ = help_menu_->AddItem("actionsearch", this, &MainMenu::ActionSearchTriggered, tr("/"));
|
||||
help_menu_->addSeparator();
|
||||
help_feedback_item_ = help_menu_->AddItem("feedback", this, &MainMenu::HelpFeedbackTriggered);
|
||||
help_menu_->addSeparator();
|
||||
@@ -669,6 +681,7 @@ void MainMenu::Retranslate()
|
||||
file_open_recent_menu_->setTitle(tr("Open &Recent"));
|
||||
file_open_recent_clear_item_->setText(tr("&Clear Recent List"));
|
||||
file_save_all_item_->setText(tr("Sa&ve All Projects"));
|
||||
file_revert_item_->setText(tr("Revert"));
|
||||
file_import_item_->setText(tr("&Import..."));
|
||||
file_export_menu_->setTitle(tr("&Export"));
|
||||
file_export_media_item_->setText(tr("&Media..."));
|
||||
@@ -748,6 +761,8 @@ void MainMenu::Retranslate()
|
||||
tools_hand_item_->setText(tr("Hand Tool"));
|
||||
tools_zoom_item_->setText(tr("Zoom Tool"));
|
||||
tools_transition_item_->setText(tr("Transition Tool"));
|
||||
tools_add_item_->setText(tr("Add Tool"));
|
||||
tools_record_item_->setText(tr("Record Tool"));
|
||||
tools_snapping_item_->setText(tr("Enable Snapping"));
|
||||
tools_preferences_item_->setText(tr("Preferences"));
|
||||
|
||||
|
||||
@@ -203,6 +203,7 @@ private:
|
||||
QAction* file_save_item_;
|
||||
QAction* file_save_as_item_;
|
||||
QAction* file_save_all_item_;
|
||||
QAction* file_revert_item_;
|
||||
QAction* file_import_item_;
|
||||
Menu* file_export_menu_;
|
||||
QAction* file_export_media_item_;
|
||||
@@ -279,6 +280,8 @@ private:
|
||||
QAction* tools_hand_item_;
|
||||
QAction* tools_zoom_item_;
|
||||
QAction* tools_transition_item_;
|
||||
QAction* tools_add_item_;
|
||||
QAction* tools_record_item_;
|
||||
QAction* tools_snapping_item_;
|
||||
QAction* tools_preferences_item_;
|
||||
|
||||
|
||||
@@ -677,7 +677,7 @@ void SaveCustomShortcutsInternal(QMenu* menu, QMap<QString, QString>* shortcuts)
|
||||
if (a->menu()) {
|
||||
SaveCustomShortcutsInternal(a->menu(), shortcuts);
|
||||
} else if (!a->isSeparator()) {
|
||||
QString default_shortcut = a->property("keydefault").toString();
|
||||
QString default_shortcut = a->property("keydefault").value<QKeySequence>().toString();
|
||||
QString current_shortcut = a->shortcut().toString();
|
||||
if (current_shortcut != default_shortcut) {
|
||||
QString action_id = a->property("id").toString();
|
||||
|
||||