Merge branch 'master' into transition-offsets

This commit is contained in:
itsmattkc
2021-08-12 14:41:30 -07:00
55 changed files with 1370 additions and 998 deletions
-4
View File
@@ -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
)
+6 -12
View File
@@ -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 &params)
{
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)
+11 -14
View File
@@ -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;
+1 -2
View File
@@ -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 &params);
CodecStream stream_;
+2 -3
View File
@@ -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) {
-1
View File
@@ -37,7 +37,6 @@ extern "C" {
#include <QWaitCondition>
#include "codec/decoder.h"
#include "codec/waveoutput.h"
#include "ffmpegframepool.h"
namespace olive {
+10 -5
View File
@@ -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;
}
-244
View File
@@ -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_ );
}
}
-180
View File
@@ -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);
}
}
+56 -2
View File
@@ -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;
}
}
+7
View File
@@ -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
*/
+3 -3
View File
@@ -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() {
+4 -2
View 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;
}
}
+1
View File
@@ -59,6 +59,7 @@ public:
kValueNode,
kTimeRemapNode,
kSubtitleBlock,
kShapeGenerator,
// Count value
kInternalNodeCount
+1
View File
@@ -16,6 +16,7 @@
add_subdirectory(matrix)
add_subdirectory(polygon)
add_subdirectory(shape)
add_subdirectory(solid)
add_subdirectory(text)
+24
View File
@@ -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
)
+89
View File
@@ -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
+5 -1
View File
@@ -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;
+23 -9
View File
@@ -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_++;
}
}
+7
View File
@@ -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_;
};
/**
+39
View File
@@ -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;
}
+501 -358
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -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");
+3
View File
@@ -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;
+4
View File
@@ -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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

+155
View File
@@ -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

+4
View File
@@ -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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

+155
View File
@@ -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

+1 -1
View File
@@ -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);
+4 -4
View File
@@ -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();
+17 -17
View File
@@ -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;
}
+5 -1
View File
@@ -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()
+1 -1
View File
@@ -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);
}
}
+22
View File
@@ -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_);
}
}
+21
View File
@@ -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
+2
View File
@@ -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);
+5 -1
View File
@@ -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--) {
+2
View File
@@ -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()) {
+72 -57
View File
@@ -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"));
+3
View File
@@ -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_;
+1 -1
View File
@@ -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();