diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index 39e788e12..7f4c433e0 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -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))); diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index c48baacd3..ff4c52a14 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -335,7 +335,7 @@ void SpeedDialog::accept() { for (int i=0;iopen) close_clip(c); + if (c->open) close_clip(c, true); if (c->track >= 0 && maintain_pitch->checkState() != Qt::PartiallyChecked diff --git a/effects/internal/audionoiseeffect.cpp b/effects/internal/audionoiseeffect.cpp index bd0283a5b..effd65f6e 100644 --- a/effects/internal/audionoiseeffect.cpp +++ b/effects/internal/audionoiseeffect.cpp @@ -4,15 +4,15 @@ #include 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) { diff --git a/effects/internal/cornerpineffect.cpp b/effects/internal/cornerpineffect.cpp index 96069b0bf..5be93b671 100644 --- a/effects/internal/cornerpineffect.cpp +++ b/effects/internal/cornerpineffect.cpp @@ -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); } diff --git a/effects/internal/paneffect.cpp b/effects/internal/paneffect.cpp index 0a7b25a58..fcb862192 100644 --- a/effects/internal/paneffect.cpp +++ b/effects/internal/paneffect.cpp @@ -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) { diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index 0687a71ad..92975fe8a 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -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); diff --git a/effects/internal/solideffect.cpp b/effects/internal/solideffect.cpp index 0f888da61..512dccda6 100644 --- a/effects/internal/solideffect.cpp +++ b/effects/internal/solideffect.cpp @@ -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(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(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 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 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); } diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index 66a6a6108..cb3ea3155 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -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) { diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index f09b7fecc..baba8c50d 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -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); } diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index bb0fb7ac8..b17478e40 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -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) { diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 42b54497c..352e0a6e6 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -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;ikeyframes.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); } diff --git a/effects/internal/transformeffect.h b/effects/internal/transformeffect.h index c198ffb0a..c20ff6a6d 100644 --- a/effects/internal/transformeffect.h +++ b/effects/internal/transformeffect.h @@ -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 diff --git a/effects/internal/volumeeffect.cpp b/effects/internal/volumeeffect.cpp index 6a71d06a7..b7b18017b 100644 --- a/effects/internal/volumeeffect.cpp +++ b/effects/internal/volumeeffect.cpp @@ -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) { diff --git a/io/config.h b/io/config.h index ea91f7f99..394710270 100644 --- a/io/config.h +++ b/io/config.h @@ -3,8 +3,8 @@ #include -#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 diff --git a/io/loadthread.cpp b/io/loadthread.cpp index b7688a1a6..65bdc50f4 100644 --- a/io/loadthread.cpp +++ b/io/loadthread.cpp @@ -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"; } diff --git a/io/math.cpp b/io/math.cpp index c26bb5cad..4d71229a4 100644 --- a/io/math.cpp +++ b/io/math.cpp @@ -1,15 +1,54 @@ #include "math.h" #include +#include + +#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; } diff --git a/io/math.h b/io/math.h index 927e988ac..ba1e7ed9e 100644 --- a/io/math.h +++ b/io/math.h @@ -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 diff --git a/mainwindow.cpp b/mainwindow.cpp index 5ea809050..6bdb414ed 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -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(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(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(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(); diff --git a/mainwindow.h b/mainwindow.h index ed04b9e0c..76c03eb31 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -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(); diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 000000000..1daae80cf --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,973 @@ + + + MainWindow + + + + 0 + 0 + 653 + 394 + + + + MainWindow + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + 0 + 0 + 653 + 16 + + + + + &File + + + + &New + + + + + + + + + Open Recent + + + + + + + + + + + + + + + + + &Edit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Window + + + + + + + + + + + + + &Help + + + + + + &View + + + + Title/Action Safe Area + + + + + + + + + + + + + + + + + + + + + + + + + + + &Playback + + + + + + + + + + + + + &Tools + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Open Project + + + Ctrl+O + + + + + &Save Project + + + Ctrl+S + + + + + Save Project &As + + + Ctrl+Shift+S + + + + + &Import... + + + Ctrl+I + + + + + &Export... + + + Ctrl+M + + + + + E&xit + + + + + About... + + + + + &Undo + + + Ctrl+Z + + + + + &Redo + + + Ctrl+Shift+Z + + + + + Cu&t + + + Ctrl+X + + + + + Cop&y + + + Ctrl+C + + + + + &Paste + + + Ctrl+V + + + + + Paste Insert + + + Ctrl+Shift+V + + + + + Delete + + + Del + + + + + Select &All + + + Ctrl+A + + + + + true + + + Track Lines + + + + + Project... + + + Ctrl+N + + + + + Sequence... + + + Ctrl+Shift+N + + + + + Zoom In + + + = + + + + + Zoom Out + + + - + + + + + true + + + true + + + Project + + + + + true + + + true + + + Effect Controls + + + + + true + + + true + + + Sequence Viewer + + + + + true + + + true + + + Timeline + + + + + Ripple Delete + + + Shift+Del + + + + + Split at Playhead + + + Ctrl+K + + + + + Deselect All + + + + + Go to Start + + + Home + + + + + Reset to default layout + + + + + Previous Frame + + + Left + + + + + Play/Pause + + + Space + + + + + Next Frame + + + Right + + + + + Go to End + + + End + + + + + Crash + + + false + + + + + Pointer Tool + + + V + + + + + Edit Tool + + + X + + + + + Razor Tool + + + C + + + + + Ripple Tool + + + B + + + + + Rolling Tool + + + N + + + false + + + + + Preferences + + + Ctrl+. + + + + + true + + + Snapping + + + Snapping + + + S + + + + + Slip Tool + + + Y + + + + + Go to Previous Cut + + + Up + + + + + Go to Next Cut + + + Down + + + + + true + + + Edit Tool Also Seeks + + + + + Increase Track Height + + + Ctrl+= + + + + + Decrease Track Height + + + Ctrl+- + + + + + true + + + Frames + + + + + true + + + Drop-Frame + + + + + true + + + Non-Drop Frame + + + + + true + + + Edit Tool Selects Links + + + + + Duplicate + + + Ctrl+D + + + + + true + + + Selecting Also Seeks + + + + + true + + + Seek to the End of Pastes + + + + + Add Default Transition + + + Ctrl+Shift+D + + + + + Slide Tool + + + U + + + + + Folder + + + + + Clear Recent List + + + + + true + + + Scroll Wheel Zooms + + + + + Link/Unlink + + + Ctrl+L + + + + + Ripple to In Point + + + Q + + + + + Ripple to Out Point + + + W + + + + + Set In Point + + + I + + + + + Set Out Point + + + O + + + + + Clear In/Out + + + G + + + + + Delete In/Out + + + ; + + + + + Ripple Delete In/Out + + + ' + + + + + true + + + Rectified Waveforms + + + + + true + + + Default + + + + + true + + + Off + + + + + true + + + 4:3 + + + + + true + + + 16:9 + + + + + true + + + Custom + + + + + true + + + Enable Drag Files to Timeline + + + + + true + + + Auto-scale by Default + + + + + Set/Edit Marker + + + M + + + + + Enable/Disable Clip + + + Shift+E + + + + + true + + + Enable Seek to Import + + + + + true + + + Audio Scrubbing + + + + + Transition Tool + + + T + + + + + Edit to In Point + + + Ctrl+Alt+Q + + + + + Edit to Out Point + + + Ctrl+Alt+W + + + + + Nest + + + + + Toggle Show All + + + \ + + + + + true + + + Enable Drop File on Media to Replace + + + + + true + + + Footage Viewer + + + + + true + + + No Auto-scroll + + + + + true + + + Page Auto-scroll + + + + + true + + + Smooth Auto-scroll + + + + + true + + + Milliseconds + + + + + true + + + Enable Hover Focus for Zooming + + + + + true + + + Full Screen + + + F11 + + + + + Hand Tool + + + H + + + + + true + + + Graph Editor + + + + + + + diff --git a/olive.pro b/olive.pro index 1ab9f79e8..234d5c5f1 100644 --- a/olive.pro +++ b/olive.pro @@ -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 += diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 123633448..de1726900 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -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(video_effect_area->layout()); QVBoxLayout* audio_layout = static_cast(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); diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp new file mode 100644 index 000000000..2cb44f673 --- /dev/null +++ b/panels/grapheditor.cpp @@ -0,0 +1,239 @@ +#include "grapheditor.h" + +#include +#include +#include +#include + +#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;ifieldCount();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;iisKeyframing()) { + for (int i=0;ifieldCount();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(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;iset_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); +} diff --git a/panels/grapheditor.h b/panels/grapheditor.h new file mode 100644 index 000000000..1a5277b58 --- /dev/null +++ b/panels/grapheditor.h @@ -0,0 +1,44 @@ +#ifndef GRAPHEDITOR_H +#define GRAPHEDITOR_H + +#include + +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 slider_proxies; + QVector slider_proxy_buttons; + QVector 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 diff --git a/panels/panels.cpp b/panels/panels.cpp index dd8206ccc..35bed658a 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -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; +} diff --git a/panels/panels.h b/panels/panels.h index 042a652d8..691736b82 100644 --- a/panels/panels.h +++ b/panels/panels.h @@ -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 diff --git a/panels/project.cpp b/panels/project.cpp index 4b31f4676..9edfb2bc4 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -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;mlogicalDotsPerInch() / 96) * TRACK_DEFAULT_HEIGHT; diff --git a/panels/timeline.h b/panels/timeline.h index a76adc3a9..278aacbb3 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -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& ignore = QVector()); diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 5563a5943..4d5a98d32 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -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; diff --git a/playback/audio.cpp b/playback/audio.cpp index aa0c38b2b..302ad6af6 100644 --- a/playback/audio.cpp +++ b/playback/audio.cpp @@ -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() { diff --git a/playback/playback.cpp b/playback/playback.cpp index 9dd4a399d..dfb67461a 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -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;ieffects.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& 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, QVectorget_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(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(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(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;ieffects.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;ieffects.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 empty; - if (cache) cache_clip(c, playhead, reset, false, empty); + QVector 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;iclips.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;iclips.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); } } } diff --git a/playback/playback.h b/playback/playback.h index 67a18c6d4..db4a80600 100644 --- a/playback/playback.h +++ b/playback/playback.h @@ -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 &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 diff --git a/project/clip.cpp b/project/clip.cpp index a2f4eafb6..05758928c 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -12,6 +12,7 @@ #include "project/media.h" #include "io/clipboard.h" #include "undo.h" +#include "debug.h" extern "C" { #include @@ -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;irow_count();j++) { EffectRow* r = e->row(j); - for (int k=0;kkeyframe_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;lfieldCount();l++) { + EffectField* f = r->field(l); + for (int k=0;kkeyframes.size();k++) { + ca->append(new SetLong(&f->keyframes[k].time, f->keyframes[k].time, f->keyframes[k].time * multiplier)); + } } } } diff --git a/project/effect.cpp b/project/effect.cpp index 99268ba7c..0315e1c4f 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -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 get_effects_paths() { QList 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 effects_paths = get_effects_paths(); for (int h=0;henabled_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;irows.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;jfieldCount();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().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().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;kkeyframe_times.append(keyframe_frame); - row->keyframe_types.append(keyframe_type); + for (int k=0;kfieldCount();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;ktype, 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;isavable) { - stream.writeStartElement("row"); // row - stream.writeStartElement("keyframes"); // keyframes - stream.writeAttribute("enabled", QString::number(row->isKeyframing())); - for (int j=0;jkeyframe_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;jfieldCount();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;kkeyframe_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;jfieldCount();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;kkeyframes.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 effects_paths = get_effects_paths(); + const QString& test_fn = vertPath.isEmpty() ? fragPath : vertPath; + for (int i=0;iaddShaderFromSourceFile(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;ifield(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;ix_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;ix_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;iget_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;jget_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) { diff --git a/project/effect.h b/project/effect.h index 3123af3cc..290fa6cc7 100644 --- a/project/effect.h +++ b/project/effect.h @@ -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 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 rows; - QVector gizmos; + QVector gizmos; QGridLayout* ui_layout; - QWidget* ui; + QWidget* ui; bool bound; // superimpose functions @@ -201,6 +201,7 @@ private: QVector cachedValues; void delete_texture(); int get_index_in_clip(); + void validate_meta_path(); }; class EffectInit : public QThread { diff --git a/project/effectfield.cpp b/project/effectfield.cpp index d2166a835..6eda75505 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -15,347 +15,366 @@ #include "project/sequence.h" #include "io/math.h" -#include +#include + +#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(ui_element)->getPreviousValue(); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->getPreviousValue(); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPreviousValue(); - case EFFECT_FIELD_BOOL: return !static_cast(ui_element)->isChecked(); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->getPreviousIndex(); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->getPreviousValue(); - } - return QVariant(); + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_BOOL: return !static_cast(ui_element)->isChecked(); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->getPreviousIndex(); + case EFFECT_FIELD_FONT: return static_cast(ui_element)->getPreviousValue(); + } + return QVariant(); } QVariant EffectField::get_current_data() { - switch (type) { - case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->value(); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->get_color(); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPlainTextEx(); - case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); - } - return QVariant(); + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->value(); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->get_color(); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPlainTextEx(); + case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); + case EFFECT_FIELD_FONT: return static_cast(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(ui_element)->set_value(data.toDouble(), false); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->set_color(data.value()); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->setPlainTextEx(data.toString()); - case EFFECT_FIELD_BOOL: return static_cast(ui_element)->setChecked(data.toBool()); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->setCurrentIndexEx(data.toInt()); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->setCurrentTextEx(data.toString()); - } + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->set_value(data.toDouble(), false); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->set_color(data.value()); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->setPlainTextEx(data.toString()); + case EFFECT_FIELD_BOOL: return static_cast(ui_element)->setChecked(data.toBool()); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->setCurrentIndexEx(data.toInt()); + case EFFECT_FIELD_FONT: return static_cast(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;ikeyframe_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 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(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(); - } else { - QColor before_data = keyframe_data.at(before_keyframe).value(); - QColor after_data = keyframe_data.at(after_keyframe).value(); - 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(ui_element)->set_color(value); - } - break; - case EFFECT_FIELD_STRING: - if (async) { - return before_data; - } - static_cast(ui_element)->setPlainTextEx(before_data.toString()); - break; - case EFFECT_FIELD_BOOL: - if (async) { - return before_data; - } - static_cast(ui_element)->setChecked(before_data.toBool()); - break; - case EFFECT_FIELD_COMBO: - if (async) { - return before_data; - } - static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); - break; - case EFFECT_FIELD_FONT: - if (async) { - return before_data; - } - static_cast(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(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(); + } else { + QColor before_data = keyframes.at(before_keyframe).data.value(); + QColor after_data = keyframes.at(after_keyframe).data.value(); + 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(ui_element)->set_color(value); + } + break; + case EFFECT_FIELD_STRING: + if (async) { + return before_data; + } + static_cast(ui_element)->setPlainTextEx(before_data.toString()); + break; + case EFFECT_FIELD_BOOL: + if (async) { + return before_data; + } + static_cast(ui_element)->setChecked(before_data.toBool()); + break; + case EFFECT_FIELD_COMBO: + if (async) { + return before_data; + } + static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); + break; + case EFFECT_FIELD_FONT: + if (async) { + return before_data; + } + static_cast(ui_element)->setCurrentTextEx(before_data.toString()); + break; + } + } + return QVariant(); } void EffectField::ui_element_change() { - bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast(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(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(ui_element)->value(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toDouble(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->value(); } void EffectField::set_double_value(double v) { - static_cast(ui_element)->set_value(v, false); + static_cast(ui_element)->set_value(v, false); } void EffectField::set_double_default_value(double v) { - static_cast(ui_element)->set_default_value(v); + static_cast(ui_element)->set_default_value(v); } void EffectField::set_double_minimum_value(double v) { - static_cast(ui_element)->set_minimum_value(v); + static_cast(ui_element)->set_minimum_value(v); } void EffectField::set_double_maximum_value(double v) { - static_cast(ui_element)->set_maximum_value(v); + static_cast(ui_element)->set_maximum_value(v); } void EffectField::add_combo_item(const QString& name, const QVariant& data) { - static_cast(ui_element)->addItem(name, data); + static_cast(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(ui_element)->currentIndex(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toInt(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentIndex(); } const QVariant EffectField::get_combo_data(double timecode) { - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentData(); + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentData(); } const QString EffectField::get_combo_string(double timecode) { - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentText(); + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentText(); } void EffectField::set_combo_index(int index) { - static_cast(ui_element)->setCurrentIndexEx(index); + static_cast(ui_element)->setCurrentIndexEx(index); } void EffectField::set_combo_string(const QString& s) { - static_cast(ui_element)->setCurrentTextEx(s); + static_cast(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(ui_element)->isChecked(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toBool(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->isChecked(); } void EffectField::set_bool_value(bool b) { - return static_cast(ui_element)->setChecked(b); + return static_cast(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(ui_element)->getPlainTextEx(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->getPlainTextEx(); } void EffectField::set_string_value(const QString& s) { - static_cast(ui_element)->setPlainTextEx(s); + static_cast(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(ui_element)->currentText(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentText(); } void EffectField::set_font_name(const QString& s) { - static_cast(ui_element)->setCurrentText(s); + static_cast(ui_element)->setCurrentText(s); } QColor EffectField::get_color_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).value(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->get_color(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).value(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->get_color(); } void EffectField::set_color_value(QColor color) { - static_cast(ui_element)->set_color(color); + static_cast(ui_element)->set_color(color); } diff --git a/project/effectfield.h b/project/effectfield.h index f2b99002e..c946c475b 100644 --- a/project/effectfield.h +++ b/project/effectfield.h @@ -12,63 +12,66 @@ #include #include +#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 keyframe_data; - QWidget* ui_element; + QWidget* get_ui_element(); + void set_enabled(bool e); + QVector 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 diff --git a/project/effectrow.cpp b/project/effectrow.cpp index 1cfd81cb9..da203fd74 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -1,6 +1,5 @@ #include "effectrow.h" -#include #include #include #include @@ -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;ikeyframes.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;iclip_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;ikeyframes.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;itimeline_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 key_fields; + QVector key_field_index; + Clip* c = parent_effect->parent_clip; + for (int j=0;jkeyframes.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;iappend(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;itimeline_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;ikeyframes.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;iplayhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; - for (int i=0;iplayhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; + for (int j=0;jkeyframes.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;ikeyframes.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(); } diff --git a/project/effectrow.h b/project/effectrow.h index 943ba709f..8326cb1f2 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -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 keyframe_times; - QVector 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 fields; + bool keyframing; + QGridLayout* ui; + QString name; + int ui_row; + QVector fields; - KeyframeNavigator* keyframe_nav; + KeyframeNavigator* keyframe_nav; - bool just_made_unsafe_keyframe; + bool just_made_unsafe_keyframe; }; #endif // EFFECTROW_H diff --git a/project/keyframe.cpp b/project/keyframe.cpp new file mode 100644 index 000000000..09b73be75 --- /dev/null +++ b/project/keyframe.cpp @@ -0,0 +1,46 @@ +#include "keyframe.h" + +#include + +#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& selected_key_fields, QVector &selected_keys) { + QVector fields; + QVector key_indices; + + for (int i=0;i 0) { + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new KeyframeDelete(fields.at(i), key_indices.at(i))); + } + undo_stack.push(ca); + selected_keys.clear(); + selected_key_fields.clear(); + update_ui(false); + } +} diff --git a/project/keyframe.h b/project/keyframe.h new file mode 100644 index 000000000..fd7e4ac0f --- /dev/null +++ b/project/keyframe.h @@ -0,0 +1,25 @@ +#ifndef KEYFRAME_H +#define KEYFRAME_H + +#include + +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 &selected_key_fields, QVector &selected_keys); + +#endif // KEYFRAME_H diff --git a/project/undo.cpp b/project/undo.cpp index c77a30e67..a8f7eacda 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -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;jclips.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;iopen) { - 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;ikeyframe_times[keyframes.at(i)] -= movement; - } - mainWindow->setWindowModified(old_project_changed); -} - -void KeyframeMove::redo() { - for (int i=0;ikeyframe_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;ikeyframe_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;jfieldCount();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 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(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;ifieldCount();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;ifieldCount();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; +} diff --git a/project/undo.h b/project/undo.h index a1f79212f..c2e9f62a9 100644 --- a/project/undo.h +++ b/project/undo.h @@ -17,6 +17,7 @@ struct EffectMeta; #include "project/marker.h" #include "project/selection.h" +#include "project/effectfield.h" #include #include @@ -328,32 +329,17 @@ private: QString to; }; -class KeyframeMove : public QUndoCommand { -public: - KeyframeMove(); - QVector rows; - QVector keyframes; - long movement; - void undo(); - void redo(); -private: - bool old_project_changed; -}; - class KeyframeDelete : public QUndoCommand { public: - KeyframeDelete(); - QVector rows; - EffectRow* disable_keyframes_on_row; - QVector 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 deleted_keyframe_times; - QVector deleted_keyframe_types; - QVector 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 diff --git a/ui/clickablelabel.cpp b/ui/clickablelabel.cpp new file mode 100644 index 000000000..b5afb2327 --- /dev/null +++ b/ui/clickablelabel.cpp @@ -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(); +} diff --git a/ui/clickablelabel.h b/ui/clickablelabel.h new file mode 100644 index 000000000..785c4c7c6 --- /dev/null +++ b/ui/clickablelabel.h @@ -0,0 +1,16 @@ +#ifndef CLICKABLELABEL_H +#define CLICKABLELABEL_H + +#include + +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 diff --git a/ui/graphview.cpp b/ui/graphview.cpp new file mode 100644 index 000000000..39c05da7c --- /dev/null +++ b/ui/graphview.cpp @@ -0,0 +1,835 @@ +#include "graphview.h" + +#include +#include +#include +#include +#include + +#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;ifield(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;ifieldCount();i++) { + for (int j=0;jfield(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 sort_keys_from_field(EffectField* field) { + QVector sorted_keys; + for (int k=0;kkeyframes.size();k++) { + bool inserted = false; + for (int j=0;jkeyframes.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 sorted_keys = sort_keys_from_field(field); + + int last_key_x, last_key_y; + + // draw lines + for (int j=0;jkeyframes.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;jkeyframes.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;kseq->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;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + for (int j=0;jkeyframes.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;imodifiers() & 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;ifieldCount();i++) { + EffectField* f = row->field(i); + for (int j=0;jkeyframes.size();j++) { + bool already_selected = false; + for (int k=0;kkeyframes.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;ifield(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;ifieldCount();i++) { + for (int j=0;jfield(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;ifieldCount();i++) { + EffectField* f = row->field(i); + if (field_visibility.at(i)) { + QVector 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;jkeyframes.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;ifield(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;ifield(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 fields; + for (int i=0;ifield(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;ifieldCount();i++) { + EffectField* field = row->field(i); + for (int j=0;jkeyframes.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;ifield(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); +} diff --git a/ui/graphview.h b/ui/graphview.h new file mode 100644 index 000000000..1a678083a --- /dev/null +++ b/ui/graphview.h @@ -0,0 +1,99 @@ +#ifndef GRAPHVIEW_H +#define GRAPHVIEW_H + +#include +#include + +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 field_visibility; + + QVector selected_keys; + QVector selected_keys_fields; + QVector selected_keys_old_vals; + QVector 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 diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp new file mode 100644 index 000000000..53229e46c --- /dev/null +++ b/ui/keyframedrawing.cpp @@ -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); +} diff --git a/ui/keyframedrawing.h b/ui/keyframedrawing.h new file mode 100644 index 000000000..c8c60e9e3 --- /dev/null +++ b/ui/keyframedrawing.h @@ -0,0 +1,11 @@ +#ifndef KEYFRAMEDRAWING_H +#define KEYFRAMEDRAWING_H + +#include + +#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 diff --git a/ui/keyframenavigator.cpp b/ui/keyframenavigator.cpp index a7022fe45..f5495a135 100644 --- a/ui/keyframenavigator.cpp +++ b/ui/keyframenavigator.cpp @@ -2,15 +2,22 @@ #include #include +#include +#include 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) { diff --git a/ui/keyframenavigator.h b/ui/keyframenavigator.h index d41a7ac0d..6b6eaee85 100644 --- a/ui/keyframenavigator.h +++ b/ui/keyframenavigator.h @@ -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: diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 914b979b3..ba8557694 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -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 #include #include #include -#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;iappend(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;iappend(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;jrow_count();j++) { EffectRow* row = e->row(j); - QLabel* label = row->label; + ClickableLabel* label = row->label; QWidget* contents = e->container->contents; + QVector 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;kkeyframe_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;lfieldCount();l++) { + EffectField* f = row->field(l); + for (int k=0;kkeyframes.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;mfieldCount();m++) { + EffectField* compf = row->field(m); + for (int n=0;nkeyframes.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;idelete_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.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && mouse_y < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) { EffectRow* row = rows.at(i); - for (int j=0;jkeyframe_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;kfieldCount();k++) { + EffectField* f = row->field(k); + for (int j=0;jkeyframes.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;ifieldCount();i++) { + if (i != field_index) { + EffectField* f = rows.at(row_index)->field(i); + for (int j=0;jkeyframes.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;ikeyframes.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;iparent_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;ikeyframe_times.at(selected_keyframes.at(i)); - for (int j=0;jkeyframe_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= min_row && rowY.at(i) <= max_row) { EffectRow* row = rows.at(i); - for (int j=0;jkeyframe_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;kfieldCount();k++) { + EffectField* field = row->field(k); + for (int j=0;jkeyframes.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;iparent_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;ikeyframes.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;ikeyframes[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;iappend(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); } diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 6c3a72f31..51bbd361d 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -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 selected_rows; - QVector selected_keyframes; + QVector selected_fields; + QVector selected_keyframes; QVector rowY; - long frame_diff; QVector rows; + QVector 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 diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index 357a891db..7395ad752 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -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) { diff --git a/ui/labelslider.h b/ui/labelslider.h index 3476128ff..f671d18df 100644 --- a/ui/labelslider.h +++ b/ui/labelslider.h @@ -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 diff --git a/ui/rectangleselect.cpp b/ui/rectangleselect.cpp new file mode 100644 index 000000000..b4a9348ce --- /dev/null +++ b/ui/rectangleselect.cpp @@ -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); +} diff --git a/ui/rectangleselect.h b/ui/rectangleselect.h new file mode 100644 index 000000000..40eee2aeb --- /dev/null +++ b/ui/rectangleselect.h @@ -0,0 +1,8 @@ +#ifndef RECTANGLESELECT_H +#define RECTANGLESELECT_H + +#include + +void draw_selection_rectangle(QPainter& painter, const QRect& rect); + +#endif // RECTANGLESELECT_H diff --git a/ui/timelineheader.h b/ui/timelineheader.h index 884a44adf..2950cb0fc 100644 --- a/ui/timelineheader.h +++ b/ui/timelineheader.h @@ -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); diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index c51530b22..3dd273c77 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -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" diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 9a1681dc7..8481f90fa 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -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& 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& 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& 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) {