merged fxrestructure
This commit is contained in:
+1
-1
@@ -24,7 +24,7 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
|
||||
# move external effects into bundle
|
||||
mkdir Olive.app/Contents/Effects
|
||||
cp effects/* Olive.app/Contents/Effects
|
||||
cp effects/shaders/* Olive.app/Contents/Effects
|
||||
|
||||
# distribute in zip
|
||||
zip -r Olive-$(git rev-parse --short HEAD)-macOS.zip Olive.app
|
||||
|
||||
@@ -914,7 +914,8 @@ EXCLUDE_SYMLINKS = NO
|
||||
# exclude all test directories for example use the pattern */test/*
|
||||
|
||||
EXCLUDE_PATTERNS = */.git/* \
|
||||
*/docs/*
|
||||
*/docs/* \
|
||||
*/include/*
|
||||
|
||||
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the
|
||||
|
||||
+9
-7
@@ -1,12 +1,14 @@
|
||||
|
||||
[The following template is primarily for bugs, you can remove it if you're requesting a feature.]
|
||||
[If you are reporting a bug, please try to fill out all the information below. If you are requesting a feature, you can clear this template.]
|
||||
|
||||
**Olive version:** [Git hash from window title or Help > About]
|
||||
**Source:** [AppImage, Website etc.]
|
||||
**Operating system:** [Ubuntu 18.04 64-bit]
|
||||
**CPU:** [Intel i5-4300U]
|
||||
**RAM:** [8GB]
|
||||
**GPU:** [NVIDIA Geforce GT 1030 2GB (Driver ver xxx.xx.xx)]
|
||||
### System Information
|
||||
|
||||
**Olive version:** [e.g. Git hash from window title or Help > About]
|
||||
**Source:** [e.g. AppImage, Website etc.]
|
||||
**Operating system:** [e.g. Ubuntu 18.04 64-bit]
|
||||
**CPU:** [e.g. Intel i5-4300U]
|
||||
**RAM:** [e.g. 8GB]
|
||||
**GPU:** [e.g. NVIDIA Geforce GT 1030 2GB (Driver ver xxx.xx.xx)]
|
||||
|
||||
### Detailed Description
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Olive is a free non-linear video editor for Windows, macOS, and Linux.
|
||||
|
||||

|
||||
|
||||
**Discover more:** [Website](https://www.olivevideoeditor.org/) | [Twitter](https://twitter.com/oliveteam) | [Discord](https://discord.gg/rvJMEYQ)
|
||||
**Discover more:** [Website](https://www.olivevideoeditor.org/) | [Twitter](https://twitter.com/oliveteam) | [Discord](https://discord.gg/4Ae9KZn)
|
||||
|
||||
Please consider supporting Olive:
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include "oliveglobal.h"
|
||||
#include "global/global.h"
|
||||
|
||||
AboutDialog::AboutDialog(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
|
||||
+87
-87
@@ -25,123 +25,123 @@
|
||||
#include <QMenuBar>
|
||||
#include <QLabel>
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
ActionSearch::ActionSearch(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
QDialog(parent)
|
||||
{
|
||||
setObjectName("ASDiag");
|
||||
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
|
||||
setObjectName("ASDiag");
|
||||
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
|
||||
|
||||
resize(parent->width()/3, parent->height()/3);
|
||||
resize(parent->width()/3, parent->height()/3);
|
||||
|
||||
setWindowFlags(Qt::Popup);
|
||||
setWindowFlags(Qt::Popup);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
ActionSearchEntry* entry_field = new ActionSearchEntry(this);
|
||||
QFont entry_field_font = entry_field->font();
|
||||
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2));
|
||||
entry_field->setFont(entry_field_font);
|
||||
entry_field->setPlaceholderText(tr("Search for action..."));
|
||||
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &)));
|
||||
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
|
||||
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up()));
|
||||
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down()));
|
||||
layout->addWidget(entry_field);
|
||||
ActionSearchEntry* entry_field = new ActionSearchEntry(this);
|
||||
QFont entry_field_font = entry_field->font();
|
||||
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2));
|
||||
entry_field->setFont(entry_field_font);
|
||||
entry_field->setPlaceholderText(tr("Search for action..."));
|
||||
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &)));
|
||||
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
|
||||
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up()));
|
||||
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down()));
|
||||
layout->addWidget(entry_field);
|
||||
|
||||
list_widget = new ActionSearchList(this);
|
||||
QFont list_widget_font = list_widget->font();
|
||||
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2));
|
||||
list_widget->setFont(list_widget_font);
|
||||
layout->addWidget(list_widget);
|
||||
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
|
||||
list_widget = new ActionSearchList(this);
|
||||
QFont list_widget_font = list_widget->font();
|
||||
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2));
|
||||
list_widget->setFont(list_widget_font);
|
||||
layout->addWidget(list_widget);
|
||||
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
|
||||
|
||||
entry_field->setFocus();
|
||||
entry_field->setFocus();
|
||||
}
|
||||
|
||||
void ActionSearch::search_update(const QString &s, const QString &p, QMenu *parent) {
|
||||
if (parent == nullptr) {
|
||||
list_widget->clear();
|
||||
if (parent == nullptr) {
|
||||
list_widget->clear();
|
||||
QList<QAction*> menus = olive::MainWindow->menuBar()->actions();
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
search_update(s, p, menu);
|
||||
}
|
||||
if (list_widget->count() > 0)
|
||||
list_widget->item(0)->setSelected(true);
|
||||
} else {
|
||||
QString menu_text;
|
||||
if (!p.isEmpty()) menu_text += p + " > ";
|
||||
menu_text += parent->title().replace("&", "");
|
||||
QList<QAction*> actions = parent->actions();
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
QAction* a = actions.at(i);
|
||||
if (!a->isSeparator()) {
|
||||
if (a->menu() != nullptr) {
|
||||
search_update(s, menu_text, a->menu());
|
||||
} else {
|
||||
QString comp = a->text().replace("&", "");
|
||||
if (comp.contains(s, Qt::CaseInsensitive)) {
|
||||
QListWidgetItem* item = new QListWidgetItem(QString("%1\n(%2)").arg(comp, menu_text), list_widget);
|
||||
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
|
||||
list_widget->addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
search_update(s, p, menu);
|
||||
}
|
||||
if (list_widget->count() > 0)
|
||||
list_widget->item(0)->setSelected(true);
|
||||
} else {
|
||||
QString menu_text;
|
||||
if (!p.isEmpty()) menu_text += p + " > ";
|
||||
menu_text += parent->title().replace("&", "");
|
||||
QList<QAction*> actions = parent->actions();
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
QAction* a = actions.at(i);
|
||||
if (!a->isSeparator()) {
|
||||
if (a->menu() != nullptr) {
|
||||
search_update(s, menu_text, a->menu());
|
||||
} else {
|
||||
QString comp = a->text().replace("&", "");
|
||||
if (comp.contains(s, Qt::CaseInsensitive)) {
|
||||
QListWidgetItem* item = new QListWidgetItem(QString("%1\n(%2)").arg(comp, menu_text), list_widget);
|
||||
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
|
||||
list_widget->addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionSearch::perform_action() {
|
||||
QList<QListWidgetItem*> selected_items = list_widget->selectedItems();
|
||||
if (list_widget->count() > 0 && selected_items.size() > 0) {
|
||||
QListWidgetItem* item = selected_items.at(0);
|
||||
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
|
||||
a->trigger();
|
||||
}
|
||||
accept();
|
||||
QList<QListWidgetItem*> selected_items = list_widget->selectedItems();
|
||||
if (list_widget->count() > 0 && selected_items.size() > 0) {
|
||||
QListWidgetItem* item = selected_items.at(0);
|
||||
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
|
||||
a->trigger();
|
||||
}
|
||||
accept();
|
||||
}
|
||||
|
||||
void ActionSearch::move_selection_up() {
|
||||
int lim = list_widget->count();
|
||||
for (int i=1;i<lim;i++) {
|
||||
if (list_widget->item(i)->isSelected()) {
|
||||
list_widget->item(i-1)->setSelected(true);
|
||||
list_widget->scrollToItem(list_widget->item(i-1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
int lim = list_widget->count();
|
||||
for (int i=1;i<lim;i++) {
|
||||
if (list_widget->item(i)->isSelected()) {
|
||||
list_widget->item(i-1)->setSelected(true);
|
||||
list_widget->scrollToItem(list_widget->item(i-1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionSearch::move_selection_down() {
|
||||
int lim = list_widget->count()-1;
|
||||
for (int i=0;i<lim;i++) {
|
||||
if (list_widget->item(i)->isSelected()) {
|
||||
list_widget->item(i+1)->setSelected(true);
|
||||
list_widget->scrollToItem(list_widget->item(i+1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
int lim = list_widget->count()-1;
|
||||
for (int i=0;i<lim;i++) {
|
||||
if (list_widget->item(i)->isSelected()) {
|
||||
list_widget->item(i+1)->setSelected(true);
|
||||
list_widget->scrollToItem(list_widget->item(i+1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {}
|
||||
|
||||
void ActionSearchEntry::keyPressEvent(QKeyEvent * event) {
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Up:
|
||||
emit moveSelectionUp();
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
emit moveSelectionDown();
|
||||
break;
|
||||
default:
|
||||
QLineEdit::keyPressEvent(event);
|
||||
}
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Up:
|
||||
emit moveSelectionUp();
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
emit moveSelectionDown();
|
||||
break;
|
||||
default:
|
||||
QLineEdit::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {}
|
||||
|
||||
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) {
|
||||
emit dbl_click();
|
||||
emit dbl_click();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <QComboBox>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "io/exportthread.h"
|
||||
#include "rendering/exportthread.h"
|
||||
|
||||
/**
|
||||
* @brief The AdvancedVideoDialog class
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "project/undo.h"
|
||||
#include "undo/undo.h"
|
||||
|
||||
ClipPropertiesDialog::ClipPropertiesDialog(QWidget *parent, QVector<Clip *> clips) :
|
||||
QDialog(parent)
|
||||
@@ -33,8 +33,8 @@ ClipPropertiesDialog::ClipPropertiesDialog(QWidget *parent, QVector<Clip *> clip
|
||||
layout->addWidget(new QLabel(tr("Duration:")), row, 0);
|
||||
|
||||
duration_field_ = new LabelSlider();
|
||||
duration_field_->set_display_type(LABELSLIDER_FRAMENUMBER);
|
||||
duration_field_->set_minimum_value(1);
|
||||
duration_field_->SetDisplayType(LabelSlider::FrameNumber);
|
||||
duration_field_->SetMinimum(1);
|
||||
layout->addWidget(duration_field_, row, 1);
|
||||
|
||||
row++;
|
||||
@@ -72,15 +72,15 @@ ClipPropertiesDialog::ClipPropertiesDialog(QWidget *parent, QVector<Clip *> clip
|
||||
}
|
||||
|
||||
// it's assumed all the clips come from the same sequence
|
||||
duration_field_->set_frame_rate(first_clip->sequence->frame_rate);
|
||||
duration_field_->SetFrameRate(first_clip->sequence->frame_rate);
|
||||
|
||||
if (all_clips_have_same_duration) {
|
||||
duration_field_->set_default_value(first_clip->length());
|
||||
duration_field_->set_value(first_clip->length(), false);
|
||||
duration_field_->set_maximum_value(first_clip->media_length());
|
||||
duration_field_->SetDefault(first_clip->length());
|
||||
duration_field_->SetValue(first_clip->length());
|
||||
duration_field_->SetMaximum(first_clip->media_length());
|
||||
} else {
|
||||
duration_field_->set_default_value(qSNaN());
|
||||
duration_field_->set_value(qSNaN(), false);
|
||||
duration_field_->SetDefault(qSNaN());
|
||||
duration_field_->SetValue(qSNaN());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "project/clip.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
class ClipPropertiesDialog : public QDialog {
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
#include <QScrollBar>
|
||||
#include <QEvent>
|
||||
|
||||
#include "debug.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
DebugDialog* olive::DebugDialog = nullptr;
|
||||
|
||||
DebugDialog::DebugDialog(QWidget *parent) : QDialog(parent) {
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
textEdit = new QTextEdit(this);
|
||||
textEdit->setWordWrapMode(QTextOption::NoWrap);
|
||||
textEdit = new QTextEdit(this);
|
||||
textEdit->setWordWrapMode(QTextOption::NoWrap);
|
||||
layout->addWidget(textEdit);
|
||||
|
||||
Retranslate();
|
||||
@@ -45,7 +45,7 @@ void DebugDialog::Retranslate()
|
||||
}
|
||||
|
||||
void DebugDialog::update_log() {
|
||||
textEdit->setHtml(get_debug_str());
|
||||
textEdit->setHtml(get_debug_str());
|
||||
textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
@@ -59,5 +59,5 @@ void DebugDialog::changeEvent(QEvent *e)
|
||||
}
|
||||
|
||||
void DebugDialog::showEvent(QShowEvent *) {
|
||||
update_log();
|
||||
update_log();
|
||||
}
|
||||
|
||||
@@ -30,13 +30,14 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "oliveglobal.h"
|
||||
#include "global/global.h"
|
||||
#include "dialogs/advancedvideodialog.h"
|
||||
#include "panels/panels.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "io/exportthread.h"
|
||||
#include "mainwindow.h"
|
||||
#include "rendering/audio.h"
|
||||
#include "rendering/exportthread.h"
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
@@ -341,6 +342,7 @@ void ExportDialog::render_thread_finished() {
|
||||
QMessageBox::Ok
|
||||
);
|
||||
}
|
||||
clear_audio_ibuffer();
|
||||
prep_ui_for_render(false);
|
||||
panel_sequence_viewer->viewer_widget->makeCurrent();
|
||||
panel_sequence_viewer->viewer_widget->initializeGL();
|
||||
@@ -543,7 +545,7 @@ void ExportDialog::export_action() {
|
||||
connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished()));
|
||||
connect(et, SIGNAL(progress_changed(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
|
||||
|
||||
close_active_clips(olive::ActiveSequence);
|
||||
close_active_clips(olive::ActiveSequence.get());
|
||||
|
||||
olive::Global->set_rendering_state(true);
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
#include <QProgressBar>
|
||||
#include <QGroupBox>
|
||||
|
||||
#include "project/sequence.h"
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
#include "io/exportthread.h"
|
||||
#include "rendering/exportthread.h"
|
||||
|
||||
class ExportDialog : public QDialog
|
||||
{
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "oliveglobal.h"
|
||||
#include "global/global.h"
|
||||
|
||||
#include "panels/panels.h"
|
||||
|
||||
#include "ui/sourcetable.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
LoadDialog::LoadDialog(QWidget *parent, const QString& fn, bool autorecovery, bool clear) :
|
||||
QDialog(parent)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include "project/projectelements.h"
|
||||
#include "io/loadthread.h"
|
||||
#include "project/loadthread.h"
|
||||
|
||||
class LoadDialog : public QDialog
|
||||
{
|
||||
|
||||
+142
-141
@@ -34,178 +34,179 @@
|
||||
#include "project/footage.h"
|
||||
#include "project/media.h"
|
||||
#include "panels/project.h"
|
||||
#include "project/undo.h"
|
||||
#include "undo/undo.h"
|
||||
#include "undo/undostack.h"
|
||||
|
||||
MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) :
|
||||
QDialog(parent),
|
||||
item(i)
|
||||
QDialog(parent),
|
||||
item(i)
|
||||
{
|
||||
setWindowTitle(tr("\"%1\" Properties").arg(i->get_name()));
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setWindowTitle(tr("\"%1\" Properties").arg(i->get_name()));
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
QGridLayout* grid = new QGridLayout(this);
|
||||
QGridLayout* grid = new QGridLayout(this);
|
||||
|
||||
int row = 0;
|
||||
int row = 0;
|
||||
|
||||
FootagePtr f = item->to_footage();
|
||||
Footage* f = item->to_footage();
|
||||
|
||||
grid->addWidget(new QLabel(tr("Tracks:"), this), row, 0, 1, 2);
|
||||
row++;
|
||||
grid->addWidget(new QLabel(tr("Tracks:"), this), row, 0, 1, 2);
|
||||
row++;
|
||||
|
||||
track_list = new QListWidget(this);
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
const FootageStream& fs = f->video_tracks.at(i);
|
||||
track_list = new QListWidget(this);
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
const FootageStream& fs = f->video_tracks.at(i);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem(
|
||||
tr("Video %1: %2x%3 %4FPS").arg(
|
||||
QString::number(fs.file_index),
|
||||
QString::number(fs.video_width),
|
||||
QString::number(fs.video_height),
|
||||
QString::number(fs.video_frame_rate)
|
||||
),
|
||||
track_list
|
||||
);
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setData(Qt::UserRole+1, fs.file_index);
|
||||
track_list->addItem(item);
|
||||
}
|
||||
for (int i=0;i<f->audio_tracks.size();i++) {
|
||||
const FootageStream& fs = f->audio_tracks.at(i);
|
||||
QListWidgetItem* item = new QListWidgetItem(
|
||||
tr("Audio %1: %2Hz %3").arg(
|
||||
QString::number(fs.file_index),
|
||||
QString::number(fs.audio_frequency),
|
||||
tr("%n channel(s)", "", fs.audio_channels)
|
||||
),
|
||||
track_list
|
||||
);
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setData(Qt::UserRole+1, fs.file_index);
|
||||
track_list->addItem(item);
|
||||
}
|
||||
grid->addWidget(track_list, row, 0, 1, 2);
|
||||
row++;
|
||||
QListWidgetItem* item = new QListWidgetItem(
|
||||
tr("Video %1: %2x%3 %4FPS").arg(
|
||||
QString::number(fs.file_index),
|
||||
QString::number(fs.video_width),
|
||||
QString::number(fs.video_height),
|
||||
QString::number(fs.video_frame_rate)
|
||||
),
|
||||
track_list
|
||||
);
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setData(Qt::UserRole+1, fs.file_index);
|
||||
track_list->addItem(item);
|
||||
}
|
||||
for (int i=0;i<f->audio_tracks.size();i++) {
|
||||
const FootageStream& fs = f->audio_tracks.at(i);
|
||||
QListWidgetItem* item = new QListWidgetItem(
|
||||
tr("Audio %1: %2Hz %3").arg(
|
||||
QString::number(fs.file_index),
|
||||
QString::number(fs.audio_frequency),
|
||||
tr("%n channel(s)", "", fs.audio_channels)
|
||||
),
|
||||
track_list
|
||||
);
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(fs.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setData(Qt::UserRole+1, fs.file_index);
|
||||
track_list->addItem(item);
|
||||
}
|
||||
grid->addWidget(track_list, row, 0, 1, 2);
|
||||
row++;
|
||||
|
||||
if (f->video_tracks.size() > 0) {
|
||||
// frame conforming
|
||||
if (!f->video_tracks.at(0).infinite_length) {
|
||||
grid->addWidget(new QLabel(tr("Conform to Frame Rate:"), this), row, 0);
|
||||
conform_fr = new QDoubleSpinBox(this);
|
||||
conform_fr->setMinimum(0.01);
|
||||
conform_fr->setValue(f->video_tracks.at(0).video_frame_rate * f->speed);
|
||||
grid->addWidget(conform_fr, row, 1);
|
||||
}
|
||||
if (f->video_tracks.size() > 0) {
|
||||
// frame conforming
|
||||
if (!f->video_tracks.at(0).infinite_length) {
|
||||
grid->addWidget(new QLabel(tr("Conform to Frame Rate:"), this), row, 0);
|
||||
conform_fr = new QDoubleSpinBox(this);
|
||||
conform_fr->setMinimum(0.01);
|
||||
conform_fr->setValue(f->video_tracks.at(0).video_frame_rate * f->speed);
|
||||
grid->addWidget(conform_fr, row, 1);
|
||||
}
|
||||
|
||||
row++;
|
||||
row++;
|
||||
|
||||
// premultiplied alpha mode
|
||||
premultiply_alpha_setting = new QCheckBox(tr("Alpha is Premultiplied"), this);
|
||||
premultiply_alpha_setting->setChecked(f->alpha_is_premultiplied);
|
||||
grid->addWidget(premultiply_alpha_setting, row, 0);
|
||||
// premultiplied alpha mode
|
||||
premultiply_alpha_setting = new QCheckBox(tr("Alpha is Premultiplied"), this);
|
||||
premultiply_alpha_setting->setChecked(f->alpha_is_premultiplied);
|
||||
grid->addWidget(premultiply_alpha_setting, row, 0);
|
||||
|
||||
row++;
|
||||
row++;
|
||||
|
||||
// deinterlacing mode
|
||||
interlacing_box = new QComboBox(this);
|
||||
interlacing_box->addItem(
|
||||
tr("Auto (%1)").arg(
|
||||
get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing)
|
||||
)
|
||||
);
|
||||
interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE));
|
||||
interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST));
|
||||
interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST));
|
||||
// deinterlacing mode
|
||||
interlacing_box = new QComboBox(this);
|
||||
interlacing_box->addItem(
|
||||
tr("Auto (%1)").arg(
|
||||
get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing)
|
||||
)
|
||||
);
|
||||
interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE));
|
||||
interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST));
|
||||
interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST));
|
||||
|
||||
interlacing_box->setCurrentIndex(
|
||||
(f->video_tracks.at(0).video_auto_interlacing == f->video_tracks.at(0).video_interlacing)
|
||||
? 0
|
||||
: f->video_tracks.at(0).video_interlacing + 1);
|
||||
interlacing_box->setCurrentIndex(
|
||||
(f->video_tracks.at(0).video_auto_interlacing == f->video_tracks.at(0).video_interlacing)
|
||||
? 0
|
||||
: f->video_tracks.at(0).video_interlacing + 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Interlacing:"), this), row, 0);
|
||||
grid->addWidget(interlacing_box, row, 1);
|
||||
grid->addWidget(new QLabel(tr("Interlacing:"), this), row, 0);
|
||||
grid->addWidget(interlacing_box, row, 1);
|
||||
|
||||
row++;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
|
||||
name_box = new QLineEdit(item->get_name(), this);
|
||||
grid->addWidget(new QLabel(tr("Name:"), this), row, 0);
|
||||
grid->addWidget(name_box, row, 1);
|
||||
row++;
|
||||
name_box = new QLineEdit(item->get_name(), this);
|
||||
grid->addWidget(new QLabel(tr("Name:"), this), row, 0);
|
||||
grid->addWidget(name_box, row, 1);
|
||||
row++;
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
buttons->setCenterButtons(true);
|
||||
grid->addWidget(buttons, row, 0, 1, 2);
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
buttons->setCenterButtons(true);
|
||||
grid->addWidget(buttons, row, 0, 1, 2);
|
||||
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
}
|
||||
|
||||
void MediaPropertiesDialog::accept() {
|
||||
FootagePtr f = item->to_footage();
|
||||
Footage* f = item->to_footage();
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
// set track enable
|
||||
for (int i=0;i<track_list->count();i++) {
|
||||
QListWidgetItem* item = track_list->item(i);
|
||||
const QVariant& data = item->data(Qt::UserRole+1);
|
||||
if (!data.isNull()) {
|
||||
int index = data.toInt();
|
||||
bool found = false;
|
||||
for (int j=0;j<f->video_tracks.size();j++) {
|
||||
if (f->video_tracks.at(j).file_index == index) {
|
||||
f->video_tracks[j].enabled = (item->checkState() == Qt::Checked);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
for (int j=0;j<f->audio_tracks.size();j++) {
|
||||
if (f->audio_tracks.at(j).file_index == index) {
|
||||
f->audio_tracks[j].enabled = (item->checkState() == Qt::Checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// set track enable
|
||||
for (int i=0;i<track_list->count();i++) {
|
||||
QListWidgetItem* item = track_list->item(i);
|
||||
const QVariant& data = item->data(Qt::UserRole+1);
|
||||
if (!data.isNull()) {
|
||||
int index = data.toInt();
|
||||
bool found = false;
|
||||
for (int j=0;j<f->video_tracks.size();j++) {
|
||||
if (f->video_tracks.at(j).file_index == index) {
|
||||
f->video_tracks[j].enabled = (item->checkState() == Qt::Checked);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
for (int j=0;j<f->audio_tracks.size();j++) {
|
||||
if (f->audio_tracks.at(j).file_index == index) {
|
||||
f->audio_tracks[j].enabled = (item->checkState() == Qt::Checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool refresh_clips = false;
|
||||
bool refresh_clips = false;
|
||||
|
||||
// set interlacing
|
||||
if (f->video_tracks.size() > 0) {
|
||||
if (interlacing_box->currentIndex() > 0) {
|
||||
ca->append(new SetInt(&f->video_tracks[0].video_interlacing, interlacing_box->currentIndex() - 1));
|
||||
} else {
|
||||
ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing));
|
||||
}
|
||||
// set interlacing
|
||||
if (f->video_tracks.size() > 0) {
|
||||
if (interlacing_box->currentIndex() > 0) {
|
||||
ca->append(new SetInt(&f->video_tracks[0].video_interlacing, interlacing_box->currentIndex() - 1));
|
||||
} else {
|
||||
ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing));
|
||||
}
|
||||
|
||||
// set frame rate conform
|
||||
if (!f->video_tracks.at(0).infinite_length) {
|
||||
if (!qFuzzyCompare(conform_fr->value(), f->video_tracks.at(0).video_frame_rate)) {
|
||||
ca->append(new SetDouble(&f->speed, f->speed, conform_fr->value()/f->video_tracks.at(0).video_frame_rate));
|
||||
refresh_clips = true;
|
||||
}
|
||||
}
|
||||
// set frame rate conform
|
||||
if (!f->video_tracks.at(0).infinite_length) {
|
||||
if (!qFuzzyCompare(conform_fr->value(), f->video_tracks.at(0).video_frame_rate)) {
|
||||
ca->append(new SetDouble(&f->speed, f->speed, conform_fr->value()/f->video_tracks.at(0).video_frame_rate));
|
||||
refresh_clips = true;
|
||||
}
|
||||
}
|
||||
|
||||
// set premultiplied alpha
|
||||
f->alpha_is_premultiplied = premultiply_alpha_setting->isChecked();
|
||||
}
|
||||
// set premultiplied alpha
|
||||
f->alpha_is_premultiplied = premultiply_alpha_setting->isChecked();
|
||||
}
|
||||
|
||||
// set name
|
||||
MediaRename* mr = new MediaRename(item, name_box->text());
|
||||
// set name
|
||||
MediaRename* mr = new MediaRename(item, name_box->text());
|
||||
|
||||
ca->append(mr);
|
||||
ca->appendPost(new CloseAllClipsCommand());
|
||||
ca->appendPost(new UpdateFootageTooltip(item));
|
||||
if (refresh_clips) {
|
||||
ca->appendPost(new RefreshClips(item));
|
||||
}
|
||||
ca->appendPost(new UpdateViewer());
|
||||
ca->append(mr);
|
||||
ca->appendPost(new CloseAllClipsCommand());
|
||||
ca->appendPost(new UpdateFootageTooltip(item));
|
||||
if (refresh_clips) {
|
||||
ca->appendPost(new RefreshClips(item));
|
||||
}
|
||||
ca->appendPost(new UpdateViewer());
|
||||
|
||||
olive::UndoStack.push(ca);
|
||||
olive::UndoStack.push(ca);
|
||||
|
||||
QDialog::accept();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
+188
-187
@@ -22,9 +22,10 @@
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "panels/project.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/undo.h"
|
||||
#include "project/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "undo/undostack.h"
|
||||
#include "undo/undo.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "project/media.h"
|
||||
#include "rendering/audio.h"
|
||||
@@ -40,245 +41,245 @@
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing) :
|
||||
QDialog(parent),
|
||||
existing_item(existing)
|
||||
QDialog(parent),
|
||||
existing_item(existing)
|
||||
{
|
||||
setup_ui();
|
||||
setup_ui();
|
||||
|
||||
if (existing != nullptr) {
|
||||
existing_sequence = existing->to_sequence();
|
||||
setWindowTitle(tr("Editing \"%1\"").arg(existing_sequence->name));
|
||||
if (existing != nullptr) {
|
||||
existing_sequence = existing->to_sequence();
|
||||
setWindowTitle(tr("Editing \"%1\"").arg(existing_sequence->name));
|
||||
|
||||
width_numeric->setValue(existing_sequence->width);
|
||||
height_numeric->setValue(existing_sequence->height);
|
||||
int comp_rate = qRound(existing_sequence->frame_rate*100);
|
||||
for (int i=0;i<frame_rate_combobox->count();i++) {
|
||||
if (qRound(frame_rate_combobox->itemData(i).toDouble()*100) == comp_rate) {
|
||||
frame_rate_combobox->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sequence_name_edit->setText(existing_sequence->name);
|
||||
for (int i=0;i<audio_frequency_combobox->count();i++) {
|
||||
if (audio_frequency_combobox->itemData(i) == existing_sequence->audio_frequency) {
|
||||
audio_frequency_combobox->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
existing_sequence = nullptr;
|
||||
setWindowTitle(tr("New Sequence"));
|
||||
}
|
||||
width_numeric->setValue(existing_sequence->width);
|
||||
height_numeric->setValue(existing_sequence->height);
|
||||
int comp_rate = qRound(existing_sequence->frame_rate*100);
|
||||
for (int i=0;i<frame_rate_combobox->count();i++) {
|
||||
if (qRound(frame_rate_combobox->itemData(i).toDouble()*100) == comp_rate) {
|
||||
frame_rate_combobox->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sequence_name_edit->setText(existing_sequence->name);
|
||||
for (int i=0;i<audio_frequency_combobox->count();i++) {
|
||||
if (audio_frequency_combobox->itemData(i) == existing_sequence->audio_frequency) {
|
||||
audio_frequency_combobox->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
existing_sequence = nullptr;
|
||||
setWindowTitle(tr("New Sequence"));
|
||||
}
|
||||
}
|
||||
|
||||
NewSequenceDialog::~NewSequenceDialog()
|
||||
{}
|
||||
|
||||
void NewSequenceDialog::set_sequence_name(const QString& s) {
|
||||
sequence_name_edit->setText(s);
|
||||
sequence_name_edit->setText(s);
|
||||
}
|
||||
|
||||
void NewSequenceDialog::create() {
|
||||
if (existing_sequence == nullptr) {
|
||||
SequencePtr s(new Sequence());
|
||||
if (existing_sequence == nullptr) {
|
||||
SequencePtr s = std::make_shared<Sequence>();
|
||||
|
||||
s->name = sequence_name_edit->text();
|
||||
s->width = width_numeric->value();
|
||||
s->height = height_numeric->value();
|
||||
s->frame_rate = frame_rate_combobox->currentData().toDouble();
|
||||
s->audio_frequency = audio_frequency_combobox->currentData().toInt();
|
||||
s->audio_layout = AV_CH_LAYOUT_STEREO;
|
||||
s->name = sequence_name_edit->text();
|
||||
s->width = width_numeric->value();
|
||||
s->height = height_numeric->value();
|
||||
s->frame_rate = frame_rate_combobox->currentData().toDouble();
|
||||
s->audio_frequency = audio_frequency_combobox->currentData().toInt();
|
||||
s->audio_layout = AV_CH_LAYOUT_STEREO;
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
panel_project->create_sequence_internal(ca, s, true, nullptr);
|
||||
olive::UndoStack.push(ca);
|
||||
} else {
|
||||
ComboAction* ca = new ComboAction();
|
||||
ComboAction* ca = new ComboAction();
|
||||
panel_project->create_sequence_internal(ca, s, true, nullptr);
|
||||
olive::UndoStack.push(ca);
|
||||
} else {
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
double multiplier = frame_rate_combobox->currentData().toDouble() / existing_sequence->frame_rate;
|
||||
double multiplier = frame_rate_combobox->currentData().toDouble() / existing_sequence->frame_rate;
|
||||
|
||||
EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_sequence);
|
||||
esc->name = sequence_name_edit->text();
|
||||
esc->width = width_numeric->value();
|
||||
esc->height = height_numeric->value();
|
||||
esc->frame_rate = frame_rate_combobox->currentData().toDouble();
|
||||
esc->audio_frequency = audio_frequency_combobox->currentData().toInt();
|
||||
esc->audio_layout = AV_CH_LAYOUT_STEREO;
|
||||
ca->append(esc);
|
||||
EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_sequence);
|
||||
esc->name = sequence_name_edit->text();
|
||||
esc->width = width_numeric->value();
|
||||
esc->height = height_numeric->value();
|
||||
esc->frame_rate = frame_rate_combobox->currentData().toDouble();
|
||||
esc->audio_frequency = audio_frequency_combobox->currentData().toInt();
|
||||
esc->audio_layout = AV_CH_LAYOUT_STEREO;
|
||||
ca->append(esc);
|
||||
|
||||
for (int i=0;i<existing_sequence->clips.size();i++) {
|
||||
ClipPtr c = existing_sequence->clips.at(i);
|
||||
if (c != nullptr) {
|
||||
c->refactor_frame_rate(ca, multiplier, true);
|
||||
}
|
||||
}
|
||||
for (int i=0;i<existing_sequence->clips.size();i++) {
|
||||
ClipPtr c = existing_sequence->clips.at(i);
|
||||
if (c != nullptr) {
|
||||
c->refactor_frame_rate(ca, multiplier, true);
|
||||
}
|
||||
}
|
||||
|
||||
olive::UndoStack.push(ca);
|
||||
}
|
||||
olive::UndoStack.push(ca);
|
||||
}
|
||||
|
||||
accept();
|
||||
accept();
|
||||
}
|
||||
|
||||
void NewSequenceDialog::preset_changed(int index) {
|
||||
switch (index) {
|
||||
case 0: // FILM 4K
|
||||
width_numeric->setValue(4096);
|
||||
height_numeric->setValue(2160);
|
||||
break;
|
||||
case 1: // TV 4K
|
||||
width_numeric->setValue(3840);
|
||||
height_numeric->setValue(2160);
|
||||
break;
|
||||
case 2: // 1080p
|
||||
width_numeric->setValue(1920);
|
||||
height_numeric->setValue(1080);
|
||||
break;
|
||||
case 3: // 720p
|
||||
width_numeric->setValue(1280);
|
||||
height_numeric->setValue(720);
|
||||
break;
|
||||
case 4: // 480p
|
||||
width_numeric->setValue(640);
|
||||
height_numeric->setValue(480);
|
||||
break;
|
||||
case 5: // 360p
|
||||
width_numeric->setValue(640);
|
||||
height_numeric->setValue(360);
|
||||
break;
|
||||
case 6: // 240p
|
||||
width_numeric->setValue(320);
|
||||
height_numeric->setValue(240);
|
||||
break;
|
||||
case 7: // 144p
|
||||
width_numeric->setValue(192);
|
||||
height_numeric->setValue(144);
|
||||
break;
|
||||
case 8: // NTSC (480i)
|
||||
width_numeric->setValue(720);
|
||||
height_numeric->setValue(480);
|
||||
break;
|
||||
case 9: // PAL (576i)
|
||||
width_numeric->setValue(720);
|
||||
height_numeric->setValue(576);
|
||||
break;
|
||||
}
|
||||
switch (index) {
|
||||
case 0: // FILM 4K
|
||||
width_numeric->setValue(4096);
|
||||
height_numeric->setValue(2160);
|
||||
break;
|
||||
case 1: // TV 4K
|
||||
width_numeric->setValue(3840);
|
||||
height_numeric->setValue(2160);
|
||||
break;
|
||||
case 2: // 1080p
|
||||
width_numeric->setValue(1920);
|
||||
height_numeric->setValue(1080);
|
||||
break;
|
||||
case 3: // 720p
|
||||
width_numeric->setValue(1280);
|
||||
height_numeric->setValue(720);
|
||||
break;
|
||||
case 4: // 480p
|
||||
width_numeric->setValue(640);
|
||||
height_numeric->setValue(480);
|
||||
break;
|
||||
case 5: // 360p
|
||||
width_numeric->setValue(640);
|
||||
height_numeric->setValue(360);
|
||||
break;
|
||||
case 6: // 240p
|
||||
width_numeric->setValue(320);
|
||||
height_numeric->setValue(240);
|
||||
break;
|
||||
case 7: // 144p
|
||||
width_numeric->setValue(192);
|
||||
height_numeric->setValue(144);
|
||||
break;
|
||||
case 8: // NTSC (480i)
|
||||
width_numeric->setValue(720);
|
||||
height_numeric->setValue(480);
|
||||
break;
|
||||
case 9: // PAL (576i)
|
||||
width_numeric->setValue(720);
|
||||
height_numeric->setValue(576);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NewSequenceDialog::setup_ui() {
|
||||
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
|
||||
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
|
||||
|
||||
QWidget* widget = new QWidget(this);
|
||||
QWidget* widget = new QWidget(this);
|
||||
|
||||
QHBoxLayout* preset_layout = new QHBoxLayout(widget);
|
||||
preset_layout->setContentsMargins(0, 0, 0, 0);
|
||||
QHBoxLayout* preset_layout = new QHBoxLayout(widget);
|
||||
preset_layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
preset_layout->addWidget(new QLabel(tr("Preset:"), this));
|
||||
preset_layout->addWidget(new QLabel(tr("Preset:"), this));
|
||||
|
||||
preset_combobox = new QComboBox(widget);
|
||||
preset_combobox = new QComboBox(widget);
|
||||
|
||||
preset_combobox->addItem(tr("Film 4K"));
|
||||
preset_combobox->addItem(tr("TV 4K (Ultra HD/2160p)"));
|
||||
preset_combobox->addItem(tr("1080p"));
|
||||
preset_combobox->addItem(tr("720p"));
|
||||
preset_combobox->addItem(tr("480p"));
|
||||
preset_combobox->addItem(tr("360p"));
|
||||
preset_combobox->addItem(tr("240p"));
|
||||
preset_combobox->addItem(tr("144p"));
|
||||
preset_combobox->addItem(tr("NTSC (480i)"));
|
||||
preset_combobox->addItem(tr("PAL (576i)"));
|
||||
preset_combobox->addItem(tr("Custom"));
|
||||
preset_combobox->setCurrentIndex(2);
|
||||
preset_combobox->addItem(tr("Film 4K"));
|
||||
preset_combobox->addItem(tr("TV 4K (Ultra HD/2160p)"));
|
||||
preset_combobox->addItem(tr("1080p"));
|
||||
preset_combobox->addItem(tr("720p"));
|
||||
preset_combobox->addItem(tr("480p"));
|
||||
preset_combobox->addItem(tr("360p"));
|
||||
preset_combobox->addItem(tr("240p"));
|
||||
preset_combobox->addItem(tr("144p"));
|
||||
preset_combobox->addItem(tr("NTSC (480i)"));
|
||||
preset_combobox->addItem(tr("PAL (576i)"));
|
||||
preset_combobox->addItem(tr("Custom"));
|
||||
preset_combobox->setCurrentIndex(2);
|
||||
|
||||
preset_layout->addWidget(preset_combobox);
|
||||
preset_layout->addWidget(preset_combobox);
|
||||
|
||||
verticalLayout->addWidget(widget);
|
||||
verticalLayout->addWidget(widget);
|
||||
|
||||
QGroupBox* videoGroupBox = new QGroupBox(this);
|
||||
videoGroupBox->setTitle(tr("Video"));
|
||||
QGroupBox* videoGroupBox = new QGroupBox(this);
|
||||
videoGroupBox->setTitle(tr("Video"));
|
||||
|
||||
QGridLayout* videoLayout = new QGridLayout(videoGroupBox);
|
||||
QGridLayout* videoLayout = new QGridLayout(videoGroupBox);
|
||||
|
||||
videoLayout->addWidget(new QLabel(tr("Width:"), this), 0, 0, 1, 1);
|
||||
width_numeric = new QSpinBox(videoGroupBox);
|
||||
width_numeric->setMaximum(9999);
|
||||
width_numeric->setValue(1920);
|
||||
videoLayout->addWidget(width_numeric, 0, 2, 1, 2);
|
||||
videoLayout->addWidget(new QLabel(tr("Width:"), this), 0, 0, 1, 1);
|
||||
width_numeric = new QSpinBox(videoGroupBox);
|
||||
width_numeric->setMaximum(9999);
|
||||
width_numeric->setValue(1920);
|
||||
videoLayout->addWidget(width_numeric, 0, 2, 1, 2);
|
||||
|
||||
videoLayout->addWidget(new QLabel(tr("Height:"), this), 1, 0, 1, 2);
|
||||
height_numeric = new QSpinBox(videoGroupBox);
|
||||
height_numeric->setMaximum(9999);
|
||||
height_numeric->setValue(1080);
|
||||
videoLayout->addWidget(height_numeric, 1, 2, 1, 2);
|
||||
videoLayout->addWidget(new QLabel(tr("Height:"), this), 1, 0, 1, 2);
|
||||
height_numeric = new QSpinBox(videoGroupBox);
|
||||
height_numeric->setMaximum(9999);
|
||||
height_numeric->setValue(1080);
|
||||
videoLayout->addWidget(height_numeric, 1, 2, 1, 2);
|
||||
|
||||
videoLayout->addWidget(new QLabel(tr("Frame Rate:"), this), 2, 0, 1, 1);
|
||||
frame_rate_combobox = new QComboBox(videoGroupBox);
|
||||
frame_rate_combobox->addItem("10 FPS", 10.0);
|
||||
frame_rate_combobox->addItem("12.5 FPS", 12.5);
|
||||
frame_rate_combobox->addItem("15 FPS", 15.0);
|
||||
frame_rate_combobox->addItem("23.976 FPS", 23.976);
|
||||
frame_rate_combobox->addItem("24 FPS", 24.0);
|
||||
frame_rate_combobox->addItem("25 FPS", 25.0);
|
||||
frame_rate_combobox->addItem("29.97 FPS", 29.97);
|
||||
frame_rate_combobox->addItem("30 FPS", 30.0);
|
||||
frame_rate_combobox->addItem("50 FPS", 50.0);
|
||||
frame_rate_combobox->addItem("59.94 FPS", 59.94);
|
||||
frame_rate_combobox->addItem("60 FPS", 60.0);
|
||||
frame_rate_combobox->setCurrentIndex(6);
|
||||
videoLayout->addWidget(frame_rate_combobox, 2, 2, 1, 2);
|
||||
videoLayout->addWidget(new QLabel(tr("Frame Rate:"), this), 2, 0, 1, 1);
|
||||
frame_rate_combobox = new QComboBox(videoGroupBox);
|
||||
frame_rate_combobox->addItem("10 FPS", 10.0);
|
||||
frame_rate_combobox->addItem("12.5 FPS", 12.5);
|
||||
frame_rate_combobox->addItem("15 FPS", 15.0);
|
||||
frame_rate_combobox->addItem("23.976 FPS", 23.976);
|
||||
frame_rate_combobox->addItem("24 FPS", 24.0);
|
||||
frame_rate_combobox->addItem("25 FPS", 25.0);
|
||||
frame_rate_combobox->addItem("29.97 FPS", 29.97);
|
||||
frame_rate_combobox->addItem("30 FPS", 30.0);
|
||||
frame_rate_combobox->addItem("50 FPS", 50.0);
|
||||
frame_rate_combobox->addItem("59.94 FPS", 59.94);
|
||||
frame_rate_combobox->addItem("60 FPS", 60.0);
|
||||
frame_rate_combobox->setCurrentIndex(6);
|
||||
videoLayout->addWidget(frame_rate_combobox, 2, 2, 1, 2);
|
||||
|
||||
videoLayout->addWidget(new QLabel(tr("Pixel Aspect Ratio:"), this), 4, 0, 1, 1);
|
||||
par_combobox = new QComboBox(videoGroupBox);
|
||||
par_combobox->addItem(tr("Square Pixels (1.0)"));
|
||||
videoLayout->addWidget(par_combobox, 4, 2, 1, 2);
|
||||
videoLayout->addWidget(new QLabel(tr("Pixel Aspect Ratio:"), this), 4, 0, 1, 1);
|
||||
par_combobox = new QComboBox(videoGroupBox);
|
||||
par_combobox->addItem(tr("Square Pixels (1.0)"));
|
||||
videoLayout->addWidget(par_combobox, 4, 2, 1, 2);
|
||||
|
||||
videoLayout->addWidget(new QLabel(tr("Interlacing:"), this), 6, 0, 1, 1);
|
||||
interlacing_combobox = new QComboBox(videoGroupBox);
|
||||
interlacing_combobox->addItem(tr("None (Progressive)"));
|
||||
// interlacing_combobox->addItem("Upper Field First");
|
||||
// interlacing_combobox->addItem("Lower Field First");
|
||||
videoLayout->addWidget(interlacing_combobox, 6, 2, 1, 2);
|
||||
videoLayout->addWidget(new QLabel(tr("Interlacing:"), this), 6, 0, 1, 1);
|
||||
interlacing_combobox = new QComboBox(videoGroupBox);
|
||||
interlacing_combobox->addItem(tr("None (Progressive)"));
|
||||
// interlacing_combobox->addItem("Upper Field First");
|
||||
// interlacing_combobox->addItem("Lower Field First");
|
||||
videoLayout->addWidget(interlacing_combobox, 6, 2, 1, 2);
|
||||
|
||||
verticalLayout->addWidget(videoGroupBox);
|
||||
verticalLayout->addWidget(videoGroupBox);
|
||||
|
||||
QGroupBox* audioGroupBox = new QGroupBox(this);
|
||||
audioGroupBox->setTitle(tr("Audio"));
|
||||
QGroupBox* audioGroupBox = new QGroupBox(this);
|
||||
audioGroupBox->setTitle(tr("Audio"));
|
||||
|
||||
QGridLayout* audioLayout = new QGridLayout(audioGroupBox);
|
||||
QGridLayout* audioLayout = new QGridLayout(audioGroupBox);
|
||||
|
||||
audioLayout->addWidget(new QLabel(tr("Sample Rate: "), this), 0, 0, 1, 1);
|
||||
audioLayout->addWidget(new QLabel(tr("Sample Rate: "), this), 0, 0, 1, 1);
|
||||
|
||||
audio_frequency_combobox = new QComboBox(audioGroupBox);
|
||||
combobox_audio_sample_rates(audio_frequency_combobox);
|
||||
audio_frequency_combobox->setCurrentIndex(4);
|
||||
audio_frequency_combobox = new QComboBox(audioGroupBox);
|
||||
combobox_audio_sample_rates(audio_frequency_combobox);
|
||||
audio_frequency_combobox->setCurrentIndex(4);
|
||||
|
||||
audioLayout->addWidget(audio_frequency_combobox, 0, 1, 1, 1);
|
||||
audioLayout->addWidget(audio_frequency_combobox, 0, 1, 1, 1);
|
||||
|
||||
verticalLayout->addWidget(audioGroupBox);
|
||||
verticalLayout->addWidget(audioGroupBox);
|
||||
|
||||
QWidget* nameWidget = new QWidget(this);
|
||||
QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget);
|
||||
nameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
QWidget* nameWidget = new QWidget(this);
|
||||
QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget);
|
||||
nameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
nameLayout->addWidget(new QLabel(tr("Name:"), this));
|
||||
nameLayout->addWidget(new QLabel(tr("Name:"), this));
|
||||
|
||||
sequence_name_edit = new QLineEdit(nameWidget);
|
||||
sequence_name_edit = new QLineEdit(nameWidget);
|
||||
|
||||
nameLayout->addWidget(sequence_name_edit);
|
||||
nameLayout->addWidget(sequence_name_edit);
|
||||
|
||||
verticalLayout->addWidget(nameWidget);
|
||||
verticalLayout->addWidget(nameWidget);
|
||||
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(this);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||
buttonBox->setCenterButtons(true);
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(this);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||
buttonBox->setCenterButtons(true);
|
||||
|
||||
verticalLayout->addWidget(buttonBox);
|
||||
verticalLayout->addWidget(buttonBox);
|
||||
|
||||
connect(preset_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(preset_changed(int)));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(create()));
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(preset_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(preset_changed(int)));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(create()));
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
}
|
||||
|
||||
+18
-18
@@ -28,36 +28,36 @@
|
||||
|
||||
#include "panels/project.h"
|
||||
#include "project/media.h"
|
||||
#include "project/sequence.h"
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
class NewSequenceDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NewSequenceDialog(QWidget *parent = nullptr, Media* existing = nullptr);
|
||||
~NewSequenceDialog();
|
||||
explicit NewSequenceDialog(QWidget *parent = nullptr, Media* existing = nullptr);
|
||||
~NewSequenceDialog();
|
||||
|
||||
void set_sequence_name(const QString& s);
|
||||
void set_sequence_name(const QString& s);
|
||||
|
||||
private slots:
|
||||
void create();
|
||||
void preset_changed(int index);
|
||||
void create();
|
||||
void preset_changed(int index);
|
||||
|
||||
private:
|
||||
SequencePtr existing_sequence;
|
||||
Media* existing_item;
|
||||
SequencePtr existing_sequence;
|
||||
Media* existing_item;
|
||||
|
||||
void setup_ui();
|
||||
void setup_ui();
|
||||
|
||||
QComboBox* preset_combobox;
|
||||
QSpinBox* height_numeric;
|
||||
QSpinBox* width_numeric;
|
||||
QComboBox* par_combobox;
|
||||
QComboBox* interlacing_combobox;
|
||||
QComboBox* frame_rate_combobox;
|
||||
QComboBox* audio_frequency_combobox;
|
||||
QLineEdit* sequence_name_edit;
|
||||
QComboBox* preset_combobox;
|
||||
QSpinBox* height_numeric;
|
||||
QSpinBox* width_numeric;
|
||||
QComboBox* par_combobox;
|
||||
QComboBox* interlacing_combobox;
|
||||
QComboBox* frame_rate_combobox;
|
||||
QComboBox* audio_frequency_combobox;
|
||||
QLineEdit* sequence_name_edit;
|
||||
};
|
||||
|
||||
#endif // NEWSEQUENCEDIALOG_H
|
||||
|
||||
+106
-50
@@ -20,11 +20,12 @@
|
||||
|
||||
#include "preferencesdialog.h"
|
||||
|
||||
#include "oliveglobal.h"
|
||||
#include "io/config.h"
|
||||
#include "io/path.h"
|
||||
#include "global/global.h"
|
||||
#include "global/config.h"
|
||||
#include "global/path.h"
|
||||
#include "rendering/audio.h"
|
||||
#include "mainwindow.h"
|
||||
#include "panels/panels.h"
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
#include <QMenuBar>
|
||||
#include <QAction>
|
||||
@@ -74,7 +75,7 @@ QString KeySequenceEditor::export_shortcut() {
|
||||
if (ks != action->property("default")) {
|
||||
return action->property("id").toString() + "\t" + keySequence().toString();
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PreferencesDialog::PreferencesDialog(QWidget *parent) :
|
||||
@@ -168,6 +169,7 @@ void PreferencesDialog::save() {
|
||||
bool restart_after_saving = false;
|
||||
bool reinit_audio = false;
|
||||
bool reload_language = false;
|
||||
bool reload_effects = false;
|
||||
|
||||
// Validate whether the specified CSS file exists
|
||||
if (!custom_css_fn->text().isEmpty() && !QFileInfo::exists(custom_css_fn->text())) {
|
||||
@@ -199,11 +201,16 @@ void PreferencesDialog::save() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate whether the effects panel should refresh itself
|
||||
if (olive::CurrentConfig.effect_textbox_lines != effect_textbox_lines_field->value()) {
|
||||
reload_effects = true;
|
||||
}
|
||||
|
||||
// Check if any settings will require a restart of Olive
|
||||
if (olive::CurrentConfig.effect_textbox_lines != effect_textbox_lines_field->value()
|
||||
|| olive::CurrentConfig.use_software_fallback != use_software_fallbacks_checkbox->isChecked()
|
||||
if (olive::CurrentConfig.use_software_fallback != use_software_fallbacks_checkbox->isChecked()
|
||||
|| olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value()
|
||||
|| olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()) {
|
||||
|| olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()
|
||||
|| olive::CurrentConfig.style != static_cast<olive::styling::Style>(ui_style->currentData().toInt())) {
|
||||
|
||||
// any changes to these settings will require a restart - ask the user if we should do one now or later
|
||||
|
||||
@@ -243,8 +250,10 @@ void PreferencesDialog::save() {
|
||||
}
|
||||
|
||||
// save settings from UI to backend
|
||||
olive::CurrentConfig.css_path = custom_css_fn->text();
|
||||
olive::MainWindow->load_css_from_file(olive::CurrentConfig.css_path);
|
||||
if (olive::CurrentConfig.css_path != custom_css_fn->text()) {
|
||||
olive::CurrentConfig.css_path = custom_css_fn->text();
|
||||
olive::MainWindow->Restyle();
|
||||
}
|
||||
|
||||
olive::CurrentConfig.recording_mode = recordingComboBox->currentIndex() + 1;
|
||||
olive::CurrentConfig.img_seq_formats = imgSeqFormatEdit->text();
|
||||
@@ -265,6 +274,9 @@ void PreferencesDialog::save() {
|
||||
olive::CurrentConfig.enable_color_management = enable_color_management->isChecked();
|
||||
olive::CurrentConfig.ocio_config_path = ocio_config_file->text();
|
||||
|
||||
olive::CurrentConfig.style = static_cast<olive::styling::Style>(ui_style->currentData().toInt());
|
||||
|
||||
// Check if the thumbnail or waveform icon
|
||||
if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value()
|
||||
|| olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()) {
|
||||
// we're changing the size of thumbnails and waveforms, so let's delete them and regenerate them next start
|
||||
@@ -307,6 +319,10 @@ void PreferencesDialog::save() {
|
||||
init_audio();
|
||||
}
|
||||
|
||||
if (reload_effects) {
|
||||
panel_effect_controls->Reload();
|
||||
}
|
||||
|
||||
// reload language file if it changed
|
||||
if (reload_language) {
|
||||
olive::Global->load_translation_from_config();
|
||||
@@ -517,19 +533,6 @@ void PreferencesDialog::setup_ui() {
|
||||
|
||||
row++;
|
||||
|
||||
// General -> Custom CSS
|
||||
general_layout->addWidget(new QLabel(tr("Custom CSS:"), this), row, 0);
|
||||
|
||||
custom_css_fn = new QLineEdit(general_tab);
|
||||
custom_css_fn->setText(olive::CurrentConfig.css_path);
|
||||
general_layout->addWidget(custom_css_fn, row, 1, 1, 3);
|
||||
|
||||
QPushButton* custom_css_browse = new QPushButton(tr("Browse"), general_tab);
|
||||
connect(custom_css_browse, SIGNAL(clicked(bool)), this, SLOT(browse_css_file()));
|
||||
general_layout->addWidget(custom_css_browse, row, 4);
|
||||
|
||||
row++;
|
||||
|
||||
// General -> Image Sequence Formats
|
||||
general_layout->addWidget(new QLabel(tr("Image sequence formats:"), this), row, 0);
|
||||
|
||||
@@ -539,27 +542,6 @@ void PreferencesDialog::setup_ui() {
|
||||
|
||||
row++;
|
||||
|
||||
// General -> Audio Recording
|
||||
general_layout->addWidget(new QLabel(tr("Audio Recording:"), this), row, 0);
|
||||
|
||||
recordingComboBox = new QComboBox(general_tab);
|
||||
recordingComboBox->addItem(tr("Mono"));
|
||||
recordingComboBox->addItem(tr("Stereo"));
|
||||
recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1);
|
||||
general_layout->addWidget(recordingComboBox, row, 1, 1, 4);
|
||||
|
||||
row++;
|
||||
|
||||
// General -> Effect Textbox Lines
|
||||
general_layout->addWidget(new QLabel(tr("Effect Textbox Lines:"), this), row, 0);
|
||||
|
||||
effect_textbox_lines_field = new QSpinBox(general_tab);
|
||||
effect_textbox_lines_field->setMinimum(1);
|
||||
effect_textbox_lines_field->setValue(olive::CurrentConfig.effect_textbox_lines);
|
||||
general_layout->addWidget(effect_textbox_lines_field, row, 1, 1, 4);
|
||||
|
||||
row++;
|
||||
|
||||
// General -> Thumbnail and Waveform Resolution
|
||||
general_layout->addWidget(new QLabel(tr("Thumbnail Resolution:"), this), row, 0);
|
||||
|
||||
@@ -589,6 +571,12 @@ void PreferencesDialog::setup_ui() {
|
||||
use_software_fallbacks_checkbox->setChecked(olive::CurrentConfig.use_software_fallback);
|
||||
general_layout->addWidget(use_software_fallbacks_checkbox, row, 0, 1, 4);
|
||||
|
||||
row++;
|
||||
|
||||
// General -> Default Sequence Settings
|
||||
QPushButton* default_sequence_settings = new QPushButton(tr("Default Sequence Settings"));
|
||||
general_layout->addWidget(default_sequence_settings);
|
||||
|
||||
tabWidget->addTab(general_tab, tr("General"));
|
||||
|
||||
// Behavior
|
||||
@@ -601,6 +589,50 @@ void PreferencesDialog::setup_ui() {
|
||||
add_default_effects_to_clips->setChecked(olive::CurrentConfig.add_default_effects_to_clips);
|
||||
behavior_tab_layout->addWidget(add_default_effects_to_clips);
|
||||
|
||||
// Appearance
|
||||
QWidget* appearance_tab = new QWidget(this);
|
||||
tabWidget->addTab(appearance_tab, tr("Appearance"));
|
||||
|
||||
row = 0;
|
||||
|
||||
QGridLayout* appearance_layout = new QGridLayout(appearance_tab);
|
||||
|
||||
// Appearance -> Theme
|
||||
appearance_layout->addWidget(new QLabel(tr("Theme")), row, 0);
|
||||
|
||||
ui_style = new QComboBox();
|
||||
ui_style->addItem(tr("Olive Dark (Default)"), olive::styling::kOliveDefaultDark);
|
||||
ui_style->addItem(tr("Olive Light"), olive::styling::kOliveDefaultLight);
|
||||
ui_style->addItem(tr("Native"), olive::styling::kNativeDarkIcons);
|
||||
ui_style->addItem(tr("Native (Light Icons)"), olive::styling::kNativeLightIcons);
|
||||
ui_style->setCurrentIndex(olive::CurrentConfig.style);
|
||||
appearance_layout->addWidget(ui_style, row, 1, 1, 2);
|
||||
|
||||
row++;
|
||||
|
||||
// Appearance -> Custom CSS
|
||||
appearance_layout->addWidget(new QLabel(tr("Custom CSS:"), this), row, 0);
|
||||
|
||||
custom_css_fn = new QLineEdit(general_tab);
|
||||
custom_css_fn->setText(olive::CurrentConfig.css_path);
|
||||
appearance_layout->addWidget(custom_css_fn, row, 1);
|
||||
|
||||
QPushButton* custom_css_browse = new QPushButton(tr("Browse"), general_tab);
|
||||
connect(custom_css_browse, SIGNAL(clicked(bool)), this, SLOT(browse_css_file()));
|
||||
appearance_layout->addWidget(custom_css_browse, row, 2);
|
||||
|
||||
row++;
|
||||
|
||||
// Appearance -> Effect Textbox Lines
|
||||
appearance_layout->addWidget(new QLabel(tr("Effect Textbox Lines:"), this), row, 0);
|
||||
|
||||
effect_textbox_lines_field = new QSpinBox(general_tab);
|
||||
effect_textbox_lines_field->setMinimum(1);
|
||||
effect_textbox_lines_field->setValue(olive::CurrentConfig.effect_textbox_lines);
|
||||
appearance_layout->addWidget(effect_textbox_lines_field, row, 1, 1, 2);
|
||||
|
||||
row++;
|
||||
|
||||
// Playback
|
||||
QWidget* playback_tab = new QWidget(this);
|
||||
QVBoxLayout* playback_tab_layout = new QVBoxLayout(playback_tab);
|
||||
@@ -636,7 +668,11 @@ void PreferencesDialog::setup_ui() {
|
||||
|
||||
QGridLayout* audio_tab_layout = new QGridLayout(audio_tab);
|
||||
|
||||
audio_tab_layout->addWidget(new QLabel(tr("Output Device:")), 0, 0);
|
||||
row = 0;
|
||||
|
||||
// Audio -> Output Device
|
||||
|
||||
audio_tab_layout->addWidget(new QLabel(tr("Output Device:")), row, 0);
|
||||
|
||||
audio_output_devices = new QComboBox();
|
||||
audio_output_devices->addItem(tr("Default"), "");
|
||||
@@ -653,9 +689,13 @@ void PreferencesDialog::setup_ui() {
|
||||
}
|
||||
}
|
||||
|
||||
audio_tab_layout->addWidget(audio_output_devices, 0, 1);
|
||||
audio_tab_layout->addWidget(audio_output_devices, row, 1);
|
||||
|
||||
audio_tab_layout->addWidget(new QLabel(tr("Input Device:")), 1, 0);
|
||||
row++;
|
||||
|
||||
// Audio -> Input Device
|
||||
|
||||
audio_tab_layout->addWidget(new QLabel(tr("Input Device:")), row, 0);
|
||||
|
||||
audio_input_devices = new QComboBox();
|
||||
audio_input_devices->addItem(tr("Default"), "");
|
||||
@@ -672,9 +712,13 @@ void PreferencesDialog::setup_ui() {
|
||||
}
|
||||
}
|
||||
|
||||
audio_tab_layout->addWidget(audio_input_devices, 1, 1);
|
||||
audio_tab_layout->addWidget(audio_input_devices, row, 1);
|
||||
|
||||
audio_tab_layout->addWidget(new QLabel(tr("Sample Rate:")), 2, 0);
|
||||
row++;
|
||||
|
||||
// Audio -> Sample Rate
|
||||
|
||||
audio_tab_layout->addWidget(new QLabel(tr("Sample Rate:")), row, 0);
|
||||
|
||||
audio_sample_rate = new QComboBox();
|
||||
combobox_audio_sample_rates(audio_sample_rate);
|
||||
@@ -685,7 +729,19 @@ void PreferencesDialog::setup_ui() {
|
||||
}
|
||||
}
|
||||
|
||||
audio_tab_layout->addWidget(audio_sample_rate, 2, 1);
|
||||
audio_tab_layout->addWidget(audio_sample_rate, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Audio -> Audio Recording
|
||||
audio_tab_layout->addWidget(new QLabel(tr("Audio Recording:"), this), row, 0);
|
||||
|
||||
recordingComboBox = new QComboBox(general_tab);
|
||||
recordingComboBox->addItem(tr("Mono"));
|
||||
recordingComboBox->addItem(tr("Stereo"));
|
||||
audio_tab_layout->addWidget(recordingComboBox, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
tabWidget->addTab(audio_tab, tr("Audio"));
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
class KeySequenceEditor : public QKeySequenceEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -75,6 +77,13 @@ private:
|
||||
|
||||
// used to delete previews
|
||||
// type can be: 't' for thumbnails, 'w' for waveforms, or 1 for all
|
||||
/**
|
||||
* @brief Delete disk cached preview files (thumbnails, waveforms, etc.)
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* The types of previews to
|
||||
*/
|
||||
void delete_previews(char type);
|
||||
|
||||
QLineEdit* custom_css_fn;
|
||||
@@ -96,6 +105,8 @@ private:
|
||||
QCheckBox* add_default_effects_to_clips;
|
||||
QCheckBox* enable_color_management;
|
||||
QLineEdit* ocio_config_file;
|
||||
QComboBox* ui_style;
|
||||
Sequence sequence_settings;
|
||||
|
||||
QVector<QAction*> key_shortcut_actions;
|
||||
QVector<QTreeWidgetItem*> key_shortcut_items;
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
#include <QMessageBox>
|
||||
#include <QDebug>
|
||||
|
||||
#include "io/proxygenerator.h"
|
||||
#include "project/proxygenerator.h"
|
||||
#include "project/footage.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
ProxyDialog::ProxyDialog(QWidget *parent, const QVector<Media*> &media) :
|
||||
QDialog(parent),
|
||||
@@ -103,7 +103,7 @@ void ProxyDialog::accept() {
|
||||
info.codec_type = 0;
|
||||
info.size_multiplier = size_combobox->currentData().toDouble();
|
||||
|
||||
FootagePtr footage = selected_media.at(i)->to_footage();
|
||||
Footage* footage = selected_media.at(i)->to_footage();
|
||||
|
||||
QString base_footage_fn = QFileInfo(footage->url).baseName();
|
||||
|
||||
@@ -148,7 +148,7 @@ void ProxyDialog::accept() {
|
||||
|
||||
// all proxy info checks out, queue it with the proxy generator
|
||||
for (int i=0;i<info_list.size();i++) {
|
||||
FootagePtr footage = info_list.at(i).media->to_footage();
|
||||
Footage* footage = info_list.at(i).media->to_footage();
|
||||
footage->proxy = true;
|
||||
footage->proxy_path.clear();
|
||||
|
||||
|
||||
@@ -20,107 +20,107 @@
|
||||
|
||||
#include "replaceclipmediadialog.h"
|
||||
|
||||
#include "panels/panels.h"
|
||||
|
||||
#include "rendering/cacher.h"
|
||||
#include "project/undo.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "undo/undostack.h"
|
||||
#include "rendering/cacher.h"
|
||||
#include "undo/undo.h"
|
||||
|
||||
ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media* old_media) :
|
||||
QDialog(parent),
|
||||
media(old_media)
|
||||
QDialog(parent),
|
||||
media(old_media)
|
||||
{
|
||||
setWindowTitle(tr("Replace clips using \"%1\"").arg(old_media->get_name()));
|
||||
setWindowTitle(tr("Replace clips using \"%1\"").arg(old_media->get_name()));
|
||||
|
||||
resize(300, 400);
|
||||
resize(300, 400);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
layout->addWidget(new QLabel(tr("Select which media you want to replace this media's clips with:"), this));
|
||||
layout->addWidget(new QLabel(tr("Select which media you want to replace this media's clips with:"), this));
|
||||
|
||||
tree = new QTreeView(this);
|
||||
tree = new QTreeView(this);
|
||||
|
||||
layout->addWidget(tree);
|
||||
layout->addWidget(tree);
|
||||
|
||||
use_same_media_in_points = new QCheckBox(tr("Keep the same media in-points"), this);
|
||||
use_same_media_in_points->setChecked(true);
|
||||
layout->addWidget(use_same_media_in_points);
|
||||
use_same_media_in_points = new QCheckBox(tr("Keep the same media in-points"), this);
|
||||
use_same_media_in_points->setChecked(true);
|
||||
layout->addWidget(use_same_media_in_points);
|
||||
|
||||
QHBoxLayout* buttons = new QHBoxLayout();
|
||||
|
||||
buttons->addStretch();
|
||||
buttons->addStretch();
|
||||
|
||||
QPushButton* replace_button = new QPushButton(tr("Replace"), this);
|
||||
connect(replace_button, SIGNAL(clicked(bool)), this, SLOT(replace()));
|
||||
buttons->addWidget(replace_button);
|
||||
QPushButton* replace_button = new QPushButton(tr("Replace"), this);
|
||||
connect(replace_button, SIGNAL(clicked(bool)), this, SLOT(replace()));
|
||||
buttons->addWidget(replace_button);
|
||||
|
||||
QPushButton* cancel_button = new QPushButton(tr("Cancel"), this);
|
||||
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(close()));
|
||||
buttons->addWidget(cancel_button);
|
||||
QPushButton* cancel_button = new QPushButton(tr("Cancel"), this);
|
||||
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(close()));
|
||||
buttons->addWidget(cancel_button);
|
||||
|
||||
buttons->addStretch();
|
||||
buttons->addStretch();
|
||||
|
||||
layout->addLayout(buttons);
|
||||
layout->addLayout(buttons);
|
||||
|
||||
tree->setModel(&olive::project_model);
|
||||
tree->setModel(&olive::project_model);
|
||||
}
|
||||
|
||||
void ReplaceClipMediaDialog::replace() {
|
||||
QModelIndexList selected_items = tree->selectionModel()->selectedRows();
|
||||
if (selected_items.size() != 1) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("No media selected"),
|
||||
tr("Please select a media to replace with or click 'Cancel'."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
} else {
|
||||
QModelIndexList selected_items = tree->selectionModel()->selectedRows();
|
||||
if (selected_items.size() != 1) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("No media selected"),
|
||||
tr("Please select a media to replace with or click 'Cancel'."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
} else {
|
||||
Media* new_item = static_cast<Media*>(selected_items.at(0).internalPointer());
|
||||
if (media == new_item) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Same media selected"),
|
||||
tr("You selected the same media that you're replacing. Please select a different one or click 'Cancel'."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
} else if (new_item->get_type() == MEDIA_TYPE_FOLDER) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Folder selected"),
|
||||
tr("You cannot replace footage with a folder."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
} else {
|
||||
if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && olive::ActiveSequence == new_item->to_sequence()) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Active sequence selected"),
|
||||
tr("You cannot insert a sequence into itself."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
} else {
|
||||
ReplaceClipMediaCommand* rcmc = new ReplaceClipMediaCommand(
|
||||
media,
|
||||
new_item,
|
||||
use_same_media_in_points->isChecked()
|
||||
);
|
||||
if (media == new_item) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Same media selected"),
|
||||
tr("You selected the same media that you're replacing. Please select a different one or click 'Cancel'."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
} else if (new_item->get_type() == MEDIA_TYPE_FOLDER) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Folder selected"),
|
||||
tr("You cannot replace footage with a folder."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
} else {
|
||||
if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && olive::ActiveSequence == new_item->to_sequence()) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
tr("Active sequence selected"),
|
||||
tr("You cannot insert a sequence into itself."),
|
||||
QMessageBox::Ok
|
||||
);
|
||||
} else {
|
||||
ReplaceClipMediaCommand* rcmc = new ReplaceClipMediaCommand(
|
||||
media,
|
||||
new_item,
|
||||
use_same_media_in_points->isChecked()
|
||||
);
|
||||
|
||||
for (int i=0;i<olive::ActiveSequence->clips.size();i++) {
|
||||
for (int i=0;i<olive::ActiveSequence->clips.size();i++) {
|
||||
ClipPtr c = olive::ActiveSequence->clips.at(i);
|
||||
if (c != nullptr && c->media() == media) {
|
||||
rcmc->clips.append(c);
|
||||
}
|
||||
}
|
||||
rcmc->clips.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
olive::UndoStack.push(rcmc);
|
||||
olive::UndoStack.push(rcmc);
|
||||
|
||||
close();
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-29
@@ -26,13 +26,14 @@
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include "project/sequence.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "project/footage.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "project/undo.h"
|
||||
#include "project/effect.h"
|
||||
#include "undo/undo.h"
|
||||
#include "undo/undostack.h"
|
||||
#include "effects/effect.h"
|
||||
#include "project/media.h"
|
||||
|
||||
SpeedDialog::SpeedDialog(QWidget *parent, QVector<Clip*> clips) : QDialog(parent) {
|
||||
@@ -47,20 +48,20 @@ SpeedDialog::SpeedDialog(QWidget *parent, QVector<Clip*> clips) : QDialog(parent
|
||||
|
||||
grid->addWidget(new QLabel(tr("Speed:"), this), 0, 0);
|
||||
percent = new LabelSlider(this);
|
||||
percent->decimal_places = 2;
|
||||
percent->set_display_type(LABELSLIDER_PERCENT);
|
||||
percent->set_default_value(1);
|
||||
percent->SetDecimalPlaces(2);
|
||||
percent->SetDisplayType(LabelSlider::Percent);
|
||||
percent->SetDefault(1);
|
||||
grid->addWidget(percent, 0, 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Frame Rate:"), this), 1, 0);
|
||||
frame_rate = new LabelSlider(this);
|
||||
frame_rate->decimal_places = 3;
|
||||
frame_rate->SetDecimalPlaces(3);
|
||||
grid->addWidget(frame_rate, 1, 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Duration:"), this), 2, 0);
|
||||
duration = new LabelSlider(this);
|
||||
duration->set_display_type(LABELSLIDER_FRAMENUMBER);
|
||||
duration->set_frame_rate(olive::ActiveSequence->frame_rate);
|
||||
duration->SetDisplayType(LabelSlider::FrameNumber);
|
||||
duration->SetFrameRate(olive::ActiveSequence->frame_rate);
|
||||
grid->addWidget(duration, 2, 1);
|
||||
|
||||
main_layout->addLayout(grid);
|
||||
@@ -79,9 +80,9 @@ SpeedDialog::SpeedDialog(QWidget *parent, QVector<Clip*> clips) : QDialog(parent
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
|
||||
connect(percent, SIGNAL(valueChanged()), this, SLOT(percent_update()));
|
||||
connect(frame_rate, SIGNAL(valueChanged()), this, SLOT(frame_rate_update()));
|
||||
connect(duration, SIGNAL(valueChanged()), this, SLOT(duration_update()));
|
||||
connect(percent, SIGNAL(valueChanged(double)), this, SLOT(percent_update()));
|
||||
connect(frame_rate, SIGNAL(valueChanged(double)), this, SLOT(frame_rate_update()));
|
||||
connect(duration, SIGNAL(valueChanged(double)), this, SLOT(duration_update()));
|
||||
}
|
||||
|
||||
void SpeedDialog::run() {
|
||||
@@ -168,16 +169,16 @@ void SpeedDialog::run() {
|
||||
}
|
||||
}
|
||||
|
||||
frame_rate->set_minimum_value(1);
|
||||
percent->set_minimum_value(0.0001);
|
||||
duration->set_minimum_value(1);
|
||||
frame_rate->SetMinimum(1);
|
||||
percent->SetMinimum(0.0001);
|
||||
duration->SetMinimum(1);
|
||||
|
||||
frame_rate->setEnabled(enable_frame_rate);
|
||||
frame_rate->set_default_value(default_frame_rate);
|
||||
frame_rate->set_value(current_frame_rate, false);
|
||||
percent->set_value(current_percent, false);
|
||||
duration->set_default_value(default_length);
|
||||
duration->set_value((current_length == -1) ? qSNaN() : current_length, false);
|
||||
frame_rate->SetDefault(default_frame_rate);
|
||||
frame_rate->SetValue(current_frame_rate);
|
||||
percent->SetValue(current_percent);
|
||||
duration->SetDefault(default_length);
|
||||
duration->SetValue((current_length == -1) ? qSNaN() : current_length);
|
||||
|
||||
exec();
|
||||
}
|
||||
@@ -213,8 +214,8 @@ void SpeedDialog::percent_update() {
|
||||
}
|
||||
}
|
||||
|
||||
frame_rate->set_value(fr_val, false);
|
||||
duration->set_value((len_val == -1) ? qSNaN() : len_val, false);
|
||||
frame_rate->SetValue(fr_val);
|
||||
duration->SetValue((len_val == -1) ? qSNaN() : len_val);
|
||||
}
|
||||
|
||||
void SpeedDialog::duration_update() {
|
||||
@@ -248,8 +249,8 @@ void SpeedDialog::duration_update() {
|
||||
}
|
||||
}
|
||||
|
||||
frame_rate->set_value(fr_val, false);
|
||||
percent->set_value(pc_val, false);
|
||||
frame_rate->SetValue(fr_val);
|
||||
percent->SetValue(pc_val);
|
||||
}
|
||||
|
||||
void SpeedDialog::frame_rate_update() {
|
||||
@@ -301,7 +302,10 @@ void SpeedDialog::frame_rate_update() {
|
||||
Clip* c = clips_.at(i);
|
||||
|
||||
if (c->track() >= 0) {
|
||||
long new_clip_len = (qIsNaN(old_pc_val) || qIsNaN(pc_val)) ? c->length() : ((c->length() * c->speed().value) / pc_val);
|
||||
|
||||
long new_clip_len = (qIsNaN(old_pc_val) || qIsNaN(pc_val)) ?
|
||||
c->length() : qRound((c->length() * c->speed().value) / pc_val);
|
||||
|
||||
if (len_val > -1 && new_clip_len != len_val) {
|
||||
len_val = -1;
|
||||
break;
|
||||
@@ -309,8 +313,8 @@ void SpeedDialog::frame_rate_update() {
|
||||
}
|
||||
}
|
||||
|
||||
percent->set_value(pc_val, false);
|
||||
duration->set_value((len_val == -1) ? qSNaN() : len_val, false);
|
||||
percent->SetValue(pc_val);
|
||||
duration->SetValue((len_val == -1) ? qSNaN() : len_val);
|
||||
}
|
||||
|
||||
void set_speed(ComboAction* ca, Clip* c, double speed, bool ripple, long& ep, long& lr) {
|
||||
@@ -318,7 +322,7 @@ void set_speed(ComboAction* ca, Clip* c, double speed, bool ripple, long& ep, lo
|
||||
|
||||
long proposed_out = c->timeline_out();
|
||||
double multiplier = (c->speed().value / speed);
|
||||
proposed_out = c->timeline_in() + (c->length() * multiplier);
|
||||
proposed_out = qRound(c->timeline_in() + (c->length() * multiplier));
|
||||
ca->append(new SetSpeedAction(c, speed));
|
||||
if (!ripple && proposed_out > c->timeline_out()) {
|
||||
for (int i=0;i<c->sequence->clips.size();i++) {
|
||||
@@ -353,7 +357,7 @@ void SpeedDialog::accept() {
|
||||
SetClipProperty* reversed_action = new SetClipProperty(kSetClipPropertyReversed);
|
||||
|
||||
// undoable action for restoring clip selections
|
||||
SetSelectionsCommand* sel_command = new SetSelectionsCommand(olive::ActiveSequence);
|
||||
SetSelectionsCommand* sel_command = new SetSelectionsCommand(olive::ActiveSequence.get());
|
||||
sel_command->old_data = olive::ActiveSequence->selections;
|
||||
|
||||
// variables used to calculate ripples
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <QDialog>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "project/clip.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
class SpeedDialog : public QDialog
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SpeedDialog</class>
|
||||
<widget class="QDialog" name="SpeedDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>240</y>
|
||||
<width>341</width>
|
||||
<height>32</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
<width>47</width>
|
||||
<height>13</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Speed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>50</y>
|
||||
<width>47</width>
|
||||
<height>13</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Frame Rate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>80</y>
|
||||
<width>47</width>
|
||||
<height>13</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Duration:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SpeedDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SpeedDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
+197
-15
@@ -22,33 +22,215 @@
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QIcon>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
TextEditDialog::TextEditDialog(QWidget *parent, const QString &s) :
|
||||
QDialog(parent)
|
||||
#include "ui/icons.h"
|
||||
|
||||
TextEditDialog::TextEditDialog(QWidget *parent, const QString &s, bool rich_text) :
|
||||
QDialog(parent),
|
||||
rich_text_(rich_text)
|
||||
{
|
||||
setWindowTitle(tr("Edit Text"));
|
||||
setWindowTitle(tr("Edit Text"));
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
textEdit = new QPlainTextEdit(this);
|
||||
textEdit->setPlainText(s);
|
||||
layout->addWidget(textEdit);
|
||||
// Create central text editor object
|
||||
textEdit = new QTextEdit(this);
|
||||
textEdit->setUndoRedoEnabled(true);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
layout->addWidget(buttons);
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(save()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(cancel()));
|
||||
// Upper toolbar
|
||||
if (rich_text) {
|
||||
QHBoxLayout* toolbar = new QHBoxLayout();
|
||||
|
||||
// Bold Button
|
||||
/*
|
||||
bold_button = new QPushButton();
|
||||
bold_button->setIcon(QIcon(":/icons/bold.svg"));
|
||||
bold_button->setCheckable(true);
|
||||
connect(bold_button, SIGNAL(clicked(bool)), this, SLOT(SetBold(bool)));
|
||||
toolbar->addWidget(bold_button);
|
||||
*/
|
||||
|
||||
// Italic Button
|
||||
italic_button = new QPushButton();
|
||||
italic_button->setIcon(QIcon(":/icons/italic.svg"));
|
||||
italic_button->setCheckable(true);
|
||||
connect(italic_button, SIGNAL(clicked(bool)), textEdit, SLOT(setFontItalic(bool)));
|
||||
toolbar->addWidget(italic_button);
|
||||
|
||||
// Underline Button
|
||||
underline_button = new QPushButton();
|
||||
underline_button->setIcon(QIcon(":/icons/underline.svg"));
|
||||
underline_button->setCheckable(true);
|
||||
connect(underline_button, SIGNAL(clicked(bool)), textEdit, SLOT(setFontUnderline(bool)));
|
||||
toolbar->addWidget(underline_button);
|
||||
|
||||
// Font Name
|
||||
font_list = new QFontComboBox();
|
||||
connect(font_list, SIGNAL(currentIndexChanged(const QString&)), textEdit, SLOT(setFontFamily(const QString&)));
|
||||
toolbar->addWidget(font_list);
|
||||
|
||||
// Font Weight
|
||||
font_weight = new QComboBox();
|
||||
font_weight->addItem(tr("Thin"), QFont::Thin);
|
||||
font_weight->addItem(tr("Extra Light"), QFont::ExtraLight);
|
||||
font_weight->addItem(tr("Light"), QFont::Light);
|
||||
font_weight->addItem(tr("Normal"), QFont::Normal);
|
||||
font_weight->addItem(tr("Medium"), QFont::Medium);
|
||||
font_weight->addItem(tr("Demi Bold"), QFont::DemiBold);
|
||||
font_weight->addItem(tr("Bold"), QFont::Bold);
|
||||
font_weight->addItem(tr("Extra Bold"), QFont::ExtraBold);
|
||||
font_weight->addItem(tr("Black"), QFont::Black);
|
||||
connect(font_weight, SIGNAL(currentIndexChanged(int)), this, SLOT(SetFontWeight(int)));
|
||||
toolbar->addWidget(font_weight);
|
||||
|
||||
// Font Size
|
||||
font_size = new LabelSlider();
|
||||
connect(font_size, SIGNAL(valueChanged(double)), textEdit, SLOT(setFontPointSize(qreal)));
|
||||
toolbar->addWidget(font_size);
|
||||
|
||||
// Font Color
|
||||
font_color = new ColorButton();
|
||||
connect(font_color, SIGNAL(color_changed(const QColor&)), textEdit, SLOT(setTextColor(const QColor &)));
|
||||
toolbar->addWidget(font_color);
|
||||
|
||||
toolbar->addStretch();
|
||||
|
||||
// Left Align
|
||||
left_align_button = new QPushButton();
|
||||
left_align_button->setIcon(QIcon(":/icons/align-left.svg"));
|
||||
left_align_button->setCheckable(true);
|
||||
left_align_button->setProperty("a", Qt::AlignLeft);
|
||||
connect(left_align_button, SIGNAL(clicked(bool)), this, SLOT(SetAlignmentFromProperty()));
|
||||
toolbar->addWidget(left_align_button);
|
||||
|
||||
// Center Align
|
||||
center_align_button = new QPushButton();
|
||||
center_align_button->setIcon(QIcon(":/icons/align-center.svg"));
|
||||
center_align_button->setCheckable(true);
|
||||
center_align_button->setProperty("a", Qt::AlignCenter);
|
||||
connect(center_align_button, SIGNAL(clicked(bool)), this, SLOT(SetAlignmentFromProperty()));
|
||||
toolbar->addWidget(center_align_button);
|
||||
|
||||
// Right Align
|
||||
right_align_button = new QPushButton();
|
||||
right_align_button->setIcon(QIcon(":/icons/align-right.svg"));
|
||||
right_align_button->setCheckable(true);
|
||||
right_align_button->setProperty("a", Qt::AlignRight);
|
||||
connect(right_align_button, SIGNAL(clicked(bool)), this, SLOT(SetAlignmentFromProperty()));
|
||||
toolbar->addWidget(right_align_button);
|
||||
|
||||
// Justify Align
|
||||
justify_align_button = new QPushButton();
|
||||
justify_align_button->setIcon(QIcon(":/icons/justify-center.svg"));
|
||||
justify_align_button->setCheckable(true);
|
||||
justify_align_button->setProperty("a", Qt::AlignJustify);
|
||||
connect(justify_align_button, SIGNAL(clicked(bool)), this, SLOT(SetAlignmentFromProperty()));
|
||||
toolbar->addWidget(justify_align_button);
|
||||
|
||||
layout->addLayout(toolbar);
|
||||
}
|
||||
|
||||
layout->addWidget(textEdit);
|
||||
|
||||
// Lower toolbar
|
||||
/*
|
||||
if (rich_text) {
|
||||
QHBoxLayout* lower_toolbar = new QHBoxLayout();
|
||||
|
||||
lower_toolbar->addWidget(new QLabel(tr("Letter Spacing:")));
|
||||
|
||||
letter_spacing = new LabelSlider();
|
||||
connect(letter_spacing, SIGNAL(valueChanged(double)), this, SLOT(SetLetterSpacing(qreal)));
|
||||
lower_toolbar->addWidget(letter_spacing);
|
||||
|
||||
lower_toolbar->addStretch();
|
||||
|
||||
layout->addLayout(lower_toolbar);
|
||||
}
|
||||
*/
|
||||
|
||||
// Create dialog buttons at the bottom
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
buttons->setCenterButtons(true);
|
||||
layout->addWidget(buttons);
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(save()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(cancel()));
|
||||
|
||||
connect(textEdit, SIGNAL(cursorPositionChanged()), this, SLOT(UpdateUIFromTextCursor()));
|
||||
if (rich_text_) {
|
||||
textEdit->setHtml(s);
|
||||
} else {
|
||||
textEdit->setPlainText(s);
|
||||
}
|
||||
|
||||
// Helps ensure the UI elements update correctly at the beginning - when the cursor is at the start, the UI elements
|
||||
// show up blank...
|
||||
textEdit->moveCursor(QTextCursor::End);
|
||||
}
|
||||
|
||||
const QString& TextEditDialog::get_string() {
|
||||
return result_str;
|
||||
return result_str;
|
||||
}
|
||||
|
||||
void TextEditDialog::save() {
|
||||
result_str = textEdit->toPlainText();
|
||||
accept();
|
||||
result_str = rich_text_ ? textEdit->toHtml() : textEdit->toPlainText();
|
||||
accept();
|
||||
}
|
||||
|
||||
void TextEditDialog::cancel() {
|
||||
reject();
|
||||
reject();
|
||||
}
|
||||
|
||||
void TextEditDialog::SetBold(bool bold)
|
||||
{
|
||||
QFont f = textEdit->currentFont();
|
||||
f.setBold(bold);
|
||||
textEdit->setCurrentFont(f);
|
||||
UpdateUIFromTextCursor();
|
||||
}
|
||||
|
||||
void TextEditDialog::SetFontWeight(int i)
|
||||
{
|
||||
textEdit->setFontWeight(font_weight->itemData(i).toInt());
|
||||
}
|
||||
|
||||
void TextEditDialog::SetLetterSpacing(qreal spacing)
|
||||
{
|
||||
QFont f = textEdit->currentFont();
|
||||
f.setLetterSpacing(f.letterSpacingType(), spacing);
|
||||
textEdit->setCurrentFont(f);
|
||||
}
|
||||
|
||||
void TextEditDialog::SetAlignmentFromProperty()
|
||||
{
|
||||
textEdit->setAlignment(static_cast<Qt::Alignment>(sender()->property("a").toInt()));
|
||||
UpdateUIFromTextCursor();
|
||||
}
|
||||
|
||||
void TextEditDialog::UpdateUIFromTextCursor()
|
||||
{
|
||||
italic_button->setChecked(textEdit->fontItalic());
|
||||
underline_button->setChecked(textEdit->fontUnderline());
|
||||
font_list->setCurrentText(textEdit->fontFamily());
|
||||
font_size->SetValue(textEdit->fontPointSize());
|
||||
font_color->set_color(textEdit->textColor());
|
||||
|
||||
for (int i=0;i<font_weight->count();i++) {
|
||||
if (font_weight->itemData(i).toInt() == textEdit->fontWeight()) {
|
||||
font_weight->blockSignals(true);
|
||||
font_weight->setCurrentIndex(i);
|
||||
font_weight->blockSignals(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Alignment align = textEdit->alignment();
|
||||
left_align_button->setChecked(align == Qt::AlignLeft);
|
||||
center_align_button->setChecked(align == Qt::AlignCenter);
|
||||
right_align_button->setChecked(align == Qt::AlignRight);
|
||||
justify_align_button->setChecked(align == Qt::AlignJustify);
|
||||
}
|
||||
|
||||
@@ -23,18 +23,42 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QFontComboBox>
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/colorbutton.h"
|
||||
|
||||
class TextEditDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextEditDialog(QWidget* parent = nullptr, const QString& s = nullptr);
|
||||
const QString& get_string();
|
||||
TextEditDialog(QWidget* parent = nullptr, const QString& s = nullptr, bool rich_text = true);
|
||||
const QString& get_string();
|
||||
signals:
|
||||
void cursorPositionChanged();
|
||||
private slots:
|
||||
void save();
|
||||
void cancel();
|
||||
void save();
|
||||
void cancel();
|
||||
|
||||
void SetBold(bool bold);
|
||||
void SetFontWeight(int i);
|
||||
void SetLetterSpacing(qreal spacing);
|
||||
void SetAlignmentFromProperty();
|
||||
void UpdateUIFromTextCursor();
|
||||
private:
|
||||
QString result_str;
|
||||
QPlainTextEdit* textEdit;
|
||||
bool rich_text_;
|
||||
|
||||
QString result_str;
|
||||
QTextEdit* textEdit;
|
||||
QPushButton* italic_button;
|
||||
QPushButton* underline_button;
|
||||
QFontComboBox* font_list;
|
||||
QComboBox* font_weight;
|
||||
LabelSlider* font_size;
|
||||
ColorButton* font_color;
|
||||
QPushButton* left_align_button;
|
||||
QPushButton* center_align_button;
|
||||
QPushButton* right_align_button;
|
||||
QPushButton* justify_align_button;
|
||||
};
|
||||
|
||||
#endif // TEXTEDITDIALOG_H
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Place this folder in the runtime folder of Olive.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,8 @@
|
||||
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "ui/checkboxex.h"
|
||||
#include "effectrow.h"
|
||||
#include "effectgizmo.h"
|
||||
|
||||
class Clip;
|
||||
|
||||
@@ -71,6 +73,7 @@ namespace olive {
|
||||
extern QVector<EffectMeta> effects;
|
||||
extern QVector<BlendMode> blend_modes;
|
||||
|
||||
// TODO weird place to put this?
|
||||
extern QString generated_blending_shader;
|
||||
}
|
||||
|
||||
@@ -105,6 +108,7 @@ enum EffectInternal {
|
||||
EFFECT_INTERNAL_VST,
|
||||
EFFECT_INTERNAL_CORNERPIN,
|
||||
EFFECT_INTERNAL_FREI0R,
|
||||
EFFECT_INTERNAL_RICHTEXT,
|
||||
EFFECT_INTERNAL_COUNT
|
||||
};
|
||||
|
||||
@@ -145,22 +149,19 @@ const EffectMeta* get_meta_from_name(const QString& input);
|
||||
|
||||
qint16 mix_audio_sample(qint16 a, qint16 b);
|
||||
|
||||
#include "effectfield.h"
|
||||
#include "effectrow.h"
|
||||
#include "effectgizmo.h"
|
||||
|
||||
class Effect : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Effect(Clip *c, const EffectMeta* em);
|
||||
~Effect();
|
||||
|
||||
Clip* parent_clip;
|
||||
const EffectMeta* meta;
|
||||
int id;
|
||||
QString name;
|
||||
CollapsibleWidget* container;
|
||||
|
||||
EffectRow* add_row(const QString &name, bool savable = true, bool keyframable = true);
|
||||
void AddRow(EffectRow* row);
|
||||
|
||||
EffectRow* row(int i);
|
||||
int row_count();
|
||||
|
||||
@@ -168,8 +169,7 @@ public:
|
||||
EffectGizmo* gizmo(int i);
|
||||
int gizmo_count();
|
||||
|
||||
bool is_enabled();
|
||||
void set_enabled(bool b);
|
||||
bool IsEnabled();
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
@@ -191,10 +191,14 @@ public:
|
||||
virtual void startEffect();
|
||||
virtual void endEffect();
|
||||
|
||||
bool enable_shader;
|
||||
bool enable_coords;
|
||||
bool enable_superimpose;
|
||||
bool enable_image;
|
||||
enum VideoEffectFlags {
|
||||
ShaderFlag = 0x1,
|
||||
CoordsFlag = 0x2,
|
||||
SuperimposeFlag = 0x4,
|
||||
ImageFlag = 0x8
|
||||
};
|
||||
int Flags();
|
||||
void SetFlags(int flags);
|
||||
|
||||
int getIterations();
|
||||
void setIterations(int i);
|
||||
@@ -224,9 +228,11 @@ public:
|
||||
static EffectPtr Create(Clip *c, const EffectMeta *em);
|
||||
static const EffectMeta* GetInternalMeta(int internal_id, int type);
|
||||
public slots:
|
||||
void field_changed();
|
||||
void FieldChanged();
|
||||
void SetEnabled(bool b);
|
||||
signals:
|
||||
void EnabledChanged(bool);
|
||||
private slots:
|
||||
void show_context_menu(const QPoint&);
|
||||
void delete_self();
|
||||
void move_up();
|
||||
void move_down();
|
||||
@@ -243,25 +249,26 @@ protected:
|
||||
QOpenGLTexture* texture;
|
||||
|
||||
// enable effect to update constantly
|
||||
bool enable_always_update;
|
||||
private:
|
||||
// superimpose effect
|
||||
QString script;
|
||||
virtual bool AlwaysUpdate();
|
||||
|
||||
private:
|
||||
bool isOpen;
|
||||
QVector<EffectRow*> rows;
|
||||
QVector<EffectGizmo*> gizmos;
|
||||
QGridLayout* ui_layout;
|
||||
QWidget* ui;
|
||||
bool bound;
|
||||
int iterations;
|
||||
|
||||
bool enabled_;
|
||||
|
||||
int flags_;
|
||||
|
||||
QVector<KeyframeDataChange*> gizmo_dragging_actions_;
|
||||
|
||||
// superimpose functions
|
||||
virtual void redraw(double timecode);
|
||||
bool valueHasChanged(double timecode);
|
||||
QVector<QVariant> cachedValues;
|
||||
void delete_texture();
|
||||
int get_index_in_clip();
|
||||
void validate_meta_path();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "effectfield.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QtMath>
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/colorbutton.h"
|
||||
#include "ui/texteditex.h"
|
||||
#include "ui/checkboxex.h"
|
||||
#include "ui/comboboxex.h"
|
||||
#include "ui/fontcombobox.h"
|
||||
#include "ui/embeddedfilechooser.h"
|
||||
|
||||
#include "rendering/renderfunctions.h"
|
||||
|
||||
#include "global/config.h"
|
||||
|
||||
#include "effects/effectrow.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
#include "undo/undo.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
#include "global/math.h"
|
||||
|
||||
#include "global/debug.h"
|
||||
|
||||
EffectField::EffectField(EffectRow* parent, const QString &i, EffectFieldType t) :
|
||||
QObject(parent),
|
||||
type_(t),
|
||||
id_(i),
|
||||
enabled_(true),
|
||||
colspan_(1)
|
||||
{
|
||||
// EffectField MUST be created with a parent.
|
||||
Q_ASSERT(parent != nullptr);
|
||||
Q_ASSERT(!i.isEmpty() || t == EFFECT_FIELD_UI);
|
||||
|
||||
// Add this field to the parent row specified
|
||||
parent->AddField(this);
|
||||
|
||||
// Set a very base default value
|
||||
SetValueAt(0, 0);
|
||||
|
||||
// Connect this field to the effect's changed function
|
||||
connect(this, SIGNAL(Changed()), parent->GetParentEffect(), SLOT(FieldChanged()));
|
||||
}
|
||||
|
||||
EffectRow *EffectField::GetParentRow()
|
||||
{
|
||||
return static_cast<EffectRow*>(parent());
|
||||
}
|
||||
|
||||
int EffectField::GetColumnSpan()
|
||||
{
|
||||
return colspan_;
|
||||
}
|
||||
|
||||
void EffectField::SetColumnSpan(int i)
|
||||
{
|
||||
Q_ASSERT(i >= 1);
|
||||
colspan_ = i;
|
||||
}
|
||||
|
||||
QVariant EffectField::ConvertStringToValue(const QString &s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
QString EffectField::ConvertValueToString(const QVariant &v)
|
||||
{
|
||||
return v.toString();
|
||||
}
|
||||
|
||||
void EffectField::UpdateWidgetValue(QWidget *, double) {}
|
||||
|
||||
QVariant EffectField::GetValueAt(double timecode)
|
||||
{
|
||||
if (HasKeyframes()) {
|
||||
int before_keyframe;
|
||||
int after_keyframe;
|
||||
double progress;
|
||||
GetKeyframeData(timecode, before_keyframe, after_keyframe, progress);
|
||||
|
||||
const QVariant& before_data = keyframes.at(before_keyframe).data;
|
||||
switch (type_) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
{
|
||||
double value;
|
||||
if (before_keyframe == after_keyframe) {
|
||||
value = keyframes.at(before_keyframe).data.toDouble();
|
||||
} else {
|
||||
const EffectKeyframe& before_key = keyframes.at(before_keyframe);
|
||||
const EffectKeyframe& after_key = keyframes.at(after_keyframe);
|
||||
|
||||
double before_dbl = before_key.data.toDouble();
|
||||
double after_dbl = after_key.data.toDouble();
|
||||
|
||||
if (before_key.type == EFFECT_KEYFRAME_HOLD) {
|
||||
|
||||
// Hold keyframes will always return the previous keyframe with no interpolation
|
||||
value = before_dbl;
|
||||
|
||||
} else if (before_key.type == EFFECT_KEYFRAME_BEZIER || after_key.type == EFFECT_KEYFRAME_BEZIER) {
|
||||
|
||||
// bezier interpolation
|
||||
if (before_key.type == EFFECT_KEYFRAME_BEZIER && after_key.type == EFFECT_KEYFRAME_BEZIER) {
|
||||
|
||||
// cubic bezier
|
||||
double t = cubic_t_from_x(SecondsToFrame(timecode),
|
||||
before_key.time,
|
||||
before_key.time+GetValidKeyframeHandlePosition(before_keyframe, true),
|
||||
after_key.time+GetValidKeyframeHandlePosition(after_keyframe, false),
|
||||
after_key.time);
|
||||
|
||||
value = cubic_from_t(before_dbl,
|
||||
before_dbl+before_key.post_handle_y,
|
||||
after_dbl+after_key.pre_handle_y,
|
||||
after_dbl,
|
||||
t);
|
||||
|
||||
} else if (after_key.type == EFFECT_KEYFRAME_LINEAR) { // quadratic bezier
|
||||
|
||||
// last keyframe is the bezier one
|
||||
double t = quad_t_from_x(SecondsToFrame(timecode),
|
||||
before_key.time,
|
||||
before_key.time+GetValidKeyframeHandlePosition(before_keyframe, true),
|
||||
after_key.time);
|
||||
|
||||
value = quad_from_t(before_dbl,
|
||||
before_dbl+before_key.post_handle_y,
|
||||
after_dbl,
|
||||
t);
|
||||
|
||||
} else {
|
||||
// this keyframe is the bezier one
|
||||
double t = quad_t_from_x(SecondsToFrame(timecode),
|
||||
before_key.time,
|
||||
after_key.time+GetValidKeyframeHandlePosition(after_keyframe, false),
|
||||
after_key.time);
|
||||
|
||||
value = quad_from_t(before_dbl,
|
||||
after_dbl+after_key.pre_handle_y,
|
||||
after_dbl,
|
||||
t);
|
||||
}
|
||||
} else {
|
||||
|
||||
// Linear interpolation (default)
|
||||
value = double_lerp(before_dbl, after_dbl, progress);
|
||||
|
||||
}
|
||||
}
|
||||
persistent_data_ = value;
|
||||
break;
|
||||
}
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
QColor value;
|
||||
if (before_keyframe == after_keyframe) {
|
||||
value = keyframes.at(before_keyframe).data.value<QColor>();
|
||||
} else {
|
||||
QColor before_data = keyframes.at(before_keyframe).data.value<QColor>();
|
||||
QColor after_data = keyframes.at(after_keyframe).data.value<QColor>();
|
||||
value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress));
|
||||
}
|
||||
persistent_data_ = value;
|
||||
break;
|
||||
}
|
||||
case EFFECT_FIELD_STRING:
|
||||
case EFFECT_FIELD_BOOL:
|
||||
case EFFECT_FIELD_COMBO:
|
||||
case EFFECT_FIELD_FONT:
|
||||
case EFFECT_FIELD_FILE:
|
||||
persistent_data_ = before_data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return persistent_data_;
|
||||
}
|
||||
|
||||
void EffectField::SetValueAt(double time, const QVariant &value)
|
||||
{
|
||||
if (HasKeyframes()) {
|
||||
|
||||
// Create keyframe here
|
||||
|
||||
// Convert seconds timecode to frame
|
||||
long frame_timecode = SecondsToFrame(time);
|
||||
|
||||
// Check array if a keyframe at this time already exists
|
||||
int keyframe_index = -1;
|
||||
for (int i=0;i<keyframes.size();i++) {
|
||||
if (keyframes.at(i).time == frame_timecode) {
|
||||
keyframe_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If keyframe doesn't exist, make it
|
||||
if (keyframe_index == -1) {
|
||||
EffectKeyframe key;
|
||||
key.time = frame_timecode;
|
||||
key.data = value;
|
||||
key.type = (keyframes.isEmpty()) ? EFFECT_KEYFRAME_LINEAR : keyframes.last().type;
|
||||
keyframes.append(key);
|
||||
} else {
|
||||
EffectKeyframe& key = keyframes[keyframe_index];
|
||||
key.data = value;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
persistent_data_ = value;
|
||||
|
||||
}
|
||||
|
||||
emit Changed();
|
||||
}
|
||||
|
||||
double EffectField::Now()
|
||||
{
|
||||
Clip* c = GetParentRow()->GetParentEffect()->parent_clip;
|
||||
return playhead_to_clip_seconds(c, c->sequence->playhead);
|
||||
}
|
||||
|
||||
long EffectField::NowInFrames()
|
||||
{
|
||||
Clip* c = GetParentRow()->GetParentEffect()->parent_clip;
|
||||
return playhead_to_clip_frame(c, c->sequence->playhead);
|
||||
}
|
||||
|
||||
void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca)
|
||||
{
|
||||
if (enabled) {
|
||||
|
||||
// Create keyframe from perpetual data
|
||||
EffectKeyframe key;
|
||||
|
||||
key.time = NowInFrames();
|
||||
key.data = persistent_data_;
|
||||
key.type = EFFECT_KEYFRAME_LINEAR;
|
||||
|
||||
keyframes.append(key);
|
||||
|
||||
ca->append(new KeyframeAdd(this, keyframes.size()-1));
|
||||
|
||||
} else {
|
||||
|
||||
// Convert keyframes to one "perpetual" keyframe
|
||||
|
||||
// Set first keyframe to whatever the data is now
|
||||
ca->append(new SetQVariant(&persistent_data_, persistent_data_, GetValueAt(Now())));
|
||||
|
||||
// Delete all keyframes
|
||||
for (int i=0;i<keyframes.size();i++) {
|
||||
ca->append(new KeyframeDelete(this, 0));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const EffectField::EffectFieldType &EffectField::type()
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
const QString &EffectField::id()
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
double EffectField::GetValidKeyframeHandlePosition(int key, bool post) {
|
||||
int comp_key = -1;
|
||||
|
||||
// find keyframe before or after this one
|
||||
for (int i=0;i<keyframes.size();i++) {
|
||||
if (i != key
|
||||
&& ((keyframes.at(i).time > keyframes.at(key).time) == post)
|
||||
&& (comp_key == -1
|
||||
|| ((keyframes.at(i).time < keyframes.at(comp_key).time) == post))) {
|
||||
// compare with next keyframe for post or previous frame for pre
|
||||
comp_key = i;
|
||||
}
|
||||
}
|
||||
|
||||
double adjusted_key = post ? keyframes.at(key).post_handle_x : keyframes.at(key).pre_handle_x;
|
||||
|
||||
// if this is the earliest/latest keyframe, no validation is required
|
||||
if (comp_key == -1) {
|
||||
return adjusted_key;
|
||||
}
|
||||
|
||||
double comp = keyframes.at(comp_key).time - keyframes.at(key).time;
|
||||
|
||||
// if comp keyframe is bezier, validate with its accompanying handle
|
||||
if (keyframes.at(comp_key).type == EFFECT_KEYFRAME_BEZIER) {
|
||||
double relative_comp_handle = comp + (post ? keyframes.at(comp_key).pre_handle_x : keyframes.at(comp_key).post_handle_x);
|
||||
// return an average
|
||||
if ((post && keyframes.at(key).post_handle_x > relative_comp_handle)
|
||||
|| (!post && keyframes.at(key).pre_handle_x < relative_comp_handle)) {
|
||||
adjusted_key = (adjusted_key + relative_comp_handle)*0.5;
|
||||
}
|
||||
}
|
||||
|
||||
// don't let handle go beyond the compare keyframe's time
|
||||
if (post == (adjusted_key > comp)) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
if (post == (adjusted_key < 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// original value is valid
|
||||
return adjusted_key;
|
||||
}
|
||||
|
||||
double EffectField::FrameToSeconds(long frame) {
|
||||
return (double(frame) / GetParentRow()->GetParentEffect()->parent_clip->sequence->frame_rate);
|
||||
}
|
||||
|
||||
long EffectField::SecondsToFrame(double seconds) {
|
||||
return qRound(seconds * GetParentRow()->GetParentEffect()->parent_clip->sequence->frame_rate);
|
||||
}
|
||||
|
||||
void EffectField::GetKeyframeData(double timecode, int &before, int &after, double &progress) {
|
||||
int before_keyframe_index = -1;
|
||||
int after_keyframe_index = -1;
|
||||
long before_keyframe_time = LONG_MIN;
|
||||
long after_keyframe_time = LONG_MAX;
|
||||
long frame = SecondsToFrame(timecode);
|
||||
|
||||
for (int i=0;i<keyframes.size();i++) {
|
||||
long eval_keyframe_time = keyframes.at(i).time;
|
||||
if (eval_keyframe_time == frame) {
|
||||
before = i;
|
||||
after = i;
|
||||
return;
|
||||
} else if (eval_keyframe_time < frame && eval_keyframe_time > before_keyframe_time) {
|
||||
before_keyframe_index = i;
|
||||
before_keyframe_time = eval_keyframe_time;
|
||||
} else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) {
|
||||
after_keyframe_index = i;
|
||||
after_keyframe_time = eval_keyframe_time;
|
||||
}
|
||||
}
|
||||
|
||||
if ((type_ == EFFECT_FIELD_DOUBLE || type_ == EFFECT_FIELD_COLOR)
|
||||
&& (before_keyframe_index > -1 && after_keyframe_index > -1)) {
|
||||
// interpolate
|
||||
before = before_keyframe_index;
|
||||
after = after_keyframe_index;
|
||||
progress = (timecode-FrameToSeconds(before_keyframe_time))/(FrameToSeconds(after_keyframe_time)-FrameToSeconds(before_keyframe_time));
|
||||
} else if (before_keyframe_index > -1) {
|
||||
before = before_keyframe_index;
|
||||
after = before_keyframe_index;
|
||||
} else {
|
||||
before = after_keyframe_index;
|
||||
after = after_keyframe_index;
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectField::HasKeyframes() {
|
||||
return (GetParentRow()->IsKeyframing() && !keyframes.isEmpty());
|
||||
}
|
||||
|
||||
bool EffectField::IsEnabled() {
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
void EffectField::SetEnabled(bool e) {
|
||||
enabled_ = e;
|
||||
emit EnabledChanged(enabled_);
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef EFFECTFIELD_H
|
||||
#define EFFECTFIELD_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include "effects/keyframe.h"
|
||||
#include "undo/undo.h"
|
||||
#include "undo/undostack.h"
|
||||
|
||||
class EffectRow;
|
||||
class ComboAction;
|
||||
|
||||
/**
|
||||
* @brief The EffectField class
|
||||
*
|
||||
* Any user-interactive element of an Effect. Usually a parameter that modifies the effect output, but sometimes just
|
||||
* a UI object that performs some other function (e.g. LabelField and ButtonField).
|
||||
*
|
||||
* EffectField provides a largely abstract interface for Effect classes to pull information from. The class itself
|
||||
* handles keyframing between linear, bezier, and hold interpolation accessible through GetValueAt(). This class
|
||||
* is abstract, and therefore never intended to be used on its own. Instead you should always use a derived class.
|
||||
*
|
||||
* EffectField objects are *not* UI objects on their own. Instead, they're largely a system of values that can change
|
||||
* over time. For a widget that the user can use to edit/modify these values, use CreateWidget().
|
||||
*
|
||||
* Derived classes are expected to override at least CreateWidget() to create a visual interactive widget corresponding
|
||||
* to the field. If this
|
||||
* field is a value used in the Effect (as most will be), UpdateWidgetValue() should also be overridden to display the
|
||||
* correct value for this field as the user moves around the Timeline.
|
||||
*
|
||||
* If the field is intended to be saved and loaded from Olive project files (as most will be), ConvertStringToValue()
|
||||
* and ConvertValueToString() may also have to be overridden depending on how the derived class's data works.
|
||||
*/
|
||||
class EffectField : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief The EffectFieldType enum
|
||||
*
|
||||
* Predetermined types of fields. Used throughout Olive to identify what kind of data to expect from GetValueAt().
|
||||
*
|
||||
* This enum is also currently used to match an external XML effect's fields with the correct derived class (e.g.
|
||||
* EFFECT_FIELD_DOUBLE matches to DoubleField).
|
||||
*/
|
||||
enum EffectFieldType {
|
||||
/** Values are doubles. Also corresponds to DoubleField. */
|
||||
EFFECT_FIELD_DOUBLE,
|
||||
|
||||
/** Values are colors. Also corresponds to ColorField. */
|
||||
EFFECT_FIELD_COLOR,
|
||||
|
||||
/** Values are strings. Also corresponds to StringField. */
|
||||
EFFECT_FIELD_STRING,
|
||||
|
||||
/** Values are booleans. Also corresponds to BoolField. */
|
||||
EFFECT_FIELD_BOOL,
|
||||
|
||||
/** Values are arbitrary data. Also corresponds to ComboField. */
|
||||
EFFECT_FIELD_COMBO,
|
||||
|
||||
/** Values are font family names (in string). Also corresponds to FontField. */
|
||||
EFFECT_FIELD_FONT,
|
||||
|
||||
/** Values are filenames (in string). Also corresponds to FileField. */
|
||||
EFFECT_FIELD_FILE,
|
||||
|
||||
/** Values is a UI object with no data. Corresponds to nothing. */
|
||||
EFFECT_FIELD_UI
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief EffectField Constructor
|
||||
*
|
||||
* Creates a new EffectField object.
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* The EffectRow to add this field to. This must be a valid EffectRow. The EffectRow takes ownership of the field
|
||||
* using the QObject parent/child system to automate memory management. EffectFields are never expected
|
||||
* to change parent during their lifetime.
|
||||
*
|
||||
* @param i
|
||||
*
|
||||
* Field ID. Must be non-empty. Must also be unique within this Effect. Used for saving/loading values into project
|
||||
* files so that if ordering of fields are changed, or fields are added/removed from Effects later in development,
|
||||
* saved values in project files will still link with the correct field. Also used as the uniform variable name
|
||||
* in GLSL shaders.
|
||||
*
|
||||
* @param t
|
||||
*
|
||||
* The type of data contained within this field. This is expected to be filled by a derived class.
|
||||
*/
|
||||
EffectField(EffectRow* parent, const QString& i, EffectFieldType t);
|
||||
|
||||
/**
|
||||
* @brief Get the EffectRow that this field is a member of.
|
||||
*
|
||||
* Equivalent to `static_cast<EffectRow*>(EffectField::parent())`
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The EffectRow that this field is a member of.
|
||||
*/
|
||||
EffectRow* GetParentRow();
|
||||
|
||||
/**
|
||||
* @brief Get the type of data to expect from this field
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A member of the EffectFieldType enum.
|
||||
*/
|
||||
const EffectFieldType& type();
|
||||
|
||||
/**
|
||||
* @brief Get the unique identifier of this field set in the constructor
|
||||
*
|
||||
* Mostly used for saving/loading or interacting with GLSL-based shader effects (see EffectField() for more details).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
const QString& id();
|
||||
|
||||
/**
|
||||
* @brief Get the value of this field at a given timecode
|
||||
*
|
||||
* EffectFields are designed to be keyframable, meaning the user can make the values change over the course of the
|
||||
* Sequence. This is the main function used through Olive to retrieve what value this field will be at a given time.
|
||||
*
|
||||
* A common use case for this function would be EffectField::GetValueAt(EffectField::Now()), which will automatically
|
||||
* retrieve the timecode at the current playhead.
|
||||
*
|
||||
* If the parent EffectRow is NOT keyframing, this function will simply return persistent_data_. If it IS keyframing,
|
||||
* this will use the values in `keyframes` to determine what value should be specifically at this time.
|
||||
* (bezier or linear interpolating it between values if necessary). Therefore this function should almost always be
|
||||
* used to retrieve data from this field as the value will always be correct for the given time.
|
||||
*
|
||||
* @param timecode
|
||||
*
|
||||
* The time to retrieve the value in clip/media seconds (e.g. 0.0 is the very start of the media, 1.0 is one second
|
||||
* into the media).
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A QVariant representation of the value at the given timecode.
|
||||
*/
|
||||
QVariant GetValueAt(double timecode);
|
||||
|
||||
/**
|
||||
* @brief Set the value of this field at a given timecode
|
||||
*
|
||||
* EffectFields are designed to be keyframable, meaning the user can make the values change over the course of the
|
||||
* Sequence. This is the main function used through Olive to set what value this field will be at a given time.
|
||||
*
|
||||
* If the parent EffectRow is keyframing, this function will determine whether a keyframe exists at this time already.
|
||||
* If it does, it will change the value at that keyframe to `value`. Otherwise, it'll create a new keyframe at the
|
||||
* specified `time` with the specified `value`.
|
||||
*
|
||||
* If the parent EffectRow is not keyframing, the data is simply stored in `persistent_data_`.
|
||||
*
|
||||
* When constructing an Effect
|
||||
*
|
||||
* @param time
|
||||
*
|
||||
* The time to retrieve the value at in clip/media seconds (e.g. 0.0 is the very start of the media, 1.0 is one
|
||||
* second into the media).
|
||||
*
|
||||
* @param value
|
||||
*
|
||||
* The QVariant value to set at this time.
|
||||
*/
|
||||
void SetValueAt(double time, const QVariant& value);
|
||||
|
||||
/**
|
||||
* @brief Get the current clip/media time
|
||||
*
|
||||
* A convenience function that can be plugged into GetValueAt() to get the value wherever the appropriate Sequence's
|
||||
* playhead it.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Current clip/media time in seconds.
|
||||
*/
|
||||
double Now();
|
||||
|
||||
/**
|
||||
* @brief Set up keyframing on this field
|
||||
*
|
||||
* This should always be called if the user is enabling/disabling keyframing on the parent row. This function will
|
||||
* move data between persistent_data_ and keyframes depending on whether keyframing is being enabled or disabled.
|
||||
*
|
||||
* If keyframing is getting ENABLED, this function will create the first keyframe automatically at the current time
|
||||
* using the current value in persistent_data_.
|
||||
*
|
||||
* If keyframing is getting DISABLED, persistent_data_ is set to the current value at this time (GetValueAt(Now()))
|
||||
* and delete all current keyframes.
|
||||
*
|
||||
* @param enabled
|
||||
*
|
||||
* TRUE if keyframing is getting enabled.
|
||||
*
|
||||
* @param ca
|
||||
*
|
||||
* A valid ComboAction object. It's expected that this function will be part of a larger action to enable/disable
|
||||
* keyframing on the parent EffectRow, so this function will add commands to this ComboAction.
|
||||
*/
|
||||
void PrepareDataForKeyframing(bool enabled, ComboAction* ca);
|
||||
|
||||
/**
|
||||
* @brief Get field's column span
|
||||
*
|
||||
* Used whenever constructing the effect's UI. EffectFields are visually laid out in a table, and some widgets can
|
||||
* be set to take up multiple columns for a better visual flow.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The current column span.
|
||||
*/
|
||||
int GetColumnSpan();
|
||||
|
||||
/**
|
||||
* @brief Set field's column span
|
||||
*
|
||||
* Used whenever constructing the effect's UI. EffectFields are visually laid out in a table, and some widgets can
|
||||
* be set to take up multiple columns for a better visual flow.
|
||||
*
|
||||
* @param i
|
||||
*
|
||||
* The column span to set on this field.
|
||||
*/
|
||||
void SetColumnSpan(int i);
|
||||
|
||||
/**
|
||||
* @brief Convert a value from this field to a string
|
||||
*
|
||||
* When saving effect data to a project file, the data needs to be converted to a string format for saving in XML.
|
||||
* The needs of this string representation may differ depending on the needs of the derived class, therefore you
|
||||
* derived classes may need to override it.
|
||||
*
|
||||
* Default behavior is a simple QVariant <-> QString conversion, which should suffice in most cases.
|
||||
*
|
||||
* @param v
|
||||
*
|
||||
* The QVariant data (retrieved from this field) to convert to string
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A string representation of the QVariant data provided.
|
||||
*/
|
||||
virtual QString ConvertValueToString(const QVariant& v);
|
||||
|
||||
/**
|
||||
* @brief Convert a string to a value appropriate for this field
|
||||
*
|
||||
* This function is the inverse of ConvertValueToString(), converting a string back to field data.
|
||||
*
|
||||
* @param s
|
||||
*
|
||||
* The string to convert to data.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* QVariant data converted from the provided string.
|
||||
*/
|
||||
virtual QVariant ConvertStringToValue(const QString& s);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a widget for the user to interact with this field
|
||||
*
|
||||
* EffectField objects are *not* UI objects on their own. Instead, they're largely a system of values that can change
|
||||
* over time. This function creates a QWidget object that can be placed somewhere in the UI so the user can
|
||||
* interact with and change the data in this field.
|
||||
*
|
||||
* This function must be overridden by derived classes in order to create a widget that appropriate for that field's
|
||||
* data. The derived class is also responsible for
|
||||
* connecting signals like EnabledChanged(), Clicked(), and any other data that needs to be transferred between the
|
||||
* widget and the field (setting up the signals and slots to do so). The field does NOT retain ownership (or any
|
||||
* reference for that matter) to widgets it creates,
|
||||
* so keeping the widget and field up to date with each other relies solely on setting up signals and slots.
|
||||
* Infinite widgets can be created from a single field and used throughout Olive this way.
|
||||
*
|
||||
* Ownership is passed to the caller, and therefore the caller is responsible for freeing it.
|
||||
*
|
||||
* @param existing
|
||||
*
|
||||
* Olive allows multiple effects to attach to one UI layout. Pass a QWidget to this parameter (instead of nullptr)
|
||||
* to attach this field additionally to the widget's signals/slots without creating a new one. The QWidget must be a
|
||||
* widget previously created from the same derived class type or the result is undefined.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A new QWidget object for this EffectField, or the same QWidget passed to `existing` if one was specified.
|
||||
*/
|
||||
virtual QWidget* CreateWidget(QWidget* existing = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* @brief Update a widget created by CreateWidget() using the value at a given time
|
||||
*
|
||||
* Use this function to update a QWidget (obtained from CreateWidget()) with the correct value from the field at a
|
||||
* given time.
|
||||
*
|
||||
* Since only the derived classes know what type of QWidget it created in CreateWidget() and how to work with them,
|
||||
* derived classes are also expected to override this function if the field is an active value used in the Effect
|
||||
* that should visually update as the user moves around the Timeline. However if the field does NOT need to update
|
||||
* live (e.g. the field is just a UI wrapper like LabelField or ButtonField), this function does not need to be
|
||||
* overridden as the default behavior (to do nothing) will suffice in those cases.
|
||||
*
|
||||
* @param widget
|
||||
*
|
||||
* The QWidget to set the value of (must be a QWidget obtained from CreateWidget() or the behavior is undefined).
|
||||
*
|
||||
* @param timecode
|
||||
*
|
||||
* The time in clip/media seconds to retrieve data from.
|
||||
*/
|
||||
virtual void UpdateWidgetValue(QWidget* widget, double timecode);
|
||||
|
||||
/**
|
||||
* @brief Get the correct X position/time value of a bezier keyframe's handles
|
||||
*
|
||||
* Retrieves the X value (time value) of a bezier keyframe's handles. Internally, the handles' X values are allowed
|
||||
* to be arbitrary values. This however can lead to inadvertently creating impossible bezier curves (ones that, for
|
||||
* example, mathematically loop over each other, but obviously a field can't have two values at the same time).
|
||||
*
|
||||
* This function returns the keyframe handles' X values adjusted to prevent this from happening. All calculations
|
||||
* are consistent (i.e. the post handle of one keyframe will be adjusted the same way as the pre handle of the
|
||||
* keyframe before it). It's recommended to always use this function to retrieve keyframe handle X values.
|
||||
*
|
||||
* @param key
|
||||
*
|
||||
* Index of the keyframe (in `keyframes`) to retrieve the handle position from.
|
||||
*
|
||||
* @param post
|
||||
*
|
||||
* FALSE to retrieve the "pre" handle (handle to the left of the keyframe), TRUE to retrieve the "post" handle
|
||||
* (handle to the right of the keyframe).
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The adjusted X value of that keyframe handle.
|
||||
*/
|
||||
double GetValidKeyframeHandlePosition(int key, bool post);
|
||||
|
||||
/**
|
||||
* @brief Return whether this field is enabled or not
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* TRUE if this field is enabled.
|
||||
*/
|
||||
bool IsEnabled();
|
||||
|
||||
/**
|
||||
* @brief Set the enabled state of this field
|
||||
* @param e
|
||||
*
|
||||
* TRUE to enable this field, FALSE to disable it.
|
||||
*/
|
||||
void SetEnabled(bool e);
|
||||
|
||||
/**
|
||||
* @brief Persistent data object
|
||||
*
|
||||
* If the parent EffectRow is not keyframing, all field data is stored and retrieved here. If the row IS keyframing,
|
||||
* this variable goes basically unused unless `keyframes` is empty.
|
||||
*
|
||||
* NOTE: It is NOT recommended to access this variable directly. Use GetValueAt() instead.
|
||||
*/
|
||||
QVariant persistent_data_;
|
||||
|
||||
/**
|
||||
* @brief Keyframe array
|
||||
*
|
||||
* Contains all data about this field's keyframes, from the keyframe times, to their data, to their type (linear,
|
||||
* bezier, or hold), to the bezier handles (if using bezier). If the row is not keyframing, this array is never used
|
||||
* (`persistent_data_` is used instead). If it is, this array will always be used unless the array is empty, in which
|
||||
* case `persistent_data_` will be used again.
|
||||
*/
|
||||
QVector<EffectKeyframe> keyframes;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Changed signal
|
||||
*
|
||||
* Emitted whenever SetValueAt() is called in order to trigger a UI update and Viewer repaint. Note this is NOT
|
||||
* triggered as the value changes from keyframing. Only when the user themselves triggers a change.
|
||||
*/
|
||||
void Changed();
|
||||
|
||||
/**
|
||||
* @brief Clicked signal
|
||||
*
|
||||
* Emitted when the user clicks on a QWidget attached to this field. Derived classes should connect this to the
|
||||
* clicked signal of any QWidget's created (or attached) in CreateWidget().
|
||||
*/
|
||||
void Clicked();
|
||||
|
||||
/**
|
||||
* @brief Enable change state signal
|
||||
*
|
||||
* Emitted when the field's enabled state is changed through SetEnabled(). Derived classes should connect this to the
|
||||
* setEnabled() slot of any QWidget's created (or attached) in CreateWidget().
|
||||
*/
|
||||
void EnabledChanged(bool);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal type variable set in the constructor. Access with type().
|
||||
*/
|
||||
EffectFieldType type_;
|
||||
|
||||
/**
|
||||
* @brief Internal unique identifier for this field set in the constructor. Access with id().
|
||||
*/
|
||||
QString id_;
|
||||
|
||||
/**
|
||||
* @brief Used by GetValueAt() to determine whether to use keyframe data or persistent data
|
||||
* @return
|
||||
*
|
||||
* TRUE if this keyframe data should be retrieved, FALSE if persistent data should be retrieved
|
||||
*/
|
||||
bool HasKeyframes();
|
||||
|
||||
/**
|
||||
* @brief Convert clip time in frames to clip time in seconds
|
||||
*
|
||||
* @param frame
|
||||
*
|
||||
* Clip time in frames
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Clip time in seconds
|
||||
*/
|
||||
double FrameToSeconds(long frame);
|
||||
|
||||
/**
|
||||
* @brief Convert clip time in seconds to clip time in frames
|
||||
*
|
||||
* @param seconds
|
||||
*
|
||||
* Clip time in seconds
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Clip time in frames
|
||||
*/
|
||||
long SecondsToFrame(double seconds);
|
||||
|
||||
/**
|
||||
* @brief Internal function for determining where we are between the available keyframes
|
||||
*
|
||||
* @param timecode
|
||||
*
|
||||
* Timecode to get keyframe data at
|
||||
*
|
||||
* @param before
|
||||
*
|
||||
* The index (in the keyframes array) in the keyframe prior to this timecode.
|
||||
*
|
||||
* @param after
|
||||
*
|
||||
* The index (in the keyframes array) in the keyframe after this timecode.
|
||||
*
|
||||
* @param d
|
||||
*
|
||||
* The progress between the `before` keyframe and `after` keyframe from 0.0 to 1.0.
|
||||
*/
|
||||
void GetKeyframeData(double timecode, int& before, int& after, double& d);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the current clip as a frame number
|
||||
*
|
||||
* Same as Now() but retrieves the value as a frame number (in the appropriate Sequence's frame rate) instead of
|
||||
* seconds.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* The current clip time in frames
|
||||
*/
|
||||
long NowInFrames();
|
||||
|
||||
/**
|
||||
* @brief Internal enabled value
|
||||
*/
|
||||
bool enabled_;
|
||||
|
||||
/**
|
||||
* @brief Internal column span value
|
||||
*/
|
||||
int colspan_;
|
||||
|
||||
};
|
||||
|
||||
#endif // EFFECTFIELD_H
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef EFFECTFIELDS_H
|
||||
#define EFFECTFIELDS_H
|
||||
|
||||
/**
|
||||
|
||||
A simple convenience header for including all the available EffectField derivations.
|
||||
|
||||
*/
|
||||
|
||||
#include "fields/boolfield.h"
|
||||
#include "fields/buttonfield.h"
|
||||
#include "fields/colorfield.h"
|
||||
#include "fields/combofield.h"
|
||||
#include "fields/doublefield.h"
|
||||
#include "fields/filefield.h"
|
||||
#include "fields/fontfield.h"
|
||||
#include "fields/labelfield.h"
|
||||
#include "fields/stringfield.h"
|
||||
|
||||
#endif // EFFECTFIELDS_H
|
||||
@@ -0,0 +1,63 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "effectgizmo.h"
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "effects/fields/doublefield.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
EffectGizmo::EffectGizmo(Effect *parent, int type) :
|
||||
QObject(parent),
|
||||
x_field1(nullptr),
|
||||
x_field_multi1(1.0),
|
||||
y_field1(nullptr),
|
||||
y_field_multi1(1.0),
|
||||
x_field2(nullptr),
|
||||
x_field_multi2(1.0),
|
||||
y_field2(nullptr),
|
||||
y_field_multi2(1.0),
|
||||
type(type),
|
||||
cursor(-1)
|
||||
{
|
||||
Q_ASSERT(parent != nullptr);
|
||||
|
||||
int point_count = (type == GIZMO_TYPE_POLY) ? 4 : 1;
|
||||
world_pos.resize(point_count);
|
||||
screen_pos.resize(point_count);
|
||||
|
||||
color = Qt::white;
|
||||
}
|
||||
|
||||
int EffectGizmo::get_point_count() {
|
||||
return world_pos.size();
|
||||
}
|
||||
|
||||
int EffectGizmo::get_type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
int EffectGizmo::get_cursor() {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
void EffectGizmo::set_cursor(int c) {
|
||||
cursor = c;
|
||||
}
|
||||
@@ -22,51 +22,51 @@
|
||||
#define EFFECTGIZMO_H
|
||||
|
||||
enum GizmoType {
|
||||
GIZMO_TYPE_DOT,
|
||||
GIZMO_TYPE_POLY,
|
||||
GIZMO_TYPE_TARGET
|
||||
GIZMO_TYPE_DOT,
|
||||
GIZMO_TYPE_POLY,
|
||||
GIZMO_TYPE_TARGET
|
||||
};
|
||||
|
||||
#define GIZMO_DOT_SIZE 2.5
|
||||
#define GIZMO_TARGET_SIZE 5.0
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QRect>
|
||||
#include <QPoint>
|
||||
#include <QVector>
|
||||
#include <QColor>
|
||||
|
||||
class EffectField;
|
||||
class DoubleField;
|
||||
class Effect;
|
||||
|
||||
class EffectGizmo
|
||||
{
|
||||
class EffectGizmo : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
EffectGizmo(int type);
|
||||
EffectGizmo(Effect* parent, int type);
|
||||
|
||||
QVector<QPoint> world_pos;
|
||||
QVector<QPoint> screen_pos;
|
||||
QVector<QPoint> world_pos;
|
||||
QVector<QPoint> screen_pos;
|
||||
|
||||
EffectField* x_field1;
|
||||
double x_field_multi1;
|
||||
EffectField* y_field1;
|
||||
double y_field_multi1;
|
||||
EffectField* x_field2;
|
||||
double x_field_multi2;
|
||||
EffectField* y_field2;
|
||||
double y_field_multi2;
|
||||
DoubleField* x_field1;
|
||||
double x_field_multi1;
|
||||
DoubleField* y_field1;
|
||||
double y_field_multi1;
|
||||
DoubleField* x_field2;
|
||||
double x_field_multi2;
|
||||
DoubleField* y_field2;
|
||||
double y_field_multi2;
|
||||
|
||||
void set_previous_value();
|
||||
QColor color;
|
||||
int get_point_count();
|
||||
|
||||
QColor color;
|
||||
int get_point_count();
|
||||
int get_type();
|
||||
|
||||
int get_type();
|
||||
|
||||
int get_cursor();
|
||||
void set_cursor(int c);
|
||||
int get_cursor();
|
||||
void set_cursor(int c);
|
||||
private:
|
||||
int type;
|
||||
int cursor;
|
||||
int type;
|
||||
int cursor;
|
||||
};
|
||||
|
||||
#endif // EFFECTGIZMO_H
|
||||
@@ -20,26 +20,26 @@
|
||||
|
||||
#include "effectloaders.h"
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "project/transition.h"
|
||||
#include "io/path.h"
|
||||
#include "effects/effect.h"
|
||||
#include "effects/transition.h"
|
||||
#include "global/path.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "io/crossplatformlib.h"
|
||||
#include "io/config.h"
|
||||
#include "global/crossplatformlib.h"
|
||||
#include "global/config.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
QMutex olive::effects_loaded;
|
||||
|
||||
#ifndef NO_FREI0R
|
||||
#ifndef NOFREI0R
|
||||
#include <frei0r.h>
|
||||
typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info);
|
||||
#endif
|
||||
|
||||
QMutex olive::effects_loaded;
|
||||
|
||||
void load_internal_effects() {
|
||||
if (!olive::CurrentRuntimeConfig.shaders_are_enabled) qWarning() << "Shaders are disabled, some effects may be nonfunctional";
|
||||
|
||||
@@ -59,9 +59,11 @@ void load_internal_effects() {
|
||||
em.internal = EFFECT_INTERNAL_PAN;
|
||||
olive::effects.append(em);
|
||||
|
||||
#ifndef NOVST
|
||||
em.name = "VST Plugin 2.x";
|
||||
em.internal = EFFECT_INTERNAL_VST;
|
||||
olive::effects.append(em);
|
||||
#endif
|
||||
|
||||
em.name = "Tone";
|
||||
em.internal = EFFECT_INTERNAL_TONE;
|
||||
@@ -99,6 +101,11 @@ void load_internal_effects() {
|
||||
em.internal = EFFECT_INTERNAL_TEXT;
|
||||
olive::effects.append(em);
|
||||
|
||||
em.name = "Rich Text";
|
||||
em.category = "Render";
|
||||
em.internal = EFFECT_INTERNAL_RICHTEXT;
|
||||
olive::effects.append(em);
|
||||
|
||||
em.name = "Timecode";
|
||||
em.internal = EFFECT_INTERNAL_TIMECODE;
|
||||
olive::effects.append(em);
|
||||
@@ -137,7 +144,6 @@ void load_shader_effects() {
|
||||
const QString& effects_path = effects_paths.at(h);
|
||||
QDir effects_dir(effects_path);
|
||||
if (effects_dir.exists()) {
|
||||
// Load XML metadata for GLSL shader effects
|
||||
QList<QString> entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files);
|
||||
for (int i=0;i<entries.size();i++) {
|
||||
QFile file(effects_path + "/" + entries.at(i));
|
||||
@@ -179,27 +185,17 @@ void load_shader_effects() {
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Load blending mode shaders from file
|
||||
QList<QString> blend_mode_entries = effects_dir.entryList(QStringList("*.blend"), QDir::Files);
|
||||
for (int i=0;i<blend_mode_entries.size();i++) {
|
||||
BlendMode b;
|
||||
b.loaded = false;
|
||||
b.url = effects_dir.filePath(blend_mode_entries.at(i));
|
||||
b.name = QFileInfo(b.url).baseName();
|
||||
olive::blend_modes.append(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_effects() {
|
||||
void EffectInit::StartLoading() {
|
||||
EffectInit* init_thread = new EffectInit();
|
||||
QObject::connect(init_thread, SIGNAL(finished()), init_thread, SLOT(deleteLater()));
|
||||
init_thread->start();
|
||||
}
|
||||
|
||||
#ifndef NO_FREI0R
|
||||
#ifndef NOFREI0R
|
||||
void load_frei0r_effects_worker(const QString& dir, EffectMeta& em, QVector<QString>& loaded_names) {
|
||||
QDir search_dir(dir);
|
||||
if (search_dir.exists()) {
|
||||
@@ -266,149 +262,17 @@ void load_frei0r_effects() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void IncludeBlendingShader(BlendMode& b) {
|
||||
if (!b.loaded) {
|
||||
QFile blending_file(b.url);
|
||||
|
||||
if (blending_file.open(QFile::ReadOnly)) {
|
||||
|
||||
QTextStream stream(&blending_file);
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine();
|
||||
if (line.length() > 0 && line.at(0) == '#') {
|
||||
|
||||
if (line.startsWith("#olive name ")) {
|
||||
|
||||
// The blending mode can specify its own name
|
||||
b.name = line.mid(12);
|
||||
|
||||
} else if (line.startsWith("#pragma glslify: export(")) {
|
||||
|
||||
// Get function name
|
||||
b.function_name = line.mid(24, line.length()-25);
|
||||
|
||||
} else if (line.contains("require")) {
|
||||
|
||||
// This function wanted to include an external shader
|
||||
|
||||
int index_of_last_bracked = line.lastIndexOf('(') + 1;
|
||||
|
||||
QString include_fn = line.mid(index_of_last_bracked, line.length() - index_of_last_bracked - 1);
|
||||
include_fn.append(".blend");
|
||||
QString include_path = QFileInfo(b.url).dir().filePath(include_fn);
|
||||
|
||||
// see if this file is already in the blend modes list
|
||||
bool found = false;
|
||||
for (int i=0;i<olive::blend_modes.size();i++) {
|
||||
if (QFileInfo(olive::blend_modes.at(i).url) == QFileInfo(include_path)) {
|
||||
|
||||
// It is, so we'll include it now rather than later
|
||||
IncludeBlendingShader(olive::blend_modes[i]);
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// include the file, but don't add it to the blend mode list
|
||||
BlendMode b;
|
||||
b.url = include_path;
|
||||
IncludeBlendingShader(b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Assume this line is code and add it to the shader
|
||||
olive::generated_blending_shader.append(line);
|
||||
olive::generated_blending_shader.append("\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
blending_file.close();
|
||||
|
||||
} else {
|
||||
qWarning() << "Failed to open blending shader" << b.url;
|
||||
}
|
||||
|
||||
b.loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateBlendingShader()
|
||||
{
|
||||
olive::generated_blending_shader = "#version 110\n" // Start with GLSL version identifier
|
||||
"\n"
|
||||
"uniform sampler2D background;\n" // background (base) texture color
|
||||
"uniform sampler2D foreground;\n" // foreground (blend) texture color
|
||||
"varying vec2 vTexCoord;\n" // texture coordinate
|
||||
"uniform float opacity;\n" // foreground opacity setting
|
||||
"uniform int blendmode;\n" // blending mode switcher
|
||||
"\n"
|
||||
"\n";
|
||||
|
||||
// Import code from each blend mode file
|
||||
for (int i=0;i<olive::blend_modes.size();i++) {
|
||||
IncludeBlendingShader(olive::blend_modes[i]);
|
||||
}
|
||||
|
||||
// Create monolithic switcher function
|
||||
|
||||
olive::generated_blending_shader.append("vec3 blend(vec3 base, vec3 blend, float opacity) {\n");
|
||||
|
||||
for (int i=0;i<olive::blend_modes.size();i++) {
|
||||
if (i == 0) {
|
||||
olive::generated_blending_shader.append(" if (blendmode == 0) {\n");
|
||||
} else {
|
||||
olive::generated_blending_shader.append(QString(" else if (blendmode == %1) {\n").arg(i));
|
||||
}
|
||||
|
||||
olive::generated_blending_shader.append(QString(" return %1(base, blend, opacity);\n").arg(olive::blend_modes.at(i).function_name));
|
||||
olive::generated_blending_shader.append(" }");
|
||||
}
|
||||
|
||||
// Write the main() function for the shader
|
||||
//
|
||||
// NOTE/FIXME: Unfortunately the current blending shaders (from https://github.com/jamieowen/glsl-blend) all seem to
|
||||
// be calculated for unassociated alpha, while Olive's internal pipeline largely functions in associated alpha.
|
||||
// Therefore for the blending shaders to work as expected, the alpha has to be unassociated at this stage.
|
||||
// Naturally, this sucks, but I'm not entirely sure what the solution is apart from rewriting the blending shaders
|
||||
// or maybe finding a new library.
|
||||
|
||||
olive::generated_blending_shader.append("\n return blend;\n" // default return value
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main() {\n"
|
||||
" vec4 bg_color = texture2D(background, vTexCoord);\n" // Get background texture color
|
||||
" vec4 fg_color = texture2D(foreground, vTexCoord);\n" // Get foreground texture color
|
||||
" if (fg_color.a > 0.0) {\n"
|
||||
" float true_opacity = opacity * fg_color.a;\n"
|
||||
" vec3 unmultipled_fg = max(vec3(0.0), min(vec3(1.0), fg_color.rgb / fg_color.a));\n"
|
||||
" vec3 blended_rgb = blend(bg_color.rgb, unmultipled_fg, true_opacity);\n" // Use switcher function above to blend RGBs
|
||||
" vec4 composite = vec4(blended_rgb, bg_color.a + true_opacity);\n"
|
||||
" gl_FragColor = composite;\n"
|
||||
" } else {\n"
|
||||
" gl_FragColor = bg_color;\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
EffectInit::EffectInit() {
|
||||
olive::effects_loaded.lock();
|
||||
}
|
||||
|
||||
void EffectInit::run() {
|
||||
qInfo() << "Initializing effects...";
|
||||
qInfo() << "Initializing olive::effects...";
|
||||
load_internal_effects();
|
||||
load_shader_effects();
|
||||
#ifndef NO_FREI0R
|
||||
#ifndef NOFREI0R
|
||||
load_frei0r_effects();
|
||||
#endif
|
||||
GenerateBlendingShader();
|
||||
olive::effects_loaded.unlock();
|
||||
qInfo() << "Finished initializing effects";
|
||||
}
|
||||
@@ -29,12 +29,26 @@ namespace olive {
|
||||
extern QMutex effects_loaded;
|
||||
}
|
||||
|
||||
void init_effects();
|
||||
|
||||
/**
|
||||
* @brief The EffectInit class
|
||||
*
|
||||
* A separate thread for loading effects in the background while the rest of the program's initiation takes place.
|
||||
* The program can even run before the effects have finished loading, but any point the software needs to access
|
||||
* effects, it will have to wait for this thread to finish before it can. Fortunately this thread is usually very
|
||||
* quick and is over before the MainWindow shows.
|
||||
*/
|
||||
class EffectInit : public QThread {
|
||||
public:
|
||||
EffectInit();
|
||||
|
||||
/**
|
||||
* @brief A static convenience function to set up the EffectInit thread, start it, and free itself when complete.
|
||||
*/
|
||||
static void StartLoading();
|
||||
protected:
|
||||
/**
|
||||
* @brief Function that runs in the other thread.
|
||||
*/
|
||||
void run();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "effectrow.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "undo/undo.h"
|
||||
#include "undo/undostack.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "panels/grapheditor.h"
|
||||
#include "effect.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "ui/keyframenavigator.h"
|
||||
#include "ui/clickablelabel.h"
|
||||
|
||||
EffectRow::EffectRow(Effect *parent, const QString &n, bool savable, bool keyframable) :
|
||||
QObject(parent),
|
||||
name_(n),
|
||||
keyframable_(keyframable),
|
||||
keyframing_(false),
|
||||
savable_(savable)
|
||||
{
|
||||
Q_ASSERT(parent != nullptr);
|
||||
|
||||
parent->AddRow(this);
|
||||
}
|
||||
|
||||
void EffectRow::AddField(EffectField *field)
|
||||
{
|
||||
field->setParent(this);
|
||||
fields_.append(field);
|
||||
}
|
||||
|
||||
bool EffectRow::IsKeyframing() {
|
||||
return keyframing_;
|
||||
}
|
||||
|
||||
void EffectRow::SetKeyframingInternal(bool b) {
|
||||
// No need to run this function if the keyframing state isn't actually changing.
|
||||
if (b == keyframing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetParentEffect()->meta->type != EFFECT_TYPE_TRANSITION) {
|
||||
keyframing_ = b;
|
||||
emit KeyframingSetChanged(keyframing_);
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectRow::IsSavable()
|
||||
{
|
||||
return savable_;
|
||||
}
|
||||
|
||||
bool EffectRow::IsKeyframable()
|
||||
{
|
||||
return keyframable_;
|
||||
}
|
||||
|
||||
void EffectRow::SetKeyframingEnabled(bool enabled) {
|
||||
if (enabled == keyframing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
// Enable keyframing setting on this row
|
||||
ca->append(new SetIsKeyframing(this, true));
|
||||
|
||||
// Prepare each field's data to start keyframing
|
||||
for (int i=0;i<FieldCount();i++) {
|
||||
Field(i)->PrepareDataForKeyframing(true, ca);
|
||||
}
|
||||
|
||||
olive::UndoStack.push(ca);
|
||||
|
||||
update_ui(false);
|
||||
|
||||
} else {
|
||||
|
||||
// Confirm with the user whether they really want to disable keyframing
|
||||
if (QMessageBox::question(panel_effect_controls,
|
||||
tr("Disable Keyframes"),
|
||||
tr("Disabling keyframes will delete all current keyframes. "
|
||||
"Are you sure you want to do this?"),
|
||||
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
// Prepare each field's data to stop keyframing
|
||||
for (int i=0;i<FieldCount();i++) {
|
||||
Field(i)->PrepareDataForKeyframing(false, ca);
|
||||
}
|
||||
|
||||
// Disable keyframing setting on this row
|
||||
ca->append(new SetIsKeyframing(this, false));
|
||||
|
||||
olive::UndoStack.push(ca);
|
||||
|
||||
update_ui(false);
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
SetKeyframingInternal(true);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EffectRow::GoToPreviousKeyframe() {
|
||||
long key = LONG_MIN;
|
||||
Clip* c = GetParentEffect()->parent_clip;
|
||||
long sequence_playhead = c->sequence->playhead;
|
||||
|
||||
// Used to convert clip frame number to sequence frame number
|
||||
long time_adjustment = c->timeline_in() - c->clip_in();
|
||||
|
||||
// Loop through all of this row's fields
|
||||
for (int i=0;i<FieldCount();i++) {
|
||||
|
||||
EffectField* f = Field(i);
|
||||
|
||||
// Loop through all of this field's keyframes for a keyframe EARLIER than the playhead
|
||||
for (int j=0;j<f->keyframes.size();j++) {
|
||||
long comp = f->keyframes.at(j).time + time_adjustment;
|
||||
|
||||
// Get the closest keyframe
|
||||
if (comp < sequence_playhead) {
|
||||
key = qMax(comp, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a keyframe less than the playhead, jump to it
|
||||
if (key != LONG_MIN) panel_sequence_viewer->seek(key);
|
||||
}
|
||||
|
||||
void EffectRow::ToggleKeyframe() {
|
||||
Clip* c = GetParentEffect()->parent_clip;
|
||||
long sequence_playhead = c->sequence->playhead;
|
||||
|
||||
// Used to convert clip frame number to sequence frame number
|
||||
long time_adjustment = c->timeline_in() - c->clip_in();
|
||||
|
||||
QVector<EffectField*> key_fields;
|
||||
QVector<int> key_field_index;
|
||||
|
||||
// See if any keyframes on any fields are at the current time
|
||||
for (int j=0;j<FieldCount();j++) {
|
||||
EffectField* f = Field(j);
|
||||
for (int i=0;i<f->keyframes.size();i++) {
|
||||
long comp = f->keyframes.at(i).time + time_adjustment;
|
||||
|
||||
if (comp == sequence_playhead) {
|
||||
|
||||
// Cache the keyframes if they are at the current time
|
||||
key_fields.append(f);
|
||||
key_field_index.append(i);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
if (key_fields.isEmpty()) {
|
||||
|
||||
// If we didn't find any current keyframes, create one for each field
|
||||
SetKeyframeOnAllFields(ca);
|
||||
|
||||
} else {
|
||||
|
||||
// If we DID find keyframes at this time, delete them
|
||||
|
||||
QVector<EffectField*> sorted_key_fields;
|
||||
QVector<int> sorted_key_field_index;
|
||||
|
||||
// Since QVectors shift themselves when removing items, we need to sort these in reverse order
|
||||
for (int i=0;i<key_field_index.size();i++) {
|
||||
bool found = false;
|
||||
|
||||
for (int j=0;j<sorted_key_field_index.size();j++) {
|
||||
if (sorted_key_field_index.at(j) < key_field_index.at(i)) {
|
||||
sorted_key_fields.insert(j, key_fields.at(i));
|
||||
sorted_key_field_index.insert(j, key_field_index.at(i));
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
sorted_key_fields.append(key_fields.at(i));
|
||||
sorted_key_field_index.append(key_field_index.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<sorted_key_fields.size();i++) {
|
||||
ca->append(new KeyframeDelete(sorted_key_fields.at(i), sorted_key_field_index.at(i)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
olive::UndoStack.push(ca);
|
||||
update_ui(false);
|
||||
}
|
||||
|
||||
void EffectRow::GoToNextKeyframe() {
|
||||
long key = LONG_MAX;
|
||||
Clip* c = GetParentEffect()->parent_clip;
|
||||
for (int i=0;i<FieldCount();i++) {
|
||||
EffectField* f = Field(i);
|
||||
for (int j=0;j<f->keyframes.size();j++) {
|
||||
long comp = f->keyframes.at(j).time - c->clip_in() + c->timeline_in();
|
||||
if (comp > olive::ActiveSequence->playhead) {
|
||||
key = qMin(comp, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key != LONG_MAX) panel_sequence_viewer->seek(key);
|
||||
}
|
||||
|
||||
void EffectRow::FocusRow() {
|
||||
panel_graph_editor->set_row(this);
|
||||
}
|
||||
|
||||
void EffectRow::SetKeyframeOnAllFields(ComboAction* ca) {
|
||||
for (int i=0;i<FieldCount();i++) {
|
||||
EffectField* field = Field(i);
|
||||
|
||||
KeyframeDataChange* kdc = new KeyframeDataChange(field);
|
||||
|
||||
field->SetValueAt(field->Now(), field->GetValueAt(field->Now()));
|
||||
|
||||
kdc->SetNewKeyframes();
|
||||
ca->append(kdc);
|
||||
}
|
||||
|
||||
panel_effect_controls->update_keyframes();
|
||||
}
|
||||
|
||||
Effect *EffectRow::GetParentEffect()
|
||||
{
|
||||
return static_cast<Effect*>(parent());
|
||||
}
|
||||
|
||||
const QString &EffectRow::name() {
|
||||
return name_;
|
||||
}
|
||||
|
||||
EffectField* EffectRow::Field(int i) {
|
||||
return fields_.at(i);
|
||||
}
|
||||
|
||||
int EffectRow::FieldCount() {
|
||||
return fields_.size();
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef EFFECTROW_H
|
||||
#define EFFECTROW_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
class Effect;
|
||||
class QGridLayout;
|
||||
class EffectField;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class ComboAction;
|
||||
class QHBoxLayout;
|
||||
class KeyframeNavigator;
|
||||
class ClickableLabel;
|
||||
|
||||
#include "effectfields.h"
|
||||
|
||||
/**
|
||||
* @brief The EffectRow class
|
||||
*
|
||||
* Primarily a way of grouping EffectField objects together. As UI objects, Effects are largely formatted as a table
|
||||
* and you can think of EffectRows as the rows of the table and EffectFields as the columns.
|
||||
*
|
||||
* Within Olive, keyframing is enabled on the EffectRow (rather than individual EffectFields) so all attached fields
|
||||
* will be keyframed together.
|
||||
*
|
||||
* Unlike EffectField, there's no reason to derive from EffectRow as it's simply a container of fields and a few
|
||||
* keyframe functions.
|
||||
*/
|
||||
class EffectRow : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief EffectRow Constructor
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* Every EffectRow object must be attached to a valid Effect object. The Effect object takes ownership of the
|
||||
* EffectRow and automatically frees it through the QObject parent/child system. EffectRows are never intended to
|
||||
* change parents throughout their lifetimes.
|
||||
*
|
||||
* @param n
|
||||
*
|
||||
* Row name. This is not used as an internal identifier, it's just for the user interface, so it can be translated
|
||||
* with no issue.
|
||||
*
|
||||
* @param savable
|
||||
*
|
||||
* Whether the fields in this row should be saved to the project file. This is true by default. If the row contains
|
||||
* non-value UI widgets, setting this to false is recommended.
|
||||
*
|
||||
* @param keyframable
|
||||
*
|
||||
* Whether keyframing can be enabled on this row or not. This is true by default. Some values you may want to prevent
|
||||
* the user from keyframing (e.g. the filename of a VST plugin), which can be done by setting this to false.
|
||||
*/
|
||||
EffectRow(Effect* parent, const QString& n, bool savable = true, bool keyframable = true);
|
||||
|
||||
/**
|
||||
* @brief Add a field to this row
|
||||
*
|
||||
* Ownership of the EffectField is transferred to this row and the row will free its memory. In the Effect's UI, this
|
||||
* will add the field to an additional column.
|
||||
*
|
||||
* @param Field
|
||||
*
|
||||
* The field to add to this row.
|
||||
*/
|
||||
void AddField(EffectField* Field);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the EffectField at this index. Must be less than FieldCount().
|
||||
*
|
||||
* @param i
|
||||
*
|
||||
* Index to retrieve the EffectField at.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* EffectField at the provided index.
|
||||
*/
|
||||
EffectField* Field(int i);
|
||||
|
||||
/**
|
||||
* @brief Number of fields currently contained in this row.
|
||||
*
|
||||
* @return The number of fields currently contained in this row as an integer. Any field index (retrieved with
|
||||
* Field()) is guaranteed to be valid >= 0 and < FieldCount().
|
||||
*/
|
||||
int FieldCount();
|
||||
|
||||
/**
|
||||
* @brief Set a keyframe at the current playhead on all fields contained within this row
|
||||
*
|
||||
* @param ca
|
||||
*
|
||||
* The ComboAction to add this action to. This may not be nullptr.
|
||||
*/
|
||||
void SetKeyframeOnAllFields(ComboAction *ca);
|
||||
|
||||
/**
|
||||
* @brief Get parent Effect
|
||||
*
|
||||
* Equivalent to `static_cast<Effect*>(parent())`.
|
||||
*
|
||||
* @return The parent Effect object that this row is attached to.
|
||||
*/
|
||||
Effect* GetParentEffect();
|
||||
|
||||
/**
|
||||
* @brief Return the row's name
|
||||
*
|
||||
* @return The name specified in the constructor as a QString
|
||||
*/
|
||||
const QString& name();
|
||||
|
||||
/**
|
||||
* @brief Get whether this row is keyframing or not
|
||||
*
|
||||
* @return True if this row is keyframing.
|
||||
*/
|
||||
bool IsKeyframing();
|
||||
|
||||
/**
|
||||
* @brief Set whether this row is keyframing or not.
|
||||
*
|
||||
* It's not recommended to use this function for any user-initiated keyframe setting change as it doesn't create any
|
||||
* undoable actions. Use SetKeyframingEnabled() instead for a user-friendly variant.
|
||||
*/
|
||||
void SetKeyframingInternal(bool);
|
||||
|
||||
/**
|
||||
* @brief Get whether this row should be saved into a project file or not
|
||||
*
|
||||
* @return True if this row should be saved. This value is set in the constructor.
|
||||
*/
|
||||
bool IsSavable();
|
||||
|
||||
/**
|
||||
* @brief Get whether this row can be keyframed or not.
|
||||
*
|
||||
* @return True if this row can be keyframed. This value is set in the constructor.
|
||||
*/
|
||||
bool IsKeyframable();
|
||||
public slots:
|
||||
/**
|
||||
* @brief Go to previous keyframe
|
||||
*
|
||||
* Gets the closest keyframe prior to the current playhead and seeks to it.
|
||||
*
|
||||
* Attach to KeyframeNavigator::goto_previous_key() signal.
|
||||
*/
|
||||
void GoToPreviousKeyframe();
|
||||
|
||||
/**
|
||||
* @brief Toggle a keyframe at this point in time
|
||||
*
|
||||
* Either deletes (if any child EffectFields have any keyframes here) or creates (if none do) a keyframe on all
|
||||
* EffectField children at the current time.
|
||||
*
|
||||
* Attach to KeyframeNavigator::toggle_key() signal.
|
||||
*/
|
||||
void ToggleKeyframe();
|
||||
|
||||
/**
|
||||
* @brief Go to next keyframe
|
||||
*
|
||||
* Gets the closest keyframe after the current playhead and seeks to it.
|
||||
*
|
||||
* Attach to KeyframeNavigator::goto_next_key() signal.
|
||||
*/
|
||||
void GoToNextKeyframe();
|
||||
|
||||
/**
|
||||
* @brief Slot for whenever this EffectRow is focused.
|
||||
*
|
||||
* Connect UI objects gaining focus to this slot. Automatically updates the Graph Editor to attach to this row.
|
||||
*/
|
||||
void FocusRow();
|
||||
signals:
|
||||
/**
|
||||
* @brief Keyframing setting changed signal
|
||||
*
|
||||
* Emitted whenever keyframing is enabled or disabled.
|
||||
*
|
||||
* @param
|
||||
*
|
||||
* True if keyframing was enabled, false if keyframing was disabled.
|
||||
*/
|
||||
void KeyframingSetChanged(bool);
|
||||
private slots:
|
||||
/**
|
||||
* @brief Set keyframing enabled state
|
||||
*
|
||||
* A user-friendly function for enabling or disabling keyframes on this row. Preferred to SetKeyframingInternal()
|
||||
* for any user-initiated change. Automatically creates an undoable action so users can undo the enabling/disabling.
|
||||
* Also confirms with the user when disabling keyframing whether they wish to continue and remove all the current
|
||||
* keyframes.
|
||||
*
|
||||
* Attach to KeyframeNavigator::keyframe_enabled_changed() signal.
|
||||
*/
|
||||
void SetKeyframingEnabled(bool);
|
||||
private:
|
||||
/**
|
||||
* @brief Internal variable for the row's name
|
||||
*
|
||||
* Set in the constructor, retrieved with name().
|
||||
*/
|
||||
QString name_;
|
||||
|
||||
/**
|
||||
* @brief Internal variable for whether this row can be keyframed.
|
||||
*
|
||||
* Set in the constructor, retrieved with IsKeyframable().
|
||||
*/
|
||||
bool keyframable_;
|
||||
|
||||
/**
|
||||
* @brief Internal variable for whether this row is currently keyframing.
|
||||
*
|
||||
* Set by SetKeyframingInternal() and retrieved with IsKeyframing().
|
||||
*/
|
||||
bool keyframing_;
|
||||
|
||||
/**
|
||||
* @brief Internal variable for whether this row should be saved.
|
||||
*
|
||||
* Set in the constructor, retrieved with IsSavable().
|
||||
*/
|
||||
bool savable_;
|
||||
|
||||
/**
|
||||
* @brief Internal array of EffectField objects.
|
||||
*
|
||||
* It is not necessary to delete the elements in this array as they're already children of this QObject, so they'll
|
||||
* get freed automatically.
|
||||
*/
|
||||
QVector<EffectField*> fields_;
|
||||
};
|
||||
|
||||
#endif // EFFECTROW_H
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "boolfield.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
BoolField::BoolField(EffectRow *parent, const QString &id) :
|
||||
EffectField(parent, id, EFFECT_FIELD_BOOL)
|
||||
{}
|
||||
|
||||
bool BoolField::GetBoolAt(double timecode)
|
||||
{
|
||||
return GetValueAt(timecode).toBool();
|
||||
}
|
||||
|
||||
QWidget *BoolField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
QCheckBox* cb;
|
||||
|
||||
if (existing == nullptr) {
|
||||
|
||||
cb = new QCheckBox();
|
||||
cb->setEnabled(IsEnabled());
|
||||
|
||||
} else {
|
||||
|
||||
cb = static_cast<QCheckBox*>(existing);
|
||||
|
||||
}
|
||||
|
||||
connect(cb, SIGNAL(toggled(bool)), this, SLOT(UpdateFromWidget(bool)));
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), cb, SLOT(setEnabled(bool)));
|
||||
connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(Toggled(bool)));
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
void BoolField::UpdateWidgetValue(QWidget *widget, double timecode)
|
||||
{
|
||||
QCheckBox* cb = static_cast<QCheckBox*>(widget);
|
||||
|
||||
// Setting the checked state on the checkbox below normally triggers a change() signal that will then trickle back
|
||||
// to setting a value on this field. Therefore we block its signals while we're setting this.
|
||||
|
||||
cb->blockSignals(true);
|
||||
|
||||
if (qIsNaN(timecode)) {
|
||||
cb->setTristate(true);
|
||||
cb->setCheckState(Qt::PartiallyChecked);
|
||||
} else {
|
||||
cb->setTristate(false);
|
||||
cb->setChecked(GetBoolAt(timecode));
|
||||
}
|
||||
|
||||
cb->blockSignals(false);
|
||||
|
||||
emit Toggled(cb->isChecked());
|
||||
}
|
||||
|
||||
QVariant BoolField::ConvertStringToValue(const QString &s)
|
||||
{
|
||||
return (s == "1");
|
||||
}
|
||||
|
||||
QString BoolField::ConvertValueToString(const QVariant &v)
|
||||
{
|
||||
return QString::number(v.toBool());
|
||||
}
|
||||
|
||||
void BoolField::UpdateFromWidget(bool b)
|
||||
{
|
||||
KeyframeDataChange* kdc = new KeyframeDataChange(this);
|
||||
|
||||
SetValueAt(Now(), b);
|
||||
|
||||
kdc->SetNewKeyframes();
|
||||
olive::UndoStack.push(kdc);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef BOOLFIELD_H
|
||||
#define BOOLFIELD_H
|
||||
|
||||
#include "../effectfield.h"
|
||||
|
||||
class BoolField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BoolField(EffectRow* parent, const QString& id);
|
||||
|
||||
bool GetBoolAt(double timecode);
|
||||
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
|
||||
|
||||
virtual QVariant ConvertStringToValue(const QString& s) override;
|
||||
virtual QString ConvertValueToString(const QVariant& v) override;
|
||||
signals:
|
||||
void Toggled(bool);
|
||||
private slots:
|
||||
void UpdateFromWidget(bool b);
|
||||
};
|
||||
|
||||
#endif // BOOLFIELD_H
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "buttonfield.h"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
ButtonField::ButtonField(EffectRow *parent, const QString &string) :
|
||||
EffectField(parent, nullptr, EFFECT_FIELD_UI),
|
||||
button_text_(string)
|
||||
{}
|
||||
|
||||
void ButtonField::SetCheckable(bool c)
|
||||
{
|
||||
checkable_ = c;
|
||||
}
|
||||
|
||||
void ButtonField::SetChecked(bool c)
|
||||
{
|
||||
checked_ = c;
|
||||
emit CheckedChanged(c);
|
||||
}
|
||||
|
||||
QWidget *ButtonField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
QPushButton* button;
|
||||
|
||||
if (existing == nullptr) {
|
||||
|
||||
button = new QPushButton();
|
||||
|
||||
button->setCheckable(checkable_);
|
||||
button->setEnabled(IsEnabled());
|
||||
button->setText(button_text_);
|
||||
|
||||
} else {
|
||||
|
||||
button = static_cast<QPushButton*>(existing);
|
||||
|
||||
}
|
||||
|
||||
connect(this, SIGNAL(CheckedChanged(bool)), button, SLOT(setChecked(bool)));
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), button, SLOT(setEnabled(bool)));
|
||||
connect(button, SIGNAL(clicked(bool)), this, SIGNAL(Clicked()));
|
||||
connect(button, SIGNAL(toggled(bool)), this, SLOT(SetChecked(bool)));
|
||||
connect(button, SIGNAL(toggled(bool)), this, SIGNAL(Toggled(bool)));
|
||||
|
||||
return button;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef BUTTONFIELD_H
|
||||
#define BUTTONFIELD_H
|
||||
|
||||
#include "../effectfield.h"
|
||||
|
||||
class ButtonField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ButtonField(EffectRow* parent, const QString& string);
|
||||
|
||||
void SetCheckable(bool c);
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
|
||||
public slots:
|
||||
void SetChecked(bool c);
|
||||
|
||||
signals:
|
||||
void CheckedChanged(bool);
|
||||
void Toggled(bool);
|
||||
|
||||
private:
|
||||
bool checkable_;
|
||||
bool checked_;
|
||||
|
||||
QString button_text_;
|
||||
};
|
||||
|
||||
#endif // BUTTONFIELD_H
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "colorfield.h"
|
||||
|
||||
#include <QColor>
|
||||
|
||||
#include "ui/colorbutton.h"
|
||||
|
||||
ColorField::ColorField(EffectRow* parent, const QString& id) :
|
||||
EffectField(parent, id, EFFECT_FIELD_COLOR)
|
||||
{}
|
||||
|
||||
QColor ColorField::GetColorAt(double timecode)
|
||||
{
|
||||
return GetValueAt(timecode).value<QColor>();
|
||||
}
|
||||
|
||||
QWidget *ColorField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
ColorButton* cb = (existing != nullptr) ? static_cast<ColorButton*>(existing) : new ColorButton();
|
||||
|
||||
connect(cb, SIGNAL(color_changed(const QColor &)), this, SLOT(UpdateFromWidget(const QColor &)));
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), cb, SLOT(setEnabled(bool)));
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
void ColorField::UpdateWidgetValue(QWidget *widget, double timecode)
|
||||
{
|
||||
ColorButton* cb = static_cast<ColorButton*>(widget);
|
||||
|
||||
cb->set_color(GetColorAt(timecode));
|
||||
}
|
||||
|
||||
QVariant ColorField::ConvertStringToValue(const QString &s)
|
||||
{
|
||||
return QColor(s);
|
||||
}
|
||||
|
||||
QString ColorField::ConvertValueToString(const QVariant &v)
|
||||
{
|
||||
return v.value<QColor>().name();
|
||||
}
|
||||
|
||||
void ColorField::UpdateFromWidget(const QColor& c)
|
||||
{
|
||||
KeyframeDataChange* kdc = new KeyframeDataChange(this);
|
||||
|
||||
SetValueAt(Now(), c);
|
||||
|
||||
kdc->SetNewKeyframes();
|
||||
olive::UndoStack.push(kdc);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef COLORFIELD_H
|
||||
#define COLORFIELD_H
|
||||
|
||||
#include "../effectfield.h"
|
||||
|
||||
class ColorField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorField(EffectRow* parent, const QString& id);
|
||||
|
||||
QColor GetColorAt(double timecode);
|
||||
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
|
||||
|
||||
virtual QVariant ConvertStringToValue(const QString& s) override;
|
||||
virtual QString ConvertValueToString(const QVariant& v) override;
|
||||
private slots:
|
||||
void UpdateFromWidget(const QColor &c);
|
||||
};
|
||||
|
||||
#endif // COLORFIELD_H
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "combofield.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/comboboxex.h"
|
||||
|
||||
ComboField::ComboField(EffectRow* parent, const QString& id) :
|
||||
EffectField(parent, id, EFFECT_FIELD_COMBO)
|
||||
{}
|
||||
|
||||
void ComboField::AddItem(const QString &text, const QVariant &data)
|
||||
{
|
||||
ComboFieldItem item;
|
||||
|
||||
item.name = text;
|
||||
item.data = data;
|
||||
|
||||
items_.append(item);
|
||||
}
|
||||
|
||||
QWidget *ComboField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
ComboBoxEx* cb;
|
||||
|
||||
if (existing == nullptr) {
|
||||
cb = new ComboBoxEx();
|
||||
|
||||
cb->setScrollingEnabled(false);
|
||||
|
||||
for (int i=0;i<items_.size();i++) {
|
||||
cb->addItem(items_.at(i).name);
|
||||
}
|
||||
} else {
|
||||
cb = static_cast<ComboBoxEx*>(existing);
|
||||
}
|
||||
|
||||
connect(cb, SIGNAL(activated(int)), this, SLOT(UpdateFromWidget(int)));
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), cb, SLOT(setEnabled(bool)));
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
void ComboField::UpdateWidgetValue(QWidget *widget, double timecode)
|
||||
{
|
||||
QVariant data = GetValueAt(timecode);
|
||||
|
||||
ComboBoxEx* cb = static_cast<ComboBoxEx*>(widget);
|
||||
|
||||
for (int i=0;i<items_.size();i++) {
|
||||
if (items_.at(i).data == data) {
|
||||
cb->blockSignals(true);
|
||||
cb->setCurrentIndex(i);
|
||||
cb->blockSignals(false);
|
||||
|
||||
emit DataChanged(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "Failed to set ComboField value from data";
|
||||
}
|
||||
|
||||
void ComboField::UpdateFromWidget(int index)
|
||||
{
|
||||
KeyframeDataChange* kdc = new KeyframeDataChange(this);
|
||||
|
||||
SetValueAt(Now(), items_.at(index).data);
|
||||
|
||||
kdc->SetNewKeyframes();
|
||||
olive::UndoStack.push(kdc);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef COMBOFIELD_H
|
||||
#define COMBOFIELD_H
|
||||
|
||||
#include "../effectfield.h"
|
||||
|
||||
struct ComboFieldItem {
|
||||
QString name;
|
||||
QVariant data;
|
||||
};
|
||||
|
||||
class ComboField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ComboField(EffectRow* parent, const QString& id);
|
||||
|
||||
void AddItem(const QString& text, const QVariant& data);
|
||||
|
||||
virtual QWidget *CreateWidget(QWidget *existing = nullptr) override;
|
||||
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
|
||||
|
||||
signals:
|
||||
void DataChanged(const QVariant&);
|
||||
|
||||
private:
|
||||
QVector<ComboFieldItem> items_;
|
||||
|
||||
private slots:
|
||||
void UpdateFromWidget(int index);
|
||||
};
|
||||
|
||||
#endif // COMBOFIELD_H
|
||||
@@ -0,0 +1,131 @@
|
||||
#include "doublefield.h"
|
||||
|
||||
#include "effects/effectrow.h"
|
||||
|
||||
DoubleField::DoubleField(EffectRow* parent, const QString& id) :
|
||||
EffectField(parent, id, EFFECT_FIELD_DOUBLE),
|
||||
min_(qSNaN()),
|
||||
max_(qSNaN()),
|
||||
default_(0),
|
||||
display_type_(LabelSlider::Normal),
|
||||
frame_rate_(30),
|
||||
value_set_(false),
|
||||
kdc_(nullptr)
|
||||
{
|
||||
connect(this, SIGNAL(Changed()), this, SLOT(ValueHasBeenSet()), Qt::DirectConnection);
|
||||
}
|
||||
|
||||
double DoubleField::GetDoubleAt(double timecode)
|
||||
{
|
||||
return GetValueAt(timecode).toDouble();
|
||||
}
|
||||
|
||||
void DoubleField::SetMinimum(double minimum)
|
||||
{
|
||||
min_ = minimum;
|
||||
emit MinimumChanged(min_);
|
||||
}
|
||||
|
||||
void DoubleField::SetMaximum(double maximum)
|
||||
{
|
||||
max_ = maximum;
|
||||
emit MaximumChanged(max_);
|
||||
}
|
||||
|
||||
void DoubleField::SetDefault(double d)
|
||||
{
|
||||
default_ = d;
|
||||
|
||||
if (!value_set_) {
|
||||
SetValueAt(0, d);
|
||||
value_set_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DoubleField::SetDisplayType(LabelSlider::DisplayType type)
|
||||
{
|
||||
display_type_ = type;
|
||||
}
|
||||
|
||||
void DoubleField::SetFrameRate(const double &rate)
|
||||
{
|
||||
frame_rate_ = rate;
|
||||
}
|
||||
|
||||
QVariant DoubleField::ConvertStringToValue(const QString &s)
|
||||
{
|
||||
return s.toDouble();
|
||||
}
|
||||
|
||||
QString DoubleField::ConvertValueToString(const QVariant &v)
|
||||
{
|
||||
return QString::number(v.toDouble());
|
||||
}
|
||||
|
||||
QWidget *DoubleField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
LabelSlider* ls;
|
||||
|
||||
if (existing == nullptr) {
|
||||
|
||||
ls = new LabelSlider();
|
||||
|
||||
if (!qIsNaN(min_)) {
|
||||
ls->SetMinimum(min_);
|
||||
}
|
||||
ls->SetDefault(default_);
|
||||
if (!qIsNaN(max_)) {
|
||||
ls->SetMaximum(max_);
|
||||
}
|
||||
ls->SetDisplayType(display_type_);
|
||||
ls->SetFrameRate(frame_rate_);
|
||||
|
||||
ls->setEnabled(IsEnabled());
|
||||
|
||||
} else {
|
||||
|
||||
ls = static_cast<LabelSlider*>(existing);
|
||||
|
||||
}
|
||||
|
||||
connect(ls, SIGNAL(valueChanged(double)), this, SLOT(UpdateFromWidget(double)));
|
||||
connect(ls, SIGNAL(clicked()), this, SIGNAL(Clicked()));
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), ls, SLOT(setEnabled(bool)));
|
||||
connect(this, SIGNAL(MaximumChanged(double)), ls, SLOT(SetMaximum(double)));
|
||||
connect(this, SIGNAL(MinimumChanged(double)), ls, SLOT(SetMinimum(double)));
|
||||
|
||||
return ls;
|
||||
}
|
||||
|
||||
void DoubleField::UpdateWidgetValue(QWidget *widget, double timecode)
|
||||
{
|
||||
if (qIsNaN(timecode)) {
|
||||
static_cast<LabelSlider*>(widget)->SetValue(qSNaN());
|
||||
} else {
|
||||
static_cast<LabelSlider*>(widget)->SetValue(GetDoubleAt(timecode));
|
||||
}
|
||||
}
|
||||
|
||||
void DoubleField::ValueHasBeenSet()
|
||||
{
|
||||
value_set_ = true;
|
||||
}
|
||||
|
||||
void DoubleField::UpdateFromWidget(double d)
|
||||
{
|
||||
LabelSlider* ls = static_cast<LabelSlider*>(sender());
|
||||
|
||||
if (ls->IsDragging() && kdc_ == nullptr) {
|
||||
kdc_ = new KeyframeDataChange(this);
|
||||
}
|
||||
|
||||
SetValueAt(Now(), d);
|
||||
|
||||
if (!ls->IsDragging() && kdc_ != nullptr) {
|
||||
kdc_->SetNewKeyframes();
|
||||
|
||||
olive::UndoStack.push(kdc_);
|
||||
|
||||
kdc_ = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef DOUBLEFIELD_H
|
||||
#define DOUBLEFIELD_H
|
||||
|
||||
#include "../effectfield.h"
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
class DoubleField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DoubleField(EffectRow* parent, const QString& id);
|
||||
|
||||
/**
|
||||
* @brief Get double value at timecode
|
||||
*
|
||||
* Convenience function. Equivalent to GetValueAt().toDouble()
|
||||
*
|
||||
* @param timecode
|
||||
*
|
||||
* Timecode to retrieve value at
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Double value at the set timecode
|
||||
*/
|
||||
double GetDoubleAt(double timecode);
|
||||
|
||||
void SetMinimum(double minimum);
|
||||
void SetMaximum(double maximum);
|
||||
void SetDefault(double maximum);
|
||||
|
||||
void SetDisplayType(LabelSlider::DisplayType type);
|
||||
void SetFrameRate(const double& rate);
|
||||
|
||||
virtual QVariant ConvertStringToValue(const QString& s) override;
|
||||
virtual QString ConvertValueToString(const QVariant& v) override;
|
||||
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
|
||||
signals:
|
||||
void MaximumChanged(double maximum);
|
||||
void MinimumChanged(double maximum);
|
||||
private:
|
||||
double min_;
|
||||
double max_;
|
||||
double default_;
|
||||
|
||||
LabelSlider::DisplayType display_type_;
|
||||
double frame_rate_;
|
||||
|
||||
bool value_set_;
|
||||
|
||||
KeyframeDataChange* kdc_;
|
||||
private slots:
|
||||
void ValueHasBeenSet();
|
||||
void UpdateFromWidget(double d);
|
||||
};
|
||||
|
||||
#endif // DOUBLEFIELD_H
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "filefield.h"
|
||||
|
||||
#include "ui/embeddedfilechooser.h"
|
||||
|
||||
FileField::FileField(EffectRow* parent, const QString &id) :
|
||||
EffectField(parent, id, EFFECT_FIELD_FILE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString FileField::GetFileAt(double timecode)
|
||||
{
|
||||
return GetValueAt(timecode).toString();
|
||||
}
|
||||
|
||||
QWidget *FileField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
EmbeddedFileChooser* efc = (existing != nullptr) ? static_cast<EmbeddedFileChooser*>(existing) : new EmbeddedFileChooser();
|
||||
|
||||
connect(efc, SIGNAL(changed(const QString&)), this, SLOT(UpdateFromWidget(const QString&)));
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), efc, SLOT(setEnabled(bool)));
|
||||
|
||||
return efc;
|
||||
}
|
||||
|
||||
void FileField::UpdateFromWidget(const QString &s)
|
||||
{
|
||||
KeyframeDataChange* kdc = new KeyframeDataChange(this);
|
||||
|
||||
SetValueAt(Now(), s);
|
||||
|
||||
kdc->SetNewKeyframes();
|
||||
olive::UndoStack.push(kdc);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef FILEFIELD_H
|
||||
#define FILEFIELD_H
|
||||
|
||||
#include "../effectfield.h"
|
||||
|
||||
class FileField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FileField(EffectRow* parent, const QString& id);
|
||||
|
||||
QString GetFileAt(double timecode);
|
||||
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
private slots:
|
||||
void UpdateFromWidget(const QString &s);
|
||||
};
|
||||
|
||||
#endif // FILEFIELD_H
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "fontfield.h"
|
||||
|
||||
#include <QFontDatabase>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/comboboxex.h"
|
||||
|
||||
// NOTE/TODO: This shares a lot of similarity with ComboField, and could probably be a derived class of it
|
||||
|
||||
FontField::FontField(EffectRow* parent, const QString &id) :
|
||||
EffectField(parent, id, EFFECT_FIELD_FONT)
|
||||
{
|
||||
font_list = QFontDatabase().families();
|
||||
|
||||
SetValueAt(0, font_list.first());
|
||||
}
|
||||
|
||||
QString FontField::GetFontAt(double timecode)
|
||||
{
|
||||
return GetValueAt(timecode).toString();
|
||||
}
|
||||
|
||||
QWidget *FontField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
ComboBoxEx* fcb = new ComboBoxEx();
|
||||
|
||||
if (existing == nullptr) {
|
||||
|
||||
fcb = new ComboBoxEx();
|
||||
|
||||
fcb->setScrollingEnabled(false);
|
||||
|
||||
fcb->addItems(font_list);
|
||||
|
||||
} else {
|
||||
|
||||
fcb = static_cast<ComboBoxEx*>(existing);
|
||||
|
||||
}
|
||||
|
||||
connect(fcb, SIGNAL(currentTextChanged(const QString &)), this, SLOT(UpdateFromWidget(const QString &)));
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), fcb, SLOT(setEnabled(bool)));
|
||||
|
||||
return fcb;
|
||||
}
|
||||
|
||||
void FontField::UpdateWidgetValue(QWidget *widget, double timecode)
|
||||
{
|
||||
QVariant data = GetValueAt(timecode);
|
||||
|
||||
ComboBoxEx* cb = static_cast<ComboBoxEx*>(widget);
|
||||
|
||||
for (int i=0;i<font_list.size();i++) {
|
||||
if (font_list.at(i) == data) {
|
||||
cb->blockSignals(true);
|
||||
cb->setCurrentIndex(i);
|
||||
cb->blockSignals(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "Failed to set FontField value from data";
|
||||
}
|
||||
|
||||
void FontField::UpdateFromWidget(const QString& s)
|
||||
{
|
||||
KeyframeDataChange* kdc = new KeyframeDataChange(this);
|
||||
|
||||
SetValueAt(Now(), s);
|
||||
|
||||
kdc->SetNewKeyframes();
|
||||
olive::UndoStack.push(kdc);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef FONTFIELD_H
|
||||
#define FONTFIELD_H
|
||||
|
||||
#include "combofield.h"
|
||||
|
||||
class FontField : public EffectField {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FontField(EffectRow* parent, const QString& id);
|
||||
|
||||
QString GetFontAt(double timecode);
|
||||
|
||||
virtual QWidget *CreateWidget(QWidget *existing = nullptr) override;
|
||||
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
|
||||
|
||||
private:
|
||||
QStringList font_list;
|
||||
private slots:
|
||||
void UpdateFromWidget(const QString& index);
|
||||
};
|
||||
|
||||
#endif // FONTFIELD_H
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "labelfield.h"
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
LabelField::LabelField(EffectRow *parent, const QString &string) :
|
||||
EffectField(parent, nullptr, EFFECT_FIELD_UI),
|
||||
label_text_(string)
|
||||
{}
|
||||
|
||||
QWidget *LabelField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
QLabel* label;
|
||||
|
||||
if (existing == nullptr) {
|
||||
|
||||
label = new QLabel(label_text_);
|
||||
|
||||
label->setEnabled(IsEnabled());
|
||||
|
||||
} else {
|
||||
|
||||
label = static_cast<QLabel*>(existing);
|
||||
|
||||
}
|
||||
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), label, SLOT(setEnabled(bool)));
|
||||
|
||||
return label;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef LABELFIELD_H
|
||||
#define LABELFIELD_H
|
||||
|
||||
#include "../effectfield.h"
|
||||
|
||||
class LabelField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LabelField(EffectRow* parent, const QString& string);
|
||||
|
||||
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
|
||||
private:
|
||||
QString label_text_;
|
||||
};
|
||||
|
||||
#endif // LABELFIELD_H
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "stringfield.h"
|
||||
|
||||
#include <QtMath>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/texteditex.h"
|
||||
#include "global/config.h"
|
||||
|
||||
StringField::StringField(EffectRow* parent, const QString& id, bool rich_text) :
|
||||
EffectField(parent, id, EFFECT_FIELD_STRING),
|
||||
rich_text_(rich_text)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString StringField::GetStringAt(double timecode)
|
||||
{
|
||||
return GetValueAt(timecode).toString();
|
||||
}
|
||||
|
||||
QWidget *StringField::CreateWidget(QWidget *existing)
|
||||
{
|
||||
TextEditEx* text_edit;
|
||||
|
||||
if (existing == nullptr) {
|
||||
|
||||
text_edit = new TextEditEx(nullptr, rich_text_);
|
||||
|
||||
text_edit->setEnabled(IsEnabled());
|
||||
text_edit->setUndoRedoEnabled(true);
|
||||
|
||||
// the "2" is because the height needs one extra pixel of padding on the top and the bottom
|
||||
text_edit->setFixedHeight(qCeil(text_edit->fontMetrics().lineSpacing()*olive::CurrentConfig.effect_textbox_lines
|
||||
+ text_edit->document()->documentMargin()
|
||||
+ text_edit->document()->documentMargin() + 2));
|
||||
|
||||
} else {
|
||||
|
||||
text_edit = static_cast<TextEditEx*>(existing);
|
||||
|
||||
}
|
||||
|
||||
connect(text_edit, SIGNAL(textModified(const QString&)), this, SLOT(UpdateFromWidget(const QString&)));
|
||||
connect(this, SIGNAL(EnabledChanged(bool)), text_edit, SLOT(setEnabled(bool)));
|
||||
|
||||
return text_edit;
|
||||
}
|
||||
|
||||
void StringField::UpdateWidgetValue(QWidget *widget, double timecode)
|
||||
{
|
||||
TextEditEx* text = static_cast<TextEditEx*>(widget);
|
||||
|
||||
text->blockSignals(true);
|
||||
|
||||
int pos = text->textCursor().position();
|
||||
|
||||
if (rich_text_) {
|
||||
text->setHtml(GetValueAt(timecode).toString());
|
||||
} else {
|
||||
text->setPlainText(GetValueAt(timecode).toString());
|
||||
}
|
||||
|
||||
QTextCursor new_cursor(text->document());
|
||||
new_cursor.setPosition(pos);
|
||||
text->setTextCursor(new_cursor);
|
||||
|
||||
text->blockSignals(false);
|
||||
}
|
||||
|
||||
void StringField::UpdateFromWidget(const QString &s)
|
||||
{
|
||||
KeyframeDataChange* kdc = new KeyframeDataChange(this);
|
||||
|
||||
SetValueAt(Now(), s);
|
||||
|
||||
kdc->SetNewKeyframes();
|
||||
olive::UndoStack.push(kdc);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef STRINGFIELD_H
|
||||
#define STRINGFIELD_H
|
||||
|
||||
#include "../effectfield.h"
|
||||
|
||||
class StringField : public EffectField
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
StringField(EffectRow* parent, const QString& id, bool rich_text = true);
|
||||
|
||||
QString GetStringAt(double timecode);
|
||||
|
||||
virtual QWidget *CreateWidget(QWidget *existing = nullptr) override;
|
||||
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
|
||||
private slots:
|
||||
void UpdateFromWidget(const QString& b);
|
||||
private:
|
||||
bool rich_text_;
|
||||
};
|
||||
|
||||
#endif // STRINGFIELD_H
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Olive. Olive is a free non-linear video editor for Windows, macOS, and Linux.
|
||||
* Copyright (C) 2018 {{ organization }}
|
||||
*
|
||||
@@ -21,14 +21,15 @@
|
||||
#include <QtMath>
|
||||
|
||||
AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
amount_val = add_row(tr("Amount"))->add_field(EFFECT_FIELD_DOUBLE, "amount");
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
amount_val->set_double_default_value(20);
|
||||
|
||||
mix_val = add_row(tr("Mix"))->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val->set_bool_value(true);
|
||||
EffectRow* amount_row = new EffectRow(this, tr("Amount"));
|
||||
amount_val = new DoubleField(amount_row, "amount");
|
||||
amount_val->SetMinimum(0);
|
||||
amount_val->SetDefault(20);
|
||||
amount_val->SetMaximum(100);
|
||||
|
||||
EffectRow* mix_row = new EffectRow(this, tr("Mix"));
|
||||
mix_val = new BoolField(mix_row, "mix");
|
||||
mix_val->SetValueAt(0, true);
|
||||
}
|
||||
|
||||
void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) {
|
||||
@@ -40,12 +41,12 @@ void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end,
|
||||
qint16 right_noise_sample = this->randomNumber<qint16>();
|
||||
|
||||
// set noise volume
|
||||
double vol = log_volume( amount_val->get_double_value(timecode, true)*0.01 );
|
||||
double vol = log_volume( amount_val->GetDoubleAt(timecode)*0.01 );
|
||||
left_noise_sample *= vol;
|
||||
right_noise_sample *= vol;
|
||||
|
||||
// mix with source audio
|
||||
if (mix_val->get_bool_value(timecode, true)) {
|
||||
if (mix_val->GetBoolAt(timecode)) {
|
||||
qint16 left_sample = static_cast<qint16> (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
qint16 right_sample = static_cast<qint16> (((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF));
|
||||
left_noise_sample = mix_audio_sample(left_noise_sample, left_sample);
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
#ifndef AUDIONOISEEFFECT_H
|
||||
#define AUDIONOISEEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
class AudioNoiseEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioNoiseEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
AudioNoiseEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
EffectField* amount_val;
|
||||
EffectField* mix_val;
|
||||
DoubleField* amount_val;
|
||||
BoolField* mix_val;
|
||||
};
|
||||
|
||||
#endif // AUDIONOISEEFFECT_H
|
||||
|
||||
@@ -20,78 +20,78 @@
|
||||
|
||||
#include "cornerpineffect.h"
|
||||
|
||||
#include "io/path.h"
|
||||
#include "project/clip.h"
|
||||
#include "debug.h"
|
||||
#include "global/path.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
CornerPinEffect::CornerPinEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
enable_shader = true;
|
||||
SetFlags(Effect::CoordsFlag | Effect::ShaderFlag);
|
||||
|
||||
EffectRow* top_left = add_row(tr("Top Left"));
|
||||
top_left_x = top_left->add_field(EFFECT_FIELD_DOUBLE, "topleftx");
|
||||
top_left_y = top_left->add_field(EFFECT_FIELD_DOUBLE, "toplefty");
|
||||
EffectRow* top_left = new EffectRow(this, tr("Top Left"));
|
||||
top_left_x = new DoubleField(top_left, "topleftx");
|
||||
top_left_y = new DoubleField(top_left, "toplefty");
|
||||
|
||||
EffectRow* top_right = add_row(tr("Top Right"));
|
||||
top_right_x = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprightx");
|
||||
top_right_y = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprighty");
|
||||
EffectRow* top_right = new EffectRow(this, tr("Top Right"));
|
||||
top_right_x = new DoubleField(top_right, "toprightx");
|
||||
top_right_y = new DoubleField(top_right, "toprighty");
|
||||
|
||||
EffectRow* bottom_left = add_row(tr("Bottom Left"));
|
||||
bottom_left_x = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomleftx");
|
||||
bottom_left_y = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomlefty");
|
||||
EffectRow* bottom_left = new EffectRow(this, tr("Bottom Left"));
|
||||
bottom_left_x = new DoubleField(bottom_left, "bottomleftx");
|
||||
bottom_left_y = new DoubleField(bottom_left, "bottomlefty");
|
||||
|
||||
EffectRow* bottom_right = add_row(tr("Bottom Right"));
|
||||
bottom_right_x = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrightx");
|
||||
bottom_right_y = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrighty");
|
||||
EffectRow* bottom_right = new EffectRow(this, tr("Bottom Right"));
|
||||
bottom_right_x = new DoubleField(bottom_right, "bottomrightx");
|
||||
bottom_right_y = new DoubleField(bottom_right, "bottomrighty");
|
||||
|
||||
perspective = add_row(tr("Perspective"))->add_field(EFFECT_FIELD_BOOL, "perspective");
|
||||
perspective->set_bool_value(true);
|
||||
EffectRow* perspective_row = new EffectRow(this, tr("Perspective"));
|
||||
perspective = new BoolField(perspective_row, "perspective");
|
||||
perspective->SetValueAt(0, true);
|
||||
|
||||
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_left_gizmo->x_field1 = top_left_x;
|
||||
top_left_gizmo->y_field1 = top_left_y;
|
||||
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_left_gizmo->x_field1 = top_left_x;
|
||||
top_left_gizmo->y_field1 = top_left_y;
|
||||
|
||||
top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_right_gizmo->x_field1 = top_right_x;
|
||||
top_right_gizmo->y_field1 = top_right_y;
|
||||
top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_right_gizmo->x_field1 = top_right_x;
|
||||
top_right_gizmo->y_field1 = top_right_y;
|
||||
|
||||
bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_left_gizmo->x_field1 = bottom_left_x;
|
||||
bottom_left_gizmo->y_field1 = bottom_left_y;
|
||||
bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_left_gizmo->x_field1 = bottom_left_x;
|
||||
bottom_left_gizmo->y_field1 = bottom_left_y;
|
||||
|
||||
bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_right_gizmo->x_field1 = bottom_right_x;
|
||||
bottom_right_gizmo->y_field1 = bottom_right_y;
|
||||
bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_right_gizmo->x_field1 = bottom_right_x;
|
||||
bottom_right_gizmo->y_field1 = bottom_right_y;
|
||||
|
||||
vertPath = "cornerpin.vert";
|
||||
fragPath = "cornerpin.frag";
|
||||
vertPath = "cornerpin.vert";
|
||||
fragPath = "cornerpin.frag";
|
||||
}
|
||||
|
||||
void CornerPinEffect::process_coords(double timecode, GLTextureCoords &coords, int) {
|
||||
coords.vertexTopLeftX += top_left_x->get_double_value(timecode);
|
||||
coords.vertexTopLeftY += top_left_y->get_double_value(timecode);
|
||||
coords.vertexTopLeftX += top_left_x->GetDoubleAt(timecode);
|
||||
coords.vertexTopLeftY += top_left_y->GetDoubleAt(timecode);
|
||||
|
||||
coords.vertexTopRightX += top_right_x->get_double_value(timecode);
|
||||
coords.vertexTopRightY += top_right_y->get_double_value(timecode);
|
||||
coords.vertexTopRightX += top_right_x->GetDoubleAt(timecode);
|
||||
coords.vertexTopRightY += top_right_y->GetDoubleAt(timecode);
|
||||
|
||||
coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode);
|
||||
coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode);
|
||||
coords.vertexBottomLeftX += bottom_left_x->GetDoubleAt(timecode);
|
||||
coords.vertexBottomLeftY += bottom_left_y->GetDoubleAt(timecode);
|
||||
|
||||
coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode);
|
||||
coords.vertexBottomRightY += bottom_right_y->get_double_value(timecode);
|
||||
coords.vertexBottomRightX += bottom_right_x->GetDoubleAt(timecode);
|
||||
coords.vertexBottomRightY += bottom_right_y->GetDoubleAt(timecode);
|
||||
}
|
||||
|
||||
void CornerPinEffect::process_shader(double timecode, GLTextureCoords &coords, int) {
|
||||
glslProgram->setUniformValue("p0", GLfloat(coords.vertexBottomLeftX), GLfloat(coords.vertexBottomLeftY));
|
||||
glslProgram->setUniformValue("p1", GLfloat(coords.vertexBottomRightX), GLfloat(coords.vertexBottomRightY));
|
||||
glslProgram->setUniformValue("p2", GLfloat(coords.vertexTopLeftX), GLfloat(coords.vertexTopLeftY));
|
||||
glslProgram->setUniformValue("p3", GLfloat(coords.vertexTopRightX), GLfloat(coords.vertexTopRightY));
|
||||
glslProgram->setUniformValue("perspective", perspective->get_bool_value(timecode));
|
||||
glslProgram->setUniformValue("p0", GLfloat(coords.vertexBottomLeftX), GLfloat(coords.vertexBottomLeftY));
|
||||
glslProgram->setUniformValue("p1", GLfloat(coords.vertexBottomRightX), GLfloat(coords.vertexBottomRightY));
|
||||
glslProgram->setUniformValue("p2", GLfloat(coords.vertexTopLeftX), GLfloat(coords.vertexTopLeftY));
|
||||
glslProgram->setUniformValue("p3", GLfloat(coords.vertexTopRightX), GLfloat(coords.vertexTopRightY));
|
||||
glslProgram->setUniformValue("perspective", perspective->GetBoolAt(timecode));
|
||||
}
|
||||
|
||||
void CornerPinEffect::gizmo_draw(double, GLTextureCoords &coords) {
|
||||
top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
|
||||
top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
|
||||
bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
|
||||
bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
|
||||
top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
|
||||
top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
|
||||
bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
|
||||
bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
|
||||
}
|
||||
|
||||
@@ -21,30 +21,30 @@
|
||||
#ifndef CORNERPINEFFECT_H
|
||||
#define CORNERPINEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
class CornerPinEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
CornerPinEffect(Clip* c, const EffectMeta* em);
|
||||
void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
void process_shader(double timecode, GLTextureCoords& coords, int iterations);
|
||||
void gizmo_draw(double timecode, GLTextureCoords& coords);
|
||||
void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
void process_shader(double timecode, GLTextureCoords& coords, int iterations);
|
||||
void gizmo_draw(double timecode, GLTextureCoords& coords);
|
||||
private:
|
||||
EffectField* top_left_x;
|
||||
EffectField* top_left_y;
|
||||
EffectField* top_right_x;
|
||||
EffectField* top_right_y;
|
||||
EffectField* bottom_left_x;
|
||||
EffectField* bottom_left_y;
|
||||
EffectField* bottom_right_x;
|
||||
EffectField* bottom_right_y;
|
||||
EffectField* perspective;
|
||||
DoubleField* top_left_x;
|
||||
DoubleField* top_left_y;
|
||||
DoubleField* top_right_x;
|
||||
DoubleField* top_right_y;
|
||||
DoubleField* bottom_left_x;
|
||||
DoubleField* bottom_left_y;
|
||||
DoubleField* bottom_right_x;
|
||||
DoubleField* bottom_right_y;
|
||||
BoolField* perspective;
|
||||
|
||||
EffectGizmo* top_left_gizmo;
|
||||
EffectGizmo* top_right_gizmo;
|
||||
EffectGizmo* bottom_left_gizmo;
|
||||
EffectGizmo* bottom_right_gizmo;
|
||||
EffectGizmo* top_left_gizmo;
|
||||
EffectGizmo* top_right_gizmo;
|
||||
EffectGizmo* bottom_left_gizmo;
|
||||
EffectGizmo* bottom_right_gizmo;
|
||||
};
|
||||
|
||||
#endif // CORNERPINEFFECT_H
|
||||
|
||||
@@ -23,14 +23,12 @@
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
CrossDissolveTransition::CrossDissolveTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {
|
||||
enable_coords = true;
|
||||
|
||||
// add_row("Smooth")->add_field(EFFECT_FIELD_BOOL, "smooth");
|
||||
SetFlags(Effect::CoordsFlag);
|
||||
}
|
||||
|
||||
void CrossDissolveTransition::process_coords(double progress, GLTextureCoords& coords, int data) {
|
||||
if (!(data == kTransitionClosing && secondary_clip != nullptr)) {
|
||||
if (data == kTransitionClosing) progress = 1.0 - progress;
|
||||
coords.opacity *= progress;
|
||||
}
|
||||
if (!(data == kTransitionClosing && secondary_clip != nullptr)) {
|
||||
if (data == kTransitionClosing) progress = 1.0 - progress;
|
||||
coords.opacity *= progress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#ifndef CROSSDISSOLVETRANSITION_H
|
||||
#define CROSSDISSOLVETRANSITION_H
|
||||
|
||||
#include "project/transition.h"
|
||||
#include "effects/transition.h"
|
||||
|
||||
class CrossDissolveTransition : public Transition {
|
||||
public:
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "cubetransition.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
CubeTransition::CubeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {
|
||||
enable_coords = true;
|
||||
}
|
||||
|
||||
void CubeTransition::process_coords(double, GLTextureCoords& coords, int) {
|
||||
|
||||
coords.vertexTopLeftZ = 1;
|
||||
coords.vertexBottomLeftZ = 1;
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
#ifndef CUBETRANSITION_H
|
||||
#define CUBETRANSITION_H
|
||||
|
||||
#include "project/transition.h"
|
||||
#include "effects/transition.h"
|
||||
|
||||
class CubeTransition : public Transition {
|
||||
public:
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
#ifndef EXPONENTIALFADETRANSITION_H
|
||||
#define EXPONENTIALFADETRANSITION_H
|
||||
|
||||
#include "project/transition.h"
|
||||
#include "effects/transition.h"
|
||||
|
||||
class ExponentialFadeTransition : public Transition {
|
||||
public:
|
||||
ExponentialFadeTransition(Clip* c, Clip* s, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
};
|
||||
|
||||
#endif // LINEARFADETRANSITION_H
|
||||
|
||||
@@ -24,21 +24,21 @@
|
||||
#define FILL_TYPE_RIGHT 1
|
||||
|
||||
FillLeftRightEffect::FillLeftRightEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
EffectRow* type_row = add_row(tr("Type"));
|
||||
fill_type = type_row->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
fill_type->add_combo_item(tr("Fill Left with Right"), FILL_TYPE_LEFT);
|
||||
fill_type->add_combo_item(tr("Fill Right with Left"), FILL_TYPE_RIGHT);
|
||||
EffectRow* type_row = new EffectRow(this, tr("Type"));
|
||||
fill_type = new ComboField(type_row, "type");
|
||||
fill_type->AddItem(tr("Fill Left with Right"), FILL_TYPE_LEFT);
|
||||
fill_type->AddItem(tr("Fill Right with Left"), FILL_TYPE_RIGHT);
|
||||
}
|
||||
|
||||
void FillLeftRightEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) {
|
||||
double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
if (fill_type->get_combo_data(timecode_start+(interval*i)) == FILL_TYPE_LEFT) {
|
||||
samples[i+1] = samples[i+3];
|
||||
samples[i] = samples[i+2];
|
||||
} else {
|
||||
samples[i+3] = samples[i+1];
|
||||
samples[i+2] = samples[i];
|
||||
}
|
||||
}
|
||||
double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
if (fill_type->GetValueAt(timecode_start+(interval*i)) == FILL_TYPE_LEFT) {
|
||||
samples[i+1] = samples[i+3];
|
||||
samples[i] = samples[i+2];
|
||||
} else {
|
||||
samples[i+3] = samples[i+1];
|
||||
samples[i+2] = samples[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
#ifndef FILLLEFTRIGHTEFFECT_H
|
||||
#define FILLLEFTRIGHTEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
class FillLeftRightEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
FillLeftRightEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
FillLeftRightEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
private:
|
||||
EffectField* fill_type;
|
||||
ComboField* fill_type;
|
||||
};
|
||||
|
||||
#endif // FILLLEFTRIGHTEFFECT_H
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
|
||||
#include "project/clip.h"
|
||||
#include "timeline/clip.h"
|
||||
|
||||
typedef f0r_instance_t (*f0rConstructFunc)(unsigned int width, unsigned int height);
|
||||
typedef int (*f0rInitFunc) ();
|
||||
@@ -41,7 +41,7 @@ Frei0rEffect::Frei0rEffect(Clip* c, const EffectMeta *em) :
|
||||
Effect(c, em),
|
||||
open(false)
|
||||
{
|
||||
enable_image = true;
|
||||
SetFlags(ImageFlag);
|
||||
|
||||
// Windows DLL loading routine
|
||||
QString dll_fn = QDir(em->path).filePath(em->filename);
|
||||
@@ -92,33 +92,33 @@ Frei0rEffect::Frei0rEffect(Clip* c, const EffectMeta *em) :
|
||||
get_param_info(¶m_info, i);
|
||||
|
||||
if (param_info.type >= 0 && param_info.type <= F0R_PARAM_STRING) {
|
||||
EffectRow* row = add_row(param_info.name);
|
||||
EffectRow* row = new EffectRow(this, param_info.name);
|
||||
switch (param_info.type) {
|
||||
case F0R_PARAM_BOOL:
|
||||
row->add_field(EFFECT_FIELD_BOOL, QString::number(i));
|
||||
new BoolField(row, QString::number(i));
|
||||
break;
|
||||
case F0R_PARAM_DOUBLE:
|
||||
{
|
||||
EffectField* f = row->add_field(EFFECT_FIELD_DOUBLE, QString::number(i));
|
||||
f->set_double_minimum_value(0);
|
||||
f->set_double_maximum_value(100);
|
||||
DoubleField* f = new DoubleField(row, QString::number(i));
|
||||
f->SetMinimum(0);
|
||||
f->SetMaximum(100);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_COLOR:
|
||||
row->add_field(EFFECT_FIELD_COLOR, QString::number(i));
|
||||
new ColorField(row, QString::number(i));
|
||||
break;
|
||||
case F0R_PARAM_POSITION:
|
||||
{
|
||||
EffectField* fx = row->add_field(EFFECT_FIELD_DOUBLE, QString("%1X").arg(QString::number(i)));
|
||||
fx->set_double_minimum_value(0);
|
||||
fx->set_double_maximum_value(100);
|
||||
EffectField* fy = row->add_field(EFFECT_FIELD_DOUBLE, QString("%1Y").arg(QString::number(i)));
|
||||
fy->set_double_minimum_value(0);
|
||||
fy->set_double_maximum_value(100);
|
||||
DoubleField* fx = new DoubleField(row, QString("%1X").arg(QString::number(i)));
|
||||
fx->SetMinimum(0);
|
||||
fx->SetMaximum(100);
|
||||
DoubleField* fy = new DoubleField(row, QString("%1Y").arg(QString::number(i)));
|
||||
fy->SetMinimum(0);
|
||||
fy->SetMaximum(100);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_STRING:
|
||||
row->add_field(EFFECT_FIELD_STRING, QString::number(i));
|
||||
new StringField(row, QString::number(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -147,19 +147,21 @@ void Frei0rEffect::process_image(double timecode, uint8_t *input, uint8_t *outpu
|
||||
switch (param_info.type) {
|
||||
case F0R_PARAM_BOOL:
|
||||
{
|
||||
double b = param_row->field(0)->get_bool_value(timecode);
|
||||
double b = param_row->Field(0)->GetValueAt(timecode).toBool();
|
||||
|
||||
set_param(instance, &b, i);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_DOUBLE:
|
||||
{
|
||||
double d = param_row->field(0)->get_double_value(timecode)*0.01;
|
||||
double d = param_row->Field(0)->GetValueAt(timecode).toDouble()*0.01;
|
||||
|
||||
set_param(instance, &d, i);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_COLOR:
|
||||
{
|
||||
QColor qcolor = param_row->field(0)->get_color_value(timecode);;
|
||||
QColor qcolor = param_row->Field(0)->GetValueAt(timecode).value<QColor>();
|
||||
|
||||
f0r_param_color fcolor;
|
||||
fcolor.r = float(qcolor.redF());
|
||||
@@ -172,14 +174,17 @@ void Frei0rEffect::process_image(double timecode, uint8_t *input, uint8_t *outpu
|
||||
case F0R_PARAM_POSITION:
|
||||
{
|
||||
f0r_param_position pos;
|
||||
pos.x = param_row->field(0)->get_double_value(timecode);
|
||||
pos.y = param_row->field(1)->get_double_value(timecode);
|
||||
|
||||
pos.x = param_row->Field(0)->GetValueAt(timecode).toDouble();
|
||||
pos.y = param_row->Field(1)->GetValueAt(timecode).toDouble();
|
||||
|
||||
set_param(instance, &pos, i);
|
||||
}
|
||||
break;
|
||||
case F0R_PARAM_STRING:
|
||||
{
|
||||
QByteArray bytes = param_row->field(0)->get_string_value(timecode).toUtf8();
|
||||
QByteArray bytes = param_row->Field(0)->GetValueAt(timecode).toString().toUtf8();
|
||||
|
||||
char* byte_data = bytes.data();
|
||||
set_param(instance, &byte_data, i);
|
||||
}
|
||||
|
||||
@@ -23,11 +23,10 @@
|
||||
|
||||
#ifndef NO_FREI0R
|
||||
|
||||
#include "project/effect.h"
|
||||
|
||||
#include <frei0r.h>
|
||||
|
||||
#include "io/crossplatformlib.h"
|
||||
#include "effects/effect.h"
|
||||
#include "global/crossplatformlib.h"
|
||||
|
||||
typedef void (*f0rGetParamInfo)(f0r_param_info_t * info,
|
||||
int param_index );
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
#ifndef LINEARFADETRANSITION_H
|
||||
#define LINEARFADETRANSITION_H
|
||||
|
||||
#include "project/transition.h"
|
||||
#include "effects/transition.h"
|
||||
|
||||
class LinearFadeTransition : public Transition {
|
||||
public:
|
||||
LinearFadeTransition(Clip* c, Clip* s, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
};
|
||||
|
||||
#endif // LINEARFADETRANSITION_H
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
#ifndef LOGARITHMICFADETRANSITION_H
|
||||
#define LOGARITHMICFADETRANSITION_H
|
||||
|
||||
#include "project/transition.h"
|
||||
#include "effects/transition.h"
|
||||
|
||||
class LogarithmicFadeTransition : public Transition {
|
||||
public:
|
||||
LogarithmicFadeTransition(Clip* c, Clip* s, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
};
|
||||
|
||||
#endif // LOGARITHMICFADETRANSITION_H
|
||||
|
||||
@@ -29,35 +29,33 @@
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
EffectRow* pan_row = add_row(tr("Pan"));
|
||||
pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE, "pan");
|
||||
pan_val->set_double_minimum_value(-100);
|
||||
pan_val->set_double_maximum_value(100);
|
||||
|
||||
// set defaults
|
||||
pan_val->set_double_default_value(0);
|
||||
EffectRow* pan_row = new EffectRow(this, tr("Pan"));
|
||||
pan_val = new DoubleField(pan_row, "pan");
|
||||
pan_val->SetMinimum(-100);
|
||||
pan_val->SetDefault(0);
|
||||
pan_val->SetMaximum(100);
|
||||
}
|
||||
|
||||
void PanEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) {
|
||||
double interval = (timecode_end - timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
double pan_field_val = pan_val->get_double_value(timecode_start+(interval*i), true);
|
||||
double pval = log_volume(qAbs(pan_field_val)*0.01);
|
||||
double interval = (timecode_end - timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
double pan_field_val = pan_val->GetDoubleAt(timecode_start+(interval*i));
|
||||
double pval = log_volume(qAbs(pan_field_val)*0.01);
|
||||
|
||||
qint16 left_sample = qint16(((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
qint16 right_sample = qint16(((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF));
|
||||
qint16 left_sample = qint16(((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
qint16 right_sample = qint16(((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF));
|
||||
|
||||
if (pan_field_val < 0) {
|
||||
// affect right channel
|
||||
right_sample *= (1.0-pval);
|
||||
} else {
|
||||
// affect left channel
|
||||
left_sample *= (1.0-pval);
|
||||
}
|
||||
if (pan_field_val < 0) {
|
||||
// affect right channel
|
||||
right_sample *= (1.0-pval);
|
||||
} else {
|
||||
// affect left channel
|
||||
left_sample *= (1.0-pval);
|
||||
}
|
||||
|
||||
samples[i+3] = quint8(right_sample >> 8);
|
||||
samples[i+2] = quint8(right_sample);
|
||||
samples[i+1] = quint8(left_sample >> 8);
|
||||
samples[i] = quint8(left_sample);
|
||||
}
|
||||
samples[i+3] = quint8(right_sample >> 8);
|
||||
samples[i+2] = quint8(right_sample);
|
||||
samples[i+1] = quint8(left_sample >> 8);
|
||||
samples[i] = quint8(left_sample);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
#ifndef PANEFFECT_H
|
||||
#define PANEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
class PanEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
PanEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
PanEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
EffectField* pan_val;
|
||||
DoubleField* pan_val;
|
||||
};
|
||||
|
||||
#endif // PANEFFECT_H
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
#include "richtexteffect.h"
|
||||
|
||||
#include <QTextDocument>
|
||||
#include <QtMath>
|
||||
|
||||
#include "timeline/clip.h"
|
||||
#include "ui/blur.h"
|
||||
|
||||
enum AutoscrollDirection {
|
||||
SCROLL_OFF,
|
||||
SCROLL_UP,
|
||||
SCROLL_DOWN,
|
||||
SCROLL_LEFT,
|
||||
SCROLL_RIGHT,
|
||||
};
|
||||
|
||||
RichTextEffect::RichTextEffect(Clip *c, const EffectMeta *em) :
|
||||
Effect(c, em)
|
||||
{
|
||||
SetFlags(Effect::SuperimposeFlag);
|
||||
|
||||
EffectRow* text_row = new EffectRow(this, tr("Text"));
|
||||
text_val = new StringField(text_row, "text");
|
||||
text_val->SetColumnSpan(2);
|
||||
|
||||
EffectRow* padding_row = new EffectRow(this, tr("Padding"));
|
||||
padding_field = new DoubleField(padding_row, "padding");
|
||||
padding_field->SetColumnSpan(2);
|
||||
|
||||
EffectRow* position_row = new EffectRow(this, tr("Position"));
|
||||
position_x = new DoubleField(position_row, "posx");
|
||||
position_y = new DoubleField(position_row, "posy");
|
||||
|
||||
EffectRow* vertical_align_row = new EffectRow(this, tr("Vertical Align:"));
|
||||
vertical_align = new ComboField(vertical_align_row, "valign");
|
||||
vertical_align->AddItem(tr("Top"), Qt::AlignTop);
|
||||
vertical_align->AddItem(tr("Center"), Qt::AlignCenter);
|
||||
vertical_align->AddItem(tr("Bottom"), Qt::AlignBottom);
|
||||
vertical_align->SetValueAt(0, Qt::AlignCenter);
|
||||
vertical_align->SetColumnSpan(2);
|
||||
|
||||
EffectRow* autoscroll_row = new EffectRow(this, tr("Auto-Scroll"));
|
||||
autoscroll = new ComboField(autoscroll_row, "autoscroll");
|
||||
autoscroll->AddItem(tr("Off"), SCROLL_OFF);
|
||||
autoscroll->AddItem(tr("Up"), SCROLL_UP);
|
||||
autoscroll->AddItem(tr("Down"), SCROLL_DOWN);
|
||||
autoscroll->AddItem(tr("Left"), SCROLL_LEFT);
|
||||
autoscroll->AddItem(tr("Right"), SCROLL_RIGHT);
|
||||
autoscroll->SetColumnSpan(2);
|
||||
|
||||
EffectRow* shadow_row = new EffectRow(this, tr("Shadow"));
|
||||
shadow_bool = new BoolField(shadow_row, "shadow");
|
||||
shadow_bool->SetColumnSpan(2);
|
||||
|
||||
EffectRow* shadow_color_row = new EffectRow(this, tr("Shadow Color"));
|
||||
shadow_color = new ColorField(shadow_color_row, "shadowcolor");
|
||||
shadow_color->SetColumnSpan(2);
|
||||
|
||||
EffectRow* shadow_angle_row = new EffectRow(this, tr("Shadow Angle"));
|
||||
shadow_angle = new DoubleField(shadow_angle_row, "shadowangle");
|
||||
shadow_angle->SetColumnSpan(2);
|
||||
|
||||
EffectRow* shadow_distance_row = new EffectRow(this, tr("Shadow Distance"));
|
||||
shadow_distance = new DoubleField(shadow_distance_row, "shadowdistance");
|
||||
shadow_distance->SetColumnSpan(2);
|
||||
shadow_distance->SetMinimum(0);
|
||||
|
||||
EffectRow* shadow_softness_row = new EffectRow(this, tr("Shadow Softness"));
|
||||
shadow_softness = new DoubleField(shadow_softness_row, "shadowsoftness");
|
||||
shadow_softness->SetColumnSpan(2);
|
||||
shadow_softness->SetMinimum(0);
|
||||
|
||||
EffectRow* shadow_opacity_row = new EffectRow(this, tr("Shadow Opacity"));
|
||||
shadow_opacity = new DoubleField(shadow_opacity_row, "shadowopacity");
|
||||
shadow_opacity->SetColumnSpan(2);
|
||||
shadow_opacity->SetMinimum(0);
|
||||
shadow_opacity->SetMaximum(100);
|
||||
|
||||
// Create default text
|
||||
text_val->SetValueAt(0, "<html>"
|
||||
"<body style=\"color: #ffffff; font-size: 36pt;\">"
|
||||
"<center>Sample Text</center>"
|
||||
"</body>"
|
||||
"</html>");
|
||||
}
|
||||
|
||||
void RichTextEffect::redraw(double timecode)
|
||||
{
|
||||
QPainter p(&img);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
int width = img.width();
|
||||
int height = img.height();
|
||||
|
||||
int padding = qRound(padding_field->GetDoubleAt(timecode));
|
||||
|
||||
width -= 2 * padding;
|
||||
height -= 2 * padding;
|
||||
|
||||
QTextDocument td;
|
||||
td.setHtml(text_val->GetStringAt(timecode));
|
||||
td.setTextWidth(width);
|
||||
|
||||
int translate_x = qRound(position_x->GetDoubleAt(timecode) + padding);
|
||||
int translate_y = qRound(position_y->GetDoubleAt(timecode) + padding);
|
||||
|
||||
int doc_height = qRound(td.size().height());
|
||||
|
||||
AutoscrollDirection auto_scroll_dir = static_cast<AutoscrollDirection>(autoscroll->GetValueAt(timecode).toInt());
|
||||
|
||||
double scroll_progress = 0;
|
||||
|
||||
if (auto_scroll_dir != SCROLL_OFF) {
|
||||
double clip_length_secs = double(parent_clip->length()) / parent_clip->media_frame_rate();
|
||||
scroll_progress = (timecode - double(parent_clip->clip_in()) / parent_clip->media_frame_rate()) / clip_length_secs;
|
||||
}
|
||||
|
||||
if (auto_scroll_dir == SCROLL_OFF || auto_scroll_dir == SCROLL_LEFT || auto_scroll_dir == SCROLL_RIGHT) {
|
||||
|
||||
// If we're not auto-scrolling the vertical direction, respect the vertical alignment
|
||||
if (vertical_align->GetValueAt(timecode).toInt() == Qt::AlignCenter) {
|
||||
translate_y += height / 2 - doc_height / 2;
|
||||
} else if (vertical_align->GetValueAt(timecode).toInt() == Qt::AlignBottom) {
|
||||
translate_y += height - doc_height;
|
||||
}
|
||||
|
||||
// Check if we are autoscrolling
|
||||
if (auto_scroll_dir != SCROLL_OFF) {
|
||||
|
||||
if (auto_scroll_dir == SCROLL_LEFT) {
|
||||
scroll_progress = 1.0 - scroll_progress;
|
||||
}
|
||||
|
||||
int doc_width = qRound(td.size().width());
|
||||
translate_x += qRound(-doc_width + (img.width() + doc_width) * scroll_progress);
|
||||
}
|
||||
|
||||
} else if (auto_scroll_dir == SCROLL_UP || auto_scroll_dir == SCROLL_DOWN) {
|
||||
|
||||
// Auto-scroll bottom to top or top to bottom
|
||||
|
||||
if (auto_scroll_dir == SCROLL_UP) {
|
||||
scroll_progress = 1.0 - scroll_progress;
|
||||
}
|
||||
|
||||
translate_y += qRound(-doc_height + (img.height() + doc_height)*scroll_progress);
|
||||
|
||||
}
|
||||
|
||||
QRect clip_rect = img.rect();
|
||||
clip_rect.translate(-translate_x, -translate_y);
|
||||
p.translate(translate_x, translate_y);
|
||||
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
// draw software shadow
|
||||
if (shadow_bool->GetBoolAt(timecode)) {
|
||||
|
||||
// calculate offset using distance and angle
|
||||
double angle = shadow_angle->GetDoubleAt(timecode) * M_PI / 180.0;
|
||||
double distance = qFloor(shadow_distance->GetDoubleAt(timecode));
|
||||
int shadow_x_offset = qRound(qCos(angle) * distance);
|
||||
int shadow_y_offset = qRound(qSin(angle) * distance);
|
||||
|
||||
p.translate(shadow_x_offset, shadow_y_offset);
|
||||
clip_rect.translate(-shadow_x_offset, -shadow_y_offset);
|
||||
|
||||
td.drawContents(&p, clip_rect);
|
||||
|
||||
int blurSoftness = qFloor(shadow_softness->GetDoubleAt(timecode));
|
||||
if (blurSoftness > 0) {
|
||||
olive::ui::blur(img, img.rect(), blurSoftness, true);
|
||||
}
|
||||
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||
|
||||
p.fillRect(img.rect(), shadow_color->GetColorAt(timecode));
|
||||
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
p.translate(-shadow_x_offset, -shadow_y_offset);
|
||||
clip_rect.translate(shadow_x_offset, shadow_y_offset);
|
||||
}
|
||||
|
||||
td.drawContents(&p, clip_rect);
|
||||
|
||||
p.end();
|
||||
}
|
||||
|
||||
bool RichTextEffect::AlwaysUpdate()
|
||||
{
|
||||
return autoscroll->GetValueAt(autoscroll->Now()).toInt() != SCROLL_OFF;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef RICHTEXTEFFECT_H
|
||||
#define RICHTEXTEFFECT_H
|
||||
|
||||
#include "effects/effect.h"
|
||||
|
||||
class RichTextEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RichTextEffect(Clip* c, const EffectMeta *em);
|
||||
void redraw(double timecode);
|
||||
protected:
|
||||
virtual bool AlwaysUpdate() override;
|
||||
private:
|
||||
StringField* text_val;
|
||||
DoubleField* padding_field;
|
||||
DoubleField* position_x;
|
||||
DoubleField* position_y;
|
||||
ComboField* vertical_align;
|
||||
ComboField* autoscroll;
|
||||
|
||||
BoolField* shadow_bool;
|
||||
DoubleField* shadow_angle;
|
||||
DoubleField* shadow_distance;
|
||||
ColorField* shadow_color;
|
||||
DoubleField* shadow_softness;
|
||||
DoubleField* shadow_opacity;
|
||||
};
|
||||
|
||||
#endif // RICHTEXTEFFECT_H
|
||||
@@ -27,69 +27,66 @@
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "panels/timeline.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
ShakeEffect::ShakeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
SetFlags(Effect::CoordsFlag);
|
||||
|
||||
EffectRow* intensity_row = add_row(tr("Intensity"));
|
||||
intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity");
|
||||
intensity_val->set_double_minimum_value(0);
|
||||
EffectRow* intensity_row = new EffectRow(this, tr("Intensity"));
|
||||
intensity_val = new DoubleField(intensity_row, "intensity");
|
||||
intensity_val->SetMinimum(0);
|
||||
intensity_val->SetDefault(25);
|
||||
|
||||
EffectRow* rotation_row = add_row(tr("Rotation"));
|
||||
rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation");
|
||||
rotation_val->set_double_minimum_value(0);
|
||||
EffectRow* rotation_row = new EffectRow(this, tr("Rotation"));
|
||||
rotation_val = new DoubleField(rotation_row, "rotation");
|
||||
rotation_val->SetMinimum(0);
|
||||
rotation_val->SetDefault(10);
|
||||
|
||||
EffectRow* frequency_row = add_row(tr("Frequency"));
|
||||
frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
frequency_val->set_double_minimum_value(0);
|
||||
EffectRow* frequency_row = new EffectRow(this, tr("Frequency"));
|
||||
frequency_val = new DoubleField(frequency_row, "frequency");
|
||||
frequency_val->SetMinimum(0);
|
||||
frequency_val->SetDefault(5);
|
||||
|
||||
// set defaults
|
||||
intensity_val->set_double_default_value(25);
|
||||
rotation_val->set_double_default_value(10);
|
||||
frequency_val->set_double_default_value(5);
|
||||
|
||||
const auto limit = std::numeric_limits<int32_t>::max();
|
||||
for (int i=0;i<RANDOM_VAL_SIZE;i++) {
|
||||
random_vals[i] = static_cast<double>(this->randomNumber<int32_t>()) / limit;
|
||||
}
|
||||
const auto limit = std::numeric_limits<int32_t>::max();
|
||||
for (int i=0;i<RANDOM_VAL_SIZE;i++) {
|
||||
random_vals[i] = static_cast<double>(this->randomNumber<int32_t>()) / limit;
|
||||
}
|
||||
}
|
||||
|
||||
void ShakeEffect::process_coords(double timecode, GLTextureCoords& coords, int) {
|
||||
int lim = RANDOM_VAL_SIZE/6;
|
||||
int lim = RANDOM_VAL_SIZE/6;
|
||||
|
||||
double multiplier = intensity_val->get_double_value(timecode)/lim;
|
||||
double rotmult = rotation_val->get_double_value(timecode)/lim/10;
|
||||
double x = timecode * frequency_val->get_double_value(timecode);
|
||||
double multiplier = intensity_val->GetDoubleAt(timecode)/lim;
|
||||
double rotmult = rotation_val->GetDoubleAt(timecode)/lim/10;
|
||||
double x = timecode * frequency_val->GetDoubleAt(timecode);
|
||||
|
||||
double xoff = 0;
|
||||
double yoff = 0;
|
||||
double rotoff = 0;
|
||||
double xoff = 0;
|
||||
double yoff = 0;
|
||||
double rotoff = 0;
|
||||
|
||||
for (int i=0;i<lim;i++) {
|
||||
int offset = 6*i;
|
||||
xoff += qSin((x+random_vals[offset])*random_vals[offset+1]);
|
||||
yoff += qSin((x+random_vals[offset+2])*random_vals[offset+3]);
|
||||
rotoff += qSin((x+random_vals[offset+4])*random_vals[offset+5]);
|
||||
}
|
||||
for (int i=0;i<lim;i++) {
|
||||
int offset = 6*i;
|
||||
xoff += qSin((x+random_vals[offset])*random_vals[offset+1]);
|
||||
yoff += qSin((x+random_vals[offset+2])*random_vals[offset+3]);
|
||||
rotoff += qSin((x+random_vals[offset+4])*random_vals[offset+5]);
|
||||
}
|
||||
|
||||
xoff *= multiplier;
|
||||
yoff *= multiplier;
|
||||
rotoff *= rotmult;
|
||||
xoff *= multiplier;
|
||||
yoff *= multiplier;
|
||||
rotoff *= rotmult;
|
||||
|
||||
coords.vertexTopLeftX += xoff;
|
||||
coords.vertexTopRightX += xoff;
|
||||
coords.vertexBottomLeftX += xoff;
|
||||
coords.vertexBottomRightX += xoff;
|
||||
coords.vertexTopLeftX += xoff;
|
||||
coords.vertexTopRightX += xoff;
|
||||
coords.vertexBottomLeftX += xoff;
|
||||
coords.vertexBottomRightX += xoff;
|
||||
|
||||
coords.vertexTopLeftY += yoff;
|
||||
coords.vertexTopRightY += yoff;
|
||||
coords.vertexBottomLeftY += yoff;
|
||||
coords.vertexBottomRightY += yoff;
|
||||
coords.vertexTopLeftY += yoff;
|
||||
coords.vertexTopRightY += yoff;
|
||||
coords.vertexBottomLeftY += yoff;
|
||||
coords.vertexBottomRightY += yoff;
|
||||
|
||||
glRotatef(rotoff, 0, 0, 1);
|
||||
glRotatef(rotoff, 0, 0, 1);
|
||||
}
|
||||
|
||||
@@ -21,21 +21,21 @@
|
||||
#ifndef SHAKEEFFECT_H
|
||||
#define SHAKEEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
#define RANDOM_VAL_SIZE 30
|
||||
|
||||
class ShakeEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShakeEffect(Clip* c, const EffectMeta* em);
|
||||
void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
|
||||
EffectField* intensity_val;
|
||||
EffectField* rotation_val;
|
||||
EffectField* frequency_val;
|
||||
DoubleField* intensity_val;
|
||||
DoubleField* rotation_val;
|
||||
DoubleField* frequency_val;
|
||||
private:
|
||||
double random_vals[RANDOM_VAL_SIZE];
|
||||
double random_vals[RANDOM_VAL_SIZE];
|
||||
};
|
||||
|
||||
#endif // SHAKEEFFECT_H
|
||||
|
||||
+161
-148
@@ -27,171 +27,184 @@
|
||||
#include <QImage>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
#define SOLID_TYPE_COLOR 0
|
||||
#define SOLID_TYPE_BARS 1
|
||||
#define SOLID_TYPE_CHECKERBOARD 2
|
||||
const int SMPTE_BARS = 7;
|
||||
const int SMPTE_STRIP_COUNT = 3;
|
||||
const int SMPTE_LOWER_BARS = 4;
|
||||
|
||||
#define SMPTE_BARS 7
|
||||
#define SMPTE_STRIP_COUNT 3
|
||||
#define SMPTE_LOWER_BARS 4
|
||||
SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) :
|
||||
Effect(c, em)
|
||||
{
|
||||
SetFlags(Effect::SuperimposeFlag);
|
||||
|
||||
SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
|
||||
enable_superimpose = true;
|
||||
// Field for solid type
|
||||
EffectRow* type_row = new EffectRow(this, tr("Type"));
|
||||
solid_type = new ComboField(type_row, "type");
|
||||
solid_type->AddItem(tr("Solid Color"), SOLID_TYPE_COLOR);
|
||||
solid_type->AddItem(tr("SMPTE Bars"), SOLID_TYPE_BARS);
|
||||
solid_type->AddItem(tr("Checkerboard"), SOLID_TYPE_CHECKERBOARD);
|
||||
|
||||
solid_type = add_row(tr("Type"))->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
solid_type->add_combo_item(tr("Solid Color"), SOLID_TYPE_COLOR);
|
||||
solid_type->add_combo_item(tr("SMPTE Bars"), SOLID_TYPE_BARS);
|
||||
solid_type->add_combo_item(tr("Checkerboard"), SOLID_TYPE_CHECKERBOARD);
|
||||
EffectRow* opacity_row = new EffectRow(this, tr("Opacity"));
|
||||
opacity_field = new DoubleField(opacity_row, "opacity");
|
||||
opacity_field->SetMinimum(0);
|
||||
opacity_field->SetDefault(100);
|
||||
opacity_field->SetMaximum(100);
|
||||
|
||||
opacity_field = add_row(tr("Opacity"))->add_field(EFFECT_FIELD_DOUBLE, "opacity");
|
||||
opacity_field->set_double_minimum_value(0);
|
||||
opacity_field->set_double_maximum_value(100);
|
||||
opacity_field->set_double_default_value(100);
|
||||
EffectRow* solid_color_row = new EffectRow(this, tr("Color"));
|
||||
solid_color_field = new ColorField(solid_color_row, "color");
|
||||
solid_color_field->SetValueAt(0, QColor(Qt::red));
|
||||
|
||||
solid_color_field = add_row(tr("Color"))->add_field(EFFECT_FIELD_COLOR, "color");
|
||||
solid_color_field->set_color_value(Qt::red);
|
||||
EffectRow* checkerboard_size = new EffectRow(this, tr("Checkerboard Size"));
|
||||
checkerboard_size_field = new DoubleField(checkerboard_size, "checker_size");
|
||||
checkerboard_size_field->SetMinimum(1);
|
||||
checkerboard_size_field->SetDefault(10);
|
||||
|
||||
checkerboard_size_field = add_row(tr("Checkerboard Size"))->add_field(EFFECT_FIELD_DOUBLE, "checker_size");
|
||||
checkerboard_size_field->set_double_minimum_value(1);
|
||||
checkerboard_size_field->set_double_default_value(10);
|
||||
connect(solid_type, SIGNAL(DataChanged(const QVariant&)), this, SLOT(ui_update(const QVariant&)));
|
||||
|
||||
// hacky but eh
|
||||
QComboBox* solid_type_combo = static_cast<QComboBox*>(solid_type->get_ui_element());
|
||||
connect(solid_type_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(ui_update(int)));
|
||||
ui_update(solid_type_combo->currentIndex());
|
||||
// Set default UI
|
||||
solid_type->SetValueAt(0, SOLID_TYPE_COLOR);
|
||||
|
||||
/*vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/solideffect.frag";*/
|
||||
// TODO necessary? Isn't this called from the connect() above?
|
||||
ui_update(SOLID_TYPE_COLOR);
|
||||
|
||||
/*vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/solideffect.frag";*/
|
||||
}
|
||||
|
||||
void SolidEffect::redraw(double timecode) {
|
||||
int w = img.width();
|
||||
int h = img.height();
|
||||
int alpha = qRound(opacity_field->get_double_value(timecode)*2.55);
|
||||
switch (solid_type->get_combo_data(timecode).toInt()) {
|
||||
case SOLID_TYPE_COLOR:
|
||||
{
|
||||
QColor solidColor = solid_color_field->get_color_value(timecode);
|
||||
solidColor.setAlpha(alpha);
|
||||
img.fill(solidColor);
|
||||
}
|
||||
break;
|
||||
case SOLID_TYPE_BARS:
|
||||
{
|
||||
// draw smpte bars
|
||||
QPainter p(&img);
|
||||
img.fill(Qt::transparent);
|
||||
int bar_width = qCeil((double) w / 7.0);
|
||||
int first_bar_height = qCeil((double) h / 3.0 * 2.0);
|
||||
int second_bar_height = qCeil((double) h / 12.5);
|
||||
int third_bar_y = first_bar_height + second_bar_height;
|
||||
int third_bar_height = h - third_bar_y;
|
||||
int third_bar_width = 0;
|
||||
int bar_x, strip_width;
|
||||
QColor first_color, second_color, third_color;
|
||||
for (int i=0;i<SMPTE_BARS;i++) {
|
||||
bar_x = bar_width*i;
|
||||
switch (i) {
|
||||
case 0:
|
||||
first_color = QColor(192, 192, 192);
|
||||
second_color = QColor(0, 0, 192);
|
||||
break;
|
||||
case 1:
|
||||
first_color = QColor(192, 192, 0);
|
||||
second_color = QColor(19, 19, 19);
|
||||
break;
|
||||
case 2:
|
||||
first_color = QColor(0, 192, 192);
|
||||
second_color = QColor(192, 0, 192);
|
||||
break;
|
||||
case 3:
|
||||
first_color = QColor(0, 192, 0);
|
||||
second_color = QColor(19, 19, 19);
|
||||
break;
|
||||
case 4:
|
||||
first_color = QColor(192, 0, 192);
|
||||
second_color = QColor(0, 192, 192);
|
||||
break;
|
||||
case 5:
|
||||
third_bar_width = qRound((double) bar_x / (double) SMPTE_LOWER_BARS);
|
||||
int w = img.width();
|
||||
int h = img.height();
|
||||
int alpha = qRound(opacity_field->GetDoubleAt(timecode)*2.55);
|
||||
switch (solid_type->GetValueAt(timecode).toInt()) {
|
||||
case SOLID_TYPE_COLOR:
|
||||
{
|
||||
QColor solidColor = solid_color_field->GetColorAt(timecode);
|
||||
solidColor.setAlpha(alpha);
|
||||
img.fill(solidColor);
|
||||
}
|
||||
break;
|
||||
case SOLID_TYPE_BARS:
|
||||
{
|
||||
// draw smpte bars
|
||||
QPainter p(&img);
|
||||
img.fill(Qt::transparent);
|
||||
int bar_width = qCeil(double(w) / 7.0);
|
||||
int first_bar_height = qCeil(double(h) / 3.0 * 2.0);
|
||||
int second_bar_height = qCeil(double(h) / 12.5);
|
||||
int third_bar_y = first_bar_height + second_bar_height;
|
||||
int third_bar_height = h - third_bar_y;
|
||||
int third_bar_width = 0;
|
||||
int bar_x, strip_width;
|
||||
QColor first_color, second_color, third_color;
|
||||
for (int i=0;i<SMPTE_BARS;i++) {
|
||||
bar_x = bar_width*i;
|
||||
switch (i) {
|
||||
case 0:
|
||||
first_color = QColor(192, 192, 192);
|
||||
second_color = QColor(0, 0, 192);
|
||||
break;
|
||||
case 1:
|
||||
first_color = QColor(192, 192, 0);
|
||||
second_color = QColor(19, 19, 19);
|
||||
break;
|
||||
case 2:
|
||||
first_color = QColor(0, 192, 192);
|
||||
second_color = QColor(192, 0, 192);
|
||||
break;
|
||||
case 3:
|
||||
first_color = QColor(0, 192, 0);
|
||||
second_color = QColor(19, 19, 19);
|
||||
break;
|
||||
case 4:
|
||||
first_color = QColor(192, 0, 192);
|
||||
second_color = QColor(0, 192, 192);
|
||||
break;
|
||||
case 5:
|
||||
third_bar_width = qRound(double(bar_x) / double(SMPTE_LOWER_BARS));
|
||||
|
||||
first_color = QColor(192, 0, 0);
|
||||
second_color = QColor(19, 19, 19);
|
||||
first_color = QColor(192, 0, 0);
|
||||
second_color = QColor(19, 19, 19);
|
||||
|
||||
strip_width = qCeil(bar_width/SMPTE_STRIP_COUNT);
|
||||
for (int j=0;j<SMPTE_STRIP_COUNT;j++) {
|
||||
switch (j) {
|
||||
case 0:
|
||||
third_color = QColor(29, 29, 29, alpha);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, bar_width, third_bar_height), third_color);
|
||||
break;
|
||||
case 1:
|
||||
third_color = QColor(19, 19, 19, alpha);
|
||||
p.fillRect(QRect(bar_x + strip_width, third_bar_y, strip_width, third_bar_height), third_color);
|
||||
break;
|
||||
case 2:
|
||||
third_color = QColor(9, 9, 9, alpha);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, strip_width, third_bar_height), third_color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
first_color = QColor(0, 0, 192);
|
||||
second_color = QColor(192, 192, 192);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, bar_width, third_bar_height), QColor(19, 19, 19, alpha));
|
||||
break;
|
||||
}
|
||||
first_color.setAlpha(alpha);
|
||||
second_color.setAlpha(alpha);
|
||||
p.fillRect(QRect(bar_x, 0, bar_width, first_bar_height), first_color);
|
||||
p.fillRect(QRect(bar_x, first_bar_height, bar_width, second_bar_height), second_color);
|
||||
}
|
||||
for (int i=0;i<SMPTE_LOWER_BARS;i++) {
|
||||
bar_x = third_bar_width*i;
|
||||
switch (i) {
|
||||
case 0: third_color = QColor(0, 33, 76); break;
|
||||
case 1: third_color = QColor(255, 255, 255); break;
|
||||
case 2: third_color = QColor(50, 0, 106); break;
|
||||
case 3: third_color = QColor(19, 19, 19); break;
|
||||
}
|
||||
third_color.setAlpha(alpha);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, third_bar_width, third_bar_height), third_color);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOLID_TYPE_CHECKERBOARD:
|
||||
{
|
||||
// draw checkboard
|
||||
QPainter p(&img);
|
||||
img.fill(Qt::transparent);
|
||||
strip_width = qCeil(bar_width/SMPTE_STRIP_COUNT);
|
||||
for (int j=0;j<SMPTE_STRIP_COUNT;j++) {
|
||||
switch (j) {
|
||||
case 0:
|
||||
third_color = QColor(29, 29, 29, alpha);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, bar_width, third_bar_height), third_color);
|
||||
break;
|
||||
case 1:
|
||||
third_color = QColor(19, 19, 19, alpha);
|
||||
p.fillRect(QRect(bar_x + strip_width, third_bar_y, strip_width, third_bar_height), third_color);
|
||||
break;
|
||||
case 2:
|
||||
third_color = QColor(9, 9, 9, alpha);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, strip_width, third_bar_height), third_color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
first_color = QColor(0, 0, 192);
|
||||
second_color = QColor(192, 192, 192);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, bar_width, third_bar_height), QColor(19, 19, 19, alpha));
|
||||
break;
|
||||
}
|
||||
first_color.setAlpha(alpha);
|
||||
second_color.setAlpha(alpha);
|
||||
p.fillRect(QRect(bar_x, 0, bar_width, first_bar_height), first_color);
|
||||
p.fillRect(QRect(bar_x, first_bar_height, bar_width, second_bar_height), second_color);
|
||||
}
|
||||
for (int i=0;i<SMPTE_LOWER_BARS;i++) {
|
||||
bar_x = third_bar_width*i;
|
||||
switch (i) {
|
||||
case 0: third_color = QColor(0, 33, 76); break;
|
||||
case 1: third_color = QColor(255, 255, 255); break;
|
||||
case 2: third_color = QColor(50, 0, 106); break;
|
||||
case 3: third_color = QColor(19, 19, 19); break;
|
||||
}
|
||||
third_color.setAlpha(alpha);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, third_bar_width, third_bar_height), third_color);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOLID_TYPE_CHECKERBOARD:
|
||||
{
|
||||
// draw checkboard
|
||||
QPainter p(&img);
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
int checker_width = qCeil(checkerboard_size_field->get_double_value(timecode));
|
||||
int checker_x, checker_y;
|
||||
int checkerboard_size_w = qCeil(double(w)/checker_width);
|
||||
int checkerboard_size_h = qCeil(double(h)/checker_width);
|
||||
int checker_width = qCeil(checkerboard_size_field->GetDoubleAt(timecode));
|
||||
int checker_x, checker_y;
|
||||
int checkerboard_size_w = qCeil(double(w)/checker_width);
|
||||
int checkerboard_size_h = qCeil(double(h)/checker_width);
|
||||
|
||||
QColor checker_odd(QColor(0, 0, 0, alpha));
|
||||
QColor checker_even(solid_color_field->get_color_value(timecode));
|
||||
checker_even.setAlpha(alpha);
|
||||
QVector<QColor> checker_color{checker_odd, checker_even};
|
||||
QColor checker_odd(QColor(0, 0, 0, alpha));
|
||||
QColor checker_even(solid_color_field->GetColorAt(timecode));
|
||||
checker_even.setAlpha(alpha);
|
||||
QVector<QColor> checker_color{checker_odd, checker_even};
|
||||
|
||||
for(int i = 0; i < checkerboard_size_w; i++){
|
||||
checker_x = checker_width*i;
|
||||
for(int j = 0; j < checkerboard_size_h; j++){
|
||||
checker_y = checker_width*j;
|
||||
p.fillRect(QRect(checker_x, checker_y, checker_width, checker_width), checker_color[(i + j)%2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
for(int i = 0; i < checkerboard_size_w; i++){
|
||||
checker_x = checker_width*i;
|
||||
for(int j = 0; j < checkerboard_size_h; j++){
|
||||
checker_y = checker_width*j;
|
||||
p.fillRect(QRect(checker_x, checker_y, checker_width, checker_width), checker_color[(i + j)%2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SolidEffect::ui_update(int i) {
|
||||
solid_color_field->set_enabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD);
|
||||
checkerboard_size_field->set_enabled(i == SOLID_TYPE_CHECKERBOARD);
|
||||
void SolidEffect::SetType(SolidEffect::SolidType type)
|
||||
{
|
||||
solid_type->SetValueAt(0, type);
|
||||
}
|
||||
|
||||
void SolidEffect::ui_update(const QVariant& d) {
|
||||
int i = d.toInt();
|
||||
|
||||
solid_color_field->SetEnabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD);
|
||||
checkerboard_size_field->SetEnabled(i == SOLID_TYPE_CHECKERBOARD);
|
||||
}
|
||||
|
||||
@@ -21,22 +21,30 @@
|
||||
#ifndef SOLIDEFFECT_H
|
||||
#define SOLIDEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
#include <QImage>
|
||||
|
||||
class SolidEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
SolidEffect(Clip* c, const EffectMeta *em);
|
||||
void redraw(double timecode);
|
||||
enum SolidType {
|
||||
SOLID_TYPE_COLOR,
|
||||
SOLID_TYPE_BARS,
|
||||
SOLID_TYPE_CHECKERBOARD
|
||||
};
|
||||
|
||||
SolidEffect(Clip* c, const EffectMeta *em);
|
||||
virtual void redraw(double timecode);
|
||||
|
||||
void SetType(SolidType type);
|
||||
private slots:
|
||||
void ui_update(int);
|
||||
void ui_update(const QVariant &d);
|
||||
private:
|
||||
EffectField* solid_type;
|
||||
EffectField* solid_color_field;
|
||||
EffectField* opacity_field;
|
||||
EffectField* checkerboard_size_field;
|
||||
ComboField* solid_type;
|
||||
ColorField* solid_color_field;
|
||||
DoubleField* opacity_field;
|
||||
DoubleField* checkerboard_size_field;
|
||||
};
|
||||
|
||||
#endif // SOLIDEFFECT_H
|
||||
|
||||
+214
-253
@@ -35,319 +35,280 @@
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "ui/comboboxex.h"
|
||||
#include "ui/colorbutton.h"
|
||||
#include "ui/fontcombobox.h"
|
||||
#include "dialogs/texteditdialog.h"
|
||||
#include "io/config.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/blur.h"
|
||||
#include "global/config.h"
|
||||
|
||||
TextEffect::TextEffect(Clip* c, const EffectMeta* em) :
|
||||
Effect(c, em)
|
||||
Effect(c, em)
|
||||
{
|
||||
enable_superimpose = true;
|
||||
SetFlags(Effect::SuperimposeFlag);
|
||||
|
||||
text_val = add_row(tr("Text"))->add_field(EFFECT_FIELD_STRING, "text", 2);
|
||||
QTextEdit* text_widget = static_cast<QTextEdit*>(text_val->ui_element);
|
||||
text_widget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(text_widget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(text_edit_menu()));
|
||||
EffectRow* text_field = new EffectRow(this, tr("Text"));
|
||||
text_val = new StringField(text_field, "text", false);
|
||||
text_val->SetColumnSpan(2);
|
||||
|
||||
set_font_combobox = add_row(tr("Font"))->add_field(EFFECT_FIELD_FONT, "font", 2);
|
||||
EffectRow* font_row = new EffectRow(this, tr("Font"));
|
||||
set_font_combobox = new FontField(font_row, "font");
|
||||
set_font_combobox->SetColumnSpan(2);
|
||||
|
||||
size_val = add_row(tr("Size"))->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
EffectRow* size_row = new EffectRow(this, tr("Size"));
|
||||
size_val = new DoubleField(size_row, "size");
|
||||
size_val->SetMinimum(0);
|
||||
size_val->SetColumnSpan(2);
|
||||
|
||||
set_color_button = add_row(tr("Color"))->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
EffectRow* color_row = new EffectRow(this, tr("Color"));
|
||||
set_color_button = new ColorField(color_row, "color");
|
||||
set_color_button->SetColumnSpan(2);
|
||||
|
||||
EffectRow* alignment_row = add_row(tr("Alignment"));
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign");
|
||||
halign_field->add_combo_item(tr("Left"), Qt::AlignLeft);
|
||||
halign_field->add_combo_item(tr("Center"), Qt::AlignHCenter);
|
||||
halign_field->add_combo_item(tr("Right"), Qt::AlignRight);
|
||||
halign_field->add_combo_item(tr("Justify"), Qt::AlignJustify);
|
||||
EffectRow* alignment_row = new EffectRow(this, tr("Alignment"));
|
||||
halign_field = new ComboField(alignment_row, "halign");
|
||||
halign_field->AddItem(tr("Left"), Qt::AlignLeft);
|
||||
halign_field->AddItem(tr("Center"), Qt::AlignHCenter);
|
||||
halign_field->AddItem(tr("Right"), Qt::AlignRight);
|
||||
halign_field->AddItem(tr("Justify"), Qt::AlignJustify);
|
||||
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign");
|
||||
valign_field->add_combo_item(tr("Top"), Qt::AlignTop);
|
||||
valign_field->add_combo_item(tr("Center"), Qt::AlignVCenter);
|
||||
valign_field->add_combo_item(tr("Bottom"), Qt::AlignBottom);
|
||||
valign_field = new ComboField(alignment_row, "valign");
|
||||
valign_field->AddItem(tr("Top"), Qt::AlignTop);
|
||||
valign_field->AddItem(tr("Center"), Qt::AlignVCenter);
|
||||
valign_field->AddItem(tr("Bottom"), Qt::AlignBottom);
|
||||
|
||||
word_wrap_field = add_row(tr("Word Wrap"))->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
|
||||
EffectRow* word_wrap_row = new EffectRow(this, tr("Word Wrap"));
|
||||
word_wrap_field = new BoolField(word_wrap_row, "wordwrap");
|
||||
word_wrap_field->SetColumnSpan(2);
|
||||
|
||||
outline_bool = add_row(tr("Outline"))->add_field(EFFECT_FIELD_BOOL, "outline", 2);
|
||||
outline_color = add_row(tr("Outline Color"))->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
|
||||
outline_width = add_row(tr("Outline Width"))->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
|
||||
outline_width->set_double_minimum_value(0);
|
||||
EffectRow* padding_row = new EffectRow(this, tr("Padding"));
|
||||
padding_field = new DoubleField(padding_row, "padding");
|
||||
padding_field->SetColumnSpan(2);
|
||||
|
||||
shadow_bool = add_row(tr("Shadow"))->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
|
||||
shadow_color = add_row(tr("Shadow Color"))->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
|
||||
shadow_angle = add_row(tr("Shadow Angle"))->add_field(EFFECT_FIELD_DOUBLE, "shadowangle", 2);
|
||||
shadow_distance = add_row(tr("Shadow Distance"))->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
|
||||
shadow_distance->set_double_minimum_value(0);
|
||||
shadow_softness = add_row(tr("Shadow Softness"))->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
|
||||
shadow_softness->set_double_minimum_value(0);
|
||||
shadow_opacity = add_row(tr("Shadow Opacity"))->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
|
||||
shadow_opacity->set_double_minimum_value(0);
|
||||
shadow_opacity->set_double_maximum_value(100);
|
||||
EffectRow* position_row = new EffectRow(this, tr("Position"));
|
||||
position_x = new DoubleField(position_row, "posx");
|
||||
position_y = new DoubleField(position_row, "posy");
|
||||
|
||||
size_val->set_double_default_value(48);
|
||||
text_val->set_string_value(tr("Sample Text"));
|
||||
halign_field->set_combo_index(1);
|
||||
valign_field->set_combo_index(1);
|
||||
word_wrap_field->set_bool_value(true);
|
||||
outline_color->set_color_value(Qt::black);
|
||||
shadow_color->set_color_value(Qt::black);
|
||||
shadow_angle->set_double_default_value(45);
|
||||
shadow_opacity->set_double_default_value(100);
|
||||
shadow_softness->set_double_default_value(5);
|
||||
shadow_distance->set_double_default_value(5);
|
||||
shadow_opacity->set_double_default_value(80);
|
||||
outline_width->set_double_default_value(20);
|
||||
EffectRow* outline_row = new EffectRow(this, tr("Outline"));
|
||||
outline_bool = new BoolField(outline_row, "outline");
|
||||
outline_bool->SetColumnSpan(2);
|
||||
|
||||
outline_enable(false);
|
||||
shadow_enable(false);
|
||||
EffectRow* outline_color_row = new EffectRow(this, tr("Outline Color"));
|
||||
outline_color = new ColorField(outline_color_row, "outlinecolor");
|
||||
outline_color->SetColumnSpan(2);
|
||||
|
||||
connect(shadow_bool, SIGNAL(toggled(bool)), this, SLOT(shadow_enable(bool)));
|
||||
connect(outline_bool, SIGNAL(toggled(bool)), this, SLOT(outline_enable(bool)));
|
||||
EffectRow* outline_width_row = new EffectRow(this, tr("Outline Width"));
|
||||
outline_width = new DoubleField(outline_width_row, "outlinewidth");
|
||||
outline_width->SetColumnSpan(2);
|
||||
outline_width->SetMinimum(0);
|
||||
|
||||
vertPath = "common.vert";
|
||||
fragPath = "dropshadow.frag";
|
||||
}
|
||||
EffectRow* shadow_row = new EffectRow(this, tr("Shadow"));
|
||||
shadow_bool = new BoolField(shadow_row, "shadow");
|
||||
shadow_bool->SetColumnSpan(2);
|
||||
|
||||
void blurred2(QImage& result, const QRect& rect, int radius, bool alphaOnly = false) {
|
||||
int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
|
||||
int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1];
|
||||
EffectRow* shadow_color_row = new EffectRow(this, tr("Shadow Color"));
|
||||
shadow_color = new ColorField(shadow_color_row, "shadowcolor");
|
||||
shadow_color->SetColumnSpan(2);
|
||||
|
||||
int r1 = rect.top();
|
||||
int r2 = rect.bottom();
|
||||
int c1 = rect.left();
|
||||
int c2 = rect.right();
|
||||
EffectRow* shadow_angle_row = new EffectRow(this, tr("Shadow Angle"));
|
||||
shadow_angle = new DoubleField(shadow_angle_row, "shadowangle");
|
||||
shadow_angle->SetColumnSpan(2);
|
||||
|
||||
int bpl = result.bytesPerLine();
|
||||
int rgba[4];
|
||||
unsigned char* p;
|
||||
EffectRow* shadow_distance_row = new EffectRow(this, tr("Shadow Distance"));
|
||||
shadow_distance = new DoubleField(shadow_distance_row, "shadowdistance");
|
||||
shadow_distance->SetColumnSpan(2);
|
||||
shadow_distance->SetMinimum(0);
|
||||
|
||||
int i1 = 0;
|
||||
int i2 = 3;
|
||||
EffectRow* shadow_softness_row = new EffectRow(this, tr("Shadow Softness"));
|
||||
shadow_softness = new DoubleField(shadow_softness_row, "shadowsoftness");
|
||||
shadow_softness->SetColumnSpan(2);
|
||||
shadow_softness->SetMinimum(0);
|
||||
|
||||
if (alphaOnly)
|
||||
i1 = i2 = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
|
||||
EffectRow* shadow_opacity_row = new EffectRow(this, tr("Shadow Opacity"));
|
||||
shadow_opacity = new DoubleField(shadow_opacity_row, "shadowopacity");
|
||||
shadow_opacity->SetColumnSpan(2);
|
||||
shadow_opacity->SetMinimum(0);
|
||||
shadow_opacity->SetMaximum(100);
|
||||
|
||||
for (int col = c1; col <= c2; col++) {
|
||||
p = result.scanLine(r1) + col * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
size_val->SetDefault(48);
|
||||
text_val->SetValueAt(0, tr("Sample Text"));
|
||||
set_color_button->SetValueAt(0, QColor(Qt::white));
|
||||
halign_field->SetValueAt(0, Qt::AlignHCenter);
|
||||
valign_field->SetValueAt(0, Qt::AlignVCenter);
|
||||
word_wrap_field->SetValueAt(0, true);
|
||||
outline_color->SetValueAt(0, QColor(Qt::black));
|
||||
shadow_color->SetValueAt(0, QColor(Qt::black));
|
||||
shadow_angle->SetDefault(45);
|
||||
shadow_opacity->SetDefault(100);
|
||||
shadow_softness->SetDefault(5);
|
||||
shadow_distance->SetDefault(5);
|
||||
shadow_opacity->SetDefault(80);
|
||||
outline_width->SetDefault(20);
|
||||
|
||||
p += bpl;
|
||||
for (int j = r1; j < r2; j++, p += bpl)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
outline_enable(false);
|
||||
shadow_enable(false);
|
||||
|
||||
for (int row = r1; row <= r2; row++) {
|
||||
p = result.scanLine(row) + c1 * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
connect(shadow_bool, SIGNAL(Toggled(bool)), this, SLOT(shadow_enable(bool)));
|
||||
connect(outline_bool, SIGNAL(Toggled(bool)), this, SLOT(outline_enable(bool)));
|
||||
|
||||
p += 4;
|
||||
for (int j = c1; j < c2; j++, p += 4)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int col = c1; col <= c2; col++) {
|
||||
p = result.scanLine(r2) + col * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p -= bpl;
|
||||
for (int j = r1; j < r2; j++, p -= bpl)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int row = r1; row <= r2; row++) {
|
||||
p = result.scanLine(row) + c2 * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p -= 4;
|
||||
for (int j = c1; j < c2; j++, p -= 4)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
vertPath = "common.vert";
|
||||
fragPath = "dropshadow.frag";
|
||||
}
|
||||
|
||||
void TextEffect::redraw(double timecode) {
|
||||
QColor bkg = set_color_button->get_color_value(timecode);
|
||||
bkg.setAlpha(0);
|
||||
img.fill(bkg);
|
||||
QColor bkg = set_color_button->GetColorAt(timecode);
|
||||
bkg.setAlpha(0);
|
||||
img.fill(bkg);
|
||||
|
||||
QPainter p(&img);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
int width = img.width();
|
||||
int height = img.height();
|
||||
QPainter p(&img);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
int padding = qRound(padding_field->GetDoubleAt(timecode));
|
||||
int width = img.width() - padding * 2;
|
||||
int height = img.height() - padding * 2;
|
||||
|
||||
// set font
|
||||
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
|
||||
font.setFamily(set_font_combobox->get_font_name(timecode));
|
||||
font.setPointSize(size_val->get_double_value(timecode));
|
||||
p.setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
// set font
|
||||
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
|
||||
font.setFamily(set_font_combobox->GetFontAt(timecode));
|
||||
font.setPointSize(qRound(size_val->GetDoubleAt(timecode)));
|
||||
p.setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
|
||||
QStringList lines = text_val->get_string_value(timecode).split('\n');
|
||||
QStringList lines = text_val->GetStringAt(timecode).split('\n');
|
||||
|
||||
// word wrap function
|
||||
if (word_wrap_field->get_bool_value(timecode)) {
|
||||
for (int i=0;i<lines.size();i++) {
|
||||
QString s(lines.at(i));
|
||||
if (fm.width(s) > width) {
|
||||
int last_space_index = 0;
|
||||
for (int j=0;j<s.length();j++) {
|
||||
if (s.at(j) == ' ') {
|
||||
if (fm.width(s.left(j)) > width) {
|
||||
break;
|
||||
} else {
|
||||
last_space_index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_space_index > 0) {
|
||||
lines.insert(i+1, s.mid(last_space_index + 1));
|
||||
lines[i] = s.left(last_space_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// word wrap function
|
||||
if (word_wrap_field->GetBoolAt(timecode)) {
|
||||
for (int i=0;i<lines.size();i++) {
|
||||
QString s(lines.at(i));
|
||||
if (fm.width(s) > width) {
|
||||
int last_space_index = 0;
|
||||
for (int j=0;j<s.length();j++) {
|
||||
if (s.at(j) == ' ') {
|
||||
if (fm.width(s.left(j)) > width) {
|
||||
break;
|
||||
} else {
|
||||
last_space_index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_space_index > 0) {
|
||||
lines.insert(i+1, s.mid(last_space_index + 1));
|
||||
lines[i] = s.left(last_space_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPainterPath path;
|
||||
QPainterPath path;
|
||||
|
||||
int text_height = fm.height()*lines.size();
|
||||
int text_height = fm.height()*lines.size();
|
||||
|
||||
for (int i=0;i<lines.size();i++) {
|
||||
int text_x, text_y;
|
||||
for (int i=0;i<lines.size();i++) {
|
||||
int text_x, text_y;
|
||||
|
||||
switch (halign_field->get_combo_data(timecode).toInt()) {
|
||||
case Qt::AlignLeft: text_x = 0; break;
|
||||
case Qt::AlignRight: text_x = width - fm.width(lines.at(i)); break;
|
||||
case Qt::AlignJustify:
|
||||
// add spaces until the string is too big
|
||||
text_x = 0;
|
||||
while (fm.width(lines.at(i)) < width) {
|
||||
bool space = false;
|
||||
QString spaced(lines.at(i));
|
||||
for (int i=0;i<spaced.length();i++) {
|
||||
if (spaced.at(i) == ' ') {
|
||||
// insert a space
|
||||
spaced.insert(i, ' ');
|
||||
space = true;
|
||||
switch (halign_field->GetValueAt(timecode).toInt()) {
|
||||
case Qt::AlignLeft: text_x = 0; break;
|
||||
case Qt::AlignRight: text_x = width - fm.width(lines.at(i)); break;
|
||||
case Qt::AlignJustify:
|
||||
// add spaces until the string is too big
|
||||
text_x = 0;
|
||||
while (fm.width(lines.at(i)) < width) {
|
||||
bool space = false;
|
||||
QString spaced(lines.at(i));
|
||||
for (int i=0;i<spaced.length();i++) {
|
||||
if (spaced.at(i) == ' ') {
|
||||
// insert a space
|
||||
spaced.insert(i, ' ');
|
||||
space = true;
|
||||
|
||||
// scan to next non-space
|
||||
while (i < spaced.length() && spaced.at(i) == ' ') i++;
|
||||
}
|
||||
}
|
||||
if (fm.width(spaced) > width || !space) {
|
||||
break;
|
||||
} else {
|
||||
lines[i] = spaced;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::AlignHCenter:
|
||||
default:
|
||||
text_x = (width/2) - (fm.width(lines.at(i))/2);
|
||||
break;
|
||||
}
|
||||
// scan to next non-space
|
||||
while (i < spaced.length() && spaced.at(i) == ' ') i++;
|
||||
}
|
||||
}
|
||||
if (fm.width(spaced) > width || !space) {
|
||||
break;
|
||||
} else {
|
||||
lines[i] = spaced;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::AlignHCenter:
|
||||
default:
|
||||
text_x = (width/2) - (fm.width(lines.at(i))/2);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (valign_field->get_combo_data(timecode).toInt()) {
|
||||
case Qt::AlignTop:
|
||||
text_y = (fm.height()*i)+fm.ascent();
|
||||
break;
|
||||
case Qt::AlignBottom:
|
||||
text_y = (height - text_height - fm.descent()) + (fm.height()*(i+1));
|
||||
break;
|
||||
case Qt::AlignVCenter:
|
||||
default:
|
||||
text_y = ((height/2) - (text_height/2) - fm.descent()) + (fm.height()*(i+1));
|
||||
break;
|
||||
}
|
||||
switch (valign_field->GetValueAt(timecode).toInt()) {
|
||||
case Qt::AlignTop:
|
||||
text_y = (fm.height()*i)+fm.ascent();
|
||||
break;
|
||||
case Qt::AlignBottom:
|
||||
text_y = (height - text_height - fm.descent()) + (fm.height()*(i+1));
|
||||
break;
|
||||
case Qt::AlignVCenter:
|
||||
default:
|
||||
text_y = ((height/2) - (text_height/2) - fm.descent()) + (fm.height()*(i+1));
|
||||
break;
|
||||
}
|
||||
|
||||
path.addText(text_x, text_y, font, lines.at(i));
|
||||
}
|
||||
path.addText(text_x, text_y, font, lines.at(i));
|
||||
}
|
||||
|
||||
// draw software shadow
|
||||
if (shadow_bool->get_bool_value(timecode)) {
|
||||
p.setPen(Qt::NoPen);
|
||||
path.translate(position_x->GetDoubleAt(timecode) + padding, position_y->GetDoubleAt(timecode) + padding);
|
||||
|
||||
// draw software shadow
|
||||
if (shadow_bool->GetBoolAt(timecode)) {
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
// calculate offset using distance and angle
|
||||
double angle = shadow_angle->get_double_value(timecode) * M_PI / 180.0;
|
||||
double distance = qFloor(shadow_distance->get_double_value(timecode));
|
||||
double angle = shadow_angle->GetDoubleAt(timecode) * M_PI / 180.0;
|
||||
double distance = qFloor(shadow_distance->GetDoubleAt(timecode));
|
||||
int shadow_x_offset = qRound(qCos(angle) * distance);
|
||||
int shadow_y_offset = qRound(qSin(angle) * distance);
|
||||
|
||||
QPainterPath shadow_path(path);
|
||||
QPainterPath shadow_path(path);
|
||||
shadow_path.translate(shadow_x_offset, shadow_y_offset);
|
||||
|
||||
QColor col = shadow_color->get_color_value(timecode);
|
||||
col.setAlpha(0);
|
||||
img.fill(col);
|
||||
QColor col = shadow_color->GetColorAt(timecode);
|
||||
col.setAlpha(0);
|
||||
img.fill(col);
|
||||
|
||||
col.setAlphaF(shadow_opacity->get_double_value(timecode)*0.01);
|
||||
p.setBrush(col);
|
||||
p.drawPath(shadow_path);
|
||||
col.setAlphaF(shadow_opacity->GetDoubleAt(timecode)*0.01);
|
||||
p.setBrush(col);
|
||||
p.drawPath(shadow_path);
|
||||
|
||||
int blurSoftness = qFloor(shadow_softness->get_double_value(timecode));
|
||||
if (blurSoftness > 0) blurred2(img, img.rect(), blurSoftness, true);
|
||||
}
|
||||
int blurSoftness = qFloor(shadow_softness->GetDoubleAt(timecode));
|
||||
if (blurSoftness > 0) olive::ui::blur(img, img.rect(), blurSoftness, true);
|
||||
}
|
||||
|
||||
// draw outline
|
||||
int outline_width_val = outline_width->get_double_value(timecode);
|
||||
if (outline_bool->get_bool_value(timecode) && outline_width_val > 0) {
|
||||
QPen outline(outline_color->get_color_value(timecode));
|
||||
outline.setWidth(outline_width_val);
|
||||
p.setPen(outline);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawPath(path);
|
||||
}
|
||||
// draw outline
|
||||
int outline_width_val = qCeil(outline_width->GetDoubleAt(timecode));
|
||||
if (outline_bool->GetBoolAt(timecode) && outline_width_val > 0) {
|
||||
QPen outline(outline_color->GetColorAt(timecode));
|
||||
outline.setWidth(outline_width_val);
|
||||
p.setPen(outline);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawPath(path);
|
||||
}
|
||||
|
||||
// draw "master" text
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(set_color_button->get_color_value(timecode));
|
||||
p.drawPath(path);
|
||||
// draw "master" text
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(set_color_button->GetColorAt(timecode));
|
||||
p.drawPath(path);
|
||||
|
||||
p.end();
|
||||
p.end();
|
||||
}
|
||||
|
||||
void TextEffect::shadow_enable(bool e) {
|
||||
close();
|
||||
close();
|
||||
|
||||
shadow_color->set_enabled(e);
|
||||
shadow_angle->set_enabled(e);
|
||||
shadow_distance->set_enabled(e);
|
||||
shadow_softness->set_enabled(e);
|
||||
shadow_opacity->set_enabled(e);
|
||||
}
|
||||
|
||||
void TextEffect::text_edit_menu() {
|
||||
QMenu menu;
|
||||
|
||||
menu.addAction(tr("&Edit Text"), this, SLOT(open_text_edit()));
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void TextEffect::open_text_edit() {
|
||||
TextEditDialog ted(olive::MainWindow, text_val->get_current_data().toString());
|
||||
ted.exec();
|
||||
QString result = ted.get_string();
|
||||
if (!result.isEmpty()) {
|
||||
text_val->set_current_data(result);
|
||||
text_val->ui_element_change();
|
||||
}
|
||||
shadow_color->SetEnabled(e);
|
||||
shadow_angle->SetEnabled(e);
|
||||
shadow_distance->SetEnabled(e);
|
||||
shadow_softness->SetEnabled(e);
|
||||
shadow_opacity->SetEnabled(e);
|
||||
}
|
||||
|
||||
void TextEffect::outline_enable(bool e) {
|
||||
outline_color->set_enabled(e);
|
||||
outline_width->set_enabled(e);
|
||||
outline_color->SetEnabled(e);
|
||||
outline_width->SetEnabled(e);
|
||||
}
|
||||
|
||||
@@ -21,42 +21,44 @@
|
||||
#ifndef TEXTEFFECT_H
|
||||
#define TEXTEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
#include <QFont>
|
||||
#include <QImage>
|
||||
|
||||
class TextEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextEffect(Clip* c, const EffectMeta *em);
|
||||
void redraw(double timecode);
|
||||
|
||||
EffectField* text_val;
|
||||
EffectField* size_val;
|
||||
EffectField* set_color_button;
|
||||
EffectField* set_font_combobox;
|
||||
EffectField* halign_field;
|
||||
EffectField* valign_field;
|
||||
EffectField* word_wrap_field;
|
||||
|
||||
EffectField* outline_bool;
|
||||
EffectField* outline_width;
|
||||
EffectField* outline_color;
|
||||
|
||||
EffectField* shadow_bool;
|
||||
EffectField* shadow_angle;
|
||||
EffectField* shadow_distance;
|
||||
EffectField* shadow_color;
|
||||
EffectField* shadow_softness;
|
||||
EffectField* shadow_opacity;
|
||||
TextEffect(Clip* c, const EffectMeta *em);
|
||||
void redraw(double timecode);
|
||||
private slots:
|
||||
void outline_enable(bool);
|
||||
void shadow_enable(bool);
|
||||
void text_edit_menu();
|
||||
void open_text_edit();
|
||||
void outline_enable(bool);
|
||||
void shadow_enable(bool);
|
||||
private:
|
||||
QFont font;
|
||||
QFont font;
|
||||
|
||||
StringField* text_val;
|
||||
DoubleField* size_val;
|
||||
ColorField* set_color_button;
|
||||
FontField* set_font_combobox;
|
||||
ComboField* halign_field;
|
||||
ComboField* valign_field;
|
||||
BoolField* word_wrap_field;
|
||||
DoubleField* padding_field;
|
||||
DoubleField* position_x;
|
||||
DoubleField* position_y;
|
||||
|
||||
BoolField* outline_bool;
|
||||
DoubleField* outline_width;
|
||||
ColorField* outline_color;
|
||||
|
||||
BoolField* shadow_bool;
|
||||
DoubleField* shadow_angle;
|
||||
DoubleField* shadow_distance;
|
||||
ColorField* shadow_color;
|
||||
DoubleField* shadow_softness;
|
||||
DoubleField* shadow_opacity;
|
||||
|
||||
};
|
||||
|
||||
#endif // TEXTEFFECT_H
|
||||
|
||||
@@ -35,56 +35,70 @@
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/comboboxex.h"
|
||||
#include "ui/colorbutton.h"
|
||||
#include "ui/fontcombobox.h"
|
||||
#include "io/config.h"
|
||||
#include "global/config.h"
|
||||
|
||||
TimecodeEffect::TimecodeEffect(Clip* c, const EffectMeta* em) :
|
||||
Effect(c, em)
|
||||
{
|
||||
enable_always_update = true;
|
||||
enable_superimpose = true;
|
||||
SetFlags(Effect::SuperimposeFlag);
|
||||
|
||||
EffectRow* tc_row = add_row(tr("Timecode"));
|
||||
tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector");
|
||||
tc_select->add_combo_item(tr("Sequence"), true);
|
||||
tc_select->add_combo_item(tr("Media"), false);
|
||||
tc_select->set_combo_index(0);
|
||||
EffectRow* tc_row = new EffectRow(this, tr("Timecode"));
|
||||
tc_select = new ComboField(tc_row, "tc_selector");
|
||||
tc_select->AddItem(tr("Sequence"), true);
|
||||
tc_select->AddItem(tr("Media"), false);
|
||||
tc_select->SetValueAt(0, true);
|
||||
|
||||
scale_val = add_row(tr("Scale"))->add_field(EFFECT_FIELD_DOUBLE, "scale", 2);
|
||||
scale_val->set_double_minimum_value(1);
|
||||
scale_val->set_double_default_value(100);
|
||||
scale_val->set_double_maximum_value(1000);
|
||||
EffectRow* scale_row = new EffectRow(this, tr("Scale"));
|
||||
scale_val = new DoubleField(scale_row, "scale");
|
||||
scale_val->SetColumnSpan(2);
|
||||
scale_val->SetMinimum(1);
|
||||
scale_val->SetDefault(100);
|
||||
scale_val->SetMaximum(1000);
|
||||
|
||||
color_val = add_row(tr("Color"))->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
color_val->set_color_value(Qt::white);
|
||||
EffectRow* color_row = new EffectRow(this, tr("Color"));
|
||||
color_val = new ColorField(color_row, "color");
|
||||
color_val->SetColumnSpan(2);
|
||||
color_val->SetValueAt(0, QColor(Qt::white));
|
||||
|
||||
color_bg_val = add_row(tr("Background Color"))->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2);
|
||||
color_bg_val->set_color_value(Qt::black);
|
||||
EffectRow* color_bg_row = new EffectRow(this, tr("Background Color"));
|
||||
color_bg_val = new ColorField(color_bg_row, "bgcolor");
|
||||
color_bg_val->SetColumnSpan(2);
|
||||
color_bg_val->SetValueAt(0, QColor(Qt::black));
|
||||
|
||||
bg_alpha = add_row(tr("Background Opacity"))->add_field(EFFECT_FIELD_DOUBLE, "bgalpha", 2);
|
||||
bg_alpha->set_double_minimum_value(0);
|
||||
bg_alpha->set_double_maximum_value(100);
|
||||
bg_alpha->set_double_default_value(50);
|
||||
EffectRow* bg_alpha_row = new EffectRow(this, tr("Background Opacity"));
|
||||
bg_alpha = new DoubleField(bg_alpha_row, "bgalpha");
|
||||
bg_alpha->SetColumnSpan(2);
|
||||
bg_alpha->SetMinimum(0);
|
||||
bg_alpha->SetDefault(50);
|
||||
bg_alpha->SetMaximum(100);
|
||||
|
||||
EffectRow* offset = add_row(tr("Offset"));
|
||||
offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx");
|
||||
offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety");
|
||||
EffectRow* offset_row = new EffectRow(this, tr("Offset"));
|
||||
offset_x_val = new DoubleField(offset_row, "offsetx");
|
||||
offset_y_val = new DoubleField(offset_row, "offsety");
|
||||
|
||||
prepend_text = add_row(tr("Prepend"))->add_field(EFFECT_FIELD_STRING, "prepend", 2);
|
||||
EffectRow* prepent_text_row = new EffectRow(this, tr("Prepend"));
|
||||
prepend_text = new StringField(prepent_text_row, "prepend");
|
||||
prepend_text->SetColumnSpan(2);
|
||||
}
|
||||
|
||||
|
||||
void TimecodeEffect::redraw(double timecode) {
|
||||
if (tc_select->get_combo_data(timecode).toBool()){
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(olive::ActiveSequence->playhead, olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate);}
|
||||
else {
|
||||
if (tc_select->GetValueAt(timecode).toBool()) {
|
||||
display_timecode = prepend_text->GetStringAt(timecode) + frame_to_timecode(olive::ActiveSequence->playhead,
|
||||
olive::CurrentConfig.timecode_view,
|
||||
olive::ActiveSequence->frame_rate);
|
||||
} else {
|
||||
double media_rate = parent_clip->media_frame_rate();
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, olive::CurrentConfig.timecode_view, media_rate);}
|
||||
display_timecode = prepend_text->GetStringAt(timecode) + frame_to_timecode(qRound(timecode * media_rate),
|
||||
olive::CurrentConfig.timecode_view,
|
||||
media_rate);
|
||||
}
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
QPainter p(&img);
|
||||
@@ -95,7 +109,7 @@ void TimecodeEffect::redraw(double timecode) {
|
||||
// set font
|
||||
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
|
||||
font.setFamily("Helvetica");
|
||||
font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10)));
|
||||
font.setPixelSize(qCeil(scale_val->GetDoubleAt(timecode)*.01*(height/10)));
|
||||
p.setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
|
||||
@@ -104,12 +118,12 @@ void TimecodeEffect::redraw(double timecode) {
|
||||
int text_x, text_y, rect_y, offset_x, offset_y;
|
||||
int text_height = fm.height();
|
||||
int text_width = fm.width(display_timecode);
|
||||
QColor background_color = color_bg_val->get_color_value(timecode);
|
||||
int alpha_val = bg_alpha->get_double_value(timecode)*2.55;
|
||||
QColor background_color = color_bg_val->GetColorAt(timecode);
|
||||
int alpha_val = qCeil(bg_alpha->GetDoubleAt(timecode)*2.55);
|
||||
background_color.setAlpha(alpha_val);
|
||||
|
||||
offset_x = int(offset_x_val->get_double_value(timecode));
|
||||
offset_y = int(offset_y_val->get_double_value(timecode));
|
||||
offset_x = int(offset_x_val->GetDoubleAt(timecode));
|
||||
offset_y = int(offset_y_val->GetDoubleAt(timecode));
|
||||
|
||||
text_x = offset_x + (width/2) - (text_width/2);
|
||||
text_y = offset_y + height - height/10;
|
||||
@@ -120,6 +134,11 @@ void TimecodeEffect::redraw(double timecode) {
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(background_color);
|
||||
p.drawRect(QRect(text_x-fm.descent(), rect_y, text_width+fm.descent()*2, text_height));
|
||||
p.setBrush(color_val->get_color_value(timecode));
|
||||
p.setBrush(color_val->GetColorAt(timecode));
|
||||
p.drawPath(path);
|
||||
}
|
||||
|
||||
bool TimecodeEffect::AlwaysUpdate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -21,28 +21,31 @@
|
||||
#ifndef TIMECODEEFFECT_H
|
||||
#define TIMECODEEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
#include <QFont>
|
||||
#include <QImage>
|
||||
|
||||
class TimecodeEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
TimecodeEffect(Clip* c, const EffectMeta *em);
|
||||
void redraw(double timecode);
|
||||
EffectField * scale_val;
|
||||
EffectField * color_val;
|
||||
EffectField * color_bg_val;
|
||||
EffectField * bg_alpha;
|
||||
EffectField * offset_x_val;
|
||||
EffectField * offset_y_val;
|
||||
EffectField * prepend_text;
|
||||
EffectField * tc_select;
|
||||
TimecodeEffect(Clip* c, const EffectMeta *em);
|
||||
void redraw(double timecode);
|
||||
DoubleField* scale_val;
|
||||
ColorField* color_val;
|
||||
ColorField* color_bg_val;
|
||||
DoubleField* bg_alpha;
|
||||
DoubleField* offset_x_val;
|
||||
DoubleField* offset_y_val;
|
||||
StringField* prepend_text;
|
||||
ComboField* tc_select;
|
||||
|
||||
protected:
|
||||
virtual bool AlwaysUpdate() override;
|
||||
|
||||
private:
|
||||
QFont font;
|
||||
QString display_timecode;
|
||||
QFont font;
|
||||
QString display_timecode;
|
||||
};
|
||||
|
||||
#endif // TIMECODEEFFECT_H
|
||||
|
||||
@@ -24,49 +24,54 @@
|
||||
|
||||
#define TONE_TYPE_SINE 0
|
||||
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "debug.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
|
||||
ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_MIN) {
|
||||
type_val = add_row(tr("Type"))->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
type_val->add_combo_item("Sine", TONE_TYPE_SINE);
|
||||
EffectRow* type_row = new EffectRow(this, tr("Type"));
|
||||
type_val = new ComboField(type_row, "type");
|
||||
type_val->AddItem(tr("Sine"), TONE_TYPE_SINE);
|
||||
|
||||
freq_val = add_row(tr("Frequency"))->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
freq_val->set_double_minimum_value(20);
|
||||
freq_val->set_double_maximum_value(20000);
|
||||
freq_val->set_double_default_value(1000);
|
||||
EffectRow* frequency_row = new EffectRow(this, tr("Frequency"));
|
||||
freq_val = new DoubleField(frequency_row, "frequency");
|
||||
freq_val->SetMinimum(20);
|
||||
freq_val->SetMaximum(20000);
|
||||
freq_val->SetDefault(1000);
|
||||
|
||||
amount_val = add_row(tr("Amount"))->add_field(EFFECT_FIELD_DOUBLE, "amount");
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
amount_val->set_double_default_value(25);
|
||||
EffectRow* amount_row = new EffectRow(this, tr("Amount"));
|
||||
amount_val = new DoubleField(amount_row, "amount");
|
||||
amount_val->SetMinimum(0);
|
||||
amount_val->SetMaximum(100);
|
||||
amount_val->SetDefault(25);
|
||||
|
||||
mix_val = add_row(tr("Mix"))->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val->set_bool_value(true);
|
||||
EffectRow* mix_row = new EffectRow(this, tr("Mix"));
|
||||
mix_val = new BoolField(mix_row, "mix");
|
||||
mix_val->SetValueAt(0, true);
|
||||
}
|
||||
|
||||
void ToneEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) {
|
||||
double interval = (timecode_end - timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
double timecode = timecode_start+(interval*i);
|
||||
double interval = (timecode_end - timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
double timecode = timecode_start+(interval*i);
|
||||
|
||||
qint16 left_tone_sample = qint16(qRound(qSin((2*M_PI*sinX*freq_val->get_double_value(timecode, true))/parent_clip->sequence->audio_frequency)*log_volume(amount_val->get_double_value(timecode, true)*0.01)*INT16_MAX));
|
||||
qint16 right_tone_sample = left_tone_sample;
|
||||
qint16 left_tone_sample = qint16(qRound(qSin((2*M_PI*sinX*freq_val->GetDoubleAt(timecode))
|
||||
/parent_clip->sequence->audio_frequency)
|
||||
*log_volume(amount_val->GetDoubleAt(timecode)*0.01)*INT16_MAX));
|
||||
qint16 right_tone_sample = left_tone_sample;
|
||||
|
||||
// mix with source audio
|
||||
if (mix_val->get_bool_value(timecode, true)) {
|
||||
qint16 left_sample = qint16(((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
qint16 right_sample = qint16(((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF));
|
||||
left_tone_sample = mix_audio_sample(left_tone_sample, left_sample);
|
||||
right_tone_sample = mix_audio_sample(right_tone_sample, right_sample);
|
||||
}
|
||||
// mix with source audio
|
||||
if (mix_val->GetBoolAt(timecode)) {
|
||||
qint16 left_sample = qint16(((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
qint16 right_sample = qint16(((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF));
|
||||
left_tone_sample = mix_audio_sample(left_tone_sample, left_sample);
|
||||
right_tone_sample = mix_audio_sample(right_tone_sample, right_sample);
|
||||
}
|
||||
|
||||
samples[i+3] = quint8(right_tone_sample >> 8);
|
||||
samples[i+2] = quint8(right_tone_sample);
|
||||
samples[i+1] = quint8(left_tone_sample >> 8);
|
||||
samples[i] = quint8(left_tone_sample);
|
||||
samples[i+3] = quint8(right_tone_sample >> 8);
|
||||
samples[i+2] = quint8(right_tone_sample);
|
||||
samples[i+1] = quint8(left_tone_sample >> 8);
|
||||
samples[i] = quint8(left_tone_sample);
|
||||
|
||||
sinX++;
|
||||
}
|
||||
sinX++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,20 +21,20 @@
|
||||
#ifndef TONEEFFECT_H
|
||||
#define TONEEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
class ToneEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
ToneEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
ToneEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
EffectField* type_val;
|
||||
EffectField* freq_val;
|
||||
EffectField* amount_val;
|
||||
EffectField* mix_val;
|
||||
ComboField* type_val;
|
||||
DoubleField* freq_val;
|
||||
DoubleField* amount_val;
|
||||
BoolField* mix_val;
|
||||
private:
|
||||
int sinX;
|
||||
int sinX;
|
||||
};
|
||||
|
||||
#endif // TONEEFFECT_H
|
||||
|
||||
@@ -30,60 +30,63 @@
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "timeline/clip.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "project/footage.h"
|
||||
#include "io/math.h"
|
||||
#include "global/math.h"
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/comboboxex.h"
|
||||
#include "project/effectloaders.h"
|
||||
#include "panels/project.h"
|
||||
#include "debug.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
|
||||
TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
SetFlags(Effect::CoordsFlag);
|
||||
|
||||
EffectRow* position_row = add_row(tr("Position"));
|
||||
position_x = position_row->add_field(EFFECT_FIELD_DOUBLE, "posx"); // position X
|
||||
position_y = position_row->add_field(EFFECT_FIELD_DOUBLE, "posy"); // position Y
|
||||
EffectRow* position_row = new EffectRow(this, tr("Position"));
|
||||
|
||||
EffectRow* scale_row = add_row(tr("Scale"));
|
||||
scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scalex"); // scale X (and Y is uniform scale is selected)
|
||||
scale_x->set_double_minimum_value(0);
|
||||
scale_x->set_double_maximum_value(3000);
|
||||
scale_y = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scaley"); // scale Y (disabled if uniform scale is selected)
|
||||
scale_y->set_double_minimum_value(0);
|
||||
scale_y->set_double_maximum_value(3000);
|
||||
position_x = new DoubleField(position_row, "posx"); // position X
|
||||
position_y = new DoubleField(position_row, "posy"); // position Y
|
||||
|
||||
EffectRow* uniform_scale_row = add_row(tr("Uniform Scale"));
|
||||
uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option
|
||||
EffectRow* scale_row = new EffectRow(this, tr("Scale"));
|
||||
|
||||
EffectRow* rotation_row = add_row(tr("Rotation"));
|
||||
rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation");
|
||||
// scale X (and Y is uniform scale is selected)
|
||||
scale_x = new DoubleField(scale_row, "scalex");
|
||||
scale_x->SetMinimum(0);
|
||||
|
||||
EffectRow* anchor_point_row = add_row(tr("Anchor Point"));
|
||||
anchor_x_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchorx"); // anchor point X
|
||||
anchor_y_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchory"); // anchor point Y
|
||||
// scale Y (disabled if uniform scale is selected)
|
||||
scale_y = new DoubleField(scale_row, "scaley");
|
||||
scale_y->SetMinimum(0);
|
||||
|
||||
EffectRow* opacity_row = add_row(tr("Opacity"));
|
||||
opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity
|
||||
opacity->set_double_minimum_value(0);
|
||||
opacity->set_double_maximum_value(100);
|
||||
EffectRow* uniform_scale_row = new EffectRow(this, tr("Uniform Scale"));
|
||||
|
||||
EffectRow* blend_mode_row = add_row(tr("Blend Mode"));
|
||||
blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode", 2); // blend mode
|
||||
blend_mode_box->add_combo_item(tr("Normal"), -1);
|
||||
uniform_scale_field = new BoolField(uniform_scale_row, "uniformscale"); // uniform scale option
|
||||
|
||||
// add loaded blending modes
|
||||
olive::effects_loaded.lock();
|
||||
for (int i=0;i<olive::blend_modes.size();i++) {
|
||||
blend_mode_box->add_combo_item(olive::blend_modes.at(i).name, i);
|
||||
}
|
||||
olive::effects_loaded.unlock();
|
||||
EffectRow* rotation_row = new EffectRow(this, tr("Rotation"));
|
||||
|
||||
rotation = new DoubleField(rotation_row, "rotation");
|
||||
|
||||
EffectRow* anchor_point_row = new EffectRow(this, tr("Anchor Point"));
|
||||
|
||||
anchor_x_box = new DoubleField(anchor_point_row, "anchorx"); // anchor point X
|
||||
anchor_y_box = new DoubleField(anchor_point_row, "anchory"); // anchor point Y
|
||||
|
||||
EffectRow* opacity_row = new EffectRow(this, tr("Opacity"));
|
||||
|
||||
// opacity
|
||||
opacity = new DoubleField(opacity_row, "opacity");
|
||||
opacity->SetMinimum(0);
|
||||
opacity->SetMaximum(100);
|
||||
|
||||
EffectRow* blend_mode_row = new EffectRow(this, tr("Blend Mode"));
|
||||
|
||||
// blend mode
|
||||
blend_mode_box = new ComboField(blend_mode_row, "blendmode");
|
||||
blend_mode_box->SetColumnSpan(2);
|
||||
blend_mode_box->AddItem(tr("Normal"), -1);
|
||||
|
||||
// set up gizmos
|
||||
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
@@ -134,49 +137,29 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em)
|
||||
rect_gizmo->x_field1 = position_x;
|
||||
rect_gizmo->y_field1 = position_y;
|
||||
|
||||
connect(uniform_scale_field, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool)));
|
||||
connect(uniform_scale_field, SIGNAL(Toggled(bool)), this, SLOT(toggle_uniform_scale(bool)));
|
||||
|
||||
// set defaults
|
||||
uniform_scale_field->set_bool_value(true);
|
||||
blend_mode_box->set_combo_index(0);
|
||||
set = false;
|
||||
refresh();
|
||||
}
|
||||
uniform_scale_field->SetValueAt(0, true);
|
||||
blend_mode_box->SetValueAt(0, -1);
|
||||
anchor_x_box->SetDefault(0);
|
||||
anchor_y_box->SetDefault(0);
|
||||
opacity->SetDefault(100);
|
||||
scale_x->SetDefault(100);
|
||||
scale_y->SetDefault(100);
|
||||
|
||||
void adjust_field(EffectField* field, double old_offset, double new_offset) {
|
||||
if (field->keyframes.size() > 0) {
|
||||
for (int i=0;i<field->keyframes.size();i++) {
|
||||
field->keyframes[i].data = field->keyframes.at(i).data.toDouble() - old_offset + new_offset;
|
||||
}
|
||||
} else {
|
||||
field->set_current_data(field->get_current_data().toDouble() - old_offset + new_offset);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
void TransformEffect::refresh() {
|
||||
if (parent_clip != nullptr && parent_clip->sequence != nullptr) {
|
||||
double new_default_pos_x = parent_clip->sequence->width/2;
|
||||
double new_default_pos_y = parent_clip->sequence->height/2;
|
||||
|
||||
/*if (set) {
|
||||
adjust_field(position_x, default_pos_x, new_default_pos_x);
|
||||
adjust_field(position_y, default_pos_y, new_default_pos_y);
|
||||
}*/
|
||||
|
||||
double default_pos_x = new_default_pos_x;
|
||||
double default_pos_y = new_default_pos_y;
|
||||
|
||||
position_x->set_double_default_value(default_pos_x);
|
||||
position_y->set_double_default_value(default_pos_y);
|
||||
scale_x->set_double_default_value(100);
|
||||
scale_y->set_double_default_value(100);
|
||||
|
||||
anchor_x_box->set_double_default_value(0);
|
||||
anchor_y_box->set_double_default_value(0);
|
||||
opacity->set_double_default_value(100);
|
||||
position_x->SetDefault(parent_clip->sequence->width/2);
|
||||
position_y->SetDefault(parent_clip->sequence->height/2);
|
||||
|
||||
double x_percent_multipler = 200.0 / parent_clip->sequence->width;
|
||||
double y_percent_multipler = 200.0 / parent_clip->sequence->height;
|
||||
|
||||
top_left_gizmo->x_field_multi1 = -x_percent_multipler;
|
||||
top_left_gizmo->y_field_multi1 = -y_percent_multipler;
|
||||
top_center_gizmo->y_field_multi1 = -y_percent_multipler;
|
||||
@@ -191,12 +174,11 @@ void TransformEffect::refresh() {
|
||||
right_center_gizmo->x_field_multi1 = x_percent_multipler;
|
||||
rotate_gizmo->x_field_multi1 = x_percent_multipler;
|
||||
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TransformEffect::toggle_uniform_scale(bool enabled) {
|
||||
scale_y->set_enabled(!enabled);
|
||||
scale_y->SetEnabled(!enabled);
|
||||
|
||||
top_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
|
||||
bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
|
||||
@@ -208,11 +190,14 @@ void TransformEffect::toggle_uniform_scale(bool enabled) {
|
||||
|
||||
void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int) {
|
||||
// position
|
||||
glTranslated(position_x->get_double_value(timecode)-(parent_clip->sequence->width/2), position_y->get_double_value(timecode)-(parent_clip->sequence->height/2), 0);
|
||||
glTranslated(position_x->GetDoubleAt(timecode)-(parent_clip->sequence->width/2),
|
||||
position_y->GetDoubleAt(timecode)-(parent_clip->sequence->height/2),
|
||||
0);
|
||||
|
||||
// anchor point
|
||||
int anchor_x_offset = qRound(anchor_x_box->get_double_value(timecode));
|
||||
int anchor_y_offset = qRound(anchor_y_box->get_double_value(timecode));
|
||||
int anchor_x_offset = qRound(anchor_x_box->GetDoubleAt(timecode));
|
||||
int anchor_y_offset = qRound(anchor_y_box->GetDoubleAt(timecode));
|
||||
|
||||
coords.vertexTopLeftX -= anchor_x_offset;
|
||||
coords.vertexTopRightX -= anchor_x_offset;
|
||||
coords.vertexBottomLeftX -= anchor_x_offset;
|
||||
@@ -223,18 +208,18 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i
|
||||
coords.vertexBottomRightY -= anchor_y_offset;
|
||||
|
||||
// rotation
|
||||
glRotated(rotation->get_double_value(timecode), 0, 0, 1);
|
||||
glRotated(rotation->GetDoubleAt(timecode), 0, 0, 1);
|
||||
|
||||
// scale
|
||||
double sx = scale_x->get_double_value(timecode)*0.01;
|
||||
double sy = (uniform_scale_field->get_bool_value(timecode)) ? sx : scale_y->get_double_value(timecode)*0.01;
|
||||
double sx = scale_x->GetDoubleAt(timecode)*0.01;
|
||||
double sy = (uniform_scale_field->GetBoolAt(timecode)) ? sx : scale_y->GetDoubleAt(timecode)*0.01;
|
||||
glScaled(sx, sy, 1);
|
||||
|
||||
// blend mode
|
||||
coords.blendmode = blend_mode_box->get_combo_data(timecode).toInt();
|
||||
coords.blendmode = blend_mode_box->GetValueAt(timecode).toInt();
|
||||
|
||||
// opacity
|
||||
coords.opacity *= float(opacity->get_double_value(timecode)*0.01);
|
||||
coords.opacity *= float(opacity->GetDoubleAt(timecode)*0.01);
|
||||
}
|
||||
|
||||
void TransformEffect::gizmo_draw(double, GLTextureCoords& coords) {
|
||||
|
||||
@@ -21,43 +21,41 @@
|
||||
#ifndef TRANSFORMEFFECT_H
|
||||
#define TRANSFORMEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
class TransformEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
TransformEffect(Clip* c, const EffectMeta* em);
|
||||
void refresh();
|
||||
void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
TransformEffect(Clip* c, const EffectMeta* em);
|
||||
void refresh();
|
||||
void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
|
||||
void gizmo_draw(double timecode, GLTextureCoords& coords);
|
||||
void gizmo_draw(double timecode, GLTextureCoords& coords);
|
||||
public slots:
|
||||
void toggle_uniform_scale(bool enabled);
|
||||
void toggle_uniform_scale(bool enabled);
|
||||
private:
|
||||
EffectField* position_x;
|
||||
EffectField* position_y;
|
||||
EffectField* scale_x;
|
||||
EffectField* scale_y;
|
||||
EffectField* uniform_scale_field;
|
||||
EffectField* rotation;
|
||||
EffectField* anchor_x_box;
|
||||
EffectField* anchor_y_box;
|
||||
EffectField* opacity;
|
||||
EffectField* blend_mode_box;
|
||||
DoubleField* position_x;
|
||||
DoubleField* position_y;
|
||||
DoubleField* scale_x;
|
||||
DoubleField* scale_y;
|
||||
BoolField* uniform_scale_field;
|
||||
DoubleField* rotation;
|
||||
DoubleField* anchor_x_box;
|
||||
DoubleField* anchor_y_box;
|
||||
DoubleField* opacity;
|
||||
ComboField* blend_mode_box;
|
||||
|
||||
EffectGizmo* top_left_gizmo;
|
||||
EffectGizmo* top_center_gizmo;
|
||||
EffectGizmo* top_right_gizmo;
|
||||
EffectGizmo* bottom_left_gizmo;
|
||||
EffectGizmo* bottom_center_gizmo;
|
||||
EffectGizmo* bottom_right_gizmo;
|
||||
EffectGizmo* left_center_gizmo;
|
||||
EffectGizmo* right_center_gizmo;
|
||||
EffectGizmo* anchor_gizmo;
|
||||
EffectGizmo* rotate_gizmo;
|
||||
EffectGizmo* rect_gizmo;
|
||||
|
||||
bool set;
|
||||
EffectGizmo* top_left_gizmo;
|
||||
EffectGizmo* top_center_gizmo;
|
||||
EffectGizmo* top_right_gizmo;
|
||||
EffectGizmo* bottom_left_gizmo;
|
||||
EffectGizmo* bottom_center_gizmo;
|
||||
EffectGizmo* bottom_right_gizmo;
|
||||
EffectGizmo* left_center_gizmo;
|
||||
EffectGizmo* right_center_gizmo;
|
||||
EffectGizmo* anchor_gizmo;
|
||||
EffectGizmo* rotate_gizmo;
|
||||
EffectGizmo* rect_gizmo;
|
||||
};
|
||||
|
||||
#endif // TRANSFORMEFFECT_H
|
||||
|
||||
@@ -25,68 +25,69 @@
|
||||
#include <QFile>
|
||||
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "debug.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
VoidEffect::VoidEffect(Clip* c, const QString& n) : Effect(c, nullptr) {
|
||||
name = n;
|
||||
QString display_name;
|
||||
if (n.isEmpty()) {
|
||||
display_name = tr("(unknown)");
|
||||
} else {
|
||||
display_name = n;
|
||||
}
|
||||
EffectRow* row = add_row(tr("Missing Effect"), false, false);
|
||||
row->add_widget(new QLabel(display_name));
|
||||
container->setText(display_name);
|
||||
QString display_name;
|
||||
if (n.isEmpty()) {
|
||||
display_name = tr("(unknown)");
|
||||
} else {
|
||||
display_name = n;
|
||||
}
|
||||
EffectRow* row = new EffectRow(this, tr("Missing Effect"), false, false);
|
||||
|
||||
void_meta.type = EFFECT_TYPE_EFFECT;
|
||||
meta = &void_meta;
|
||||
new LabelField(row, display_name);
|
||||
|
||||
name = display_name;
|
||||
|
||||
void_meta.type = EFFECT_TYPE_EFFECT;
|
||||
meta = &void_meta;
|
||||
}
|
||||
|
||||
EffectPtr VoidEffect::copy(Clip* c) {
|
||||
EffectPtr copy(new VoidEffect(c, name));
|
||||
copy->set_enabled(is_enabled());
|
||||
copy_field_keyframes(copy);
|
||||
return copy;
|
||||
EffectPtr copy = std::make_shared<VoidEffect>(c, name);
|
||||
copy->SetEnabled(IsEnabled());
|
||||
copy_field_keyframes(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
void VoidEffect::load(QXmlStreamReader &stream) {
|
||||
QString tag = stream.name().toString();
|
||||
QString tag = stream.name().toString();
|
||||
|
||||
QXmlStreamWriter writer(&bytes);
|
||||
QXmlStreamWriter writer(&bytes);
|
||||
|
||||
// copy XML from reader to writer
|
||||
while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) {
|
||||
stream.readNext();
|
||||
// copy XML from reader to writer
|
||||
while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) {
|
||||
stream.readNext();
|
||||
|
||||
if (stream.isStartElement()) {
|
||||
writer.writeStartElement(stream.name().toString());
|
||||
}
|
||||
if (stream.isEndElement()) {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
if (stream.isCharacters()) {
|
||||
writer.writeCharacters(stream.text().toString());
|
||||
}
|
||||
for (int i=0;i<stream.attributes().size();i++) {
|
||||
writer.writeAttribute(stream.attributes().at(i));
|
||||
}
|
||||
if (stream.isStartElement()) {
|
||||
writer.writeStartElement(stream.name().toString());
|
||||
}
|
||||
if (stream.isEndElement()) {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
if (stream.isCharacters()) {
|
||||
writer.writeCharacters(stream.text().toString());
|
||||
}
|
||||
for (int i=0;i<stream.attributes().size();i++) {
|
||||
writer.writeAttribute(stream.attributes().at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoidEffect::save(QXmlStreamWriter &stream) {
|
||||
if (!name.isEmpty()) {
|
||||
stream.writeAttribute("name", name);
|
||||
stream.writeAttribute("enabled", QString::number(is_enabled()));
|
||||
if (!name.isEmpty()) {
|
||||
stream.writeAttribute("name", name);
|
||||
stream.writeAttribute("enabled", QString::number(IsEnabled()));
|
||||
|
||||
// force xml writer to expand <effect> tag, ignored when loading
|
||||
stream.writeStartElement("void");
|
||||
stream.writeEndElement();
|
||||
// force xml writer to expand <effect> tag, ignored when loading
|
||||
stream.writeStartElement("void");
|
||||
stream.writeEndElement();
|
||||
|
||||
if (!bytes.isEmpty()) {
|
||||
// write stored data
|
||||
QIODevice* device = stream.device();
|
||||
device->write(bytes);
|
||||
}
|
||||
}
|
||||
if (!bytes.isEmpty()) {
|
||||
// write stored data
|
||||
QIODevice* device = stream.device();
|
||||
device->write(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,19 +27,19 @@
|
||||
* isn't lost if the user saves over the project.
|
||||
*/
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
class VoidEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
VoidEffect(Clip* c, const QString& n);
|
||||
VoidEffect(Clip* c, const QString& n);
|
||||
|
||||
virtual EffectPtr copy(Clip* c) override;
|
||||
virtual void load(QXmlStreamReader &stream) override;
|
||||
virtual void save(QXmlStreamWriter &stream) override;
|
||||
virtual EffectPtr copy(Clip* c) override;
|
||||
virtual void load(QXmlStreamReader &stream) override;
|
||||
virtual void save(QXmlStreamWriter &stream) override;
|
||||
private:
|
||||
QByteArray bytes;
|
||||
EffectMeta void_meta;
|
||||
QByteArray bytes;
|
||||
EffectMeta void_meta;
|
||||
};
|
||||
|
||||
#endif // VOIDEFFECT_H
|
||||
|
||||
@@ -29,41 +29,40 @@
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
EffectRow* volume_row = add_row(tr("Volume"));
|
||||
volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE, "volume");
|
||||
EffectRow* volume_row = new EffectRow(this, tr("Volume"));
|
||||
volume_val = new DoubleField(volume_row, "volume");
|
||||
|
||||
// set defaults
|
||||
volume_val->set_double_default_value(1);
|
||||
static_cast<LabelSlider*>(volume_val->get_ui_element())->set_display_type(LABELSLIDER_DECIBEL);
|
||||
// set defaults
|
||||
volume_val->SetDefault(1);
|
||||
volume_val->SetDisplayType(LabelSlider::Decibel);
|
||||
}
|
||||
|
||||
void VolumeEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) {
|
||||
double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
// double vol_val = log_volume(volume_val->get_double_value(timecode_start+(interval*i), true));
|
||||
double vol_val = volume_val->get_double_value(timecode_start+(interval*i), true);
|
||||
double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
double vol_val = volume_val->GetDoubleAt(timecode_start+(interval*i));
|
||||
|
||||
qint32 right_samp = qint16(((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF));
|
||||
qint32 left_samp = qint16(((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
qint32 right_samp = qint16(((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF));
|
||||
qint32 left_samp = qint16(((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
|
||||
left_samp *= vol_val;
|
||||
right_samp *= vol_val;
|
||||
left_samp *= vol_val;
|
||||
right_samp *= vol_val;
|
||||
|
||||
if (left_samp > INT16_MAX) {
|
||||
left_samp = INT16_MAX;
|
||||
} else if (left_samp < INT16_MIN) {
|
||||
left_samp = INT16_MIN;
|
||||
}
|
||||
if (left_samp > INT16_MAX) {
|
||||
left_samp = INT16_MAX;
|
||||
} else if (left_samp < INT16_MIN) {
|
||||
left_samp = INT16_MIN;
|
||||
}
|
||||
|
||||
if (right_samp > INT16_MAX) {
|
||||
right_samp = INT16_MAX;
|
||||
} else if (right_samp < INT16_MIN) {
|
||||
right_samp = INT16_MIN;
|
||||
}
|
||||
if (right_samp > INT16_MAX) {
|
||||
right_samp = INT16_MAX;
|
||||
} else if (right_samp < INT16_MIN) {
|
||||
right_samp = INT16_MIN;
|
||||
}
|
||||
|
||||
samples[i+3] = (quint8) (right_samp >> 8);
|
||||
samples[i+2] = (quint8) right_samp;
|
||||
samples[i+1] = (quint8) (left_samp >> 8);
|
||||
samples[i] = (quint8) left_samp;
|
||||
}
|
||||
samples[i+3] = (quint8) (right_samp >> 8);
|
||||
samples[i+2] = (quint8) right_samp;
|
||||
samples[i+1] = (quint8) (left_samp >> 8);
|
||||
samples[i] = (quint8) left_samp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
#ifndef VOLUMEEFFECT_H
|
||||
#define VOLUMEEFFECT_H
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
class VolumeEffect : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
VolumeEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
VolumeEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
EffectField* volume_val;
|
||||
DoubleField* volume_val;
|
||||
};
|
||||
|
||||
#endif // VOLUMEEFFECT_H
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
#include <QWindow>
|
||||
|
||||
#include "rendering/audio.h"
|
||||
#include "mainwindow.h"
|
||||
#include "debug.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "global/debug.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <X11/X.h>
|
||||
@@ -104,7 +104,7 @@ typedef int32_t (*processEventsFuncPtr)(VstEvents *events);
|
||||
typedef void (*processFuncPtr)(AEffect *effect, float **inputs, float **outputs, int32_t sampleFrames);
|
||||
|
||||
void VSTHost::loadPlugin() {
|
||||
QString dll_fn = file_field->get_filename(0, true);
|
||||
QString dll_fn = file_field->GetFileAt(0);
|
||||
|
||||
if (dll_fn.isEmpty()) {
|
||||
return;
|
||||
@@ -254,7 +254,22 @@ void VSTHost::processAudio(long numFrames) {
|
||||
plugin->processReplacing(plugin, inputs, outputs, numFrames);
|
||||
}
|
||||
|
||||
VSTHost::VSTHost(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
void VSTHost::CreateDialogIfNull()
|
||||
{
|
||||
if (dialog == nullptr) {
|
||||
dialog = new QDialog(olive::MainWindow);
|
||||
dialog->setWindowTitle(tr("VST Plugin"));
|
||||
dialog->setAttribute(Qt::WA_NativeWindow, true);
|
||||
dialog->setWindowFlags(dialog->windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
|
||||
connect(dialog, SIGNAL(finished(int)), this, SLOT(uncheck_show_button()));
|
||||
}
|
||||
}
|
||||
|
||||
VSTHost::VSTHost(Clip* c, const EffectMeta *em) :
|
||||
Effect(c, em),
|
||||
plugin(nullptr),
|
||||
dialog(nullptr)
|
||||
{
|
||||
plugin = nullptr;
|
||||
|
||||
inputs = new float* [CHANNEL_COUNT];
|
||||
@@ -264,21 +279,16 @@ VSTHost::VSTHost(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
outputs[channel] = new float[BLOCK_SIZE];
|
||||
}
|
||||
|
||||
file_field = add_row(tr("Plugin"), true, false)->add_field(EFFECT_FIELD_FILE, "filename");
|
||||
connect(file_field, SIGNAL(changed()), this, SLOT(change_plugin()));
|
||||
EffectRow* file_row = new EffectRow(this, tr("Plugin"), true, false);
|
||||
file_field = new FileField(file_row, "filename");
|
||||
connect(file_field, SIGNAL(Changed()), this, SLOT(change_plugin()));
|
||||
|
||||
EffectRow* interface_row = add_row(tr("Interface"), false, false);
|
||||
show_interface_btn = new QPushButton(tr("Show"));
|
||||
show_interface_btn->setCheckable(true);
|
||||
show_interface_btn->setEnabled(false);
|
||||
connect(show_interface_btn, SIGNAL(toggled(bool)), this, SLOT(show_interface(bool)));
|
||||
interface_row->add_widget(show_interface_btn);
|
||||
EffectRow* interface_row = new EffectRow(this, tr("Interface"), false, false);
|
||||
|
||||
dialog = new QDialog(olive::MainWindow);
|
||||
dialog->setWindowTitle(tr("VST Plugin"));
|
||||
dialog->setAttribute(Qt::WA_NativeWindow, true);
|
||||
dialog->setWindowFlags(dialog->windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
|
||||
connect(dialog, SIGNAL(finished(int)), this, SLOT(uncheck_show_button()));
|
||||
show_interface_btn = new ButtonField(interface_row, tr("Show"));
|
||||
show_interface_btn->SetCheckable(true);
|
||||
show_interface_btn->SetEnabled(false);
|
||||
connect(show_interface_btn, SIGNAL(Toggled(bool)), this, SLOT(show_interface(bool)));
|
||||
}
|
||||
|
||||
VSTHost::~VSTHost() {
|
||||
@@ -290,9 +300,6 @@ VSTHost::~VSTHost() {
|
||||
delete [] inputs;
|
||||
|
||||
freePlugin();
|
||||
|
||||
delete show_interface_btn;
|
||||
delete dialog;
|
||||
}
|
||||
|
||||
void VSTHost::process_audio(double, double, quint8* samples, int nb_bytes, int) {
|
||||
@@ -354,6 +361,7 @@ void VSTHost::save(QXmlStreamWriter &stream) {
|
||||
}
|
||||
|
||||
void VSTHost::show_interface(bool show) {
|
||||
CreateDialogIfNull();
|
||||
dialog->setVisible(show);
|
||||
|
||||
if (show) {
|
||||
@@ -370,7 +378,7 @@ void VSTHost::show_interface(bool show) {
|
||||
}
|
||||
|
||||
void VSTHost::uncheck_show_button() {
|
||||
show_interface_btn->setChecked(false);
|
||||
show_interface_btn->SetChecked(false);
|
||||
}
|
||||
|
||||
void VSTHost::change_plugin() {
|
||||
@@ -379,9 +387,13 @@ void VSTHost::change_plugin() {
|
||||
if (plugin != nullptr) {
|
||||
if (configurePluginCallbacks()) {
|
||||
startPlugin();
|
||||
|
||||
VSTRect* eRect = nullptr;
|
||||
plugin->dispatcher(plugin, effEditGetRect, 0, 0, &eRect, 0);
|
||||
|
||||
CreateDialogIfNull();
|
||||
dialog->setFixedSize(eRect->right - eRect->left, eRect->bottom - eRect->top);
|
||||
|
||||
} else {
|
||||
#ifdef __APPLE__
|
||||
CFBundleUnloadExecutable(bundle);
|
||||
@@ -392,5 +404,5 @@ void VSTHost::change_plugin() {
|
||||
plugin = nullptr;
|
||||
}
|
||||
}
|
||||
show_interface_btn->setEnabled(plugin != nullptr);
|
||||
show_interface_btn->SetEnabled(plugin != nullptr);
|
||||
}
|
||||
|
||||
+31
-30
@@ -23,9 +23,9 @@
|
||||
|
||||
#ifndef NOVST
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
#include "io/crossplatformlib.h"
|
||||
#include "global/crossplatformlib.h"
|
||||
|
||||
#include "include/vestige.h"
|
||||
|
||||
@@ -35,42 +35,43 @@ typedef intptr_t (*dispatcherFuncPtr)(AEffect *effect, int32_t opCode, int32_t i
|
||||
#include <QDialog>
|
||||
|
||||
class VSTHost : public Effect {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
VSTHost(Clip* c, const EffectMeta* em);
|
||||
~VSTHost();
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
VSTHost(Clip* c, const EffectMeta* em);
|
||||
~VSTHost();
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
void custom_load(QXmlStreamReader& stream);
|
||||
void save(QXmlStreamWriter& stream);
|
||||
void custom_load(QXmlStreamReader& stream);
|
||||
void save(QXmlStreamWriter& stream);
|
||||
private slots:
|
||||
void show_interface(bool show);
|
||||
void uncheck_show_button();
|
||||
void change_plugin();
|
||||
void show_interface(bool show);
|
||||
void uncheck_show_button();
|
||||
void change_plugin();
|
||||
private:
|
||||
EffectField* file_field;
|
||||
FileField* file_field;
|
||||
ButtonField* show_interface_btn;
|
||||
|
||||
void loadPlugin();
|
||||
void freePlugin();
|
||||
dispatcherFuncPtr dispatcher;
|
||||
AEffect* plugin;
|
||||
bool configurePluginCallbacks();
|
||||
void startPlugin();
|
||||
void stopPlugin();
|
||||
void resumePlugin();
|
||||
void suspendPlugin();
|
||||
bool canPluginDo(char *canDoString);
|
||||
void processAudio(long numFrames);
|
||||
float** inputs;
|
||||
float** outputs;
|
||||
QDialog* dialog;
|
||||
QPushButton* show_interface_btn;
|
||||
QByteArray data_cache;
|
||||
void loadPlugin();
|
||||
void freePlugin();
|
||||
dispatcherFuncPtr dispatcher;
|
||||
AEffect* plugin;
|
||||
bool configurePluginCallbacks();
|
||||
void startPlugin();
|
||||
void stopPlugin();
|
||||
void resumePlugin();
|
||||
void suspendPlugin();
|
||||
bool canPluginDo(char *canDoString);
|
||||
void processAudio(long numFrames);
|
||||
void CreateDialogIfNull();
|
||||
float** inputs;
|
||||
float** outputs;
|
||||
QDialog* dialog;
|
||||
QByteArray data_cache;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
CFBundleRef bundle;
|
||||
CFBundleRef bundle;
|
||||
#else
|
||||
ModulePtr modulePtr;
|
||||
ModulePtr modulePtr;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "keyframe.h"
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include "effectfields.h"
|
||||
#include "undo/undo.h"
|
||||
#include "undo/undostack.h"
|
||||
#include "panels/panels.h"
|
||||
|
||||
EffectKeyframe::EffectKeyframe()
|
||||
{
|
||||
pre_handle_x = -40;
|
||||
pre_handle_y = 0;
|
||||
post_handle_x = 40;
|
||||
post_handle_y = 0;
|
||||
}
|
||||
|
||||
void delete_keyframes(QVector<EffectField *>& selected_key_fields, QVector<int> &selected_keys) {
|
||||
QVector<EffectField*> fields;
|
||||
QVector<int> key_indices;
|
||||
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
bool added = false;
|
||||
for (int j=0;j<key_indices.size();j++) {
|
||||
if (key_indices.at(j) < selected_keys.at(i)) {
|
||||
key_indices.insert(j, selected_keys.at(i));
|
||||
fields.insert(j, selected_key_fields.at(i));
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
key_indices.append(selected_keys.at(i));
|
||||
fields.append(selected_key_fields.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (fields.size() > 0) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<key_indices.size();i++) {
|
||||
ca->append(new KeyframeDelete(fields.at(i), key_indices.at(i)));
|
||||
}
|
||||
olive::UndoStack.push(ca);
|
||||
selected_keys.clear();
|
||||
selected_key_fields.clear();
|
||||
update_ui(false);
|
||||
}
|
||||
}
|
||||
@@ -27,17 +27,17 @@ class EffectField;
|
||||
|
||||
class EffectKeyframe {
|
||||
public:
|
||||
EffectKeyframe();
|
||||
EffectKeyframe();
|
||||
|
||||
long time;
|
||||
int type;
|
||||
QVariant data;
|
||||
int type;
|
||||
long time;
|
||||
QVariant data;
|
||||
|
||||
// only for bezier type
|
||||
double pre_handle_x;
|
||||
double pre_handle_y;
|
||||
double post_handle_x;
|
||||
double post_handle_y;
|
||||
// only for bezier type
|
||||
double pre_handle_x;
|
||||
double pre_handle_y;
|
||||
double post_handle_x;
|
||||
double post_handle_y;
|
||||
};
|
||||
|
||||
void delete_keyframes(QVector<EffectField *> &selected_key_fields, QVector<int> &selected_keys);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user