Merge pull request #245 from olive-editor/bezierkeys
bezier keyframes and keyframe system overhaul
This commit is contained in:
+44
-44
@@ -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)));
|
||||
|
||||
@@ -335,7 +335,7 @@ void SpeedDialog::accept() {
|
||||
|
||||
for (int i=0;i<clips.size();i++) {
|
||||
Clip* c = clips.at(i);
|
||||
if (c->open) close_clip(c);
|
||||
if (c->open) close_clip(c, true);
|
||||
|
||||
if (c->track >= 0
|
||||
&& maintain_pitch->checkState() != Qt::PartiallyChecked
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
#include <QtMath>
|
||||
|
||||
AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE, "amount");
|
||||
amount_val = add_row("Amount")->add_field(EFFECT_FIELD_DOUBLE, "amount");
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
amount_val->set_double_default_value(20);
|
||||
amount_val->set_double_default_value(20);
|
||||
|
||||
mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val = add_row("Mix")->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val->set_bool_value(true);
|
||||
|
||||
srand(QDateTime::currentMSecsSinceEpoch());
|
||||
srand(QDateTime::currentMSecsSinceEpoch());
|
||||
}
|
||||
|
||||
void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) {
|
||||
|
||||
@@ -5,59 +5,59 @@
|
||||
#include "debug.h"
|
||||
|
||||
CornerPinEffect::CornerPinEffect(Clip *c, const EffectMeta *em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
enable_coords = true;
|
||||
enable_shader = true;
|
||||
|
||||
EffectRow* top_left = add_row("Top Left:");
|
||||
top_left_x = top_left->add_field(EFFECT_FIELD_DOUBLE, "topleftx");
|
||||
top_left_y = top_left->add_field(EFFECT_FIELD_DOUBLE, "toplefty");
|
||||
EffectRow* top_left = add_row("Top Left");
|
||||
top_left_x = top_left->add_field(EFFECT_FIELD_DOUBLE, "topleftx");
|
||||
top_left_y = top_left->add_field(EFFECT_FIELD_DOUBLE, "toplefty");
|
||||
|
||||
EffectRow* top_right = add_row("Top Right:");
|
||||
top_right_x = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprightx");
|
||||
top_right_y = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprighty");
|
||||
EffectRow* top_right = add_row("Top Right");
|
||||
top_right_x = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprightx");
|
||||
top_right_y = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprighty");
|
||||
|
||||
EffectRow* bottom_left = add_row("Bottom Left:");
|
||||
bottom_left_x = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomleftx");
|
||||
bottom_left_y = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomlefty");
|
||||
EffectRow* bottom_left = add_row("Bottom Left");
|
||||
bottom_left_x = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomleftx");
|
||||
bottom_left_y = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomlefty");
|
||||
|
||||
EffectRow* bottom_right = add_row("Bottom Right:");
|
||||
bottom_right_x = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrightx");
|
||||
bottom_right_y = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrighty");
|
||||
EffectRow* bottom_right = add_row("Bottom Right");
|
||||
bottom_right_x = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrightx");
|
||||
bottom_right_y = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrighty");
|
||||
|
||||
perspective = add_row("Perspective:")->add_field(EFFECT_FIELD_BOOL, "perspective");
|
||||
perspective->set_bool_value(true);
|
||||
perspective = add_row("Perspective")->add_field(EFFECT_FIELD_BOOL, "perspective");
|
||||
perspective->set_bool_value(true);
|
||||
|
||||
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_left_gizmo->x_field1 = top_left_x;
|
||||
top_left_gizmo->y_field1 = top_left_y;
|
||||
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_left_gizmo->x_field1 = top_left_x;
|
||||
top_left_gizmo->y_field1 = top_left_y;
|
||||
|
||||
top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_right_gizmo->x_field1 = top_right_x;
|
||||
top_right_gizmo->y_field1 = top_right_y;
|
||||
top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
top_right_gizmo->x_field1 = top_right_x;
|
||||
top_right_gizmo->y_field1 = top_right_y;
|
||||
|
||||
bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_left_gizmo->x_field1 = bottom_left_x;
|
||||
bottom_left_gizmo->y_field1 = bottom_left_y;
|
||||
bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_left_gizmo->x_field1 = bottom_left_x;
|
||||
bottom_left_gizmo->y_field1 = bottom_left_y;
|
||||
|
||||
bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_right_gizmo->x_field1 = bottom_right_x;
|
||||
bottom_right_gizmo->y_field1 = bottom_right_y;
|
||||
bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
bottom_right_gizmo->x_field1 = bottom_right_x;
|
||||
bottom_right_gizmo->y_field1 = bottom_right_y;
|
||||
|
||||
vertPath = "cornerpin.vert";
|
||||
fragPath = "cornerpin.frag";
|
||||
}
|
||||
|
||||
void CornerPinEffect::process_coords(double timecode, GLTextureCoords &coords, int data) {
|
||||
coords.vertexTopLeftX += top_left_x->get_double_value(timecode);
|
||||
coords.vertexTopLeftY += top_left_y->get_double_value(timecode);
|
||||
coords.vertexTopLeftX += top_left_x->get_double_value(timecode);
|
||||
coords.vertexTopLeftY += top_left_y->get_double_value(timecode);
|
||||
|
||||
coords.vertexTopRightX += top_right_x->get_double_value(timecode);
|
||||
coords.vertexTopRightY += top_right_y->get_double_value(timecode);
|
||||
coords.vertexTopRightX += top_right_x->get_double_value(timecode);
|
||||
coords.vertexTopRightY += top_right_y->get_double_value(timecode);
|
||||
|
||||
coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode);
|
||||
coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode);
|
||||
coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode);
|
||||
coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode);
|
||||
|
||||
coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode);
|
||||
coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode);
|
||||
coords.vertexBottomRightY += bottom_right_y->get_double_value(timecode);
|
||||
}
|
||||
|
||||
@@ -66,12 +66,12 @@ void CornerPinEffect::process_shader(double timecode, GLTextureCoords &coords) {
|
||||
glslProgram->setUniformValue("p1", (GLfloat) coords.vertexBottomRightX, (GLfloat) coords.vertexBottomRightY);
|
||||
glslProgram->setUniformValue("p2", (GLfloat) coords.vertexTopLeftX, (GLfloat) coords.vertexTopLeftY);
|
||||
glslProgram->setUniformValue("p3", (GLfloat) coords.vertexTopRightX, (GLfloat) coords.vertexTopRightY);
|
||||
glslProgram->setUniformValue("perspective", perspective->get_bool_value(timecode));
|
||||
glslProgram->setUniformValue("perspective", perspective->get_bool_value(timecode));
|
||||
}
|
||||
|
||||
void CornerPinEffect::gizmo_draw(double timecode, GLTextureCoords &coords) {
|
||||
top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
|
||||
top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
|
||||
bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
|
||||
bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
|
||||
top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
|
||||
top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
|
||||
bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
|
||||
bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
EffectRow* pan_row = add_row("Pan:");
|
||||
pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE, "pan");
|
||||
EffectRow* pan_row = add_row("Pan");
|
||||
pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE, "pan");
|
||||
pan_val->set_double_minimum_value(-100);
|
||||
pan_val->set_double_maximum_value(100);
|
||||
pan_val->set_double_maximum_value(100);
|
||||
|
||||
// set defaults
|
||||
pan_val->set_double_default_value(0);
|
||||
pan_val->set_double_default_value(0);
|
||||
}
|
||||
|
||||
void PanEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) {
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
ShakeEffect::ShakeEffect(Clip *c, const EffectMeta *em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
|
||||
EffectRow* intensity_row = add_row("Intensity:");
|
||||
intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity");
|
||||
EffectRow* intensity_row = add_row("Intensity");
|
||||
intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity");
|
||||
intensity_val->set_double_minimum_value(0);
|
||||
|
||||
EffectRow* rotation_row = add_row("Rotation:");
|
||||
rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation");
|
||||
EffectRow* rotation_row = add_row("Rotation");
|
||||
rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation");
|
||||
rotation_val->set_double_minimum_value(0);
|
||||
|
||||
EffectRow* frequency_row = add_row("Frequency:");
|
||||
frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
EffectRow* frequency_row = add_row("Frequency");
|
||||
frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
frequency_val->set_double_minimum_value(0);
|
||||
|
||||
// set defaults
|
||||
// set defaults
|
||||
intensity_val->set_double_default_value(25);
|
||||
rotation_val->set_double_default_value(10);
|
||||
frequency_val->set_double_default_value(5);
|
||||
|
||||
@@ -21,36 +21,36 @@
|
||||
SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
|
||||
enable_superimpose = true;
|
||||
|
||||
solid_type = add_row("Type:")->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
solid_type = add_row("Type")->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
solid_type->add_combo_item("Solid Color", SOLID_TYPE_COLOR);
|
||||
solid_type->add_combo_item("SMPTE Bars", SOLID_TYPE_BARS);
|
||||
solid_type->add_combo_item("Checkerboard", SOLID_TYPE_CHECKERBOARD);
|
||||
solid_type->add_combo_item("Checkerboard", SOLID_TYPE_CHECKERBOARD);
|
||||
|
||||
opacity_field = add_row("Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "opacity");
|
||||
opacity_field->set_double_minimum_value(0);
|
||||
opacity_field->set_double_maximum_value(100);
|
||||
opacity_field->set_double_default_value(100);
|
||||
opacity_field = add_row("Opacity")->add_field(EFFECT_FIELD_DOUBLE, "opacity");
|
||||
opacity_field->set_double_minimum_value(0);
|
||||
opacity_field->set_double_maximum_value(100);
|
||||
opacity_field->set_double_default_value(100);
|
||||
|
||||
solid_color_field = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color");
|
||||
solid_color_field->set_color_value(Qt::red);
|
||||
solid_color_field = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color");
|
||||
solid_color_field->set_color_value(Qt::red);
|
||||
|
||||
checkerboard_size_field = add_row("Checkerboard Size:")->add_field(EFFECT_FIELD_DOUBLE, "checker_size");
|
||||
checkerboard_size_field->set_double_minimum_value(1);
|
||||
checkerboard_size_field->set_double_default_value(10);
|
||||
checkerboard_size_field = add_row("Checkerboard Size")->add_field(EFFECT_FIELD_DOUBLE, "checker_size");
|
||||
checkerboard_size_field->set_double_minimum_value(1);
|
||||
checkerboard_size_field->set_double_default_value(10);
|
||||
|
||||
// hacky but eh
|
||||
QComboBox* solid_type_combo = static_cast<QComboBox*>(solid_type->get_ui_element());
|
||||
connect(solid_type_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(ui_update(int)));
|
||||
ui_update(solid_type_combo->currentIndex());
|
||||
// hacky but eh
|
||||
QComboBox* solid_type_combo = static_cast<QComboBox*>(solid_type->get_ui_element());
|
||||
connect(solid_type_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(ui_update(int)));
|
||||
ui_update(solid_type_combo->currentIndex());
|
||||
|
||||
/*vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/solideffect.frag";*/
|
||||
/*vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/solideffect.frag";*/
|
||||
}
|
||||
|
||||
void SolidEffect::redraw(double timecode) {
|
||||
int w = img.width();
|
||||
int h = img.height();
|
||||
int alpha = qRound(opacity_field->get_double_value(timecode)*2.55);
|
||||
int alpha = qRound(opacity_field->get_double_value(timecode)*2.55);
|
||||
switch (solid_type->get_combo_data(timecode).toInt()) {
|
||||
case SOLID_TYPE_COLOR:
|
||||
{
|
||||
@@ -141,37 +141,37 @@ void SolidEffect::redraw(double timecode) {
|
||||
third_color.setAlpha(alpha);
|
||||
p.fillRect(QRect(bar_x, third_bar_y, third_bar_width, third_bar_height), third_color);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOLID_TYPE_CHECKERBOARD:
|
||||
{
|
||||
// draw checkboard
|
||||
QPainter p(&img);
|
||||
img.fill(Qt::transparent);
|
||||
}
|
||||
break;
|
||||
case SOLID_TYPE_CHECKERBOARD:
|
||||
{
|
||||
// draw checkboard
|
||||
QPainter p(&img);
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
int checker_width = qCeil(checkerboard_size_field->get_double_value(timecode));
|
||||
int checker_x, checker_y;
|
||||
int checkerboard_size_w = qCeil(double(w)/checker_width);
|
||||
int checkerboard_size_h = qCeil(double(h)/checker_width);
|
||||
int checker_width = qCeil(checkerboard_size_field->get_double_value(timecode));
|
||||
int checker_x, checker_y;
|
||||
int checkerboard_size_w = qCeil(double(w)/checker_width);
|
||||
int checkerboard_size_h = qCeil(double(h)/checker_width);
|
||||
|
||||
QColor checker_odd(QColor(0, 0, 0, alpha));
|
||||
QColor checker_even(solid_color_field->get_color_value(timecode));
|
||||
checker_even.setAlpha(alpha);
|
||||
QVector<QColor> checker_color{checker_odd, checker_even};
|
||||
QColor checker_odd(QColor(0, 0, 0, alpha));
|
||||
QColor checker_even(solid_color_field->get_color_value(timecode));
|
||||
checker_even.setAlpha(alpha);
|
||||
QVector<QColor> checker_color{checker_odd, checker_even};
|
||||
|
||||
for(int i = 0; i < checkerboard_size_w; i++){
|
||||
checker_x = checker_width*i;
|
||||
for(int j = 0; j < checkerboard_size_h; j++){
|
||||
checker_y = checker_width*j;
|
||||
p.fillRect(QRect(checker_x, checker_y, checker_width, checker_width), checker_color[(i + j)%2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
for(int i = 0; i < checkerboard_size_w; i++){
|
||||
checker_x = checker_width*i;
|
||||
for(int j = 0; j < checkerboard_size_h; j++){
|
||||
checker_y = checker_width*j;
|
||||
p.fillRect(QRect(checker_x, checker_y, checker_width, checker_width), checker_color[(i + j)%2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SolidEffect::ui_update(int i) {
|
||||
solid_color_field->set_enabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD);
|
||||
checkerboard_size_field->set_enabled(i == SOLID_TYPE_CHECKERBOARD);
|
||||
solid_color_field->set_enabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD);
|
||||
checkerboard_size_field->set_enabled(i == SOLID_TYPE_CHECKERBOARD);
|
||||
}
|
||||
|
||||
@@ -24,43 +24,43 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
|
||||
Effect(c, em)
|
||||
{
|
||||
enable_superimpose = true;
|
||||
enable_shader = true;
|
||||
enable_shader = true;
|
||||
|
||||
text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, "text", 2);
|
||||
text_val = add_row("Text")->add_field(EFFECT_FIELD_STRING, "text", 2);
|
||||
|
||||
set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, "font", 2);
|
||||
set_font_combobox = add_row("Font")->add_field(EFFECT_FIELD_FONT, "font", 2);
|
||||
|
||||
size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
size_val = add_row("Size")->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
|
||||
set_color_button = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
set_color_button = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
|
||||
EffectRow* alignment_row = add_row("Alignment:");
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign");
|
||||
EffectRow* alignment_row = add_row("Alignment");
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign");
|
||||
halign_field->add_combo_item("Left", Qt::AlignLeft);
|
||||
halign_field->add_combo_item("Center", Qt::AlignHCenter);
|
||||
halign_field->add_combo_item("Right", Qt::AlignRight);
|
||||
halign_field->add_combo_item("Justify", Qt::AlignJustify);
|
||||
halign_field->add_combo_item("Justify", Qt::AlignJustify);
|
||||
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign");
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign");
|
||||
valign_field->add_combo_item("Top", Qt::AlignTop);
|
||||
valign_field->add_combo_item("Center", Qt::AlignVCenter);
|
||||
valign_field->add_combo_item("Bottom", Qt::AlignBottom);
|
||||
valign_field->add_combo_item("Bottom", Qt::AlignBottom);
|
||||
|
||||
word_wrap_field = add_row("Word Wrap:")->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
|
||||
word_wrap_field = add_row("Word Wrap")->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
|
||||
|
||||
outline_bool = add_row("Outline:")->add_field(EFFECT_FIELD_BOOL, "outline", 2);
|
||||
outline_color = add_row("Outline Color:")->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
|
||||
outline_width = add_row("Outline Width:")->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
|
||||
outline_bool = add_row("Outline")->add_field(EFFECT_FIELD_BOOL, "outline", 2);
|
||||
outline_color = add_row("Outline Color")->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
|
||||
outline_width = add_row("Outline Width")->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
|
||||
outline_width->set_double_minimum_value(0);
|
||||
|
||||
shadow_bool = add_row("Shadow:")->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
|
||||
shadow_color = add_row("Shadow Color:")->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
|
||||
shadow_distance = add_row("Shadow Distance:")->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
|
||||
shadow_bool = add_row("Shadow")->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
|
||||
shadow_color = add_row("Shadow Color")->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
|
||||
shadow_distance = add_row("Shadow Distance")->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
|
||||
shadow_distance->set_double_minimum_value(0);
|
||||
shadow_softness = add_row("Shadow Softness:")->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
|
||||
shadow_softness = add_row("Shadow Softness")->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
|
||||
shadow_softness->set_double_minimum_value(0);
|
||||
shadow_opacity = add_row("Shadow Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
|
||||
shadow_opacity = add_row("Shadow Opacity")->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
|
||||
shadow_opacity->set_double_minimum_value(0);
|
||||
shadow_opacity->set_double_maximum_value(100);
|
||||
|
||||
@@ -78,13 +78,13 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
|
||||
outline_width->set_double_default_value(20);
|
||||
|
||||
outline_enable(false);
|
||||
shadow_enable(false);
|
||||
shadow_enable(false);
|
||||
|
||||
connect(shadow_bool, SIGNAL(toggled(bool)), this, SLOT(shadow_enable(bool)));
|
||||
connect(outline_bool, SIGNAL(toggled(bool)), this, SLOT(outline_enable(bool)));
|
||||
|
||||
vertPath = "common.vert";
|
||||
fragPath = "dropshadow.frag";
|
||||
vertPath = "common.vert";
|
||||
fragPath = "dropshadow.frag";
|
||||
}
|
||||
|
||||
void TextEffect::redraw(double timecode) {
|
||||
@@ -170,7 +170,7 @@ void TextEffect::redraw(double timecode) {
|
||||
}
|
||||
|
||||
path.addText(text_x, text_y, font, lines.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// draw outline
|
||||
int outline_width_val = outline_width->get_double_value(timecode);
|
||||
@@ -185,7 +185,7 @@ void TextEffect::redraw(double timecode) {
|
||||
// draw "master" text
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(set_color_button->get_color_value(timecode));
|
||||
p.drawPath(path);
|
||||
p.drawPath(path);
|
||||
}
|
||||
|
||||
void TextEffect::shadow_enable(bool e) {
|
||||
|
||||
@@ -24,82 +24,82 @@
|
||||
#include "playback/playback.h"
|
||||
|
||||
TimecodeEffect::TimecodeEffect(Clip *c, const EffectMeta* em) :
|
||||
Effect(c, em)
|
||||
Effect(c, em)
|
||||
{
|
||||
enable_always_update = true;
|
||||
enable_always_update = true;
|
||||
enable_superimpose = true;
|
||||
|
||||
EffectRow* tc_row = add_row("Timecode:");
|
||||
tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector");
|
||||
tc_select->add_combo_item("Sequence", true);
|
||||
tc_select->add_combo_item("Media", false);
|
||||
tc_select->set_combo_index(0);
|
||||
EffectRow* tc_row = add_row("Timecode");
|
||||
tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector");
|
||||
tc_select->add_combo_item("Sequence", true);
|
||||
tc_select->add_combo_item("Media", false);
|
||||
tc_select->set_combo_index(0);
|
||||
|
||||
scale_val = add_row("Scale:")->add_field(EFFECT_FIELD_DOUBLE, "scale", 2);
|
||||
scale_val->set_double_minimum_value(1);
|
||||
scale_val->set_double_default_value(100);
|
||||
scale_val->set_double_maximum_value(1000);
|
||||
scale_val = add_row("Scale")->add_field(EFFECT_FIELD_DOUBLE, "scale", 2);
|
||||
scale_val->set_double_minimum_value(1);
|
||||
scale_val->set_double_default_value(100);
|
||||
scale_val->set_double_maximum_value(1000);
|
||||
|
||||
color_val = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
color_val->set_color_value(Qt::white);
|
||||
color_val = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color", 2);
|
||||
color_val->set_color_value(Qt::white);
|
||||
|
||||
color_bg_val = add_row("Background Color:")->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2);
|
||||
color_bg_val->set_color_value(Qt::black);
|
||||
color_bg_val = add_row("Background Color")->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2);
|
||||
color_bg_val->set_color_value(Qt::black);
|
||||
|
||||
bg_alpha = add_row("Background Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "bgalpha", 2);
|
||||
bg_alpha->set_double_minimum_value(0);
|
||||
bg_alpha->set_double_maximum_value(100);
|
||||
bg_alpha->set_double_default_value(50);
|
||||
bg_alpha = add_row("Background Opacity")->add_field(EFFECT_FIELD_DOUBLE, "bgalpha", 2);
|
||||
bg_alpha->set_double_minimum_value(0);
|
||||
bg_alpha->set_double_maximum_value(100);
|
||||
bg_alpha->set_double_default_value(50);
|
||||
|
||||
EffectRow* offset = add_row("Offset:");
|
||||
offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx");
|
||||
offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety");
|
||||
EffectRow* offset = add_row("Offset");
|
||||
offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx");
|
||||
offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety");
|
||||
|
||||
prepend_text = add_row("Prepend:")->add_field(EFFECT_FIELD_STRING, "prepend", 2);
|
||||
prepend_text = add_row("Prepend")->add_field(EFFECT_FIELD_STRING, "prepend", 2);
|
||||
}
|
||||
|
||||
|
||||
void TimecodeEffect::redraw(double timecode) {
|
||||
if (tc_select->get_combo_data(timecode).toBool()){
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(sequence->playhead, config.timecode_view, sequence->frame_rate);}
|
||||
else {
|
||||
double media_rate = parent_clip->getMediaFrameRate();
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, config.timecode_view, media_rate);}
|
||||
img.fill(Qt::transparent);
|
||||
if (tc_select->get_combo_data(timecode).toBool()){
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(sequence->playhead, config.timecode_view, sequence->frame_rate);}
|
||||
else {
|
||||
double media_rate = parent_clip->getMediaFrameRate();
|
||||
display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, config.timecode_view, media_rate);}
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
QPainter p(&img);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
int width = img.width();
|
||||
int height = img.height();
|
||||
|
||||
// set font
|
||||
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
|
||||
font.setFamily("Helvetica");
|
||||
font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10)));
|
||||
p.setFont(font);
|
||||
font.setFamily("Helvetica");
|
||||
font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10)));
|
||||
p.setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
|
||||
QPainterPath path;
|
||||
QPainterPath path;
|
||||
|
||||
int text_x, text_y, rect_y, offset_x, offset_y;
|
||||
int text_height = fm.height();
|
||||
int text_width = fm.width(display_timecode);
|
||||
QColor background_color = color_bg_val->get_color_value(timecode);
|
||||
int alpha_val = bg_alpha->get_double_value(timecode)*2.55;
|
||||
background_color.setAlpha(alpha_val);
|
||||
int text_x, text_y, rect_y, offset_x, offset_y;
|
||||
int text_height = fm.height();
|
||||
int text_width = fm.width(display_timecode);
|
||||
QColor background_color = color_bg_val->get_color_value(timecode);
|
||||
int alpha_val = bg_alpha->get_double_value(timecode)*2.55;
|
||||
background_color.setAlpha(alpha_val);
|
||||
|
||||
offset_x = int(offset_x_val->get_double_value(timecode));
|
||||
offset_y = int(offset_y_val->get_double_value(timecode));
|
||||
offset_x = int(offset_x_val->get_double_value(timecode));
|
||||
offset_y = int(offset_y_val->get_double_value(timecode));
|
||||
|
||||
text_x = offset_x + (width/2) - (text_width/2);
|
||||
text_y = offset_y + height - height/10;
|
||||
rect_y = text_y + fm.descent()/2 - text_height;
|
||||
text_x = offset_x + (width/2) - (text_width/2);
|
||||
text_y = offset_y + height - height/10;
|
||||
rect_y = text_y + fm.descent()/2 - text_height;
|
||||
|
||||
path.addText(text_x, text_y, font, display_timecode);
|
||||
path.addText(text_x, text_y, font, display_timecode);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(background_color);
|
||||
p.drawRect(QRect(text_x-fm.descent()/2, rect_y, text_width+fm.descent(), text_height));
|
||||
p.setBrush(color_val->get_color_value(timecode));
|
||||
p.drawPath(path);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(background_color);
|
||||
p.drawRect(QRect(text_x-fm.descent()/2, rect_y, text_width+fm.descent(), text_height));
|
||||
p.setBrush(color_val->get_color_value(timecode));
|
||||
p.drawPath(path);
|
||||
}
|
||||
|
||||
@@ -9,21 +9,21 @@
|
||||
#include "debug.h"
|
||||
|
||||
ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_MIN) {
|
||||
type_val = add_row("Type:")->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
type_val = add_row("Type")->add_field(EFFECT_FIELD_COMBO, "type");
|
||||
type_val->add_combo_item("Sine", TONE_TYPE_SINE);
|
||||
|
||||
freq_val = add_row("Frequency:")->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
freq_val = add_row("Frequency")->add_field(EFFECT_FIELD_DOUBLE, "frequency");
|
||||
freq_val->set_double_minimum_value(20);
|
||||
freq_val->set_double_maximum_value(20000);
|
||||
freq_val->set_double_default_value(1000);
|
||||
|
||||
amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE, "amount");
|
||||
amount_val = add_row("Amount")->add_field(EFFECT_FIELD_DOUBLE, "amount");
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
amount_val->set_double_default_value(25);
|
||||
|
||||
mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val->set_bool_value(true);
|
||||
mix_val = add_row("Mix")->add_field(EFFECT_FIELD_BOOL, "mix");
|
||||
mix_val->set_bool_value(true);
|
||||
}
|
||||
|
||||
void ToneEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) {
|
||||
|
||||
@@ -28,147 +28,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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -1,15 +1,54 @@
|
||||
#include "math.h"
|
||||
|
||||
#include <QtMath>
|
||||
#include <QVector>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
int lerp(int a, int b, double t) {
|
||||
return qRound(((1.0 - t) * a) + (t * b));
|
||||
return qRound(((1.0 - t) * a) + (t * b));
|
||||
}
|
||||
|
||||
float float_lerp(float a, float b, float t) {
|
||||
return ((1.0F - t) * a) + (t * b);
|
||||
return ((1.0F - t) * a) + (t * b);
|
||||
}
|
||||
|
||||
double double_lerp(double a, double b, double t) {
|
||||
return ((1.0 - t) * a) + (t * b);
|
||||
return ((1.0 - t) * a) + (t * b);
|
||||
}
|
||||
|
||||
double quad_from_t(double a, double b, double c, double t) {
|
||||
return qPow(1.0 - t, 2)*a + 2*(1.0 - t)*t*b + qPow(t, 2)*c;
|
||||
}
|
||||
|
||||
double quad_t_from_x(double x, double a, double b, double c) {
|
||||
return (a - b + qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c);
|
||||
// alt: return (a - b - qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c);
|
||||
}
|
||||
|
||||
double cubic_from_t(double a, double b, double c, double d, double t) {
|
||||
return qPow(1.0 - t, 3)*a + 3*qPow(1.0 - t, 2)*t*b + 3*(1.0 - t)*qPow(t, 2)*c + qPow(t, 3)*d;
|
||||
}
|
||||
|
||||
double cubic_t_from_x(double x_target, double a, double b, double c, double d) {
|
||||
double tolerance = 0.0001;
|
||||
|
||||
double lower = 0.0;
|
||||
double upper = 1.0;
|
||||
|
||||
double percent = 0.5;
|
||||
double x = cubic_from_t(a, b, c, d, percent);
|
||||
|
||||
while (qAbs(x_target - x) > tolerance) {
|
||||
if (x_target > x) {
|
||||
lower = percent;
|
||||
} else {
|
||||
upper = percent;
|
||||
}
|
||||
|
||||
percent = (upper + lower) / 2.0;
|
||||
x = cubic_from_t(a, b, c, d, percent);
|
||||
}
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
@@ -4,5 +4,10 @@
|
||||
int lerp(int a, int b, double t);
|
||||
float float_lerp(float a, float b, float t);
|
||||
double double_lerp(double a, double b, double t);
|
||||
double quad_from_t(double a, double b, double c, double t);
|
||||
double quad_t_from_x(double x, double a, double b, double c);
|
||||
double cubic_from_t(double a, double b, double c, double d, double t);
|
||||
double cubic_t_from_x(double x_target, double a, double b, double c, double d);
|
||||
double solveCubicBezier(double p0, double p1, double p2, double p3, double x);
|
||||
|
||||
#endif // MATH_H
|
||||
|
||||
+78
-71
@@ -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();
|
||||
|
||||
@@ -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
@@ -0,0 +1,973 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>653</width>
|
||||
<height>394</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>653</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_File">
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_New">
|
||||
<property name="title">
|
||||
<string>&New</string>
|
||||
</property>
|
||||
<addaction name="actionProject"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSequence"/>
|
||||
<addaction name="actionFolder"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuOpen_Recent">
|
||||
<property name="title">
|
||||
<string>Open Recent</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menu_New"/>
|
||||
<addaction name="action_Open_Project"/>
|
||||
<addaction name="menuOpen_Recent"/>
|
||||
<addaction name="action_Save_Project"/>
|
||||
<addaction name="actionSave_Project_As"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Import"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExport"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEdit">
|
||||
<property name="title">
|
||||
<string>&Edit</string>
|
||||
</property>
|
||||
<addaction name="action_Undo"/>
|
||||
<addaction name="action_Redo"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCu_t"/>
|
||||
<addaction name="actionCop_y"/>
|
||||
<addaction name="action_Paste"/>
|
||||
<addaction name="actionPasteInsert"/>
|
||||
<addaction name="actionDuplicate"/>
|
||||
<addaction name="actionDelete"/>
|
||||
<addaction name="actionRipple_Delete"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSelect_All"/>
|
||||
<addaction name="actionDeselect_All"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionAdd_Default_Transition"/>
|
||||
<addaction name="actionLink_Unlink"/>
|
||||
<addaction name="actionRipple_To_In_Point"/>
|
||||
<addaction name="actionRipple_to_Out_Point"/>
|
||||
<addaction name="actionEdit_to_In_Point"/>
|
||||
<addaction name="actionEdit_to_Out_Point"/>
|
||||
<addaction name="actionSplit_at_Playhead"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSet_In_Point"/>
|
||||
<addaction name="actionSet_Out_Point"/>
|
||||
<addaction name="actionClear_In_Out"/>
|
||||
<addaction name="actionDelete_In_Out"/>
|
||||
<addaction name="actionRipple_Delete_In_Out"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSet_Edit_Marker"/>
|
||||
<addaction name="actionEnable_Disable_Clip"/>
|
||||
<addaction name="actionNest"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuWindow">
|
||||
<property name="title">
|
||||
<string>&Window</string>
|
||||
</property>
|
||||
<addaction name="actionProject_2"/>
|
||||
<addaction name="actionEffect_Controls"/>
|
||||
<addaction name="actionFootage_Viewer"/>
|
||||
<addaction name="actionViewer"/>
|
||||
<addaction name="actionTimeline"/>
|
||||
<addaction name="actionGraph_Editor"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionReset_to_default_layout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Help">
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_View">
|
||||
<property name="title">
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuTitle_Action_Safe_Area">
|
||||
<property name="title">
|
||||
<string>Title/Action Safe Area</string>
|
||||
</property>
|
||||
<addaction name="actionOff"/>
|
||||
<addaction name="actionDefault"/>
|
||||
<addaction name="action4_3"/>
|
||||
<addaction name="action16_9"/>
|
||||
<addaction name="actionCustom"/>
|
||||
</widget>
|
||||
<addaction name="actionTimeline_Track_Lines"/>
|
||||
<addaction name="actionZoom_In"/>
|
||||
<addaction name="actionZoom_out"/>
|
||||
<addaction name="actionIncrease_Track_Height"/>
|
||||
<addaction name="actionDecrease_Track_Height"/>
|
||||
<addaction name="actionToggle_Show_All"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionFrames"/>
|
||||
<addaction name="actionDrop_Frame"/>
|
||||
<addaction name="actionNon_Drop_Frame"/>
|
||||
<addaction name="actionMilliseconds"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuTitle_Action_Safe_Area"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionFull_Screen"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuPlayback">
|
||||
<property name="title">
|
||||
<string>&Playback</string>
|
||||
</property>
|
||||
<addaction name="actionGo_to_start"/>
|
||||
<addaction name="actionPrevious_Frame"/>
|
||||
<addaction name="actionPlay_Pause"/>
|
||||
<addaction name="actionNext_Frame"/>
|
||||
<addaction name="actionGo_to_End"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionGo_to_Previous_Cut"/>
|
||||
<addaction name="actionGo_to_Next_Cut"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Tools">
|
||||
<property name="title">
|
||||
<string>&Tools</string>
|
||||
</property>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPointer_Tool"/>
|
||||
<addaction name="actionEdit_Tool"/>
|
||||
<addaction name="actionRipple_Tool"/>
|
||||
<addaction name="actionRolling_Tool"/>
|
||||
<addaction name="actionRazor_Tool"/>
|
||||
<addaction name="actionSlip_Tool"/>
|
||||
<addaction name="actionSlide_Tool"/>
|
||||
<addaction name="actionHand_Tool"/>
|
||||
<addaction name="actionTransition_Tool"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionToggle_Snapping"/>
|
||||
<addaction name="actionSelecting_Also_Seeks"/>
|
||||
<addaction name="actionEdit_Tool_Also_Seeks"/>
|
||||
<addaction name="actionEdit_Tool_Selects_Links"/>
|
||||
<addaction name="actionSeek_to_the_End_of_Pastes"/>
|
||||
<addaction name="actionScroll_Wheel_Zooms"/>
|
||||
<addaction name="actionRectified_Waveforms"/>
|
||||
<addaction name="actionEnable_Drag_Files_to_Timeline"/>
|
||||
<addaction name="actionAuto_scale_by_Default"/>
|
||||
<addaction name="actionEnable_Seek_to_Import"/>
|
||||
<addaction name="actionAudio_Scrubbing"/>
|
||||
<addaction name="actionEnable_Drop_on_Media_to_Replace"/>
|
||||
<addaction name="actionEnable_Hover_Focus"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionNo_autoscroll"/>
|
||||
<addaction name="actionPage_Autoscroll"/>
|
||||
<addaction name="actionSmooth_Auto_scroll"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
<addaction name="actionCrash"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
<addaction name="menuEdit"/>
|
||||
<addaction name="menu_View"/>
|
||||
<addaction name="menuPlayback"/>
|
||||
<addaction name="menuWindow"/>
|
||||
<addaction name="menu_Tools"/>
|
||||
<addaction name="menu_Help"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<action name="action_Open_Project">
|
||||
<property name="text">
|
||||
<string>&Open Project</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Save_Project">
|
||||
<property name="text">
|
||||
<string>&Save Project</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSave_Project_As">
|
||||
<property name="text">
|
||||
<string>Save Project &As</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Import">
|
||||
<property name="text">
|
||||
<string>&Import...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport">
|
||||
<property name="text">
|
||||
<string>&Export...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+M</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="text">
|
||||
<string>E&xit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>About...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Undo">
|
||||
<property name="text">
|
||||
<string>&Undo</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Z</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Redo">
|
||||
<property name="text">
|
||||
<string>&Redo</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+Z</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCu_t">
|
||||
<property name="text">
|
||||
<string>Cu&t</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+X</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCop_y">
|
||||
<property name="text">
|
||||
<string>Cop&y</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+C</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Paste">
|
||||
<property name="text">
|
||||
<string>&Paste</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+V</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPasteInsert">
|
||||
<property name="text">
|
||||
<string>Paste Insert</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+V</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Del</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSelect_All">
|
||||
<property name="text">
|
||||
<string>Select &All</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTimeline_Track_Lines">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Track Lines</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionProject">
|
||||
<property name="text">
|
||||
<string>Project...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSequence">
|
||||
<property name="text">
|
||||
<string>Sequence...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZoom_In">
|
||||
<property name="text">
|
||||
<string>Zoom In</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>=</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZoom_out">
|
||||
<property name="text">
|
||||
<string>Zoom Out</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>-</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionProject_2">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Project</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEffect_Controls">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Effect Controls</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewer">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sequence Viewer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTimeline">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Timeline</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_Delete">
|
||||
<property name="text">
|
||||
<string>Ripple Delete</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Shift+Del</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSplit_at_Playhead">
|
||||
<property name="text">
|
||||
<string>Split at Playhead</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+K</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDeselect_All">
|
||||
<property name="text">
|
||||
<string>Deselect All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGo_to_start">
|
||||
<property name="text">
|
||||
<string>Go to Start</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Home</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReset_to_default_layout">
|
||||
<property name="text">
|
||||
<string>Reset to default layout</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPrevious_Frame">
|
||||
<property name="text">
|
||||
<string>Previous Frame</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Left</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPlay_Pause">
|
||||
<property name="text">
|
||||
<string>Play/Pause</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Space</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNext_Frame">
|
||||
<property name="text">
|
||||
<string>Next Frame</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Right</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGo_to_End">
|
||||
<property name="text">
|
||||
<string>Go to End</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>End</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCrash">
|
||||
<property name="text">
|
||||
<string>Crash</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPointer_Tool">
|
||||
<property name="text">
|
||||
<string>Pointer Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>V</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_Tool">
|
||||
<property name="text">
|
||||
<string>Edit Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>X</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRazor_Tool">
|
||||
<property name="text">
|
||||
<string>Razor Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>C</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_Tool">
|
||||
<property name="text">
|
||||
<string>Ripple Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>B</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRolling_Tool">
|
||||
<property name="text">
|
||||
<string>Rolling Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>N</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPreferences">
|
||||
<property name="text">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToggle_Snapping">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Snapping</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Snapping</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSlip_Tool">
|
||||
<property name="text">
|
||||
<string>Slip Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Y</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGo_to_Previous_Cut">
|
||||
<property name="text">
|
||||
<string>Go to Previous Cut</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Up</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGo_to_Next_Cut">
|
||||
<property name="text">
|
||||
<string>Go to Next Cut</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Down</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_Tool_Also_Seeks">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit Tool Also Seeks</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionIncrease_Track_Height">
|
||||
<property name="text">
|
||||
<string>Increase Track Height</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+=</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDecrease_Track_Height">
|
||||
<property name="text">
|
||||
<string>Decrease Track Height</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+-</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFrames">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Frames</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDrop_Frame">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Drop-Frame</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNon_Drop_Frame">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Non-Drop Frame</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_Tool_Selects_Links">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit Tool Selects Links</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDuplicate">
|
||||
<property name="text">
|
||||
<string>Duplicate</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSelecting_Also_Seeks">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Selecting Also Seeks</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSeek_to_the_End_of_Pastes">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Seek to the End of Pastes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_Default_Transition">
|
||||
<property name="text">
|
||||
<string>Add Default Transition</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSlide_Tool">
|
||||
<property name="text">
|
||||
<string>Slide Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>U</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFolder">
|
||||
<property name="text">
|
||||
<string>Folder</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClear_Recent_List">
|
||||
<property name="text">
|
||||
<string>Clear Recent List</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionScroll_Wheel_Zooms">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Scroll Wheel Zooms</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLink_Unlink">
|
||||
<property name="text">
|
||||
<string>Link/Unlink</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+L</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_To_In_Point">
|
||||
<property name="text">
|
||||
<string>Ripple to In Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_to_Out_Point">
|
||||
<property name="text">
|
||||
<string>Ripple to Out Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>W</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_In_Point">
|
||||
<property name="text">
|
||||
<string>Set In Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_Out_Point">
|
||||
<property name="text">
|
||||
<string>Set Out Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClear_In_Out">
|
||||
<property name="text">
|
||||
<string>Clear In/Out</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>G</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete_In_Out">
|
||||
<property name="text">
|
||||
<string>Delete In/Out</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>;</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRipple_Delete_In_Out">
|
||||
<property name="text">
|
||||
<string>Ripple Delete In/Out</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>'</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRectified_Waveforms">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rectified Waveforms</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDefault">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOff">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action4_3">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>4:3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action16_9">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>16:9</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCustom">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Drag_Files_to_Timeline">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Drag Files to Timeline</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAuto_scale_by_Default">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto-scale by Default</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSet_Edit_Marker">
|
||||
<property name="text">
|
||||
<string>Set/Edit Marker</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>M</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Disable_Clip">
|
||||
<property name="text">
|
||||
<string>Enable/Disable Clip</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Shift+E</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Seek_to_Import">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Seek to Import</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAudio_Scrubbing">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Audio Scrubbing</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTransition_Tool">
|
||||
<property name="text">
|
||||
<string>Transition Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>T</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_to_In_Point">
|
||||
<property name="text">
|
||||
<string>Edit to In Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Alt+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEdit_to_Out_Point">
|
||||
<property name="text">
|
||||
<string>Edit to Out Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Alt+W</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNest">
|
||||
<property name="text">
|
||||
<string>Nest</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToggle_Show_All">
|
||||
<property name="text">
|
||||
<string>Toggle Show All</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>\</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Drop_on_Media_to_Replace">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Drop File on Media to Replace</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFootage_Viewer">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Footage Viewer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNo_autoscroll">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No Auto-scroll</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPage_Autoscroll">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Page Auto-scroll</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSmooth_Auto_scroll">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Smooth Auto-scroll</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMilliseconds">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Milliseconds</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnable_Hover_Focus">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Hover Focus for Zooming</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFull_Screen">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Full Screen</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F11</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHand_Tool">
|
||||
<property name="text">
|
||||
<string>Hand Tool</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>H</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGraph_Editor">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Graph Editor</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -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 +=
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "panels/project.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "panels/grapheditor.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "io/clipboard.h"
|
||||
#include "ui/timelineheader.h"
|
||||
@@ -36,6 +37,8 @@ EffectControls::EffectControls(QWidget *parent) :
|
||||
panel_name("Effects: "),
|
||||
mode(TA_NO_TRANSITION)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
setup_ui();
|
||||
|
||||
init_effects();
|
||||
@@ -210,6 +213,9 @@ void EffectControls::clear_effects(bool clear_cache) {
|
||||
// clear existing clips
|
||||
deselect_all_effects(NULL);
|
||||
|
||||
// clear graph editor
|
||||
if (panel_graph_editor != NULL) panel_graph_editor->set_row(NULL);
|
||||
|
||||
QVBoxLayout* video_layout = static_cast<QVBoxLayout*>(video_effect_area->layout());
|
||||
QVBoxLayout* audio_layout = static_cast<QVBoxLayout*>(audio_effect_area->layout());
|
||||
QLayoutItem* item;
|
||||
@@ -249,8 +255,6 @@ void EffectControls::open_effect(QVBoxLayout* layout, Effect* e) {
|
||||
}
|
||||
|
||||
void EffectControls::setup_ui() {
|
||||
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
|
||||
QWidget* contents = new QWidget();
|
||||
|
||||
QHBoxLayout* layout = new QHBoxLayout(contents);
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
#include "grapheditor.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVariant>
|
||||
|
||||
#include "ui/keyframenavigator.h"
|
||||
#include "ui/timelineheader.h"
|
||||
#include "ui/timelinetools.h"
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/graphview.h"
|
||||
#include "project/effect.h"
|
||||
#include "project/effectfield.h"
|
||||
#include "project/effectrow.h"
|
||||
#include "project/clip.h"
|
||||
#include "panels.h"
|
||||
#include "debug.h"
|
||||
|
||||
GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) {
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
setWindowTitle("Graph Editor");
|
||||
resize(720, 480);
|
||||
|
||||
QWidget* main_widget = new QWidget();
|
||||
setWidget(main_widget);
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
main_widget->setLayout(layout);
|
||||
|
||||
QWidget* tool_widget = new QWidget();
|
||||
tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
QHBoxLayout* tools = new QHBoxLayout();
|
||||
tool_widget->setLayout(tools);
|
||||
|
||||
QWidget* left_tool_widget = new QWidget();
|
||||
QHBoxLayout* left_tool_layout = new QHBoxLayout();
|
||||
left_tool_layout->setSpacing(0);
|
||||
left_tool_layout->setMargin(0);
|
||||
left_tool_widget->setLayout(left_tool_layout);
|
||||
tools->addWidget(left_tool_widget);
|
||||
QWidget* center_tool_widget = new QWidget();
|
||||
QHBoxLayout* center_tool_layout = new QHBoxLayout();
|
||||
center_tool_layout->setSpacing(0);
|
||||
center_tool_layout->setMargin(0);
|
||||
center_tool_widget->setLayout(center_tool_layout);
|
||||
tools->addWidget(center_tool_widget);
|
||||
QWidget* right_tool_widget = new QWidget();
|
||||
QHBoxLayout* right_tool_layout = new QHBoxLayout();
|
||||
right_tool_layout->setSpacing(0);
|
||||
right_tool_layout->setMargin(0);
|
||||
right_tool_widget->setLayout(right_tool_layout);
|
||||
tools->addWidget(right_tool_widget);
|
||||
|
||||
keyframe_nav = new KeyframeNavigator();
|
||||
keyframe_nav->enable_keyframes(true);
|
||||
keyframe_nav->enable_keyframe_toggle(false);
|
||||
left_tool_layout->addWidget(keyframe_nav);
|
||||
left_tool_layout->addStretch();
|
||||
|
||||
linear_button = new QPushButton("Linear");
|
||||
linear_button->setProperty("type", KEYFRAME_TYPE_LINEAR);
|
||||
linear_button->setCheckable(true);
|
||||
bezier_button = new QPushButton("Bezier");
|
||||
bezier_button->setProperty("type", KEYFRAME_TYPE_BEZIER);
|
||||
bezier_button->setCheckable(true);
|
||||
hold_button = new QPushButton("Hold");
|
||||
hold_button->setProperty("type", KEYFRAME_TYPE_HOLD);
|
||||
hold_button->setCheckable(true);
|
||||
|
||||
center_tool_layout->addStretch();
|
||||
center_tool_layout->addWidget(linear_button);
|
||||
center_tool_layout->addWidget(bezier_button);
|
||||
center_tool_layout->addWidget(hold_button);
|
||||
|
||||
layout->addWidget(tool_widget);
|
||||
|
||||
QWidget* central_widget = new QWidget();
|
||||
QVBoxLayout* central_layout = new QVBoxLayout();
|
||||
central_widget->setLayout(central_layout);
|
||||
central_layout->setSpacing(0);
|
||||
central_layout->setMargin(0);
|
||||
header = new TimelineHeader();
|
||||
header->viewer = panel_sequence_viewer;
|
||||
central_layout->addWidget(header);
|
||||
view = new GraphView();
|
||||
central_layout->addWidget(view);
|
||||
|
||||
layout->addWidget(central_widget);
|
||||
|
||||
QWidget* value_widget = new QWidget();
|
||||
value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
QHBoxLayout* values = new QHBoxLayout();
|
||||
value_widget->setLayout(values);
|
||||
values->addStretch();
|
||||
|
||||
QWidget* central_value_widget = new QWidget();
|
||||
value_layout = new QHBoxLayout();
|
||||
value_layout->setMargin(0);
|
||||
value_layout->addWidget(new QLabel("")); // a spacer so the layout doesn't jump
|
||||
central_value_widget->setLayout(value_layout);
|
||||
values->addWidget(central_value_widget);
|
||||
|
||||
values->addStretch();
|
||||
layout->addWidget(value_widget);
|
||||
|
||||
current_row_desc = new QLabel();
|
||||
current_row_desc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
current_row_desc->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(current_row_desc);
|
||||
|
||||
connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double)));
|
||||
connect(view, SIGNAL(x_scroll_changed(int)), header, SLOT(set_scroll(int)));
|
||||
connect(view, SIGNAL(selection_changed(bool, int)), this, SLOT(set_key_button_enabled(bool, int)));
|
||||
|
||||
connect(linear_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type()));
|
||||
connect(bezier_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type()));
|
||||
connect(hold_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type()));
|
||||
}
|
||||
|
||||
void GraphEditor::update_panel() {
|
||||
if (isVisible()) {
|
||||
if (row != NULL) {
|
||||
int slider_index = 0;
|
||||
for (int i=0;i<row->fieldCount();i++) {
|
||||
EffectField* field = row->field(i);
|
||||
if (field->type == EFFECT_FIELD_DOUBLE) {
|
||||
slider_proxies.at(slider_index)->set_value(row->field(i)->get_current_data().toDouble(), false);
|
||||
slider_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header->update();
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEditor::set_row(EffectRow *r) {
|
||||
for (int i=0;i<slider_proxies.size();i++) {
|
||||
delete slider_proxies.at(i);
|
||||
delete slider_proxy_buttons.at(i);
|
||||
}
|
||||
slider_proxies.clear();
|
||||
slider_proxy_buttons.clear();
|
||||
slider_proxy_sources.clear();
|
||||
|
||||
if (row != NULL) {
|
||||
// clear old row connections
|
||||
disconnect(keyframe_nav, SIGNAL(goto_previous_key()), row, SLOT(goto_previous_key()));
|
||||
disconnect(keyframe_nav, SIGNAL(toggle_key()), row, SLOT(toggle_key()));
|
||||
disconnect(keyframe_nav, SIGNAL(goto_next_key()), row, SLOT(goto_next_key()));
|
||||
}
|
||||
|
||||
bool found_vals = false;
|
||||
|
||||
if (r != NULL && r->isKeyframing()) {
|
||||
for (int i=0;i<r->fieldCount();i++) {
|
||||
EffectField* field = r->field(i);
|
||||
if (field->type == EFFECT_FIELD_DOUBLE) {
|
||||
QPushButton* slider_button = new QPushButton();
|
||||
slider_button->setCheckable(true);
|
||||
slider_button->setChecked(true);
|
||||
slider_button->setIcon(QIcon(":/icons/record.png"));
|
||||
slider_button->setProperty("field", i);
|
||||
slider_button->setIconSize(QSize(8, 8));
|
||||
slider_button->setMaximumSize(QSize(12, 12));
|
||||
connect(slider_button, SIGNAL(toggled(bool)), this, SLOT(set_field_visibility(bool)));
|
||||
slider_proxy_buttons.append(slider_button);
|
||||
value_layout->addWidget(slider_button);
|
||||
|
||||
LabelSlider* slider = new LabelSlider();
|
||||
slider->set_color(get_curve_color(i, r->fieldCount()).name());
|
||||
connect(slider, SIGNAL(valueChanged()), this, SLOT(passthrough_slider_value()));
|
||||
slider_proxies.append(slider);
|
||||
value_layout->addWidget(slider);
|
||||
|
||||
slider_proxy_sources.append(static_cast<LabelSlider*>(field->ui_element));
|
||||
|
||||
found_vals = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_vals) {
|
||||
row = r;
|
||||
current_row_desc->setText(row->parent_effect->parent_clip->name + " :: " + row->parent_effect->meta->name + " :: " + row->get_name());
|
||||
header->set_visible_in(r->parent_effect->parent_clip->timeline_in);
|
||||
|
||||
connect(keyframe_nav, SIGNAL(goto_previous_key()), row, SLOT(goto_previous_key()));
|
||||
connect(keyframe_nav, SIGNAL(toggle_key()), row, SLOT(toggle_key()));
|
||||
connect(keyframe_nav, SIGNAL(goto_next_key()), row, SLOT(goto_next_key()));
|
||||
} else {
|
||||
row = NULL;
|
||||
current_row_desc->setText(0);
|
||||
}
|
||||
view->set_row(row);
|
||||
update_panel();
|
||||
}
|
||||
|
||||
bool GraphEditor::view_is_focused() {
|
||||
return view->hasFocus() || header->hasFocus();
|
||||
}
|
||||
|
||||
void GraphEditor::delete_selected_keys() {
|
||||
view->delete_selected_keys();
|
||||
}
|
||||
|
||||
void GraphEditor::select_all() {
|
||||
view->select_all();
|
||||
}
|
||||
|
||||
void GraphEditor::set_key_button_enabled(bool e, int type) {
|
||||
linear_button->setEnabled(e);
|
||||
linear_button->setChecked(type == KEYFRAME_TYPE_LINEAR);
|
||||
bezier_button->setEnabled(e);
|
||||
bezier_button->setChecked(type == KEYFRAME_TYPE_BEZIER);
|
||||
hold_button->setEnabled(e);
|
||||
hold_button->setChecked(type == KEYFRAME_TYPE_HOLD);
|
||||
}
|
||||
|
||||
void GraphEditor::passthrough_slider_value() {
|
||||
for (int i=0;i<slider_proxies.size();i++) {
|
||||
if (slider_proxies.at(i) == sender()) {
|
||||
slider_proxy_sources.at(i)->set_value(slider_proxies.at(i)->value(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEditor::set_keyframe_type() {
|
||||
linear_button->setChecked(linear_button == sender());
|
||||
bezier_button->setChecked(bezier_button == sender());
|
||||
hold_button->setChecked(hold_button == sender());
|
||||
view->set_selected_keyframe_type(sender()->property("type").toInt());
|
||||
}
|
||||
|
||||
void GraphEditor::set_field_visibility(bool b) {
|
||||
view->set_field_visibility(sender()->property("field").toInt(), b);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef GRAPHEDITOR_H
|
||||
#define GRAPHEDITOR_H
|
||||
|
||||
#include <QDockWidget>
|
||||
|
||||
class GraphView;
|
||||
class TimelineHeader;
|
||||
class QPushButton;
|
||||
class EffectRow;
|
||||
class QHBoxLayout;
|
||||
class LabelSlider;
|
||||
class QLabel;
|
||||
class KeyframeNavigator;
|
||||
|
||||
class GraphEditor : public QDockWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GraphEditor(QWidget* parent = 0);
|
||||
void update_panel();
|
||||
void set_row(EffectRow* r);
|
||||
bool view_is_focused();
|
||||
void delete_selected_keys();
|
||||
void select_all();
|
||||
private:
|
||||
GraphView* view;
|
||||
TimelineHeader* header;
|
||||
QHBoxLayout* value_layout;
|
||||
QVector<LabelSlider*> slider_proxies;
|
||||
QVector<QPushButton*> slider_proxy_buttons;
|
||||
QVector<LabelSlider*> slider_proxy_sources;
|
||||
QLabel* current_row_desc;
|
||||
EffectRow* row;
|
||||
KeyframeNavigator* keyframe_nav;
|
||||
QPushButton* linear_button;
|
||||
QPushButton* bezier_button;
|
||||
QPushButton* hold_button;
|
||||
private slots:
|
||||
void set_key_button_enabled(bool e, int type);
|
||||
void passthrough_slider_value();
|
||||
void set_keyframe_type();
|
||||
void set_field_visibility(bool b);
|
||||
};
|
||||
|
||||
#endif // GRAPHEDITOR_H
|
||||
+37
-5
@@ -8,6 +8,7 @@
|
||||
#include "project/clip.h"
|
||||
#include "project/transition.h"
|
||||
#include "io/config.h"
|
||||
#include "grapheditor.h"
|
||||
#include "debug.h"
|
||||
|
||||
Project* panel_project = 0;
|
||||
@@ -15,6 +16,7 @@ EffectControls* panel_effect_controls = 0;
|
||||
Viewer* panel_sequence_viewer = 0;
|
||||
Viewer* panel_footage_viewer = 0;
|
||||
Timeline* panel_timeline = 0;
|
||||
GraphEditor* panel_graph_editor = 0;
|
||||
|
||||
void update_effect_controls() {
|
||||
// SEND CLIPS TO EFFECT CONTROLS
|
||||
@@ -105,20 +107,21 @@ void update_ui(bool modified) {
|
||||
panel_effect_controls->update_keyframes();
|
||||
panel_timeline->repaint_timeline();
|
||||
panel_sequence_viewer->update_viewer();
|
||||
panel_graph_editor->update_panel();
|
||||
}
|
||||
|
||||
QDockWidget *get_focused_panel() {
|
||||
QDockWidget* w = NULL;
|
||||
if (config.hover_focus) {
|
||||
if (panel_project->rect().contains(panel_project->mapFromGlobal(QCursor::pos()))) {
|
||||
if (panel_project->underMouse()) {
|
||||
w = panel_project;
|
||||
} else if (panel_effect_controls->rect().contains(panel_effect_controls->mapFromGlobal(QCursor::pos()))) {
|
||||
} else if (panel_effect_controls->underMouse()) {
|
||||
w = panel_effect_controls;
|
||||
} else if (panel_sequence_viewer->rect().contains(panel_sequence_viewer->mapFromGlobal(QCursor::pos()))) {
|
||||
} else if (panel_sequence_viewer->underMouse()) {
|
||||
w = panel_sequence_viewer;
|
||||
} else if (panel_footage_viewer->rect().contains(panel_footage_viewer->mapFromGlobal(QCursor::pos()))) {
|
||||
} else if (panel_footage_viewer->underMouse()) {
|
||||
w = panel_footage_viewer;
|
||||
} else if (panel_timeline->rect().contains(panel_timeline->mapFromGlobal(QCursor::pos()))) {
|
||||
} else if (panel_timeline->underMouse()) {
|
||||
w = panel_timeline;
|
||||
}
|
||||
}
|
||||
@@ -137,3 +140,32 @@ QDockWidget *get_focused_panel() {
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
void alloc_panels(QWidget* parent) {
|
||||
// TODO maybe replace these with non-pointers later on?
|
||||
panel_sequence_viewer = new Viewer(parent);
|
||||
panel_sequence_viewer->setObjectName("seq_viewer");
|
||||
panel_footage_viewer = new Viewer(parent);
|
||||
panel_footage_viewer->setObjectName("footage_viewer");
|
||||
panel_project = new Project(parent);
|
||||
panel_project->setObjectName("proj_root");
|
||||
panel_effect_controls = new EffectControls(parent);
|
||||
panel_effect_controls->setObjectName("fx_controls");
|
||||
panel_timeline = new Timeline(parent);
|
||||
panel_timeline->setObjectName("timeline");
|
||||
panel_graph_editor = new GraphEditor(parent);
|
||||
panel_graph_editor->setObjectName("graph_editor");
|
||||
}
|
||||
|
||||
void free_panels() {
|
||||
delete panel_sequence_viewer;
|
||||
panel_sequence_viewer = NULL;
|
||||
delete panel_footage_viewer;
|
||||
panel_footage_viewer = NULL;
|
||||
delete panel_project;
|
||||
panel_project = NULL;
|
||||
delete panel_effect_controls;
|
||||
panel_effect_controls = NULL;
|
||||
delete panel_timeline;
|
||||
panel_timeline = NULL;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ class Project;
|
||||
class EffectControls;
|
||||
class Viewer;
|
||||
class Timeline;
|
||||
class GraphEditor;
|
||||
|
||||
class QWidget;
|
||||
class QDockWidget;
|
||||
|
||||
extern Project* panel_project;
|
||||
@@ -12,8 +15,11 @@ extern EffectControls* panel_effect_controls;
|
||||
extern Viewer* panel_sequence_viewer;
|
||||
extern Viewer* panel_footage_viewer;
|
||||
extern Timeline* panel_timeline;
|
||||
extern GraphEditor* panel_graph_editor;
|
||||
|
||||
void update_ui(bool modified);
|
||||
QDockWidget* get_focused_panel();
|
||||
void alloc_panels(QWidget *parent);
|
||||
void free_panels();
|
||||
|
||||
#endif // PANELS_H
|
||||
|
||||
+4
-2
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -10,6 +10,7 @@
|
||||
#include "project/clip.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "panels/grapheditor.h"
|
||||
#include "ui/checkboxex.h"
|
||||
#include "debug.h"
|
||||
#include "io/path.h"
|
||||
@@ -51,14 +52,14 @@ Effect* create_effect(Clip* c, const EffectMeta* em) {
|
||||
switch (em->internal) {
|
||||
case EFFECT_INTERNAL_TRANSFORM: return new TransformEffect(c, em);
|
||||
case EFFECT_INTERNAL_TEXT: return new TextEffect(c, em);
|
||||
case EFFECT_INTERNAL_TIMECODE: return new TimecodeEffect(c, em);
|
||||
case EFFECT_INTERNAL_TIMECODE: return new TimecodeEffect(c, em);
|
||||
case EFFECT_INTERNAL_SOLID: return new SolidEffect(c, em);
|
||||
case EFFECT_INTERNAL_NOISE: return new AudioNoiseEffect(c, em);
|
||||
case EFFECT_INTERNAL_VOLUME: return new VolumeEffect(c, em);
|
||||
case EFFECT_INTERNAL_PAN: return new PanEffect(c, em);
|
||||
case EFFECT_INTERNAL_TONE: return new ToneEffect(c, em);
|
||||
case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em);
|
||||
case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em);
|
||||
case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em);
|
||||
case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em);
|
||||
}
|
||||
} else {
|
||||
dout << "[ERROR] Invalid effect data";
|
||||
@@ -68,12 +69,12 @@ Effect* create_effect(Clip* c, const EffectMeta* em) {
|
||||
}
|
||||
|
||||
const EffectMeta* get_internal_meta(int internal_id, int type) {
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
if (effects.at(i).internal == internal_id && effects.at(i).type == type) {
|
||||
return &effects.at(i);
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
if (effects.at(i).internal == internal_id && effects.at(i).type == type) {
|
||||
return &effects.at(i);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void load_internal_effects() {
|
||||
@@ -81,85 +82,90 @@ void load_internal_effects() {
|
||||
|
||||
// internal effects
|
||||
em.type = EFFECT_TYPE_EFFECT;
|
||||
em.subtype = EFFECT_TYPE_AUDIO;
|
||||
em.subtype = EFFECT_TYPE_AUDIO;
|
||||
|
||||
em.name = "Volume";
|
||||
em.internal = EFFECT_INTERNAL_VOLUME;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Pan";
|
||||
em.internal = EFFECT_INTERNAL_PAN;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Tone";
|
||||
em.internal = EFFECT_INTERNAL_TONE;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Noise";
|
||||
em.internal = EFFECT_INTERNAL_NOISE;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.subtype = EFFECT_TYPE_VIDEO;
|
||||
em.subtype = EFFECT_TYPE_VIDEO;
|
||||
|
||||
em.name = "Transform";
|
||||
em.category = "Distort";
|
||||
em.internal = EFFECT_INTERNAL_TRANSFORM;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Corner Pin";
|
||||
em.internal = EFFECT_INTERNAL_CORNERPIN;
|
||||
effects.append(em);
|
||||
em.name = "Corner Pin";
|
||||
em.internal = EFFECT_INTERNAL_CORNERPIN;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Mask";
|
||||
em.internal = EFFECT_INTERNAL_MASK;
|
||||
effects.append(em);
|
||||
em.name = "Mask";
|
||||
em.internal = EFFECT_INTERNAL_MASK;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Shake";
|
||||
em.internal = EFFECT_INTERNAL_SHAKE;
|
||||
effects.append(em);
|
||||
em.name = "Shake";
|
||||
em.internal = EFFECT_INTERNAL_SHAKE;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Text";
|
||||
em.category = "Render";
|
||||
em.internal = EFFECT_INTERNAL_TEXT;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Timecode";
|
||||
em.internal = EFFECT_INTERNAL_TIMECODE;
|
||||
effects.append(em);
|
||||
em.name = "Timecode";
|
||||
em.internal = EFFECT_INTERNAL_TIMECODE;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Solid";
|
||||
em.name = "Solid";
|
||||
em.internal = EFFECT_INTERNAL_SOLID;
|
||||
effects.append(em);
|
||||
effects.append(em);
|
||||
|
||||
// internal transitions
|
||||
em.type = EFFECT_TYPE_TRANSITION;
|
||||
em.category = "";
|
||||
|
||||
em.name = "Cross Dissolve";
|
||||
em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE;
|
||||
effects.append(em);
|
||||
em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE;
|
||||
effects.append(em);
|
||||
|
||||
em.subtype = EFFECT_TYPE_AUDIO;
|
||||
em.subtype = EFFECT_TYPE_AUDIO;
|
||||
|
||||
em.name = "Linear Fade";
|
||||
em.internal = TRANSITION_INTERNAL_LINEARFADE;
|
||||
effects.append(em);
|
||||
em.internal = TRANSITION_INTERNAL_LINEARFADE;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Exponential Fade";
|
||||
em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE;
|
||||
effects.append(em);
|
||||
em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE;
|
||||
effects.append(em);
|
||||
|
||||
em.name = "Logarithmic Fade";
|
||||
em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE;
|
||||
effects.append(em);
|
||||
em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE;
|
||||
effects.append(em);
|
||||
}
|
||||
|
||||
void load_shader_effects() {
|
||||
QList<QString> get_effects_paths() {
|
||||
QList<QString> effects_paths;
|
||||
effects_paths.append(get_app_dir() + "/effects");
|
||||
effects_paths.append(get_app_dir() + "/../share/olive-editor/effects");
|
||||
QString env_path(qgetenv("OLIVE_EFFECTS_PATH"));
|
||||
if (!env_path.isEmpty()) effects_paths.append(env_path);
|
||||
return effects_paths;
|
||||
}
|
||||
|
||||
void load_shader_effects() {
|
||||
QList<QString> effects_paths = get_effects_paths();
|
||||
|
||||
for (int h=0;h<effects_paths.size();h++) {
|
||||
const QString& effects_path = effects_paths.at(h);
|
||||
@@ -235,29 +241,29 @@ void EffectInit::run() {
|
||||
|
||||
Effect::Effect(Clip* c, const EffectMeta *em) :
|
||||
parent_clip(c),
|
||||
meta(em),
|
||||
meta(em),
|
||||
enable_shader(false),
|
||||
enable_coords(false),
|
||||
enable_superimpose(false),
|
||||
enable_image(false),
|
||||
enable_superimpose(false),
|
||||
enable_image(false),
|
||||
glslProgram(NULL),
|
||||
texture(NULL),
|
||||
isOpen(false),
|
||||
bound(false),
|
||||
enable_always_update(false)
|
||||
texture(NULL),
|
||||
isOpen(false),
|
||||
bound(false),
|
||||
enable_always_update(false)
|
||||
{
|
||||
// set up base UI
|
||||
container = new CollapsibleWidget();
|
||||
connect(container->enabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed()));
|
||||
ui = new QWidget();
|
||||
ui_layout = new QGridLayout();
|
||||
// set up base UI
|
||||
container = new CollapsibleWidget();
|
||||
connect(container->enabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed()));
|
||||
ui = new QWidget();
|
||||
ui_layout = new QGridLayout();
|
||||
ui_layout->setSpacing(4);
|
||||
ui->setLayout(ui_layout);
|
||||
ui->setLayout(ui_layout);
|
||||
container->setContents(ui);
|
||||
|
||||
connect(container->title_bar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&)));
|
||||
|
||||
// set up UI from effect file
|
||||
// set up UI from effect file
|
||||
container->setText(em->name);
|
||||
|
||||
if (!em->filename.isEmpty()) {
|
||||
@@ -276,7 +282,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) :
|
||||
}
|
||||
}
|
||||
if (!row_name.isEmpty()) {
|
||||
EffectRow* row = add_row(row_name + ":");
|
||||
EffectRow* row = add_row(row_name);
|
||||
while (!reader.atEnd() && !(reader.name() == "row" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "field" && reader.isStartElement()) {
|
||||
@@ -310,7 +316,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) :
|
||||
if (id.isEmpty()) {
|
||||
dout << "[ERROR] Couldn't load field from" << em->filename << "- ID cannot be empty.";
|
||||
} else if (type > -1) {
|
||||
EffectField* field = row->add_field(type, id);
|
||||
EffectField* field = row->add_field(type, id);
|
||||
connect(field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
@@ -446,9 +452,9 @@ Effect::~Effect() {
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
delete rows.at(i);
|
||||
}
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
delete gizmos.at(i);
|
||||
}
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
delete gizmos.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::copy_field_keyframes(Effect* e) {
|
||||
@@ -456,19 +462,17 @@ void Effect::copy_field_keyframes(Effect* e) {
|
||||
EffectRow* row = rows.at(i);
|
||||
EffectRow* copy_row = e->rows.at(i);
|
||||
copy_row->setKeyframing(row->isKeyframing());
|
||||
copy_row->keyframe_times = row->keyframe_times;
|
||||
copy_row->keyframe_types = row->keyframe_types;
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
copy_row->field(j)->set_current_data(field->get_current_data());
|
||||
copy_row->field(j)->keyframe_data = field->keyframe_data;
|
||||
EffectField* copy_field = copy_row->field(j);
|
||||
copy_field->keyframes = field->keyframes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EffectRow* Effect::add_row(const QString& name, bool savable) {
|
||||
EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size());
|
||||
rows.append(row);
|
||||
EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size());
|
||||
rows.append(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
@@ -477,52 +481,53 @@ EffectRow* Effect::row(int i) {
|
||||
}
|
||||
|
||||
int Effect::row_count() {
|
||||
return rows.size();
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
EffectGizmo *Effect::add_gizmo(int type) {
|
||||
EffectGizmo* gizmo = new EffectGizmo(type);
|
||||
gizmos.append(gizmo);
|
||||
return gizmo;
|
||||
EffectGizmo* gizmo = new EffectGizmo(type);
|
||||
gizmos.append(gizmo);
|
||||
return gizmo;
|
||||
}
|
||||
|
||||
EffectGizmo *Effect::gizmo(int i) {
|
||||
return gizmos.at(i);
|
||||
return gizmos.at(i);
|
||||
}
|
||||
|
||||
int Effect::gizmo_count(){
|
||||
return gizmos.size();
|
||||
return gizmos.size();
|
||||
}
|
||||
|
||||
void Effect::refresh() {}
|
||||
|
||||
void Effect::field_changed() {
|
||||
panel_sequence_viewer->viewer_widget->update();
|
||||
panel_sequence_viewer->viewer_widget->update();
|
||||
panel_graph_editor->update_panel();
|
||||
}
|
||||
|
||||
void Effect::show_context_menu(const QPoint& pos) {
|
||||
if (meta->type == EFFECT_TYPE_EFFECT) {
|
||||
QMenu menu(mainWindow);
|
||||
if (meta->type == EFFECT_TYPE_EFFECT) {
|
||||
QMenu menu(mainWindow);
|
||||
|
||||
int index = get_index_in_clip();
|
||||
int index = get_index_in_clip();
|
||||
|
||||
if (index > 0) {
|
||||
QAction* move_up = menu.addAction("Move &Up");
|
||||
connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up()));
|
||||
}
|
||||
if (index > 0) {
|
||||
QAction* move_up = menu.addAction("Move &Up");
|
||||
connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up()));
|
||||
}
|
||||
|
||||
if (index < parent_clip->effects.size() - 1) {
|
||||
QAction* move_down = menu.addAction("Move &Down");
|
||||
connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down()));
|
||||
}
|
||||
if (index < parent_clip->effects.size() - 1) {
|
||||
QAction* move_down = menu.addAction("Move &Down");
|
||||
connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down()));
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
menu.addSeparator();
|
||||
|
||||
QAction* del_action = menu.addAction("D&elete");
|
||||
connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self()));
|
||||
QAction* del_action = menu.addAction("D&elete");
|
||||
connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self()));
|
||||
|
||||
menu.exec(container->title_bar->mapToGlobal(pos));
|
||||
}
|
||||
menu.exec(container->title_bar->mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::delete_self() {
|
||||
@@ -572,24 +577,24 @@ void Effect::set_enabled(bool b) {
|
||||
|
||||
QVariant load_data_from_string(int type, const QString& string) {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return string.toDouble();
|
||||
case EFFECT_FIELD_COLOR: return QColor(string);
|
||||
case EFFECT_FIELD_STRING: return string;
|
||||
case EFFECT_FIELD_BOOL: return (string == "1");
|
||||
case EFFECT_FIELD_COMBO: return string.toInt();
|
||||
case EFFECT_FIELD_FONT: return string;
|
||||
case EFFECT_FIELD_DOUBLE: return string.toDouble();
|
||||
case EFFECT_FIELD_COLOR: return QColor(string);
|
||||
case EFFECT_FIELD_STRING: return string;
|
||||
case EFFECT_FIELD_BOOL: return (string == "1");
|
||||
case EFFECT_FIELD_COMBO: return string.toInt();
|
||||
case EFFECT_FIELD_FONT: return string;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QString save_data_to_string(int type, const QVariant& data) {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble());
|
||||
case EFFECT_FIELD_COLOR: return data.value<QColor>().name();
|
||||
case EFFECT_FIELD_STRING: return data.toString();
|
||||
case EFFECT_FIELD_BOOL: return QString::number(data.toBool());
|
||||
case EFFECT_FIELD_COMBO: return QString::number(data.toInt());
|
||||
case EFFECT_FIELD_FONT: return data.toString();
|
||||
case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble());
|
||||
case EFFECT_FIELD_COLOR: return data.value<QColor>().name();
|
||||
case EFFECT_FIELD_STRING: return data.toString();
|
||||
case EFFECT_FIELD_BOOL: return QString::number(data.toBool());
|
||||
case EFFECT_FIELD_COMBO: return QString::number(data.toInt());
|
||||
case EFFECT_FIELD_FONT: return data.toString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
@@ -610,7 +615,7 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
stream.readNext();
|
||||
|
||||
// read keyframes
|
||||
if (stream.name() == "keyframes" && stream.isStartElement()) {
|
||||
/*if (stream.name() == "keyframes" && stream.isStartElement()) {
|
||||
for (int k=0;k<stream.attributes().size();k++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(k);
|
||||
if (attr.name() == "enabled") {
|
||||
@@ -631,14 +636,19 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
keyframe_type = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
row->keyframe_times.append(keyframe_frame);
|
||||
row->keyframe_types.append(keyframe_type);
|
||||
for (int k=0;k<row->fieldCount();k++) {
|
||||
EffectField* field = row->field(k);
|
||||
EffectKeyframe key;
|
||||
key.time = keyframe_frame;
|
||||
key.type = keyframe_type;
|
||||
field->keyframes.append(key);
|
||||
}
|
||||
}
|
||||
stream.readNext();
|
||||
}
|
||||
}
|
||||
stream.readNext();
|
||||
}
|
||||
}*/
|
||||
|
||||
// read field
|
||||
if (stream.name() == "field" && stream.isStartElement()) {
|
||||
@@ -678,10 +688,30 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) {
|
||||
stream.readNext();
|
||||
|
||||
// read all keyframes
|
||||
// read keyframes
|
||||
if (stream.name() == "key" && stream.isStartElement()) {
|
||||
stream.readNext();
|
||||
field->keyframe_data.append(load_data_from_string(field->type, stream.text().toString()));
|
||||
row->setKeyframing(true);
|
||||
|
||||
EffectKeyframe key;
|
||||
for (int k=0;k<stream.attributes().size();k++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(k);
|
||||
if (attr.name() == "value") {
|
||||
key.data = load_data_from_string(field->type, attr.value().toString());
|
||||
} else if (attr.name() == "frame") {
|
||||
key.time = attr.value().toLong();
|
||||
} else if (attr.name() == "type") {
|
||||
key.type = attr.value().toInt();
|
||||
} else if (attr.name() == "prehx") {
|
||||
key.pre_handle_x = attr.value().toDouble();
|
||||
} else if (attr.name() == "prehy") {
|
||||
key.pre_handle_y = attr.value().toDouble();
|
||||
} else if (attr.name() == "posthx") {
|
||||
key.post_handle_x = attr.value().toDouble();
|
||||
} else if (attr.name() == "posthy") {
|
||||
key.post_handle_y = attr.value().toDouble();
|
||||
}
|
||||
}
|
||||
field->keyframes.append(key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -701,33 +731,54 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
|
||||
void Effect::save(QXmlStreamWriter& stream) {
|
||||
stream.writeAttribute("name", meta->name);
|
||||
stream.writeAttribute("enabled", QString::number(is_enabled()));
|
||||
stream.writeAttribute("enabled", QString::number(is_enabled()));
|
||||
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
EffectRow* row = rows.at(i);
|
||||
if (row->savable) {
|
||||
stream.writeStartElement("row"); // row
|
||||
stream.writeStartElement("keyframes"); // keyframes
|
||||
stream.writeAttribute("enabled", QString::number(row->isKeyframing()));
|
||||
for (int j=0;j<row->keyframe_times.size();j++) {
|
||||
stream.writeStartElement("key"); // key
|
||||
stream.writeAttribute("frame", QString::number(row->keyframe_times.at(j)));
|
||||
stream.writeAttribute("type", QString::number(row->keyframe_types.at(j)));
|
||||
stream.writeEndElement(); // key
|
||||
}
|
||||
stream.writeEndElement(); // keyframes
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
stream.writeStartElement("field"); // field
|
||||
stream.writeAttribute("id", field->id);
|
||||
stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data()));
|
||||
for (int k=0;k<field->keyframe_data.size();k++) {
|
||||
stream.writeTextElement("key", save_data_to_string(field->type, field->keyframe_data.at(k)));
|
||||
}
|
||||
stream.writeEndElement(); // field
|
||||
}
|
||||
stream.writeEndElement(); // row
|
||||
}
|
||||
if (row->savable) {
|
||||
stream.writeStartElement("row"); // row
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
stream.writeStartElement("field"); // field
|
||||
stream.writeAttribute("id", field->id);
|
||||
stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data()));
|
||||
for (int k=0;k<field->keyframes.size();k++) {
|
||||
const EffectKeyframe& key = field->keyframes.at(k);
|
||||
stream.writeStartElement("key");
|
||||
stream.writeAttribute("value", save_data_to_string(field->type, key.data));
|
||||
stream.writeAttribute("frame", QString::number(key.time));
|
||||
stream.writeAttribute("type", QString::number(key.type));
|
||||
stream.writeAttribute("prehx", QString::number(key.pre_handle_x));
|
||||
stream.writeAttribute("prehy", QString::number(key.pre_handle_y));
|
||||
stream.writeAttribute("posthx", QString::number(key.post_handle_x));
|
||||
stream.writeAttribute("posthy", QString::number(key.post_handle_y));
|
||||
stream.writeEndElement(); // key
|
||||
}
|
||||
stream.writeEndElement(); // field
|
||||
}
|
||||
stream.writeEndElement(); // row
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Effect::is_open() {
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
void Effect::validate_meta_path() {
|
||||
if (!meta->path.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return;
|
||||
QList<QString> effects_paths = get_effects_paths();
|
||||
const QString& test_fn = vertPath.isEmpty() ? fragPath : vertPath;
|
||||
for (int i=0;i<effects_paths.size();i++) {
|
||||
if (QFileInfo::exists(effects_paths.at(i) + "/" + test_fn)) {
|
||||
for (int j=0;j<effects.size();j++) {
|
||||
if (&effects.at(j) == meta) {
|
||||
effects[j].path = effects_paths.at(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,6 +792,7 @@ void Effect::open() {
|
||||
dout << "[WARNING] No current context to create a shader program for - will retry next repaint";
|
||||
} else {
|
||||
glslProgram = new QOpenGLShaderProgram();
|
||||
validate_meta_path();
|
||||
if (!vertPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath);
|
||||
if (!fragPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath);
|
||||
glslProgram->link();
|
||||
@@ -777,16 +829,16 @@ void Effect::startEffect() {
|
||||
|
||||
void Effect::endEffect() {
|
||||
if (bound) glslProgram->release();
|
||||
bound = false;
|
||||
bound = false;
|
||||
}
|
||||
|
||||
void Effect::process_image(double, uint8_t *, int) {}
|
||||
|
||||
Effect* Effect::copy(Clip* c) {
|
||||
Effect* copy = create_effect(c, meta);
|
||||
copy->set_enabled(is_enabled());
|
||||
copy_field_keyframes(copy);
|
||||
return copy;
|
||||
copy->set_enabled(is_enabled());
|
||||
copy_field_keyframes(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
void Effect::process_shader(double timecode, GLTextureCoords&) {
|
||||
@@ -831,7 +883,7 @@ GLuint Effect::process_superimpose(double timecode) {
|
||||
recreate_texture = true;
|
||||
}
|
||||
|
||||
if (valueHasChanged(timecode) || recreate_texture || enable_always_update) {
|
||||
if (valueHasChanged(timecode) || recreate_texture || enable_always_update) {
|
||||
redraw(timecode);
|
||||
}
|
||||
|
||||
@@ -852,78 +904,78 @@ GLuint Effect::process_superimpose(double timecode) {
|
||||
void Effect::process_audio(double, double, quint8*, int, int) {
|
||||
// only volume/pan, hand off to AU and VST for all other cases
|
||||
|
||||
/*double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
/*double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
|
||||
for (int i=0;i<nb_bytes;i+=2) {
|
||||
qint32 samp = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
|
||||
jsEngine.globalObject().setProperty("sample", samp);
|
||||
jsEngine.globalObject().setProperty("sample", samp);
|
||||
jsEngine.globalObject().setProperty("volume", row(0)->field(0)->get_double_value(timecode_start+(interval*i), true));
|
||||
QJSValue result = eval.call();
|
||||
samp = result.toInt();
|
||||
QJSValueList args;
|
||||
args << samples << nb_bytes;
|
||||
samp = result.toInt();
|
||||
QJSValueList args;
|
||||
args << samples << nb_bytes;
|
||||
|
||||
|
||||
samples[i+1] = (quint8) (samp >> 8);
|
||||
samples[i] = (quint8) samp;
|
||||
}*/
|
||||
}*/
|
||||
}
|
||||
|
||||
void Effect::gizmo_draw(double, GLTextureCoords &) {}
|
||||
|
||||
void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) {
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
if (gizmos.at(i) == gizmo) {
|
||||
ComboAction* ca = NULL;
|
||||
if (done) ca = new ComboAction();
|
||||
if (gizmo->x_field1 != NULL) {
|
||||
gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1);
|
||||
gizmo->x_field1->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->y_field1 != NULL) {
|
||||
gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1);
|
||||
gizmo->y_field1->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->x_field2 != NULL) {
|
||||
gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2);
|
||||
gizmo->x_field2->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->y_field2 != NULL) {
|
||||
gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2);
|
||||
gizmo->y_field2->make_key_from_change(ca);
|
||||
}
|
||||
if (done) undo_stack.push(ca);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
if (gizmos.at(i) == gizmo) {
|
||||
ComboAction* ca = NULL;
|
||||
if (done) ca = new ComboAction();
|
||||
if (gizmo->x_field1 != NULL) {
|
||||
gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1);
|
||||
gizmo->x_field1->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->y_field1 != NULL) {
|
||||
gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1);
|
||||
gizmo->y_field1->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->x_field2 != NULL) {
|
||||
gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2);
|
||||
gizmo->x_field2->make_key_from_change(ca);
|
||||
}
|
||||
if (gizmo->y_field2 != NULL) {
|
||||
gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2);
|
||||
gizmo->y_field2->make_key_from_change(ca);
|
||||
}
|
||||
if (done) undo_stack.push(ca);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::gizmo_world_to_screen() {
|
||||
GLfloat view_val[16];
|
||||
GLfloat projection_val[16];
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, view_val);
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, projection_val);
|
||||
GLfloat view_val[16];
|
||||
GLfloat projection_val[16];
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, view_val);
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, projection_val);
|
||||
|
||||
QMatrix4x4 view_matrix(view_val);
|
||||
QMatrix4x4 projection_matrix(projection_val);
|
||||
QMatrix4x4 view_matrix(view_val);
|
||||
QMatrix4x4 projection_matrix(projection_val);
|
||||
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
EffectGizmo* g = gizmos.at(i);
|
||||
for (int i=0;i<gizmos.size();i++) {
|
||||
EffectGizmo* g = gizmos.at(i);
|
||||
|
||||
for (int j=0;j<g->get_point_count();j++) {
|
||||
QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix);
|
||||
for (int j=0;j<g->get_point_count();j++) {
|
||||
QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix);
|
||||
|
||||
int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width);
|
||||
int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height);
|
||||
int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width);
|
||||
int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height);
|
||||
|
||||
g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1);
|
||||
}
|
||||
}
|
||||
g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Effect::are_gizmos_enabled() {
|
||||
return (gizmos.size() > 0);
|
||||
return (gizmos.size() > 0);
|
||||
}
|
||||
|
||||
void Effect::redraw(double) {
|
||||
|
||||
+45
-44
@@ -23,16 +23,15 @@ class QXmlStreamWriter;
|
||||
class Effect;
|
||||
class EffectRow;
|
||||
class CheckboxEx;
|
||||
class KeyframeDelete;
|
||||
|
||||
struct EffectMeta {
|
||||
QString name;
|
||||
QString category;
|
||||
QString filename;
|
||||
QString name;
|
||||
QString category;
|
||||
QString filename;
|
||||
QString path;
|
||||
int internal;
|
||||
int type;
|
||||
int subtype;
|
||||
int subtype;
|
||||
};
|
||||
|
||||
extern QVector<EffectMeta> effects;
|
||||
@@ -70,37 +69,37 @@ extern QMutex effects_loaded;
|
||||
#define EFFECT_INTERNAL_COUNT 13
|
||||
|
||||
#define KEYFRAME_TYPE_LINEAR 0
|
||||
#define KEYFRAME_TYPE_SMOOTH 1
|
||||
#define KEYFRAME_TYPE_BEZIER 2
|
||||
#define KEYFRAME_TYPE_BEZIER 1
|
||||
#define KEYFRAME_TYPE_HOLD 2
|
||||
|
||||
struct GLTextureCoords {
|
||||
int grid_size;
|
||||
int grid_size;
|
||||
|
||||
int vertexTopLeftX;
|
||||
int vertexTopLeftY;
|
||||
int vertexTopLeftZ;
|
||||
int vertexTopLeftZ;
|
||||
int vertexTopRightX;
|
||||
int vertexTopRightY;
|
||||
int vertexTopRightZ;
|
||||
int vertexTopRightZ;
|
||||
int vertexBottomLeftX;
|
||||
int vertexBottomLeftY;
|
||||
int vertexBottomLeftZ;
|
||||
int vertexBottomLeftZ;
|
||||
int vertexBottomRightX;
|
||||
int vertexBottomRightY;
|
||||
int vertexBottomRightZ;
|
||||
int vertexBottomRightZ;
|
||||
|
||||
float textureTopLeftX;
|
||||
float textureTopLeftY;
|
||||
float textureTopLeftQ;
|
||||
float textureTopRightX;
|
||||
float textureTopRightY;
|
||||
float textureTopRightQ;
|
||||
float textureBottomRightX;
|
||||
float textureBottomRightY;
|
||||
float textureBottomRightQ;
|
||||
float textureBottomLeftX;
|
||||
float textureBottomLeftY;
|
||||
float textureBottomLeftQ;
|
||||
float textureTopLeftX;
|
||||
float textureTopLeftY;
|
||||
float textureTopLeftQ;
|
||||
float textureTopRightX;
|
||||
float textureTopRightY;
|
||||
float textureTopRightQ;
|
||||
float textureBottomRightX;
|
||||
float textureBottomRightY;
|
||||
float textureBottomRightQ;
|
||||
float textureBottomLeftX;
|
||||
float textureBottomLeftY;
|
||||
float textureBottomLeftQ;
|
||||
};
|
||||
|
||||
qint16 mix_audio_sample(qint16 a, qint16 b);
|
||||
@@ -114,21 +113,21 @@ class Effect : public QObject {
|
||||
public:
|
||||
Effect(Clip* c, const EffectMeta* em);
|
||||
~Effect();
|
||||
Clip* parent_clip;
|
||||
Clip* parent_clip;
|
||||
const EffectMeta* meta;
|
||||
int id;
|
||||
int id;
|
||||
QString name;
|
||||
CollapsibleWidget* container;
|
||||
|
||||
EffectRow* add_row(const QString &name, bool savable = true);
|
||||
EffectRow* add_row(const QString &name, bool savable = true);
|
||||
EffectRow* row(int i);
|
||||
int row_count();
|
||||
|
||||
EffectGizmo* add_gizmo(int type);
|
||||
EffectGizmo* gizmo(int i);
|
||||
int gizmo_count();
|
||||
EffectGizmo* add_gizmo(int type);
|
||||
EffectGizmo* gizmo(int i);
|
||||
int gizmo_count();
|
||||
|
||||
bool is_enabled();
|
||||
bool is_enabled();
|
||||
void set_enabled(bool b);
|
||||
|
||||
virtual void refresh();
|
||||
@@ -137,9 +136,10 @@ public:
|
||||
void copy_field_keyframes(Effect *e);
|
||||
|
||||
void load(QXmlStreamReader& stream);
|
||||
void save(QXmlStreamWriter& stream);
|
||||
void save(QXmlStreamWriter& stream);
|
||||
|
||||
// glsl handling
|
||||
bool is_open();
|
||||
void open();
|
||||
void close();
|
||||
virtual void startEffect();
|
||||
@@ -147,24 +147,24 @@ public:
|
||||
|
||||
bool enable_shader;
|
||||
bool enable_coords;
|
||||
bool enable_superimpose;
|
||||
bool enable_image;
|
||||
bool enable_superimpose;
|
||||
bool enable_image;
|
||||
|
||||
int getIterations();
|
||||
void setIterations(int i);
|
||||
|
||||
const char* ffmpeg_filter;
|
||||
|
||||
virtual void process_image(double timecode, uint8_t* data, int size);
|
||||
virtual void process_image(double timecode, uint8_t* data, int size);
|
||||
virtual void process_shader(double timecode, GLTextureCoords&);
|
||||
virtual void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
virtual void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
virtual GLuint process_superimpose(double timecode);
|
||||
virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
virtual void gizmo_draw(double timecode, GLTextureCoords& coords);
|
||||
void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done);
|
||||
void gizmo_world_to_screen();
|
||||
bool are_gizmos_enabled();
|
||||
virtual void gizmo_draw(double timecode, GLTextureCoords& coords);
|
||||
void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done);
|
||||
void gizmo_world_to_screen();
|
||||
bool are_gizmos_enabled();
|
||||
public slots:
|
||||
void field_changed();
|
||||
private slots:
|
||||
@@ -182,17 +182,17 @@ protected:
|
||||
QImage img;
|
||||
QOpenGLTexture* texture;
|
||||
|
||||
// enable effect to update constantly
|
||||
bool enable_always_update;
|
||||
// enable effect to update constantly
|
||||
bool enable_always_update;
|
||||
private:
|
||||
// superimpose effect
|
||||
QString script;
|
||||
|
||||
bool isOpen;
|
||||
QVector<EffectRow*> rows;
|
||||
QVector<EffectGizmo*> gizmos;
|
||||
QVector<EffectGizmo*> gizmos;
|
||||
QGridLayout* ui_layout;
|
||||
QWidget* ui;
|
||||
QWidget* ui;
|
||||
bool bound;
|
||||
|
||||
// superimpose functions
|
||||
@@ -201,6 +201,7 @@ private:
|
||||
QVector<QVariant> cachedValues;
|
||||
void delete_texture();
|
||||
int get_index_in_clip();
|
||||
void validate_meta_path();
|
||||
};
|
||||
|
||||
class EffectInit : public QThread {
|
||||
|
||||
+262
-243
@@ -15,347 +15,366 @@
|
||||
#include "project/sequence.h"
|
||||
|
||||
#include "io/math.h"
|
||||
#include <QtMath>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
EffectField::EffectField(EffectRow *parent, int t, const QString &i) :
|
||||
parent_row(parent),
|
||||
type(t),
|
||||
id(i)
|
||||
parent_row(parent),
|
||||
type(t),
|
||||
id(i)
|
||||
{
|
||||
switch (t) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
{
|
||||
LabelSlider* ls = new LabelSlider();
|
||||
ui_element = ls;
|
||||
connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
ColorButton* cb = new ColorButton();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(color_changed()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
{
|
||||
TextEditEx* edit = new TextEditEx();
|
||||
edit->setUndoRedoEnabled(true);
|
||||
ui_element = edit;
|
||||
connect(edit, SIGNAL(textChanged()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
{
|
||||
CheckboxEx* cb = new CheckboxEx();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(clicked(bool)), this, SLOT(ui_element_change()));
|
||||
connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool)));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
{
|
||||
ComboBoxEx* cb = new ComboBoxEx();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(activated(int)), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
{
|
||||
FontCombobox* fcb = new FontCombobox();
|
||||
ui_element = fcb;
|
||||
connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (t) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
{
|
||||
LabelSlider* ls = new LabelSlider();
|
||||
ui_element = ls;
|
||||
connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change()));
|
||||
connect(ls, SIGNAL(clicked()), this, SIGNAL(clicked()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
ColorButton* cb = new ColorButton();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(color_changed()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
{
|
||||
TextEditEx* edit = new TextEditEx();
|
||||
edit->setUndoRedoEnabled(true);
|
||||
ui_element = edit;
|
||||
connect(edit, SIGNAL(textChanged()), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
{
|
||||
CheckboxEx* cb = new CheckboxEx();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(clicked(bool)), this, SLOT(ui_element_change()));
|
||||
connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool)));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
{
|
||||
ComboBoxEx* cb = new ComboBoxEx();
|
||||
ui_element = cb;
|
||||
connect(cb, SIGNAL(activated(int)), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
{
|
||||
FontCombobox* fcb = new FontCombobox();
|
||||
ui_element = fcb;
|
||||
connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QVariant EffectField::get_previous_data() {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_BOOL: return !static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->getPreviousIndex();
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->getPreviousValue();
|
||||
}
|
||||
return QVariant();
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->getPreviousValue();
|
||||
case EFFECT_FIELD_BOOL: return !static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->getPreviousIndex();
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->getPreviousValue();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant EffectField::get_current_data() {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->value();
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->getPlainTextEx();
|
||||
case EFFECT_FIELD_BOOL: return static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
}
|
||||
return QVariant();
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->value();
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->getPlainTextEx();
|
||||
case EFFECT_FIELD_BOOL: return static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
double EffectField::frameToTimecode(long frame) {
|
||||
return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate);
|
||||
return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate);
|
||||
}
|
||||
|
||||
long EffectField::timecodeToFrame(double timecode) {
|
||||
return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate);
|
||||
return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate);
|
||||
}
|
||||
|
||||
void EffectField::set_current_data(const QVariant& data) {
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->set_value(data.toDouble(), false);
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->set_color(data.value<QColor>());
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->setPlainTextEx(data.toString());
|
||||
case EFFECT_FIELD_BOOL: return static_cast<QCheckBox*>(ui_element)->setChecked(data.toBool());
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(data.toInt());
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->setCurrentTextEx(data.toString());
|
||||
}
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE: return static_cast<LabelSlider*>(ui_element)->set_value(data.toDouble(), false);
|
||||
case EFFECT_FIELD_COLOR: return static_cast<ColorButton*>(ui_element)->set_color(data.value<QColor>());
|
||||
case EFFECT_FIELD_STRING: return static_cast<TextEditEx*>(ui_element)->setPlainTextEx(data.toString());
|
||||
case EFFECT_FIELD_BOOL: return static_cast<QCheckBox*>(ui_element)->setChecked(data.toBool());
|
||||
case EFFECT_FIELD_COMBO: return static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(data.toInt());
|
||||
case EFFECT_FIELD_FONT: return static_cast<FontCombobox*>(ui_element)->setCurrentTextEx(data.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void EffectField::get_keyframe_data(double timecode, int &before, int &after, double &progress) {
|
||||
int before_keyframe_index = -1;
|
||||
int after_keyframe_index = -1;
|
||||
long before_keyframe_time = LONG_MIN;
|
||||
long after_keyframe_time = LONG_MAX;
|
||||
long frame = timecodeToFrame(timecode);
|
||||
int before_keyframe_index = -1;
|
||||
int after_keyframe_index = -1;
|
||||
long before_keyframe_time = LONG_MIN;
|
||||
long after_keyframe_time = LONG_MAX;
|
||||
long frame = timecodeToFrame(timecode);
|
||||
|
||||
for (int i=0;i<parent_row->keyframe_times.size();i++) {
|
||||
long eval_keyframe_time = parent_row->keyframe_times.at(i);
|
||||
if (eval_keyframe_time == frame) {
|
||||
before = i;
|
||||
after = i;
|
||||
return;
|
||||
} else if (eval_keyframe_time < frame && eval_keyframe_time > before_keyframe_time) {
|
||||
before_keyframe_index = i;
|
||||
before_keyframe_time = eval_keyframe_time;
|
||||
} else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) {
|
||||
after_keyframe_index = i;
|
||||
after_keyframe_time = eval_keyframe_time;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<keyframes.size();i++) {
|
||||
long eval_keyframe_time = keyframes.at(i).time;
|
||||
if (eval_keyframe_time == frame) {
|
||||
before = i;
|
||||
after = i;
|
||||
return;
|
||||
} else if (eval_keyframe_time < frame && eval_keyframe_time > before_keyframe_time) {
|
||||
before_keyframe_index = i;
|
||||
before_keyframe_time = eval_keyframe_time;
|
||||
} else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) {
|
||||
after_keyframe_index = i;
|
||||
after_keyframe_time = eval_keyframe_time;
|
||||
}
|
||||
}
|
||||
|
||||
if ((type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) && (before_keyframe_index > -1 && after_keyframe_index > -1)) {
|
||||
// interpolate
|
||||
before = before_keyframe_index;
|
||||
after = after_keyframe_index;
|
||||
|
||||
progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time));
|
||||
} else if (before_keyframe_index > -1) {
|
||||
before = before_keyframe_index;
|
||||
after = before_keyframe_index;
|
||||
} else {
|
||||
before = after_keyframe_index;
|
||||
after = after_keyframe_index;
|
||||
}
|
||||
if ((type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) && (before_keyframe_index > -1 && after_keyframe_index > -1)) {
|
||||
// interpolate
|
||||
before = before_keyframe_index;
|
||||
after = after_keyframe_index;
|
||||
progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time));
|
||||
} else if (before_keyframe_index > -1) {
|
||||
before = before_keyframe_index;
|
||||
after = before_keyframe_index;
|
||||
} else {
|
||||
before = after_keyframe_index;
|
||||
after = after_keyframe_index;
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectField::hasKeyframes() {
|
||||
return (parent_row->isKeyframing() && keyframe_data.size() > 0);
|
||||
return (parent_row->isKeyframing() && keyframes.size() > 0);
|
||||
}
|
||||
|
||||
QVariant EffectField::validate_keyframe_data(double timecode, bool async) {
|
||||
if (hasKeyframes()) {
|
||||
int before_keyframe;
|
||||
int after_keyframe;
|
||||
double progress;
|
||||
get_keyframe_data(timecode, before_keyframe, after_keyframe, progress);
|
||||
if (hasKeyframes()) {
|
||||
int before_keyframe;
|
||||
int after_keyframe;
|
||||
double progress;
|
||||
get_keyframe_data(timecode, before_keyframe, after_keyframe, progress);
|
||||
|
||||
int kf_type = (progress < 0.5) ? parent_row->keyframe_types.at(before_keyframe) : parent_row->keyframe_types.at(after_keyframe);
|
||||
if (kf_type == KEYFRAME_TYPE_SMOOTH) {
|
||||
double x = (8.0 * progress) - 4.0;
|
||||
progress = 1.0 / (1.0 + qPow(M_E, -x));
|
||||
progress *= 1.0373;
|
||||
progress -= 0.01865;
|
||||
}
|
||||
const QVariant& before_data = keyframes.at(before_keyframe).data;
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
{
|
||||
double value;
|
||||
if (before_keyframe == after_keyframe) {
|
||||
value = keyframes.at(before_keyframe).data.toDouble();
|
||||
} else {
|
||||
const EffectKeyframe& before_key = keyframes.at(before_keyframe);
|
||||
const EffectKeyframe& after_key = keyframes.at(after_keyframe);
|
||||
|
||||
const QVariant& before_data = keyframe_data.at(before_keyframe);
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
{
|
||||
double value;
|
||||
if (before_keyframe == after_keyframe) {
|
||||
value = keyframe_data.at(before_keyframe).toDouble();
|
||||
} else {
|
||||
double before_dbl = keyframe_data.at(before_keyframe).toDouble();
|
||||
double after_dbl = keyframe_data.at(after_keyframe).toDouble();
|
||||
value = double_lerp(before_dbl, after_dbl, progress);
|
||||
}
|
||||
if (async) {
|
||||
return value;
|
||||
}
|
||||
static_cast<LabelSlider*>(ui_element)->set_value(value, false);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
QColor value;
|
||||
if (before_keyframe == after_keyframe) {
|
||||
value = keyframe_data.at(before_keyframe).value<QColor>();
|
||||
} else {
|
||||
QColor before_data = keyframe_data.at(before_keyframe).value<QColor>();
|
||||
QColor after_data = keyframe_data.at(after_keyframe).value<QColor>();
|
||||
value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress));
|
||||
}
|
||||
if (async) {
|
||||
return value;
|
||||
}
|
||||
static_cast<ColorButton*>(ui_element)->set_color(value);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<TextEditEx*>(ui_element)->setPlainTextEx(before_data.toString());
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<QCheckBox*>(ui_element)->setChecked(before_data.toBool());
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(before_data.toInt());
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentTextEx(before_data.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
double before_dbl = before_key.data.toDouble();
|
||||
double after_dbl = after_key.data.toDouble();
|
||||
|
||||
if (before_key.type == KEYFRAME_TYPE_HOLD) {
|
||||
// hold
|
||||
value = before_dbl;
|
||||
} else if (before_key.type == KEYFRAME_TYPE_BEZIER || after_key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
// bezier interpolation
|
||||
if (before_key.type == KEYFRAME_TYPE_BEZIER && after_key.type == KEYFRAME_TYPE_BEZIER) {
|
||||
// cubic bezier
|
||||
double t = cubic_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+before_key.post_handle_x, after_key.time+after_key.pre_handle_x, after_key.time);
|
||||
value = cubic_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl+after_key.pre_handle_y, after_dbl, t);
|
||||
} else if (after_key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier
|
||||
// last keyframe is the bezier one
|
||||
double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+before_key.post_handle_x, after_key.time);
|
||||
value = quad_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl, t);
|
||||
} else {
|
||||
// this keyframe is the bezier one
|
||||
double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, after_key.time+after_key.pre_handle_x, after_key.time);
|
||||
value = quad_from_t(before_dbl, after_dbl+after_key.pre_handle_y, after_dbl, t);
|
||||
}
|
||||
} else {
|
||||
// linear
|
||||
value = double_lerp(before_dbl, after_dbl, progress);
|
||||
}
|
||||
}
|
||||
if (async) {
|
||||
return value;
|
||||
}
|
||||
static_cast<LabelSlider*>(ui_element)->set_value(value, false);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
QColor value;
|
||||
if (before_keyframe == after_keyframe) {
|
||||
value = keyframes.at(before_keyframe).data.value<QColor>();
|
||||
} else {
|
||||
QColor before_data = keyframes.at(before_keyframe).data.value<QColor>();
|
||||
QColor after_data = keyframes.at(after_keyframe).data.value<QColor>();
|
||||
value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress));
|
||||
}
|
||||
if (async) {
|
||||
return value;
|
||||
}
|
||||
static_cast<ColorButton*>(ui_element)->set_color(value);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<TextEditEx*>(ui_element)->setPlainTextEx(before_data.toString());
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<QCheckBox*>(ui_element)->setChecked(before_data.toBool());
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(before_data.toInt());
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
if (async) {
|
||||
return before_data;
|
||||
}
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentTextEx(before_data.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void EffectField::ui_element_change() {
|
||||
bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast<LabelSlider*>(ui_element)->is_dragging());
|
||||
ComboAction* ca = NULL;
|
||||
if (!dragging_double) ca = new ComboAction();
|
||||
make_key_from_change(ca);
|
||||
if (!dragging_double) undo_stack.push(ca);
|
||||
emit changed();
|
||||
bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast<LabelSlider*>(ui_element)->is_dragging());
|
||||
ComboAction* ca = NULL;
|
||||
if (!dragging_double) ca = new ComboAction();
|
||||
make_key_from_change(ca);
|
||||
if (!dragging_double) undo_stack.push(ca);
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void EffectField::make_key_from_change(ComboAction* ca) {
|
||||
if (parent_row->isKeyframing()) {
|
||||
parent_row->set_keyframe_now(ca);
|
||||
} else if (ca != NULL) {
|
||||
// set undo
|
||||
ca->append(new EffectFieldUndo(this));
|
||||
}
|
||||
if (parent_row->isKeyframing()) {
|
||||
parent_row->set_keyframe_now(ca);
|
||||
} else if (ca != NULL) {
|
||||
// set undo
|
||||
ca->append(new EffectFieldUndo(this));
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* EffectField::get_ui_element() {
|
||||
return ui_element;
|
||||
return ui_element;
|
||||
}
|
||||
|
||||
void EffectField::set_enabled(bool e) {
|
||||
ui_element->setEnabled(e);
|
||||
ui_element->setEnabled(e);
|
||||
}
|
||||
|
||||
double EffectField::get_double_value(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toDouble();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<LabelSlider*>(ui_element)->value();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toDouble();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<LabelSlider*>(ui_element)->value();
|
||||
}
|
||||
|
||||
void EffectField::set_double_value(double v) {
|
||||
static_cast<LabelSlider*>(ui_element)->set_value(v, false);
|
||||
static_cast<LabelSlider*>(ui_element)->set_value(v, false);
|
||||
}
|
||||
|
||||
void EffectField::set_double_default_value(double v) {
|
||||
static_cast<LabelSlider*>(ui_element)->set_default_value(v);
|
||||
static_cast<LabelSlider*>(ui_element)->set_default_value(v);
|
||||
}
|
||||
|
||||
void EffectField::set_double_minimum_value(double v) {
|
||||
static_cast<LabelSlider*>(ui_element)->set_minimum_value(v);
|
||||
static_cast<LabelSlider*>(ui_element)->set_minimum_value(v);
|
||||
}
|
||||
|
||||
void EffectField::set_double_maximum_value(double v) {
|
||||
static_cast<LabelSlider*>(ui_element)->set_maximum_value(v);
|
||||
static_cast<LabelSlider*>(ui_element)->set_maximum_value(v);
|
||||
}
|
||||
|
||||
void EffectField::add_combo_item(const QString& name, const QVariant& data) {
|
||||
static_cast<ComboBoxEx*>(ui_element)->addItem(name, data);
|
||||
static_cast<ComboBoxEx*>(ui_element)->addItem(name, data);
|
||||
}
|
||||
|
||||
int EffectField::get_combo_index(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toInt();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toInt();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
}
|
||||
|
||||
const QVariant EffectField::get_combo_data(double timecode) {
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentData();
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentData();
|
||||
}
|
||||
|
||||
const QString EffectField::get_combo_string(double timecode) {
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentText();
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentText();
|
||||
}
|
||||
|
||||
void EffectField::set_combo_index(int index) {
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(index);
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(index);
|
||||
}
|
||||
|
||||
void EffectField::set_combo_string(const QString& s) {
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentTextEx(s);
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentTextEx(s);
|
||||
}
|
||||
|
||||
bool EffectField::get_bool_value(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toBool();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toBool();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<QCheckBox*>(ui_element)->isChecked();
|
||||
}
|
||||
|
||||
void EffectField::set_bool_value(bool b) {
|
||||
return static_cast<QCheckBox*>(ui_element)->setChecked(b);
|
||||
return static_cast<QCheckBox*>(ui_element)->setChecked(b);
|
||||
}
|
||||
|
||||
const QString EffectField::get_string_value(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toString();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<TextEditEx*>(ui_element)->getPlainTextEx();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toString();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<TextEditEx*>(ui_element)->getPlainTextEx();
|
||||
}
|
||||
|
||||
void EffectField::set_string_value(const QString& s) {
|
||||
static_cast<TextEditEx*>(ui_element)->setPlainTextEx(s);
|
||||
static_cast<TextEditEx*>(ui_element)->setPlainTextEx(s);
|
||||
}
|
||||
|
||||
const QString EffectField::get_font_name(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toString();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).toString();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
}
|
||||
|
||||
void EffectField::set_font_name(const QString& s) {
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentText(s);
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentText(s);
|
||||
}
|
||||
|
||||
QColor EffectField::get_color_value(double timecode, bool async) {
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).value<QColor>();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
if (async && hasKeyframes()) {
|
||||
return validate_keyframe_data(timecode, true).value<QColor>();
|
||||
}
|
||||
validate_keyframe_data(timecode);
|
||||
return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
}
|
||||
|
||||
void EffectField::set_color_value(QColor color) {
|
||||
static_cast<ColorButton*>(ui_element)->set_color(color);
|
||||
static_cast<ColorButton*>(ui_element)->set_color(color);
|
||||
}
|
||||
|
||||
+43
-40
@@ -12,63 +12,66 @@
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include "keyframe.h"
|
||||
|
||||
class EffectRow;
|
||||
class ComboAction;
|
||||
|
||||
class EffectField : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
EffectField(EffectRow* parent, int t, const QString& i);
|
||||
EffectRow* parent_row;
|
||||
int type;
|
||||
QString id;
|
||||
EffectField(EffectRow* parent, int t, const QString& i);
|
||||
EffectRow* parent_row;
|
||||
int type;
|
||||
QString id;
|
||||
|
||||
QVariant get_previous_data();
|
||||
QVariant get_current_data();
|
||||
double frameToTimecode(long frame);
|
||||
long timecodeToFrame(double timecode);
|
||||
void set_current_data(const QVariant&);
|
||||
void get_keyframe_data(double timecode, int& before, int& after, double& d);
|
||||
QVariant validate_keyframe_data(double timecode, bool async = false);
|
||||
QVariant get_previous_data();
|
||||
QVariant get_current_data();
|
||||
double frameToTimecode(long frame);
|
||||
long timecodeToFrame(double timecode);
|
||||
void set_current_data(const QVariant&);
|
||||
void get_keyframe_data(double timecode, int& before, int& after, double& d);
|
||||
QVariant validate_keyframe_data(double timecode, bool async = false);
|
||||
|
||||
double get_double_value(double timecode, bool async = false);
|
||||
void set_double_value(double v);
|
||||
void set_double_default_value(double v);
|
||||
void set_double_minimum_value(double v);
|
||||
void set_double_maximum_value(double v);
|
||||
double get_double_value(double timecode, bool async = false);
|
||||
void set_double_value(double v);
|
||||
void set_double_default_value(double v);
|
||||
void set_double_minimum_value(double v);
|
||||
void set_double_maximum_value(double v);
|
||||
|
||||
const QString get_string_value(double timecode, bool async = false);
|
||||
void set_string_value(const QString &s);
|
||||
const QString get_string_value(double timecode, bool async = false);
|
||||
void set_string_value(const QString &s);
|
||||
|
||||
void add_combo_item(const QString& name, const QVariant &data);
|
||||
int get_combo_index(double timecode, bool async = false);
|
||||
const QVariant get_combo_data(double timecode);
|
||||
const QString get_combo_string(double timecode);
|
||||
void set_combo_index(int index);
|
||||
void set_combo_string(const QString& s);
|
||||
void add_combo_item(const QString& name, const QVariant &data);
|
||||
int get_combo_index(double timecode, bool async = false);
|
||||
const QVariant get_combo_data(double timecode);
|
||||
const QString get_combo_string(double timecode);
|
||||
void set_combo_index(int index);
|
||||
void set_combo_string(const QString& s);
|
||||
|
||||
bool get_bool_value(double timecode, bool async = false);
|
||||
void set_bool_value(bool b);
|
||||
bool get_bool_value(double timecode, bool async = false);
|
||||
void set_bool_value(bool b);
|
||||
|
||||
const QString get_font_name(double timecode, bool async = false);
|
||||
void set_font_name(const QString& s);
|
||||
const QString get_font_name(double timecode, bool async = false);
|
||||
void set_font_name(const QString& s);
|
||||
|
||||
QColor get_color_value(double timecode, bool async = false);
|
||||
void set_color_value(QColor color);
|
||||
QColor get_color_value(double timecode, bool async = false);
|
||||
void set_color_value(QColor color);
|
||||
|
||||
QWidget* get_ui_element();
|
||||
void set_enabled(bool e);
|
||||
QVector<QVariant> keyframe_data;
|
||||
QWidget* ui_element;
|
||||
QWidget* get_ui_element();
|
||||
void set_enabled(bool e);
|
||||
QVector<EffectKeyframe> keyframes;
|
||||
QWidget* ui_element;
|
||||
|
||||
void make_key_from_change(ComboAction* ca);
|
||||
void make_key_from_change(ComboAction* ca);
|
||||
private:
|
||||
bool hasKeyframes();
|
||||
bool hasKeyframes();
|
||||
private slots:
|
||||
void ui_element_change();
|
||||
void ui_element_change();
|
||||
signals:
|
||||
void changed();
|
||||
void toggled(bool);
|
||||
void changed();
|
||||
void toggled(bool);
|
||||
void clicked();
|
||||
};
|
||||
|
||||
#endif // EFFECTFIELD_H
|
||||
|
||||
+146
-119
@@ -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
@@ -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
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "keyframe.h"
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include "effectfield.h"
|
||||
#include "undo.h"
|
||||
#include "panels/panels.h"
|
||||
|
||||
EffectKeyframe::EffectKeyframe() {
|
||||
pre_handle_x = -40;
|
||||
pre_handle_y = 0;
|
||||
post_handle_x = 40;
|
||||
post_handle_y = 0;
|
||||
}
|
||||
|
||||
void delete_keyframes(QVector<EffectField *>& selected_key_fields, QVector<int> &selected_keys) {
|
||||
QVector<EffectField*> fields;
|
||||
QVector<int> key_indices;
|
||||
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
bool added = false;
|
||||
for (int j=0;j<key_indices.size();j++) {
|
||||
if (key_indices.at(j) < selected_keys.at(i)) {
|
||||
key_indices.insert(j, selected_keys.at(i));
|
||||
fields.insert(j, selected_key_fields.at(i));
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
key_indices.append(selected_keys.at(i));
|
||||
fields.append(selected_key_fields.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (fields.size() > 0) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<key_indices.size();i++) {
|
||||
ca->append(new KeyframeDelete(fields.at(i), key_indices.at(i)));
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
selected_keys.clear();
|
||||
selected_key_fields.clear();
|
||||
update_ui(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef KEYFRAME_H
|
||||
#define KEYFRAME_H
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
class EffectField;
|
||||
|
||||
class EffectKeyframe {
|
||||
public:
|
||||
EffectKeyframe();
|
||||
|
||||
long time;
|
||||
int type;
|
||||
QVariant data;
|
||||
|
||||
// only for bezier type
|
||||
double pre_handle_x;
|
||||
double pre_handle_y;
|
||||
double post_handle_x;
|
||||
double post_handle_y;
|
||||
};
|
||||
|
||||
void delete_keyframes(QVector<EffectField *> &selected_key_fields, QVector<int> &selected_keys);
|
||||
|
||||
#endif // KEYFRAME_H
|
||||
+92
-91
@@ -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
@@ -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
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "clickablelabel.h"
|
||||
|
||||
ClickableLabel::ClickableLabel(QWidget *parent, Qt::WindowFlags f) :
|
||||
QLabel(parent, f)
|
||||
{}
|
||||
|
||||
ClickableLabel::ClickableLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) :
|
||||
QLabel(text, parent, f)
|
||||
{}
|
||||
|
||||
void ClickableLabel::mousePressEvent(QMouseEvent *) {
|
||||
emit clicked();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef CLICKABLELABEL_H
|
||||
#define CLICKABLELABEL_H
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
class ClickableLabel : public QLabel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClickableLabel(QWidget * parent = 0, Qt::WindowFlags f = 0);
|
||||
ClickableLabel(const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0);
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
signals:
|
||||
void clicked();
|
||||
};
|
||||
|
||||
#endif // CLICKABLELABEL_H
|
||||
@@ -0,0 +1,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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "keyframedrawing.h"
|
||||
|
||||
#include "project/effect.h"
|
||||
|
||||
#define KEYFRAME_POINT_COUNT 4
|
||||
|
||||
void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r, int g, int b) {
|
||||
if (darker) {
|
||||
r *= 0.625;
|
||||
g *= 0.625;
|
||||
b *= 0.625;
|
||||
}
|
||||
p.setPen(QColor(0, 0, 0));
|
||||
p.setBrush(QColor(r, g, b));
|
||||
|
||||
switch (type) {
|
||||
case KEYFRAME_TYPE_LINEAR:
|
||||
{
|
||||
QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)};
|
||||
p.drawPolygon(points, KEYFRAME_POINT_COUNT);
|
||||
}
|
||||
break;
|
||||
case KEYFRAME_TYPE_BEZIER:
|
||||
p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE);
|
||||
break;
|
||||
case KEYFRAME_TYPE_HOLD:
|
||||
p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2));
|
||||
break;
|
||||
}
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef KEYFRAMEDRAWING_H
|
||||
#define KEYFRAMEDRAWING_H
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#define KEYFRAME_SIZE 6
|
||||
#define KEYFRAME_COLOR 160
|
||||
|
||||
void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r = KEYFRAME_COLOR, int g = KEYFRAME_COLOR, int b = KEYFRAME_COLOR);
|
||||
|
||||
#endif // KEYFRAMEDRAWING_H
|
||||
@@ -2,15 +2,22 @@
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
QSize icon_size(12, 12);
|
||||
QSize button_size(20, 20);
|
||||
int icon_size_val = 8*QApplication::desktop()->devicePixelRatio();
|
||||
int clock_size_val = 12*QApplication::desktop()->devicePixelRatio();
|
||||
int button_size_val = 20*QApplication::desktop()->devicePixelRatio();
|
||||
|
||||
QSize icon_size(icon_size_val, icon_size_val);
|
||||
QSize clock_size(clock_size_val, clock_size_val);
|
||||
QSize button_size(button_size_val, button_size_val);
|
||||
|
||||
key_controls = new QHBoxLayout();
|
||||
key_controls->setSpacing(0);
|
||||
key_controls->setMargin(0);
|
||||
key_controls->addStretch();
|
||||
//key_controls->addStretch();
|
||||
|
||||
setLayout(key_controls);
|
||||
|
||||
@@ -21,6 +28,7 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
left_key_nav->setVisible(false);
|
||||
key_controls->addWidget(left_key_nav);
|
||||
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key()));
|
||||
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
key_addremove = new QPushButton();
|
||||
key_addremove->setIcon(QIcon(":/icons/diamond.png"));
|
||||
@@ -29,6 +37,7 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
key_addremove->setVisible(false);
|
||||
key_controls->addWidget(key_addremove);
|
||||
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key()));
|
||||
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
right_key_nav = new QPushButton();
|
||||
right_key_nav->setIcon(QIcon(":/icons/tri-right.png"));
|
||||
@@ -37,20 +46,26 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
|
||||
right_key_nav->setVisible(false);
|
||||
key_controls->addWidget(right_key_nav);
|
||||
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key()));
|
||||
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
|
||||
keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), "");
|
||||
keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
|
||||
keyframe_enable->setMaximumSize(button_size);
|
||||
keyframe_enable->setIconSize(icon_size);
|
||||
keyframe_enable->setIconSize(clock_size);
|
||||
keyframe_enable->setCheckable(true);
|
||||
keyframe_enable->setToolTip("Enable Keyframes");
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(set_keyframe_enabled(bool)));
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(keyframe_enabled_changed(bool)));
|
||||
connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool)));
|
||||
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
|
||||
key_controls->addWidget(keyframe_enable);
|
||||
}
|
||||
|
||||
void KeyframeNavigator::enable_keyframes(bool b) {
|
||||
keyframe_enable->setChecked(b);
|
||||
keyframe_enable->setChecked(b);
|
||||
}
|
||||
|
||||
void KeyframeNavigator::enable_keyframe_toggle(bool b) {
|
||||
keyframe_enable->setVisible(b);
|
||||
}
|
||||
|
||||
void KeyframeNavigator::keyframe_ui_enabled(bool enabled) {
|
||||
|
||||
@@ -12,11 +12,13 @@ class KeyframeNavigator : public QWidget
|
||||
public:
|
||||
KeyframeNavigator(QWidget* parent = 0);
|
||||
void enable_keyframes(bool);
|
||||
void enable_keyframe_toggle(bool);
|
||||
signals:
|
||||
void goto_previous_key();
|
||||
void toggle_key();
|
||||
void goto_next_key();
|
||||
void set_keyframe_enabled(bool);
|
||||
void keyframe_enabled_changed(bool);
|
||||
void clicked();
|
||||
private slots:
|
||||
void keyframe_ui_enabled(bool);
|
||||
private:
|
||||
|
||||
+188
-125
@@ -11,16 +11,18 @@
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "project/sequence.h"
|
||||
#include "panels/grapheditor.h"
|
||||
#include "ui/keyframedrawing.h"
|
||||
#include "ui/clickablelabel.h"
|
||||
#include "ui/resizablescrollbar.h"
|
||||
#include "ui/rectangleselect.h"
|
||||
#include "project/keyframe.h"
|
||||
#include "ui/graphview.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QtMath>
|
||||
#include <QMenu>
|
||||
|
||||
#define KEYFRAME_SIZE 6
|
||||
#define KEYFRAME_POINT_COUNT 4
|
||||
|
||||
long KeyframeView::adjust_row_keyframe(EffectRow* row, long time) {
|
||||
return time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in);
|
||||
}
|
||||
@@ -45,26 +47,36 @@ KeyframeView::KeyframeView(QWidget *parent) :
|
||||
}
|
||||
|
||||
void KeyframeView::show_context_menu(const QPoint& pos) {
|
||||
if (selected_rows.size() > 0) {
|
||||
if (selected_fields.size() > 0) {
|
||||
QMenu menu(this);
|
||||
|
||||
QAction* linear = menu.addAction("Linear");
|
||||
linear->setData(KEYFRAME_TYPE_LINEAR);
|
||||
QAction* smooth = menu.addAction("Smooth");
|
||||
smooth->setData(KEYFRAME_TYPE_SMOOTH);
|
||||
/*QAction* bezier = menu.addAction("Bezier");
|
||||
bezier->setData(KEYFRAME_TYPE_BEZIER);*/
|
||||
QAction* bezier = menu.addAction("Bezier");
|
||||
bezier->setData(KEYFRAME_TYPE_BEZIER);
|
||||
QAction* hold = menu.addAction("Hold");
|
||||
hold->setData(KEYFRAME_TYPE_HOLD);
|
||||
menu.addSeparator();
|
||||
menu.addAction("Graph Editor");
|
||||
|
||||
connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_set_key_type(QAction*)));
|
||||
menu.exec(mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::menu_set_key_type(QAction* a) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<selected_rows.size();i++) {
|
||||
ca->append(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt()));
|
||||
if (a->data().isNull()) {
|
||||
// load graph editor
|
||||
panel_graph_editor->show();
|
||||
} else {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<selected_fields.size();i++) {
|
||||
EffectField* f = selected_fields.at(i);
|
||||
ca->append(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt()));
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
update_ui(false);
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
update();
|
||||
}
|
||||
|
||||
void KeyframeView::paintEvent(QPaintEvent*) {
|
||||
@@ -91,15 +103,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
@@ -7,6 +7,7 @@
|
||||
struct Clip;
|
||||
class Effect;
|
||||
class EffectRow;
|
||||
class EffectField;
|
||||
class TimelineHeader;
|
||||
|
||||
class KeyframeView : public QWidget {
|
||||
@@ -23,26 +24,25 @@ public:
|
||||
public slots:
|
||||
void set_x_scroll(int);
|
||||
void set_y_scroll(int);
|
||||
void resize_move(double d);
|
||||
void resize_move(double d);
|
||||
private:
|
||||
long adjust_row_keyframe(EffectRow* row, long time);
|
||||
QVector<EffectRow*> selected_rows;
|
||||
QVector<int> selected_keyframes;
|
||||
QVector<EffectField*> selected_fields;
|
||||
QVector<int> selected_keyframes;
|
||||
QVector<int> rowY;
|
||||
long frame_diff;
|
||||
QVector<EffectRow*> rows;
|
||||
QVector<long> old_key_vals;
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void draw_keyframe(QPainter& p, int type, int x, int y, bool darker);
|
||||
bool mousedown;
|
||||
bool mousedown;
|
||||
bool dragging;
|
||||
bool keys_selected;
|
||||
bool select_rect;
|
||||
bool scroll_drag;
|
||||
bool select_rect;
|
||||
bool scroll_drag;
|
||||
|
||||
bool keyframeIsSelected(EffectRow* row, int keyframe);
|
||||
bool keyframeIsSelected(EffectField *field, int keyframe);
|
||||
|
||||
long drag_frame_start;
|
||||
long last_frame_diff;
|
||||
@@ -50,12 +50,15 @@ private:
|
||||
int rect_select_y;
|
||||
int rect_select_w;
|
||||
int rect_select_h;
|
||||
int rect_select_offset;
|
||||
|
||||
int x_scroll;
|
||||
int y_scroll;
|
||||
|
||||
void update_keys();
|
||||
private slots:
|
||||
void show_context_menu(const QPoint& pos);
|
||||
void menu_set_key_type(QAction*);
|
||||
void show_context_menu(const QPoint& pos);
|
||||
void menu_set_key_type(QAction*);
|
||||
};
|
||||
|
||||
#endif // KEYFRAMEVIEW_H
|
||||
|
||||
+8
-2
@@ -16,7 +16,7 @@ LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) {
|
||||
drag_proc = false;
|
||||
min_enabled = false;
|
||||
max_enabled = false;
|
||||
setStyleSheet("QLabel{color:#ffc000;text-decoration:underline;}QLabel:disabled{color:#808080;}");
|
||||
set_color();
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
internal_value = -1;
|
||||
set = false;
|
||||
@@ -75,7 +75,12 @@ double LabelSlider::getPreviousValue() {
|
||||
}
|
||||
|
||||
void LabelSlider::set_previous_value() {
|
||||
previous_value = internal_value;
|
||||
previous_value = internal_value;
|
||||
}
|
||||
|
||||
void LabelSlider::set_color(QString c) {
|
||||
if (c.isEmpty()) c = "#ffc000";
|
||||
setStyleSheet("QLabel{color:" + c + ";text-decoration:underline;}QLabel:disabled{color:#808080;}");
|
||||
}
|
||||
|
||||
double LabelSlider::value() {
|
||||
@@ -115,6 +120,7 @@ void LabelSlider::mousePressEvent(QMouseEvent *ev) {
|
||||
drag_start_x = cursor().pos().x();
|
||||
drag_start_y = cursor().pos().y();
|
||||
}
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void LabelSlider::mouseMoveEvent(QMouseEvent* event) {
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
QString valueToString(double v);
|
||||
double getPreviousValue();
|
||||
void set_previous_value();
|
||||
void set_color(QString c = 0);
|
||||
int decimal_places;
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
@@ -53,6 +54,7 @@ private:
|
||||
double frame_rate;
|
||||
signals:
|
||||
void valueChanged();
|
||||
void clicked();
|
||||
};
|
||||
|
||||
#endif // LABELSLIDER_H
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "rectangleselect.h"
|
||||
|
||||
void draw_selection_rectangle(QPainter& painter, const QRect& rect) {
|
||||
painter.setPen(QColor(204, 204, 204));
|
||||
painter.setBrush(QColor(0, 0, 0, 32));
|
||||
painter.drawRect(rect);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef RECTANGLESELECT_H
|
||||
#define RECTANGLESELECT_H
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
void draw_selection_rectangle(QPainter& painter, const QRect& rect);
|
||||
|
||||
#endif // RECTANGLESELECT_H
|
||||
+1
-1
@@ -21,12 +21,12 @@ public:
|
||||
bool snapping;
|
||||
|
||||
void show_text(bool enable);
|
||||
void update_zoom(double z);
|
||||
double get_zoom();
|
||||
void delete_markers();
|
||||
void set_scrollbar_max(QScrollBar* bar, long sequence_end_frame, int offset);
|
||||
|
||||
public slots:
|
||||
void update_zoom(double z);
|
||||
void set_scroll(int);
|
||||
void set_visible_in(long i);
|
||||
void show_context_menu(const QPoint &pos);
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user