Merge pull request #245 from olive-editor/bezierkeys

bezier keyframes and keyframe system overhaul
This commit is contained in:
itsmattkc
2019-01-04 23:12:13 +11:00
committed by GitHub
61 changed files with 4191 additions and 1479 deletions
+44 -44
View File
@@ -57,7 +57,7 @@ enum ExportFormats {
ExportDialog::ExportDialog(QWidget *parent) :
QDialog(parent)
{
setWindowTitle("Export \"" + sequence->name + "\"");
setWindowTitle("Export \"" + sequence->name + "\"");
setup_ui();
rangeCombobox->setCurrentIndex(0);
@@ -479,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();
@@ -603,40 +603,40 @@ void ExportDialog::setup_ui() {
videoGroupbox->setFlat(false);
videoGroupbox->setCheckable(true);
QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox);
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);
videoGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1);
vcodecCombobox = new QComboBox(videoGroupbox);
videoGridLayout->addWidget(vcodecCombobox, 0, 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);
videoGridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1);
widthSpinbox = new QSpinBox(videoGroupbox);
widthSpinbox->setMaximum(16777216);
videoGridLayout->addWidget(widthSpinbox, 1, 1, 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("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);
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);
videoGridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 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);
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);
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);
@@ -644,60 +644,60 @@ void ExportDialog::setup_ui() {
audioGroupbox->setTitle("Audio");
audioGroupbox->setCheckable(true);
QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox);
QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox);
audioGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1);
audioGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1);
acodecCombobox = new QComboBox(audioGroupbox);
audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1);
audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1);
audioGridLayout->addWidget(new QLabel("Sampling Rate:"), 1, 0, 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);
audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1);
audioGridLayout->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);
audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1);
audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1);
verticalLayout->addWidget(audioGroupbox);
QHBoxLayout* progressLayout = new QHBoxLayout();
QHBoxLayout* progressLayout = new QHBoxLayout();
progressBar = new QProgressBar(this);
progressBar->setFormat("%p% (ETA: 0:00:00)");
progressBar->setEnabled(false);
progressBar->setValue(0);
progressLayout->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);
progressLayout->addWidget(renderCancel);
verticalLayout->addLayout(progressLayout);
verticalLayout->addLayout(progressLayout);
QHBoxLayout* buttonLayout = new QHBoxLayout();
buttonLayout->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()));
buttonLayout->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()));
buttonLayout->addWidget(cancel_button);
buttonLayout->addWidget(cancel_button);
buttonLayout->addStretch();
buttonLayout->addStretch();
verticalLayout->addLayout(buttonLayout);
verticalLayout->addLayout(buttonLayout);
connect(formatCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(format_changed(int)));
connect(compressionTypeCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(comp_type_changed(int)));
+1 -1
View File
@@ -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 -4
View File
@@ -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) {
+39 -39
View File
@@ -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);
}
+4 -4
View File
@@ -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) {
+7 -7
View File
@@ -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);
+45 -45
View File
@@ -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);
}
+25 -25
View File
@@ -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) {
+51 -51
View File
@@ -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);
}
+5 -5
View File
@@ -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) {
+149 -119
View File
@@ -28,147 +28,177 @@
#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);
}*/
default_pos_x = new_default_pos_x;
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;
int new_default_anchor_x = parent_clip->getWidth()/2;
int new_default_anchor_y = parent_clip->getHeight()/2;
if (default_anchor_x == 0) default_anchor_x = default_pos_x;
if (default_anchor_y == 0) default_anchor_y = default_pos_y;
if (new_default_anchor_x == 0) new_default_anchor_x = default_pos_x;
if (new_default_anchor_y == 0) new_default_anchor_y = default_pos_y;
// adjust anchors for new size
if (set) {
adjust_field(anchor_x_box, default_anchor_x, new_default_anchor_x);
adjust_field(anchor_y_box, default_anchor_y, new_default_anchor_y);
}
default_anchor_x = new_default_anchor_x;
default_anchor_y = new_default_anchor_y;
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);
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;
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) {
@@ -188,51 +218,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);
}
+3
View File
@@ -39,6 +39,9 @@ private:
int default_anchor_x;
int default_anchor_y;
double default_pos_x;
double default_pos_y;
bool set;
};
#endif // TRANSFORMEFFECT_H
+3 -3
View File
@@ -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) {
+2 -2
View File
@@ -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
+1 -1
View File
@@ -237,7 +237,7 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
} else if (m->url.contains('%')) {
// hack for image sequences (qt won't be able to find the URL with %, but ffmpeg may)
m->url = internal_proj_dir_test;
dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's current directory";
dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's internal directory";
} else {
dout << "[INFO] Failed to match" << attr.value().toString() << "to file";
}
+42 -3
View File
@@ -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;
}
+5
View File
@@ -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
+78 -71
View File
@@ -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"
@@ -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_data_path() + "/layout");
if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) {
restoreState(panel_config.readAll(), 0);
panel_config.close();
}
}
layout()->update();
@@ -195,12 +193,7 @@ MainWindow::MainWindow(QWidget *parent) :
}
}
// 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);
@@ -213,6 +206,7 @@ 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) {
enable_launch_with_project = false;
open_project_worker(autorecovery_filename, true);
}
}
@@ -227,45 +221,7 @@ 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();
}
@@ -303,12 +259,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();
}
}
@@ -654,6 +614,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));
@@ -778,7 +742,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
@@ -812,6 +776,34 @@ 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();
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();
e->accept();
} else {
e->ignore();
@@ -859,7 +851,10 @@ void MainWindow::reset_layout() {
}
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();
@@ -867,7 +862,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();
@@ -875,7 +873,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();
@@ -883,7 +884,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();
@@ -891,7 +895,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();
-1
View File
@@ -112,7 +112,6 @@ 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();
+973
View File
@@ -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>&amp;File</string>
</property>
<widget class="QMenu" name="menu_New">
<property name="title">
<string>&amp;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>&amp;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>&amp;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>&amp;Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<widget class="QMenu" name="menu_View">
<property name="title">
<string>&amp;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>&amp;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>&amp;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>&amp;Open Project</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
<action name="action_Save_Project">
<property name="text">
<string>&amp;Save Project</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionSave_Project_As">
<property name="text">
<string>Save Project &amp;As</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+S</string>
</property>
</action>
<action name="action_Import">
<property name="text">
<string>&amp;Import...</string>
</property>
<property name="shortcut">
<string>Ctrl+I</string>
</property>
</action>
<action name="actionExport">
<property name="text">
<string>&amp;Export...</string>
</property>
<property name="shortcut">
<string>Ctrl+M</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>E&amp;xit</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About...</string>
</property>
</action>
<action name="action_Undo">
<property name="text">
<string>&amp;Undo</string>
</property>
<property name="shortcut">
<string>Ctrl+Z</string>
</property>
</action>
<action name="action_Redo">
<property name="text">
<string>&amp;Redo</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+Z</string>
</property>
</action>
<action name="actionCu_t">
<property name="text">
<string>Cu&amp;t</string>
</property>
<property name="shortcut">
<string>Ctrl+X</string>
</property>
</action>
<action name="actionCop_y">
<property name="text">
<string>Cop&amp;y</string>
</property>
<property name="shortcut">
<string>Ctrl+C</string>
</property>
</action>
<action name="action_Paste">
<property name="text">
<string>&amp;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 &amp;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>
+14 -2
View File
@@ -105,7 +105,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 \
@@ -185,7 +191,13 @@ 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 +=
+6 -2
View File
@@ -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);
+239
View File
@@ -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);
}
+44
View File
@@ -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
View File
@@ -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;
}
+6
View File
@@ -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
+4 -2
View File
@@ -60,7 +60,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);
@@ -469,12 +469,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++) {
+2 -6
View File
@@ -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;
-1
View File
@@ -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>());
+15 -11
View File
@@ -46,6 +46,8 @@ Viewer::Viewer(QWidget *parent) :
panel_name("Viewer: "),
minimum_zoom(1.0)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setup_ui();
headers->viewer = this;
@@ -449,8 +451,6 @@ void Viewer::set_sb_max() {
}
void Viewer::setup_ui() {
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
QWidget* contents = new QWidget();
QVBoxLayout* layout = new QVBoxLayout(contents);
@@ -573,14 +573,14 @@ void Viewer::set_media(Media* m) {
seq->frame_rate = 30;
if (footage->video_tracks.size() > 0) {
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;
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;
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 +594,12 @@ void Viewer::set_media(Media* m) {
}
if (footage->audio_tracks.size() > 0) {
const 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 +610,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 {
@@ -698,6 +698,10 @@ void Viewer::clean_created_seq() {
void Viewer::set_sequence(bool main, Sequence *s) {
reset_all_audio();
if (seq != NULL) {
closeActiveClips(seq);
}
main_sequence = main;
seq = (main) ? sequence : s;
+2
View File
@@ -101,8 +101,10 @@ 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() {
+85 -86
View File
@@ -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,16 +112,16 @@ 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) {
const 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)));
if (ms->video_interlacing != VIDEO_PROGRESSIVE) {
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;
}
@@ -132,7 +133,7 @@ void get_clip_frame(Clip* c, long playhead) {
c->queue_lock.lock();
if (c->queue.size() > 0) {
if (ms->infinite_length) {
if (ms->infinite_length) {
target_frame = c->queue.at(0);
#ifdef GCF_DEBUG
dout << "GCF ==> USE PRECISE (INFINITE)";
@@ -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,13 +253,13 @@ 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) {
@@ -278,11 +279,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 +294,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 +324,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
View File
@@ -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
+14 -19
View File
@@ -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,8 +237,8 @@ void Clip::recalculateMaxLength() {
case MEDIA_TYPE_FOOTAGE:
{
Footage* m = media->to_footage();
const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream);
if (ms != NULL && ms->infinite_length) {
const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream);
if (ms != NULL && ms->infinite_length) {
calculated_length = LONG_MAX;
} else {
calculated_length = m->get_length_in_frames(fr);
@@ -269,7 +265,7 @@ int Clip::getWidth() {
switch (media->get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
const 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:
{
const 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+146 -119
View File
@@ -1,6 +1,5 @@
#include "effectrow.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QPushButton>
#include <QMessageBox>
@@ -11,169 +10,197 @@
#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();
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));
}
}
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;
}
}
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);
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;
}
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();
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();
}
+28 -29
View File
@@ -8,47 +8,46 @@ 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;
bool just_made_unsafe_keyframe;
};
#endif // EFFECTROW_H
+46
View File
@@ -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);
}
}
+25
View File
@@ -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
+92 -91
View File
@@ -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,85 +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) :
@@ -835,7 +769,7 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) :
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);
old_values[i] = field->keyframes.at(index).data;
}
}
new_values[i] = field->get_current_data();
@@ -846,15 +780,11 @@ 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();
row->field(i)->keyframes.removeLast();
} else {
row->field(i)->keyframe_data[index] = old_values.at(i);
row->field(i)->keyframes[index].data = old_values.at(i);
}
}
@@ -864,15 +794,16 @@ void KeyframeSet::undo() {
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++) {
EffectField* f = row->field(i);
if (append) {
row->field(i)->keyframe_data.append(new_values.at(i));
EffectKeyframe k;
k.data = new_values.at(i);
k.time = time;
k.type = (f->keyframes.size() > 0) ? f->keyframes.last().type : EFFECT_KEYFRAME_LINEAR;
f->keyframes.append(k);
} else {
row->field(i)->keyframe_data[index] = new_values.at(i);
f->keyframes[index].data = new_values.at(i);
}
}
row->setKeyframing(true);
@@ -1169,7 +1100,7 @@ void CloseAllClipsCommand::undo() {
}
void CloseAllClipsCommand::redo() {
closeActiveClips(sequence, true);
closeActiveClips(sequence);
}
UpdateFootageTooltip::UpdateFootageTooltip(Media *i) :
@@ -1290,3 +1221,73 @@ 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;
}
+56 -20
View File
@@ -17,6 +17,7 @@ struct EffectMeta;
#include "project/marker.h"
#include "project/selection.h"
#include "project/effectfield.h"
#include <QUndoStack>
#include <QUndoCommand>
@@ -328,32 +329,17 @@ 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;
};
@@ -374,6 +360,21 @@ private:
bool done;
};
// a more modern version of the above, could probably replace it
// assumes the keyframe already exists
class KeyframeFieldSet : public QUndoCommand {
public:
KeyframeFieldSet(EffectField* ifield, int ii);
void undo();
void redo();
private:
EffectField* field;
int index;
EffectKeyframe key;
bool done;
bool old_project_changed;
};
class EffectFieldUndo : public QUndoCommand {
public:
EffectFieldUndo(EffectField* field);
@@ -523,6 +524,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 +630,15 @@ 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;
};
#endif // UNDO_H
+13
View File
@@ -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();
}
+16
View File
@@ -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
+835
View File
@@ -0,0 +1,835 @@
#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)
{
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));
//set_scroll_y(height() - y1);
}
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) {
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);
} 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 (click_add) {
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 (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 (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 *) {
if (click_add) {
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;
if (rect_select) {
rect_select = false;
selection_update();
update();
}
}
void GraphView::wheelEvent(QWheelEvent *event) {
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(x_scroll + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom));
set_scroll_y(qRound(y_scroll + 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;
}
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);
}
+99
View File
@@ -0,0 +1,99 @@
#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;
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
+32
View File
@@ -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);
}
+11
View File
@@ -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
+21 -6
View File
@@ -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) {
+3 -1
View File
@@ -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:
+188 -125
View File
@@ -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,41 @@ 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
bool solo = true;
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
&& !(m == l && k == n)) {
solo = false;
break;
}
}
}
if (solo) {
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 +172,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 +207,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 +229,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 +252,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 +311,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 +330,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 +413,5 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent*) {
mousedown = false;
scroll_drag = false;
panel_timeline->snapped = false;
update();
update_ui(false);
}
+14 -11
View File
@@ -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
View File
@@ -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) {
+2
View File
@@ -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
+7
View File
@@ -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);
}
+8
View File
@@ -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
View File
@@ -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);
+1
View File
@@ -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"
+4 -4
View File
@@ -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) {
const 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) {