updated to most recent master
This commit is contained in:
@@ -11,7 +11,7 @@ QDebug debug_out(&debug_file);
|
||||
|
||||
void setup_debug() {
|
||||
#ifndef QT_DEBUG
|
||||
debug_file.setFileName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/debug_log");
|
||||
debug_file.setFileName(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/debug_log");
|
||||
if (debug_file.open(QFile::WriteOnly)) {
|
||||
QString debug_intro = "Olive Session " + QString::number(QDateTime::currentMSecsSinceEpoch());
|
||||
debug_file.write(debug_intro.toUtf8());
|
||||
|
||||
+41
-62
@@ -57,6 +57,7 @@ enum ExportFormats {
|
||||
ExportDialog::ExportDialog(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
setWindowTitle("Export \"" + sequence->name + "\"");
|
||||
setup_ui();
|
||||
|
||||
rangeCombobox->setCurrentIndex(0);
|
||||
@@ -478,7 +479,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)));
|
||||
|
||||
closeActiveClips(sequence, true);
|
||||
closeActiveClips(sequence);
|
||||
|
||||
mainWindow->autorecover_interval();
|
||||
|
||||
@@ -601,54 +602,41 @@ void ExportDialog::setup_ui() {
|
||||
videoGroupbox->setTitle("Video");
|
||||
videoGroupbox->setFlat(false);
|
||||
videoGroupbox->setCheckable(true);
|
||||
QGridLayout* gridLayout = new QGridLayout(videoGroupbox);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 1, 1);
|
||||
|
||||
compressionTypeCombobox = new QComboBox(videoGroupbox);
|
||||
compressionTypeCombobox->addItem("Quality-based (Constant Rate Factor)");
|
||||
compressionTypeCombobox->addItem("File size-based (Two-Pass)");
|
||||
|
||||
gridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1);
|
||||
QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox);
|
||||
|
||||
videoGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1);
|
||||
vcodecCombobox = new QComboBox(videoGroupbox);
|
||||
videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1);
|
||||
|
||||
gridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Height:"), 2, 0, 1, 1);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1);
|
||||
|
||||
heightSpinbox = new QSpinBox(videoGroupbox);
|
||||
heightSpinbox->setMaximum(16777216);
|
||||
|
||||
gridLayout->addWidget(heightSpinbox, 2, 1, 1, 1);
|
||||
|
||||
videoGridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1);
|
||||
widthSpinbox = new QSpinBox(videoGroupbox);
|
||||
widthSpinbox->setMaximum(16777216);
|
||||
videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1);
|
||||
|
||||
gridLayout->addWidget(widthSpinbox, 1, 1, 1, 1);
|
||||
|
||||
videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox);
|
||||
videobitrateSpinbox->setMaximum(100);
|
||||
videobitrateSpinbox->setValue(2);
|
||||
|
||||
gridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1);
|
||||
|
||||
videoBitrateLabel = new QLabel(videoGroupbox);
|
||||
|
||||
gridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Frame Rate:"), 3, 0, 1, 1);
|
||||
videoGridLayout->addWidget(new QLabel("Height:"), 2, 0, 1, 1);
|
||||
heightSpinbox = new QSpinBox(videoGroupbox);
|
||||
heightSpinbox->setMaximum(16777216);
|
||||
videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1);
|
||||
|
||||
videoGridLayout->addWidget(new QLabel("Frame Rate:"), 3, 0, 1, 1);
|
||||
framerateSpinbox = new QDoubleSpinBox(videoGroupbox);
|
||||
framerateSpinbox->setMaximum(60);
|
||||
framerateSpinbox->setValue(0);
|
||||
videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1);
|
||||
|
||||
gridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1);
|
||||
videoGridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 1, 1);
|
||||
compressionTypeCombobox = new QComboBox(videoGroupbox);
|
||||
compressionTypeCombobox->addItem("Quality-based (Constant Rate Factor)");
|
||||
compressionTypeCombobox->addItem("File size-based (Two-Pass)");
|
||||
videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1);
|
||||
|
||||
videoBitrateLabel = new QLabel(videoGroupbox);
|
||||
videoGridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1);
|
||||
videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox);
|
||||
videobitrateSpinbox->setMaximum(100);
|
||||
videobitrateSpinbox->setValue(2);
|
||||
videoGridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1);
|
||||
|
||||
verticalLayout->addWidget(videoGroupbox);
|
||||
|
||||
@@ -656,69 +644,60 @@ void ExportDialog::setup_ui() {
|
||||
audioGroupbox->setTitle("Audio");
|
||||
audioGroupbox->setCheckable(true);
|
||||
|
||||
QGridLayout* gridLayout_2 = new QGridLayout(audioGroupbox);
|
||||
gridLayout_2->addWidget(new QLabel("Codec:"), 0, 0, 1, 1);
|
||||
QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox);
|
||||
|
||||
audioGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1);
|
||||
acodecCombobox = new QComboBox(audioGroupbox);
|
||||
audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1);
|
||||
|
||||
gridLayout_2->addWidget(acodecCombobox, 0, 1, 1, 1);
|
||||
|
||||
audioGridLayout->addWidget(new QLabel("Sampling Rate:"), 1, 0, 1, 1);
|
||||
samplingRateSpinbox = new QSpinBox(audioGroupbox);
|
||||
samplingRateSpinbox->setMaximum(96000);
|
||||
samplingRateSpinbox->setValue(0);
|
||||
audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1);
|
||||
|
||||
gridLayout_2->addWidget(samplingRateSpinbox, 1, 1, 1, 1);
|
||||
|
||||
gridLayout_2->addWidget(new QLabel("Sampling Rate:"), 1, 0, 1, 1);
|
||||
|
||||
gridLayout_2->addWidget(new QLabel("Bitrate (Kbps/CBR):"), 3, 0, 1, 1);
|
||||
|
||||
audioGridLayout->addWidget(new QLabel("Bitrate (Kbps/CBR):"), 3, 0, 1, 1);
|
||||
audiobitrateSpinbox = new QSpinBox(audioGroupbox);
|
||||
audiobitrateSpinbox->setMaximum(320);
|
||||
audiobitrateSpinbox->setValue(256);
|
||||
|
||||
gridLayout_2->addWidget(audiobitrateSpinbox, 3, 1, 1, 1);
|
||||
|
||||
audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1);
|
||||
|
||||
verticalLayout->addWidget(audioGroupbox);
|
||||
|
||||
QHBoxLayout* horizontalLayout_3 = new QHBoxLayout();
|
||||
QHBoxLayout* progressLayout = new QHBoxLayout();
|
||||
progressBar = new QProgressBar(this);
|
||||
progressBar->setFormat("%p% (ETA: 0:00:00)");
|
||||
progressBar->setEnabled(false);
|
||||
progressBar->setValue(0);
|
||||
|
||||
horizontalLayout_3->addWidget(progressBar);
|
||||
progressLayout->addWidget(progressBar);
|
||||
|
||||
renderCancel = new QPushButton(this);
|
||||
renderCancel->setText("x");
|
||||
renderCancel->setEnabled(false);
|
||||
renderCancel->setMaximumSize(QSize(20, 16777215));
|
||||
connect(renderCancel, SIGNAL(clicked(bool)), this, SLOT(cancel_render()));
|
||||
progressLayout->addWidget(renderCancel);
|
||||
|
||||
horizontalLayout_3->addWidget(renderCancel);
|
||||
verticalLayout->addLayout(progressLayout);
|
||||
|
||||
|
||||
verticalLayout->addLayout(horizontalLayout_3);
|
||||
|
||||
QHBoxLayout* horizontalLayout_2 = new QHBoxLayout();
|
||||
horizontalLayout_2->addStretch();
|
||||
QHBoxLayout* buttonLayout = new QHBoxLayout();
|
||||
buttonLayout->addStretch();
|
||||
|
||||
export_button = new QPushButton(this);
|
||||
export_button->setText("Export");
|
||||
connect(export_button, SIGNAL(clicked(bool)), this, SLOT(export_action()));
|
||||
|
||||
horizontalLayout_2->addWidget(export_button);
|
||||
buttonLayout->addWidget(export_button);
|
||||
|
||||
cancel_button = new QPushButton(this);
|
||||
cancel_button->setText("Cancel");
|
||||
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject()));
|
||||
|
||||
horizontalLayout_2->addWidget(cancel_button);
|
||||
buttonLayout->addWidget(cancel_button);
|
||||
|
||||
horizontalLayout_2->addStretch();
|
||||
buttonLayout->addStretch();
|
||||
|
||||
verticalLayout->addLayout(horizontalLayout_2);
|
||||
verticalLayout->addLayout(buttonLayout);
|
||||
|
||||
connect(formatCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(format_changed(int)));
|
||||
connect(compressionTypeCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(comp_type_changed(int)));
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <QLineEdit>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QGroupBox>
|
||||
#include <QListWidget>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "project/footage.h"
|
||||
#include "project/media.h"
|
||||
@@ -13,59 +16,136 @@
|
||||
#include "project/undo.h"
|
||||
|
||||
MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) :
|
||||
QDialog(parent),
|
||||
item(i)
|
||||
QDialog(parent),
|
||||
item(i)
|
||||
{
|
||||
setWindowTitle("\"" + i->get_name() + "\" Properties");
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
QGridLayout* grid = new QGridLayout();
|
||||
setLayout(grid);
|
||||
|
||||
Footage* f = item->to_footage();
|
||||
if (f->video_tracks.size() > 0) {
|
||||
interlacing_box = new QComboBox();
|
||||
interlacing_box->addItem("Auto (" + 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));
|
||||
int row = 0;
|
||||
|
||||
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);
|
||||
Footage* f = item->to_footage();
|
||||
|
||||
grid->addWidget(new QLabel("Interlacing:"), 0, 0);
|
||||
grid->addWidget(interlacing_box, 0, 1);
|
||||
}
|
||||
grid->addWidget(new QLabel("Tracks:"), row, 0, 1, 2);
|
||||
row++;
|
||||
|
||||
name_box = new QLineEdit(item->get_name());
|
||||
grid->addWidget(new QLabel("Name:"), 1, 0);
|
||||
grid->addWidget(name_box, 1, 1);
|
||||
track_list = new QListWidget();
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
const FootageStream& fs = f->video_tracks.at(i);
|
||||
QListWidgetItem* item = new QListWidgetItem("Video " + QString::number(fs.file_index) + ": " + QString::number(fs.video_width) + "x" + QString::number(fs.video_height) + " " + QString::number(fs.video_frame_rate) + "FPS");
|
||||
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("Audio " + QString::number(fs.file_index) + ": " + QString::number(fs.audio_frequency) + "Hz " + QString::number(fs.audio_channels) + " channels");
|
||||
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
|
||||
grid->addWidget(new QLabel("Conform to Frame Rate:"), row, 0);
|
||||
conform_fr = new QDoubleSpinBox();
|
||||
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++;
|
||||
|
||||
// deinterlacing mode
|
||||
interlacing_box = new QComboBox();
|
||||
interlacing_box->addItem("Auto (" + 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);
|
||||
|
||||
grid->addWidget(new QLabel("Interlacing:"), row, 0);
|
||||
grid->addWidget(interlacing_box, row, 1);
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
name_box = new QLineEdit(item->get_name());
|
||||
grid->addWidget(new QLabel("Name:"), row, 0);
|
||||
grid->addWidget(name_box, row, 1);
|
||||
row++;
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttons->setCenterButtons(true);
|
||||
grid->addWidget(buttons, 2, 0, 1, 2);
|
||||
grid->addWidget(buttons, row, 0, 1, 2);
|
||||
|
||||
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
}
|
||||
|
||||
void MediaPropertiesDialog::accept() {
|
||||
Footage* f = item->to_footage();
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
//set interlacing
|
||||
Footage* f = item->to_footage();
|
||||
if (interlacing_box->currentIndex() > 0) {
|
||||
ca->append(new SetInt(&f->video_tracks.at(0)->video_interlacing, interlacing_box->currentIndex() - 1));
|
||||
} else {
|
||||
ca->append(new SetInt(&f->video_tracks.at(0)->video_interlacing, f->video_tracks.at(0)->video_auto_interlacing));
|
||||
// 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 name
|
||||
MediaRename* mr = new MediaRename(item, name_box->text());
|
||||
bool refresh_clips = false;
|
||||
|
||||
// set interlacing
|
||||
if (f->video_tracks.size() > 0) {
|
||||
if (interlacing_box->currentIndex() > 0) {
|
||||
ca->append(new SetInt(&f->video_tracks[0].video_interlacing, interlacing_box->currentIndex() - 1));
|
||||
} else {
|
||||
ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing));
|
||||
}
|
||||
|
||||
// set frame rate conform
|
||||
if (!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 name
|
||||
MediaRename* mr = new MediaRename(item, name_box->text());
|
||||
|
||||
ca->append(mr);
|
||||
ca->appendPost(new CloseAllClipsCommand());
|
||||
ca->appendPost(new UpdateFootageTooltip(item));
|
||||
ca->appendPost(new UpdateFootageTooltip(item));
|
||||
if (refresh_clips) ca->appendPost(new RefreshClips(item));
|
||||
|
||||
undo_stack.push(ca);
|
||||
|
||||
QDialog::accept();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
@@ -7,17 +7,21 @@ struct Footage;
|
||||
class QComboBox;
|
||||
class QLineEdit;
|
||||
class Media;
|
||||
class QListWidget;
|
||||
class QDoubleSpinBox;
|
||||
|
||||
class MediaPropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MediaPropertiesDialog(QWidget *parent, Media* i);
|
||||
MediaPropertiesDialog(QWidget *parent, Media* i);
|
||||
private:
|
||||
QComboBox* interlacing_box;
|
||||
QLineEdit* name_box;
|
||||
Media* item;
|
||||
QLineEdit* name_box;
|
||||
Media* item;
|
||||
QListWidget* track_list;
|
||||
QDoubleSpinBox* conform_fr;
|
||||
private slots:
|
||||
void accept();
|
||||
void accept();
|
||||
};
|
||||
|
||||
#endif // MEDIAPROPERTIESDIALOG_H
|
||||
|
||||
@@ -42,7 +42,7 @@ NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing) :
|
||||
break;
|
||||
}
|
||||
}
|
||||
lineEdit->setText(existing_sequence->name);
|
||||
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);
|
||||
@@ -59,14 +59,14 @@ NewSequenceDialog::~NewSequenceDialog()
|
||||
{}
|
||||
|
||||
void NewSequenceDialog::set_sequence_name(const QString& s) {
|
||||
lineEdit->setText(s);
|
||||
sequence_name_edit->setText(s);
|
||||
}
|
||||
|
||||
void NewSequenceDialog::create() {
|
||||
if (existing_sequence == NULL) {
|
||||
Sequence* s = new Sequence();
|
||||
|
||||
s->name = lineEdit->text();
|
||||
s->name = sequence_name_edit->text();
|
||||
s->width = width_numeric->value();
|
||||
s->height = height_numeric->value();
|
||||
s->frame_rate = frame_rate_combobox->currentData().toDouble();
|
||||
@@ -82,7 +82,7 @@ void NewSequenceDialog::create() {
|
||||
double multiplier = frame_rate_combobox->currentData().toDouble() / existing_sequence->frame_rate;
|
||||
|
||||
EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_sequence);
|
||||
esc->name = lineEdit->text();
|
||||
esc->name = sequence_name_edit->text();
|
||||
esc->width = width_numeric->value();
|
||||
esc->height = height_numeric->value();
|
||||
esc->frame_rate = frame_rate_combobox->currentData().toDouble();
|
||||
@@ -154,10 +154,10 @@ void NewSequenceDialog::setup_ui() {
|
||||
|
||||
QWidget* widget = new QWidget(this);
|
||||
|
||||
QHBoxLayout* horizontalLayout_2 = new QHBoxLayout(widget);
|
||||
horizontalLayout_2->setContentsMargins(0, 0, 0, 0);
|
||||
QHBoxLayout* preset_layout = new QHBoxLayout(widget);
|
||||
preset_layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
horizontalLayout_2->addWidget(new QLabel("Preset:"));
|
||||
preset_layout->addWidget(new QLabel("Preset:"));
|
||||
|
||||
preset_combobox = new QComboBox(widget);
|
||||
|
||||
@@ -174,75 +174,65 @@ void NewSequenceDialog::setup_ui() {
|
||||
preset_combobox->addItem("Custom");
|
||||
preset_combobox->setCurrentIndex(2);
|
||||
|
||||
horizontalLayout_2->addWidget(preset_combobox);
|
||||
preset_layout->addWidget(preset_combobox);
|
||||
|
||||
verticalLayout->addWidget(widget);
|
||||
|
||||
QGroupBox* groupBox = new QGroupBox(this);
|
||||
groupBox->setTitle("Video");
|
||||
QGroupBox* videoGroupBox = new QGroupBox(this);
|
||||
videoGroupBox->setTitle("Video");
|
||||
|
||||
QGridLayout* gridLayout = new QGridLayout(groupBox);
|
||||
QGridLayout* videoLayout = new QGridLayout(videoGroupBox);
|
||||
|
||||
height_numeric = new QSpinBox(groupBox);
|
||||
videoLayout->addWidget(new QLabel("Width:"), 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("Height:"), 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);
|
||||
|
||||
gridLayout->addWidget(height_numeric, 1, 2, 1, 2);
|
||||
videoLayout->addWidget(new QLabel("Frame Rate:"), 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);
|
||||
|
||||
width_numeric = new QSpinBox(groupBox);
|
||||
width_numeric->setMaximum(9999);
|
||||
width_numeric->setValue(1920);
|
||||
videoLayout->addWidget(new QLabel("Pixel Aspect Ratio:"), 4, 0, 1, 1);
|
||||
par_combobox = new QComboBox(videoGroupBox);
|
||||
par_combobox->addItem("Square Pixels (1.0)");
|
||||
videoLayout->addWidget(par_combobox, 4, 2, 1, 2);
|
||||
|
||||
gridLayout->addWidget(width_numeric, 0, 2, 1, 2);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Width:"), 0, 0, 1, 1);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Pixel Aspect Ratio:"), 4, 0, 1, 1);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Interlacing:"), 6, 0, 1, 1);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Height:"), 1, 0, 1, 2);
|
||||
|
||||
par_combobox = new QComboBox(groupBox);
|
||||
par_combobox->addItem("Square Pixels (1.0)");
|
||||
|
||||
gridLayout->addWidget(par_combobox, 4, 2, 1, 2);
|
||||
|
||||
interlacing_combobox = new QComboBox(groupBox);
|
||||
videoLayout->addWidget(new QLabel("Interlacing:"), 6, 0, 1, 1);
|
||||
interlacing_combobox = new QComboBox(videoGroupBox);
|
||||
interlacing_combobox->addItem("None (Progressive)");
|
||||
interlacing_combobox->addItem("Upper Field First");
|
||||
interlacing_combobox->addItem("Lower Field First");
|
||||
// interlacing_combobox->addItem("Upper Field First");
|
||||
// interlacing_combobox->addItem("Lower Field First");
|
||||
videoLayout->addWidget(interlacing_combobox, 6, 2, 1, 2);
|
||||
|
||||
gridLayout->addWidget(interlacing_combobox, 6, 2, 1, 2);
|
||||
verticalLayout->addWidget(videoGroupBox);
|
||||
|
||||
frame_rate_combobox = new QComboBox(groupBox);
|
||||
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);
|
||||
QGroupBox* audioGroupBox = new QGroupBox(this);
|
||||
audioGroupBox->setTitle("Audio");
|
||||
|
||||
gridLayout->addWidget(frame_rate_combobox, 2, 2, 1, 2);
|
||||
QGridLayout* audioLayout = new QGridLayout(audioGroupBox);
|
||||
|
||||
gridLayout->addWidget(new QLabel("Frame Rate:"), 2, 0, 1, 1);
|
||||
audioLayout->addWidget(new QLabel("Sample Rate: "), 0, 0, 1, 1);
|
||||
|
||||
verticalLayout->addWidget(groupBox);
|
||||
|
||||
QGroupBox* groupBox_2 = new QGroupBox(this);
|
||||
groupBox_2->setTitle("Audio");
|
||||
|
||||
QGridLayout* gridLayout_2 = new QGridLayout(groupBox_2);
|
||||
|
||||
gridLayout_2->addWidget(new QLabel("Sample Rate: "), 0, 0, 1, 1);
|
||||
|
||||
audio_frequency_combobox = new QComboBox(groupBox_2);
|
||||
audio_frequency_combobox = new QComboBox(audioGroupBox);
|
||||
audio_frequency_combobox->addItem("22050 Hz", 22050);
|
||||
audio_frequency_combobox->addItem("24000 Hz", 24000);
|
||||
audio_frequency_combobox->addItem("32000 Hz", 32000);
|
||||
@@ -252,21 +242,21 @@ void NewSequenceDialog::setup_ui() {
|
||||
audio_frequency_combobox->addItem("96000 Hz", 96000);
|
||||
audio_frequency_combobox->setCurrentIndex(4);
|
||||
|
||||
gridLayout_2->addWidget(audio_frequency_combobox, 0, 1, 1, 1);
|
||||
audioLayout->addWidget(audio_frequency_combobox, 0, 1, 1, 1);
|
||||
|
||||
verticalLayout->addWidget(groupBox_2);
|
||||
verticalLayout->addWidget(audioGroupBox);
|
||||
|
||||
QWidget* widget_2 = new QWidget(this);
|
||||
QHBoxLayout* horizontalLayout = new QHBoxLayout(widget_2);
|
||||
horizontalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
QWidget* nameWidget = new QWidget(this);
|
||||
QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget);
|
||||
nameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
horizontalLayout->addWidget(new QLabel("Name:"));
|
||||
nameLayout->addWidget(new QLabel("Name:"));
|
||||
|
||||
lineEdit = new QLineEdit(widget_2);
|
||||
sequence_name_edit = new QLineEdit(nameWidget);
|
||||
|
||||
horizontalLayout->addWidget(lineEdit);
|
||||
nameLayout->addWidget(sequence_name_edit);
|
||||
|
||||
verticalLayout->addWidget(widget_2);
|
||||
verticalLayout->addWidget(nameWidget);
|
||||
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(this);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
|
||||
@@ -37,7 +37,7 @@ private:
|
||||
QComboBox* interlacing_combobox;
|
||||
QComboBox* frame_rate_combobox;
|
||||
QComboBox* audio_frequency_combobox;
|
||||
QLineEdit* lineEdit;
|
||||
QLineEdit* sequence_name_edit;
|
||||
};
|
||||
|
||||
#endif // NEWSEQUENCEDIALOG_H
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTreeWidget>
|
||||
#include <QVector>
|
||||
#include <QPushButton>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QList>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a)
|
||||
: QKeySequenceEdit(parent), action(a) {
|
||||
@@ -53,6 +58,7 @@ void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem*
|
||||
if (a->menu() != NULL) {
|
||||
setup_kbd_shortcut_worker(a->menu(), item);
|
||||
} else {
|
||||
item->setData(0, Qt::UserRole + 1, reinterpret_cast<quintptr>(a));
|
||||
key_shortcut_items.append(item);
|
||||
key_shortcut_actions.append(a);
|
||||
}
|
||||
@@ -94,6 +100,59 @@ void PreferencesDialog::save() {
|
||||
accept();
|
||||
}
|
||||
|
||||
void PreferencesDialog::reset_default_shortcut() {
|
||||
QList<QTreeWidgetItem*> items = keyboard_tree->selectedItems();
|
||||
for (int i=0;i<items.size();i++) {
|
||||
QTreeWidgetItem* item = keyboard_tree->selectedItems().at(i);
|
||||
const QVariant& data = item->data(0, Qt::UserRole + 1);
|
||||
if (!data.isNull()) {
|
||||
QAction* a = reinterpret_cast<QAction*>(data.value<quintptr>());
|
||||
QKeySequence ks(a->property("default").toString());
|
||||
static_cast<QKeySequenceEdit*>(keyboard_tree->itemWidget(item, 1))->setKeySequence(ks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PreferencesDialog::refine_shortcut_list(const QString &s, QTreeWidgetItem* parent) {
|
||||
if (parent == NULL) {
|
||||
for (int i=0;i<keyboard_tree->topLevelItemCount();i++) {
|
||||
refine_shortcut_list(s, keyboard_tree->topLevelItem(i));
|
||||
}
|
||||
} else {
|
||||
parent->setExpanded(!s.isEmpty());
|
||||
|
||||
bool all_children_are_hidden = !s.isEmpty();
|
||||
|
||||
for (int i=0;i<parent->childCount();i++) {
|
||||
QTreeWidgetItem* item = parent->child(i);
|
||||
if (item->childCount() > 0) {
|
||||
all_children_are_hidden = refine_shortcut_list(s, item);
|
||||
} else {
|
||||
item->setHidden(false);
|
||||
if (s.isEmpty()) {
|
||||
all_children_are_hidden = false;
|
||||
} else {
|
||||
QString shortcut;
|
||||
if (keyboard_tree->itemWidget(item, 1) != NULL) {
|
||||
shortcut = static_cast<QKeySequenceEdit*>(keyboard_tree->itemWidget(item, 1))->keySequence().toString();
|
||||
}
|
||||
if (item->text(0).contains(s, Qt::CaseInsensitive) || shortcut.contains(s, Qt::CaseInsensitive)) {
|
||||
all_children_are_hidden = false;
|
||||
} else {
|
||||
item->setHidden(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->text(0).contains(s, Qt::CaseInsensitive)) all_children_are_hidden = false;
|
||||
|
||||
parent->setHidden(all_children_are_hidden);
|
||||
|
||||
return all_children_are_hidden;
|
||||
}
|
||||
}
|
||||
|
||||
void PreferencesDialog::setup_ui() {
|
||||
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
|
||||
QTabWidget* tabWidget = new QTabWidget(this);
|
||||
@@ -136,17 +195,33 @@ void PreferencesDialog::setup_ui() {
|
||||
verticalLayout_2->addWidget(groupBox);
|
||||
|
||||
tabWidget->addTab(tab_4, "Playback");
|
||||
QWidget* tab_3 = new QWidget();
|
||||
QHBoxLayout* horizontalLayout = new QHBoxLayout(tab_3);
|
||||
horizontalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QWidget* shortcut_tab = new QWidget();
|
||||
|
||||
QVBoxLayout* shortcut_layout = new QVBoxLayout(shortcut_tab);
|
||||
|
||||
QLineEdit* key_search_line = new QLineEdit();
|
||||
key_search_line->setPlaceholderText("Search for action or shortcut");
|
||||
connect(key_search_line, SIGNAL(textChanged(const QString &)), this, SLOT(refine_shortcut_list(const QString &)));
|
||||
|
||||
shortcut_layout->addWidget(key_search_line);
|
||||
|
||||
keyboard_tree = new QTreeWidget();
|
||||
QTreeWidgetItem* tree_header = keyboard_tree->headerItem();
|
||||
tree_header->setText(0, "Action");
|
||||
tree_header->setText(1, "Shortcut");
|
||||
horizontalLayout->addWidget(keyboard_tree);
|
||||
shortcut_layout->addWidget(keyboard_tree);
|
||||
|
||||
tabWidget->addTab(tab_3, "Keyboard");
|
||||
QHBoxLayout* reset_shortcut_layout = new QHBoxLayout();
|
||||
reset_shortcut_layout->addStretch();
|
||||
|
||||
reset_shortcut_button = new QPushButton("Reset to Default");
|
||||
reset_shortcut_layout->addWidget(reset_shortcut_button);
|
||||
connect(reset_shortcut_button, SIGNAL(clicked(bool)), this, SLOT(reset_default_shortcut()));
|
||||
|
||||
shortcut_layout->addLayout(reset_shortcut_layout);
|
||||
|
||||
tabWidget->addTab(shortcut_tab, "Keyboard");
|
||||
|
||||
verticalLayout->addWidget(tabWidget);
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ public:
|
||||
|
||||
private slots:
|
||||
void save();
|
||||
void reset_default_shortcut();
|
||||
bool refine_shortcut_list(const QString &, QTreeWidgetItem* parent = NULL);
|
||||
|
||||
private:
|
||||
void setup_ui();
|
||||
@@ -46,6 +48,8 @@ private:
|
||||
QVector<QAction*> key_shortcut_actions;
|
||||
QVector<QTreeWidgetItem*> key_shortcut_items;
|
||||
QVector<KeySequenceEditor*> key_shortcut_fields;
|
||||
|
||||
QPushButton* reset_shortcut_button;
|
||||
};
|
||||
|
||||
#endif // PREFERENCESDIALOG_H
|
||||
|
||||
@@ -23,6 +23,8 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media *old_media
|
||||
QDialog(parent),
|
||||
media(old_media)
|
||||
{
|
||||
setWindowTitle("Replace clips using \"" + old_media->get_name() + "\"");
|
||||
|
||||
resize(300, 400);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
@@ -56,12 +58,10 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media *old_media
|
||||
setLayout(layout);
|
||||
|
||||
tree->setModel(&project_model);
|
||||
|
||||
//copy_tree(NULL, NULL);
|
||||
}
|
||||
|
||||
void ReplaceClipMediaDialog::replace() {
|
||||
QModelIndexList selected_items = panel_project->get_current_selected();
|
||||
QModelIndexList selected_items = tree->selectionModel()->selectedRows();
|
||||
if (selected_items.size() != 1) {
|
||||
QMessageBox::critical(this, "No media selected", "Please select a media to replace with or click 'Cancel'.", QMessageBox::Ok);
|
||||
} else {
|
||||
|
||||
@@ -335,7 +335,7 @@ void SpeedDialog::accept() {
|
||||
|
||||
for (int i=0;i<clips.size();i++) {
|
||||
Clip* c = clips.at(i);
|
||||
if (c->open) close_clip(c);
|
||||
if (c->open) close_clip(c, true);
|
||||
|
||||
if (c->track >= 0
|
||||
&& maintain_pitch->checkState() != Qt::PartiallyChecked
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
#include <QtMath>
|
||||
|
||||
AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE, "amount");
|
||||
amount_val = add_row("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);
|
||||
amount_val->set_double_default_value(20);
|
||||
|
||||
mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val = add_row("Mix")->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val->set_bool_value(true);
|
||||
|
||||
srand(QDateTime::currentMSecsSinceEpoch());
|
||||
srand(QDateTime::currentMSecsSinceEpoch());
|
||||
}
|
||||
|
||||
void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) {
|
||||
|
||||
@@ -5,59 +5,59 @@
|
||||
#include "debug.h"
|
||||
|
||||
CornerPinEffect::CornerPinEffect(Clip *c, const EffectMeta *em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
enable_coords = true;
|
||||
enable_shader = true;
|
||||
|
||||
EffectRow* top_left = add_row("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 = add_row("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_right = add_row("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 = add_row("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* bottom_left = add_row("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 = add_row("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_right = add_row("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 = add_row("Bottom Right");
|
||||
bottom_right_x = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrightx");
|
||||
bottom_right_y = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrighty");
|
||||
|
||||
perspective = add_row("Perspective:")->add_field(EFFECT_FIELD_BOOL, "perspective");
|
||||
perspective->set_bool_value(true);
|
||||
perspective = add_row("Perspective")->add_field(EFFECT_FIELD_BOOL, "perspective");
|
||||
perspective->set_bool_value(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";
|
||||
}
|
||||
|
||||
void CornerPinEffect::process_coords(double timecode, GLTextureCoords &coords, int data) {
|
||||
coords.vertexTopLeftX += top_left_x->get_double_value(timecode);
|
||||
coords.vertexTopLeftY += top_left_y->get_double_value(timecode);
|
||||
coords.vertexTopLeftX += top_left_x->get_double_value(timecode);
|
||||
coords.vertexTopLeftY += top_left_y->get_double_value(timecode);
|
||||
|
||||
coords.vertexTopRightX += top_right_x->get_double_value(timecode);
|
||||
coords.vertexTopRightY += top_right_y->get_double_value(timecode);
|
||||
coords.vertexTopRightX += top_right_x->get_double_value(timecode);
|
||||
coords.vertexTopRightY += top_right_y->get_double_value(timecode);
|
||||
|
||||
coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode);
|
||||
coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode);
|
||||
coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode);
|
||||
coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode);
|
||||
|
||||
coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode);
|
||||
coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode);
|
||||
coords.vertexBottomRightY += bottom_right_y->get_double_value(timecode);
|
||||
}
|
||||
|
||||
@@ -66,12 +66,12 @@ void CornerPinEffect::process_shader(double timecode, GLTextureCoords &coords) {
|
||||
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("perspective", perspective->get_bool_value(timecode));
|
||||
}
|
||||
|
||||
void CornerPinEffect::gizmo_draw(double timecode, 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);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
EffectRow* pan_row = add_row("Pan:");
|
||||
pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE, "pan");
|
||||
EffectRow* pan_row = add_row("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);
|
||||
pan_val->set_double_maximum_value(100);
|
||||
|
||||
// set defaults
|
||||
pan_val->set_double_default_value(0);
|
||||
pan_val->set_double_default_value(0);
|
||||
}
|
||||
|
||||
void PanEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) {
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
ShakeEffect::ShakeEffect(Clip *c, const EffectMeta *em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
|
||||
EffectRow* intensity_row = add_row("Intensity:");
|
||||
intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity");
|
||||
EffectRow* intensity_row = add_row("Intensity");
|
||||
intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity");
|
||||
intensity_val->set_double_minimum_value(0);
|
||||
|
||||
EffectRow* rotation_row = add_row("Rotation:");
|
||||
rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation");
|
||||
EffectRow* rotation_row = add_row("Rotation");
|
||||
rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation");
|
||||
rotation_val->set_double_minimum_value(0);
|
||||
|
||||
EffectRow* frequency_row = add_row("Frequency:");
|
||||
frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
EffectRow* frequency_row = add_row("Frequency");
|
||||
frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
frequency_val->set_double_minimum_value(0);
|
||||
|
||||
// set defaults
|
||||
// set defaults
|
||||
intensity_val->set_double_default_value(25);
|
||||
rotation_val->set_double_default_value(10);
|
||||
frequency_val->set_double_default_value(5);
|
||||
|
||||
@@ -21,36 +21,36 @@
|
||||
SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
|
||||
enable_superimpose = true;
|
||||
|
||||
solid_type = add_row("Type:")->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
solid_type = add_row("Type")->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
solid_type->add_combo_item("Solid Color", SOLID_TYPE_COLOR);
|
||||
solid_type->add_combo_item("SMPTE Bars", SOLID_TYPE_BARS);
|
||||
solid_type->add_combo_item("Checkerboard", SOLID_TYPE_CHECKERBOARD);
|
||||
solid_type->add_combo_item("Checkerboard", SOLID_TYPE_CHECKERBOARD);
|
||||
|
||||
opacity_field = add_row("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);
|
||||
opacity_field = add_row("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);
|
||||
|
||||
solid_color_field = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color");
|
||||
solid_color_field->set_color_value(Qt::red);
|
||||
solid_color_field = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color");
|
||||
solid_color_field->set_color_value(Qt::red);
|
||||
|
||||
checkerboard_size_field = add_row("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);
|
||||
checkerboard_size_field = add_row("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);
|
||||
|
||||
// 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());
|
||||
// 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());
|
||||
|
||||
/*vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/solideffect.frag";*/
|
||||
/*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);
|
||||
int alpha = qRound(opacity_field->get_double_value(timecode)*2.55);
|
||||
switch (solid_type->get_combo_data(timecode).toInt()) {
|
||||
case SOLID_TYPE_COLOR:
|
||||
{
|
||||
@@ -141,37 +141,37 @@ void SolidEffect::redraw(double timecode) {
|
||||
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);
|
||||
}
|
||||
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->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);
|
||||
|
||||
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->get_color_value(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);
|
||||
solid_color_field->set_enabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD);
|
||||
checkerboard_size_field->set_enabled(i == SOLID_TYPE_CHECKERBOARD);
|
||||
}
|
||||
|
||||
@@ -24,43 +24,43 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
|
||||
Effect(c, em)
|
||||
{
|
||||
enable_superimpose = true;
|
||||
enable_shader = true;
|
||||
enable_shader = true;
|
||||
|
||||
text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, "text", 2);
|
||||
text_val = add_row("Text")->add_field(EFFECT_FIELD_STRING, "text", 2);
|
||||
|
||||
set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, "font", 2);
|
||||
set_font_combobox = add_row("Font")->add_field(EFFECT_FIELD_FONT, "font", 2);
|
||||
|
||||
size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
size_val = add_row("Size")->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
|
||||
set_color_button = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
set_color_button = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
|
||||
EffectRow* alignment_row = add_row("Alignment:");
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign");
|
||||
EffectRow* alignment_row = add_row("Alignment");
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign");
|
||||
halign_field->add_combo_item("Left", Qt::AlignLeft);
|
||||
halign_field->add_combo_item("Center", Qt::AlignHCenter);
|
||||
halign_field->add_combo_item("Right", Qt::AlignRight);
|
||||
halign_field->add_combo_item("Justify", Qt::AlignJustify);
|
||||
halign_field->add_combo_item("Justify", Qt::AlignJustify);
|
||||
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign");
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign");
|
||||
valign_field->add_combo_item("Top", Qt::AlignTop);
|
||||
valign_field->add_combo_item("Center", Qt::AlignVCenter);
|
||||
valign_field->add_combo_item("Bottom", Qt::AlignBottom);
|
||||
valign_field->add_combo_item("Bottom", Qt::AlignBottom);
|
||||
|
||||
word_wrap_field = add_row("Word Wrap:")->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
|
||||
word_wrap_field = add_row("Word Wrap")->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
|
||||
|
||||
outline_bool = add_row("Outline:")->add_field(EFFECT_FIELD_BOOL, "outline", 2);
|
||||
outline_color = add_row("Outline Color:")->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
|
||||
outline_width = add_row("Outline Width:")->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
|
||||
outline_bool = add_row("Outline")->add_field(EFFECT_FIELD_BOOL, "outline", 2);
|
||||
outline_color = add_row("Outline Color")->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
|
||||
outline_width = add_row("Outline Width")->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
|
||||
outline_width->set_double_minimum_value(0);
|
||||
|
||||
shadow_bool = add_row("Shadow:")->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
|
||||
shadow_color = add_row("Shadow Color:")->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
|
||||
shadow_distance = add_row("Shadow Distance:")->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
|
||||
shadow_bool = add_row("Shadow")->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
|
||||
shadow_color = add_row("Shadow Color")->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
|
||||
shadow_distance = add_row("Shadow Distance")->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
|
||||
shadow_distance->set_double_minimum_value(0);
|
||||
shadow_softness = add_row("Shadow Softness:")->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
|
||||
shadow_softness = add_row("Shadow Softness")->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
|
||||
shadow_softness->set_double_minimum_value(0);
|
||||
shadow_opacity = add_row("Shadow Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
|
||||
shadow_opacity = add_row("Shadow Opacity")->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
|
||||
shadow_opacity->set_double_minimum_value(0);
|
||||
shadow_opacity->set_double_maximum_value(100);
|
||||
|
||||
@@ -78,13 +78,13 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
|
||||
outline_width->set_double_default_value(20);
|
||||
|
||||
outline_enable(false);
|
||||
shadow_enable(false);
|
||||
shadow_enable(false);
|
||||
|
||||
connect(shadow_bool, SIGNAL(toggled(bool)), this, SLOT(shadow_enable(bool)));
|
||||
connect(outline_bool, SIGNAL(toggled(bool)), this, SLOT(outline_enable(bool)));
|
||||
|
||||
vertPath = "common.vert";
|
||||
fragPath = "dropshadow.frag";
|
||||
vertPath = "common.vert";
|
||||
fragPath = "dropshadow.frag";
|
||||
}
|
||||
|
||||
void TextEffect::redraw(double timecode) {
|
||||
@@ -170,7 +170,7 @@ void TextEffect::redraw(double timecode) {
|
||||
}
|
||||
|
||||
path.addText(text_x, text_y, font, lines.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// draw outline
|
||||
int outline_width_val = outline_width->get_double_value(timecode);
|
||||
@@ -185,7 +185,7 @@ void TextEffect::redraw(double timecode) {
|
||||
// draw "master" text
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(set_color_button->get_color_value(timecode));
|
||||
p.drawPath(path);
|
||||
p.drawPath(path);
|
||||
}
|
||||
|
||||
void TextEffect::shadow_enable(bool e) {
|
||||
|
||||
@@ -24,82 +24,82 @@
|
||||
#include "playback/playback.h"
|
||||
|
||||
TimecodeEffect::TimecodeEffect(Clip *c, const EffectMeta* em) :
|
||||
Effect(c, em)
|
||||
Effect(c, em)
|
||||
{
|
||||
enable_always_update = true;
|
||||
enable_always_update = true;
|
||||
enable_superimpose = true;
|
||||
|
||||
EffectRow* tc_row = add_row("Timecode:");
|
||||
tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector");
|
||||
tc_select->add_combo_item("Sequence", true);
|
||||
tc_select->add_combo_item("Media", false);
|
||||
tc_select->set_combo_index(0);
|
||||
EffectRow* tc_row = add_row("Timecode");
|
||||
tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector");
|
||||
tc_select->add_combo_item("Sequence", true);
|
||||
tc_select->add_combo_item("Media", false);
|
||||
tc_select->set_combo_index(0);
|
||||
|
||||
scale_val = add_row("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);
|
||||
scale_val = add_row("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);
|
||||
|
||||
color_val = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
color_val->set_color_value(Qt::white);
|
||||
color_val = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
color_val->set_color_value(Qt::white);
|
||||
|
||||
color_bg_val = add_row("Background Color:")->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2);
|
||||
color_bg_val->set_color_value(Qt::black);
|
||||
color_bg_val = add_row("Background Color")->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2);
|
||||
color_bg_val->set_color_value(Qt::black);
|
||||
|
||||
bg_alpha = add_row("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);
|
||||
bg_alpha = add_row("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* offset = add_row("Offset:");
|
||||
offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx");
|
||||
offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety");
|
||||
EffectRow* offset = add_row("Offset");
|
||||
offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx");
|
||||
offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety");
|
||||
|
||||
prepend_text = add_row("Prepend:")->add_field(EFFECT_FIELD_STRING, "prepend", 2);
|
||||
prepend_text = add_row("Prepend")->add_field(EFFECT_FIELD_STRING, "prepend", 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(sequence->playhead, config.timecode_view, sequence->frame_rate);}
|
||||
else {
|
||||
double media_rate = parent_clip->getMediaFrameRate();
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, config.timecode_view, media_rate);}
|
||||
img.fill(Qt::transparent);
|
||||
if (tc_select->get_combo_data(timecode).toBool()){
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(sequence->playhead, config.timecode_view, sequence->frame_rate);}
|
||||
else {
|
||||
double media_rate = parent_clip->getMediaFrameRate();
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, config.timecode_view, media_rate);}
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
QPainter p(&img);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
int width = img.width();
|
||||
int height = img.height();
|
||||
|
||||
// set font
|
||||
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
|
||||
font.setFamily("Helvetica");
|
||||
font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10)));
|
||||
p.setFont(font);
|
||||
font.setFamily("Helvetica");
|
||||
font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10)));
|
||||
p.setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
|
||||
QPainterPath path;
|
||||
QPainterPath path;
|
||||
|
||||
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;
|
||||
background_color.setAlpha(alpha_val);
|
||||
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;
|
||||
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->get_double_value(timecode));
|
||||
offset_y = int(offset_y_val->get_double_value(timecode));
|
||||
|
||||
text_x = offset_x + (width/2) - (text_width/2);
|
||||
text_y = offset_y + height - height/10;
|
||||
rect_y = text_y + fm.descent()/2 - text_height;
|
||||
text_x = offset_x + (width/2) - (text_width/2);
|
||||
text_y = offset_y + height - height/10;
|
||||
rect_y = text_y + fm.descent()/2 - text_height;
|
||||
|
||||
path.addText(text_x, text_y, font, display_timecode);
|
||||
path.addText(text_x, text_y, font, display_timecode);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(background_color);
|
||||
p.drawRect(QRect(text_x-fm.descent()/2, rect_y, text_width+fm.descent(), text_height));
|
||||
p.setBrush(color_val->get_color_value(timecode));
|
||||
p.drawPath(path);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(background_color);
|
||||
p.drawRect(QRect(text_x-fm.descent()/2, rect_y, text_width+fm.descent(), text_height));
|
||||
p.setBrush(color_val->get_color_value(timecode));
|
||||
p.drawPath(path);
|
||||
}
|
||||
|
||||
@@ -9,21 +9,21 @@
|
||||
#include "debug.h"
|
||||
|
||||
ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_MIN) {
|
||||
type_val = add_row("Type:")->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
type_val = add_row("Type")->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
type_val->add_combo_item("Sine", TONE_TYPE_SINE);
|
||||
|
||||
freq_val = add_row("Frequency:")->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
freq_val = add_row("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);
|
||||
|
||||
amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE, "amount");
|
||||
amount_val = add_row("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);
|
||||
|
||||
mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val->set_bool_value(true);
|
||||
mix_val = add_row("Mix")->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val->set_bool_value(true);
|
||||
}
|
||||
|
||||
void ToneEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) {
|
||||
|
||||
@@ -28,147 +28,162 @@
|
||||
#define BLEND_MODE_MULTIPLY 2
|
||||
#define BLEND_MODE_OVERLAY 3
|
||||
|
||||
TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
|
||||
EffectRow* position_row = add_row("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 = add_row("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* scale_row = add_row("Scale:");
|
||||
scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scalex"); // scale X (and Y is uniform scale is selected)
|
||||
EffectRow* scale_row = add_row("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 = 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);
|
||||
|
||||
EffectRow* uniform_scale_row = add_row("Uniform Scale:");
|
||||
uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option
|
||||
EffectRow* uniform_scale_row = add_row("Uniform Scale");
|
||||
uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option
|
||||
|
||||
EffectRow* rotation_row = add_row("Rotation:");
|
||||
rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation");
|
||||
EffectRow* rotation_row = add_row("Rotation");
|
||||
rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation");
|
||||
|
||||
EffectRow* anchor_point_row = add_row("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
|
||||
EffectRow* anchor_point_row = add_row("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
|
||||
|
||||
EffectRow* opacity_row = add_row("Opacity:");
|
||||
opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity
|
||||
EffectRow* opacity_row = add_row("Opacity");
|
||||
opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity
|
||||
opacity->set_double_minimum_value(0);
|
||||
opacity->set_double_maximum_value(100);
|
||||
|
||||
EffectRow* blend_mode_row = add_row("Blend Mode:");
|
||||
blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode"); // blend mode
|
||||
EffectRow* blend_mode_row = add_row("Blend Mode");
|
||||
blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode"); // blend mode
|
||||
blend_mode_box->add_combo_item("Normal", BLEND_MODE_NORMAL);
|
||||
blend_mode_box->add_combo_item("Overlay", BLEND_MODE_OVERLAY);
|
||||
blend_mode_box->add_combo_item("Screen", BLEND_MODE_SCREEN);
|
||||
blend_mode_box->add_combo_item("Multiply", BLEND_MODE_MULTIPLY);
|
||||
|
||||
// set up gizmos
|
||||
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_left_gizmo->set_cursor(Qt::SizeFDiagCursor);
|
||||
top_left_gizmo->x_field1 = scale_x;
|
||||
// set up gizmos
|
||||
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_left_gizmo->set_cursor(Qt::SizeFDiagCursor);
|
||||
top_left_gizmo->x_field1 = scale_x;
|
||||
|
||||
top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_center_gizmo->set_cursor(Qt::SizeVerCursor);
|
||||
top_center_gizmo->y_field1 = scale_x;
|
||||
top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_center_gizmo->set_cursor(Qt::SizeVerCursor);
|
||||
top_center_gizmo->y_field1 = scale_x;
|
||||
|
||||
top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_right_gizmo->set_cursor(Qt::SizeBDiagCursor);
|
||||
top_right_gizmo->x_field1 = scale_x;
|
||||
top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_right_gizmo->set_cursor(Qt::SizeBDiagCursor);
|
||||
top_right_gizmo->x_field1 = scale_x;
|
||||
|
||||
bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor);
|
||||
bottom_left_gizmo->x_field1 = scale_x;
|
||||
bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor);
|
||||
bottom_left_gizmo->x_field1 = scale_x;
|
||||
|
||||
bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_center_gizmo->set_cursor(Qt::SizeVerCursor);
|
||||
bottom_center_gizmo->y_field1 = scale_x;
|
||||
bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_center_gizmo->set_cursor(Qt::SizeVerCursor);
|
||||
bottom_center_gizmo->y_field1 = scale_x;
|
||||
|
||||
bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor);
|
||||
bottom_right_gizmo->x_field1 = scale_x;
|
||||
bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor);
|
||||
bottom_right_gizmo->x_field1 = scale_x;
|
||||
|
||||
left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
left_center_gizmo->set_cursor(Qt::SizeHorCursor);
|
||||
left_center_gizmo->x_field1 = scale_x;
|
||||
left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
left_center_gizmo->set_cursor(Qt::SizeHorCursor);
|
||||
left_center_gizmo->x_field1 = scale_x;
|
||||
|
||||
right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
right_center_gizmo->set_cursor(Qt::SizeHorCursor);
|
||||
right_center_gizmo->x_field1 = scale_x;
|
||||
right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
right_center_gizmo->set_cursor(Qt::SizeHorCursor);
|
||||
right_center_gizmo->x_field1 = scale_x;
|
||||
|
||||
anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET);
|
||||
anchor_gizmo->set_cursor(Qt::SizeAllCursor);
|
||||
anchor_gizmo->x_field1 = anchor_x_box;
|
||||
anchor_gizmo->y_field1 = anchor_y_box;
|
||||
anchor_gizmo->x_field2 = position_x;
|
||||
anchor_gizmo->y_field2 = position_y;
|
||||
anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET);
|
||||
anchor_gizmo->set_cursor(Qt::SizeAllCursor);
|
||||
anchor_gizmo->x_field1 = anchor_x_box;
|
||||
anchor_gizmo->y_field1 = anchor_y_box;
|
||||
anchor_gizmo->x_field2 = position_x;
|
||||
anchor_gizmo->y_field2 = position_y;
|
||||
|
||||
rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
rotate_gizmo->color = Qt::green;
|
||||
rotate_gizmo->set_cursor(Qt::SizeAllCursor);
|
||||
rotate_gizmo->x_field1 = rotation;
|
||||
rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
rotate_gizmo->color = Qt::green;
|
||||
rotate_gizmo->set_cursor(Qt::SizeAllCursor);
|
||||
rotate_gizmo->x_field1 = rotation;
|
||||
|
||||
rect_gizmo = add_gizmo(GIZMO_TYPE_POLY);
|
||||
rect_gizmo->x_field1 = position_x;
|
||||
rect_gizmo->y_field1 = position_y;
|
||||
rect_gizmo = add_gizmo(GIZMO_TYPE_POLY);
|
||||
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
|
||||
// set defaults
|
||||
uniform_scale_field->set_bool_value(true);
|
||||
blend_mode_box->set_combo_index(0);
|
||||
set = false;
|
||||
refresh();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformEffect::refresh() {
|
||||
if (parent_clip != NULL && parent_clip->sequence != NULL) {
|
||||
double default_pos_x = parent_clip->sequence->width/2;
|
||||
double default_pos_y = parent_clip->sequence->height/2;
|
||||
if (parent_clip != NULL && parent_clip->sequence != NULL) {
|
||||
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);
|
||||
|
||||
default_anchor_x = parent_clip->getWidth()/2;
|
||||
default_anchor_y = parent_clip->getHeight()/2;
|
||||
anchor_x_box->set_double_default_value(0);
|
||||
anchor_y_box->set_double_default_value(0);
|
||||
opacity->set_double_default_value(100);
|
||||
|
||||
if (default_anchor_x == 0) default_anchor_x = default_pos_x;
|
||||
if (default_anchor_y == 0) default_anchor_y = default_pos_y;
|
||||
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;
|
||||
top_right_gizmo->x_field_multi1 = x_percent_multipler;
|
||||
top_right_gizmo->y_field_multi1 = -y_percent_multipler;
|
||||
bottom_left_gizmo->x_field_multi1 = -x_percent_multipler;
|
||||
bottom_left_gizmo->y_field_multi1 = y_percent_multipler;
|
||||
bottom_center_gizmo->y_field_multi1 = y_percent_multipler;
|
||||
bottom_right_gizmo->x_field_multi1 = x_percent_multipler;
|
||||
bottom_right_gizmo->y_field_multi1 = y_percent_multipler;
|
||||
left_center_gizmo->x_field_multi1 = -x_percent_multipler;
|
||||
right_center_gizmo->x_field_multi1 = x_percent_multipler;
|
||||
rotate_gizmo->x_field_multi1 = x_percent_multipler;
|
||||
|
||||
anchor_x_box->set_double_default_value(default_anchor_x);
|
||||
anchor_y_box->set_double_default_value(default_anchor_y);
|
||||
opacity->set_double_default_value(100);
|
||||
|
||||
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;
|
||||
top_right_gizmo->x_field_multi1 = x_percent_multipler;
|
||||
top_right_gizmo->y_field_multi1 = -y_percent_multipler;
|
||||
bottom_left_gizmo->x_field_multi1 = -x_percent_multipler;
|
||||
bottom_left_gizmo->y_field_multi1 = y_percent_multipler;
|
||||
bottom_center_gizmo->y_field_multi1 = y_percent_multipler;
|
||||
bottom_right_gizmo->x_field_multi1 = x_percent_multipler;
|
||||
bottom_right_gizmo->y_field_multi1 = y_percent_multipler;
|
||||
left_center_gizmo->x_field_multi1 = -x_percent_multipler;
|
||||
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);
|
||||
|
||||
top_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
|
||||
bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
|
||||
top_left_gizmo->y_field1 = enabled ? NULL : scale_y;
|
||||
top_right_gizmo->y_field1 = enabled ? NULL : scale_y;
|
||||
bottom_left_gizmo->y_field1 = enabled ? NULL : scale_y;
|
||||
bottom_right_gizmo->y_field1 = enabled ? NULL : scale_y;
|
||||
top_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
|
||||
bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
|
||||
top_left_gizmo->y_field1 = enabled ? NULL : scale_y;
|
||||
top_right_gizmo->y_field1 = enabled ? NULL : scale_y;
|
||||
bottom_left_gizmo->y_field1 = enabled ? NULL : scale_y;
|
||||
bottom_right_gizmo->y_field1 = enabled ? NULL : scale_y;
|
||||
}
|
||||
|
||||
void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int data) {
|
||||
@@ -176,8 +191,8 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i
|
||||
glTranslatef(position_x->get_double_value(timecode)-(parent_clip->sequence->width/2), position_y->get_double_value(timecode)-(parent_clip->sequence->height/2), 0);
|
||||
|
||||
// anchor point
|
||||
int anchor_x_offset = (anchor_x_box->get_double_value(timecode)-default_anchor_x);
|
||||
int anchor_y_offset = (anchor_y_box->get_double_value(timecode)-default_anchor_y);
|
||||
int anchor_x_offset = (anchor_x_box->get_double_value(timecode));
|
||||
int anchor_y_offset = (anchor_y_box->get_double_value(timecode));
|
||||
coords.vertexTopLeftX -= anchor_x_offset;
|
||||
coords.vertexTopRightX -= anchor_x_offset;
|
||||
coords.vertexBottomLeftX -= anchor_x_offset;
|
||||
@@ -188,51 +203,51 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i
|
||||
coords.vertexBottomRightY -= anchor_y_offset;
|
||||
|
||||
// rotation
|
||||
glRotatef(rotation->get_double_value(timecode), 0, 0, 1);
|
||||
glRotatef(rotation->get_double_value(timecode), 0, 0, 1);
|
||||
|
||||
// scale
|
||||
float sx = scale_x->get_double_value(timecode)*0.01;
|
||||
float sy = (uniform_scale_field->get_bool_value(timecode)) ? sx : scale_y->get_double_value(timecode)*0.01;
|
||||
glScalef(sx, sy, 1);
|
||||
glScalef(sx, sy, 1);
|
||||
|
||||
// blend mode
|
||||
// blend mode
|
||||
switch (blend_mode_box->get_combo_data(timecode).toInt()) {
|
||||
case BLEND_MODE_NORMAL:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
case BLEND_MODE_OVERLAY:
|
||||
case BLEND_MODE_NORMAL:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
case BLEND_MODE_OVERLAY:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
break;
|
||||
case BLEND_MODE_SCREEN:
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
|
||||
break;
|
||||
case BLEND_MODE_MULTIPLY:
|
||||
break;
|
||||
case BLEND_MODE_SCREEN:
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
|
||||
break;
|
||||
case BLEND_MODE_MULTIPLY:
|
||||
glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
dout << "[ERROR] Invalid blend mode. This is a bug - please contact developers";
|
||||
}
|
||||
}
|
||||
|
||||
// opacity
|
||||
float color[4];
|
||||
glGetFloatv(GL_CURRENT_COLOR, color);
|
||||
glColor4f(1.0, 1.0, 1.0, color[3]*(opacity->get_double_value(timecode)*0.01));
|
||||
float color[4];
|
||||
glGetFloatv(GL_CURRENT_COLOR, color);
|
||||
glColor4f(1.0, 1.0, 1.0, color[3]*(opacity->get_double_value(timecode)*0.01));
|
||||
}
|
||||
|
||||
void TransformEffect::gizmo_draw(double timecode, GLTextureCoords& coords) {
|
||||
top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
|
||||
top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5));
|
||||
top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
|
||||
right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5));
|
||||
bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
|
||||
bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5));
|
||||
bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
|
||||
left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5));
|
||||
top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
|
||||
top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5));
|
||||
top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
|
||||
right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5));
|
||||
bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
|
||||
bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5));
|
||||
bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
|
||||
left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5));
|
||||
|
||||
rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1));
|
||||
rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1));
|
||||
|
||||
rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
|
||||
rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
|
||||
rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
|
||||
rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
|
||||
rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
|
||||
rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
|
||||
rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
|
||||
rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
|
||||
}
|
||||
|
||||
@@ -8,37 +8,36 @@ class TransformEffect : public Effect {
|
||||
public:
|
||||
TransformEffect(Clip* c, const EffectMeta* em);
|
||||
void refresh();
|
||||
void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
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);
|
||||
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;
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
int default_anchor_x;
|
||||
int default_anchor_y;
|
||||
bool set;
|
||||
};
|
||||
|
||||
#endif // TRANSFORMEFFECT_H
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
EffectRow* volume_row = add_row("Volume:");
|
||||
volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE, "volume");
|
||||
EffectRow* volume_row = add_row("Volume");
|
||||
volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE, "volume");
|
||||
volume_val->set_double_minimum_value(0);
|
||||
|
||||
// set defaults
|
||||
volume_val->set_double_default_value(100);
|
||||
volume_val->set_double_default_value(100);
|
||||
}
|
||||
|
||||
void VolumeEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) {
|
||||
|
||||
+134
-121
@@ -4,93 +4,98 @@
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "panels/project.h"
|
||||
#include "panels/panels.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
Config config;
|
||||
|
||||
Config::Config()
|
||||
: saved_layout(false),
|
||||
: saved_layout(false),
|
||||
show_track_lines(true),
|
||||
scroll_zooms(false),
|
||||
edit_tool_selects_links(false),
|
||||
edit_tool_also_seeks(false),
|
||||
select_also_seeks(false),
|
||||
scroll_zooms(false),
|
||||
edit_tool_selects_links(false),
|
||||
edit_tool_also_seeks(false),
|
||||
select_also_seeks(false),
|
||||
paste_seeks(true),
|
||||
img_seq_formats("jpg|jpeg|bmp|tiff|tif|psd|png|tga|jp2|gif"),
|
||||
img_seq_formats("jpg|jpeg|bmp|tiff|tif|psd|png|tga|jp2|gif"),
|
||||
rectified_waveforms(false),
|
||||
default_transition_length(30),
|
||||
timecode_view(TIMECODE_DROP),
|
||||
show_title_safe_area(false),
|
||||
use_custom_title_safe_ratio(false),
|
||||
default_transition_length(30),
|
||||
timecode_view(TIMECODE_DROP),
|
||||
show_title_safe_area(false),
|
||||
use_custom_title_safe_ratio(false),
|
||||
custom_title_safe_ratio(1),
|
||||
enable_drag_files_to_timeline(true),
|
||||
autoscale_by_default(false),
|
||||
recording_mode(2),
|
||||
enable_seek_to_import(false),
|
||||
enable_audio_scrubbing(true),
|
||||
drop_on_media_to_replace(true),
|
||||
autoscroll(AUTOSCROLL_PAGE_SCROLL),
|
||||
audio_rate(48000),
|
||||
fast_seeking(false),
|
||||
hover_focus(false),
|
||||
project_view_type(PROJECT_VIEW_TREE)
|
||||
enable_seek_to_import(false),
|
||||
enable_audio_scrubbing(true),
|
||||
drop_on_media_to_replace(true),
|
||||
autoscroll(AUTOSCROLL_PAGE_SCROLL),
|
||||
audio_rate(48000),
|
||||
fast_seeking(false),
|
||||
hover_focus(false),
|
||||
project_view_type(PROJECT_VIEW_TREE),
|
||||
set_name_with_marker(true),
|
||||
show_project_toolbar(false)
|
||||
{}
|
||||
|
||||
void Config::load(QString path) {
|
||||
QFile f(path);
|
||||
if (f.exists() && f.open(QIODevice::ReadOnly)) {
|
||||
QXmlStreamReader stream(&f);
|
||||
QFile f(path);
|
||||
if (f.exists() && f.open(QIODevice::ReadOnly)) {
|
||||
QXmlStreamReader stream(&f);
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
stream.readNext();
|
||||
if (stream.isStartElement()) {
|
||||
if (stream.name() == "SavedLayout") {
|
||||
stream.readNext();
|
||||
saved_layout = (stream.text() == "1");
|
||||
} else if (stream.name() == "ShowTrackLines") {
|
||||
stream.readNext();
|
||||
show_track_lines = (stream.text() == "1");
|
||||
} else if (stream.name() == "ScrollZooms") {
|
||||
stream.readNext();
|
||||
scroll_zooms = (stream.text() == "1");
|
||||
} else if (stream.name() == "EditToolSelectsLinks") {
|
||||
stream.readNext();
|
||||
edit_tool_selects_links = (stream.text() == "1");
|
||||
} else if (stream.name() == "EditToolAlsoSeeks") {
|
||||
stream.readNext();
|
||||
edit_tool_also_seeks = (stream.text() == "1");
|
||||
} else if (stream.name() == "SelectAlsoSeeks") {
|
||||
stream.readNext();
|
||||
select_also_seeks = (stream.text() == "1");
|
||||
} else if (stream.name() == "PasteSeeks") {
|
||||
stream.readNext();
|
||||
paste_seeks = (stream.text() == "1");
|
||||
} else if (stream.name() == "ImageSequenceFormats") {
|
||||
stream.readNext();
|
||||
img_seq_formats = stream.text().toString();
|
||||
while (!stream.atEnd()) {
|
||||
stream.readNext();
|
||||
if (stream.isStartElement()) {
|
||||
if (stream.name() == "SavedLayout") {
|
||||
stream.readNext();
|
||||
saved_layout = (stream.text() == "1");
|
||||
} else if (stream.name() == "ShowTrackLines") {
|
||||
stream.readNext();
|
||||
show_track_lines = (stream.text() == "1");
|
||||
} else if (stream.name() == "ScrollZooms") {
|
||||
stream.readNext();
|
||||
scroll_zooms = (stream.text() == "1");
|
||||
} else if (stream.name() == "EditToolSelectsLinks") {
|
||||
stream.readNext();
|
||||
edit_tool_selects_links = (stream.text() == "1");
|
||||
} else if (stream.name() == "EditToolAlsoSeeks") {
|
||||
stream.readNext();
|
||||
edit_tool_also_seeks = (stream.text() == "1");
|
||||
} else if (stream.name() == "SelectAlsoSeeks") {
|
||||
stream.readNext();
|
||||
select_also_seeks = (stream.text() == "1");
|
||||
} else if (stream.name() == "PasteSeeks") {
|
||||
stream.readNext();
|
||||
paste_seeks = (stream.text() == "1");
|
||||
} else if (stream.name() == "ImageSequenceFormats") {
|
||||
stream.readNext();
|
||||
img_seq_formats = stream.text().toString();
|
||||
} else if (stream.name() == "RectifiedWaveforms") {
|
||||
stream.readNext();
|
||||
stream.readNext();
|
||||
rectified_waveforms = (stream.text() == "1");
|
||||
} else if (stream.name() == "DefaultTransitionLength") {
|
||||
stream.readNext();
|
||||
default_transition_length = stream.text().toInt();
|
||||
} else if (stream.name() == "TimecodeView") {
|
||||
stream.readNext();
|
||||
timecode_view = stream.text().toInt();
|
||||
}else if (stream.name() == "ShowTitleSafeArea") {
|
||||
stream.readNext();
|
||||
show_title_safe_area = (stream.text() == "1");
|
||||
} else if (stream.name() == "UseCustomTitleSafeRatio") {
|
||||
stream.readNext();
|
||||
use_custom_title_safe_ratio = (stream.text() == "1");
|
||||
} else if (stream.name() == "CustomTitleSafeRatio") {
|
||||
stream.readNext();
|
||||
custom_title_safe_ratio = stream.text().toDouble();
|
||||
stream.readNext();
|
||||
default_transition_length = stream.text().toInt();
|
||||
} else if (stream.name() == "TimecodeView") {
|
||||
stream.readNext();
|
||||
timecode_view = stream.text().toInt();
|
||||
} else if (stream.name() == "ShowTitleSafeArea") {
|
||||
stream.readNext();
|
||||
show_title_safe_area = (stream.text() == "1");
|
||||
} else if (stream.name() == "UseCustomTitleSafeRatio") {
|
||||
stream.readNext();
|
||||
use_custom_title_safe_ratio = (stream.text() == "1");
|
||||
} else if (stream.name() == "CustomTitleSafeRatio") {
|
||||
stream.readNext();
|
||||
custom_title_safe_ratio = stream.text().toDouble();
|
||||
} else if (stream.name() == "EnableDragFilesToTimeline") {
|
||||
stream.readNext();
|
||||
enable_drag_files_to_timeline = (stream.text() == "1");;
|
||||
} else if (stream.name() == "AutoscaleByDefault") {
|
||||
stream.readNext();
|
||||
} else if (stream.name() == "AutoscaleByDefault") {
|
||||
stream.readNext();
|
||||
autoscale_by_default = (stream.text() == "1");
|
||||
} else if (stream.name() == "RecordingMode") {
|
||||
stream.readNext();
|
||||
@@ -98,78 +103,86 @@ void Config::load(QString path) {
|
||||
} else if (stream.name() == "EnableSeekToImport") {
|
||||
stream.readNext();
|
||||
enable_seek_to_import = (stream.text() == "1");
|
||||
} else if (stream.name() == "AudioScrubbing") {
|
||||
stream.readNext();
|
||||
enable_audio_scrubbing = (stream.text() == "1");
|
||||
} else if (stream.name() == "DropFileOnMediaToReplace") {
|
||||
stream.readNext();
|
||||
drop_on_media_to_replace = (stream.text() == "1");
|
||||
} else if (stream.name() == "Autoscroll") {
|
||||
stream.readNext();
|
||||
autoscroll = stream.text().toInt();
|
||||
} else if (stream.name() == "AudioRate") {
|
||||
stream.readNext();
|
||||
audio_rate = stream.text().toInt();
|
||||
} else if (stream.name() == "FastSeeking") {
|
||||
stream.readNext();
|
||||
fast_seeking = (stream.text() == "1");
|
||||
} else if (stream.name() == "HoverFocus") {
|
||||
stream.readNext();
|
||||
hover_focus = (stream.text() == "1");
|
||||
} else if (stream.name() == "ProjectViewType") {
|
||||
stream.readNext();
|
||||
project_view_type = stream.text().toInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stream.hasError()) {
|
||||
} else if (stream.name() == "AudioScrubbing") {
|
||||
stream.readNext();
|
||||
enable_audio_scrubbing = (stream.text() == "1");
|
||||
} else if (stream.name() == "DropFileOnMediaToReplace") {
|
||||
stream.readNext();
|
||||
drop_on_media_to_replace = (stream.text() == "1");
|
||||
} else if (stream.name() == "Autoscroll") {
|
||||
stream.readNext();
|
||||
autoscroll = stream.text().toInt();
|
||||
} else if (stream.name() == "AudioRate") {
|
||||
stream.readNext();
|
||||
audio_rate = stream.text().toInt();
|
||||
} else if (stream.name() == "FastSeeking") {
|
||||
stream.readNext();
|
||||
fast_seeking = (stream.text() == "1");
|
||||
} else if (stream.name() == "HoverFocus") {
|
||||
stream.readNext();
|
||||
hover_focus = (stream.text() == "1");
|
||||
} else if (stream.name() == "ProjectViewType") {
|
||||
stream.readNext();
|
||||
project_view_type = stream.text().toInt();
|
||||
} else if (stream.name() == "SetNameWithMarker") {
|
||||
stream.readNext();
|
||||
set_name_with_marker = (stream.text() == "1");
|
||||
} else if (stream.name() == "ShowProjectToolbar") {
|
||||
stream.readNext();
|
||||
show_project_toolbar = (stream.text() == "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stream.hasError()) {
|
||||
dout << "[ERROR] Error parsing config XML." << stream.errorString();
|
||||
}
|
||||
}
|
||||
|
||||
f.close();
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Config::save(QString path) {
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
dout << "[ERROR] Could not save configuration";
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QXmlStreamWriter stream(&f);
|
||||
stream.setAutoFormatting(true);
|
||||
stream.writeStartDocument(); // doc
|
||||
stream.writeStartElement("Configuration"); // configuration
|
||||
QXmlStreamWriter stream(&f);
|
||||
stream.setAutoFormatting(true);
|
||||
stream.writeStartDocument(); // doc
|
||||
stream.writeStartElement("Configuration"); // configuration
|
||||
|
||||
stream.writeTextElement("Version", QString::number(SAVE_VERSION));
|
||||
stream.writeTextElement("SavedLayout", QString::number(saved_layout));
|
||||
stream.writeTextElement("ShowTrackLines", QString::number(show_track_lines));
|
||||
stream.writeTextElement("ScrollZooms", QString::number(scroll_zooms));
|
||||
stream.writeTextElement("EditToolSelectsLinks", QString::number(edit_tool_selects_links));
|
||||
stream.writeTextElement("EditToolAlsoSeeks", QString::number(edit_tool_also_seeks));
|
||||
stream.writeTextElement("SelectAlsoSeeks", QString::number(select_also_seeks));
|
||||
stream.writeTextElement("PasteSeeks", QString::number(paste_seeks));
|
||||
stream.writeTextElement("ImageSequenceFormats", img_seq_formats);
|
||||
stream.writeTextElement("SavedLayout", QString::number(saved_layout));
|
||||
stream.writeTextElement("ShowTrackLines", QString::number(show_track_lines));
|
||||
stream.writeTextElement("ScrollZooms", QString::number(scroll_zooms));
|
||||
stream.writeTextElement("EditToolSelectsLinks", QString::number(edit_tool_selects_links));
|
||||
stream.writeTextElement("EditToolAlsoSeeks", QString::number(edit_tool_also_seeks));
|
||||
stream.writeTextElement("SelectAlsoSeeks", QString::number(select_also_seeks));
|
||||
stream.writeTextElement("PasteSeeks", QString::number(paste_seeks));
|
||||
stream.writeTextElement("ImageSequenceFormats", img_seq_formats);
|
||||
stream.writeTextElement("RectifiedWaveforms", QString::number(rectified_waveforms));
|
||||
stream.writeTextElement("DefaultTransitionLength", QString::number(default_transition_length));
|
||||
stream.writeTextElement("TimecodeView", QString::number(timecode_view));
|
||||
stream.writeTextElement("ShowTitleSafeArea", QString::number(show_title_safe_area));
|
||||
stream.writeTextElement("UseCustomTitleSafeRatio", QString::number(use_custom_title_safe_ratio));
|
||||
stream.writeTextElement("CustomTitleSafeRatio", QString::number(custom_title_safe_ratio));
|
||||
stream.writeTextElement("TimecodeView", QString::number(timecode_view));
|
||||
stream.writeTextElement("ShowTitleSafeArea", QString::number(show_title_safe_area));
|
||||
stream.writeTextElement("UseCustomTitleSafeRatio", QString::number(use_custom_title_safe_ratio));
|
||||
stream.writeTextElement("CustomTitleSafeRatio", QString::number(custom_title_safe_ratio));
|
||||
stream.writeTextElement("EnableDragFilesToTimeline", QString::number(enable_drag_files_to_timeline));
|
||||
stream.writeTextElement("AutoscaleByDefault", QString::number(autoscale_by_default));
|
||||
stream.writeTextElement("AutoscaleByDefault", QString::number(autoscale_by_default));
|
||||
stream.writeTextElement("RecordingMode", QString::number(recording_mode));
|
||||
stream.writeTextElement("EnableSeekToImport", QString::number(enable_seek_to_import));
|
||||
stream.writeTextElement("AudioScrubbing", QString::number(enable_audio_scrubbing));
|
||||
stream.writeTextElement("DropFileOnMediaToReplace", QString::number(drop_on_media_to_replace));
|
||||
stream.writeTextElement("Autoscroll", QString::number(autoscroll));
|
||||
stream.writeTextElement("AudioRate", QString::number(audio_rate));
|
||||
stream.writeTextElement("FastSeeking", QString::number(fast_seeking));
|
||||
stream.writeTextElement("HoverFocus", QString::number(hover_focus));
|
||||
stream.writeTextElement("ProjectViewType", QString::number(project_view_type));
|
||||
stream.writeTextElement("AudioScrubbing", QString::number(enable_audio_scrubbing));
|
||||
stream.writeTextElement("DropFileOnMediaToReplace", QString::number(drop_on_media_to_replace));
|
||||
stream.writeTextElement("Autoscroll", QString::number(autoscroll));
|
||||
stream.writeTextElement("AudioRate", QString::number(audio_rate));
|
||||
stream.writeTextElement("FastSeeking", QString::number(fast_seeking));
|
||||
stream.writeTextElement("HoverFocus", QString::number(hover_focus));
|
||||
stream.writeTextElement("ProjectViewType", QString::number(project_view_type));
|
||||
stream.writeTextElement("SetNameWithMarker", QString::number(set_name_with_marker));
|
||||
stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project->toolbar_widget->isVisible()));
|
||||
|
||||
stream.writeEndElement(); // configuration
|
||||
stream.writeEndDocument(); // doc
|
||||
stream.writeEndDocument(); // doc
|
||||
f.close();
|
||||
}
|
||||
|
||||
+27
-25
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
#define SAVE_VERSION 181124 // YYMMDD
|
||||
#define MIN_SAVE_VERSION 181114 // lowest compatible project version
|
||||
#define SAVE_VERSION 190104 // YYMMDD
|
||||
#define MIN_SAVE_VERSION 190104 // lowest compatible project version
|
||||
|
||||
#define TIMECODE_DROP 0
|
||||
#define TIMECODE_NONDROP 1
|
||||
@@ -22,35 +22,37 @@
|
||||
#define PROJECT_VIEW_ICON 1
|
||||
|
||||
struct Config {
|
||||
Config();
|
||||
bool saved_layout;
|
||||
bool show_track_lines;
|
||||
bool scroll_zooms;
|
||||
bool edit_tool_selects_links;
|
||||
bool edit_tool_also_seeks;
|
||||
bool select_also_seeks;
|
||||
bool paste_seeks;
|
||||
QString img_seq_formats;
|
||||
Config();
|
||||
bool saved_layout;
|
||||
bool show_track_lines;
|
||||
bool scroll_zooms;
|
||||
bool edit_tool_selects_links;
|
||||
bool edit_tool_also_seeks;
|
||||
bool select_also_seeks;
|
||||
bool paste_seeks;
|
||||
QString img_seq_formats;
|
||||
bool rectified_waveforms;
|
||||
int default_transition_length;
|
||||
int timecode_view;
|
||||
bool show_title_safe_area;
|
||||
bool use_custom_title_safe_ratio;
|
||||
double custom_title_safe_ratio;
|
||||
int timecode_view;
|
||||
bool show_title_safe_area;
|
||||
bool use_custom_title_safe_ratio;
|
||||
double custom_title_safe_ratio;
|
||||
bool enable_drag_files_to_timeline;
|
||||
bool autoscale_by_default;
|
||||
bool autoscale_by_default;
|
||||
int recording_mode;
|
||||
bool enable_seek_to_import;
|
||||
bool enable_audio_scrubbing;
|
||||
bool drop_on_media_to_replace;
|
||||
int autoscroll;
|
||||
int audio_rate;
|
||||
bool fast_seeking;
|
||||
bool hover_focus;
|
||||
int project_view_type;
|
||||
bool enable_audio_scrubbing;
|
||||
bool drop_on_media_to_replace;
|
||||
int autoscroll;
|
||||
int audio_rate;
|
||||
bool fast_seeking;
|
||||
bool hover_focus;
|
||||
int project_view_type;
|
||||
bool set_name_with_marker;
|
||||
bool show_project_toolbar;
|
||||
|
||||
void load(QString path);
|
||||
void save(QString path);
|
||||
void load(QString path);
|
||||
void save(QString path);
|
||||
};
|
||||
|
||||
extern Config config;
|
||||
|
||||
+38
-55
@@ -12,9 +12,8 @@
|
||||
#include "debug.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
@@ -25,30 +24,11 @@ extern "C" {
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QPainter>
|
||||
|
||||
AVFormatContext* fmt_ctx = NULL;
|
||||
AVStream* video_stream;
|
||||
AVCodec* vcodec;
|
||||
AVCodecContext* vcodec_ctx;
|
||||
AVFrame* video_frame;
|
||||
AVFrame* sws_frame;
|
||||
SwsContext* sws_ctx = NULL;
|
||||
AVStream* audio_stream;
|
||||
AVCodec* acodec;
|
||||
AVFrame* audio_frame;
|
||||
AVFrame* swr_frame;
|
||||
AVCodecContext* acodec_ctx;
|
||||
AVPacket video_pkt;
|
||||
AVPacket audio_pkt;
|
||||
SwrContext* swr_ctx = NULL;
|
||||
int aframe_bytes;
|
||||
int ret;
|
||||
char* c_filename;
|
||||
|
||||
ExportThread::ExportThread() : continueEncode(true) {
|
||||
surface.create();
|
||||
}
|
||||
|
||||
bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream) {
|
||||
bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale) {
|
||||
ret = avcodec_send_frame(codec_ctx, frame);
|
||||
if (ret < 0) {
|
||||
dout << "[ERROR] Failed to send frame to encoder." << ret;
|
||||
@@ -69,6 +49,7 @@ bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx,
|
||||
}
|
||||
|
||||
packet->stream_index = stream->index;
|
||||
if (rescale) av_packet_rescale_ts(packet, codec_ctx->time_base, stream->time_base);
|
||||
av_interleaved_write_frame(ofmt_ctx, packet);
|
||||
av_packet_unref(packet);
|
||||
}
|
||||
@@ -98,7 +79,7 @@ bool ExportThread::setupVideo() {
|
||||
|
||||
// allocate context
|
||||
// vcodec_ctx = video_stream->codec;
|
||||
vcodec_ctx = avcodec_alloc_context3(vcodec);
|
||||
vcodec_ctx = avcodec_alloc_context3(vcodec);
|
||||
if (!vcodec_ctx) {
|
||||
dout << "[ERROR] Could not allocate video encoding context";
|
||||
ed->export_error = "could not allocate video encoding context";
|
||||
@@ -107,6 +88,7 @@ bool ExportThread::setupVideo() {
|
||||
|
||||
// setup context
|
||||
vcodec_ctx->codec_id = static_cast<AVCodecID>(video_codec);
|
||||
vcodec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
vcodec_ctx->width = video_width;
|
||||
vcodec_ctx->height = video_height;
|
||||
vcodec_ctx->sample_aspect_ratio = {1, 1};
|
||||
@@ -122,10 +104,10 @@ bool ExportThread::setupVideo() {
|
||||
|
||||
if (vcodec_ctx->codec_id == AV_CODEC_ID_H264) {
|
||||
/*char buffer[50];
|
||||
itoa(vcodec_ctx, buffer, 10);*/
|
||||
itoa(vcodec_ctx, buffer, 10);*/
|
||||
|
||||
//av_opt_set(vcodec_ctx->priv_data, "preset", "fast", AV_OPT_SEARCH_CHILDREN);
|
||||
//av_opt_set(vcodec_ctx->priv_data, "x264opts", "opencl", AV_OPT_SEARCH_CHILDREN);
|
||||
//av_opt_set(vcodec_ctx->priv_data, "preset", "fast", AV_OPT_SEARCH_CHILDREN);
|
||||
//av_opt_set(vcodec_ctx->priv_data, "x264opts", "opencl", AV_OPT_SEARCH_CHILDREN);
|
||||
|
||||
switch (video_compression_type) {
|
||||
case COMPRESSION_TYPE_CFR:
|
||||
@@ -204,7 +186,7 @@ bool ExportThread::setupAudio() {
|
||||
|
||||
// allocate context
|
||||
// acodec_ctx = audio_stream->codec;
|
||||
acodec_ctx = avcodec_alloc_context3(acodec);
|
||||
acodec_ctx = avcodec_alloc_context3(acodec);
|
||||
if (!acodec_ctx) {
|
||||
dout << "[ERROR] Could not find allocate audio encoding context";
|
||||
ed->export_error = "could not allocate audio encoding context";
|
||||
@@ -213,6 +195,7 @@ bool ExportThread::setupAudio() {
|
||||
|
||||
// setup context
|
||||
acodec_ctx->codec_id = static_cast<AVCodecID>(audio_codec);
|
||||
acodec_ctx->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
acodec_ctx->sample_rate = audio_sampling_rate;
|
||||
acodec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is what the user sets the output audio to)
|
||||
acodec_ctx->channels = av_get_channel_layout_nb_channels(acodec_ctx->channel_layout);
|
||||
@@ -261,7 +244,7 @@ bool ExportThread::setupAudio() {
|
||||
audio_frame = av_frame_alloc();
|
||||
audio_frame->sample_rate = sequence->audio_frequency;
|
||||
audio_frame->nb_samples = acodec_ctx->frame_size;
|
||||
if (audio_frame->nb_samples == 0) audio_frame->nb_samples = 2048; // should possibly be smaller?
|
||||
if (audio_frame->nb_samples == 0) audio_frame->nb_samples = 256; // should possibly be smaller?
|
||||
audio_frame->format = AV_SAMPLE_FMT_S16;
|
||||
audio_frame->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is whatever format they're held in the internal buffer)
|
||||
audio_frame->channels = av_get_channel_layout_nb_channels(audio_frame->channel_layout);
|
||||
@@ -295,7 +278,7 @@ bool ExportThread::setupContainer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// av_dump_format(fmt_ctx, 0, c_filename, 1);
|
||||
//av_dump_format(fmt_ctx, 0, c_filename, 1);
|
||||
|
||||
ret = avio_open(&fmt_ctx->pb, c_filename, AVIO_FLAG_WRITE);
|
||||
if (ret < 0) {
|
||||
@@ -323,9 +306,9 @@ void ExportThread::run() {
|
||||
|
||||
continueEncode = setupContainer();
|
||||
|
||||
if (video_enabled && continueEncode) continueEncode = setupVideo();
|
||||
if (video_enabled && continueEncode) continueEncode = setupVideo();
|
||||
|
||||
if (audio_enabled && continueEncode) continueEncode = setupAudio();
|
||||
if (audio_enabled && continueEncode) continueEncode = setupAudio();
|
||||
|
||||
if (continueEncode) {
|
||||
ret = avformat_write_header(fmt_ctx, NULL);
|
||||
@@ -345,11 +328,11 @@ void ExportThread::run() {
|
||||
panel_sequence_viewer->viewer_widget->default_fbo = &fbo;
|
||||
|
||||
long file_audio_samples = 0;
|
||||
qint64 start_time, frame_time, avg_time, eta, total_time = 0;
|
||||
long remaining_frames, frame_count = 1;
|
||||
qint64 start_time, frame_time, avg_time, eta, total_time = 0;
|
||||
long remaining_frames, frame_count = 1;
|
||||
|
||||
while (sequence->playhead < end_frame && continueEncode) {
|
||||
start_time = QDateTime::currentMSecsSinceEpoch();
|
||||
while (sequence->playhead <= end_frame && continueEncode) {
|
||||
start_time = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
panel_sequence_viewer->viewer_widget->paintGL();
|
||||
|
||||
@@ -360,10 +343,10 @@ void ExportThread::run() {
|
||||
|
||||
// change pixel format
|
||||
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
|
||||
sws_frame->pts = round(timecode_secs/av_q2d(video_stream->time_base));
|
||||
sws_frame->pts = qRound(timecode_secs/av_q2d(video_stream->time_base));
|
||||
|
||||
// send to encoder
|
||||
if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream)) continueEncode = false;
|
||||
// send to encoder
|
||||
if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream, false)) continueEncode = false;
|
||||
}
|
||||
if (audio_enabled) {
|
||||
// do we need to encode more audio samples?
|
||||
@@ -386,25 +369,25 @@ void ExportThread::run() {
|
||||
swr_convert_frame(swr_ctx, swr_frame, audio_frame);
|
||||
swr_frame->pts = file_audio_samples;
|
||||
|
||||
// send to encoder
|
||||
if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream)) continueEncode = false;
|
||||
// send to encoder
|
||||
if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream, true)) continueEncode = false;
|
||||
|
||||
file_audio_samples += swr_frame->nb_samples;
|
||||
}
|
||||
}
|
||||
|
||||
// encoding stats
|
||||
frame_time = (QDateTime::currentMSecsSinceEpoch()-start_time);
|
||||
total_time += frame_time;
|
||||
remaining_frames = (end_frame-sequence->playhead);
|
||||
avg_time = (total_time/frame_count);
|
||||
eta = (remaining_frames*avg_time);
|
||||
// encoding stats
|
||||
frame_time = (QDateTime::currentMSecsSinceEpoch()-start_time);
|
||||
total_time += frame_time;
|
||||
remaining_frames = (end_frame-sequence->playhead);
|
||||
avg_time = (total_time/frame_count);
|
||||
eta = (remaining_frames*avg_time);
|
||||
|
||||
// dout << "[INFO] Encoded frame" << sequence->playhead << "- took" << frame_time << "ms (avg:" << avg_time << "ms, remaining:" << remaining_frames << ", ETA:" << eta << ")";
|
||||
|
||||
emit progress_changed(qRound(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100), eta);
|
||||
emit progress_changed(qRound(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100), eta);
|
||||
sequence->playhead++;
|
||||
frame_count++;
|
||||
frame_count++;
|
||||
}
|
||||
|
||||
panel_sequence_viewer->viewer_widget->default_fbo = NULL;
|
||||
@@ -418,7 +401,7 @@ void ExportThread::run() {
|
||||
swr_convert_frame(swr_ctx, swr_frame, NULL);
|
||||
if (swr_frame->nb_samples == 0) break;
|
||||
swr_frame->pts = file_audio_samples;
|
||||
if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream)) continueEncode = false;
|
||||
if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream, true)) continueEncode = false;
|
||||
file_audio_samples += swr_frame->nb_samples;
|
||||
} while (swr_frame->nb_samples > 0);
|
||||
}
|
||||
@@ -428,8 +411,8 @@ void ExportThread::run() {
|
||||
if (continueEncode) {
|
||||
// flush remaining packets
|
||||
while (continueVideo && continueAudio) {
|
||||
if (continueVideo && video_enabled) continueVideo = encode(fmt_ctx, vcodec_ctx, NULL, &video_pkt, video_stream);
|
||||
if (continueAudio && audio_enabled) continueAudio = encode(fmt_ctx, acodec_ctx, NULL, &audio_pkt, audio_stream);
|
||||
if (continueVideo && video_enabled) continueVideo = encode(fmt_ctx, vcodec_ctx, NULL, &video_pkt, video_stream, false);
|
||||
if (continueAudio && audio_enabled) continueAudio = encode(fmt_ctx, acodec_ctx, NULL, &audio_pkt, audio_stream, true);
|
||||
}
|
||||
|
||||
ret = av_write_trailer(fmt_ctx);
|
||||
@@ -439,7 +422,7 @@ void ExportThread::run() {
|
||||
continueEncode = false;
|
||||
}
|
||||
|
||||
emit progress_changed(100, 0);
|
||||
emit progress_changed(100, 0);
|
||||
}
|
||||
|
||||
avio_closep(&fmt_ctx->pb);
|
||||
@@ -448,14 +431,14 @@ void ExportThread::run() {
|
||||
avcodec_close(vcodec_ctx);
|
||||
av_packet_unref(&video_pkt);
|
||||
av_frame_free(&video_frame);
|
||||
avcodec_free_context(&vcodec_ctx);
|
||||
avcodec_free_context(&vcodec_ctx);
|
||||
}
|
||||
|
||||
if (audio_enabled) {
|
||||
avcodec_close(acodec_ctx);
|
||||
av_packet_unref(&audio_pkt);
|
||||
av_frame_free(&audio_frame);
|
||||
avcodec_free_context(&acodec_ctx);
|
||||
avcodec_free_context(&acodec_ctx);
|
||||
}
|
||||
|
||||
avformat_free_context(fmt_ctx);
|
||||
|
||||
+31
-5
@@ -10,6 +10,13 @@ struct AVCodecContext;
|
||||
struct AVFrame;
|
||||
struct AVPacket;
|
||||
struct AVStream;
|
||||
struct AVCodec;
|
||||
struct SwsContext;
|
||||
struct SwrContext;
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#define COMPRESSION_TYPE_CBR 0
|
||||
#define COMPRESSION_TYPE_CFR 1
|
||||
@@ -20,7 +27,7 @@ class ExportThread : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportThread();
|
||||
void run();
|
||||
void run();
|
||||
|
||||
// export parameters
|
||||
QString filename;
|
||||
@@ -35,8 +42,8 @@ public:
|
||||
int audio_codec;
|
||||
int audio_sampling_rate;
|
||||
int audio_bitrate;
|
||||
long start_frame;
|
||||
long end_frame;
|
||||
long start_frame;
|
||||
long end_frame;
|
||||
|
||||
QOffscreenSurface surface;
|
||||
|
||||
@@ -44,12 +51,31 @@ public:
|
||||
|
||||
bool continueEncode;
|
||||
signals:
|
||||
void progress_changed(int value, qint64 remaining_ms);
|
||||
void progress_changed(int value, qint64 remaining_ms);
|
||||
private:
|
||||
bool encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream);
|
||||
bool encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale);
|
||||
bool setupVideo();
|
||||
bool setupAudio();
|
||||
bool setupContainer();
|
||||
|
||||
AVFormatContext* fmt_ctx = NULL;
|
||||
AVStream* video_stream;
|
||||
AVCodec* vcodec;
|
||||
AVCodecContext* vcodec_ctx;
|
||||
AVFrame* video_frame;
|
||||
AVFrame* sws_frame;
|
||||
SwsContext* sws_ctx = NULL;
|
||||
AVStream* audio_stream;
|
||||
AVCodec* acodec;
|
||||
AVFrame* audio_frame;
|
||||
AVFrame* swr_frame;
|
||||
AVCodecContext* acodec_ctx;
|
||||
AVPacket video_pkt;
|
||||
AVPacket audio_pkt;
|
||||
SwrContext* swr_ctx = NULL;
|
||||
int aframe_bytes;
|
||||
int ret;
|
||||
char* c_filename;
|
||||
};
|
||||
|
||||
#endif // EXPORTTHREAD_H
|
||||
|
||||
+569
-567
File diff suppressed because it is too large
Load Diff
+42
-3
@@ -1,15 +1,54 @@
|
||||
#include "math.h"
|
||||
|
||||
#include <QtMath>
|
||||
#include <QVector>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
int lerp(int a, int b, double t) {
|
||||
return qRound(((1.0 - t) * a) + (t * b));
|
||||
return qRound(((1.0 - t) * a) + (t * b));
|
||||
}
|
||||
|
||||
float float_lerp(float a, float b, float t) {
|
||||
return ((1.0F - t) * a) + (t * b);
|
||||
return ((1.0F - t) * a) + (t * b);
|
||||
}
|
||||
|
||||
double double_lerp(double a, double b, double t) {
|
||||
return ((1.0 - t) * a) + (t * b);
|
||||
return ((1.0 - t) * a) + (t * b);
|
||||
}
|
||||
|
||||
double quad_from_t(double a, double b, double c, double t) {
|
||||
return qPow(1.0 - t, 2)*a + 2*(1.0 - t)*t*b + qPow(t, 2)*c;
|
||||
}
|
||||
|
||||
double quad_t_from_x(double x, double a, double b, double c) {
|
||||
return (a - b + qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c);
|
||||
// alt: return (a - b - qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c);
|
||||
}
|
||||
|
||||
double cubic_from_t(double a, double b, double c, double d, double t) {
|
||||
return qPow(1.0 - t, 3)*a + 3*qPow(1.0 - t, 2)*t*b + 3*(1.0 - t)*qPow(t, 2)*c + qPow(t, 3)*d;
|
||||
}
|
||||
|
||||
double cubic_t_from_x(double x_target, double a, double b, double c, double d) {
|
||||
double tolerance = 0.0001;
|
||||
|
||||
double lower = 0.0;
|
||||
double upper = 1.0;
|
||||
|
||||
double percent = 0.5;
|
||||
double x = cubic_from_t(a, b, c, d, percent);
|
||||
|
||||
while (qAbs(x_target - x) > tolerance) {
|
||||
if (x_target > x) {
|
||||
lower = percent;
|
||||
} else {
|
||||
upper = percent;
|
||||
}
|
||||
|
||||
percent = (upper + lower) / 2.0;
|
||||
x = cubic_from_t(a, b, c, d, percent);
|
||||
}
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
@@ -4,5 +4,10 @@
|
||||
int lerp(int a, int b, double t);
|
||||
float float_lerp(float a, float b, float t);
|
||||
double double_lerp(double a, double b, double t);
|
||||
double quad_from_t(double a, double b, double c, double t);
|
||||
double quad_t_from_x(double x, double a, double b, double c);
|
||||
double cubic_from_t(double a, double b, double c, double d, double t);
|
||||
double cubic_t_from_x(double x_target, double a, double b, double c, double d);
|
||||
double solveCubicBezier(double p0, double p1, double p2, double p3, double x);
|
||||
|
||||
#endif // MATH_H
|
||||
|
||||
+19
-10
@@ -9,17 +9,26 @@ QString real_app_dir;
|
||||
|
||||
QString get_app_dir() {
|
||||
if (real_app_dir.isEmpty()) {
|
||||
QString app_path = QCoreApplication::applicationFilePath();
|
||||
real_app_dir = app_path.left(app_path.lastIndexOf('/'));
|
||||
}
|
||||
return real_app_dir;
|
||||
QString app_path = QCoreApplication::applicationFilePath();
|
||||
real_app_dir = app_path.left(app_path.lastIndexOf('/'));
|
||||
}
|
||||
return real_app_dir;
|
||||
}
|
||||
|
||||
QString get_data_path() {
|
||||
QString app_dir = get_app_dir();
|
||||
if (QFileInfo::exists(app_dir + "/portable")) {
|
||||
return app_dir;
|
||||
} else {
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
}
|
||||
QString app_dir = get_app_dir();
|
||||
if (QFileInfo::exists(app_dir + "/portable")) {
|
||||
return app_dir;
|
||||
} else {
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
}
|
||||
}
|
||||
|
||||
QString get_config_path() {
|
||||
QString app_dir = get_app_dir();
|
||||
if (QFileInfo::exists(app_dir + "/portable")) {
|
||||
return app_dir;
|
||||
} else {
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
|
||||
QString get_app_dir();
|
||||
QString get_data_path();
|
||||
QString get_config_path();
|
||||
|
||||
#endif // PATH_H
|
||||
|
||||
+57
-48
@@ -55,14 +55,13 @@ void PreviewGenerator::parse_media() {
|
||||
if (avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id) == NULL) {
|
||||
dout << "[ERROR] Unsupported codec in stream" << i << "of file" << footage->name;
|
||||
} else {
|
||||
FootageStream* ms = footage->get_stream_from_file_index(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO, i);
|
||||
FootageStream ms;
|
||||
ms.preview_done = false;
|
||||
ms.file_index = i;
|
||||
ms.enabled = true;
|
||||
|
||||
bool append = false;
|
||||
if (ms == NULL) {
|
||||
ms = new FootageStream();
|
||||
ms->preview_done = false;
|
||||
ms->file_index = i;
|
||||
append = true;
|
||||
}
|
||||
|
||||
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|
||||
&& fmt_ctx->streams[i]->codecpar->width > 0
|
||||
&& fmt_ctx->streams[i]->codecpar->height > 0) {
|
||||
@@ -76,33 +75,43 @@ void PreviewGenerator::parse_media() {
|
||||
// heuristic to determine if video is a still image
|
||||
if (fmt_ctx->streams[i]->avg_frame_rate.den == 0
|
||||
&& fmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_DNXHD) { // silly hack but this is the only scenario i've seen this
|
||||
ms->infinite_length = true;
|
||||
ms.infinite_length = true;
|
||||
contains_still_image = true;
|
||||
ms->video_frame_rate = 0;
|
||||
ms.video_frame_rate = 0;
|
||||
} else {
|
||||
ms->infinite_length = false;
|
||||
ms.infinite_length = false;
|
||||
if (fmt_ctx->streams[i]->r_frame_rate.den == 0) {
|
||||
ms->video_frame_rate = av_q2d(fmt_ctx->streams[i]->avg_frame_rate);
|
||||
ms.video_frame_rate = av_q2d(fmt_ctx->streams[i]->avg_frame_rate);
|
||||
} else {
|
||||
ms->video_frame_rate = av_q2d(fmt_ctx->streams[i]->r_frame_rate);
|
||||
ms.video_frame_rate = av_q2d(fmt_ctx->streams[i]->r_frame_rate);
|
||||
}
|
||||
}
|
||||
ms->video_width = fmt_ctx->streams[i]->codecpar->width;
|
||||
ms->video_height = fmt_ctx->streams[i]->codecpar->height;
|
||||
ms.video_width = fmt_ctx->streams[i]->codecpar->width;
|
||||
ms.video_height = fmt_ctx->streams[i]->codecpar->height;
|
||||
|
||||
// default value, we get the true value later in generate_waveform()
|
||||
ms->video_auto_interlacing = VIDEO_PROGRESSIVE;
|
||||
ms->video_interlacing = VIDEO_PROGRESSIVE;
|
||||
ms.video_auto_interlacing = VIDEO_PROGRESSIVE;
|
||||
ms.video_interlacing = VIDEO_PROGRESSIVE;
|
||||
|
||||
if (append) footage->video_tracks.append(ms);
|
||||
append = true;
|
||||
} else if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
ms->audio_channels = fmt_ctx->streams[i]->codecpar->channels;
|
||||
ms->audio_layout = fmt_ctx->streams[i]->codecpar->channel_layout;
|
||||
ms->audio_frequency = fmt_ctx->streams[i]->codecpar->sample_rate;
|
||||
if (append) footage->audio_tracks.append(ms);
|
||||
} else if (append) {
|
||||
delete ms;
|
||||
}
|
||||
ms.audio_channels = fmt_ctx->streams[i]->codecpar->channels;
|
||||
ms.audio_layout = fmt_ctx->streams[i]->codecpar->channel_layout;
|
||||
ms.audio_frequency = fmt_ctx->streams[i]->codecpar->sample_rate;
|
||||
|
||||
append = true;
|
||||
}
|
||||
|
||||
if (append) {
|
||||
QVector<FootageStream>& stream_list = (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? footage->audio_tracks : footage->video_tracks;
|
||||
for (int j=0;j<stream_list.size();j++) {
|
||||
if (stream_list.at(j).file_index == i) {
|
||||
stream_list[j] = ms;
|
||||
append = false;
|
||||
}
|
||||
}
|
||||
if (append) stream_list.append(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
footage->length = fmt_ctx->duration;
|
||||
@@ -123,32 +132,32 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
|
||||
|
||||
bool found = true;
|
||||
for (int i=0;i<footage->video_tracks.size();i++) {
|
||||
FootageStream* ms = footage->video_tracks.at(i);
|
||||
FootageStream& ms = footage->video_tracks[i];
|
||||
QString thumb_path = get_thumbnail_path(hash, ms);
|
||||
QFile f(thumb_path);
|
||||
if (f.exists() && ms->video_preview.load(thumb_path)) {
|
||||
if (f.exists() && ms.video_preview.load(thumb_path)) {
|
||||
//dout << "loaded thumb" << ms->file_index << "from" << thumb_path;
|
||||
ms->make_square_thumb();
|
||||
ms->preview_done = true;
|
||||
ms.make_square_thumb();
|
||||
ms.preview_done = true;
|
||||
} else {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<footage->audio_tracks.size();i++) {
|
||||
FootageStream* ms = footage->audio_tracks.at(i);
|
||||
FootageStream& ms = footage->audio_tracks[i];
|
||||
QString waveform_path = get_waveform_path(hash, ms);
|
||||
QFile f(waveform_path);
|
||||
if (f.exists()) {
|
||||
//dout << "loaded wave" << ms->file_index << "from" << waveform_path;
|
||||
f.open(QFile::ReadOnly);
|
||||
QByteArray data = f.readAll();
|
||||
ms->audio_preview.resize(data.size());
|
||||
ms.audio_preview.resize(data.size());
|
||||
for (int j=0;j<data.size();j++) {
|
||||
// faster way?
|
||||
ms->audio_preview[j] = data.at(j);
|
||||
ms.audio_preview[j] = data.at(j);
|
||||
}
|
||||
ms->preview_done = true;
|
||||
ms.preview_done = true;
|
||||
f.close();
|
||||
} else {
|
||||
found = false;
|
||||
@@ -157,13 +166,13 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
|
||||
}
|
||||
if (!found) {
|
||||
for (int i=0;i<footage->video_tracks.size();i++) {
|
||||
FootageStream* ms = footage->video_tracks.at(i);
|
||||
ms->preview_done = false;
|
||||
FootageStream& ms = footage->video_tracks[i];
|
||||
ms.preview_done = false;
|
||||
}
|
||||
for (int i=0;i<footage->audio_tracks.size();i++) {
|
||||
FootageStream* ms = footage->audio_tracks.at(i);
|
||||
ms->audio_preview.clear();
|
||||
ms->preview_done = false;
|
||||
FootageStream& ms = footage->audio_tracks[i];
|
||||
ms.audio_preview.clear();
|
||||
ms.preview_done = false;
|
||||
}
|
||||
}
|
||||
return !found;
|
||||
@@ -336,7 +345,7 @@ void PreviewGenerator::generate_waveform() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
s->audio_preview.append(min >> 8);
|
||||
s->audio_preview.append(min >> 8);
|
||||
s->audio_preview.append(max >> 8);
|
||||
if (cancelled) break;
|
||||
}
|
||||
@@ -359,7 +368,7 @@ void PreviewGenerator::generate_waveform() {
|
||||
} else if (footage->audio_tracks.size() == 0) {
|
||||
done = true;
|
||||
for (int i=0;i<footage->video_tracks.size();i++) {
|
||||
if (!footage->video_tracks.at(i)->preview_done) {
|
||||
if (!footage->video_tracks.at(i).preview_done) {
|
||||
done = false;
|
||||
break;
|
||||
}
|
||||
@@ -373,7 +382,7 @@ void PreviewGenerator::generate_waveform() {
|
||||
}
|
||||
}
|
||||
for (int i=0;i<footage->audio_tracks.size();i++) {
|
||||
footage->audio_tracks.at(i)->preview_done = true;
|
||||
footage->audio_tracks[i].preview_done = true;
|
||||
}
|
||||
av_frame_free(&temp_frame);
|
||||
av_packet_free(&packet);
|
||||
@@ -397,12 +406,12 @@ void PreviewGenerator::generate_waveform() {
|
||||
delete [] codec_ctx;
|
||||
}
|
||||
|
||||
QString PreviewGenerator::get_thumbnail_path(const QString& hash, FootageStream* ms) {
|
||||
return data_path + "/" + hash + "t" + QString::number(ms->file_index);
|
||||
QString PreviewGenerator::get_thumbnail_path(const QString& hash, const FootageStream& ms) {
|
||||
return data_path + "/" + hash + "t" + QString::number(ms.file_index);
|
||||
}
|
||||
|
||||
QString PreviewGenerator::get_waveform_path(const QString& hash, FootageStream* ms) {
|
||||
return data_path + "/" + hash + "w" + QString::number(ms->file_index);
|
||||
QString PreviewGenerator::get_waveform_path(const QString& hash, const FootageStream& ms) {
|
||||
return data_path + "/" + hash + "w" + QString::number(ms.file_index);
|
||||
}
|
||||
|
||||
void PreviewGenerator::run() {
|
||||
@@ -445,15 +454,15 @@ void PreviewGenerator::run() {
|
||||
|
||||
// save preview to file
|
||||
for (int i=0;i<footage->video_tracks.size();i++) {
|
||||
FootageStream* ms = footage->video_tracks.at(i);
|
||||
ms->video_preview.save(get_thumbnail_path(hash, ms), "PNG");
|
||||
FootageStream& ms = footage->video_tracks[i];
|
||||
ms.video_preview.save(get_thumbnail_path(hash, ms), "PNG");
|
||||
//dout << "saved" << ms->file_index << "thumbnail to" << get_thumbnail_path(hash, ms);
|
||||
}
|
||||
for (int i=0;i<footage->audio_tracks.size();i++) {
|
||||
FootageStream* ms = footage->audio_tracks.at(i);
|
||||
FootageStream& ms = footage->audio_tracks[i];
|
||||
QFile f(get_waveform_path(hash, ms));
|
||||
f.open(QFile::WriteOnly);
|
||||
f.write(ms->audio_preview.constData(), ms->audio_preview.size());
|
||||
f.write(ms.audio_preview.constData(), ms.audio_preview.size());
|
||||
f.close();
|
||||
//dout << "saved" << ms->file_index << "waveform to" << get_waveform_path(hash, ms);
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ private:
|
||||
bool replace;
|
||||
bool cancelled;
|
||||
QString data_path;
|
||||
QString get_thumbnail_path(const QString &hash, FootageStream* ms);
|
||||
QString get_waveform_path(const QString& hash, FootageStream* ms);
|
||||
QString get_thumbnail_path(const QString &hash, const FootageStream &ms);
|
||||
QString get_waveform_path(const QString& hash, const FootageStream &ms);
|
||||
};
|
||||
|
||||
#endif // PREVIEWGENERATOR_H
|
||||
|
||||
@@ -9,10 +9,11 @@ extern "C" {
|
||||
int main(int argc, char *argv[]) {
|
||||
// init ffmpeg subsystem
|
||||
av_register_all();
|
||||
avfilter_register_all();
|
||||
avfilter_register_all();
|
||||
|
||||
QApplication a(argc, argv);
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
if (argc > 1) w.launch_with_project(argv[1]);
|
||||
w.showMaximized();
|
||||
|
||||
return a.exec();
|
||||
|
||||
+187
-84
@@ -19,6 +19,7 @@
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/grapheditor.h"
|
||||
|
||||
#include "dialogs/aboutdialog.h"
|
||||
#include "dialogs/newsequencedialog.h"
|
||||
@@ -53,7 +54,7 @@ MainWindow* mainWindow;
|
||||
#define OLIVE_FILE_FILTER "Olive Project (*.ove)"
|
||||
|
||||
QTimer autorecovery_timer;
|
||||
QString config_dir;
|
||||
QString config_fn;
|
||||
QString appName;
|
||||
bool demoNoticeShown = false;
|
||||
|
||||
@@ -63,31 +64,28 @@ void MainWindow::setup_layout(bool reset) {
|
||||
panel_footage_viewer->show();
|
||||
panel_sequence_viewer->show();
|
||||
panel_timeline->show();
|
||||
panel_graph_editor->hide();
|
||||
|
||||
bool load_default = true;
|
||||
|
||||
/*if (!reset) {
|
||||
QFile panel_config(get_data_path() + "/layout");
|
||||
if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) {
|
||||
if (restoreState(panel_config.readAll(), 0)) {
|
||||
load_default = false;
|
||||
}
|
||||
panel_config.close();
|
||||
}
|
||||
}*/
|
||||
|
||||
if (load_default) {
|
||||
addDockWidget(Qt::TopDockWidgetArea, panel_project);
|
||||
addDockWidget(Qt::TopDockWidgetArea, panel_footage_viewer);
|
||||
tabifyDockWidget(panel_footage_viewer, panel_effect_controls);
|
||||
panel_footage_viewer->raise();
|
||||
addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer);
|
||||
addDockWidget(Qt::BottomDockWidgetArea, panel_timeline);
|
||||
addDockWidget(Qt::TopDockWidgetArea, panel_project);
|
||||
addDockWidget(Qt::TopDockWidgetArea, panel_footage_viewer);
|
||||
tabifyDockWidget(panel_footage_viewer, panel_effect_controls);
|
||||
panel_footage_viewer->raise();
|
||||
addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer);
|
||||
addDockWidget(Qt::BottomDockWidgetArea, panel_timeline);
|
||||
panel_graph_editor->setFloating(true);
|
||||
|
||||
// workaround for strange Qt dock bug (see https://bugreports.qt.io/browse/QTBUG-65592)
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
|
||||
resizeDocks({panel_project}, {40}, Qt::Horizontal);
|
||||
resizeDocks({panel_project}, {40}, Qt::Horizontal);
|
||||
#endif
|
||||
|
||||
// load panels from file
|
||||
if (!reset) {
|
||||
QFile panel_config(get_config_path() + "/layout");
|
||||
if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) {
|
||||
restoreState(panel_config.readAll(), 0);
|
||||
panel_config.close();
|
||||
}
|
||||
}
|
||||
|
||||
layout()->update();
|
||||
@@ -96,13 +94,15 @@ void MainWindow::setup_layout(bool reset) {
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent)
|
||||
{
|
||||
appName = "Olive (December 2018 | Alpha";
|
||||
appName = "Olive (January 2019 | Alpha";
|
||||
#ifdef GITHASH
|
||||
appName += " | ";
|
||||
appName += GITHASH;
|
||||
#endif
|
||||
appName += ")";
|
||||
|
||||
enable_launch_with_project = false;
|
||||
|
||||
setup_debug();
|
||||
|
||||
mainWindow = this;
|
||||
@@ -194,18 +194,19 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
config_dir = data_dir + "/config.xml";
|
||||
config.load(config_dir);
|
||||
}
|
||||
}
|
||||
QString config_path = get_config_path();
|
||||
if (!config_path.isEmpty()) {
|
||||
QDir config_dir(config_path);
|
||||
config_dir.mkpath(".");
|
||||
config_fn = config_path + "/config.xml";
|
||||
if (QFileInfo::exists(config_fn)) {
|
||||
config.load(config_fn);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO maybe replace these with non-pointers later on?
|
||||
panel_sequence_viewer = new Viewer(this);
|
||||
panel_footage_viewer = new Viewer(this);
|
||||
panel_project = new Project(this);
|
||||
panel_effect_controls = new EffectControls(this);
|
||||
panel_timeline = new Timeline(this);
|
||||
alloc_panels(this);
|
||||
|
||||
QStatusBar* statusBar = new QStatusBar(this);
|
||||
statusBar->showMessage("Welcome to " + appName);
|
||||
@@ -218,8 +219,8 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
autorecovery_filename = data_dir + "/autorecovery.ove";
|
||||
if (QFile::exists(autorecovery_filename)) {
|
||||
if (QMessageBox::question(NULL, "Auto-recovery", "Olive didn't close properly and an autorecovery file was detected. Would you like to open it?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
updateTitle(autorecovery_filename);
|
||||
panel_project->load_project(true);
|
||||
enable_launch_with_project = false;
|
||||
open_project_worker(autorecovery_filename, true);
|
||||
}
|
||||
}
|
||||
autorecovery_timer.setInterval(60000);
|
||||
@@ -233,48 +234,15 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
panel_effect_controls->clear_effects(true);
|
||||
panel_sequence_viewer->viewer_widget->delete_function();
|
||||
panel_footage_viewer->viewer_widget->delete_function();
|
||||
|
||||
set_sequence(NULL);
|
||||
|
||||
QString data_dir = get_data_path();
|
||||
if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) {
|
||||
if (QFile::exists(autorecovery_filename)) {
|
||||
QFile::rename(autorecovery_filename, autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss"));
|
||||
}
|
||||
}
|
||||
if (!config_dir.isEmpty()) {
|
||||
// save settings
|
||||
config.save(config_dir);
|
||||
|
||||
// save panel layout
|
||||
QFile panel_config(data_dir + "/layout");
|
||||
if (panel_config.open(QFile::WriteOnly)) {
|
||||
panel_config.write(saveState(0));
|
||||
panel_config.close();
|
||||
} else {
|
||||
dout << "[ERROR] Failed to save layout";
|
||||
}
|
||||
}
|
||||
|
||||
stop_audio();
|
||||
|
||||
delete panel_sequence_viewer;
|
||||
panel_sequence_viewer = NULL;
|
||||
delete panel_footage_viewer;
|
||||
panel_footage_viewer = NULL;
|
||||
delete panel_project;
|
||||
panel_project = NULL;
|
||||
delete panel_effect_controls;
|
||||
panel_effect_controls = NULL;
|
||||
delete panel_timeline;
|
||||
panel_timeline = NULL;
|
||||
|
||||
free_panels();
|
||||
close_debug();
|
||||
}
|
||||
|
||||
void MainWindow::launch_with_project(const char* s) {
|
||||
project_url = s;
|
||||
enable_launch_with_project = true;
|
||||
}
|
||||
|
||||
void MainWindow::make_new_menu(QMenu *parent) {
|
||||
parent->addAction("&Project", this, SLOT(new_project()), QKeySequence("Ctrl+N"));
|
||||
parent->addSeparator();
|
||||
@@ -304,12 +272,16 @@ void MainWindow::delete_slot() {
|
||||
panel_project->delete_selected_media();
|
||||
} else if (panel_effect_controls->keyframe_focus()) {
|
||||
panel_effect_controls->delete_selected_keyframes();
|
||||
} else if (panel_graph_editor->view_is_focused()) {
|
||||
panel_graph_editor->delete_selected_keys();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::select_all() {
|
||||
if (panel_timeline->focused()) {
|
||||
panel_timeline->select_all();
|
||||
} else if (panel_graph_editor->view_is_focused()) {
|
||||
panel_graph_editor->select_all();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,6 +450,47 @@ bool MainWindow::can_close_project() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void kbd_shortcut_processor(QByteArray& file, QMenu* menu, bool save) {
|
||||
QList<QAction*> actions = menu->actions();
|
||||
for (int i=0;i<actions.size();i++) {
|
||||
QAction* a = actions.at(i);
|
||||
if (a->menu() != NULL) {
|
||||
kbd_shortcut_processor(file, a->menu(), save);
|
||||
} else if (!a->isSeparator()) {
|
||||
if (save) {
|
||||
// saving custom shortcuts
|
||||
if (!a->property("default").isNull()) {
|
||||
QKeySequence defks(a->property("default").toString());
|
||||
if (a->shortcut() != defks) {
|
||||
// custom shortcut
|
||||
if (!file.isEmpty()) file.append('\n');
|
||||
file.append(a->text().replace("&", ""));
|
||||
file.append('\t');
|
||||
file.append(a->shortcut().toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// loading custom shortcuts
|
||||
a->setProperty("default", a->shortcut().toString());
|
||||
QString comp_str = a->text().replace("&", "");
|
||||
int shortcut_index = file.indexOf(comp_str);
|
||||
if (shortcut_index == 0 || (shortcut_index > 0 && file.at(shortcut_index-1) == '\n')) {
|
||||
shortcut_index += comp_str.size() + 1;
|
||||
QString shortcut;
|
||||
while (shortcut_index < file.size() && file.at(shortcut_index) != '\n') {
|
||||
shortcut.append(file.at(shortcut_index));
|
||||
shortcut_index++;
|
||||
}
|
||||
QKeySequence ks(shortcut);
|
||||
if (!ks.isEmpty()) {
|
||||
a->setShortcut(ks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setup_menus() {
|
||||
QMenuBar* menuBar = new QMenuBar(this);
|
||||
setMenuBar(menuBar);
|
||||
@@ -655,6 +668,10 @@ void MainWindow::setup_menus() {
|
||||
window_timeline_action->setCheckable(true);
|
||||
window_timeline_action->setData(reinterpret_cast<quintptr>(panel_timeline));
|
||||
|
||||
QAction* window_graph_editor_action = window_menu->addAction("Graph Editor", this, SLOT(toggle_panel_visibility()));
|
||||
window_graph_editor_action->setCheckable(true);
|
||||
window_graph_editor_action->setData(reinterpret_cast<quintptr>(panel_graph_editor));
|
||||
|
||||
QAction* window_footageviewer_action = window_menu->addAction("Footage Viewer", this, SLOT(toggle_panel_visibility()));
|
||||
window_footageviewer_action->setCheckable(true);
|
||||
window_footageviewer_action->setData(reinterpret_cast<quintptr>(panel_footage_viewer));
|
||||
@@ -756,6 +773,10 @@ void MainWindow::setup_menus() {
|
||||
enable_hover_focus->setCheckable(true);
|
||||
enable_hover_focus->setData(reinterpret_cast<quintptr>(&config.hover_focus));
|
||||
|
||||
set_name_and_marker = tools_menu->addAction("Ask For Name When Setting Marker", this, SLOT(toggle_bool_action()));
|
||||
set_name_and_marker->setCheckable(true);
|
||||
set_name_and_marker->setData(reinterpret_cast<quintptr>(&config.set_name_with_marker));
|
||||
|
||||
tools_menu->addSeparator();
|
||||
|
||||
no_autoscroll = tools_menu->addAction("No Auto-Scroll", this, SLOT(set_autoscroll()));
|
||||
@@ -775,7 +796,7 @@ void MainWindow::setup_menus() {
|
||||
tools_menu->addAction("Preferences", this, SLOT(preferences()), QKeySequence("Ctrl+."));
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack()), QKeySequence("Ctrl+."));
|
||||
tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack()));
|
||||
#endif
|
||||
|
||||
// INITIALIZE HELP MENU
|
||||
@@ -783,6 +804,16 @@ void MainWindow::setup_menus() {
|
||||
QMenu* help_menu = menuBar->addMenu("&Help");
|
||||
|
||||
help_menu->addAction("&About...", this, SLOT(show_about()));
|
||||
|
||||
QFile shortcut_path(get_config_path() + "/shortcuts");
|
||||
if (shortcut_path.exists() && shortcut_path.open(QFile::ReadOnly)) {
|
||||
QList<QAction*> menus = menuBar->actions();
|
||||
QByteArray shortcut_bytes = shortcut_path.readAll();
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
kbd_shortcut_processor(shortcut_bytes, menu, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::set_bool_action_checked(QAction *a) {
|
||||
@@ -809,6 +840,50 @@ void MainWindow::updateTitle(const QString& url) {
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *e) {
|
||||
if (can_close_project()) {
|
||||
panel_effect_controls->clear_effects(true);
|
||||
|
||||
set_sequence(NULL);
|
||||
|
||||
panel_footage_viewer->set_main_sequence();
|
||||
|
||||
QString data_dir = get_data_path();
|
||||
QString config_dir = get_config_path();
|
||||
if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) {
|
||||
if (QFile::exists(autorecovery_filename)) {
|
||||
QFile::rename(autorecovery_filename, autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss"));
|
||||
}
|
||||
}
|
||||
if (!config_dir.isEmpty() && !config_fn.isEmpty()) {
|
||||
// save settings
|
||||
config.save(config_fn);
|
||||
|
||||
// save panel layout
|
||||
QFile panel_config(config_dir + "/layout");
|
||||
if (panel_config.open(QFile::WriteOnly)) {
|
||||
panel_config.write(saveState(0));
|
||||
panel_config.close();
|
||||
} else {
|
||||
dout << "[ERROR] Failed to save layout";
|
||||
}
|
||||
|
||||
// save main menu actions
|
||||
QList<QAction*> menus = menuBar()->actions();
|
||||
QByteArray shortcut_file;
|
||||
for (int i=0;i<menus.size();i++) {
|
||||
QMenu* menu = menus.at(i)->menu();
|
||||
kbd_shortcut_processor(shortcut_file, menu, true);
|
||||
}
|
||||
QFile shortcut_file_io(config_dir + "/shortcuts");
|
||||
if (shortcut_file_io.open(QFile::WriteOnly)) {
|
||||
shortcut_file_io.write(shortcut_file);
|
||||
shortcut_file_io.close();
|
||||
} else {
|
||||
dout << "[ERROR] Failed to save shortcut file";
|
||||
}
|
||||
}
|
||||
|
||||
stop_audio();
|
||||
|
||||
e->accept();
|
||||
} else {
|
||||
e->ignore();
|
||||
@@ -817,6 +892,10 @@ void MainWindow::closeEvent(QCloseEvent *e) {
|
||||
|
||||
void MainWindow::paintEvent(QPaintEvent *event) {
|
||||
QMainWindow::paintEvent(event);
|
||||
if (enable_launch_with_project) {
|
||||
QTimer::singleShot(10, this, SLOT(load_with_launch()));
|
||||
enable_launch_with_project = false;
|
||||
}
|
||||
#ifndef QT_DEBUG
|
||||
if (!demoNoticeShown) {
|
||||
DemoNotice* d = new DemoNotice(this);
|
||||
@@ -833,18 +912,29 @@ void MainWindow::clear_undo_stack() {
|
||||
void MainWindow::open_project() {
|
||||
QString fn = QFileDialog::getOpenFileName(this, "Open Project...", "", OLIVE_FILE_FILTER);
|
||||
if (!fn.isEmpty() && can_close_project()) {
|
||||
updateTitle(fn);
|
||||
panel_project->load_project(false);
|
||||
undo_stack.clear();
|
||||
open_project_worker(fn, false);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::open_project_worker(const QString& fn, bool autorecovery) {
|
||||
updateTitle(fn);
|
||||
panel_project->load_project(autorecovery);
|
||||
undo_stack.clear();
|
||||
}
|
||||
|
||||
void MainWindow::load_with_launch() {
|
||||
open_project_worker(project_url, false);
|
||||
}
|
||||
|
||||
void MainWindow::reset_layout() {
|
||||
setup_layout(true);
|
||||
}
|
||||
|
||||
void MainWindow::go_to_start() {
|
||||
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
|
||||
if (panel_timeline->focused()
|
||||
|| panel_sequence_viewer->is_focused()
|
||||
|| panel_effect_controls->keyframe_focus()
|
||||
|| panel_graph_editor->view_is_focused()) {
|
||||
panel_sequence_viewer->go_to_start();
|
||||
} else if (panel_footage_viewer->is_focused()) {
|
||||
panel_footage_viewer->go_to_start();
|
||||
@@ -852,7 +942,10 @@ void MainWindow::go_to_start() {
|
||||
}
|
||||
|
||||
void MainWindow::prev_frame() {
|
||||
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
|
||||
if (panel_timeline->focused()
|
||||
|| panel_sequence_viewer->is_focused()
|
||||
|| panel_effect_controls->keyframe_focus()
|
||||
|| panel_graph_editor->view_is_focused()) {
|
||||
panel_sequence_viewer->previous_frame();
|
||||
} else if (panel_footage_viewer->is_focused()) {
|
||||
panel_footage_viewer->previous_frame();
|
||||
@@ -860,7 +953,10 @@ void MainWindow::prev_frame() {
|
||||
}
|
||||
|
||||
void MainWindow::next_frame() {
|
||||
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
|
||||
if (panel_timeline->focused()
|
||||
|| panel_sequence_viewer->is_focused()
|
||||
|| panel_effect_controls->keyframe_focus()
|
||||
|| panel_graph_editor->view_is_focused()) {
|
||||
panel_sequence_viewer->next_frame();
|
||||
} else if (panel_footage_viewer->is_focused()) {
|
||||
panel_footage_viewer->next_frame();
|
||||
@@ -868,7 +964,10 @@ void MainWindow::next_frame() {
|
||||
}
|
||||
|
||||
void MainWindow::go_to_end() {
|
||||
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
|
||||
if (panel_timeline->focused()
|
||||
|| panel_sequence_viewer->is_focused()
|
||||
|| panel_effect_controls->keyframe_focus()
|
||||
|| panel_graph_editor->view_is_focused()) {
|
||||
panel_sequence_viewer->go_to_end();
|
||||
} else if (panel_footage_viewer->is_focused()) {
|
||||
panel_footage_viewer->go_to_end();
|
||||
@@ -876,7 +975,10 @@ void MainWindow::go_to_end() {
|
||||
}
|
||||
|
||||
void MainWindow::playpause() {
|
||||
if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) {
|
||||
if (panel_timeline->focused()
|
||||
|| panel_sequence_viewer->is_focused()
|
||||
|| panel_effect_controls->keyframe_focus()
|
||||
|| panel_graph_editor->view_is_focused()) {
|
||||
panel_sequence_viewer->toggle_play();
|
||||
} else if (panel_footage_viewer->is_focused()) {
|
||||
panel_footage_viewer->toggle_play();
|
||||
@@ -962,6 +1064,7 @@ void MainWindow::toolMenu_About_To_Be_Shown() {
|
||||
set_bool_action_checked(enable_audio_scrubbing);
|
||||
set_bool_action_checked(enable_drop_on_media_to_replace);
|
||||
set_bool_action_checked(enable_hover_focus);
|
||||
set_bool_action_checked(set_name_and_marker);
|
||||
|
||||
set_int_action_checked(no_autoscroll, config.autoscroll);
|
||||
set_int_action_checked(page_autoscroll, config.autoscroll);
|
||||
@@ -1019,8 +1122,7 @@ void MainWindow::load_recent_project() {
|
||||
panel_project->save_recent_projects();
|
||||
}
|
||||
} else if (can_close_project()) {
|
||||
updateTitle(recent_url);
|
||||
panel_project->load_project(false);
|
||||
open_project_worker(recent_url, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1203,7 +1305,8 @@ void MainWindow::nest() {
|
||||
Media* m = panel_project->new_sequence(ca, s, false, NULL);
|
||||
|
||||
// add nested sequence to active sequence
|
||||
QVector<Media*> media_list = {m};
|
||||
QVector<Media*> media_list;
|
||||
media_list.append(m);
|
||||
panel_timeline->create_ghosts_from_media(sequence, earliest_point, media_list);
|
||||
panel_timeline->add_clips_from_ghosts(ca, sequence);
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ public:
|
||||
void updateTitle(const QString &url);
|
||||
~MainWindow();
|
||||
|
||||
void launch_with_project(const char *s);
|
||||
|
||||
void make_new_menu(QMenu* parent);
|
||||
void make_inout_menu(QMenu* parent);
|
||||
|
||||
@@ -110,6 +112,9 @@ private slots:
|
||||
void menu_click_button();
|
||||
void toggle_panel_visibility();
|
||||
void set_timecode_view();
|
||||
void open_project_worker(const QString &fn, bool autorecovery);
|
||||
|
||||
void load_with_launch();
|
||||
|
||||
private:
|
||||
void setup_layout(bool reset);
|
||||
@@ -161,6 +166,7 @@ private:
|
||||
QAction* enable_audio_scrubbing;
|
||||
QAction* enable_drop_on_media_to_replace;
|
||||
QAction* enable_hover_focus;
|
||||
QAction* set_name_and_marker;
|
||||
|
||||
// edit menu actions
|
||||
QAction* undo_action;
|
||||
@@ -169,6 +175,8 @@ private:
|
||||
void set_bool_action_checked(QAction* a);
|
||||
void set_int_action_checked(QAction* a, const int& i);
|
||||
void set_button_action_checked(QAction* a);
|
||||
|
||||
bool enable_launch_with_project;
|
||||
};
|
||||
|
||||
extern MainWindow* mainWindow;
|
||||
|
||||
+973
@@ -0,0 +1,973 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>653</width>
|
||||
<height>394</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>653</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_File">
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_New">
|
||||
<property name="title">
|
||||
<string>&New</string>
|
||||
</property>
|
||||
<addaction name="actionProject"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSequence"/>
|
||||
<addaction name="actionFolder"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuOpen_Recent">
|
||||
<property name="title">
|
||||
<string>Open Recent</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menu_New"/>
|
||||
<addaction name="action_Open_Project"/>
|
||||
<addaction name="menuOpen_Recent"/>
|
||||
<addaction name="action_Save_Project"/>
|
||||
<addaction name="actionSave_Project_As"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Import"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExport"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEdit">
|
||||
<property name="title">
|
||||
<string>&Edit</string>
|
||||
</property>
|
||||
<addaction name="action_Undo"/>
|
||||
<addaction name="action_Redo"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCu_t"/>
|
||||
<addaction name="actionCop_y"/>
|
||||
<addaction name="action_Paste"/>
|
||||
<addaction name="actionPasteInsert"/>
|
||||
<addaction name="actionDuplicate"/>
|
||||
<addaction name="actionDelete"/>
|
||||
<addaction name="actionRipple_Delete"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSelect_All"/>
|
||||
<addaction name="actionDeselect_All"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionAdd_Default_Transition"/>
|
||||
<addaction name="actionLink_Unlink"/>
|
||||
<addaction name="actionRipple_To_In_Point"/>
|
||||
<addaction name="actionRipple_to_Out_Point"/>
|
||||
<addaction name="actionEdit_to_In_Point"/>
|
||||
<addaction name="actionEdit_to_Out_Point"/>
|
||||
<addaction name="actionSplit_at_Playhead"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSet_In_Point"/>
|
||||
<addaction name="actionSet_Out_Point"/>
|
||||
<addaction name="actionClear_In_Out"/>
|
||||
<addaction name="actionDelete_In_Out"/>
|
||||
<addaction name="actionRipple_Delete_In_Out"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSet_Edit_Marker"/>
|
||||
<addaction name="actionEnable_Disable_Clip"/>
|
||||
<addaction name="actionNest"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuWindow">
|
||||
<property name="title">
|
||||
<string>&Window</string>
|
||||
</property>
|
||||
<addaction name="actionProject_2"/>
|
||||
<addaction name="actionEffect_Controls"/>
|
||||
<addaction name="actionFootage_Viewer"/>
|
||||
<addaction name="actionViewer"/>
|
||||
<addaction name="actionTimeline"/>
|
||||
<addaction name="actionGraph_Editor"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionReset_to_default_layout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Help">
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_View">
|
||||
<property name="title">
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuTitle_Action_Safe_Area">
|
||||
<property name="title">
|
||||
<string>Title/Action Safe Area</string>
|
||||
</property>
|
||||
<addaction name="actionOff"/>
|
||||
<addaction name="actionDefault"/>
|
||||
<addaction name="action4_3"/>
|
||||
<addaction name="action16_9"/>
|
||||
<addaction name="actionCustom"/>
|
||||
</widget>
|
||||
<addaction name="actionTimeline_Track_Lines"/>
|
||||
<addaction name="actionZoom_In"/>
|
||||
<addaction name="actionZoom_out"/>
|
||||
<addaction name="actionIncrease_Track_Height"/>
|
||||
<addaction name="actionDecrease_Track_Height"/>
|
||||
<addaction name="actionToggle_Show_All"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionFrames"/>
|
||||
<addaction name="actionDrop_Frame"/>
|
||||
<addaction name="actionNon_Drop_Frame"/>
|
||||
<addaction name="actionMilliseconds"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuTitle_Action_Safe_Area"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionFull_Screen"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuPlayback">
|
||||
<property name="title">
|
||||
<string>&Playback</string>
|
||||
</property>
|
||||
<addaction name="actionGo_to_start"/>
|
||||
<addaction name="actionPrevious_Frame"/>
|
||||
<addaction name="actionPlay_Pause"/>
|
||||
<addaction name="actionNext_Frame"/>
|
||||
<addaction name="actionGo_to_End"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionGo_to_Previous_Cut"/>
|
||||
<addaction name="actionGo_to_Next_Cut"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Tools">
|
||||
<property name="title">
|
||||
<string>&Tools</string>
|
||||
</property>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPointer_Tool"/>
|
||||
<addaction name="actionEdit_Tool"/>
|
||||
<addaction name="actionRipple_Tool"/>
|
||||
<addaction name="actionRolling_Tool"/>
|
||||
<addaction name="actionRazor_Tool"/>
|
||||
<addaction name="actionSlip_Tool"/>
|
||||
<addaction name="actionSlide_Tool"/>
|
||||
<addaction name="actionHand_Tool"/>
|
||||
<addaction name="actionTransition_Tool"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionToggle_Snapping"/>
|
||||
<addaction name="actionSelecting_Also_Seeks"/>
|
||||
<addaction name="actionEdit_Tool_Also_Seeks"/>
|
||||
<addaction name="actionEdit_Tool_Selects_Links"/>
|
||||
<addaction name="actionSeek_to_the_End_of_Pastes"/>
|
||||
<addaction name="actionScroll_Wheel_Zooms"/>
|
||||
<addaction name="actionRectified_Waveforms"/>
|
||||
<addaction name="actionEnable_Drag_Files_to_Timeline"/>
|
||||
<addaction name="actionAuto_scale_by_Default"/>
|
||||
<addaction name="actionEnable_Seek_to_Import"/>
|
||||
<addaction name="actionAudio_Scrubbing"/>
|
||||
<addaction name="actionEnable_Drop_on_Media_to_Replace"/>
|
||||
<addaction name="actionEnable_Hover_Focus"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionNo_autoscroll"/>
|
||||
<addaction name="actionPage_Autoscroll"/>
|
||||
<addaction name="actionSmooth_Auto_scroll"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
<addaction name="actionCrash"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
<addaction name="menuEdit"/>
|
||||
<addaction name="menu_View"/>
|
||||
<addaction name="menuPlayback"/>
|
||||
<addaction name="menuWindow"/>
|
||||
<addaction name="menu_Tools"/>
|
||||
<addaction name="menu_Help"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<action name="action_Open_Project">
|
||||
<property name="text">
|
||||
<string>&Open Project</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Save_Project">
|
||||
<property name="text">
|
||||
<string>&Save Project</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSave_Project_As">
|
||||
<property name="text">
|
||||
<string>Save Project &As</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Import">
|
||||
<property name="text">
|
||||
<string>&Import...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport">
|
||||
<property name="text">
|
||||
<string>&Export...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+M</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="text">
|
||||
<string>E&xit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>About...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Undo">
|
||||
<property name="text">
|
||||
<string>&Undo</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Z</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Redo">
|
||||
<property name="text">
|
||||
<string>&Redo</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+Z</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCu_t">
|
||||
<property name="text">
|
||||
<string>Cu&t</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+X</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCop_y">
|
||||
<property name="text">
|
||||
<string>Cop&y</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+C</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Paste">
|
||||
<property name="text">
|
||||
<string>&Paste</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+V</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPasteInsert">
|
||||
<property name="text">
|
||||
<string>Paste Insert</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+V</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Del</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSelect_All">
|
||||
<property name="text">
|
||||
<string>Select &All</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTimeline_Track_Lines">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Track Lines</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionProject">
|
||||
<property name="text">
|
||||
<string>Project...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSequence">
|
||||
<property name="text">
|
||||
<string>Sequence...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZoom_In">
|
||||
<property name="text">
|
||||
<string>Zoom In</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>=</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZoom_out">
|
||||
<property name="text">
|
||||
<string>Zoom Out</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>-</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionProject_2">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Project</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEffect_Controls">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Effect Controls</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewer">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sequence Viewer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTimeline">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Timeline</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_Delete">
|
||||
<property name="text">
|
||||
<string>Ripple Delete</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Shift+Del</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSplit_at_Playhead">
|
||||
<property name="text">
|
||||
<string>Split at Playhead</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+K</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDeselect_All">
|
||||
<property name="text">
|
||||
<string>Deselect All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGo_to_start">
|
||||
<property name="text">
|
||||
<string>Go to Start</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Home</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReset_to_default_layout">
|
||||
<property name="text">
|
||||
<string>Reset to default layout</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPrevious_Frame">
|
||||
<property name="text">
|
||||
<string>Previous Frame</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Left</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPlay_Pause">
|
||||
<property name="text">
|
||||
<string>Play/Pause</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Space</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNext_Frame">
|
||||
<property name="text">
|
||||
<string>Next Frame</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Right</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGo_to_End">
|
||||
<property name="text">
|
||||
<string>Go to End</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>End</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCrash">
|
||||
<property name="text">
|
||||
<string>Crash</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPointer_Tool">
|
||||
<property name="text">
|
||||
<string>Pointer Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>V</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_Tool">
|
||||
<property name="text">
|
||||
<string>Edit Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>X</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRazor_Tool">
|
||||
<property name="text">
|
||||
<string>Razor Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>C</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_Tool">
|
||||
<property name="text">
|
||||
<string>Ripple Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>B</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRolling_Tool">
|
||||
<property name="text">
|
||||
<string>Rolling Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>N</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPreferences">
|
||||
<property name="text">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToggle_Snapping">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Snapping</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Snapping</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSlip_Tool">
|
||||
<property name="text">
|
||||
<string>Slip Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Y</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGo_to_Previous_Cut">
|
||||
<property name="text">
|
||||
<string>Go to Previous Cut</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Up</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGo_to_Next_Cut">
|
||||
<property name="text">
|
||||
<string>Go to Next Cut</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Down</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_Tool_Also_Seeks">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit Tool Also Seeks</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionIncrease_Track_Height">
|
||||
<property name="text">
|
||||
<string>Increase Track Height</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+=</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDecrease_Track_Height">
|
||||
<property name="text">
|
||||
<string>Decrease Track Height</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+-</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFrames">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Frames</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDrop_Frame">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Drop-Frame</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNon_Drop_Frame">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Non-Drop Frame</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_Tool_Selects_Links">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit Tool Selects Links</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDuplicate">
|
||||
<property name="text">
|
||||
<string>Duplicate</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSelecting_Also_Seeks">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Selecting Also Seeks</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSeek_to_the_End_of_Pastes">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Seek to the End of Pastes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_Default_Transition">
|
||||
<property name="text">
|
||||
<string>Add Default Transition</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSlide_Tool">
|
||||
<property name="text">
|
||||
<string>Slide Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>U</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFolder">
|
||||
<property name="text">
|
||||
<string>Folder</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClear_Recent_List">
|
||||
<property name="text">
|
||||
<string>Clear Recent List</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionScroll_Wheel_Zooms">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Scroll Wheel Zooms</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLink_Unlink">
|
||||
<property name="text">
|
||||
<string>Link/Unlink</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+L</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_To_In_Point">
|
||||
<property name="text">
|
||||
<string>Ripple to In Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_to_Out_Point">
|
||||
<property name="text">
|
||||
<string>Ripple to Out Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>W</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_In_Point">
|
||||
<property name="text">
|
||||
<string>Set In Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_Out_Point">
|
||||
<property name="text">
|
||||
<string>Set Out Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClear_In_Out">
|
||||
<property name="text">
|
||||
<string>Clear In/Out</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>G</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete_In_Out">
|
||||
<property name="text">
|
||||
<string>Delete In/Out</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>;</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_Delete_In_Out">
|
||||
<property name="text">
|
||||
<string>Ripple Delete In/Out</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>'</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRectified_Waveforms">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rectified Waveforms</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDefault">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOff">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action4_3">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>4:3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action16_9">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>16:9</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCustom">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Drag_Files_to_Timeline">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Drag Files to Timeline</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAuto_scale_by_Default">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto-scale by Default</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_Edit_Marker">
|
||||
<property name="text">
|
||||
<string>Set/Edit Marker</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>M</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Disable_Clip">
|
||||
<property name="text">
|
||||
<string>Enable/Disable Clip</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Shift+E</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Seek_to_Import">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Seek to Import</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAudio_Scrubbing">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Audio Scrubbing</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTransition_Tool">
|
||||
<property name="text">
|
||||
<string>Transition Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>T</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_to_In_Point">
|
||||
<property name="text">
|
||||
<string>Edit to In Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Alt+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_to_Out_Point">
|
||||
<property name="text">
|
||||
<string>Edit to Out Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Alt+W</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNest">
|
||||
<property name="text">
|
||||
<string>Nest</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToggle_Show_All">
|
||||
<property name="text">
|
||||
<string>Toggle Show All</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>\</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Drop_on_Media_to_Replace">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Drop File on Media to Replace</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFootage_Viewer">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Footage Viewer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNo_autoscroll">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No Auto-scroll</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPage_Autoscroll">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Page Auto-scroll</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSmooth_Auto_scroll">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Smooth Auto-scroll</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMilliseconds">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Milliseconds</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Hover_Focus">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Hover Focus for Zooming</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFull_Screen">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Full Screen</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F11</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHand_Tool">
|
||||
<property name="text">
|
||||
<string>Hand Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>H</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGraph_Editor">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Graph Editor</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1 +0,0 @@
|
||||
olive.pro
|
||||
@@ -110,7 +110,13 @@ SOURCES += \
|
||||
ui/resizablescrollbar.cpp \
|
||||
ui/sourceiconview.cpp \
|
||||
project/sourcescommon.cpp \
|
||||
ui/keyframenavigator.cpp
|
||||
ui/keyframenavigator.cpp \
|
||||
panels/grapheditor.cpp \
|
||||
ui/graphview.cpp \
|
||||
ui/keyframedrawing.cpp \
|
||||
ui/clickablelabel.cpp \
|
||||
project/keyframe.cpp \
|
||||
ui/rectangleselect.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
@@ -190,18 +196,24 @@ HEADERS += \
|
||||
ui/resizablescrollbar.h \
|
||||
ui/sourceiconview.h \
|
||||
project/sourcescommon.h \
|
||||
ui/keyframenavigator.h
|
||||
ui/keyframenavigator.h \
|
||||
panels/grapheditor.h \
|
||||
ui/graphview.h \
|
||||
ui/keyframedrawing.h \
|
||||
ui/clickablelabel.h \
|
||||
project/keyframe.h \
|
||||
ui/rectangleselect.h
|
||||
|
||||
FORMS +=
|
||||
|
||||
win32 {
|
||||
RC_FILE = icons/resources.rc
|
||||
RC_FILE = packaging/windows/resources.rc
|
||||
LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lopengl32
|
||||
}
|
||||
|
||||
mac {
|
||||
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample
|
||||
ICON = icons/olive.icns
|
||||
ICON = packaging/macos/olive.icns
|
||||
INCLUDEPATH = /usr/local/include
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<id>org.olivevideoeditor.Olive</id>
|
||||
<name>Olive</name>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPLv3</project_license>
|
||||
<project_license>GPL-3.0</project_license>
|
||||
<developer_name>Olive Team</developer_name>
|
||||
<summary>Non-linear video editor</summary>
|
||||
<summary xml:lang="pt_BR">Editor de vídeo não-linear</summary>
|
||||
@@ -15,9 +15,9 @@
|
||||
<description xml:lang="ru"><p>Olive — свободный нелинейный видеоредактор, задуманный как полноценная замена закрытым коммерческим продуктам.</p></description>
|
||||
<url type="homepage">https://www.olivevideoeditor.org</url>
|
||||
<url type="donation">https://www.patreon.com/olivevideoeditor</url>
|
||||
<url type="help">https://github.com/olive-editor/olive/issues</url>
|
||||
<url type="bugtracker">https://github.com/olive-editor/olive/issues</url>
|
||||
<screenshots>
|
||||
<screenshot type="default"><image>http://images.libregraphicsworld.org/video/2018/12/introducing-olive/olive-alpha-main-window.jpg</image></screenshot>
|
||||
<screenshot type="default"><image>https://olivevideoeditor.org/img/screenshot.jpg</image></screenshot>
|
||||
</screenshots>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
<mime-type type="application/vnd.olive-project">
|
||||
<comment>Olive project</comment>
|
||||
<glob pattern="*.ove"/>
|
||||
<icon name="org.olivevideoeditor.Olive"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
|
||||
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
@@ -22,6 +22,7 @@
|
||||
#include "panels/project.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "panels/grapheditor.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "io/clipboard.h"
|
||||
#include "ui/timelineheader.h"
|
||||
@@ -36,6 +37,8 @@ EffectControls::EffectControls(QWidget *parent) :
|
||||
panel_name("Effects: "),
|
||||
mode(TA_NO_TRANSITION)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
setup_ui();
|
||||
|
||||
init_effects();
|
||||
@@ -210,6 +213,9 @@ void EffectControls::clear_effects(bool clear_cache) {
|
||||
// clear existing clips
|
||||
deselect_all_effects(NULL);
|
||||
|
||||
// clear graph editor
|
||||
if (panel_graph_editor != NULL) panel_graph_editor->set_row(NULL);
|
||||
|
||||
QVBoxLayout* video_layout = static_cast<QVBoxLayout*>(video_effect_area->layout());
|
||||
QVBoxLayout* audio_layout = static_cast<QVBoxLayout*>(audio_effect_area->layout());
|
||||
QLayoutItem* item;
|
||||
@@ -249,8 +255,6 @@ void EffectControls::open_effect(QVBoxLayout* layout, Effect* e) {
|
||||
}
|
||||
|
||||
void EffectControls::setup_ui() {
|
||||
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
|
||||
QWidget* contents = new QWidget();
|
||||
|
||||
QHBoxLayout* layout = new QHBoxLayout(contents);
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
#include "grapheditor.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVariant>
|
||||
|
||||
#include "ui/keyframenavigator.h"
|
||||
#include "ui/timelineheader.h"
|
||||
#include "ui/timelinetools.h"
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/graphview.h"
|
||||
#include "project/effect.h"
|
||||
#include "project/effectfield.h"
|
||||
#include "project/effectrow.h"
|
||||
#include "project/clip.h"
|
||||
#include "panels.h"
|
||||
#include "debug.h"
|
||||
|
||||
GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) {
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
setWindowTitle("Graph Editor");
|
||||
resize(720, 480);
|
||||
|
||||
QWidget* main_widget = new QWidget();
|
||||
setWidget(main_widget);
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
main_widget->setLayout(layout);
|
||||
|
||||
QWidget* tool_widget = new QWidget();
|
||||
tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
QHBoxLayout* tools = new QHBoxLayout();
|
||||
tool_widget->setLayout(tools);
|
||||
|
||||
QWidget* left_tool_widget = new QWidget();
|
||||
QHBoxLayout* left_tool_layout = new QHBoxLayout();
|
||||
left_tool_layout->setSpacing(0);
|
||||
left_tool_layout->setMargin(0);
|
||||
left_tool_widget->setLayout(left_tool_layout);
|
||||
tools->addWidget(left_tool_widget);
|
||||
QWidget* center_tool_widget = new QWidget();
|
||||
QHBoxLayout* center_tool_layout = new QHBoxLayout();
|
||||
center_tool_layout->setSpacing(0);
|
||||
center_tool_layout->setMargin(0);
|
||||
center_tool_widget->setLayout(center_tool_layout);
|
||||
tools->addWidget(center_tool_widget);
|
||||
QWidget* right_tool_widget = new QWidget();
|
||||
QHBoxLayout* right_tool_layout = new QHBoxLayout();
|
||||
right_tool_layout->setSpacing(0);
|
||||
right_tool_layout->setMargin(0);
|
||||
right_tool_widget->setLayout(right_tool_layout);
|
||||
tools->addWidget(right_tool_widget);
|
||||
|
||||
keyframe_nav = new KeyframeNavigator();
|
||||
keyframe_nav->enable_keyframes(true);
|
||||
keyframe_nav->enable_keyframe_toggle(false);
|
||||
left_tool_layout->addWidget(keyframe_nav);
|
||||
left_tool_layout->addStretch();
|
||||
|
||||
linear_button = new QPushButton("Linear");
|
||||
linear_button->setProperty("type", KEYFRAME_TYPE_LINEAR);
|
||||
linear_button->setCheckable(true);
|
||||
bezier_button = new QPushButton("Bezier");
|
||||
bezier_button->setProperty("type", KEYFRAME_TYPE_BEZIER);
|
||||
bezier_button->setCheckable(true);
|
||||
hold_button = new QPushButton("Hold");
|
||||
hold_button->setProperty("type", KEYFRAME_TYPE_HOLD);
|
||||
hold_button->setCheckable(true);
|
||||
|
||||
center_tool_layout->addStretch();
|
||||
center_tool_layout->addWidget(linear_button);
|
||||
center_tool_layout->addWidget(bezier_button);
|
||||
center_tool_layout->addWidget(hold_button);
|
||||
|
||||
layout->addWidget(tool_widget);
|
||||
|
||||
QWidget* central_widget = new QWidget();
|
||||
QVBoxLayout* central_layout = new QVBoxLayout();
|
||||
central_widget->setLayout(central_layout);
|
||||
central_layout->setSpacing(0);
|
||||
central_layout->setMargin(0);
|
||||
header = new TimelineHeader();
|
||||
header->viewer = panel_sequence_viewer;
|
||||
central_layout->addWidget(header);
|
||||
view = new GraphView();
|
||||
central_layout->addWidget(view);
|
||||
|
||||
layout->addWidget(central_widget);
|
||||
|
||||
QWidget* value_widget = new QWidget();
|
||||
value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
QHBoxLayout* values = new QHBoxLayout();
|
||||
value_widget->setLayout(values);
|
||||
values->addStretch();
|
||||
|
||||
QWidget* central_value_widget = new QWidget();
|
||||
value_layout = new QHBoxLayout();
|
||||
value_layout->setMargin(0);
|
||||
value_layout->addWidget(new QLabel("")); // a spacer so the layout doesn't jump
|
||||
central_value_widget->setLayout(value_layout);
|
||||
values->addWidget(central_value_widget);
|
||||
|
||||
values->addStretch();
|
||||
layout->addWidget(value_widget);
|
||||
|
||||
current_row_desc = new QLabel();
|
||||
current_row_desc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
current_row_desc->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(current_row_desc);
|
||||
|
||||
connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double)));
|
||||
connect(view, SIGNAL(x_scroll_changed(int)), header, SLOT(set_scroll(int)));
|
||||
connect(view, SIGNAL(selection_changed(bool, int)), this, SLOT(set_key_button_enabled(bool, int)));
|
||||
|
||||
connect(linear_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type()));
|
||||
connect(bezier_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type()));
|
||||
connect(hold_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type()));
|
||||
}
|
||||
|
||||
void GraphEditor::update_panel() {
|
||||
if (isVisible()) {
|
||||
if (row != NULL) {
|
||||
int slider_index = 0;
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
EffectField* field = row->field(i);
|
||||
if (field->type == EFFECT_FIELD_DOUBLE) {
|
||||
slider_proxies.at(slider_index)->set_value(row->field(i)->get_current_data().toDouble(), false);
|
||||
slider_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header->update();
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEditor::set_row(EffectRow *r) {
|
||||
for (int i=0;i<slider_proxies.size();i++) {
|
||||
delete slider_proxies.at(i);
|
||||
delete slider_proxy_buttons.at(i);
|
||||
}
|
||||
slider_proxies.clear();
|
||||
slider_proxy_buttons.clear();
|
||||
slider_proxy_sources.clear();
|
||||
|
||||
if (row != NULL) {
|
||||
// clear old row connections
|
||||
disconnect(keyframe_nav, SIGNAL(goto_previous_key()), row, SLOT(goto_previous_key()));
|
||||
disconnect(keyframe_nav, SIGNAL(toggle_key()), row, SLOT(toggle_key()));
|
||||
disconnect(keyframe_nav, SIGNAL(goto_next_key()), row, SLOT(goto_next_key()));
|
||||
}
|
||||
|
||||
bool found_vals = false;
|
||||
|
||||
if (r != NULL && r->isKeyframing()) {
|
||||
for (int i=0;i<r->fieldCount();i++) {
|
||||
EffectField* field = r->field(i);
|
||||
if (field->type == EFFECT_FIELD_DOUBLE) {
|
||||
QPushButton* slider_button = new QPushButton();
|
||||
slider_button->setCheckable(true);
|
||||
slider_button->setChecked(true);
|
||||
slider_button->setIcon(QIcon(":/icons/record.png"));
|
||||
slider_button->setProperty("field", i);
|
||||
slider_button->setIconSize(QSize(8, 8));
|
||||
slider_button->setMaximumSize(QSize(12, 12));
|
||||
connect(slider_button, SIGNAL(toggled(bool)), this, SLOT(set_field_visibility(bool)));
|
||||
slider_proxy_buttons.append(slider_button);
|
||||
value_layout->addWidget(slider_button);
|
||||
|
||||
LabelSlider* slider = new LabelSlider();
|
||||
slider->set_color(get_curve_color(i, r->fieldCount()).name());
|
||||
connect(slider, SIGNAL(valueChanged()), this, SLOT(passthrough_slider_value()));
|
||||
slider_proxies.append(slider);
|
||||
value_layout->addWidget(slider);
|
||||
|
||||
slider_proxy_sources.append(static_cast<LabelSlider*>(field->ui_element));
|
||||
|
||||
found_vals = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_vals) {
|
||||
row = r;
|
||||
current_row_desc->setText(row->parent_effect->parent_clip->name + " :: " + row->parent_effect->meta->name + " :: " + row->get_name());
|
||||
header->set_visible_in(r->parent_effect->parent_clip->timeline_in);
|
||||
|
||||
connect(keyframe_nav, SIGNAL(goto_previous_key()), row, SLOT(goto_previous_key()));
|
||||
connect(keyframe_nav, SIGNAL(toggle_key()), row, SLOT(toggle_key()));
|
||||
connect(keyframe_nav, SIGNAL(goto_next_key()), row, SLOT(goto_next_key()));
|
||||
} else {
|
||||
row = NULL;
|
||||
current_row_desc->setText(0);
|
||||
}
|
||||
view->set_row(row);
|
||||
update_panel();
|
||||
}
|
||||
|
||||
bool GraphEditor::view_is_focused() {
|
||||
return view->hasFocus() || header->hasFocus();
|
||||
}
|
||||
|
||||
void GraphEditor::delete_selected_keys() {
|
||||
view->delete_selected_keys();
|
||||
}
|
||||
|
||||
void GraphEditor::select_all() {
|
||||
view->select_all();
|
||||
}
|
||||
|
||||
void GraphEditor::set_key_button_enabled(bool e, int type) {
|
||||
linear_button->setEnabled(e);
|
||||
linear_button->setChecked(type == KEYFRAME_TYPE_LINEAR);
|
||||
bezier_button->setEnabled(e);
|
||||
bezier_button->setChecked(type == KEYFRAME_TYPE_BEZIER);
|
||||
hold_button->setEnabled(e);
|
||||
hold_button->setChecked(type == KEYFRAME_TYPE_HOLD);
|
||||
}
|
||||
|
||||
void GraphEditor::passthrough_slider_value() {
|
||||
for (int i=0;i<slider_proxies.size();i++) {
|
||||
if (slider_proxies.at(i) == sender()) {
|
||||
slider_proxy_sources.at(i)->set_value(slider_proxies.at(i)->value(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEditor::set_keyframe_type() {
|
||||
linear_button->setChecked(linear_button == sender());
|
||||
bezier_button->setChecked(bezier_button == sender());
|
||||
hold_button->setChecked(hold_button == sender());
|
||||
view->set_selected_keyframe_type(sender()->property("type").toInt());
|
||||
}
|
||||
|
||||
void GraphEditor::set_field_visibility(bool b) {
|
||||
view->set_field_visibility(sender()->property("field").toInt(), b);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef GRAPHEDITOR_H
|
||||
#define GRAPHEDITOR_H
|
||||
|
||||
#include <QDockWidget>
|
||||
|
||||
class GraphView;
|
||||
class TimelineHeader;
|
||||
class QPushButton;
|
||||
class EffectRow;
|
||||
class QHBoxLayout;
|
||||
class LabelSlider;
|
||||
class QLabel;
|
||||
class KeyframeNavigator;
|
||||
|
||||
class GraphEditor : public QDockWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GraphEditor(QWidget* parent = 0);
|
||||
void update_panel();
|
||||
void set_row(EffectRow* r);
|
||||
bool view_is_focused();
|
||||
void delete_selected_keys();
|
||||
void select_all();
|
||||
private:
|
||||
GraphView* view;
|
||||
TimelineHeader* header;
|
||||
QHBoxLayout* value_layout;
|
||||
QVector<LabelSlider*> slider_proxies;
|
||||
QVector<QPushButton*> slider_proxy_buttons;
|
||||
QVector<LabelSlider*> slider_proxy_sources;
|
||||
QLabel* current_row_desc;
|
||||
EffectRow* row;
|
||||
KeyframeNavigator* keyframe_nav;
|
||||
QPushButton* linear_button;
|
||||
QPushButton* bezier_button;
|
||||
QPushButton* hold_button;
|
||||
private slots:
|
||||
void set_key_button_enabled(bool e, int type);
|
||||
void passthrough_slider_value();
|
||||
void set_keyframe_type();
|
||||
void set_field_visibility(bool b);
|
||||
};
|
||||
|
||||
#endif // GRAPHEDITOR_H
|
||||
+37
-5
@@ -8,6 +8,7 @@
|
||||
#include "project/clip.h"
|
||||
#include "project/transition.h"
|
||||
#include "io/config.h"
|
||||
#include "grapheditor.h"
|
||||
#include "debug.h"
|
||||
|
||||
Project* panel_project = 0;
|
||||
@@ -15,6 +16,7 @@ EffectControls* panel_effect_controls = 0;
|
||||
Viewer* panel_sequence_viewer = 0;
|
||||
Viewer* panel_footage_viewer = 0;
|
||||
Timeline* panel_timeline = 0;
|
||||
GraphEditor* panel_graph_editor = 0;
|
||||
|
||||
void update_effect_controls() {
|
||||
// SEND CLIPS TO EFFECT CONTROLS
|
||||
@@ -105,20 +107,21 @@ void update_ui(bool modified) {
|
||||
panel_effect_controls->update_keyframes();
|
||||
panel_timeline->repaint_timeline();
|
||||
panel_sequence_viewer->update_viewer();
|
||||
panel_graph_editor->update_panel();
|
||||
}
|
||||
|
||||
QDockWidget *get_focused_panel() {
|
||||
QDockWidget* w = NULL;
|
||||
if (config.hover_focus) {
|
||||
if (panel_project->rect().contains(panel_project->mapFromGlobal(QCursor::pos()))) {
|
||||
if (panel_project->underMouse()) {
|
||||
w = panel_project;
|
||||
} else if (panel_effect_controls->rect().contains(panel_effect_controls->mapFromGlobal(QCursor::pos()))) {
|
||||
} else if (panel_effect_controls->underMouse()) {
|
||||
w = panel_effect_controls;
|
||||
} else if (panel_sequence_viewer->rect().contains(panel_sequence_viewer->mapFromGlobal(QCursor::pos()))) {
|
||||
} else if (panel_sequence_viewer->underMouse()) {
|
||||
w = panel_sequence_viewer;
|
||||
} else if (panel_footage_viewer->rect().contains(panel_footage_viewer->mapFromGlobal(QCursor::pos()))) {
|
||||
} else if (panel_footage_viewer->underMouse()) {
|
||||
w = panel_footage_viewer;
|
||||
} else if (panel_timeline->rect().contains(panel_timeline->mapFromGlobal(QCursor::pos()))) {
|
||||
} else if (panel_timeline->underMouse()) {
|
||||
w = panel_timeline;
|
||||
}
|
||||
}
|
||||
@@ -137,3 +140,32 @@ QDockWidget *get_focused_panel() {
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
void alloc_panels(QWidget* parent) {
|
||||
// TODO maybe replace these with non-pointers later on?
|
||||
panel_sequence_viewer = new Viewer(parent);
|
||||
panel_sequence_viewer->setObjectName("seq_viewer");
|
||||
panel_footage_viewer = new Viewer(parent);
|
||||
panel_footage_viewer->setObjectName("footage_viewer");
|
||||
panel_project = new Project(parent);
|
||||
panel_project->setObjectName("proj_root");
|
||||
panel_effect_controls = new EffectControls(parent);
|
||||
panel_effect_controls->setObjectName("fx_controls");
|
||||
panel_timeline = new Timeline(parent);
|
||||
panel_timeline->setObjectName("timeline");
|
||||
panel_graph_editor = new GraphEditor(parent);
|
||||
panel_graph_editor->setObjectName("graph_editor");
|
||||
}
|
||||
|
||||
void free_panels() {
|
||||
delete panel_sequence_viewer;
|
||||
panel_sequence_viewer = NULL;
|
||||
delete panel_footage_viewer;
|
||||
panel_footage_viewer = NULL;
|
||||
delete panel_project;
|
||||
panel_project = NULL;
|
||||
delete panel_effect_controls;
|
||||
panel_effect_controls = NULL;
|
||||
delete panel_timeline;
|
||||
panel_timeline = NULL;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ class Project;
|
||||
class EffectControls;
|
||||
class Viewer;
|
||||
class Timeline;
|
||||
class GraphEditor;
|
||||
|
||||
class QWidget;
|
||||
class QDockWidget;
|
||||
|
||||
extern Project* panel_project;
|
||||
@@ -12,8 +15,11 @@ extern EffectControls* panel_effect_controls;
|
||||
extern Viewer* panel_sequence_viewer;
|
||||
extern Viewer* panel_footage_viewer;
|
||||
extern Timeline* panel_timeline;
|
||||
extern GraphEditor* panel_graph_editor;
|
||||
|
||||
void update_ui(bool modified);
|
||||
QDockWidget* get_focused_panel();
|
||||
void alloc_panels(QWidget *parent);
|
||||
void free_panels();
|
||||
|
||||
#endif // PANELS_H
|
||||
|
||||
+82
-21
@@ -42,6 +42,7 @@
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QSizePolicy>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMenu>
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
@@ -60,7 +61,7 @@ QString recent_proj_file;
|
||||
Project::Project(QWidget *parent) :
|
||||
QDockWidget(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
QWidget* dockWidgetContents = new QWidget();
|
||||
QVBoxLayout* verticalLayout = new QVBoxLayout(dockWidgetContents);
|
||||
@@ -74,11 +75,62 @@ Project::Project(QWidget *parent) :
|
||||
sorter = new QSortFilterProxyModel(this);
|
||||
sorter->setSourceModel(&project_model);
|
||||
|
||||
// optional toolbar
|
||||
toolbar_widget = new QWidget();
|
||||
toolbar_widget->setVisible(config.show_project_toolbar);
|
||||
QHBoxLayout* toolbar = new QHBoxLayout();
|
||||
toolbar->setMargin(0);
|
||||
toolbar->setSpacing(0);
|
||||
toolbar_widget->setLayout(toolbar);
|
||||
|
||||
QPushButton* toolbar_new = new QPushButton("New");
|
||||
toolbar_new->setIcon(QIcon(":/icons/tri-down.png"));
|
||||
toolbar_new->setIconSize(QSize(8, 8));
|
||||
toolbar_new->setToolTip("New");
|
||||
connect(toolbar_new, SIGNAL(clicked(bool)), this, SLOT(make_new_menu()));
|
||||
toolbar->addWidget(toolbar_new);
|
||||
|
||||
QPushButton* toolbar_open = new QPushButton("Open");
|
||||
toolbar_open->setToolTip("Open Project");
|
||||
connect(toolbar_open, SIGNAL(clicked(bool)), mainWindow, SLOT(open_project()));
|
||||
toolbar->addWidget(toolbar_open);
|
||||
|
||||
QPushButton* toolbar_save = new QPushButton("Save");
|
||||
toolbar_save->setToolTip("Save Project");
|
||||
connect(toolbar_save, SIGNAL(clicked(bool)), mainWindow, SLOT(save_project()));
|
||||
toolbar->addWidget(toolbar_save);
|
||||
|
||||
QPushButton* toolbar_undo = new QPushButton("Undo");
|
||||
toolbar_undo->setToolTip("Undo");
|
||||
connect(toolbar_undo, SIGNAL(clicked(bool)), mainWindow, SLOT(undo()));
|
||||
toolbar->addWidget(toolbar_undo);
|
||||
|
||||
QPushButton* toolbar_redo = new QPushButton("Redo");
|
||||
toolbar_redo->setToolTip("Redo");
|
||||
connect(toolbar_redo, SIGNAL(clicked(bool)), mainWindow, SLOT(redo()));
|
||||
toolbar->addWidget(toolbar_redo);
|
||||
|
||||
toolbar->addStretch();
|
||||
|
||||
QPushButton* toolbar_tree_view = new QPushButton("Tree View");
|
||||
toolbar_tree_view->setToolTip("Tree View");
|
||||
connect(toolbar_tree_view, SIGNAL(clicked(bool)), this, SLOT(set_tree_view()));
|
||||
toolbar->addWidget(toolbar_tree_view);
|
||||
|
||||
QPushButton* toolbar_icon_view = new QPushButton("Icon View");
|
||||
toolbar_icon_view->setToolTip("Icon View");
|
||||
connect(toolbar_icon_view, SIGNAL(clicked(bool)), this, SLOT(set_icon_view()));
|
||||
toolbar->addWidget(toolbar_icon_view);
|
||||
|
||||
verticalLayout->addWidget(toolbar_widget);
|
||||
|
||||
// tree view
|
||||
tree_view = new SourceTable(dockWidgetContents);
|
||||
tree_view->project_parent = this;
|
||||
tree_view->setModel(sorter);
|
||||
verticalLayout->addWidget(tree_view);
|
||||
|
||||
// icon view
|
||||
icon_view_container = new QWidget();
|
||||
|
||||
QVBoxLayout* icon_view_container_layout = new QVBoxLayout();
|
||||
@@ -181,13 +233,13 @@ Sequence* create_sequence_from_media(QVector<Media*>& media_list) {
|
||||
if (m->ready) {
|
||||
if (!got_video_values) {
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
FootageStream* ms = m->video_tracks.at(j);
|
||||
s->width = ms->video_width;
|
||||
s->height = ms->video_height;
|
||||
if (ms->video_frame_rate != 0) {
|
||||
s->frame_rate = ms->video_frame_rate;
|
||||
const FootageStream& ms = m->video_tracks.at(j);
|
||||
s->width = ms.video_width;
|
||||
s->height = ms.video_height;
|
||||
if (ms.video_frame_rate != 0) {
|
||||
s->frame_rate = ms.video_frame_rate * m->speed;
|
||||
|
||||
if (ms->video_interlacing != VIDEO_PROGRESSIVE) s->frame_rate *= 2;
|
||||
if (ms.video_interlacing != VIDEO_PROGRESSIVE) s->frame_rate *= 2;
|
||||
|
||||
// only break with a decent frame rate, otherwise there may be a better candidate
|
||||
got_video_values = true;
|
||||
@@ -197,8 +249,8 @@ Sequence* create_sequence_from_media(QVector<Media*>& media_list) {
|
||||
}
|
||||
if (!got_audio_values) {
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
FootageStream* ms = m->audio_tracks.at(j);
|
||||
s->audio_frequency = ms->audio_frequency;
|
||||
const FootageStream& ms = m->audio_tracks.at(j);
|
||||
s->audio_frequency = ms.audio_frequency;
|
||||
got_audio_values = true;
|
||||
break;
|
||||
}
|
||||
@@ -469,12 +521,14 @@ void Project::delete_selected_media() {
|
||||
if (confirm_delete) {
|
||||
delete_clips_in_clipboard_with_media(ca, item);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// remove
|
||||
if (remove) {
|
||||
panel_effect_controls->clear_effects(true);
|
||||
sequence->selections.clear();
|
||||
|
||||
// remove media and parents
|
||||
for (int m=0;m<parents.size();m++) {
|
||||
for (int l=0;l<items.size();l++) {
|
||||
@@ -850,23 +904,24 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only,
|
||||
stream.writeAttribute("using_inout", QString::number(f->using_inout));
|
||||
stream.writeAttribute("in", QString::number(f->in));
|
||||
stream.writeAttribute("out", QString::number(f->out));
|
||||
stream.writeAttribute("speed", QString::number(f->speed));
|
||||
for (int j=0;j<f->video_tracks.size();j++) {
|
||||
FootageStream* ms = f->video_tracks.at(j);
|
||||
const FootageStream& ms = f->video_tracks.at(j);
|
||||
stream.writeStartElement("video");
|
||||
stream.writeAttribute("id", QString::number(ms->file_index));
|
||||
stream.writeAttribute("width", QString::number(ms->video_width));
|
||||
stream.writeAttribute("height", QString::number(ms->video_height));
|
||||
stream.writeAttribute("framerate", QString::number(ms->video_frame_rate, 'f', 10));
|
||||
stream.writeAttribute("infinite", QString::number(ms->infinite_length));
|
||||
stream.writeAttribute("id", QString::number(ms.file_index));
|
||||
stream.writeAttribute("width", QString::number(ms.video_width));
|
||||
stream.writeAttribute("height", QString::number(ms.video_height));
|
||||
stream.writeAttribute("framerate", QString::number(ms.video_frame_rate, 'f', 10));
|
||||
stream.writeAttribute("infinite", QString::number(ms.infinite_length));
|
||||
stream.writeEndElement();
|
||||
}
|
||||
for (int j=0;j<f->audio_tracks.size();j++) {
|
||||
FootageStream* ms = f->audio_tracks.at(j);
|
||||
const FootageStream& ms = f->audio_tracks.at(j);
|
||||
stream.writeStartElement("audio");
|
||||
stream.writeAttribute("id", QString::number(ms->file_index));
|
||||
stream.writeAttribute("channels", QString::number(ms->audio_channels));
|
||||
stream.writeAttribute("layout", QString::number(ms->audio_layout));
|
||||
stream.writeAttribute("frequency", QString::number(ms->audio_frequency));
|
||||
stream.writeAttribute("id", QString::number(ms.file_index));
|
||||
stream.writeAttribute("channels", QString::number(ms.audio_channels));
|
||||
stream.writeAttribute("layout", QString::number(ms.audio_layout));
|
||||
stream.writeAttribute("frequency", QString::number(ms.audio_frequency));
|
||||
stream.writeEndElement();
|
||||
}
|
||||
stream.writeEndElement();
|
||||
@@ -1084,6 +1139,12 @@ void Project::go_up_dir() {
|
||||
set_up_dir_enabled();
|
||||
}
|
||||
|
||||
void Project::make_new_menu() {
|
||||
QMenu new_menu(this);
|
||||
mainWindow->make_new_menu(&new_menu);
|
||||
new_menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void Project::add_recent_project(QString url) {
|
||||
bool found = false;
|
||||
for (int i=0;i<recent_projects.size();i++) {
|
||||
|
||||
@@ -77,6 +77,8 @@ public:
|
||||
|
||||
void start_preview_generator(Media* item, bool replacing);
|
||||
void get_all_media_from_table(QList<Media *> &items, QList<Media *> &list, int type = -1);
|
||||
|
||||
QWidget* toolbar_widget;
|
||||
public slots:
|
||||
void import_dialog();
|
||||
void delete_selected_media();
|
||||
@@ -103,6 +105,7 @@ private slots:
|
||||
void set_icon_view_size(int);
|
||||
void set_up_dir_enabled();
|
||||
void go_up_dir();
|
||||
void make_new_menu();
|
||||
};
|
||||
|
||||
class MediaThrobber : public QObject {
|
||||
|
||||
+72
-51
@@ -43,12 +43,6 @@ long refactor_frame_number(long framenumber, double source_frame_rate, double ta
|
||||
return qRound(((double)framenumber/source_frame_rate)*target_frame_rate);
|
||||
}
|
||||
|
||||
void draw_selection_rectangle(QPainter& painter, const QRect& rect) {
|
||||
painter.setPen(QColor(204, 204, 204));
|
||||
painter.setBrush(QColor(0, 0, 0, 32));
|
||||
painter.drawRect(rect);
|
||||
}
|
||||
|
||||
Timeline::Timeline(QWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
cursor_frame(0),
|
||||
@@ -80,6 +74,8 @@ Timeline::Timeline(QWidget *parent) :
|
||||
last_frame(0),
|
||||
scroll(0)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
setup_ui();
|
||||
|
||||
default_track_height = (QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96) * TRACK_DEFAULT_HEIGHT;
|
||||
@@ -182,7 +178,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
|
||||
can_import = m->ready;
|
||||
if (m->using_inout) {
|
||||
double source_fr = 30;
|
||||
if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0)->video_frame_rate)) source_fr = m->video_tracks.at(0)->video_frame_rate;
|
||||
if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate * m->speed;
|
||||
default_clip_in = refactor_frame_number(m->in, source_fr, seq->frame_rate);
|
||||
default_clip_out = refactor_frame_number(m->out, source_fr, seq->frame_rate);
|
||||
}
|
||||
@@ -214,7 +210,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
|
||||
switch (medium->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
// is video source a still image?
|
||||
if (m->video_tracks.size() > 0 && m->video_tracks[0]->infinite_length && m->audio_tracks.size() == 0) {
|
||||
if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) {
|
||||
g.out = g.in + 100;
|
||||
} else {
|
||||
long length = m->get_length_in_frames(seq->frame_rate);
|
||||
@@ -225,16 +221,20 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
|
||||
}
|
||||
|
||||
for (int j=0;j<m->audio_tracks.size();j++) {
|
||||
g.track = j;
|
||||
g.media_stream = m->audio_tracks.at(j)->file_index;
|
||||
ghosts.append(g);
|
||||
audio_ghosts = true;
|
||||
if (m->audio_tracks.at(j).enabled) {
|
||||
g.track = j;
|
||||
g.media_stream = m->audio_tracks.at(j).file_index;
|
||||
ghosts.append(g);
|
||||
audio_ghosts = true;
|
||||
}
|
||||
}
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = m->video_tracks.at(j)->file_index;
|
||||
ghosts.append(g);
|
||||
video_ghosts = true;
|
||||
if (m->video_tracks.at(j).enabled) {
|
||||
g.track = -1-j;
|
||||
g.media_stream = m->video_tracks.at(j).file_index;
|
||||
ghosts.append(g);
|
||||
video_ghosts = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
@@ -1043,15 +1043,17 @@ void Timeline::paste(bool insert) {
|
||||
void Timeline::ripple_to_in_point(bool in, bool ripple) {
|
||||
if (sequence != NULL) {
|
||||
if (sequence->clips.size() > 0) {
|
||||
if (!in && sequence->playhead == 0) return;
|
||||
|
||||
// get track count
|
||||
int track_min = INT_MAX;
|
||||
int track_max = INT_MIN;
|
||||
long sequence_end = 0;
|
||||
|
||||
bool playhead_falls_on_in = false;
|
||||
bool playhead_falls_on_out = false;
|
||||
long next_cut = LONG_MAX;
|
||||
long prev_cut = 0;
|
||||
|
||||
// find closest in point to playhead
|
||||
long in_point = in ? LONG_MIN : LONG_MAX;
|
||||
for (int i=0;i<sequence->clips.size();i++) {
|
||||
Clip* c = sequence->clips.at(i);
|
||||
if (c != NULL) {
|
||||
@@ -1060,32 +1062,36 @@ void Timeline::ripple_to_in_point(bool in, bool ripple) {
|
||||
|
||||
sequence_end = qMax(c->timeline_out, sequence_end);
|
||||
|
||||
if (sequence->playhead != in_point) {
|
||||
if ((in && c->timeline_in > in_point && c->timeline_in <= sequence->playhead)
|
||||
|| (!in && c->timeline_in < in_point && c->timeline_in >= sequence->playhead)) {
|
||||
in_point = c->timeline_in;
|
||||
}
|
||||
if ((in && c->timeline_out > in_point && c->timeline_out <= sequence->playhead)
|
||||
|| (!in && c->timeline_out < in_point && c->timeline_out >= sequence->playhead)) {
|
||||
in_point = c->timeline_out;
|
||||
}
|
||||
}
|
||||
if (c->timeline_in == sequence->playhead)
|
||||
playhead_falls_on_in = true;
|
||||
if (c->timeline_out == sequence->playhead)
|
||||
playhead_falls_on_out = true;
|
||||
if (c->timeline_in > sequence->playhead)
|
||||
next_cut = qMin(c->timeline_in, next_cut);
|
||||
if (c->timeline_out > sequence->playhead)
|
||||
next_cut = qMin(c->timeline_out, next_cut);
|
||||
if (c->timeline_in < sequence->playhead)
|
||||
prev_cut = qMax(c->timeline_in, prev_cut);
|
||||
if (c->timeline_out < sequence->playhead)
|
||||
prev_cut = qMax(c->timeline_out, prev_cut);
|
||||
}
|
||||
}
|
||||
|
||||
if (in && sequence->playhead == sequence_end) return;
|
||||
next_cut = qMin(sequence_end, next_cut);
|
||||
|
||||
QVector<Selection> areas;
|
||||
ComboAction* ca = new ComboAction();
|
||||
bool push_undo = true;
|
||||
long seek = sequence->playhead;
|
||||
|
||||
if (sequence->playhead == in_point) { // one frame mode
|
||||
if ((in && (playhead_falls_on_out || (playhead_falls_on_in && sequence->playhead == 0)))
|
||||
|| (!in && (playhead_falls_on_in || (playhead_falls_on_out && sequence->playhead == sequence_end)))) { // one frame mode
|
||||
if (ripple) {
|
||||
// set up deletion areas based on track count
|
||||
if (in) {
|
||||
in_point = sequence->playhead;
|
||||
} else {
|
||||
in_point = sequence->playhead - 1;
|
||||
long in_point = sequence->playhead;
|
||||
if (!in) {
|
||||
in_point--;
|
||||
seek--;
|
||||
}
|
||||
|
||||
if (in_point >= 0) {
|
||||
@@ -1110,16 +1116,22 @@ void Timeline::ripple_to_in_point(bool in, bool ripple) {
|
||||
} else {
|
||||
// set up deletion areas based on track count
|
||||
Selection s;
|
||||
s.in = qMin(in_point, sequence->playhead);
|
||||
s.out = qMax(in_point, sequence->playhead);
|
||||
for (int i=track_min;i<=track_max;i++) {
|
||||
s.track = i;
|
||||
areas.append(s);
|
||||
}
|
||||
if (in) seek = prev_cut;
|
||||
s.in = in ? prev_cut : sequence->playhead;
|
||||
s.out = in ? sequence->playhead : next_cut;
|
||||
|
||||
// trim and move clips around the in point
|
||||
delete_areas_and_relink(ca, areas);
|
||||
if (ripple) ripple_clips(ca, sequence, in_point, (in) ? (in_point - sequence->playhead) : (sequence->playhead - in_point));
|
||||
if (s.in == s.out) {
|
||||
push_undo = false;
|
||||
} else {
|
||||
for (int i=track_min;i<=track_max;i++) {
|
||||
s.track = i;
|
||||
areas.append(s);
|
||||
}
|
||||
|
||||
// trim and move clips around the in point
|
||||
delete_areas_and_relink(ca, areas);
|
||||
if (ripple) ripple_clips(ca, sequence, s.in, s.in - s.out);
|
||||
}
|
||||
}
|
||||
|
||||
if (push_undo) {
|
||||
@@ -1127,7 +1139,7 @@ void Timeline::ripple_to_in_point(bool in, bool ripple) {
|
||||
|
||||
update_ui(true);
|
||||
|
||||
if (in_point < sequence->playhead && ripple) panel_sequence_viewer->seek(in_point);
|
||||
if (seek != sequence->playhead && ripple) panel_sequence_viewer->seek(seek);
|
||||
} else {
|
||||
delete ca;
|
||||
}
|
||||
@@ -1355,12 +1367,21 @@ bool Timeline::snap_to_timeline(long* l, bool use_playhead, bool use_markers, bo
|
||||
}
|
||||
|
||||
void Timeline::set_marker() {
|
||||
QInputDialog d(this);
|
||||
d.setWindowTitle("Set Marker");
|
||||
d.setLabelText("Set marker name:");
|
||||
d.setInputMode(QInputDialog::TextInput);
|
||||
if (d.exec() == QDialog::Accepted) {
|
||||
undo_stack.push(new AddMarkerAction(sequence, sequence->playhead, d.textValue()));
|
||||
bool add_marker = !config.set_name_with_marker;
|
||||
QString marker_name;
|
||||
|
||||
if (!add_marker) {
|
||||
QInputDialog d(this);
|
||||
d.setWindowTitle("Set Marker");
|
||||
d.setLabelText("Set marker name:");
|
||||
d.setInputMode(QInputDialog::TextInput);
|
||||
add_marker = (d.exec() == QDialog::Accepted);
|
||||
marker_name = d.textValue();
|
||||
}
|
||||
|
||||
|
||||
if (add_marker) {
|
||||
undo_stack.push(new AddMarkerAction(sequence, sequence->playhead, marker_name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ struct FootageStream;
|
||||
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
|
||||
int getScreenPointFromFrame(double zoom, long frame);
|
||||
long getFrameFromScreenPoint(double zoom, int x);
|
||||
void draw_selection_rectangle(QPainter& painter, const QRect& rect);
|
||||
bool selection_contains_transition(const Selection& s, Clip* c, int type);
|
||||
void move_clip(ComboAction *ca, Clip *c, long iin, long iout, long iclip_in, int itrack, bool verify_transitions = true, bool relative = false);
|
||||
void ripple_clips(ComboAction *ca, Sequence* s, long point, long length, const QVector<int>& ignore = QVector<int>());
|
||||
|
||||
+36
-51
@@ -46,13 +46,15 @@ Viewer::Viewer(QWidget *parent) :
|
||||
panel_name("Viewer: "),
|
||||
minimum_zoom(1.0)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
setup_ui();
|
||||
|
||||
headers->viewer = this;
|
||||
headers->snapping = false;
|
||||
headers->show_text(false);
|
||||
glViewerPane->viewer = this;
|
||||
viewer_widget = glViewerPane->child;
|
||||
viewer_container->viewer = this;
|
||||
viewer_widget = viewer_container->child;
|
||||
viewer_widget->viewer = this;
|
||||
set_media(NULL);
|
||||
|
||||
@@ -67,9 +69,9 @@ Viewer::Viewer(QWidget *parent) :
|
||||
|
||||
connect(&playback_updater, SIGNAL(timeout()), this, SLOT(timer_update()));
|
||||
connect(&recording_flasher, SIGNAL(timeout()), this, SLOT(recording_flasher_update()));
|
||||
connect(horizontalScrollBar, SIGNAL(valueChanged(int)), headers, SLOT(set_scroll(int)));
|
||||
connect(horizontalScrollBar, SIGNAL(valueChanged(int)), viewer_widget, SLOT(set_waveform_scroll(int)));
|
||||
connect(horizontalScrollBar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double)));
|
||||
connect(horizontal_bar, SIGNAL(valueChanged(int)), headers, SLOT(set_scroll(int)));
|
||||
connect(horizontal_bar, SIGNAL(valueChanged(int)), viewer_widget, SLOT(set_waveform_scroll(int)));
|
||||
connect(horizontal_bar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double)));
|
||||
|
||||
update_playhead_timecode(0);
|
||||
update_end_timecode();
|
||||
@@ -439,35 +441,33 @@ void Viewer::set_zoom_value(double d) {
|
||||
}
|
||||
if (seq != NULL) {
|
||||
set_sb_max();
|
||||
if (!horizontalScrollBar->is_resizing())
|
||||
center_scroll_to_playhead(horizontalScrollBar, headers->get_zoom(), seq->playhead);
|
||||
if (!horizontal_bar->is_resizing())
|
||||
center_scroll_to_playhead(horizontal_bar, headers->get_zoom(), seq->playhead);
|
||||
}
|
||||
}
|
||||
|
||||
void Viewer::set_sb_max() {
|
||||
headers->set_scrollbar_max(horizontalScrollBar, seq->getEndFrame(), headers->width());
|
||||
headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width());
|
||||
}
|
||||
|
||||
void Viewer::setup_ui() {
|
||||
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
|
||||
QWidget* contents = new QWidget();
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(contents);
|
||||
layout->setSpacing(0);
|
||||
layout->setMargin(0);
|
||||
|
||||
glViewerPane = new ViewerContainer(contents);
|
||||
glViewerPane->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(glViewerPane);
|
||||
viewer_container = new ViewerContainer(contents);
|
||||
viewer_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(viewer_container);
|
||||
|
||||
headers = new TimelineHeader(contents);
|
||||
layout->addWidget(headers);
|
||||
|
||||
horizontalScrollBar = new ResizableScrollBar(contents);
|
||||
horizontalScrollBar->setSingleStep(20);
|
||||
horizontalScrollBar->setOrientation(Qt::Horizontal);
|
||||
layout->addWidget(horizontalScrollBar);
|
||||
horizontal_bar = new ResizableScrollBar(contents);
|
||||
horizontal_bar->setSingleStep(20);
|
||||
horizontal_bar->setOrientation(Qt::Horizontal);
|
||||
layout->addWidget(horizontal_bar);
|
||||
|
||||
QWidget* lower_controls = new QWidget(contents);
|
||||
|
||||
@@ -506,9 +506,8 @@ void Viewer::setup_ui() {
|
||||
playback_control_layout->addWidget(btnRewind);
|
||||
|
||||
btnPlay = new QPushButton(playback_controls);
|
||||
QIcon playIcon;
|
||||
playIcon.addFile(QStringLiteral(":/icons/play.png"), QSize(), QIcon::Normal, QIcon::Off);
|
||||
playIcon.addFile(QStringLiteral(":/icons/play-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
|
||||
playIcon.addFile(QStringLiteral(":/icons/play.png"), QSize(), QIcon::Normal, QIcon::On);
|
||||
playIcon.addFile(QStringLiteral(":/icons/play-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
|
||||
btnPlay->setIcon(playIcon);
|
||||
connect(btnPlay, SIGNAL(clicked(bool)), this, SLOT(toggle_play()));
|
||||
playback_control_layout->addWidget(btnPlay);
|
||||
@@ -573,14 +572,14 @@ void Viewer::set_media(Media* m) {
|
||||
seq->frame_rate = 30;
|
||||
|
||||
if (footage->video_tracks.size() > 0) {
|
||||
FootageStream* video_stream = footage->video_tracks.at(0);
|
||||
seq->width = video_stream->video_width;
|
||||
seq->height = video_stream->video_height;
|
||||
if (video_stream->video_frame_rate > 0 && !video_stream->infinite_length) seq->frame_rate = video_stream->video_frame_rate;
|
||||
const FootageStream& video_stream = footage->video_tracks.at(0);
|
||||
seq->width = video_stream.video_width;
|
||||
seq->height = video_stream.video_height;
|
||||
if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate * footage->speed;
|
||||
|
||||
Clip* c = new Clip(seq);
|
||||
c->media = media;
|
||||
c->media_stream = video_stream->file_index;
|
||||
c->media_stream = video_stream.file_index;
|
||||
c->timeline_in = 0;
|
||||
c->timeline_out = footage->get_length_in_frames(seq->frame_rate);
|
||||
if (c->timeline_out <= 0) c->timeline_out = 150;
|
||||
@@ -594,12 +593,12 @@ void Viewer::set_media(Media* m) {
|
||||
}
|
||||
|
||||
if (footage->audio_tracks.size() > 0) {
|
||||
FootageStream* audio_stream = footage->audio_tracks.at(0);
|
||||
seq->audio_frequency = audio_stream->audio_frequency;
|
||||
const FootageStream& audio_stream = footage->audio_tracks.at(0);
|
||||
seq->audio_frequency = audio_stream.audio_frequency;
|
||||
|
||||
Clip* c = new Clip(seq);
|
||||
c->media = media;
|
||||
c->media_stream = audio_stream->file_index;
|
||||
c->media_stream = audio_stream.file_index;
|
||||
c->timeline_in = 0;
|
||||
c->timeline_out = footage->get_length_in_frames(seq->frame_rate);
|
||||
c->track = 0;
|
||||
@@ -610,7 +609,7 @@ void Viewer::set_media(Media* m) {
|
||||
if (footage->video_tracks.size() == 0) {
|
||||
viewer_widget->waveform = true;
|
||||
viewer_widget->waveform_clip = c;
|
||||
viewer_widget->waveform_ms = audio_stream;
|
||||
viewer_widget->waveform_ms = &audio_stream;
|
||||
viewer_widget->update();
|
||||
}
|
||||
} else {
|
||||
@@ -628,26 +627,6 @@ void Viewer::set_media(Media* m) {
|
||||
set_sequence(false, seq);
|
||||
}
|
||||
|
||||
void Viewer::on_btnSkipToStart_clicked() {
|
||||
go_to_start();
|
||||
}
|
||||
|
||||
void Viewer::on_btnSkipToEnd_clicked() {
|
||||
go_to_end();
|
||||
}
|
||||
|
||||
void Viewer::on_btnRewind_clicked() {
|
||||
previous_frame();
|
||||
}
|
||||
|
||||
void Viewer::on_btnFastForward_clicked() {
|
||||
next_frame();
|
||||
}
|
||||
|
||||
void Viewer::on_btnPlay_clicked() {
|
||||
toggle_play();
|
||||
}
|
||||
|
||||
void Viewer::update_playhead() {
|
||||
seek(currentTimecode->value());
|
||||
}
|
||||
@@ -696,8 +675,14 @@ void Viewer::clean_created_seq() {
|
||||
}
|
||||
|
||||
void Viewer::set_sequence(bool main, Sequence *s) {
|
||||
pause();
|
||||
|
||||
reset_all_audio();
|
||||
|
||||
if (seq != NULL) {
|
||||
closeActiveClips(seq);
|
||||
}
|
||||
|
||||
main_sequence = main;
|
||||
seq = (main) ? sequence : s;
|
||||
|
||||
@@ -721,7 +706,7 @@ void Viewer::set_sequence(bool main, Sequence *s) {
|
||||
update_playhead_timecode(seq->playhead);
|
||||
update_end_timecode();
|
||||
|
||||
glViewerPane->adjust();
|
||||
viewer_container->adjust();
|
||||
|
||||
setWindowTitle(panel_name + seq->name);
|
||||
} else {
|
||||
@@ -739,5 +724,5 @@ void Viewer::set_sequence(bool main, Sequence *s) {
|
||||
}
|
||||
|
||||
void Viewer::set_playpause_icon(bool play) {
|
||||
btnPlay->setIcon(QIcon((play) ? ":/icons/play.png" : ":/icons/pause.png"));
|
||||
btnPlay->setIcon(play ? playIcon : QIcon(":/icons/pause.png"));
|
||||
}
|
||||
|
||||
+5
-8
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QTimer>
|
||||
#include <QIcon>
|
||||
|
||||
class Timeline;
|
||||
class ViewerWidget;
|
||||
@@ -77,12 +78,6 @@ public slots:
|
||||
void go_to_end();
|
||||
|
||||
private slots:
|
||||
void on_btnSkipToStart_clicked();
|
||||
void on_btnRewind_clicked();
|
||||
void on_btnPlay_clicked();
|
||||
void on_btnFastForward_clicked();
|
||||
void on_btnSkipToEnd_clicked();
|
||||
|
||||
void update_playhead();
|
||||
void timer_update();
|
||||
void recording_flasher_update();
|
||||
@@ -99,11 +94,13 @@ private:
|
||||
void set_zoom_value(double d);
|
||||
void set_sb_max();
|
||||
|
||||
QIcon playIcon;
|
||||
|
||||
void setup_ui();
|
||||
|
||||
TimelineHeader* headers;
|
||||
ResizableScrollBar* horizontalScrollBar;
|
||||
ViewerContainer* glViewerPane;
|
||||
ResizableScrollBar* horizontal_bar;
|
||||
ViewerContainer* viewer_container;
|
||||
LabelSlider* currentTimecode;
|
||||
QLabel* endTimecode;
|
||||
|
||||
|
||||
+8
-1
@@ -8,6 +8,7 @@
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/audiomonitor.h"
|
||||
#include "playback/playback.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <QApplication>
|
||||
@@ -100,13 +101,19 @@ void stop_audio() {
|
||||
}
|
||||
|
||||
void clear_audio_ibuffer() {
|
||||
if (audio_thread != NULL) audio_thread->lock.lock();
|
||||
memset(audio_ibuffer, 0, audio_ibuffer_size);
|
||||
audio_ibuffer_read = 0;
|
||||
if (audio_thread != NULL) audio_thread->lock.unlock();
|
||||
}
|
||||
|
||||
int current_audio_freq() {
|
||||
return rendering ? sequence->audio_frequency : audio_output->format().sampleRate();
|
||||
}
|
||||
|
||||
int get_buffer_offset_from_frame(double framerate, long frame) {
|
||||
if (frame >= audio_ibuffer_frame) {
|
||||
return qFloor(((double) (frame - audio_ibuffer_frame)/framerate)*audio_output->format().sampleRate())*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
|
||||
return qFloor(((double) (frame - audio_ibuffer_frame)/framerate)*current_audio_freq())*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
|
||||
} else {
|
||||
dout << "[WARNING] Invalid values passed to get_buffer_offset_from_frame";
|
||||
return 0;
|
||||
|
||||
@@ -43,6 +43,8 @@ extern double audio_ibuffer_timecode;
|
||||
extern bool audio_scrub;
|
||||
void clear_audio_ibuffer();
|
||||
|
||||
int current_audio_freq();
|
||||
|
||||
bool is_audio_device_set();
|
||||
|
||||
void init_audio();
|
||||
|
||||
+516
-513
File diff suppressed because it is too large
Load Diff
+86
-85
@@ -37,39 +37,39 @@ bool texture_failed = false;
|
||||
bool rendering = false;
|
||||
|
||||
bool clip_uses_cacher(Clip* clip) {
|
||||
return (clip->media == NULL && clip->track >= 0) || (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE);
|
||||
return (clip->media == NULL && clip->track >= 0) || (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE);
|
||||
}
|
||||
|
||||
void open_clip(Clip* clip, bool multithreaded) {
|
||||
if (clip_uses_cacher(clip)) {
|
||||
clip->multithreaded = multithreaded;
|
||||
if (multithreaded) {
|
||||
if (clip->open_lock.tryLock()) {
|
||||
// maybe keep cacher instance in memory while clip exists for performance?
|
||||
clip->cacher = new Cacher(clip);
|
||||
QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater()));
|
||||
clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority);
|
||||
}
|
||||
} else {
|
||||
clip->finished_opening = false;
|
||||
clip->open = true;
|
||||
if (clip_uses_cacher(clip)) {
|
||||
clip->multithreaded = multithreaded;
|
||||
if (multithreaded) {
|
||||
if (clip->open_lock.tryLock()) {
|
||||
// maybe keep cacher instance in memory while clip exists for performance?
|
||||
clip->cacher = new Cacher(clip);
|
||||
QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater()));
|
||||
clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority);
|
||||
}
|
||||
} else {
|
||||
clip->finished_opening = false;
|
||||
clip->open = true;
|
||||
|
||||
open_clip_worker(clip);
|
||||
}
|
||||
} else {
|
||||
clip->open = true;
|
||||
}
|
||||
open_clip_worker(clip);
|
||||
}
|
||||
} else {
|
||||
clip->open = true;
|
||||
}
|
||||
}
|
||||
|
||||
void close_clip(Clip* clip) {
|
||||
void close_clip(Clip* clip, bool wait) {
|
||||
// destroy opengl texture in main thread
|
||||
if (clip->texture != NULL) {
|
||||
if (clip->texture != NULL) {
|
||||
delete clip->texture;
|
||||
clip->texture = NULL;
|
||||
}
|
||||
|
||||
for (int i=0;i<clip->effects.size();i++) {
|
||||
clip->effects.at(i)->close();
|
||||
if (clip->effects.at(i)->is_open()) clip->effects.at(i)->close();
|
||||
}
|
||||
|
||||
if (clip->fbo != NULL) {
|
||||
@@ -79,29 +79,30 @@ void close_clip(Clip* clip) {
|
||||
clip->fbo = NULL;
|
||||
}
|
||||
|
||||
if (clip_uses_cacher(clip)) {
|
||||
if (clip->multithreaded) {
|
||||
clip->cacher->caching = false;
|
||||
clip->can_cache.wakeAll();
|
||||
} else {
|
||||
close_clip_worker(clip);
|
||||
}
|
||||
} else {
|
||||
if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_SEQUENCE)
|
||||
closeActiveClips(clip->media->to_sequence(), false);
|
||||
if (clip_uses_cacher(clip)) {
|
||||
if (clip->multithreaded) {
|
||||
clip->cacher->caching = false;
|
||||
clip->can_cache.wakeAll();
|
||||
if (wait) clip->cacher->wait();
|
||||
} else {
|
||||
close_clip_worker(clip);
|
||||
}
|
||||
} else {
|
||||
if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_SEQUENCE)
|
||||
closeActiveClips(clip->media->to_sequence());
|
||||
|
||||
clip->open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip*>& nests) {
|
||||
if (clip_uses_cacher(clip)) {
|
||||
if (clip_uses_cacher(clip)) {
|
||||
if (clip->multithreaded) {
|
||||
clip->cacher->playhead = playhead;
|
||||
clip->cacher->reset = reset;
|
||||
clip->cacher->nests = nests;
|
||||
clip->cacher->scrubbing = scrubbing;
|
||||
if (reset && clip->queue.size() > 0) clip->cacher->interrupt = true;
|
||||
clip->cacher->scrubbing = scrubbing;
|
||||
if (reset && clip->queue.size() > 0) clip->cacher->interrupt = true;
|
||||
|
||||
clip->can_cache.wakeAll();
|
||||
} else {
|
||||
@@ -111,15 +112,15 @@ void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<C
|
||||
}
|
||||
|
||||
double get_timecode(Clip* c, long playhead) {
|
||||
return ((double)(playhead-c->get_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate);
|
||||
return ((double)(playhead-c->get_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate);
|
||||
}
|
||||
|
||||
void get_clip_frame(Clip* c, long playhead) {
|
||||
if (c->finished_opening) {
|
||||
FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
|
||||
int64_t target_pts = qMax(static_cast<int64_t>(0), playhead_to_timestamp(c, playhead));
|
||||
int64_t second_pts = qRound64(av_q2d(av_inv_q(c->stream->time_base)));
|
||||
int64_t target_pts = qMax(static_cast<int64_t>(0), playhead_to_timestamp(c, playhead));
|
||||
int64_t second_pts = qRound64(av_q2d(av_inv_q(c->stream->time_base)));
|
||||
if (ms->video_interlacing != VIDEO_PROGRESSIVE) {
|
||||
target_pts *= 2;
|
||||
second_pts *= 2;
|
||||
@@ -192,14 +193,14 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
#endif
|
||||
c->reached_end = false;
|
||||
cache = false;
|
||||
} else if (target_pts != c->last_invalid_ts && (target_pts < target_frame->pts || pts_diff > second_pts)) {
|
||||
} else if (target_pts != c->last_invalid_ts && (target_pts < target_frame->pts || pts_diff > second_pts)) {
|
||||
|
||||
#ifdef GCF_DEBUG
|
||||
dout << "GCF ==> RESET" << target_pts << "(" << target_frame->pts << "-" << target_frame->pts+target_frame->pkt_duration << ")";
|
||||
#endif
|
||||
if (!config.fast_seeking) target_frame = NULL;
|
||||
if (!config.fast_seeking) target_frame = NULL;
|
||||
reset = true;
|
||||
c->last_invalid_ts = target_pts;
|
||||
c->last_invalid_ts = target_pts;
|
||||
} else {
|
||||
#ifdef GCF_DEBUG
|
||||
dout << "GCF ==> WAIT - target pts:" << target_pts << "closest frame:" << target_frame->pts;
|
||||
@@ -225,26 +226,26 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
int nb_components = av_pix_fmt_desc_get(static_cast<enum AVPixelFormat>(c->pix_fmt))->nb_components;
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, target_frame->linesize[0]/nb_components);
|
||||
|
||||
bool copied = false;
|
||||
uint8_t* data = target_frame->data[0];
|
||||
int frame_size;
|
||||
bool copied = false;
|
||||
uint8_t* data = target_frame->data[0];
|
||||
int frame_size;
|
||||
|
||||
for (int i=0;i<c->effects.size();i++) {
|
||||
Effect* e = c->effects.at(i);
|
||||
if (e->enable_image) {
|
||||
if (!copied) {
|
||||
frame_size = target_frame->linesize[0]*target_frame->height;
|
||||
data = new uint8_t[frame_size];
|
||||
memcpy(data, target_frame->data[0], frame_size);
|
||||
copied = true;
|
||||
}
|
||||
e->process_image(get_timecode(c, playhead), data, frame_size);
|
||||
}
|
||||
}
|
||||
for (int i=0;i<c->effects.size();i++) {
|
||||
Effect* e = c->effects.at(i);
|
||||
if (e->enable_image) {
|
||||
if (!copied) {
|
||||
frame_size = target_frame->linesize[0]*target_frame->height;
|
||||
data = new uint8_t[frame_size];
|
||||
memcpy(data, target_frame->data[0], frame_size);
|
||||
copied = true;
|
||||
}
|
||||
e->process_image(get_timecode(c, playhead), data, frame_size);
|
||||
}
|
||||
}
|
||||
|
||||
c->texture->setData(0, get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8, data);
|
||||
|
||||
if (copied) delete [] data;
|
||||
if (copied) delete [] data;
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
@@ -252,20 +253,22 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
c->queue_lock.unlock();
|
||||
|
||||
// get more frames
|
||||
QVector<Clip*> empty;
|
||||
if (cache) cache_clip(c, playhead, reset, false, empty);
|
||||
QVector<Clip*> empty;
|
||||
if (cache) cache_clip(c, playhead, reset, false, empty);
|
||||
}
|
||||
}
|
||||
|
||||
long playhead_to_clip_frame(Clip* c, long playhead) {
|
||||
return (qMax(0L, playhead - c->get_timeline_in_with_transition()) + c->get_clip_in_with_transition());
|
||||
return (qMax(0L, playhead - c->get_timeline_in_with_transition()) + c->get_clip_in_with_transition());
|
||||
}
|
||||
|
||||
double playhead_to_clip_seconds(Clip* c, long playhead) {
|
||||
// returns time in seconds
|
||||
long clip_frame = playhead_to_clip_frame(c, playhead);
|
||||
if (c->reverse) clip_frame = c->getMaximumLength() - clip_frame - 1;
|
||||
return ((double) clip_frame/c->sequence->frame_rate)*c->speed;
|
||||
double secs = ((double) clip_frame/c->sequence->frame_rate)*c->speed;
|
||||
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) secs *= c->media->to_footage()->speed;
|
||||
return secs;
|
||||
}
|
||||
|
||||
int64_t seconds_to_timestamp(Clip* c, double seconds) {
|
||||
@@ -278,11 +281,11 @@ int64_t playhead_to_timestamp(Clip* c, long playhead) {
|
||||
|
||||
int retrieve_next_frame(Clip* c, AVFrame* f) {
|
||||
int result = 0;
|
||||
int receive_ret;
|
||||
int receive_ret;
|
||||
|
||||
// do we need to retrieve a new packet for a new frame?
|
||||
av_frame_unref(f);
|
||||
while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) {
|
||||
while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) {
|
||||
int read_ret = 0;
|
||||
do {
|
||||
if (c->pkt_written) {
|
||||
@@ -293,15 +296,15 @@ int retrieve_next_frame(Clip* c, AVFrame* f) {
|
||||
if (read_ret >= 0) {
|
||||
c->pkt_written = true;
|
||||
}
|
||||
} while (read_ret >= 0 && c->pkt->stream_index != c->media_stream);
|
||||
} while (read_ret >= 0 && c->pkt->stream_index != c->media_stream);
|
||||
|
||||
if (read_ret >= 0) {
|
||||
int send_ret = avcodec_send_packet(c->codecCtx, c->pkt);
|
||||
if (send_ret < 0) {
|
||||
dout << "[ERROR] Failed to send packet to decoder." << send_ret;
|
||||
return send_ret;
|
||||
return send_ret;
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
if (read_ret == AVERROR_EOF) {
|
||||
int send_ret = avcodec_send_packet(c->codecCtx, NULL);
|
||||
if (send_ret < 0) {
|
||||
@@ -323,32 +326,30 @@ int retrieve_next_frame(Clip* c, AVFrame* f) {
|
||||
}
|
||||
|
||||
bool is_clip_active(Clip* c, long playhead) {
|
||||
return c->enabled
|
||||
&& c->get_timeline_in_with_transition() < playhead + ceil(c->sequence->frame_rate*2)
|
||||
&& c->get_timeline_out_with_transition() > playhead
|
||||
&& playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition() < c->getMaximumLength();
|
||||
return c->enabled
|
||||
&& c->get_timeline_in_with_transition() < playhead + ceil(c->sequence->frame_rate*2)
|
||||
&& c->get_timeline_out_with_transition() > playhead
|
||||
&& playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition() < c->getMaximumLength();
|
||||
}
|
||||
|
||||
void set_sequence(Sequence* s) {
|
||||
closeActiveClips(sequence, true);
|
||||
panel_effect_controls->clear_effects(true);
|
||||
sequence = s;
|
||||
sequence = s;
|
||||
panel_sequence_viewer->set_main_sequence();
|
||||
panel_timeline->update_sequence();
|
||||
panel_timeline->setFocus();
|
||||
panel_timeline->update_sequence();
|
||||
panel_timeline->setFocus();
|
||||
}
|
||||
|
||||
void closeActiveClips(Sequence *s, bool wait) {
|
||||
void closeActiveClips(Sequence *s) {
|
||||
if (s != NULL) {
|
||||
for (int i=0;i<s->clips.size();i++) {
|
||||
Clip* c = s->clips.at(i);
|
||||
if (c != NULL) {
|
||||
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
closeActiveClips(c->media->to_sequence(), wait);
|
||||
if (c->open) close_clip(c);
|
||||
} else if (clip_uses_cacher(c) && c->open) {
|
||||
close_clip(c);
|
||||
if (c->multithreaded && wait) c->cacher->wait();
|
||||
for (int i=0;i<s->clips.size();i++) {
|
||||
Clip* c = s->clips.at(i);
|
||||
if (c != NULL) {
|
||||
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
closeActiveClips(c->media->to_sequence());
|
||||
if (c->open) close_clip(c, true);
|
||||
} else if (c->open) {
|
||||
close_clip(c, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ extern bool rendering;
|
||||
bool clip_uses_cacher(Clip* clip);
|
||||
void open_clip(Clip* clip, bool multithreaded);
|
||||
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip *> &nests);
|
||||
void close_clip(Clip* clip);
|
||||
void close_clip(Clip* clip, bool wait);
|
||||
void cache_audio_worker(Clip* c, bool write_A);
|
||||
void cache_video_worker(Clip* c, long playhead);
|
||||
void handle_media(Sequence* sequence, long playhead, bool multithreaded);
|
||||
@@ -32,6 +32,6 @@ int retrieve_next_frame(Clip* c, AVFrame* f);
|
||||
bool is_clip_active(Clip* c, long playhead);
|
||||
void get_next_audio(Clip* c, bool mix);
|
||||
void set_sequence(Sequence* s);
|
||||
void closeActiveClips(Sequence* s, bool wait);
|
||||
void closeActiveClips(Sequence* s);
|
||||
|
||||
#endif // PLAYBACK_H
|
||||
|
||||
+13
-18
@@ -12,6 +12,7 @@
|
||||
#include "project/media.h"
|
||||
#include "io/clipboard.h"
|
||||
#include "undo.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
@@ -113,9 +114,9 @@ void Clip::refresh() {
|
||||
Footage* m = media->to_footage();
|
||||
|
||||
if (track < 0 && m->video_tracks.size() > 0) {
|
||||
media_stream = m->video_tracks.at(0)->file_index;
|
||||
media_stream = m->video_tracks.at(0).file_index;
|
||||
} else if (track >= 0 && m->audio_tracks.size() > 0) {
|
||||
media_stream = m->audio_tracks.at(0)->file_index;
|
||||
media_stream = m->audio_tracks.at(0).file_index;
|
||||
}
|
||||
}
|
||||
replaced = false;
|
||||
@@ -171,12 +172,7 @@ Transition* Clip::get_closing_transition() {
|
||||
|
||||
Clip::~Clip() {
|
||||
if (open) {
|
||||
close_clip(this);
|
||||
|
||||
// make sure clip has closed before clip is destroyed
|
||||
if (multithreaded && media != NULL && media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
cacher->wait();
|
||||
}
|
||||
close_clip(this, true);
|
||||
}
|
||||
|
||||
if (opening_transition != -1) this->sequence->hard_delete_transition(this, TA_OPENING_TRANSITION);
|
||||
@@ -241,7 +237,7 @@ void Clip::recalculateMaxLength() {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Footage* m = media->to_footage();
|
||||
FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream);
|
||||
const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream);
|
||||
if (ms != NULL && ms->infinite_length) {
|
||||
calculated_length = LONG_MAX;
|
||||
} else {
|
||||
@@ -269,7 +265,7 @@ int Clip::getWidth() {
|
||||
switch (media->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream);
|
||||
const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream);
|
||||
if (ms != NULL) return ms->video_width;
|
||||
if (sequence != NULL) return sequence->width;
|
||||
}
|
||||
@@ -287,7 +283,7 @@ int Clip::getHeight() {
|
||||
switch (media->get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream);
|
||||
const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream);
|
||||
if (ms != NULL) return ms->video_height;
|
||||
if (sequence != NULL) return sequence->height;
|
||||
}
|
||||
@@ -309,17 +305,16 @@ void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_t
|
||||
track);
|
||||
}
|
||||
|
||||
// move keyframes
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
Effect* e = effects.at(i);
|
||||
for (int j=0;j<e->row_count();j++) {
|
||||
EffectRow* r = e->row(j);
|
||||
for (int k=0;k<r->keyframe_times.size();k++) {
|
||||
long new_pos = r->keyframe_times.at(k) * multiplier;
|
||||
KeyframeMove* km = new KeyframeMove();
|
||||
km->movement = new_pos - r->keyframe_times.at(k);
|
||||
km->rows.append(r);
|
||||
km->keyframes.append(k);
|
||||
ca->append(km);
|
||||
for (int l=0;l<r->fieldCount();l++) {
|
||||
EffectField* f = r->field(l);
|
||||
for (int k=0;k<f->keyframes.size();k++) {
|
||||
ca->append(new SetLong(&f->keyframes[k].time, f->keyframes[k].time, f->keyframes[k].time * multiplier));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+234
-182
@@ -10,6 +10,7 @@
|
||||
#include "project/clip.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "panels/grapheditor.h"
|
||||
#include "ui/checkboxex.h"
|
||||
#include "debug.h"
|
||||
#include "io/path.h"
|
||||
@@ -51,14 +52,14 @@ Effect* create_effect(Clip* c, const EffectMeta* em) {
|
||||
switch (em->internal) {
|
||||
case EFFECT_INTERNAL_TRANSFORM: return new TransformEffect(c, em);
|
||||
case EFFECT_INTERNAL_TEXT: return new TextEffect(c, em);
|
||||
case EFFECT_INTERNAL_TIMECODE: return new TimecodeEffect(c, em);
|
||||
case EFFECT_INTERNAL_TIMECODE: return new TimecodeEffect(c, em);
|
||||
case EFFECT_INTERNAL_SOLID: return new SolidEffect(c, em);
|
||||
case EFFECT_INTERNAL_NOISE: return new AudioNoiseEffect(c, em);
|
||||
case EFFECT_INTERNAL_VOLUME: return new VolumeEffect(c, em);
|
||||
case EFFECT_INTERNAL_PAN: return new PanEffect(c, em);
|
||||
case EFFECT_INTERNAL_TONE: return new ToneEffect(c, em);
|
||||
case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em);
|
||||
case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em);
|
||||
case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em);
|
||||
case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em);
|
||||
}
|
||||
} else {
|
||||
dout << "[ERROR] Invalid effect data";
|
||||
@@ -68,12 +69,12 @@ Effect* create_effect(Clip* c, const EffectMeta* em) {
|
||||
}
|
||||
|
||||
const EffectMeta* get_internal_meta(int internal_id, int type) {
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
if (effects.at(i).internal == internal_id && effects.at(i).type == type) {
|
||||
return &effects.at(i);
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
if (effects.at(i).internal == internal_id && effects.at(i).type == type) {
|
||||
return &effects.at(i);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void load_internal_effects() {
|
||||
@@ -81,85 +82,90 @@ void load_internal_effects() {
|
||||
|
||||
// internal effects
|
||||
em.type = EFFECT_TYPE_EFFECT;
|
||||
em.subtype = EFFECT_TYPE_AUDIO;
|
||||
em.subtype = EFFECT_TYPE_AUDIO;
|
||||
|
||||
em.name = "Volume";
|
||||
em.internal = EFFECT_INTERNAL_VOLUME;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Pan";
|
||||
em.internal = EFFECT_INTERNAL_PAN;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Tone";
|
||||
em.internal = EFFECT_INTERNAL_TONE;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Noise";
|
||||
em.internal = EFFECT_INTERNAL_NOISE;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.subtype = EFFECT_TYPE_VIDEO;
|
||||
em.subtype = EFFECT_TYPE_VIDEO;
|
||||
|
||||
em.name = "Transform";
|
||||
em.category = "Distort";
|
||||
em.internal = EFFECT_INTERNAL_TRANSFORM;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Corner Pin";
|
||||
em.internal = EFFECT_INTERNAL_CORNERPIN;
|
||||
effects.append(em);
|
||||
em.name = "Corner Pin";
|
||||
em.internal = EFFECT_INTERNAL_CORNERPIN;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Mask";
|
||||
em.internal = EFFECT_INTERNAL_MASK;
|
||||
effects.append(em);
|
||||
/*em.name = "Mask";
|
||||
em.internal = EFFECT_INTERNAL_MASK;
|
||||
effects.append(em);*/
|
||||
|
||||
em.name = "Shake";
|
||||
em.internal = EFFECT_INTERNAL_SHAKE;
|
||||
effects.append(em);
|
||||
em.name = "Shake";
|
||||
em.internal = EFFECT_INTERNAL_SHAKE;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Text";
|
||||
em.category = "Render";
|
||||
em.internal = EFFECT_INTERNAL_TEXT;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Timecode";
|
||||
em.internal = EFFECT_INTERNAL_TIMECODE;
|
||||
effects.append(em);
|
||||
em.name = "Timecode";
|
||||
em.internal = EFFECT_INTERNAL_TIMECODE;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Solid";
|
||||
em.name = "Solid";
|
||||
em.internal = EFFECT_INTERNAL_SOLID;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
// internal transitions
|
||||
em.type = EFFECT_TYPE_TRANSITION;
|
||||
em.category = "";
|
||||
|
||||
em.name = "Cross Dissolve";
|
||||
em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE;
|
||||
effects.append(em);
|
||||
em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE;
|
||||
effects.append(em);
|
||||
|
||||
em.subtype = EFFECT_TYPE_AUDIO;
|
||||
em.subtype = EFFECT_TYPE_AUDIO;
|
||||
|
||||
em.name = "Linear Fade";
|
||||
em.internal = TRANSITION_INTERNAL_LINEARFADE;
|
||||
effects.append(em);
|
||||
em.internal = TRANSITION_INTERNAL_LINEARFADE;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Exponential Fade";
|
||||
em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE;
|
||||
effects.append(em);
|
||||
em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Logarithmic Fade";
|
||||
em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE;
|
||||
effects.append(em);
|
||||
em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE;
|
||||
effects.append(em);
|
||||
}
|
||||
|
||||
void load_shader_effects() {
|
||||
QList<QString> get_effects_paths() {
|
||||
QList<QString> effects_paths;
|
||||
effects_paths.append(get_app_dir() + "/effects");
|
||||
effects_paths.append(get_app_dir() + "/../share/olive-editor/effects");
|
||||
QString env_path(qgetenv("OLIVE_EFFECTS_PATH"));
|
||||
if (!env_path.isEmpty()) effects_paths.append(env_path);
|
||||
return effects_paths;
|
||||
}
|
||||
|
||||
void load_shader_effects() {
|
||||
QList<QString> effects_paths = get_effects_paths();
|
||||
|
||||
for (int h=0;h<effects_paths.size();h++) {
|
||||
const QString& effects_path = effects_paths.at(h);
|
||||
@@ -235,29 +241,29 @@ void EffectInit::run() {
|
||||
|
||||
Effect::Effect(Clip* c, const EffectMeta *em) :
|
||||
parent_clip(c),
|
||||
meta(em),
|
||||
meta(em),
|
||||
enable_shader(false),
|
||||
enable_coords(false),
|
||||
enable_superimpose(false),
|
||||
enable_image(false),
|
||||
enable_superimpose(false),
|
||||
enable_image(false),
|
||||
glslProgram(NULL),
|
||||
texture(NULL),
|
||||
isOpen(false),
|
||||
bound(false),
|
||||
enable_always_update(false)
|
||||
texture(NULL),
|
||||
isOpen(false),
|
||||
bound(false),
|
||||
enable_always_update(false)
|
||||
{
|
||||
// set up base UI
|
||||
container = new CollapsibleWidget();
|
||||
connect(container->enabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed()));
|
||||
ui = new QWidget();
|
||||
ui_layout = new QGridLayout();
|
||||
// set up base UI
|
||||
container = new CollapsibleWidget();
|
||||
connect(container->enabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed()));
|
||||
ui = new QWidget();
|
||||
ui_layout = new QGridLayout();
|
||||
ui_layout->setSpacing(4);
|
||||
ui->setLayout(ui_layout);
|
||||
ui->setLayout(ui_layout);
|
||||
container->setContents(ui);
|
||||
|
||||
connect(container->title_bar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&)));
|
||||
|
||||
// set up UI from effect file
|
||||
// set up UI from effect file
|
||||
container->setText(em->name);
|
||||
|
||||
if (!em->filename.isEmpty()) {
|
||||
@@ -276,7 +282,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) :
|
||||
}
|
||||
}
|
||||
if (!row_name.isEmpty()) {
|
||||
EffectRow* row = add_row(row_name + ":");
|
||||
EffectRow* row = add_row(row_name);
|
||||
while (!reader.atEnd() && !(reader.name() == "row" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "field" && reader.isStartElement()) {
|
||||
@@ -310,7 +316,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) :
|
||||
if (id.isEmpty()) {
|
||||
dout << "[ERROR] Couldn't load field from" << em->filename << "- ID cannot be empty.";
|
||||
} else if (type > -1) {
|
||||
EffectField* field = row->add_field(type, id);
|
||||
EffectField* field = row->add_field(type, id);
|
||||
connect(field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
@@ -446,9 +452,9 @@ Effect::~Effect() {
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
delete rows.at(i);
|
||||
}
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
delete gizmos.at(i);
|
||||
}
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
delete gizmos.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::copy_field_keyframes(Effect* e) {
|
||||
@@ -456,19 +462,17 @@ void Effect::copy_field_keyframes(Effect* e) {
|
||||
EffectRow* row = rows.at(i);
|
||||
EffectRow* copy_row = e->rows.at(i);
|
||||
copy_row->setKeyframing(row->isKeyframing());
|
||||
copy_row->keyframe_times = row->keyframe_times;
|
||||
copy_row->keyframe_types = row->keyframe_types;
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
copy_row->field(j)->set_current_data(field->get_current_data());
|
||||
copy_row->field(j)->keyframe_data = field->keyframe_data;
|
||||
EffectField* copy_field = copy_row->field(j);
|
||||
copy_field->keyframes = field->keyframes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EffectRow* Effect::add_row(const QString& name, bool savable) {
|
||||
EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size());
|
||||
rows.append(row);
|
||||
EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size());
|
||||
rows.append(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
@@ -477,52 +481,53 @@ EffectRow* Effect::row(int i) {
|
||||
}
|
||||
|
||||
int Effect::row_count() {
|
||||
return rows.size();
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
EffectGizmo *Effect::add_gizmo(int type) {
|
||||
EffectGizmo* gizmo = new EffectGizmo(type);
|
||||
gizmos.append(gizmo);
|
||||
return gizmo;
|
||||
EffectGizmo* gizmo = new EffectGizmo(type);
|
||||
gizmos.append(gizmo);
|
||||
return gizmo;
|
||||
}
|
||||
|
||||
EffectGizmo *Effect::gizmo(int i) {
|
||||
return gizmos.at(i);
|
||||
return gizmos.at(i);
|
||||
}
|
||||
|
||||
int Effect::gizmo_count(){
|
||||
return gizmos.size();
|
||||
return gizmos.size();
|
||||
}
|
||||
|
||||
void Effect::refresh() {}
|
||||
|
||||
void Effect::field_changed() {
|
||||
panel_sequence_viewer->viewer_widget->update();
|
||||
panel_sequence_viewer->viewer_widget->update();
|
||||
panel_graph_editor->update_panel();
|
||||
}
|
||||
|
||||
void Effect::show_context_menu(const QPoint& pos) {
|
||||
if (meta->type == EFFECT_TYPE_EFFECT) {
|
||||
QMenu menu(mainWindow);
|
||||
if (meta->type == EFFECT_TYPE_EFFECT) {
|
||||
QMenu menu(mainWindow);
|
||||
|
||||
int index = get_index_in_clip();
|
||||
int index = get_index_in_clip();
|
||||
|
||||
if (index > 0) {
|
||||
QAction* move_up = menu.addAction("Move &Up");
|
||||
connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up()));
|
||||
}
|
||||
if (index > 0) {
|
||||
QAction* move_up = menu.addAction("Move &Up");
|
||||
connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up()));
|
||||
}
|
||||
|
||||
if (index < parent_clip->effects.size() - 1) {
|
||||
QAction* move_down = menu.addAction("Move &Down");
|
||||
connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down()));
|
||||
}
|
||||
if (index < parent_clip->effects.size() - 1) {
|
||||
QAction* move_down = menu.addAction("Move &Down");
|
||||
connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down()));
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
menu.addSeparator();
|
||||
|
||||
QAction* del_action = menu.addAction("D&elete");
|
||||
connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self()));
|
||||
QAction* del_action = menu.addAction("D&elete");
|
||||
connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self()));
|
||||
|
||||
menu.exec(container->title_bar->mapToGlobal(pos));
|
||||
}
|
||||
menu.exec(container->title_bar->mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::delete_self() {
|
||||
@@ -572,24 +577,24 @@ void Effect::set_enabled(bool b) {
|
||||
|
||||
QVariant load_data_from_string(int type, const QString& string) {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return string.toDouble();
|
||||
case EFFECT_FIELD_COLOR: return QColor(string);
|
||||
case EFFECT_FIELD_STRING: return string;
|
||||
case EFFECT_FIELD_BOOL: return (string == "1");
|
||||
case EFFECT_FIELD_COMBO: return string.toInt();
|
||||
case EFFECT_FIELD_FONT: return string;
|
||||
case EFFECT_FIELD_DOUBLE: return string.toDouble();
|
||||
case EFFECT_FIELD_COLOR: return QColor(string);
|
||||
case EFFECT_FIELD_STRING: return string;
|
||||
case EFFECT_FIELD_BOOL: return (string == "1");
|
||||
case EFFECT_FIELD_COMBO: return string.toInt();
|
||||
case EFFECT_FIELD_FONT: return string;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QString save_data_to_string(int type, const QVariant& data) {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble());
|
||||
case EFFECT_FIELD_COLOR: return data.value<QColor>().name();
|
||||
case EFFECT_FIELD_STRING: return data.toString();
|
||||
case EFFECT_FIELD_BOOL: return QString::number(data.toBool());
|
||||
case EFFECT_FIELD_COMBO: return QString::number(data.toInt());
|
||||
case EFFECT_FIELD_FONT: return data.toString();
|
||||
case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble());
|
||||
case EFFECT_FIELD_COLOR: return data.value<QColor>().name();
|
||||
case EFFECT_FIELD_STRING: return data.toString();
|
||||
case EFFECT_FIELD_BOOL: return QString::number(data.toBool());
|
||||
case EFFECT_FIELD_COMBO: return QString::number(data.toInt());
|
||||
case EFFECT_FIELD_FONT: return data.toString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
@@ -610,7 +615,7 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
stream.readNext();
|
||||
|
||||
// read keyframes
|
||||
if (stream.name() == "keyframes" && stream.isStartElement()) {
|
||||
/*if (stream.name() == "keyframes" && stream.isStartElement()) {
|
||||
for (int k=0;k<stream.attributes().size();k++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(k);
|
||||
if (attr.name() == "enabled") {
|
||||
@@ -631,14 +636,19 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
keyframe_type = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
row->keyframe_times.append(keyframe_frame);
|
||||
row->keyframe_types.append(keyframe_type);
|
||||
for (int k=0;k<row->fieldCount();k++) {
|
||||
EffectField* field = row->field(k);
|
||||
EffectKeyframe key;
|
||||
key.time = keyframe_frame;
|
||||
key.type = keyframe_type;
|
||||
field->keyframes.append(key);
|
||||
}
|
||||
}
|
||||
stream.readNext();
|
||||
}
|
||||
}
|
||||
stream.readNext();
|
||||
}
|
||||
}*/
|
||||
|
||||
// read field
|
||||
if (stream.name() == "field" && stream.isStartElement()) {
|
||||
@@ -678,10 +688,30 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) {
|
||||
stream.readNext();
|
||||
|
||||
// read all keyframes
|
||||
// read keyframes
|
||||
if (stream.name() == "key" && stream.isStartElement()) {
|
||||
stream.readNext();
|
||||
field->keyframe_data.append(load_data_from_string(field->type, stream.text().toString()));
|
||||
row->setKeyframing(true);
|
||||
|
||||
EffectKeyframe key;
|
||||
for (int k=0;k<stream.attributes().size();k++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(k);
|
||||
if (attr.name() == "value") {
|
||||
key.data = load_data_from_string(field->type, attr.value().toString());
|
||||
} else if (attr.name() == "frame") {
|
||||
key.time = attr.value().toLong();
|
||||
} else if (attr.name() == "type") {
|
||||
key.type = attr.value().toInt();
|
||||
} else if (attr.name() == "prehx") {
|
||||
key.pre_handle_x = attr.value().toDouble();
|
||||
} else if (attr.name() == "prehy") {
|
||||
key.pre_handle_y = attr.value().toDouble();
|
||||
} else if (attr.name() == "posthx") {
|
||||
key.post_handle_x = attr.value().toDouble();
|
||||
} else if (attr.name() == "posthy") {
|
||||
key.post_handle_y = attr.value().toDouble();
|
||||
}
|
||||
}
|
||||
field->keyframes.append(key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -701,33 +731,54 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
|
||||
void Effect::save(QXmlStreamWriter& stream) {
|
||||
stream.writeAttribute("name", meta->name);
|
||||
stream.writeAttribute("enabled", QString::number(is_enabled()));
|
||||
stream.writeAttribute("enabled", QString::number(is_enabled()));
|
||||
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
EffectRow* row = rows.at(i);
|
||||
if (row->savable) {
|
||||
stream.writeStartElement("row"); // row
|
||||
stream.writeStartElement("keyframes"); // keyframes
|
||||
stream.writeAttribute("enabled", QString::number(row->isKeyframing()));
|
||||
for (int j=0;j<row->keyframe_times.size();j++) {
|
||||
stream.writeStartElement("key"); // key
|
||||
stream.writeAttribute("frame", QString::number(row->keyframe_times.at(j)));
|
||||
stream.writeAttribute("type", QString::number(row->keyframe_types.at(j)));
|
||||
stream.writeEndElement(); // key
|
||||
}
|
||||
stream.writeEndElement(); // keyframes
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
stream.writeStartElement("field"); // field
|
||||
stream.writeAttribute("id", field->id);
|
||||
stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data()));
|
||||
for (int k=0;k<field->keyframe_data.size();k++) {
|
||||
stream.writeTextElement("key", save_data_to_string(field->type, field->keyframe_data.at(k)));
|
||||
}
|
||||
stream.writeEndElement(); // field
|
||||
}
|
||||
stream.writeEndElement(); // row
|
||||
}
|
||||
if (row->savable) {
|
||||
stream.writeStartElement("row"); // row
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
stream.writeStartElement("field"); // field
|
||||
stream.writeAttribute("id", field->id);
|
||||
stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data()));
|
||||
for (int k=0;k<field->keyframes.size();k++) {
|
||||
const EffectKeyframe& key = field->keyframes.at(k);
|
||||
stream.writeStartElement("key");
|
||||
stream.writeAttribute("value", save_data_to_string(field->type, key.data));
|
||||
stream.writeAttribute("frame", QString::number(key.time));
|
||||
stream.writeAttribute("type", QString::number(key.type));
|
||||
stream.writeAttribute("prehx", QString::number(key.pre_handle_x));
|
||||
stream.writeAttribute("prehy", QString::number(key.pre_handle_y));
|
||||
stream.writeAttribute("posthx", QString::number(key.post_handle_x));
|
||||
stream.writeAttribute("posthy", QString::number(key.post_handle_y));
|
||||
stream.writeEndElement(); // key
|
||||
}
|
||||
stream.writeEndElement(); // field
|
||||
}
|
||||
stream.writeEndElement(); // row
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Effect::is_open() {
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
void Effect::validate_meta_path() {
|
||||
if (!meta->path.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return;
|
||||
QList<QString> effects_paths = get_effects_paths();
|
||||
const QString& test_fn = vertPath.isEmpty() ? fragPath : vertPath;
|
||||
for (int i=0;i<effects_paths.size();i++) {
|
||||
if (QFileInfo::exists(effects_paths.at(i) + "/" + test_fn)) {
|
||||
for (int j=0;j<effects.size();j++) {
|
||||
if (&effects.at(j) == meta) {
|
||||
effects[j].path = effects_paths.at(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,6 +792,7 @@ void Effect::open() {
|
||||
dout << "[WARNING] No current context to create a shader program for - will retry next repaint";
|
||||
} else {
|
||||
glslProgram = new QOpenGLShaderProgram();
|
||||
validate_meta_path();
|
||||
if (!vertPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath);
|
||||
if (!fragPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath);
|
||||
glslProgram->link();
|
||||
@@ -777,16 +829,16 @@ void Effect::startEffect() {
|
||||
|
||||
void Effect::endEffect() {
|
||||
if (bound) glslProgram->release();
|
||||
bound = false;
|
||||
bound = false;
|
||||
}
|
||||
|
||||
void Effect::process_image(double, uint8_t *, int) {}
|
||||
|
||||
Effect* Effect::copy(Clip* c) {
|
||||
Effect* copy = create_effect(c, meta);
|
||||
copy->set_enabled(is_enabled());
|
||||
copy_field_keyframes(copy);
|
||||
return copy;
|
||||
copy->set_enabled(is_enabled());
|
||||
copy_field_keyframes(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
void Effect::process_shader(double timecode, GLTextureCoords&) {
|
||||
@@ -831,7 +883,7 @@ GLuint Effect::process_superimpose(double timecode) {
|
||||
recreate_texture = true;
|
||||
}
|
||||
|
||||
if (valueHasChanged(timecode) || recreate_texture || enable_always_update) {
|
||||
if (valueHasChanged(timecode) || recreate_texture || enable_always_update) {
|
||||
redraw(timecode);
|
||||
}
|
||||
|
||||
@@ -852,78 +904,78 @@ GLuint Effect::process_superimpose(double timecode) {
|
||||
void Effect::process_audio(double, double, quint8*, int, int) {
|
||||
// only volume/pan, hand off to AU and VST for all other cases
|
||||
|
||||
/*double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
/*double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
|
||||
for (int i=0;i<nb_bytes;i+=2) {
|
||||
qint32 samp = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
|
||||
jsEngine.globalObject().setProperty("sample", samp);
|
||||
jsEngine.globalObject().setProperty("sample", samp);
|
||||
jsEngine.globalObject().setProperty("volume", row(0)->field(0)->get_double_value(timecode_start+(interval*i), true));
|
||||
QJSValue result = eval.call();
|
||||
samp = result.toInt();
|
||||
QJSValueList args;
|
||||
args << samples << nb_bytes;
|
||||
samp = result.toInt();
|
||||
QJSValueList args;
|
||||
args << samples << nb_bytes;
|
||||
|
||||
|
||||
samples[i+1] = (quint8) (samp >> 8);
|
||||
samples[i] = (quint8) samp;
|
||||
}*/
|
||||
}*/
|
||||
}
|
||||
|
||||
void Effect::gizmo_draw(double, GLTextureCoords &) {}
|
||||
|
||||
void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) {
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
if (gizmos.at(i) == gizmo) {
|
||||
ComboAction* ca = NULL;
|
||||
if (done) ca = new ComboAction();
|
||||
if (gizmo->x_field1 != NULL) {
|
||||
gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1);
|
||||
gizmo->x_field1->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->y_field1 != NULL) {
|
||||
gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1);
|
||||
gizmo->y_field1->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->x_field2 != NULL) {
|
||||
gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2);
|
||||
gizmo->x_field2->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->y_field2 != NULL) {
|
||||
gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2);
|
||||
gizmo->y_field2->make_key_from_change(ca);
|
||||
}
|
||||
if (done) undo_stack.push(ca);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
if (gizmos.at(i) == gizmo) {
|
||||
ComboAction* ca = NULL;
|
||||
if (done) ca = new ComboAction();
|
||||
if (gizmo->x_field1 != NULL) {
|
||||
gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1);
|
||||
gizmo->x_field1->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->y_field1 != NULL) {
|
||||
gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1);
|
||||
gizmo->y_field1->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->x_field2 != NULL) {
|
||||
gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2);
|
||||
gizmo->x_field2->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->y_field2 != NULL) {
|
||||
gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2);
|
||||
gizmo->y_field2->make_key_from_change(ca);
|
||||
}
|
||||
if (done) undo_stack.push(ca);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::gizmo_world_to_screen() {
|
||||
GLfloat view_val[16];
|
||||
GLfloat projection_val[16];
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, view_val);
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, projection_val);
|
||||
GLfloat view_val[16];
|
||||
GLfloat projection_val[16];
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, view_val);
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, projection_val);
|
||||
|
||||
QMatrix4x4 view_matrix(view_val);
|
||||
QMatrix4x4 projection_matrix(projection_val);
|
||||
QMatrix4x4 view_matrix(view_val);
|
||||
QMatrix4x4 projection_matrix(projection_val);
|
||||
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
EffectGizmo* g = gizmos.at(i);
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
EffectGizmo* g = gizmos.at(i);
|
||||
|
||||
for (int j=0;j<g->get_point_count();j++) {
|
||||
QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix);
|
||||
for (int j=0;j<g->get_point_count();j++) {
|
||||
QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix);
|
||||
|
||||
int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width);
|
||||
int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height);
|
||||
int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width);
|
||||
int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height);
|
||||
|
||||
g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1);
|
||||
}
|
||||
}
|
||||
g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Effect::are_gizmos_enabled() {
|
||||
return (gizmos.size() > 0);
|
||||
return (gizmos.size() > 0);
|
||||
}
|
||||
|
||||
void Effect::redraw(double) {
|
||||
|
||||
+45
-44
@@ -23,16 +23,15 @@ class QXmlStreamWriter;
|
||||
class Effect;
|
||||
class EffectRow;
|
||||
class CheckboxEx;
|
||||
class KeyframeDelete;
|
||||
|
||||
struct EffectMeta {
|
||||
QString name;
|
||||
QString category;
|
||||
QString filename;
|
||||
QString name;
|
||||
QString category;
|
||||
QString filename;
|
||||
QString path;
|
||||
int internal;
|
||||
int type;
|
||||
int subtype;
|
||||
int subtype;
|
||||
};
|
||||
|
||||
extern QVector<EffectMeta> effects;
|
||||
@@ -70,37 +69,37 @@ extern QMutex effects_loaded;
|
||||
#define EFFECT_INTERNAL_COUNT 13
|
||||
|
||||
#define KEYFRAME_TYPE_LINEAR 0
|
||||
#define KEYFRAME_TYPE_SMOOTH 1
|
||||
#define KEYFRAME_TYPE_BEZIER 2
|
||||
#define KEYFRAME_TYPE_BEZIER 1
|
||||
#define KEYFRAME_TYPE_HOLD 2
|
||||
|
||||
struct GLTextureCoords {
|
||||
int grid_size;
|
||||
int grid_size;
|
||||
|
||||
int vertexTopLeftX;
|
||||
int vertexTopLeftY;
|
||||
int vertexTopLeftZ;
|
||||
int vertexTopLeftZ;
|
||||
int vertexTopRightX;
|
||||
int vertexTopRightY;
|
||||
int vertexTopRightZ;
|
||||
int vertexTopRightZ;
|
||||
int vertexBottomLeftX;
|
||||
int vertexBottomLeftY;
|
||||
int vertexBottomLeftZ;
|
||||
int vertexBottomLeftZ;
|
||||
int vertexBottomRightX;
|
||||
int vertexBottomRightY;
|
||||
int vertexBottomRightZ;
|
||||
int vertexBottomRightZ;
|
||||
|
||||
float textureTopLeftX;
|
||||
float textureTopLeftY;
|
||||
float textureTopLeftQ;
|
||||
float textureTopRightX;
|
||||
float textureTopRightY;
|
||||
float textureTopRightQ;
|
||||
float textureBottomRightX;
|
||||
float textureBottomRightY;
|
||||
float textureBottomRightQ;
|
||||
float textureBottomLeftX;
|
||||
float textureBottomLeftY;
|
||||
float textureBottomLeftQ;
|
||||
float textureTopLeftX;
|
||||
float textureTopLeftY;
|
||||
float textureTopLeftQ;
|
||||
float textureTopRightX;
|
||||
float textureTopRightY;
|
||||
float textureTopRightQ;
|
||||
float textureBottomRightX;
|
||||
float textureBottomRightY;
|
||||
float textureBottomRightQ;
|
||||
float textureBottomLeftX;
|
||||
float textureBottomLeftY;
|
||||
float textureBottomLeftQ;
|
||||
};
|
||||
|
||||
qint16 mix_audio_sample(qint16 a, qint16 b);
|
||||
@@ -114,21 +113,21 @@ class Effect : public QObject {
|
||||
public:
|
||||
Effect(Clip* c, const EffectMeta* em);
|
||||
~Effect();
|
||||
Clip* parent_clip;
|
||||
Clip* parent_clip;
|
||||
const EffectMeta* meta;
|
||||
int id;
|
||||
int id;
|
||||
QString name;
|
||||
CollapsibleWidget* container;
|
||||
|
||||
EffectRow* add_row(const QString &name, bool savable = true);
|
||||
EffectRow* add_row(const QString &name, bool savable = true);
|
||||
EffectRow* row(int i);
|
||||
int row_count();
|
||||
|
||||
EffectGizmo* add_gizmo(int type);
|
||||
EffectGizmo* gizmo(int i);
|
||||
int gizmo_count();
|
||||
EffectGizmo* add_gizmo(int type);
|
||||
EffectGizmo* gizmo(int i);
|
||||
int gizmo_count();
|
||||
|
||||
bool is_enabled();
|
||||
bool is_enabled();
|
||||
void set_enabled(bool b);
|
||||
|
||||
virtual void refresh();
|
||||
@@ -137,9 +136,10 @@ public:
|
||||
void copy_field_keyframes(Effect *e);
|
||||
|
||||
void load(QXmlStreamReader& stream);
|
||||
void save(QXmlStreamWriter& stream);
|
||||
void save(QXmlStreamWriter& stream);
|
||||
|
||||
// glsl handling
|
||||
bool is_open();
|
||||
void open();
|
||||
void close();
|
||||
virtual void startEffect();
|
||||
@@ -147,24 +147,24 @@ public:
|
||||
|
||||
bool enable_shader;
|
||||
bool enable_coords;
|
||||
bool enable_superimpose;
|
||||
bool enable_image;
|
||||
bool enable_superimpose;
|
||||
bool enable_image;
|
||||
|
||||
int getIterations();
|
||||
void setIterations(int i);
|
||||
|
||||
const char* ffmpeg_filter;
|
||||
|
||||
virtual void process_image(double timecode, uint8_t* data, int size);
|
||||
virtual void process_image(double timecode, uint8_t* data, int size);
|
||||
virtual void process_shader(double timecode, GLTextureCoords&);
|
||||
virtual void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
virtual void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
virtual GLuint process_superimpose(double timecode);
|
||||
virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
virtual void gizmo_draw(double timecode, GLTextureCoords& coords);
|
||||
void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done);
|
||||
void gizmo_world_to_screen();
|
||||
bool are_gizmos_enabled();
|
||||
virtual void gizmo_draw(double timecode, GLTextureCoords& coords);
|
||||
void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done);
|
||||
void gizmo_world_to_screen();
|
||||
bool are_gizmos_enabled();
|
||||
public slots:
|
||||
void field_changed();
|
||||
private slots:
|
||||
@@ -182,17 +182,17 @@ protected:
|
||||
QImage img;
|
||||
QOpenGLTexture* texture;
|
||||
|
||||
// enable effect to update constantly
|
||||
bool enable_always_update;
|
||||
// enable effect to update constantly
|
||||
bool enable_always_update;
|
||||
private:
|
||||
// superimpose effect
|
||||
QString script;
|
||||
|
||||
bool isOpen;
|
||||
QVector<EffectRow*> rows;
|
||||
QVector<EffectGizmo*> gizmos;
|
||||
QVector<EffectGizmo*> gizmos;
|
||||
QGridLayout* ui_layout;
|
||||
QWidget* ui;
|
||||
QWidget* ui;
|
||||
bool bound;
|
||||
|
||||
// superimpose functions
|
||||
@@ -201,6 +201,7 @@ private:
|
||||
QVector<QVariant> cachedValues;
|
||||
void delete_texture();
|
||||
int get_index_in_clip();
|
||||
void validate_meta_path();
|
||||
};
|
||||
|
||||
class EffectInit : public QThread {
|
||||
|
||||
+262
-243
@@ -15,347 +15,366 @@
|
||||
#include "project/sequence.h"
|
||||
|
||||
#include "io/math.h"
|
||||
#include <QtMath>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
EffectField::EffectField(EffectRow *parent, int t, const QString &i) :
|
||||
parent_row(parent),
|
||||
type(t),
|
||||
id(i)
|
||||
parent_row(parent),
|
||||
type(t),
|
||||
id(i)
|
||||
{
|
||||
switch (t) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
{
|
||||
LabelSlider* ls = new LabelSlider();
|
||||
ui_element = ls;
|
||||
connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
ColorButton* cb = new ColorButton();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(color_changed()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
{
|
||||
TextEditEx* edit = new TextEditEx();
|
||||
edit->setUndoRedoEnabled(true);
|
||||
ui_element = edit;
|
||||
connect(edit, SIGNAL(textChanged()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
{
|
||||
CheckboxEx* cb = new CheckboxEx();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(clicked(bool)), this, SLOT(ui_element_change()));
|
||||
connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool)));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
{
|
||||
ComboBoxEx* cb = new ComboBoxEx();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(activated(int)), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
{
|
||||
FontCombobox* fcb = new FontCombobox();
|
||||
ui_element = fcb;
|
||||
connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (t) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
{
|
||||
LabelSlider* ls = new LabelSlider();
|
||||
ui_element = ls;
|
||||
connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change()));
|
||||
connect(ls, SIGNAL(clicked()), this, SIGNAL(clicked()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
ColorButton* cb = new ColorButton();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(color_changed()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
{
|
||||
TextEditEx* edit = new TextEditEx();
|
||||
edit->setUndoRedoEnabled(true);
|
||||
ui_element = edit;
|
||||
connect(edit, SIGNAL(textChanged()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
{
|
||||
CheckboxEx* cb = new CheckboxEx();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(clicked(bool)), this, SLOT(ui_element_change()));
|
||||
connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool)));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
{
|
||||
ComboBoxEx* cb = new ComboBoxEx();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(activated(int)), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
{
|
||||
FontCombobox* fcb = new FontCombobox();
|
||||
ui_element = fcb;
|
||||
connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QVariant EffectField::get_previous_data() {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_BOOL: return !static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->getPreviousIndex();
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->getPreviousValue();
|
||||
}
|
||||
return QVariant();
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_BOOL: return !static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->getPreviousIndex();
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->getPreviousValue();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant EffectField::get_current_data() {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->value();
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->getPlainTextEx();
|
||||
case EFFECT_FIELD_BOOL: return static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
}
|
||||
return QVariant();
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->value();
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->getPlainTextEx();
|
||||
case EFFECT_FIELD_BOOL: return static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
double EffectField::frameToTimecode(long frame) {
|
||||
return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate);
|
||||
return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate);
|
||||
}
|
||||
|
||||
long EffectField::timecodeToFrame(double timecode) {
|
||||
return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate);
|
||||
return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate);
|
||||
}
|
||||
|
||||
void EffectField::set_current_data(const QVariant& data) {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->set_value(data.toDouble(), false);
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->set_color(data.value<QColor>());
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->setPlainTextEx(data.toString());
|
||||
case EFFECT_FIELD_BOOL: return static_cast<QCheckBox*>(ui_element)->setChecked(data.toBool());
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(data.toInt());
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->setCurrentTextEx(data.toString());
|
||||
}
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->set_value(data.toDouble(), false);
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->set_color(data.value<QColor>());
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->setPlainTextEx(data.toString());
|
||||
case EFFECT_FIELD_BOOL: return static_cast<QCheckBox*>(ui_element)->setChecked(data.toBool());
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(data.toInt());
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->setCurrentTextEx(data.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void EffectField::get_keyframe_data(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 = timecodeToFrame(timecode);
|
||||
int before_keyframe_index = -1;
|
||||
int after_keyframe_index = -1;
|
||||
long before_keyframe_time = LONG_MIN;
|
||||
long after_keyframe_time = LONG_MAX;
|
||||
long frame = timecodeToFrame(timecode);
|
||||
|
||||
for (int i=0;i<parent_row->keyframe_times.size();i++) {
|
||||
long eval_keyframe_time = parent_row->keyframe_times.at(i);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(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;
|
||||
}
|
||||
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-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(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 (parent_row->isKeyframing() && keyframe_data.size() > 0);
|
||||
return (parent_row->isKeyframing() && keyframes.size() > 0);
|
||||
}
|
||||
|
||||
QVariant EffectField::validate_keyframe_data(double timecode, bool async) {
|
||||
if (hasKeyframes()) {
|
||||
int before_keyframe;
|
||||
int after_keyframe;
|
||||
double progress;
|
||||
get_keyframe_data(timecode, before_keyframe, after_keyframe, progress);
|
||||
if (hasKeyframes()) {
|
||||
int before_keyframe;
|
||||
int after_keyframe;
|
||||
double progress;
|
||||
get_keyframe_data(timecode, before_keyframe, after_keyframe, progress);
|
||||
|
||||
int kf_type = (progress < 0.5) ? parent_row->keyframe_types.at(before_keyframe) : parent_row->keyframe_types.at(after_keyframe);
|
||||
if (kf_type == KEYFRAME_TYPE_SMOOTH) {
|
||||
double x = (8.0 * progress) - 4.0;
|
||||
progress = 1.0 / (1.0 + qPow(M_E, -x));
|
||||
progress *= 1.0373;
|
||||
progress -= 0.01865;
|
||||
}
|
||||
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);
|
||||
|
||||
const QVariant& before_data = keyframe_data.at(before_keyframe);
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
{
|
||||
double value;
|
||||
if (before_keyframe == after_keyframe) {
|
||||
value = keyframe_data.at(before_keyframe).toDouble();
|
||||
} else {
|
||||
double before_dbl = keyframe_data.at(before_keyframe).toDouble();
|
||||
double after_dbl = keyframe_data.at(after_keyframe).toDouble();
|
||||
value = double_lerp(before_dbl, after_dbl, progress);
|
||||
}
|
||||
if (async) {
|
||||
return value;
|
||||
}
|
||||
static_cast<LabelSlider*>(ui_element)->set_value(value, false);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
QColor value;
|
||||
if (before_keyframe == after_keyframe) {
|
||||
value = keyframe_data.at(before_keyframe).value<QColor>();
|
||||
} else {
|
||||
QColor before_data = keyframe_data.at(before_keyframe).value<QColor>();
|
||||
QColor after_data = keyframe_data.at(after_keyframe).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));
|
||||
}
|
||||
if (async) {
|
||||
return value;
|
||||
}
|
||||
static_cast<ColorButton*>(ui_element)->set_color(value);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<TextEditEx*>(ui_element)->setPlainTextEx(before_data.toString());
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<QCheckBox*>(ui_element)->setChecked(before_data.toBool());
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(before_data.toInt());
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentTextEx(before_data.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
double before_dbl = before_key.data.toDouble();
|
||||
double after_dbl = after_key.data.toDouble();
|
||||
|
||||
if (before_key.type == KEYFRAME_TYPE_HOLD) {
|
||||
// hold
|
||||
value = before_dbl;
|
||||
} else if (before_key.type == KEYFRAME_TYPE_BEZIER || after_key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
// bezier interpolation
|
||||
if (before_key.type == KEYFRAME_TYPE_BEZIER && after_key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
// cubic bezier
|
||||
double t = cubic_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+before_key.post_handle_x, after_key.time+after_key.pre_handle_x, 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 == KEYFRAME_TYPE_LINEAR) { // quadratic bezier
|
||||
// last keyframe is the bezier one
|
||||
double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+before_key.post_handle_x, after_key.time);
|
||||
value = quad_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl, t);
|
||||
} else {
|
||||
// this keyframe is the bezier one
|
||||
double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, after_key.time+after_key.pre_handle_x, after_key.time);
|
||||
value = quad_from_t(before_dbl, after_dbl+after_key.pre_handle_y, after_dbl, t);
|
||||
}
|
||||
} else {
|
||||
// linear
|
||||
value = double_lerp(before_dbl, after_dbl, progress);
|
||||
}
|
||||
}
|
||||
if (async) {
|
||||
return value;
|
||||
}
|
||||
static_cast<LabelSlider*>(ui_element)->set_value(value, false);
|
||||
}
|
||||
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));
|
||||
}
|
||||
if (async) {
|
||||
return value;
|
||||
}
|
||||
static_cast<ColorButton*>(ui_element)->set_color(value);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<TextEditEx*>(ui_element)->setPlainTextEx(before_data.toString());
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<QCheckBox*>(ui_element)->setChecked(before_data.toBool());
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(before_data.toInt());
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentTextEx(before_data.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void EffectField::ui_element_change() {
|
||||
bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast<LabelSlider*>(ui_element)->is_dragging());
|
||||
ComboAction* ca = NULL;
|
||||
if (!dragging_double) ca = new ComboAction();
|
||||
make_key_from_change(ca);
|
||||
if (!dragging_double) undo_stack.push(ca);
|
||||
emit changed();
|
||||
bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast<LabelSlider*>(ui_element)->is_dragging());
|
||||
ComboAction* ca = NULL;
|
||||
if (!dragging_double) ca = new ComboAction();
|
||||
make_key_from_change(ca);
|
||||
if (!dragging_double) undo_stack.push(ca);
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void EffectField::make_key_from_change(ComboAction* ca) {
|
||||
if (parent_row->isKeyframing()) {
|
||||
parent_row->set_keyframe_now(ca);
|
||||
} else if (ca != NULL) {
|
||||
// set undo
|
||||
ca->append(new EffectFieldUndo(this));
|
||||
}
|
||||
if (parent_row->isKeyframing()) {
|
||||
parent_row->set_keyframe_now(ca);
|
||||
} else if (ca != NULL) {
|
||||
// set undo
|
||||
ca->append(new EffectFieldUndo(this));
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* EffectField::get_ui_element() {
|
||||
return ui_element;
|
||||
return ui_element;
|
||||
}
|
||||
|
||||
void EffectField::set_enabled(bool e) {
|
||||
ui_element->setEnabled(e);
|
||||
ui_element->setEnabled(e);
|
||||
}
|
||||
|
||||
double EffectField::get_double_value(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toDouble();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<LabelSlider*>(ui_element)->value();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toDouble();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<LabelSlider*>(ui_element)->value();
|
||||
}
|
||||
|
||||
void EffectField::set_double_value(double v) {
|
||||
static_cast<LabelSlider*>(ui_element)->set_value(v, false);
|
||||
static_cast<LabelSlider*>(ui_element)->set_value(v, false);
|
||||
}
|
||||
|
||||
void EffectField::set_double_default_value(double v) {
|
||||
static_cast<LabelSlider*>(ui_element)->set_default_value(v);
|
||||
static_cast<LabelSlider*>(ui_element)->set_default_value(v);
|
||||
}
|
||||
|
||||
void EffectField::set_double_minimum_value(double v) {
|
||||
static_cast<LabelSlider*>(ui_element)->set_minimum_value(v);
|
||||
static_cast<LabelSlider*>(ui_element)->set_minimum_value(v);
|
||||
}
|
||||
|
||||
void EffectField::set_double_maximum_value(double v) {
|
||||
static_cast<LabelSlider*>(ui_element)->set_maximum_value(v);
|
||||
static_cast<LabelSlider*>(ui_element)->set_maximum_value(v);
|
||||
}
|
||||
|
||||
void EffectField::add_combo_item(const QString& name, const QVariant& data) {
|
||||
static_cast<ComboBoxEx*>(ui_element)->addItem(name, data);
|
||||
static_cast<ComboBoxEx*>(ui_element)->addItem(name, data);
|
||||
}
|
||||
|
||||
int EffectField::get_combo_index(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toInt();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toInt();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
}
|
||||
|
||||
const QVariant EffectField::get_combo_data(double timecode) {
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentData();
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentData();
|
||||
}
|
||||
|
||||
const QString EffectField::get_combo_string(double timecode) {
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentText();
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentText();
|
||||
}
|
||||
|
||||
void EffectField::set_combo_index(int index) {
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(index);
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(index);
|
||||
}
|
||||
|
||||
void EffectField::set_combo_string(const QString& s) {
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentTextEx(s);
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentTextEx(s);
|
||||
}
|
||||
|
||||
bool EffectField::get_bool_value(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toBool();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toBool();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
}
|
||||
|
||||
void EffectField::set_bool_value(bool b) {
|
||||
return static_cast<QCheckBox*>(ui_element)->setChecked(b);
|
||||
return static_cast<QCheckBox*>(ui_element)->setChecked(b);
|
||||
}
|
||||
|
||||
const QString EffectField::get_string_value(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toString();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<TextEditEx*>(ui_element)->getPlainTextEx();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toString();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<TextEditEx*>(ui_element)->getPlainTextEx();
|
||||
}
|
||||
|
||||
void EffectField::set_string_value(const QString& s) {
|
||||
static_cast<TextEditEx*>(ui_element)->setPlainTextEx(s);
|
||||
static_cast<TextEditEx*>(ui_element)->setPlainTextEx(s);
|
||||
}
|
||||
|
||||
const QString EffectField::get_font_name(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toString();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toString();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
}
|
||||
|
||||
void EffectField::set_font_name(const QString& s) {
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentText(s);
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentText(s);
|
||||
}
|
||||
|
||||
QColor EffectField::get_color_value(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).value<QColor>();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).value<QColor>();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
}
|
||||
|
||||
void EffectField::set_color_value(QColor color) {
|
||||
static_cast<ColorButton*>(ui_element)->set_color(color);
|
||||
static_cast<ColorButton*>(ui_element)->set_color(color);
|
||||
}
|
||||
|
||||
+43
-40
@@ -12,63 +12,66 @@
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include "keyframe.h"
|
||||
|
||||
class EffectRow;
|
||||
class ComboAction;
|
||||
|
||||
class EffectField : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
EffectField(EffectRow* parent, int t, const QString& i);
|
||||
EffectRow* parent_row;
|
||||
int type;
|
||||
QString id;
|
||||
EffectField(EffectRow* parent, int t, const QString& i);
|
||||
EffectRow* parent_row;
|
||||
int type;
|
||||
QString id;
|
||||
|
||||
QVariant get_previous_data();
|
||||
QVariant get_current_data();
|
||||
double frameToTimecode(long frame);
|
||||
long timecodeToFrame(double timecode);
|
||||
void set_current_data(const QVariant&);
|
||||
void get_keyframe_data(double timecode, int& before, int& after, double& d);
|
||||
QVariant validate_keyframe_data(double timecode, bool async = false);
|
||||
QVariant get_previous_data();
|
||||
QVariant get_current_data();
|
||||
double frameToTimecode(long frame);
|
||||
long timecodeToFrame(double timecode);
|
||||
void set_current_data(const QVariant&);
|
||||
void get_keyframe_data(double timecode, int& before, int& after, double& d);
|
||||
QVariant validate_keyframe_data(double timecode, bool async = false);
|
||||
|
||||
double get_double_value(double timecode, bool async = false);
|
||||
void set_double_value(double v);
|
||||
void set_double_default_value(double v);
|
||||
void set_double_minimum_value(double v);
|
||||
void set_double_maximum_value(double v);
|
||||
double get_double_value(double timecode, bool async = false);
|
||||
void set_double_value(double v);
|
||||
void set_double_default_value(double v);
|
||||
void set_double_minimum_value(double v);
|
||||
void set_double_maximum_value(double v);
|
||||
|
||||
const QString get_string_value(double timecode, bool async = false);
|
||||
void set_string_value(const QString &s);
|
||||
const QString get_string_value(double timecode, bool async = false);
|
||||
void set_string_value(const QString &s);
|
||||
|
||||
void add_combo_item(const QString& name, const QVariant &data);
|
||||
int get_combo_index(double timecode, bool async = false);
|
||||
const QVariant get_combo_data(double timecode);
|
||||
const QString get_combo_string(double timecode);
|
||||
void set_combo_index(int index);
|
||||
void set_combo_string(const QString& s);
|
||||
void add_combo_item(const QString& name, const QVariant &data);
|
||||
int get_combo_index(double timecode, bool async = false);
|
||||
const QVariant get_combo_data(double timecode);
|
||||
const QString get_combo_string(double timecode);
|
||||
void set_combo_index(int index);
|
||||
void set_combo_string(const QString& s);
|
||||
|
||||
bool get_bool_value(double timecode, bool async = false);
|
||||
void set_bool_value(bool b);
|
||||
bool get_bool_value(double timecode, bool async = false);
|
||||
void set_bool_value(bool b);
|
||||
|
||||
const QString get_font_name(double timecode, bool async = false);
|
||||
void set_font_name(const QString& s);
|
||||
const QString get_font_name(double timecode, bool async = false);
|
||||
void set_font_name(const QString& s);
|
||||
|
||||
QColor get_color_value(double timecode, bool async = false);
|
||||
void set_color_value(QColor color);
|
||||
QColor get_color_value(double timecode, bool async = false);
|
||||
void set_color_value(QColor color);
|
||||
|
||||
QWidget* get_ui_element();
|
||||
void set_enabled(bool e);
|
||||
QVector<QVariant> keyframe_data;
|
||||
QWidget* ui_element;
|
||||
QWidget* get_ui_element();
|
||||
void set_enabled(bool e);
|
||||
QVector<EffectKeyframe> keyframes;
|
||||
QWidget* ui_element;
|
||||
|
||||
void make_key_from_change(ComboAction* ca);
|
||||
void make_key_from_change(ComboAction* ca);
|
||||
private:
|
||||
bool hasKeyframes();
|
||||
bool hasKeyframes();
|
||||
private slots:
|
||||
void ui_element_change();
|
||||
void ui_element_change();
|
||||
signals:
|
||||
void changed();
|
||||
void toggled(bool);
|
||||
void changed();
|
||||
void toggled(bool);
|
||||
void clicked();
|
||||
};
|
||||
|
||||
#endif // EFFECTFIELD_H
|
||||
|
||||
+215
-119
@@ -1,6 +1,5 @@
|
||||
#include "effectrow.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
@@ -11,169 +10,266 @@
|
||||
#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, bool save, QGridLayout *uilayout, const QString &n, int row) :
|
||||
parent_effect(parent),
|
||||
savable(save),
|
||||
keyframing(false),
|
||||
ui(uilayout),
|
||||
name(n),
|
||||
ui_row(row),
|
||||
just_made_unsafe_keyframe(false)
|
||||
parent_effect(parent),
|
||||
savable(save),
|
||||
keyframing(false),
|
||||
ui(uilayout),
|
||||
name(n),
|
||||
ui_row(row),
|
||||
just_made_unsafe_keyframe(false)
|
||||
{
|
||||
label = new QLabel(name);
|
||||
ui->addWidget(label, row, 0);
|
||||
label = new ClickableLabel(name + ":");
|
||||
|
||||
if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) {
|
||||
keyframe_nav = new KeyframeNavigator();
|
||||
connect(keyframe_nav, SIGNAL(goto_previous_key()), this, SLOT(goto_previous_key()));
|
||||
connect(keyframe_nav, SIGNAL(toggle_key()), this, SLOT(toggle_key()));
|
||||
connect(keyframe_nav, SIGNAL(goto_next_key()), this, SLOT(goto_next_key()));
|
||||
connect(keyframe_nav, SIGNAL(set_keyframe_enabled(bool)), this, SLOT(set_keyframe_enabled(bool)));
|
||||
ui->addWidget(keyframe_nav, row, 6);
|
||||
}
|
||||
ui->addWidget(label, row, 0);
|
||||
|
||||
if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) {
|
||||
connect(label, SIGNAL(clicked()), this, SLOT(focus_row()));
|
||||
|
||||
keyframe_nav = new KeyframeNavigator();
|
||||
connect(keyframe_nav, SIGNAL(goto_previous_key()), this, SLOT(goto_previous_key()));
|
||||
connect(keyframe_nav, SIGNAL(toggle_key()), this, SLOT(toggle_key()));
|
||||
connect(keyframe_nav, SIGNAL(goto_next_key()), this, SLOT(goto_next_key()));
|
||||
connect(keyframe_nav, SIGNAL(keyframe_enabled_changed(bool)), this, SLOT(set_keyframe_enabled(bool)));
|
||||
connect(keyframe_nav, SIGNAL(clicked()), this, SLOT(focus_row()));
|
||||
ui->addWidget(keyframe_nav, row, 6);
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectRow::isKeyframing() {
|
||||
return keyframing;
|
||||
return keyframing;
|
||||
}
|
||||
|
||||
void EffectRow::setKeyframing(bool b) {
|
||||
if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) {
|
||||
keyframing = b;
|
||||
keyframe_nav->enable_keyframes(b);
|
||||
}
|
||||
if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) {
|
||||
keyframing = b;
|
||||
keyframe_nav->enable_keyframes(b);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectRow::set_keyframe_enabled(bool enabled) {
|
||||
if (enabled) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
set_keyframe_now(ca);
|
||||
undo_stack.push(ca);
|
||||
} else {
|
||||
if (QMessageBox::question(panel_effect_controls, "Disable Keyframes", "Disabling keyframes will delete all current keyframes. Are you sure you want to do this?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
// clear
|
||||
KeyframeDelete* kd = new KeyframeDelete();
|
||||
for (int i=keyframe_times.size()-1;i>=0;i--) {
|
||||
delete_keyframe(kd, i);
|
||||
}
|
||||
kd->disable_keyframes_on_row = this;
|
||||
undo_stack.push(kd);
|
||||
panel_effect_controls->update_keyframes();
|
||||
} else {
|
||||
setKeyframing(true);
|
||||
}
|
||||
}
|
||||
if (enabled) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
ca->append(new SetKeyframing(this, true));
|
||||
set_keyframe_now(ca);
|
||||
undo_stack.push(ca);
|
||||
} else {
|
||||
if (QMessageBox::question(panel_effect_controls, "Disable Keyframes", "Disabling keyframes will delete all current keyframes. Are you sure you want to do this?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
// clear
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<fieldCount();i++) {
|
||||
EffectField* f = field(i);
|
||||
for (int j=0;j<f->keyframes.size();j++) {
|
||||
ca->append(new KeyframeDelete(f, 0));
|
||||
}
|
||||
}
|
||||
ca->append(new SetKeyframing(this, false));
|
||||
undo_stack.push(ca);
|
||||
panel_effect_controls->update_keyframes();
|
||||
} else {
|
||||
setKeyframing(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EffectRow::goto_previous_key() {
|
||||
long key = LONG_MIN;
|
||||
Clip* c = parent_effect->parent_clip;
|
||||
for (int i=0;i<keyframe_times.size();i++) {
|
||||
long comp = keyframe_times.at(i) - c->clip_in + c->timeline_in;
|
||||
if (comp < sequence->playhead) {
|
||||
key = qMax(comp, key);
|
||||
}
|
||||
}
|
||||
if (key != LONG_MIN) panel_sequence_viewer->seek(key);
|
||||
long key = LONG_MIN;
|
||||
Clip* c = parent_effect->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 < sequence->playhead) {
|
||||
key = qMax(comp, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key != LONG_MIN) panel_sequence_viewer->seek(key);
|
||||
}
|
||||
|
||||
void EffectRow::toggle_key() {
|
||||
int index = -1;
|
||||
Clip* c = parent_effect->parent_clip;
|
||||
for (int i=0;i<keyframe_times.size();i++) {
|
||||
long comp = c->timeline_in - c->clip_in + keyframe_times.at(i);
|
||||
if (comp == sequence->playhead) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < 0) {
|
||||
// keyframe doesn't exist, set one
|
||||
ComboAction* ca = new ComboAction();
|
||||
set_keyframe_now(ca);
|
||||
undo_stack.push(ca);
|
||||
} else {
|
||||
KeyframeDelete* kd = new KeyframeDelete();
|
||||
delete_keyframe(kd, index);
|
||||
undo_stack.push(kd);
|
||||
panel_effect_controls->update_keyframes();
|
||||
panel_sequence_viewer->viewer_widget->update();
|
||||
}
|
||||
QVector<EffectField*> key_fields;
|
||||
QVector<int> key_field_index;
|
||||
Clip* c = parent_effect->parent_clip;
|
||||
for (int j=0;j<fieldCount();j++) {
|
||||
EffectField* f = field(j);
|
||||
for (int i=0;i<f->keyframes.size();i++) {
|
||||
long comp = c->timeline_in - c->clip_in + f->keyframes.at(i).time;
|
||||
if (comp == sequence->playhead) {
|
||||
key_fields.append(f);
|
||||
key_field_index.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComboAction* ca = new ComboAction();
|
||||
if (key_fields.size() == 0) {
|
||||
// keyframe doesn't exist, set one
|
||||
set_keyframe_now(ca);
|
||||
} else {
|
||||
for (int i=0;i<key_fields.size();i++) {
|
||||
ca->append(new KeyframeDelete(key_fields.at(i), key_field_index.at(i)));
|
||||
}
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
update_ui(false);
|
||||
}
|
||||
|
||||
void EffectRow::goto_next_key() {
|
||||
long key = LONG_MAX;
|
||||
Clip* c = parent_effect->parent_clip;
|
||||
for (int i=0;i<keyframe_times.size();i++) {
|
||||
long comp = c->timeline_in - c->clip_in + keyframe_times.at(i);
|
||||
if (comp > sequence->playhead) {
|
||||
key = qMin(comp, key);
|
||||
}
|
||||
}
|
||||
if (key != LONG_MAX) panel_sequence_viewer->seek(key);
|
||||
long key = LONG_MAX;
|
||||
Clip* c = parent_effect->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 > sequence->playhead) {
|
||||
key = qMin(comp, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key != LONG_MAX) panel_sequence_viewer->seek(key);
|
||||
}
|
||||
|
||||
void EffectRow::focus_row() {
|
||||
panel_graph_editor->set_row(this);
|
||||
}
|
||||
|
||||
EffectField* EffectRow::add_field(int type, const QString& id, int colspan) {
|
||||
EffectField* field = new EffectField(this, type, id);
|
||||
fields.append(field);
|
||||
QWidget* element = field->get_ui_element();
|
||||
ui->addWidget(element, ui_row, fields.size(), 1, colspan);
|
||||
connect(field, SIGNAL(changed()), parent_effect, SLOT(field_changed()));
|
||||
return field;
|
||||
EffectField* field = new EffectField(this, type, id);
|
||||
if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) connect(field, SIGNAL(clicked()), this, SLOT(focus_row()));
|
||||
fields.append(field);
|
||||
QWidget* element = field->get_ui_element();
|
||||
ui->addWidget(element, ui_row, fields.size(), 1, colspan);
|
||||
connect(field, SIGNAL(changed()), parent_effect, SLOT(field_changed()));
|
||||
return field;
|
||||
}
|
||||
|
||||
EffectRow::~EffectRow() {
|
||||
for (int i=0;i<fields.size();i++) {
|
||||
delete fields.at(i);
|
||||
}
|
||||
for (int i=0;i<fields.size();i++) {
|
||||
delete fields.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectRow::set_keyframe_now(ComboAction* ca) {
|
||||
int index = -1;
|
||||
long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in;
|
||||
for (int i=0;i<keyframe_times.size();i++) {
|
||||
if (keyframe_times.at(i) == time) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in;
|
||||
|
||||
KeyframeSet* ks = new KeyframeSet(this, index, time, just_made_unsafe_keyframe);
|
||||
if (!just_made_unsafe_keyframe) {
|
||||
EffectKeyframe key;
|
||||
key.time = time;
|
||||
|
||||
if (ca != NULL) {
|
||||
just_made_unsafe_keyframe = false;
|
||||
ca->append(ks);
|
||||
} else {
|
||||
if (index == -1) just_made_unsafe_keyframe = true;
|
||||
ks->redo();
|
||||
delete ks;
|
||||
}
|
||||
unsafe_keys.resize(fieldCount());
|
||||
unsafe_old_data.resize(fieldCount());
|
||||
key_is_new.resize(fieldCount());
|
||||
|
||||
panel_effect_controls->update_keyframes();
|
||||
for (int i=0;i<fieldCount();i++) {
|
||||
EffectField* f = field(i);
|
||||
|
||||
int exist_key = -1;
|
||||
int closest_key = 0;
|
||||
for (int j=0;j<f->keyframes.size();j++) {
|
||||
if (f->keyframes.at(j).time == time) {
|
||||
exist_key = j;
|
||||
} else if (f->keyframes.at(j).time < time
|
||||
&& f->keyframes.at(closest_key).time < f->keyframes.at(j).time) {
|
||||
closest_key = j;
|
||||
}
|
||||
}
|
||||
if (exist_key == -1) {
|
||||
key.type = (f->keyframes.size() == 0) ? KEYFRAME_TYPE_LINEAR : f->keyframes.at(closest_key).type;
|
||||
key.data = f->get_current_data();//f->keyframes.at(closest_key).data;
|
||||
unsafe_keys[i] = f->keyframes.size();
|
||||
f->keyframes.append(key);
|
||||
key_is_new[i] = true;
|
||||
} else {
|
||||
unsafe_keys[i] = exist_key;
|
||||
key_is_new[i] = false;
|
||||
}
|
||||
unsafe_old_data[i] = f->get_current_data();
|
||||
}
|
||||
just_made_unsafe_keyframe = true;
|
||||
}
|
||||
|
||||
for (int i=0;i<fieldCount();i++) {
|
||||
field(i)->keyframes[unsafe_keys.at(i)].data = field(i)->get_current_data();
|
||||
}
|
||||
|
||||
if (ca != NULL) {
|
||||
for (int i=0;i<fieldCount();i++) {
|
||||
if (key_is_new.at(i)) ca->append(new KeyframeFieldSet(field(i), unsafe_keys.at(i)));
|
||||
ca->append(new SetQVariant(&field(i)->keyframes[unsafe_keys.at(i)].data, unsafe_old_data.at(i), field(i)->get_current_data()));
|
||||
}
|
||||
unsafe_keys.clear();
|
||||
unsafe_old_data.clear();
|
||||
just_made_unsafe_keyframe = false;
|
||||
}
|
||||
|
||||
panel_effect_controls->update_keyframes();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*if (ca != NULL) {
|
||||
just_made_unsafe_keyframe = false;
|
||||
} else {
|
||||
if (!just_made_unsafe_keyframe) {
|
||||
just_made_unsafe_keyframe = true;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/*int index = -1;
|
||||
long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in;
|
||||
for (int j=0;j<fieldCount();j++) {
|
||||
EffectField* f = field(j);
|
||||
for (int i=0;i<f->keyframes.size();i++) {
|
||||
if (f->keyframes.at(i).time == time) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeyframeSet* ks = new KeyframeSet(this, index, time, just_made_unsafe_keyframe);
|
||||
|
||||
if (ca != NULL) {
|
||||
just_made_unsafe_keyframe = false;
|
||||
ca->append(ks);
|
||||
} else {
|
||||
if (index == -1) just_made_unsafe_keyframe = true;
|
||||
ks->redo();
|
||||
delete ks;
|
||||
}
|
||||
|
||||
panel_effect_controls->update_keyframes();*/
|
||||
}
|
||||
|
||||
void EffectRow::delete_keyframe_at_time(KeyframeDelete* kd, long time) {
|
||||
for (int i=0;i<keyframe_times.size();i++) {
|
||||
if (keyframe_times.at(i) == time) {
|
||||
delete_keyframe(kd, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void EffectRow::delete_keyframe_at_time(ComboAction* ca, long time) {
|
||||
for (int j=0;j<fieldCount();j++) {
|
||||
EffectField* f = field(j);
|
||||
for (int i=0;i<f->keyframes.size();i++) {
|
||||
if (f->keyframes.at(i).time == time) {
|
||||
ca->append(new KeyframeDelete(f, i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EffectRow::delete_keyframe(KeyframeDelete* kd, int index) {
|
||||
kd->rows.append(this);
|
||||
kd->keyframes.append(index);
|
||||
const QString &EffectRow::get_name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
EffectField* EffectRow::field(int i) {
|
||||
return fields.at(i);
|
||||
return fields.at(i);
|
||||
}
|
||||
|
||||
int EffectRow::fieldCount() {
|
||||
return fields.size();
|
||||
return fields.size();
|
||||
}
|
||||
|
||||
+32
-29
@@ -8,47 +8,50 @@ class Effect;
|
||||
class QGridLayout;
|
||||
class EffectField;
|
||||
class QLabel;
|
||||
class KeyframeDelete;
|
||||
class QPushButton;
|
||||
class ComboAction;
|
||||
class QHBoxLayout;
|
||||
class KeyframeNavigator;
|
||||
class ClickableLabel;
|
||||
|
||||
class EffectRow : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
EffectRow(Effect* parent, bool save, QGridLayout* uilayout, const QString& n, int row);
|
||||
~EffectRow();
|
||||
EffectField* add_field(int type, const QString &id, int colspan = 1);
|
||||
EffectField* field(int i);
|
||||
int fieldCount();
|
||||
void set_keyframe_now(ComboAction *ca);
|
||||
void delete_keyframe(KeyframeDelete *kd, int index);
|
||||
void delete_keyframe_at_time(KeyframeDelete* kd, long time);
|
||||
QLabel* label;
|
||||
Effect* parent_effect;
|
||||
bool savable;
|
||||
EffectRow(Effect* parent, bool save, QGridLayout* uilayout, const QString& n, int row);
|
||||
~EffectRow();
|
||||
EffectField* add_field(int type, const QString &id, int colspan = 1);
|
||||
EffectField* field(int i);
|
||||
int fieldCount();
|
||||
void set_keyframe_now(ComboAction *ca);
|
||||
void delete_keyframe_at_time(ComboAction *ca, long time);
|
||||
ClickableLabel* label;
|
||||
Effect* parent_effect;
|
||||
bool savable;
|
||||
const QString& get_name();
|
||||
|
||||
bool isKeyframing();
|
||||
void setKeyframing(bool);
|
||||
|
||||
QVector<long> keyframe_times;
|
||||
QVector<int> keyframe_types;
|
||||
bool isKeyframing();
|
||||
void setKeyframing(bool);
|
||||
public slots:
|
||||
void goto_previous_key();
|
||||
void toggle_key();
|
||||
void goto_next_key();
|
||||
void focus_row();
|
||||
private slots:
|
||||
void set_keyframe_enabled(bool);
|
||||
void goto_previous_key();
|
||||
void toggle_key();
|
||||
void goto_next_key();
|
||||
void set_keyframe_enabled(bool);
|
||||
private:
|
||||
bool keyframing;
|
||||
QGridLayout* ui;
|
||||
QString name;
|
||||
int ui_row;
|
||||
QVector<EffectField*> fields;
|
||||
bool keyframing;
|
||||
QGridLayout* ui;
|
||||
QString name;
|
||||
int ui_row;
|
||||
QVector<EffectField*> fields;
|
||||
|
||||
KeyframeNavigator* keyframe_nav;
|
||||
KeyframeNavigator* keyframe_nav;
|
||||
|
||||
bool just_made_unsafe_keyframe;
|
||||
QVector<int> unsafe_keys;
|
||||
QVector<QVariant> unsafe_old_data;
|
||||
QVector<bool> key_is_new;
|
||||
|
||||
bool just_made_unsafe_keyframe;
|
||||
};
|
||||
|
||||
#endif // EFFECTROW_H
|
||||
|
||||
+33
-39
@@ -11,61 +11,55 @@ extern "C" {
|
||||
|
||||
#include "project/clip.h"
|
||||
|
||||
Footage::Footage() : ready(false), preview_gen(NULL), invalid(false), in(0), out(0) {
|
||||
ready_lock.lock();
|
||||
Footage::Footage() : ready(false), preview_gen(NULL), invalid(false), in(0), out(0), speed(1.0) {
|
||||
ready_lock.lock();
|
||||
}
|
||||
|
||||
Footage::~Footage() {
|
||||
reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
void Footage::reset() {
|
||||
if (preview_gen != NULL) {
|
||||
preview_gen->cancel();
|
||||
preview_gen->wait();
|
||||
}
|
||||
for (int i=0;i<video_tracks.size();i++) {
|
||||
delete video_tracks.at(i);
|
||||
}
|
||||
for (int i=0;i<audio_tracks.size();i++) {
|
||||
delete audio_tracks.at(i);
|
||||
}
|
||||
video_tracks.clear();
|
||||
audio_tracks.clear();
|
||||
ready = false;
|
||||
}
|
||||
video_tracks.clear();
|
||||
audio_tracks.clear();
|
||||
ready = false;
|
||||
}
|
||||
|
||||
long Footage::get_length_in_frames(double frame_rate) {
|
||||
if (length >= 0) return qFloor(((double) length / (double) AV_TIME_BASE) * frame_rate);
|
||||
return 0;
|
||||
if (length >= 0) return qFloor(((double) length / (double) AV_TIME_BASE) * frame_rate / speed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FootageStream* Footage::get_stream_from_file_index(bool video, int index) {
|
||||
if (video) {
|
||||
for (int i=0;i<video_tracks.size();i++) {
|
||||
if (video_tracks.at(i)->file_index == index) {
|
||||
return video_tracks.at(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i=0;i<audio_tracks.size();i++) {
|
||||
if (audio_tracks.at(i)->file_index == index) {
|
||||
return audio_tracks.at(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
if (video) {
|
||||
for (int i=0;i<video_tracks.size();i++) {
|
||||
if (video_tracks.at(i).file_index == index) {
|
||||
return &video_tracks[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i=0;i<audio_tracks.size();i++) {
|
||||
if (audio_tracks.at(i).file_index == index) {
|
||||
return &audio_tracks[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FootageStream::make_square_thumb() {
|
||||
// generate square version for QListView?
|
||||
int square_size = qMax(video_preview.width(), video_preview.height());
|
||||
QPixmap pixmap(square_size, square_size);
|
||||
pixmap.fill(Qt::transparent);
|
||||
QPainter p(&pixmap);
|
||||
int diff = (video_preview.width() - video_preview.height())>>1;
|
||||
int sqx = (diff < 0) ? -diff : 0;
|
||||
int sqy = (diff > 0) ? diff : 0;
|
||||
p.drawImage(sqx, sqy, video_preview);
|
||||
video_preview_square = QIcon(pixmap);
|
||||
// generate square version for QListView?
|
||||
int square_size = qMax(video_preview.width(), video_preview.height());
|
||||
QPixmap pixmap(square_size, square_size);
|
||||
pixmap.fill(Qt::transparent);
|
||||
QPainter p(&pixmap);
|
||||
int diff = (video_preview.width() - video_preview.height())>>1;
|
||||
int sqx = (diff < 0) ? -diff : 0;
|
||||
int sqy = (diff > 0) ? diff : 0;
|
||||
p.drawImage(sqx, sqy, video_preview);
|
||||
video_preview_square = QIcon(pixmap);
|
||||
}
|
||||
|
||||
+29
-27
@@ -23,44 +23,46 @@ struct FootageStream {
|
||||
int video_width;
|
||||
int video_height;
|
||||
bool infinite_length;
|
||||
double video_frame_rate;
|
||||
int video_interlacing;
|
||||
double video_frame_rate;
|
||||
int video_interlacing;
|
||||
int video_auto_interlacing;
|
||||
int audio_channels;
|
||||
int audio_layout;
|
||||
int audio_frequency;
|
||||
int audio_channels;
|
||||
int audio_layout;
|
||||
int audio_frequency;
|
||||
bool enabled;
|
||||
|
||||
// preview thumbnail/waveform
|
||||
bool preview_done;
|
||||
QImage video_preview;
|
||||
QIcon video_preview_square;
|
||||
// preview thumbnail/waveform
|
||||
bool preview_done;
|
||||
QImage video_preview;
|
||||
QIcon video_preview_square;
|
||||
QVector<char> audio_preview;
|
||||
void make_square_thumb();
|
||||
void make_square_thumb();
|
||||
};
|
||||
|
||||
struct Footage {
|
||||
Footage();
|
||||
~Footage();
|
||||
Footage();
|
||||
~Footage();
|
||||
|
||||
QString url;
|
||||
QString name;
|
||||
QString url;
|
||||
QString name;
|
||||
int64_t length;
|
||||
QVector<FootageStream*> video_tracks;
|
||||
QVector<FootageStream*> audio_tracks;
|
||||
int save_id;
|
||||
bool ready;
|
||||
bool invalid;
|
||||
QVector<FootageStream> video_tracks;
|
||||
QVector<FootageStream> audio_tracks;
|
||||
int save_id;
|
||||
bool ready;
|
||||
bool invalid;
|
||||
double speed;
|
||||
|
||||
PreviewGenerator* preview_gen;
|
||||
QMutex ready_lock;
|
||||
PreviewGenerator* preview_gen;
|
||||
QMutex ready_lock;
|
||||
|
||||
bool using_inout;
|
||||
long in;
|
||||
long out;
|
||||
bool using_inout;
|
||||
long in;
|
||||
long out;
|
||||
|
||||
long get_length_in_frames(double frame_rate);
|
||||
FootageStream* get_stream_from_file_index(bool video, int index);
|
||||
void reset();
|
||||
long get_length_in_frames(double frame_rate);
|
||||
FootageStream *get_stream_from_file_index(bool video, int index);
|
||||
void reset();
|
||||
};
|
||||
|
||||
#endif // FOOTAGE_H
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "keyframe.h"
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include "effectfield.h"
|
||||
#include "undo.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)));
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
selected_keys.clear();
|
||||
selected_key_fields.clear();
|
||||
update_ui(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef KEYFRAME_H
|
||||
#define KEYFRAME_H
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
class EffectField;
|
||||
|
||||
class EffectKeyframe {
|
||||
public:
|
||||
EffectKeyframe();
|
||||
|
||||
long time;
|
||||
int type;
|
||||
QVariant data;
|
||||
|
||||
// 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);
|
||||
|
||||
#endif // KEYFRAME_H
|
||||
+200
-199
@@ -13,310 +13,311 @@
|
||||
#include "debug.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
QString get_interlacing_name(int interlacing) {
|
||||
switch (interlacing) {
|
||||
case VIDEO_PROGRESSIVE: return "None (Progressive)";
|
||||
case VIDEO_TOP_FIELD_FIRST: return "Top Field First";
|
||||
case VIDEO_BOTTOM_FIELD_FIRST: return "Bottom Field First";
|
||||
default: return "Invalid";
|
||||
}
|
||||
switch (interlacing) {
|
||||
case VIDEO_PROGRESSIVE: return "None (Progressive)";
|
||||
case VIDEO_TOP_FIELD_FIRST: return "Top Field First";
|
||||
case VIDEO_BOTTOM_FIELD_FIRST: return "Bottom Field First";
|
||||
default: return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
QString get_channel_layout_name(int channels, uint64_t layout) {
|
||||
switch (channels) {
|
||||
case 0: return "Invalid"; break;
|
||||
case 1: return "Mono"; break;
|
||||
case 2: return "Stereo"; break;
|
||||
default: {
|
||||
char buf[50];
|
||||
av_get_channel_layout_string(buf, sizeof(buf), channels, layout);
|
||||
return QString(buf);
|
||||
}
|
||||
}
|
||||
switch (channels) {
|
||||
case 0: return "Invalid"; break;
|
||||
case 1: return "Mono"; break;
|
||||
case 2: return "Stereo"; break;
|
||||
default: {
|
||||
char buf[50];
|
||||
av_get_channel_layout_string(buf, sizeof(buf), channels, layout);
|
||||
return QString(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Media::Media(Media* iparent) :
|
||||
parent(iparent),
|
||||
throbber(NULL),
|
||||
root(false),
|
||||
type(-1)
|
||||
parent(iparent),
|
||||
throbber(NULL),
|
||||
root(false),
|
||||
type(-1)
|
||||
{}
|
||||
|
||||
Media::~Media() {
|
||||
switch (get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE: delete to_footage(); break;
|
||||
case MEDIA_TYPE_SEQUENCE: if (object != NULL) delete to_sequence(); break;
|
||||
}
|
||||
if (throbber != NULL) delete throbber;
|
||||
qDeleteAll(children);
|
||||
switch (get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE: delete to_footage(); break;
|
||||
case MEDIA_TYPE_SEQUENCE: if (object != NULL) delete to_sequence(); break;
|
||||
}
|
||||
if (throbber != NULL) delete throbber;
|
||||
qDeleteAll(children);
|
||||
}
|
||||
|
||||
Footage *Media::to_footage() {
|
||||
return static_cast<Footage*>(object);
|
||||
return static_cast<Footage*>(object);
|
||||
}
|
||||
|
||||
Sequence *Media::to_sequence() {
|
||||
return static_cast<Sequence*>(object);
|
||||
return static_cast<Sequence*>(object);
|
||||
}
|
||||
|
||||
void Media::set_footage(Footage *f) {
|
||||
type = MEDIA_TYPE_FOOTAGE;
|
||||
object = f;
|
||||
type = MEDIA_TYPE_FOOTAGE;
|
||||
object = f;
|
||||
}
|
||||
|
||||
void Media::set_sequence(Sequence *s) {
|
||||
set_icon(QIcon(":/icons/sequence.png"));
|
||||
type = MEDIA_TYPE_SEQUENCE;
|
||||
object = s;
|
||||
if (s != NULL) update_tooltip();
|
||||
set_icon(QIcon(":/icons/sequence.png"));
|
||||
type = MEDIA_TYPE_SEQUENCE;
|
||||
object = s;
|
||||
if (s != NULL) update_tooltip();
|
||||
}
|
||||
|
||||
void Media::set_folder() {
|
||||
if (folder_name.isEmpty()) folder_name = "New Folder";
|
||||
set_icon(QIcon(":/icons/folder.png"));
|
||||
type = MEDIA_TYPE_FOLDER;
|
||||
object = NULL;
|
||||
if (folder_name.isEmpty()) folder_name = "New Folder";
|
||||
set_icon(QIcon(":/icons/folder.png"));
|
||||
type = MEDIA_TYPE_FOLDER;
|
||||
object = NULL;
|
||||
}
|
||||
|
||||
void Media::set_icon(const QIcon &ico) {
|
||||
icon = ico;
|
||||
icon = ico;
|
||||
}
|
||||
|
||||
void Media::set_parent(Media *p) {
|
||||
parent = p;
|
||||
parent = p;
|
||||
}
|
||||
|
||||
void Media::update_tooltip(const QString& error) {
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Footage* f = to_footage();
|
||||
tooltip = "Name: " + f->name + "\nFilename: " + f->url + "\n";
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Footage* f = to_footage();
|
||||
tooltip = "Name: " + f->name + "\nFilename: " + f->url + "\n";
|
||||
|
||||
if (error.isEmpty()) {
|
||||
if (f->video_tracks.size() > 0) {
|
||||
tooltip += "Video Dimensions: ";
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
tooltip += QString::number(f->video_tracks.at(i)->video_width) + "x" + QString::number(f->video_tracks.at(i)->video_height);
|
||||
}
|
||||
tooltip += "\n";
|
||||
if (error.isEmpty()) {
|
||||
if (f->video_tracks.size() > 0) {
|
||||
tooltip += "Video Dimensions: ";
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
tooltip += QString::number(f->video_tracks.at(i).video_width) + "x" + QString::number(f->video_tracks.at(i).video_height);
|
||||
}
|
||||
tooltip += "\n";
|
||||
|
||||
if (!f->video_tracks.at(0)->infinite_length) {
|
||||
tooltip += "Frame Rate: ";
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
if (f->video_tracks.at(i)->video_interlacing == VIDEO_PROGRESSIVE) {
|
||||
tooltip += QString::number(f->video_tracks.at(i)->video_frame_rate);
|
||||
} else {
|
||||
tooltip += QString::number(f->video_tracks.at(i)->video_frame_rate * 2);
|
||||
tooltip += " fields (" + QString::number(f->video_tracks.at(i)->video_frame_rate) + " frames)";
|
||||
}
|
||||
}
|
||||
tooltip += "\n";
|
||||
}
|
||||
if (!f->video_tracks.at(0).infinite_length) {
|
||||
tooltip += "Frame Rate: ";
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
if (f->video_tracks.at(i).video_interlacing == VIDEO_PROGRESSIVE) {
|
||||
tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * f->speed);
|
||||
} else {
|
||||
tooltip += QString::number(f->video_tracks.at(i).video_frame_rate * f->speed * 2);
|
||||
tooltip += " fields (" + QString::number(f->video_tracks.at(i).video_frame_rate * f->speed) + " frames)";
|
||||
}
|
||||
}
|
||||
tooltip += "\n";
|
||||
}
|
||||
|
||||
tooltip += "Interlacing: ";
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
tooltip += get_interlacing_name(f->video_tracks.at(i)->video_interlacing);
|
||||
}
|
||||
}
|
||||
tooltip += "Interlacing: ";
|
||||
for (int i=0;i<f->video_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
tooltip += get_interlacing_name(f->video_tracks.at(i).video_interlacing);
|
||||
}
|
||||
}
|
||||
|
||||
if (f->audio_tracks.size() > 0) {
|
||||
tooltip += "\n";
|
||||
if (f->audio_tracks.size() > 0) {
|
||||
tooltip += "\n";
|
||||
|
||||
tooltip += "Audio Frequency: ";
|
||||
for (int i=0;i<f->audio_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
tooltip += QString::number(f->audio_tracks.at(i)->audio_frequency);
|
||||
}
|
||||
tooltip += "\n";
|
||||
tooltip += "Audio Frequency: ";
|
||||
for (int i=0;i<f->audio_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
tooltip += QString::number(f->audio_tracks.at(i).audio_frequency * f->speed);
|
||||
}
|
||||
tooltip += "\n";
|
||||
|
||||
tooltip += "Audio Channels: ";
|
||||
for (int i=0;i<f->audio_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
tooltip += get_channel_layout_name(f->audio_tracks.at(i)->audio_channels, f->audio_tracks.at(i)->audio_layout);
|
||||
}
|
||||
// tooltip += "\n";
|
||||
}
|
||||
} else {
|
||||
tooltip += error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
{
|
||||
Sequence* s = to_sequence();
|
||||
tooltip = "Name: " + s->name
|
||||
+ "\nVideo Dimensions: " + QString::number(s->width) + "x" + QString::number(s->height)
|
||||
+ "\nFrame Rate: " + QString::number(s->frame_rate)
|
||||
+ "\nAudio Frequency: " + QString::number(s->audio_frequency)
|
||||
+ "\nAudio Layout: " + get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
tooltip += "Audio Channels: ";
|
||||
for (int i=0;i<f->audio_tracks.size();i++) {
|
||||
if (i > 0) {
|
||||
tooltip += ", ";
|
||||
}
|
||||
tooltip += get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout);
|
||||
}
|
||||
// tooltip += "\n";
|
||||
}
|
||||
} else {
|
||||
tooltip += error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
{
|
||||
Sequence* s = to_sequence();
|
||||
tooltip = "Name: " + s->name
|
||||
+ "\nVideo Dimensions: " + QString::number(s->width) + "x" + QString::number(s->height)
|
||||
+ "\nFrame Rate: " + QString::number(s->frame_rate)
|
||||
+ "\nAudio Frequency: " + QString::number(s->audio_frequency)
|
||||
+ "\nAudio Layout: " + get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void *Media::to_object() {
|
||||
return object;
|
||||
return object;
|
||||
}
|
||||
|
||||
int Media::get_type() {
|
||||
return type;
|
||||
return type;
|
||||
}
|
||||
|
||||
const QString &Media::get_name() {
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE: return to_footage()->name;
|
||||
case MEDIA_TYPE_SEQUENCE: return to_sequence()->name;
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE: return to_footage()->name;
|
||||
case MEDIA_TYPE_SEQUENCE: return to_sequence()->name;
|
||||
default: return folder_name;
|
||||
}
|
||||
}
|
||||
|
||||
void Media::set_name(const QString &n) {
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE: to_footage()->name = n; break;
|
||||
case MEDIA_TYPE_SEQUENCE: to_sequence()->name = n; break;
|
||||
case MEDIA_TYPE_FOLDER: folder_name = n; break;
|
||||
}
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE: to_footage()->name = n; break;
|
||||
case MEDIA_TYPE_SEQUENCE: to_sequence()->name = n; break;
|
||||
case MEDIA_TYPE_FOLDER: folder_name = n; break;
|
||||
}
|
||||
}
|
||||
|
||||
double Media::get_frame_rate(int stream) {
|
||||
switch (get_type()) {
|
||||
switch (get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Footage* f = to_footage();
|
||||
if (stream < 0) return f->video_tracks.at(0)->video_frame_rate;
|
||||
return f->get_stream_from_file_index(true, stream)->video_frame_rate;
|
||||
if (stream < 0) return f->video_tracks.at(0).video_frame_rate * f->speed;
|
||||
return f->get_stream_from_file_index(true, stream)->video_frame_rate * f->speed;
|
||||
}
|
||||
case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate;
|
||||
}
|
||||
case MEDIA_TYPE_SEQUENCE: return to_sequence()->frame_rate;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int Media::get_sampling_rate(int stream) {
|
||||
switch (get_type()) {
|
||||
switch (get_type()) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Footage* f = to_footage();
|
||||
if (stream < 0) return f->audio_tracks.at(0)->audio_frequency;
|
||||
return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency;
|
||||
if (stream < 0) return f->audio_tracks.at(0).audio_frequency * f->speed;
|
||||
return to_footage()->get_stream_from_file_index(false, stream)->audio_frequency * f->speed;
|
||||
}
|
||||
case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency;
|
||||
}
|
||||
return 0;
|
||||
case MEDIA_TYPE_SEQUENCE: return to_sequence()->audio_frequency;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Media::appendChild(Media *child) {
|
||||
child->set_parent(this);
|
||||
children.append(child);
|
||||
child->set_parent(this);
|
||||
children.append(child);
|
||||
}
|
||||
|
||||
bool Media::setData(int col, const QVariant &value) {
|
||||
if (col == 0) {
|
||||
QString n = value.toString();
|
||||
if (!n.isEmpty() && get_name() != n) {
|
||||
undo_stack.push(new MediaRename(this, value.toString()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if (col == 0) {
|
||||
QString n = value.toString();
|
||||
if (!n.isEmpty() && get_name() != n) {
|
||||
undo_stack.push(new MediaRename(this, value.toString()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Media *Media::child(int row) {
|
||||
return children.value(row);
|
||||
return children.value(row);
|
||||
}
|
||||
|
||||
int Media::childCount() const {
|
||||
return children.count();
|
||||
return children.count();
|
||||
}
|
||||
|
||||
int Media::columnCount() const {
|
||||
return 3;
|
||||
return 3;
|
||||
}
|
||||
|
||||
QVariant Media::data(int column, int role) {
|
||||
switch (role) {
|
||||
case Qt::DecorationRole:
|
||||
if (column == 0) {
|
||||
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = to_footage();
|
||||
if (f->video_tracks.size() > 0
|
||||
&& f->video_tracks.at(0)->preview_done) {
|
||||
return f->video_tracks.at(0)->video_preview_square;
|
||||
}
|
||||
}
|
||||
switch (role) {
|
||||
case Qt::DecorationRole:
|
||||
if (column == 0) {
|
||||
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = to_footage();
|
||||
if (f->video_tracks.size() > 0
|
||||
&& f->video_tracks.at(0).preview_done) {
|
||||
return f->video_tracks.at(0).video_preview_square;
|
||||
}
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
break;
|
||||
case Qt::DisplayRole:
|
||||
switch (column) {
|
||||
case 0: return (root) ? "Name" : get_name();
|
||||
case 1:
|
||||
if (root) return "Duration";
|
||||
if (get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
Sequence* s = to_sequence();
|
||||
return frame_to_timecode(s->getEndFrame(), config.timecode_view, s->frame_rate);
|
||||
}
|
||||
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = to_footage();
|
||||
double r = 30;
|
||||
return icon;
|
||||
}
|
||||
break;
|
||||
case Qt::DisplayRole:
|
||||
switch (column) {
|
||||
case 0: return (root) ? "Name" : get_name();
|
||||
case 1:
|
||||
if (root) return "Duration";
|
||||
if (get_type() == MEDIA_TYPE_SEQUENCE) {
|
||||
Sequence* s = to_sequence();
|
||||
return frame_to_timecode(s->getEndFrame(), config.timecode_view, s->frame_rate);
|
||||
}
|
||||
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = to_footage();
|
||||
double r = 30;
|
||||
|
||||
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0)->video_frame_rate)) r = f->video_tracks.at(0)->video_frame_rate;
|
||||
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate))
|
||||
r = f->video_tracks.at(0).video_frame_rate * f->speed;
|
||||
|
||||
long len = f->get_length_in_frames(r);
|
||||
if (len > 0) return frame_to_timecode(len, config.timecode_view, r);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (root) return "Rate";
|
||||
if (get_type() == MEDIA_TYPE_SEQUENCE) return QString::number(get_frame_rate()) + " FPS";
|
||||
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = to_footage();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (root) return "Rate";
|
||||
if (get_type() == MEDIA_TYPE_SEQUENCE) return QString::number(get_frame_rate()) + " FPS";
|
||||
if (get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
Footage* f = to_footage();
|
||||
double r;
|
||||
if (f->video_tracks.size() > 0 && !qIsNull(r = get_frame_rate())) {
|
||||
return QString::number(get_frame_rate()) + " FPS";
|
||||
} else if (f->audio_tracks.size() > 0) {
|
||||
return QString::number(get_sampling_rate()) + " Hz";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::ToolTipRole:
|
||||
return tooltip;
|
||||
}
|
||||
return QVariant();
|
||||
} else if (f->audio_tracks.size() > 0) {
|
||||
return QString::number(get_sampling_rate()) + " Hz";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::ToolTipRole:
|
||||
return tooltip;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int Media::row() const {
|
||||
if (parent) {
|
||||
return parent->children.indexOf(const_cast<Media*>(this));
|
||||
}
|
||||
return 0;
|
||||
if (parent) {
|
||||
return parent->children.indexOf(const_cast<Media*>(this));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Media *Media::parentItem() {
|
||||
return parent;
|
||||
return parent;
|
||||
}
|
||||
|
||||
void Media::removeChild(int i) {
|
||||
children.removeAt(i);
|
||||
children.removeAt(i);
|
||||
}
|
||||
|
||||
@@ -127,6 +127,11 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it
|
||||
QAction* icon_view_action = menu.addAction("Icon View");
|
||||
connect(icon_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_icon_view()));
|
||||
|
||||
QAction* toolbar_action = menu.addAction("Show Toolbar");
|
||||
toolbar_action->setCheckable(true);
|
||||
toolbar_action->setChecked(project_parent->toolbar_widget->isVisible());
|
||||
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent->toolbar_widget, SLOT(setVisible(bool)));
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
|
||||
+119
-147
@@ -150,7 +150,7 @@ void DeleteClipAction::redo() {
|
||||
// remove ref to clip
|
||||
ref = seq->clips.at(index);
|
||||
if (ref->open) {
|
||||
close_clip(ref);
|
||||
close_clip(ref, true);
|
||||
}
|
||||
seq->clips[index] = NULL;
|
||||
|
||||
@@ -493,7 +493,7 @@ void AddClipCommand::undo() {
|
||||
Clip* c = seq->clips.last();
|
||||
panel_timeline->deselect_area(c->timeline_in, c->timeline_out, c->track);
|
||||
undone_clips.prepend(c);
|
||||
if (c->open) close_clip(c);
|
||||
if (c->open) close_clip(c, true);
|
||||
seq->clips.removeLast();
|
||||
}
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
@@ -587,8 +587,7 @@ void ReplaceMediaCommand::replace(QString& filename) {
|
||||
for (int j=0;j<s->clips.size();j++) {
|
||||
Clip* c = s->clips.at(j);
|
||||
if (c != NULL && c->media == item && c->open) {
|
||||
close_clip(c);
|
||||
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) c->cacher->wait();
|
||||
close_clip(c, true);
|
||||
c->replaced = true;
|
||||
}
|
||||
}
|
||||
@@ -628,8 +627,7 @@ void ReplaceClipMediaCommand::replace(bool undo) {
|
||||
for (int i=0;i<clips.size();i++) {
|
||||
Clip* c = clips.at(i);
|
||||
if (c->open) {
|
||||
close_clip(c);
|
||||
if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) c->cacher->wait();
|
||||
close_clip(c, true);
|
||||
}
|
||||
|
||||
if (undo) {
|
||||
@@ -737,148 +735,21 @@ void MediaRename::redo() {
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
|
||||
KeyframeMove::KeyframeMove() : old_project_changed(mainWindow->isWindowModified()) {}
|
||||
|
||||
void KeyframeMove::undo() {
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
rows.at(i)->keyframe_times[keyframes.at(i)] -= movement;
|
||||
}
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
}
|
||||
|
||||
void KeyframeMove::redo() {
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
rows.at(i)->keyframe_times[keyframes.at(i)] += movement;
|
||||
}
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
|
||||
KeyframeDelete::KeyframeDelete() :
|
||||
disable_keyframes_on_row(NULL),
|
||||
old_project_changed(mainWindow->isWindowModified()),
|
||||
sorted(false)
|
||||
KeyframeDelete::KeyframeDelete(EffectField *ifield, int iindex) :
|
||||
field(ifield),
|
||||
index(iindex),
|
||||
old_project_changed(mainWindow->isWindowModified())
|
||||
{}
|
||||
|
||||
void KeyframeDelete::undo() {
|
||||
if (disable_keyframes_on_row != NULL) disable_keyframes_on_row->setKeyframing(true);
|
||||
|
||||
int data_index = deleted_keyframe_data.size()-1;
|
||||
for (int i=rows.size()-1;i>=0;i--) {
|
||||
EffectRow* row = rows.at(i);
|
||||
int keyframe_index = keyframes.at(i);
|
||||
|
||||
row->keyframe_times.insert(keyframe_index, deleted_keyframe_times.at(i));
|
||||
row->keyframe_types.insert(keyframe_index, deleted_keyframe_types.at(i));
|
||||
|
||||
for (int j=row->fieldCount()-1;j>=0;j--) {
|
||||
row->field(j)->keyframe_data.insert(keyframe_index, deleted_keyframe_data.at(data_index));
|
||||
data_index--;
|
||||
}
|
||||
}
|
||||
|
||||
field->keyframes.insert(index, deleted_key);
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
}
|
||||
|
||||
void KeyframeDelete::redo() {
|
||||
if (!sorted) {
|
||||
deleted_keyframe_times.resize(rows.size());
|
||||
deleted_keyframe_types.resize(rows.size());
|
||||
deleted_keyframe_data.clear();
|
||||
}
|
||||
|
||||
for (int i=0;i<keyframes.size();i++) {
|
||||
EffectRow* row = rows.at(i);
|
||||
int keyframe_index = keyframes.at(i);
|
||||
|
||||
if (!sorted) {
|
||||
deleted_keyframe_times[i] = (row->keyframe_times.at(keyframe_index));
|
||||
deleted_keyframe_types[i] = (row->keyframe_types.at(keyframe_index));
|
||||
}
|
||||
|
||||
row->keyframe_times.removeAt(keyframe_index);
|
||||
row->keyframe_types.removeAt(keyframe_index);
|
||||
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
if (!sorted) deleted_keyframe_data.append(row->field(j)->keyframe_data.at(keyframe_index));
|
||||
row->field(j)->keyframe_data.removeAt(keyframe_index);
|
||||
}
|
||||
|
||||
// correct following indices
|
||||
if (!sorted) {
|
||||
for (int j=i+1;j<keyframes.size();j++) {
|
||||
if (rows.at(j) == row && keyframes.at(j) > keyframe_index) {
|
||||
keyframes[j]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (disable_keyframes_on_row != NULL) disable_keyframes_on_row->setKeyframing(false);
|
||||
deleted_key = field->keyframes.at(index);
|
||||
field->keyframes.removeAt(index);
|
||||
mainWindow->setWindowModified(true);
|
||||
sorted = true;
|
||||
}
|
||||
|
||||
KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) :
|
||||
old_project_changed(mainWindow->isWindowModified()),
|
||||
row(r),
|
||||
index(i),
|
||||
time(t),
|
||||
just_made_keyframe(justMadeKeyframe),
|
||||
done(true)
|
||||
{
|
||||
enable_keyframes = !row->isKeyframing();
|
||||
if (index != -1) old_values.resize(row->fieldCount());
|
||||
new_values.resize(row->fieldCount());
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
EffectField* field = row->field(i);
|
||||
if (index != -1) {
|
||||
if (field->type == EFFECT_FIELD_DOUBLE) {
|
||||
old_values[i] = static_cast<LabelSlider*>(field->ui_element)->getPreviousValue();
|
||||
} else {
|
||||
old_values[i] = field->keyframe_data.at(index);
|
||||
}
|
||||
}
|
||||
new_values[i] = field->get_current_data();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeSet::undo() {
|
||||
if (enable_keyframes) row->setKeyframing(false);
|
||||
|
||||
bool append = (index == -1 || just_made_keyframe);
|
||||
if (append) {
|
||||
row->keyframe_times.removeLast();
|
||||
row->keyframe_types.removeLast();
|
||||
}
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
if (append) {
|
||||
row->field(i)->keyframe_data.removeLast();
|
||||
} else {
|
||||
row->field(i)->keyframe_data[index] = old_values.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
done = false;
|
||||
}
|
||||
|
||||
void KeyframeSet::redo() {
|
||||
bool append = (index == -1 || (just_made_keyframe && !done));
|
||||
if (append) {
|
||||
row->keyframe_times.append(time);
|
||||
row->keyframe_types.append((row->keyframe_types.size() > 0) ? row->keyframe_types.last() : EFFECT_KEYFRAME_LINEAR);
|
||||
}
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
if (append) {
|
||||
row->field(i)->keyframe_data.append(new_values.at(i));
|
||||
} else {
|
||||
row->field(i)->keyframe_data[index] = new_values.at(i);
|
||||
}
|
||||
}
|
||||
row->setKeyframing(true);
|
||||
|
||||
mainWindow->setWindowModified(true);
|
||||
done = true;
|
||||
}
|
||||
|
||||
EffectFieldUndo::EffectFieldUndo(EffectField* f) :
|
||||
@@ -1122,12 +993,7 @@ void EditSequenceCommand::update() {
|
||||
item->set_sequence(seq);
|
||||
|
||||
for (int i=0;i<seq->clips.size();i++) {
|
||||
// TODO shift in/out/clipin points to match new frame rate
|
||||
// BUT ALSO copy/paste must need a similar routine, no?
|
||||
// See if one exists or if you have to make one, make it
|
||||
// re-usable
|
||||
|
||||
seq->clips.at(i)->refresh();
|
||||
if (seq->clips.at(i) != NULL) seq->clips.at(i)->refresh();
|
||||
}
|
||||
|
||||
if (sequence == seq) {
|
||||
@@ -1174,7 +1040,7 @@ void CloseAllClipsCommand::undo() {
|
||||
}
|
||||
|
||||
void CloseAllClipsCommand::redo() {
|
||||
closeActiveClips(sequence, true);
|
||||
closeActiveClips(sequence);
|
||||
}
|
||||
|
||||
UpdateFootageTooltip::UpdateFootageTooltip(Media *i) :
|
||||
@@ -1295,3 +1161,109 @@ void RippleAction::redo() {
|
||||
}
|
||||
ca->redo();
|
||||
}
|
||||
|
||||
SetDouble::SetDouble(double* pointer, double old_value, double new_value) :
|
||||
p(pointer),
|
||||
oldval(old_value),
|
||||
newval(new_value),
|
||||
old_project_changed(mainWindow->isWindowModified())
|
||||
{}
|
||||
|
||||
void SetDouble::undo() {
|
||||
*p = oldval;
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
}
|
||||
|
||||
void SetDouble::redo() {
|
||||
*p = newval;
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
|
||||
SetQVariant::SetQVariant(QVariant *itarget, const QVariant &iold, const QVariant &inew) :
|
||||
target(itarget),
|
||||
old_val(iold),
|
||||
new_val(inew)
|
||||
{}
|
||||
|
||||
void SetQVariant::undo() {
|
||||
*target = old_val;
|
||||
}
|
||||
|
||||
void SetQVariant::redo() {
|
||||
*target = new_val;
|
||||
}
|
||||
|
||||
SetLong::SetLong(long *pointer, long old_value, long new_value) :
|
||||
p(pointer),
|
||||
oldval(old_value),
|
||||
newval(new_value),
|
||||
old_project_changed(mainWindow->isWindowModified())
|
||||
{}
|
||||
|
||||
void SetLong::undo() {
|
||||
*p = oldval;
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
}
|
||||
|
||||
void SetLong::redo() {
|
||||
*p = newval;
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
|
||||
KeyframeFieldSet::KeyframeFieldSet(EffectField *ifield, int ii) :
|
||||
field(ifield),
|
||||
index(ii),
|
||||
key(ifield->keyframes.at(ii)),
|
||||
done(true),
|
||||
old_project_changed(mainWindow->isWindowModified())
|
||||
{}
|
||||
|
||||
void KeyframeFieldSet::undo() {
|
||||
field->keyframes.removeAt(index);
|
||||
mainWindow->setWindowModified(old_project_changed);
|
||||
done = false;
|
||||
}
|
||||
|
||||
void KeyframeFieldSet::redo() {
|
||||
if (!done) {
|
||||
field->keyframes.insert(index, key);
|
||||
mainWindow->setWindowModified(true);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
|
||||
SetKeyframing::SetKeyframing(EffectRow *irow, bool ib) :
|
||||
row(irow),
|
||||
b(ib)
|
||||
{}
|
||||
|
||||
void SetKeyframing::undo() {
|
||||
row->setKeyframing(!b);
|
||||
}
|
||||
|
||||
void SetKeyframing::redo() {
|
||||
row->setKeyframing(b);
|
||||
}
|
||||
|
||||
RefreshClips::RefreshClips(Media *m) :
|
||||
media(m)
|
||||
{}
|
||||
|
||||
void RefreshClips::undo() {
|
||||
redo();
|
||||
}
|
||||
|
||||
void RefreshClips::redo() {
|
||||
// close any clips currently using this media
|
||||
QVector<Media*> all_sequences = panel_project->list_all_project_sequences();
|
||||
for (int i=0;i<all_sequences.size();i++) {
|
||||
Sequence* s = all_sequences.at(i)->to_sequence();
|
||||
for (int j=0;j<s->clips.size();j++) {
|
||||
Clip* c = s->clips.at(j);
|
||||
if (c != NULL && c->media == media) {
|
||||
c->replaced = true;
|
||||
c->refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+67
-30
@@ -17,6 +17,7 @@ struct EffectMeta;
|
||||
|
||||
#include "project/marker.h"
|
||||
#include "project/selection.h"
|
||||
#include "project/effectfield.h"
|
||||
|
||||
#include <QUndoStack>
|
||||
#include <QUndoCommand>
|
||||
@@ -328,50 +329,32 @@ private:
|
||||
QString to;
|
||||
};
|
||||
|
||||
class KeyframeMove : public QUndoCommand {
|
||||
public:
|
||||
KeyframeMove();
|
||||
QVector<EffectRow*> rows;
|
||||
QVector<int> keyframes;
|
||||
long movement;
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
bool old_project_changed;
|
||||
};
|
||||
|
||||
class KeyframeDelete : public QUndoCommand {
|
||||
public:
|
||||
KeyframeDelete();
|
||||
QVector<EffectRow*> rows;
|
||||
EffectRow* disable_keyframes_on_row;
|
||||
QVector<int> keyframes;
|
||||
KeyframeDelete(EffectField* ifield, int iindex);
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
EffectField* field;
|
||||
int index;
|
||||
bool done;
|
||||
EffectKeyframe deleted_key;
|
||||
bool old_project_changed;
|
||||
QVector<long> deleted_keyframe_times;
|
||||
QVector<int> deleted_keyframe_types;
|
||||
QVector<QVariant> deleted_keyframe_data;
|
||||
bool sorted;
|
||||
};
|
||||
|
||||
|
||||
class KeyframeSet : public QUndoCommand {
|
||||
// a more modern version of the above, could probably replace it
|
||||
// assumes the keyframe already exists
|
||||
class KeyframeFieldSet : public QUndoCommand {
|
||||
public:
|
||||
KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe);
|
||||
KeyframeFieldSet(EffectField* ifield, int ii);
|
||||
void undo();
|
||||
void redo();
|
||||
QVector<QVariant> old_values;
|
||||
QVector<QVariant> new_values;
|
||||
private:
|
||||
bool old_project_changed;
|
||||
EffectRow* row;
|
||||
EffectField* field;
|
||||
int index;
|
||||
long time;
|
||||
bool enable_keyframes;
|
||||
bool just_made_keyframe;
|
||||
EffectKeyframe key;
|
||||
bool done;
|
||||
bool old_project_changed;
|
||||
};
|
||||
|
||||
class EffectFieldUndo : public QUndoCommand {
|
||||
@@ -523,6 +506,30 @@ private:
|
||||
bool old_project_changed;
|
||||
};
|
||||
|
||||
class SetLong : public QUndoCommand {
|
||||
public:
|
||||
SetLong(long* pointer, long old_value, long new_value);
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
long* p;
|
||||
long oldval;
|
||||
long newval;
|
||||
bool old_project_changed;
|
||||
};
|
||||
|
||||
class SetDouble : public QUndoCommand {
|
||||
public:
|
||||
SetDouble(double* pointer, double old_value, double new_value);
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
double* p;
|
||||
double oldval;
|
||||
double newval;
|
||||
bool old_project_changed;
|
||||
};
|
||||
|
||||
class SetString : public QUndoCommand {
|
||||
public:
|
||||
SetString(QString* pointer, QString new_value);
|
||||
@@ -605,4 +612,34 @@ public:
|
||||
void redo();
|
||||
};
|
||||
|
||||
class SetQVariant : public QUndoCommand {
|
||||
public:
|
||||
SetQVariant(QVariant* itarget, const QVariant& iold, const QVariant& inew);
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
QVariant* target;
|
||||
QVariant old_val;
|
||||
QVariant new_val;
|
||||
};
|
||||
|
||||
class SetKeyframing : public QUndoCommand {
|
||||
public:
|
||||
SetKeyframing(EffectRow* irow, bool ib);
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
EffectRow* row;
|
||||
bool b;
|
||||
};
|
||||
|
||||
class RefreshClips : public QUndoCommand {
|
||||
public:
|
||||
RefreshClips(Media* m);
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
Media* media;
|
||||
};
|
||||
|
||||
#endif // UNDO_H
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "clickablelabel.h"
|
||||
|
||||
ClickableLabel::ClickableLabel(QWidget *parent, Qt::WindowFlags f) :
|
||||
QLabel(parent, f)
|
||||
{}
|
||||
|
||||
ClickableLabel::ClickableLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) :
|
||||
QLabel(text, parent, f)
|
||||
{}
|
||||
|
||||
void ClickableLabel::mousePressEvent(QMouseEvent *) {
|
||||
emit clicked();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef CLICKABLELABEL_H
|
||||
#define CLICKABLELABEL_H
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
class ClickableLabel : public QLabel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClickableLabel(QWidget * parent = 0, Qt::WindowFlags f = 0);
|
||||
ClickableLabel(const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0);
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
signals:
|
||||
void clicked();
|
||||
};
|
||||
|
||||
#endif // CLICKABLELABEL_H
|
||||
@@ -0,0 +1,840 @@
|
||||
#include "graphview.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QtMath>
|
||||
#include <QMenu>
|
||||
#include <cfloat>
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/effectrow.h"
|
||||
#include "project/effectfield.h"
|
||||
#include "ui/keyframedrawing.h"
|
||||
#include "project/undo.h"
|
||||
#include "project/effect.h"
|
||||
#include "project/clip.h"
|
||||
#include "ui/rectangleselect.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#define GRAPH_ZOOM_SPEED 0.05
|
||||
#define GRAPH_SIZE 100
|
||||
#define BEZIER_HANDLE_SIZE 3
|
||||
#define BEZIER_LINE_SIZE 2
|
||||
|
||||
#define BEZIER_HANDLE_NONE 1
|
||||
#define BEZIER_HANDLE_PRE 2
|
||||
#define BEZIER_HANDLE_POST 3
|
||||
|
||||
QColor get_curve_color(int index, int length) {
|
||||
QColor c;
|
||||
int hue = qRound((double(index)/double(length))*255);
|
||||
c.setHsv(hue, 255, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
GraphView::GraphView(QWidget* parent) :
|
||||
QWidget(parent),
|
||||
x_scroll(0),
|
||||
y_scroll(0),
|
||||
mousedown(false),
|
||||
zoom(1.0),
|
||||
row(NULL),
|
||||
moved_keys(false),
|
||||
current_handle(BEZIER_HANDLE_NONE),
|
||||
rect_select(false),
|
||||
visible_in(0),
|
||||
click_add_proc(false)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&)));
|
||||
}
|
||||
|
||||
void GraphView::show_context_menu(const QPoint& pos) {
|
||||
QMenu menu(this);
|
||||
|
||||
QAction* zoom_to_selection = menu.addAction("Zoom to Selection");
|
||||
if (selected_keys.size() == 0) {
|
||||
zoom_to_selection->setEnabled(false);
|
||||
} else {
|
||||
connect(zoom_to_selection, SIGNAL(triggered(bool)), this, SLOT(set_view_to_selection()));
|
||||
}
|
||||
|
||||
QAction* zoom_to_all = menu.addAction("Zoom to Show All");
|
||||
connect(zoom_to_all, SIGNAL(triggered(bool)), this, SLOT(set_view_to_all()));
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
QAction* reset_action = menu.addAction("Reset View");
|
||||
connect(reset_action, SIGNAL(triggered(bool)), this, SLOT(reset_view()));
|
||||
|
||||
menu.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void GraphView::reset_view() {
|
||||
zoom = 1.0;
|
||||
set_scroll_x(0);
|
||||
set_scroll_y(0);
|
||||
emit zoom_changed(zoom);
|
||||
update();
|
||||
}
|
||||
|
||||
void GraphView::set_view_to_selection() {
|
||||
if (selected_keys.size() > 0) {
|
||||
long min_time = LONG_MAX;
|
||||
long max_time = LONG_MIN;
|
||||
double min_dbl = DBL_MAX;
|
||||
double max_dbl = DBL_MIN;
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
const EffectKeyframe& key = row->field(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i));
|
||||
min_time = qMin(key.time, min_time);
|
||||
max_time = qMax(key.time, max_time);
|
||||
min_dbl = qMin(key.data.toDouble(), min_dbl);
|
||||
max_dbl = qMax(key.data.toDouble(), max_dbl);
|
||||
}
|
||||
set_view_to_rect(min_time, min_dbl, max_time, max_dbl);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::set_view_to_all() {
|
||||
bool can_set = false;
|
||||
|
||||
long min_time = LONG_MAX;
|
||||
long max_time = LONG_MIN;
|
||||
double min_dbl = DBL_MAX;
|
||||
double max_dbl = DBL_MIN;
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
for (int j=0;j<row->field(i)->keyframes.size();j++) {
|
||||
const EffectKeyframe& key = row->field(i)->keyframes.at(j);
|
||||
min_time = qMin(key.time, min_time);
|
||||
max_time = qMax(key.time, max_time);
|
||||
min_dbl = qMin(key.data.toDouble(), min_dbl);
|
||||
max_dbl = qMax(key.data.toDouble(), max_dbl);
|
||||
can_set = true;
|
||||
}
|
||||
}
|
||||
if (can_set) {
|
||||
set_view_to_rect(min_time, min_dbl, max_time, max_dbl);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::set_view_to_rect(int x1, double y1, int x2, double y2) {
|
||||
double padding = 1.5;
|
||||
double x_diff = double(x2 - x1);
|
||||
double y_diff = (y2 - y1);
|
||||
double x_diff_padded = (x_diff+10)*padding;
|
||||
double y_diff_padded = (y_diff+10)*padding;
|
||||
set_zoom(qMin(double(width()) / x_diff_padded, double(height()) / y_diff_padded));
|
||||
|
||||
set_scroll_x(qRound((double(x1) - ((x_diff_padded-x_diff)/2))*zoom));
|
||||
set_scroll_y(qRound((double(y1) - ((y_diff_padded-y_diff)/2))*zoom));
|
||||
}
|
||||
|
||||
void GraphView::draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos) {
|
||||
// draws last line's text
|
||||
QString str = QString::number(line_no*GRAPH_SIZE);
|
||||
int text_sz = vert ? fontMetrics().height() : fontMetrics().width(str);
|
||||
if (text_sz < (next_line_pos - line_pos)) {
|
||||
QRect text_rect = vert ? QRect(0, line_pos-50, 50, 50) : QRect(line_pos, height()-50, 50, 50);
|
||||
p.drawText(text_rect, Qt::AlignBottom | Qt::AlignLeft, str);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::draw_lines(QPainter& p, bool vert) {
|
||||
int last_line = INT_MIN;
|
||||
int last_line_x = INT_MIN;
|
||||
int lim = vert ? height() : width();
|
||||
int scroll = vert ? y_scroll : x_scroll;
|
||||
for (int i=0;i<lim;i++) {
|
||||
int scroll_offs = vert ? height() - i + scroll : i + scroll;
|
||||
int this_line = qFloor(scroll_offs/(GRAPH_SIZE*zoom));
|
||||
if (vert) this_line++;
|
||||
if (this_line != last_line) {
|
||||
if (vert) {
|
||||
p.drawLine(0, i, width(), i);
|
||||
} else {
|
||||
p.drawLine(i, 0, i, height());
|
||||
}
|
||||
|
||||
draw_line_text(p, vert, last_line, last_line_x, i);
|
||||
|
||||
last_line_x = i;
|
||||
last_line = this_line;
|
||||
}
|
||||
}
|
||||
draw_line_text(p, vert, last_line, last_line_x, width());
|
||||
}
|
||||
|
||||
QVector<int> sort_keys_from_field(EffectField* field) {
|
||||
QVector<int> sorted_keys;
|
||||
for (int k=0;k<field->keyframes.size();k++) {
|
||||
bool inserted = false;
|
||||
for (int j=0;j<sorted_keys.size();j++) {
|
||||
if (field->keyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) {
|
||||
sorted_keys.insert(j, k);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inserted) {
|
||||
sorted_keys.append(k);
|
||||
}
|
||||
}
|
||||
return sorted_keys;
|
||||
}
|
||||
|
||||
void GraphView::paintEvent(QPaintEvent *) {
|
||||
QPainter p(this);
|
||||
|
||||
if (panel_sequence_viewer->seq != NULL) {
|
||||
// draw grid lines
|
||||
|
||||
p.setPen(Qt::gray);
|
||||
|
||||
draw_lines(p, true);
|
||||
draw_lines(p, false);
|
||||
|
||||
// draw keyframes
|
||||
if (row != NULL) {
|
||||
QPen line_pen;
|
||||
line_pen.setWidth(BEZIER_LINE_SIZE);
|
||||
|
||||
for (int i=row->fieldCount()-1;i>=0;i--) {
|
||||
EffectField* field = row->field(i);
|
||||
|
||||
if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) {
|
||||
// sort keyframes by time
|
||||
QVector<int> sorted_keys = sort_keys_from_field(field);
|
||||
|
||||
int last_key_x, last_key_y;
|
||||
|
||||
// draw lines
|
||||
for (int j=0;j<sorted_keys.size();j++) {
|
||||
const EffectKeyframe& key = field->keyframes.at(sorted_keys.at(j));
|
||||
|
||||
int key_x = get_screen_x(key.time);
|
||||
int key_y = get_screen_y(key.data.toDouble());
|
||||
|
||||
line_pen.setColor(get_curve_color(i, row->fieldCount()));
|
||||
p.setPen(line_pen);
|
||||
if (j == 0) {
|
||||
p.drawLine(0, key_y, key_x, key_y);
|
||||
} else {
|
||||
const EffectKeyframe& last_key = field->keyframes.at(sorted_keys.at(j-1));
|
||||
if (last_key.type == KEYFRAME_TYPE_HOLD) {
|
||||
// hold
|
||||
p.drawLine(last_key_x, last_key_y, key_x, last_key_y);
|
||||
p.drawLine(key_x, last_key_y, key_x, key_y);
|
||||
} else if (last_key.type == KEYFRAME_TYPE_BEZIER || key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
QPainterPath bezier_path;
|
||||
bezier_path.moveTo(last_key_x, last_key_y);
|
||||
if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
// cubic bezier
|
||||
bezier_path.cubicTo(
|
||||
QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom),
|
||||
QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom),
|
||||
QPointF(key_x, key_y)
|
||||
);
|
||||
} else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier
|
||||
// last keyframe is the bezier one
|
||||
bezier_path.quadTo(
|
||||
QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom),
|
||||
QPointF(key_x, key_y)
|
||||
);
|
||||
} else {
|
||||
// this keyframe is the bezier one
|
||||
bezier_path.quadTo(
|
||||
QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom),
|
||||
QPointF(key_x, key_y)
|
||||
);
|
||||
}
|
||||
p.drawPath(bezier_path);
|
||||
} else {
|
||||
// linear
|
||||
p.drawLine(last_key_x, last_key_y, key_x, key_y);
|
||||
}
|
||||
}
|
||||
last_key_x = key_x;
|
||||
last_key_y = key_y;
|
||||
}
|
||||
if (last_key_x < width()) p.drawLine(last_key_x, last_key_y, width(), last_key_y);
|
||||
|
||||
// draw keys
|
||||
for (int j=0;j<sorted_keys.size();j++) {
|
||||
const EffectKeyframe& key = field->keyframes.at(sorted_keys.at(j));
|
||||
|
||||
int key_x = get_screen_x(key.time);
|
||||
int key_y = get_screen_y(key.data.toDouble());
|
||||
|
||||
if (key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
p.setPen(Qt::gray);
|
||||
|
||||
// pre handle line
|
||||
QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom);
|
||||
p.drawLine(pre_point, QPointF(key_x, key_y));
|
||||
p.drawEllipse(pre_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE);
|
||||
|
||||
// post handle line
|
||||
QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom);
|
||||
p.drawLine(post_point, QPointF(key_x, key_y));
|
||||
p.drawEllipse(post_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE);
|
||||
}
|
||||
|
||||
bool selected = false;
|
||||
for (int k=0;k<selected_keys.size();k++) {
|
||||
if (selected_keys.at(k) == sorted_keys.at(j) && selected_keys_fields.at(k) == i) {
|
||||
selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
draw_keyframe(p, key.type, key_x, key_y, selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// draw playhead
|
||||
p.setPen(Qt::red);
|
||||
int playhead_x = get_screen_x(panel_sequence_viewer->seq->playhead - visible_in);
|
||||
p.drawLine(playhead_x, 0, playhead_x, height());
|
||||
|
||||
if (rect_select) {
|
||||
draw_selection_rectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h));
|
||||
p.setBrush(Qt::NoBrush);
|
||||
}
|
||||
}
|
||||
|
||||
p.setPen(Qt::white);
|
||||
|
||||
QRect border = rect();
|
||||
border.setWidth(border.width()-1);
|
||||
border.setHeight(border.height()-1);
|
||||
p.drawRect(border);
|
||||
}
|
||||
|
||||
void GraphView::mousePressEvent(QMouseEvent *event) {
|
||||
if (row != NULL) {
|
||||
mousedown = true;
|
||||
start_x = event->pos().x();
|
||||
start_y = event->pos().y();
|
||||
|
||||
// selecting
|
||||
int sel_key = -1;
|
||||
int sel_key_field = -1;
|
||||
current_handle = BEZIER_HANDLE_NONE;
|
||||
|
||||
if (click_add && (event->buttons() & Qt::LeftButton)) {
|
||||
selected_keys.clear();
|
||||
selected_keys_fields.clear();
|
||||
|
||||
EffectKeyframe key;
|
||||
key.time = get_value_x(event->pos().x());
|
||||
key.data = get_value_y(event->pos().y());
|
||||
key.type = click_add_type;
|
||||
click_add_key = click_add_field->keyframes.size();
|
||||
click_add_field->keyframes.append(key);
|
||||
update_ui(false);
|
||||
click_add_proc = true;
|
||||
} else {
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
EffectField* field = row->field(i);
|
||||
if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) {
|
||||
for (int j=0;j<field->keyframes.size();j++) {
|
||||
const EffectKeyframe& key = field->keyframes.at(j);
|
||||
int key_x = get_screen_x(key.time);
|
||||
int key_y = get_screen_y(key.data.toDouble());
|
||||
if (event->pos().x() > key_x-KEYFRAME_SIZE
|
||||
&& event->pos().x() < key_x+KEYFRAME_SIZE
|
||||
&& event->pos().y() > key_y-KEYFRAME_SIZE
|
||||
&& event->pos().y() < key_y+KEYFRAME_SIZE) {
|
||||
sel_key = j;
|
||||
sel_key_field = i;
|
||||
break;
|
||||
} else {
|
||||
// selecting a handle
|
||||
QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom);
|
||||
QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom);
|
||||
if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE
|
||||
&& event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE
|
||||
&& event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE
|
||||
&& event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) {
|
||||
current_handle = BEZIER_HANDLE_PRE;
|
||||
} else if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE
|
||||
&& event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE
|
||||
&& event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE
|
||||
&& event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) {
|
||||
current_handle = BEZIER_HANDLE_POST;
|
||||
}
|
||||
|
||||
if (current_handle != BEZIER_HANDLE_NONE) {
|
||||
sel_key = j;
|
||||
sel_key_field = i;
|
||||
handle_index = j;
|
||||
handle_field = i;
|
||||
old_pre_handle_x = key.pre_handle_x;
|
||||
old_pre_handle_y = key.pre_handle_y;
|
||||
old_post_handle_x = key.post_handle_x;
|
||||
old_post_handle_y = key.post_handle_y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sel_key > -1) break;
|
||||
}
|
||||
|
||||
bool already_selected = false;
|
||||
if (sel_key > -1) {
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
if (selected_keys.at(i) == sel_key && selected_keys_fields.at(i) == sel_key_field) {
|
||||
if ((event->modifiers() & Qt::ShiftModifier) && current_handle == BEZIER_HANDLE_NONE) {
|
||||
selected_keys.removeAt(i);
|
||||
selected_keys_fields.removeAt(i);
|
||||
}
|
||||
already_selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!already_selected) {
|
||||
if (!(event->modifiers() & Qt::ShiftModifier)) {
|
||||
selected_keys.clear();
|
||||
selected_keys_fields.clear();
|
||||
}
|
||||
if (sel_key > -1) {
|
||||
selected_keys.append(sel_key);
|
||||
selected_keys_fields.append(sel_key_field);
|
||||
} else {
|
||||
rect_select = true;
|
||||
rect_select_x = event->pos().x();
|
||||
rect_select_y = event->pos().y();
|
||||
rect_select_w = 0;
|
||||
rect_select_h = 0;
|
||||
rect_select_offset = selected_keys.size();
|
||||
}
|
||||
}
|
||||
|
||||
selection_update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::mouseMoveEvent(QMouseEvent *event) {
|
||||
if (!mousedown || !click_add) unsetCursor();
|
||||
if (mousedown) {
|
||||
if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) {
|
||||
set_scroll_x(x_scroll + start_x - event->pos().x());
|
||||
set_scroll_y(y_scroll + event->pos().y() - start_y);
|
||||
start_x = event->pos().x();
|
||||
start_y = event->pos().y();
|
||||
update();
|
||||
} else if (click_add_proc) {
|
||||
click_add_field->keyframes[click_add_key].time = get_value_x(event->pos().x());
|
||||
click_add_field->keyframes[click_add_key].data = get_value_y(event->pos().y());
|
||||
update_ui(false);
|
||||
} else if (rect_select) {
|
||||
rect_select_w = event->pos().x() - rect_select_x;
|
||||
rect_select_h = event->pos().y() - rect_select_y;
|
||||
|
||||
selected_keys.resize(rect_select_offset);
|
||||
selected_keys_fields.resize(rect_select_offset);
|
||||
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
EffectField* f = row->field(i);
|
||||
for (int j=0;j<f->keyframes.size();j++) {
|
||||
bool already_selected = false;
|
||||
for (int k=0;k<selected_keys.size();k++) {
|
||||
if (selected_keys.at(k) == j && selected_keys_fields.at(k) == i) {
|
||||
already_selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!already_selected) {
|
||||
QPoint key_screen_point(get_screen_x(f->keyframes.at(j).time), get_screen_y(f->keyframes.at(j).data.toDouble()));
|
||||
QRect select_rect(rect_select_x, rect_select_y, rect_select_w, rect_select_h);
|
||||
if (select_rect.contains(key_screen_point)) {
|
||||
selected_keys.append(j);
|
||||
selected_keys_fields.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
update();
|
||||
} else {
|
||||
switch (current_handle) {
|
||||
case BEZIER_HANDLE_NONE:
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].time = qRound(selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom));
|
||||
if (event->modifiers() & Qt::ShiftModifier) {
|
||||
row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(i);
|
||||
} else {
|
||||
row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom));
|
||||
}
|
||||
}
|
||||
moved_keys = true;
|
||||
update_ui(false);
|
||||
break;
|
||||
case BEZIER_HANDLE_PRE:
|
||||
case BEZIER_HANDLE_POST:
|
||||
{
|
||||
double new_pre_handle_x = old_pre_handle_x;
|
||||
double new_pre_handle_y = old_pre_handle_y;
|
||||
double new_post_handle_x = old_post_handle_x;
|
||||
double new_post_handle_y = old_post_handle_y;
|
||||
|
||||
double x_diff = double(event->pos().x() - start_x)/zoom;
|
||||
double y_diff = double(start_y - event->pos().y())/zoom;
|
||||
|
||||
if (current_handle == BEZIER_HANDLE_PRE) {
|
||||
new_pre_handle_x += x_diff;
|
||||
if (!(event->modifiers() & Qt::ShiftModifier)) new_pre_handle_y += y_diff;
|
||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||
new_post_handle_x = -new_pre_handle_x;
|
||||
new_post_handle_y = -new_pre_handle_y;
|
||||
}
|
||||
} else {
|
||||
new_post_handle_x += x_diff;
|
||||
if (!(event->modifiers() & Qt::ShiftModifier)) new_post_handle_y += y_diff;
|
||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||
new_pre_handle_x = -new_post_handle_x;
|
||||
new_pre_handle_y = -new_post_handle_y;
|
||||
}
|
||||
}
|
||||
|
||||
EffectKeyframe& key = row->field(handle_field)->keyframes[handle_index];
|
||||
key.pre_handle_x = new_pre_handle_x;
|
||||
key.pre_handle_y = new_pre_handle_y;
|
||||
key.post_handle_x = new_post_handle_x;
|
||||
key.post_handle_y = new_post_handle_y;
|
||||
|
||||
moved_keys = true;
|
||||
update_ui(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (row != NULL) {
|
||||
// clicking on the curve
|
||||
click_add = false;
|
||||
|
||||
bool hovering_key = false;
|
||||
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
for (int j=0;j<row->field(i)->keyframes.size();j++) {
|
||||
const EffectKeyframe& key = row->field(i)->keyframes.at(j);
|
||||
int key_x = get_screen_x(key.time);
|
||||
int key_y = get_screen_y(key.data.toDouble());
|
||||
QRect test_rect(
|
||||
key_x - KEYFRAME_SIZE,
|
||||
key_y - KEYFRAME_SIZE,
|
||||
KEYFRAME_SIZE+KEYFRAME_SIZE,
|
||||
KEYFRAME_SIZE+KEYFRAME_SIZE
|
||||
);
|
||||
QRect pre_rect(
|
||||
qRound(key_x + key.pre_handle_x*zoom - BEZIER_HANDLE_SIZE),
|
||||
qRound(key_y + key.pre_handle_y*zoom - BEZIER_HANDLE_SIZE),
|
||||
BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE,
|
||||
BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE
|
||||
);
|
||||
QRect post_rect(
|
||||
qRound(key_x + key.post_handle_x*zoom - BEZIER_HANDLE_SIZE),
|
||||
qRound(key_y + key.post_handle_y*zoom - BEZIER_HANDLE_SIZE),
|
||||
BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE,
|
||||
BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE
|
||||
);
|
||||
|
||||
if (test_rect.contains(event->pos())
|
||||
|| pre_rect.contains(event->pos())
|
||||
|| post_rect.contains(event->pos())) {
|
||||
hovering_key = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hovering_key) {
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
EffectField* f = row->field(i);
|
||||
if (field_visibility.at(i)) {
|
||||
QVector<int> sorted_keys = sort_keys_from_field(f);
|
||||
|
||||
if (event->pos().x() <= get_screen_x(f->keyframes.at(sorted_keys.first()).time)) {
|
||||
int y_comp = get_screen_y(f->keyframes.at(sorted_keys.first()).data.toDouble());
|
||||
if (event->pos().y() >= y_comp-BEZIER_LINE_SIZE
|
||||
&& event->pos().y() <= y_comp+BEZIER_LINE_SIZE) {
|
||||
// dout << "make an EARLY key on field" << i;
|
||||
click_add = true;
|
||||
click_add_type = f->keyframes.at(sorted_keys.first()).type;
|
||||
}
|
||||
} else if (event->pos().x() >= get_screen_x(f->keyframes.at(sorted_keys.last()).time)) {
|
||||
int y_comp = get_screen_y(f->keyframes.at(sorted_keys.last()).data.toDouble());
|
||||
if (event->pos().y() >= y_comp-BEZIER_LINE_SIZE
|
||||
&& event->pos().y() <= y_comp+BEZIER_LINE_SIZE) {
|
||||
// dout << "make an LATE key on field" << i;
|
||||
click_add = true;
|
||||
click_add_type = f->keyframes.at(sorted_keys.last()).type;
|
||||
}
|
||||
} else {
|
||||
for (int j=1;j<sorted_keys.size();j++) {
|
||||
const EffectKeyframe& last_key = f->keyframes.at(sorted_keys.at(j-1));
|
||||
const EffectKeyframe& key = f->keyframes.at(sorted_keys.at(j));
|
||||
|
||||
int last_key_x = get_screen_x(last_key.time);
|
||||
int key_x = get_screen_x(key.time);
|
||||
int last_key_y = get_screen_y(last_key.data.toDouble());
|
||||
int key_y = get_screen_y(key.data.toDouble());
|
||||
|
||||
click_add_type = last_key.type;
|
||||
|
||||
if (event->pos().x() >= last_key_x
|
||||
&& event->pos().x() <= key_x) {
|
||||
QRect mouse_rect(event->pos().x()-BEZIER_LINE_SIZE, event->pos().y()-BEZIER_LINE_SIZE, BEZIER_LINE_SIZE+BEZIER_LINE_SIZE, BEZIER_LINE_SIZE+BEZIER_LINE_SIZE);
|
||||
// NOTE: FILTHY copy/paste from paintEvent
|
||||
if (last_key.type == KEYFRAME_TYPE_HOLD) {
|
||||
// hold
|
||||
if (event->pos().y() >= last_key_y-BEZIER_LINE_SIZE
|
||||
&& event->pos().y() <= last_key_y+BEZIER_LINE_SIZE) {
|
||||
// dout << "make an HOLD key on field" << i << "after key" << j;
|
||||
click_add = true;
|
||||
}
|
||||
} else if (last_key.type == KEYFRAME_TYPE_BEZIER || key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
QPainterPath bezier_path;
|
||||
bezier_path.moveTo(last_key_x, last_key_y);
|
||||
if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
// cubic bezier
|
||||
bezier_path.cubicTo(
|
||||
QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom),
|
||||
QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom),
|
||||
QPointF(key_x, key_y)
|
||||
);
|
||||
} else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier
|
||||
// last keyframe is the bezier one
|
||||
bezier_path.quadTo(
|
||||
QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom),
|
||||
QPointF(key_x, key_y)
|
||||
);
|
||||
} else {
|
||||
// this keyframe is the bezier one
|
||||
bezier_path.quadTo(
|
||||
QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom),
|
||||
QPointF(key_x, key_y)
|
||||
);
|
||||
}
|
||||
if (bezier_path.intersects(mouse_rect)) {
|
||||
// dout << "make an BEZIER key on field" << i << "after key" << j;
|
||||
click_add = true;
|
||||
}
|
||||
} else {
|
||||
// linear
|
||||
QPainterPath linear_path;
|
||||
linear_path.moveTo(last_key_x, last_key_y);
|
||||
linear_path.lineTo(key_x, key_y);
|
||||
if (linear_path.intersects(mouse_rect)) {
|
||||
// dout << "make an LINEAR key on field" << i << "after key" << j;
|
||||
click_add = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (click_add) {
|
||||
click_add_field = f;
|
||||
setCursor(Qt::CrossCursor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (click_add_proc) {
|
||||
undo_stack.push(new KeyframeFieldSet(click_add_field, click_add_key));
|
||||
} else if (moved_keys && selected_keys.size() > 0) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
switch (current_handle) {
|
||||
case BEZIER_HANDLE_NONE:
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
EffectKeyframe& key = row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)];
|
||||
ca->append(new SetLong(&key.time, selected_keys_old_vals.at(i), key.time));
|
||||
ca->append(new SetQVariant(&key.data, selected_keys_old_doubles.at(i), key.data));
|
||||
}
|
||||
break;
|
||||
case BEZIER_HANDLE_PRE:
|
||||
case BEZIER_HANDLE_POST:
|
||||
{
|
||||
EffectKeyframe& key = row->field(handle_field)->keyframes[handle_index];
|
||||
ca->append(new SetDouble(&key.pre_handle_x, old_pre_handle_x, key.pre_handle_x));
|
||||
ca->append(new SetDouble(&key.pre_handle_y, old_pre_handle_y, key.pre_handle_y));
|
||||
ca->append(new SetDouble(&key.post_handle_x, old_post_handle_x, key.post_handle_x));
|
||||
ca->append(new SetDouble(&key.post_handle_y, old_post_handle_y, key.post_handle_y));
|
||||
}
|
||||
break;
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
}
|
||||
moved_keys = false;
|
||||
mousedown = false;
|
||||
click_add = false;
|
||||
click_add_proc = false;
|
||||
if (rect_select) {
|
||||
rect_select = false;
|
||||
selection_update();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::wheelEvent(QWheelEvent *event) {
|
||||
dout << x_scroll << y_scroll;
|
||||
|
||||
bool redraw = false;
|
||||
bool shift = (event->modifiers() & Qt::ShiftModifier); // scroll instead of zoom
|
||||
|
||||
if (shift) {
|
||||
// scroll
|
||||
set_scroll_x(x_scroll + event->angleDelta().x()/10);
|
||||
set_scroll_y(y_scroll + event->angleDelta().y()/10);
|
||||
redraw = true;
|
||||
} else {
|
||||
// set zoom
|
||||
if (event->angleDelta().y() != 0) {
|
||||
double zoom_diff = (GRAPH_ZOOM_SPEED*zoom);
|
||||
double new_zoom = (event->angleDelta().y() < 0) ? zoom - zoom_diff : zoom + zoom_diff;
|
||||
|
||||
// center zoom on screen
|
||||
set_scroll_x(qRound((double(x_scroll)/zoom*new_zoom) + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom));
|
||||
set_scroll_y(qRound((double(y_scroll)/zoom*new_zoom) + double(height()-event->pos().y())*new_zoom - double(height()-event->pos().y())*zoom));
|
||||
|
||||
set_zoom(new_zoom);
|
||||
|
||||
redraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (redraw) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::set_row(EffectRow *r) {
|
||||
if (row != r) {
|
||||
selected_keys.clear();
|
||||
selected_keys_fields.clear();
|
||||
selected_keys_old_vals.clear();
|
||||
selected_keys_old_doubles.clear();
|
||||
emit selection_changed(false, -1);
|
||||
row = r;
|
||||
if (row != NULL) {
|
||||
field_visibility.resize(row->fieldCount());
|
||||
field_visibility.fill(true);
|
||||
visible_in = row->parent_effect->parent_clip->timeline_in;
|
||||
set_view_to_all();
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::set_selected_keyframe_type(int type) {
|
||||
if (selected_keys.size() > 0) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
EffectKeyframe& key = row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)];
|
||||
ca->append(new SetInt(&key.type, type));
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
update_ui(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::set_field_visibility(int field, bool b) {
|
||||
field_visibility[field] = b;
|
||||
update();
|
||||
}
|
||||
|
||||
void GraphView::delete_selected_keys() {
|
||||
if (row != NULL) {
|
||||
QVector<EffectField*> fields;
|
||||
for (int i=0;i<selected_keys_fields.size();i++) {
|
||||
fields.append(row->field(selected_keys_fields.at(i)));
|
||||
}
|
||||
delete_keyframes(fields, selected_keys);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::select_all() {
|
||||
if (row != NULL) {
|
||||
selected_keys.clear();
|
||||
selected_keys_fields.clear();
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
EffectField* field = row->field(i);
|
||||
for (int j=0;j<field->keyframes.size();j++) {
|
||||
selected_keys.append(j);
|
||||
selected_keys_fields.append(i);
|
||||
}
|
||||
}
|
||||
selection_update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphView::set_scroll_x(int s) {
|
||||
x_scroll = s;
|
||||
emit x_scroll_changed(x_scroll);
|
||||
}
|
||||
|
||||
void GraphView::set_scroll_y(int s) {
|
||||
y_scroll = s;
|
||||
emit y_scroll_changed(y_scroll);
|
||||
}
|
||||
|
||||
void GraphView::set_zoom(double z) {
|
||||
zoom = z;
|
||||
emit zoom_changed(zoom);
|
||||
}
|
||||
|
||||
int GraphView::get_screen_x(double d) {
|
||||
return qRound((d*zoom) - x_scroll);
|
||||
}
|
||||
|
||||
int GraphView::get_screen_y(double d) {
|
||||
return qRound(height() + y_scroll - d*zoom);
|
||||
}
|
||||
|
||||
long GraphView::get_value_x(int i) {
|
||||
return qRound((i + x_scroll)/zoom);
|
||||
}
|
||||
|
||||
double GraphView::get_value_y(int i) {
|
||||
return double(height() + y_scroll - i)/zoom;
|
||||
}
|
||||
|
||||
void GraphView::selection_update() {
|
||||
selected_keys_old_vals.clear();
|
||||
selected_keys_old_doubles.clear();
|
||||
|
||||
int selected_key_type = -1;
|
||||
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
const EffectKeyframe& key = row->field(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i));
|
||||
selected_keys_old_vals.append(key.time);
|
||||
selected_keys_old_doubles.append(key.data.toDouble());
|
||||
|
||||
if (selected_key_type == -1) {
|
||||
selected_key_type = key.type;
|
||||
} else if (selected_key_type != key.type) {
|
||||
selected_key_type = -2;
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
emit selection_changed(selected_keys.size() > 0, selected_key_type);
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#ifndef GRAPHVIEW_H
|
||||
#define GRAPHVIEW_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
|
||||
class EffectRow;
|
||||
class EffectField;
|
||||
|
||||
QColor get_curve_color(int index, int length);
|
||||
|
||||
class GraphView : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GraphView(QWidget* parent = 0);
|
||||
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void mouseMoveEvent(QMouseEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
void wheelEvent(QWheelEvent *event);
|
||||
|
||||
void set_row(EffectRow* r);
|
||||
|
||||
void set_selected_keyframe_type(int type);
|
||||
void set_field_visibility(int field, bool b);
|
||||
|
||||
void delete_selected_keys();
|
||||
void select_all();
|
||||
signals:
|
||||
void x_scroll_changed(int);
|
||||
void y_scroll_changed(int);
|
||||
void zoom_changed(double);
|
||||
void selection_changed(bool, int);
|
||||
private:
|
||||
int x_scroll;
|
||||
int y_scroll;
|
||||
bool mousedown;
|
||||
int start_x;
|
||||
int start_y;
|
||||
double zoom;
|
||||
|
||||
void set_scroll_x(int s);
|
||||
void set_scroll_y(int s);
|
||||
void set_zoom(double z);
|
||||
|
||||
int get_screen_x(double);
|
||||
int get_screen_y(double);
|
||||
long get_value_x(int);
|
||||
double get_value_y(int);
|
||||
|
||||
void selection_update();
|
||||
|
||||
QVector<bool> field_visibility;
|
||||
|
||||
QVector<int> selected_keys;
|
||||
QVector<int> selected_keys_fields;
|
||||
QVector<long> selected_keys_old_vals;
|
||||
QVector<double> selected_keys_old_doubles;
|
||||
|
||||
double old_pre_handle_x;
|
||||
double old_pre_handle_y;
|
||||
double old_post_handle_x;
|
||||
double old_post_handle_y;
|
||||
|
||||
int handle_field;
|
||||
int handle_index;
|
||||
|
||||
bool moved_keys;
|
||||
|
||||
int current_handle;
|
||||
|
||||
void draw_lines(QPainter &p, bool vert);
|
||||
void draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos);
|
||||
|
||||
EffectRow* row;
|
||||
|
||||
bool rect_select;
|
||||
int rect_select_x;
|
||||
int rect_select_y;
|
||||
int rect_select_w;
|
||||
int rect_select_h;
|
||||
int rect_select_offset;
|
||||
|
||||
long visible_in;
|
||||
|
||||
bool click_add;
|
||||
bool click_add_proc;
|
||||
EffectField* click_add_field;
|
||||
int click_add_key;
|
||||
int click_add_type;
|
||||
private slots:
|
||||
void show_context_menu(const QPoint& pos);
|
||||
void reset_view();
|
||||
void set_view_to_selection();
|
||||
void set_view_to_all();
|
||||
void set_view_to_rect(int x1, double y1, int x2, double y2);
|
||||
};
|
||||
|
||||
#endif // GRAPHVIEW_H
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "keyframedrawing.h"
|
||||
|
||||
#include "project/effect.h"
|
||||
|
||||
#define KEYFRAME_POINT_COUNT 4
|
||||
|
||||
void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r, int g, int b) {
|
||||
if (darker) {
|
||||
r *= 0.625;
|
||||
g *= 0.625;
|
||||
b *= 0.625;
|
||||
}
|
||||
p.setPen(QColor(0, 0, 0));
|
||||
p.setBrush(QColor(r, g, b));
|
||||
|
||||
switch (type) {
|
||||
case KEYFRAME_TYPE_LINEAR:
|
||||
{
|
||||
QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)};
|
||||
p.drawPolygon(points, KEYFRAME_POINT_COUNT);
|
||||
}
|
||||
break;
|
||||
case KEYFRAME_TYPE_BEZIER:
|
||||
p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE);
|
||||
break;
|
||||
case KEYFRAME_TYPE_HOLD:
|
||||
p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2));
|
||||
break;
|
||||
}
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef KEYFRAMEDRAWING_H
|
||||
#define KEYFRAMEDRAWING_H
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#define KEYFRAME_SIZE 6
|
||||
#define KEYFRAME_COLOR 160
|
||||
|
||||
void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r = KEYFRAME_COLOR, int g = KEYFRAME_COLOR, int b = KEYFRAME_COLOR);
|
||||
|
||||
#endif // KEYFRAMEDRAWING_H
|
||||
@@ -2,15 +2,22 @@
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
QSize icon_size(12, 12);
|
||||
QSize button_size(20, 20);
|
||||
int icon_size_val = 8*QApplication::desktop()->devicePixelRatio();
|
||||
int clock_size_val = 12*QApplication::desktop()->devicePixelRatio();
|
||||
int button_size_val = 20*QApplication::desktop()->devicePixelRatio();
|
||||
|
||||
QSize icon_size(icon_size_val, icon_size_val);
|
||||
QSize clock_size(clock_size_val, clock_size_val);
|
||||
QSize button_size(button_size_val, button_size_val);
|
||||
|
||||
key_controls = new QHBoxLayout();
|
||||
key_controls->setSpacing(0);
|
||||
key_controls->setMargin(0);
|
||||
key_controls->addStretch();
|
||||
//key_controls->addStretch();
|
||||
|
||||
setLayout(key_controls);
|
||||
|
||||
@@ -21,6 +28,7 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
left_key_nav->setVisible(false);
|
||||
key_controls->addWidget(left_key_nav);
|
||||
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key()));
|
||||
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
key_addremove = new QPushButton();
|
||||
key_addremove->setIcon(QIcon(":/icons/diamond.png"));
|
||||
@@ -29,6 +37,7 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
key_addremove->setVisible(false);
|
||||
key_controls->addWidget(key_addremove);
|
||||
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key()));
|
||||
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
right_key_nav = new QPushButton();
|
||||
right_key_nav->setIcon(QIcon(":/icons/tri-right.png"));
|
||||
@@ -37,20 +46,26 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
right_key_nav->setVisible(false);
|
||||
key_controls->addWidget(right_key_nav);
|
||||
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key()));
|
||||
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), "");
|
||||
keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
|
||||
keyframe_enable->setMaximumSize(button_size);
|
||||
keyframe_enable->setIconSize(icon_size);
|
||||
keyframe_enable->setIconSize(clock_size);
|
||||
keyframe_enable->setCheckable(true);
|
||||
keyframe_enable->setToolTip("Enable Keyframes");
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(set_keyframe_enabled(bool)));
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(keyframe_enabled_changed(bool)));
|
||||
connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool)));
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
key_controls->addWidget(keyframe_enable);
|
||||
}
|
||||
|
||||
void KeyframeNavigator::enable_keyframes(bool b) {
|
||||
keyframe_enable->setChecked(b);
|
||||
keyframe_enable->setChecked(b);
|
||||
}
|
||||
|
||||
void KeyframeNavigator::enable_keyframe_toggle(bool b) {
|
||||
keyframe_enable->setVisible(b);
|
||||
}
|
||||
|
||||
void KeyframeNavigator::keyframe_ui_enabled(bool enabled) {
|
||||
|
||||
@@ -12,11 +12,13 @@ class KeyframeNavigator : public QWidget
|
||||
public:
|
||||
KeyframeNavigator(QWidget* parent = 0);
|
||||
void enable_keyframes(bool);
|
||||
void enable_keyframe_toggle(bool);
|
||||
signals:
|
||||
void goto_previous_key();
|
||||
void toggle_key();
|
||||
void goto_next_key();
|
||||
void set_keyframe_enabled(bool);
|
||||
void keyframe_enabled_changed(bool);
|
||||
void clicked();
|
||||
private slots:
|
||||
void keyframe_ui_enabled(bool);
|
||||
private:
|
||||
|
||||
+186
-125
@@ -11,16 +11,18 @@
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "project/sequence.h"
|
||||
#include "panels/grapheditor.h"
|
||||
#include "ui/keyframedrawing.h"
|
||||
#include "ui/clickablelabel.h"
|
||||
#include "ui/resizablescrollbar.h"
|
||||
#include "ui/rectangleselect.h"
|
||||
#include "project/keyframe.h"
|
||||
#include "ui/graphview.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QtMath>
|
||||
#include <QMenu>
|
||||
|
||||
#define KEYFRAME_SIZE 6
|
||||
#define KEYFRAME_POINT_COUNT 4
|
||||
|
||||
long KeyframeView::adjust_row_keyframe(EffectRow* row, long time) {
|
||||
return time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in);
|
||||
}
|
||||
@@ -45,26 +47,36 @@ KeyframeView::KeyframeView(QWidget *parent) :
|
||||
}
|
||||
|
||||
void KeyframeView::show_context_menu(const QPoint& pos) {
|
||||
if (selected_rows.size() > 0) {
|
||||
if (selected_fields.size() > 0) {
|
||||
QMenu menu(this);
|
||||
|
||||
QAction* linear = menu.addAction("Linear");
|
||||
linear->setData(KEYFRAME_TYPE_LINEAR);
|
||||
QAction* smooth = menu.addAction("Smooth");
|
||||
smooth->setData(KEYFRAME_TYPE_SMOOTH);
|
||||
/*QAction* bezier = menu.addAction("Bezier");
|
||||
bezier->setData(KEYFRAME_TYPE_BEZIER);*/
|
||||
QAction* bezier = menu.addAction("Bezier");
|
||||
bezier->setData(KEYFRAME_TYPE_BEZIER);
|
||||
QAction* hold = menu.addAction("Hold");
|
||||
hold->setData(KEYFRAME_TYPE_HOLD);
|
||||
menu.addSeparator();
|
||||
menu.addAction("Graph Editor");
|
||||
|
||||
connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_set_key_type(QAction*)));
|
||||
menu.exec(mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::menu_set_key_type(QAction* a) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<selected_rows.size();i++) {
|
||||
ca->append(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt()));
|
||||
if (a->data().isNull()) {
|
||||
// load graph editor
|
||||
panel_graph_editor->show();
|
||||
} else {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<selected_fields.size();i++) {
|
||||
EffectField* f = selected_fields.at(i);
|
||||
ca->append(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt()));
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
update_ui(false);
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
update();
|
||||
}
|
||||
|
||||
void KeyframeView::paintEvent(QPaintEvent*) {
|
||||
@@ -91,15 +103,39 @@ void KeyframeView::paintEvent(QPaintEvent*) {
|
||||
for (int j=0;j<e->row_count();j++) {
|
||||
EffectRow* row = e->row(j);
|
||||
|
||||
QLabel* label = row->label;
|
||||
ClickableLabel* label = row->label;
|
||||
QWidget* contents = e->container->contents;
|
||||
|
||||
QVector<long> key_times;
|
||||
int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height()/* - y_scroll*/;
|
||||
for (int k=0;k<row->keyframe_times.size();k++) {
|
||||
bool keyframe_selected = keyframeIsSelected(row, k);
|
||||
long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(k));
|
||||
if (dragging && keyframe_selected) keyframe_frame += frame_diff;
|
||||
draw_keyframe(p, row->keyframe_types.at(k), getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected);
|
||||
for (int l=0;l<row->fieldCount();l++) {
|
||||
EffectField* f = row->field(l);
|
||||
for (int k=0;k<f->keyframes.size();k++) {
|
||||
if (!key_times.contains(f->keyframes.at(k).time)) {
|
||||
bool keyframe_selected = keyframeIsSelected(f, k);
|
||||
long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time);
|
||||
|
||||
// see if any other keyframes have this time
|
||||
int appearances = 0;
|
||||
for (int m=0;m<row->fieldCount();m++) {
|
||||
EffectField* compf = row->field(m);
|
||||
for (int n=0;n<compf->keyframes.size();n++) {
|
||||
if (f->keyframes.at(k).time == compf->keyframes.at(n).time) {
|
||||
appearances++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (appearances != row->fieldCount()) {
|
||||
QColor cc = get_curve_color(l, row->fieldCount());
|
||||
draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected, cc.red(), cc.green(), cc.blue());
|
||||
} else {
|
||||
draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected);
|
||||
}
|
||||
|
||||
key_times.append(f->keyframes.at(k).time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rows.append(row);
|
||||
@@ -134,60 +170,32 @@ void KeyframeView::paintEvent(QPaintEvent*) {
|
||||
}*/
|
||||
}
|
||||
|
||||
bool KeyframeView::keyframeIsSelected(EffectRow *row, int keyframe) {
|
||||
for (int i=0;i<selected_rows.size();i++) {
|
||||
if (selected_rows.at(i) == row && selected_keyframes.at(i) == keyframe) {
|
||||
bool KeyframeView::keyframeIsSelected(EffectField *field, int keyframe) {
|
||||
for (int i=0;i<selected_fields.size();i++) {
|
||||
if (selected_fields.at(i) == field && selected_keyframes.at(i) == keyframe) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyframeView::delete_selected_keyframes() {
|
||||
KeyframeDelete* kd = new KeyframeDelete();
|
||||
bool del = false;
|
||||
for (int i=0;i<selected_keyframes.size();i++) {
|
||||
selected_rows.at(i)->delete_keyframe(kd, selected_keyframes.at(i));
|
||||
del = true;
|
||||
}
|
||||
if (del) {
|
||||
undo_stack.push(kd);
|
||||
void KeyframeView::update_keys() {
|
||||
// panel_graph_editor->update_panel();
|
||||
update();
|
||||
}
|
||||
|
||||
selected_keyframes.clear();
|
||||
selected_rows.clear();
|
||||
update();
|
||||
panel_sequence_viewer->viewer_widget->update();
|
||||
} else {
|
||||
delete kd;
|
||||
}
|
||||
void KeyframeView::delete_selected_keyframes() {
|
||||
delete_keyframes(selected_fields, selected_keyframes);
|
||||
}
|
||||
|
||||
void KeyframeView::set_x_scroll(int s) {
|
||||
x_scroll = s;
|
||||
update();
|
||||
update_keys();
|
||||
}
|
||||
|
||||
void KeyframeView::set_y_scroll(int s) {
|
||||
y_scroll = s;
|
||||
update();
|
||||
}
|
||||
|
||||
void KeyframeView::draw_keyframe(QPainter &p, int type, int x, int y, bool darker) {
|
||||
int color = (darker) ? 100 : 160;
|
||||
p.setPen(QColor(0, 0, 0));
|
||||
p.setBrush(QColor(color, color, color));
|
||||
|
||||
switch (type) {
|
||||
case KEYFRAME_TYPE_LINEAR:
|
||||
{
|
||||
QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)};
|
||||
p.drawPolygon(points, KEYFRAME_POINT_COUNT);
|
||||
}
|
||||
break;
|
||||
case KEYFRAME_TYPE_SMOOTH:
|
||||
p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE);
|
||||
break;
|
||||
}
|
||||
update_keys();
|
||||
}
|
||||
|
||||
void KeyframeView::resize_move(double d) {
|
||||
@@ -197,15 +205,20 @@ void KeyframeView::resize_move(double d) {
|
||||
void KeyframeView::mousePressEvent(QMouseEvent *event) {
|
||||
rect_select_x = event->x();
|
||||
rect_select_y = event->y();
|
||||
rect_select_w = 0;
|
||||
rect_select_h = 0;
|
||||
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_HAND || event->buttons() & Qt::MiddleButton) {
|
||||
scroll_drag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
old_key_vals.clear();
|
||||
|
||||
int mouse_x = event->x() + x_scroll;
|
||||
int mouse_y = event->y();
|
||||
int row_index = -1;
|
||||
int field_index = -1;
|
||||
int keyframe_index = -1;
|
||||
long frame_diff = 0;
|
||||
long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x-KEYFRAME_SIZE);
|
||||
@@ -214,14 +227,21 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) {
|
||||
for (int i=0;i<rowY.size();i++) {
|
||||
if (mouse_y > rowY.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && mouse_y < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) {
|
||||
EffectRow* row = rows.at(i);
|
||||
for (int j=0;j<row->keyframe_times.size();j++) {
|
||||
long eval_keyframe_time = row->keyframe_times.at(j)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in);
|
||||
if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) {
|
||||
long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start);
|
||||
if (keyframe_index == -1 || eval_frame_diff < frame_diff) {
|
||||
row_index = i;
|
||||
keyframe_index = j;
|
||||
frame_diff = eval_frame_diff;
|
||||
|
||||
row->focus_row();
|
||||
|
||||
for (int k=0;k<row->fieldCount();k++) {
|
||||
EffectField* f = row->field(k);
|
||||
for (int j=0;j<f->keyframes.size();j++) {
|
||||
long eval_keyframe_time = f->keyframes.at(j).time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in);
|
||||
if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) {
|
||||
long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start);
|
||||
if (keyframe_index == -1 || eval_frame_diff < frame_diff) {
|
||||
row_index = i;
|
||||
field_index = k;
|
||||
keyframe_index = j;
|
||||
frame_diff = eval_frame_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,23 +250,46 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) {
|
||||
}
|
||||
bool already_selected = false;
|
||||
keys_selected = false;
|
||||
if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index), keyframe_index);
|
||||
if (keyframe_index > -1) {
|
||||
already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index);
|
||||
} else {
|
||||
select_rect = true;
|
||||
}
|
||||
if (!already_selected) {
|
||||
if (!(event->modifiers() & Qt::ShiftModifier)) {
|
||||
selected_rows.clear();
|
||||
selected_fields.clear();
|
||||
selected_keyframes.clear();
|
||||
}
|
||||
if (keyframe_index > -1) {
|
||||
selected_rows.append(rows.at(row_index));
|
||||
selected_fields.append(rows.at(row_index)->field(field_index));
|
||||
selected_keyframes.append(keyframe_index);
|
||||
|
||||
// find other field with keyframes at the same time
|
||||
long comp_time = rows.at(row_index)->field(field_index)->keyframes.at(keyframe_index).time;
|
||||
for (int i=0;i<rows.at(row_index)->fieldCount();i++) {
|
||||
if (i != field_index) {
|
||||
EffectField* f = rows.at(row_index)->field(i);
|
||||
for (int j=0;j<f->keyframes.size();j++) {
|
||||
if (f->keyframes.at(j).time == comp_time) {
|
||||
selected_fields.append(f);
|
||||
selected_keyframes.append(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_rows.size() > 0) {
|
||||
if (selected_fields.size() > 0) {
|
||||
for (int i=0;i<selected_fields.size();i++) {
|
||||
old_key_vals.append(selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time);
|
||||
}
|
||||
keys_selected = true;
|
||||
}
|
||||
|
||||
update();
|
||||
rect_select_offset = selected_fields.size();
|
||||
|
||||
update_keys();
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
mousedown = true;
|
||||
@@ -266,46 +309,11 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) {
|
||||
rect_select_y = event->pos().y();
|
||||
} else if (mousedown) {
|
||||
int mouse_x = event->x() + x_scroll;
|
||||
if (keys_selected) {
|
||||
// move keyframes
|
||||
frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start;
|
||||
|
||||
// snapping to playhead
|
||||
if (panel_timeline->snapping) {
|
||||
for (int i=0;i<selected_keyframes.size();i++) {
|
||||
EffectRow* row = selected_rows.at(i);
|
||||
Clip* c = row->parent_effect->parent_clip;
|
||||
long key_time = row->keyframe_times.at(selected_keyframes.at(i)) + frame_diff - c->clip_in + c->timeline_in;
|
||||
long key_eval = key_time;
|
||||
if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) {
|
||||
frame_diff += (key_eval - key_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate frame_diff (make sure no keyframes overlap each other)
|
||||
for (int i=0;i<selected_keyframes.size();i++) {
|
||||
EffectRow* row = selected_rows.at(i);
|
||||
long eval_key = row->keyframe_times.at(selected_keyframes.at(i));
|
||||
for (int j=0;j<row->keyframe_times.size();j++) {
|
||||
while (!keyframeIsSelected(row, j) && row->keyframe_times.at(j) == eval_key + frame_diff) {
|
||||
if (last_frame_diff > frame_diff) {
|
||||
frame_diff++;
|
||||
panel_timeline->snapped = false;
|
||||
} else {
|
||||
frame_diff--;
|
||||
panel_timeline->snapped = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_frame_diff = frame_diff;
|
||||
|
||||
dragging = true;
|
||||
} else {
|
||||
if (select_rect) {
|
||||
// do a rect select
|
||||
selected_fields.resize(rect_select_offset);
|
||||
selected_keyframes.resize(rect_select_offset);
|
||||
|
||||
rect_select_w = event->x() - rect_select_x;
|
||||
rect_select_h = event->y() - rect_select_y;
|
||||
|
||||
@@ -320,29 +328,82 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) {
|
||||
for (int i=0;i<rowY.size();i++) {
|
||||
if (rowY.at(i) >= min_row && rowY.at(i) <= max_row) {
|
||||
EffectRow* row = rows.at(i);
|
||||
for (int j=0;j<row->keyframe_times.size();j++) {
|
||||
long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(j));
|
||||
if (!keyframeIsSelected(row, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) {
|
||||
selected_rows.append(rows.at(i));
|
||||
selected_keyframes.append(j);
|
||||
for (int k=0;k<row->fieldCount();k++) {
|
||||
EffectField* field = row->field(k);
|
||||
for (int j=0;j<field->keyframes.size();j++) {
|
||||
long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time);
|
||||
if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) {
|
||||
selected_fields.append(field);
|
||||
selected_keyframes.append(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select_rect = true;
|
||||
update_keys();
|
||||
} else if (keys_selected) {
|
||||
// move keyframes
|
||||
long frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start;
|
||||
|
||||
// snapping to playhead
|
||||
panel_timeline->snapped = false;
|
||||
if (panel_timeline->snapping) {
|
||||
for (int i=0;i<selected_keyframes.size();i++) {
|
||||
EffectField* field = selected_fields.at(i);
|
||||
Clip* c = field->parent_row->parent_effect->parent_clip;
|
||||
long key_time = old_key_vals.at(i) + frame_diff - c->clip_in + c->timeline_in;
|
||||
long key_eval = key_time;
|
||||
if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) {
|
||||
frame_diff += (key_eval - key_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate frame_diff (make sure no keyframes overlap each other)
|
||||
for (int i=0;i<selected_fields.size();i++) {
|
||||
EffectField* field = selected_fields.at(i);
|
||||
long eval_key = old_key_vals.at(i);
|
||||
for (int j=0;j<field->keyframes.size();j++) {
|
||||
while (!keyframeIsSelected(field, j) && field->keyframes.at(j).time == eval_key + frame_diff) {
|
||||
if (last_frame_diff > frame_diff) {
|
||||
frame_diff++;
|
||||
panel_timeline->snapped = false;
|
||||
} else {
|
||||
frame_diff--;
|
||||
panel_timeline->snapped = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply frame_diffs
|
||||
for (int i=0;i<selected_keyframes.size();i++) {
|
||||
EffectField* field = selected_fields.at(i);
|
||||
field->keyframes[selected_keyframes.at(i)].time = old_key_vals.at(i) + frame_diff;
|
||||
}
|
||||
|
||||
last_frame_diff = frame_diff;
|
||||
|
||||
dragging = true;
|
||||
|
||||
update_ui(false);
|
||||
}
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::mouseReleaseEvent(QMouseEvent*) {
|
||||
if (dragging && frame_diff != 0) {
|
||||
KeyframeMove* ka = new KeyframeMove();
|
||||
ka->movement = frame_diff;
|
||||
ka->keyframes = selected_keyframes;
|
||||
ka->rows = selected_rows;
|
||||
undo_stack.push(ka);
|
||||
if (dragging) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<selected_fields.size();i++) {
|
||||
ca->append(new SetLong(
|
||||
&selected_fields.at(i)->keyframes[selected_keyframes.at(i)].time,
|
||||
old_key_vals.at(i),
|
||||
selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time
|
||||
));
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
}
|
||||
|
||||
select_rect = false;
|
||||
@@ -350,5 +411,5 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent*) {
|
||||
mousedown = false;
|
||||
scroll_drag = false;
|
||||
panel_timeline->snapped = false;
|
||||
update();
|
||||
update_ui(false);
|
||||
}
|
||||
|
||||
+14
-11
@@ -7,6 +7,7 @@
|
||||
struct Clip;
|
||||
class Effect;
|
||||
class EffectRow;
|
||||
class EffectField;
|
||||
class TimelineHeader;
|
||||
|
||||
class KeyframeView : public QWidget {
|
||||
@@ -23,26 +24,25 @@ public:
|
||||
public slots:
|
||||
void set_x_scroll(int);
|
||||
void set_y_scroll(int);
|
||||
void resize_move(double d);
|
||||
void resize_move(double d);
|
||||
private:
|
||||
long adjust_row_keyframe(EffectRow* row, long time);
|
||||
QVector<EffectRow*> selected_rows;
|
||||
QVector<int> selected_keyframes;
|
||||
QVector<EffectField*> selected_fields;
|
||||
QVector<int> selected_keyframes;
|
||||
QVector<int> rowY;
|
||||
long frame_diff;
|
||||
QVector<EffectRow*> rows;
|
||||
QVector<long> old_key_vals;
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void draw_keyframe(QPainter& p, int type, int x, int y, bool darker);
|
||||
bool mousedown;
|
||||
bool mousedown;
|
||||
bool dragging;
|
||||
bool keys_selected;
|
||||
bool select_rect;
|
||||
bool scroll_drag;
|
||||
bool select_rect;
|
||||
bool scroll_drag;
|
||||
|
||||
bool keyframeIsSelected(EffectRow* row, int keyframe);
|
||||
bool keyframeIsSelected(EffectField *field, int keyframe);
|
||||
|
||||
long drag_frame_start;
|
||||
long last_frame_diff;
|
||||
@@ -50,12 +50,15 @@ private:
|
||||
int rect_select_y;
|
||||
int rect_select_w;
|
||||
int rect_select_h;
|
||||
int rect_select_offset;
|
||||
|
||||
int x_scroll;
|
||||
int y_scroll;
|
||||
|
||||
void update_keys();
|
||||
private slots:
|
||||
void show_context_menu(const QPoint& pos);
|
||||
void menu_set_key_type(QAction*);
|
||||
void show_context_menu(const QPoint& pos);
|
||||
void menu_set_key_type(QAction*);
|
||||
};
|
||||
|
||||
#endif // KEYFRAMEVIEW_H
|
||||
|
||||
+8
-2
@@ -16,7 +16,7 @@ LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) {
|
||||
drag_proc = false;
|
||||
min_enabled = false;
|
||||
max_enabled = false;
|
||||
setStyleSheet("QLabel{color:#ffc000;text-decoration:underline;}QLabel:disabled{color:#808080;}");
|
||||
set_color();
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
internal_value = -1;
|
||||
set = false;
|
||||
@@ -75,7 +75,12 @@ double LabelSlider::getPreviousValue() {
|
||||
}
|
||||
|
||||
void LabelSlider::set_previous_value() {
|
||||
previous_value = internal_value;
|
||||
previous_value = internal_value;
|
||||
}
|
||||
|
||||
void LabelSlider::set_color(QString c) {
|
||||
if (c.isEmpty()) c = "#ffc000";
|
||||
setStyleSheet("QLabel{color:" + c + ";text-decoration:underline;}QLabel:disabled{color:#808080;}");
|
||||
}
|
||||
|
||||
double LabelSlider::value() {
|
||||
@@ -115,6 +120,7 @@ void LabelSlider::mousePressEvent(QMouseEvent *ev) {
|
||||
drag_start_x = cursor().pos().x();
|
||||
drag_start_y = cursor().pos().y();
|
||||
}
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void LabelSlider::mouseMoveEvent(QMouseEvent* event) {
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
QString valueToString(double v);
|
||||
double getPreviousValue();
|
||||
void set_previous_value();
|
||||
void set_color(QString c = 0);
|
||||
int decimal_places;
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
@@ -53,6 +54,7 @@ private:
|
||||
double frame_rate;
|
||||
signals:
|
||||
void valueChanged();
|
||||
void clicked();
|
||||
};
|
||||
|
||||
#endif // LABELSLIDER_H
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "rectangleselect.h"
|
||||
|
||||
void draw_selection_rectangle(QPainter& painter, const QRect& rect) {
|
||||
painter.setPen(QColor(204, 204, 204));
|
||||
painter.setBrush(QColor(0, 0, 0, 32));
|
||||
painter.drawRect(rect);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef RECTANGLESELECT_H
|
||||
#define RECTANGLESELECT_H
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
void draw_selection_rectangle(QPainter& painter, const QRect& rect);
|
||||
|
||||
#endif // RECTANGLESELECT_H
|
||||
+1
-1
@@ -21,12 +21,12 @@ public:
|
||||
bool snapping;
|
||||
|
||||
void show_text(bool enable);
|
||||
void update_zoom(double z);
|
||||
double get_zoom();
|
||||
void delete_markers();
|
||||
void set_scrollbar_max(QScrollBar* bar, long sequence_end_frame, int offset);
|
||||
|
||||
public slots:
|
||||
void update_zoom(double z);
|
||||
void set_scroll(int);
|
||||
void set_visible_in(long i);
|
||||
void show_context_menu(const QPoint &pos);
|
||||
|
||||
+23
-14
@@ -21,6 +21,7 @@
|
||||
#include "ui/resizablescrollbar.h"
|
||||
#include "dialogs/newsequencedialog.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/rectangleselect.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "project/effect.h"
|
||||
@@ -1234,15 +1235,19 @@ void validate_transitions(Clip* c, int transition_type, long& frame_diff) {
|
||||
}
|
||||
|
||||
void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
int effective_tool = panel_timeline->tool;
|
||||
if (panel_timeline->importing || panel_timeline->creating) effective_tool = TIMELINE_TOOL_POINTER;
|
||||
|
||||
int mouse_track = getTrackFromScreenPoint(mouse_pos.y());
|
||||
long frame_diff = (lock_frame) ? 0 : panel_timeline->getTimelineFrameFromScreenPoint(mouse_pos.x()) - panel_timeline->drag_frame_start;
|
||||
int track_diff = ((panel_timeline->tool == TIMELINE_TOOL_SLIDE || panel_timeline->transition_select != TA_NO_TRANSITION) && !panel_timeline->importing) ? 0 : mouse_track - panel_timeline->drag_track_start;
|
||||
int track_diff = ((effective_tool == TIMELINE_TOOL_SLIDE || panel_timeline->transition_select != TA_NO_TRANSITION) && !panel_timeline->importing) ? 0 : mouse_track - panel_timeline->drag_track_start;
|
||||
long validator;
|
||||
long earliest_in_point = LONG_MAX;
|
||||
|
||||
// first try to snap
|
||||
long fm;
|
||||
if (panel_timeline->tool != TIMELINE_TOOL_SLIP) {
|
||||
|
||||
if (effective_tool != TIMELINE_TOOL_SLIP) {
|
||||
// slipping doesn't move the clips so we don't bother snapping for it
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
@@ -1263,7 +1268,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
}
|
||||
}
|
||||
|
||||
bool clips_are_movable = (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->tool == TIMELINE_TOOL_SLIDE || panel_timeline->importing);
|
||||
bool clips_are_movable = (effective_tool == TIMELINE_TOOL_POINTER || effective_tool == TIMELINE_TOOL_SLIDE);
|
||||
|
||||
// validate ghosts
|
||||
long temp_frame_diff = frame_diff; // cache to see if we change it (thus cancelling any snap)
|
||||
@@ -1272,7 +1277,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
Clip* c = NULL;
|
||||
if (g.clip != -1) c = sequence->clips.at(g.clip);
|
||||
|
||||
FootageStream* ms = NULL;
|
||||
const FootageStream* ms = NULL;
|
||||
if (g.clip != -1 && c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
|
||||
ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
}
|
||||
@@ -1280,8 +1285,8 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
// validate ghosts for trimming
|
||||
if (panel_timeline->creating) {
|
||||
// i feel like we might need something here but we haven't so far?
|
||||
} else if (panel_timeline->tool == TIMELINE_TOOL_SLIP) {
|
||||
if (c->media->get_type() == MEDIA_TYPE_SEQUENCE
|
||||
} else if (effective_tool == TIMELINE_TOOL_SLIP) {
|
||||
if ((c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE)
|
||||
|| (ms != NULL && !ms->infinite_length)) {
|
||||
// prevent slip moving a clip below 0 clip_in
|
||||
validator = g.old_clip_in - frame_diff;
|
||||
@@ -1298,13 +1303,13 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
if (validator < 1) frame_diff -= (1 - validator);
|
||||
|
||||
// prevent timeline in from going below 0
|
||||
if (panel_timeline->tool != TIMELINE_TOOL_RIPPLE) {
|
||||
if (effective_tool != TIMELINE_TOOL_RIPPLE) {
|
||||
validator = g.old_in + frame_diff;
|
||||
if (validator < 0) frame_diff -= validator;
|
||||
}
|
||||
|
||||
// prevent clip_in from going below 0
|
||||
if (c->media->get_type() == MEDIA_TYPE_SEQUENCE
|
||||
if ((c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE)
|
||||
|| (ms != NULL && !ms->infinite_length)) {
|
||||
validator = g.old_clip_in + frame_diff;
|
||||
if (validator < 0) frame_diff -= validator;
|
||||
@@ -1349,7 +1354,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
}
|
||||
|
||||
// ripple ops
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) {
|
||||
if (effective_tool == TIMELINE_TOOL_RIPPLE) {
|
||||
for (int j=0;j<post_clips.size();j++) {
|
||||
Clip* post = post_clips.at(j);
|
||||
|
||||
@@ -1421,7 +1426,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (panel_timeline->tool == TIMELINE_TOOL_TRANSITION) {
|
||||
} else if (effective_tool == TIMELINE_TOOL_TRANSITION) {
|
||||
if (panel_timeline->transition_tool_post_clip == -1) {
|
||||
validate_transitions(c, panel_timeline->transition_tool_type, frame_diff);
|
||||
} else {
|
||||
@@ -1453,7 +1458,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
Ghost& g = panel_timeline->ghosts[i];
|
||||
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_SLIP) {
|
||||
if (effective_tool == TIMELINE_TOOL_SLIP) {
|
||||
g.clip_in = g.old_clip_in - frame_diff;
|
||||
} else if (g.trimming) {
|
||||
long ghost_diff = frame_diff;
|
||||
@@ -1506,7 +1511,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
} else if (same_sign(g.old_track, panel_timeline->drag_track_start)) {
|
||||
g.track += track_diff;
|
||||
}
|
||||
} else if (panel_timeline->tool == TIMELINE_TOOL_TRANSITION) {
|
||||
} else if (effective_tool == TIMELINE_TOOL_TRANSITION) {
|
||||
if (panel_timeline->transition_tool_post_clip > -1) {
|
||||
g.in = g.old_in - frame_diff;
|
||||
g.out = g.old_out + frame_diff;
|
||||
@@ -1521,7 +1526,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) {
|
||||
}
|
||||
|
||||
// apply changes to selections
|
||||
if (panel_timeline->tool != TIMELINE_TOOL_SLIP && !panel_timeline->importing && !panel_timeline->creating) {
|
||||
if (effective_tool != TIMELINE_TOOL_SLIP && !panel_timeline->importing && !panel_timeline->creating) {
|
||||
for (int i=0;i<sequence->selections.size();i++) {
|
||||
Selection& s = sequence->selections[i];
|
||||
if (panel_timeline->trim_target > -1) {
|
||||
@@ -2145,7 +2150,7 @@ int color_brightness(int r, int g, int b) {
|
||||
return (0.2126*r + 0.7152*g + 0.0722*b);
|
||||
}
|
||||
|
||||
void draw_waveform(Clip* clip, FootageStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) {
|
||||
void draw_waveform(Clip* clip, const FootageStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) {
|
||||
int divider = ms->audio_channels*2;
|
||||
int channel_height = clip_rect.height()/ms->audio_channels;
|
||||
|
||||
@@ -2342,6 +2347,10 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
p.drawImage(QRect(thumb_x, clip_rect.y()+thumb_y, thumb_clip_width, thumb_height), ms->video_preview, QRect(0, 0, thumb_clip_width*((double)ms->video_preview.width()/(double)thumb_width), ms->video_preview.height()));
|
||||
}
|
||||
}
|
||||
if (clip->timeline_out - clip->timeline_in + clip->clip_in > clip->getMaximumLength()) {
|
||||
draw_checkerboard = true;
|
||||
checkerboard_rect.setLeft(panel_timeline->getTimelineScreenPointFromFrame(clip->getMaximumLength() + clip->timeline_in - clip->clip_in));
|
||||
}
|
||||
} else if (clip_rect.height() > TRACK_MIN_HEIGHT) {
|
||||
// draw waveform
|
||||
p.setPen(QColor(80, 80, 80));
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ class QPainter;
|
||||
class Media;
|
||||
|
||||
bool same_sign(int a, int b);
|
||||
void draw_waveform(Clip* clip, FootageStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
|
||||
void draw_waveform(Clip* clip, const FootageStream *ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
|
||||
|
||||
class TimelineWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
+4
-4
@@ -72,7 +72,7 @@ void ViewerWidget::delete_function() {
|
||||
// destroy all textures as well
|
||||
if (viewer->seq != NULL) {
|
||||
makeCurrent();
|
||||
closeActiveClips(viewer->seq, true);
|
||||
closeActiveClips(viewer->seq);
|
||||
doneCurrent();
|
||||
}
|
||||
}
|
||||
@@ -485,7 +485,7 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
|
||||
Footage* m = c->media->to_footage();
|
||||
if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) {
|
||||
if (m->ready) {
|
||||
FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
if (ms != NULL && is_clip_active(c, playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
@@ -495,7 +495,7 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
|
||||
clip_is_active = true;
|
||||
if (c->track >= 0) audio_track_count++;
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
close_clip(c, false);
|
||||
}
|
||||
} else {
|
||||
//dout << "[WARNING] Media '" + m->name + "' was not ready, retrying...";
|
||||
@@ -507,7 +507,7 @@ GLuint ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio)
|
||||
if (!c->open) open_clip(c, !rendering);
|
||||
clip_is_active = true;
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
close_clip(c, false);
|
||||
}
|
||||
}
|
||||
if (clip_is_active) {
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
bool waveform;
|
||||
Clip* waveform_clip;
|
||||
FootageStream* waveform_ms;
|
||||
const FootageStream* waveform_ms;
|
||||
double waveform_zoom;
|
||||
int waveform_scroll;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user