diff --git a/dialogs/aboutdialog.ui b/dialogs/aboutdialog.ui index 8033a5596..e56d0baa1 100644 --- a/dialogs/aboutdialog.ui +++ b/dialogs/aboutdialog.ui @@ -6,18 +6,18 @@ 0 0 - 400 - 220 + 358 + 422 - Dialog + About Olive - Olive is a non-linear video editor. This software is free and protected by the GNU GPL. + <html><head/><body><p><img src=":/icons/olive-splash.png"/></p><p><a href="https://www.olivevideoeditor.org/"><span style=" text-decoration: underline; color:#007af4;">https://www.olivevideoeditor.org/</span></a></p><p>Olive is a non-linear video editor. This software is free and protected by the GNU GPL.</p><p>Olive Team is obliged to inform users that Olive source code is available for download from its website.</p><p>Olive uses (at least) the following libraries in accordance with the GNU GPL/LGPL:</p><p>Qt, FFmpeg, libass, libfreetype, libmp3lame, libopenjpeg, libopus, libtheora, libtwolame, libvpx, libwavpack, libwebp, libx264, libx265, lzma, bzlib, zlib, libvidstab, libvorbis.</p></body></html> Qt::AlignCenter diff --git a/effects/effect.cpp b/effects/effect.cpp index be1bd5113..3e8254332 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -29,6 +29,7 @@ #include #include #include +#include QVector video_effect_names; QVector audio_effect_names; @@ -101,7 +102,7 @@ Effect::~Effect() { void Effect::copy_field_keyframes(Effect* e) { for (int i=0;irows.at(i)->keyframing = rows.at(i)->keyframing; + e->rows.at(i)->setKeyframing(rows.at(i)->isKeyframing()); e->rows.at(i)->keyframe_times = rows.at(i)->keyframe_times; e->rows.at(i)->keyframe_types = rows.at(i)->keyframe_types; for (int j=0;jfieldCount();j++) { @@ -178,11 +179,11 @@ void Effect::load(QXmlStreamReader& stream) { for (int k=0;kkeyframing = (attr.value() == "1"); + row->setKeyframing(attr.value() == "1"); break; } } - if (row->keyframing) { + if (row->isKeyframing()) { stream.readNext(); while (!stream.atEnd() && !(stream.name() == "keyframes" && stream.isEndElement())) { if (stream.name() == "key" && stream.isStartElement()) { @@ -248,7 +249,7 @@ void Effect::save(QXmlStreamWriter& stream) { EffectRow* row = rows.at(i); stream.writeStartElement("row"); // row stream.writeStartElement("keyframes"); // keyframes - stream.writeAttribute("enabled", QString::number(row->keyframing)); + 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))); @@ -286,20 +287,44 @@ EffectRow::EffectRow(Effect *parent, QGridLayout *uilayout, const QString &n, in keyframing(false), ui(uilayout), name(n), - ui_row(row) + ui_row(row), + just_made_unsafe_keyframe(false) { label = new QLabel(name); ui->addWidget(label, row, 0); - // DEBUG STARTS - QPushButton* nkf = new QPushButton(); - nkf->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); - connect(nkf, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_now())); - ui->addWidget(nkf, row, 5); - // DEBUG ENDS - /*keyframe_enable = new CheckboxEx(); + keyframe_enable = new CheckboxEx(); keyframe_enable->setToolTip("Enable Keyframes"); - ui->addWidget(keyframe_enable, row, 5);*/ + connect(keyframe_enable, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_enabled(bool))); + ui->addWidget(keyframe_enable, row, 6); +} + +bool EffectRow::isKeyframing() { + return keyframing; +} + +void EffectRow::setKeyframing(bool b) { + keyframing = b; + keyframe_enable->setChecked(b); +} + +void EffectRow::set_keyframe_enabled(bool enabled) { + if (enabled) { + set_keyframe_now(true); + } 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); + } + } } EffectField* EffectRow::add_field(int type, int colspan) { @@ -316,27 +341,42 @@ EffectRow::~EffectRow() { } } -void EffectRow::set_keyframe_now() { - set_keyframe(panel_timeline->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in); +void EffectRow::set_keyframe_now(bool undoable) { + int index = -1; + long time = panel_timeline->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; + for (int i=0;iredo(); + delete ks; + } + + panel_effect_controls->update_keyframes(); } -void EffectRow::set_keyframe(long time) { - keyframing = true; - int index = -1; - for (int i=0;i 0) ? keyframe_types.last() : EFFECT_KEYFRAME_LINEAR); - } - for (int i=0;iset_keyframe_data(index); - } - panel_effect_controls->update_keyframes(); +void EffectRow::delete_keyframe_at_time(KeyframeDelete* kd, long time) { + for (int i=0;irows.append(this); + kd->keyframes.append(index); } EffectField* EffectRow::field(int i) { @@ -421,15 +461,7 @@ void EffectField::set_current_data(const QVariant& data) { } } -void EffectField::set_keyframe_data(int i) { - if (i == -1) { - keyframe_data.append(get_current_data()); - } else { - keyframe_data[i] = get_current_data(); - } -} - -void EffectField::get_keyframe_data(long frame, int* before, int* after, double* progress) { +void EffectField::get_keyframe_data(long frame, int &before, int &after, double &progress) { int before_keyframe_index = -1; int after_keyframe_index = -1; long before_keyframe_time = LONG_MIN; @@ -438,8 +470,8 @@ void EffectField::get_keyframe_data(long frame, int* before, int* after, double* 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; + before = i; + after = i; return; } else if (eval_keyframe_time < frame && eval_keyframe_time > before_keyframe_time) { before_keyframe_index = i; @@ -448,40 +480,40 @@ void EffectField::get_keyframe_data(long frame, int* before, int* after, double* after_keyframe_index = i; after_keyframe_time = eval_keyframe_time; } - } + } if (type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) { if (before_keyframe_index > -1 && after_keyframe_index > -1) { // interpolate - *before = before_keyframe_index; - *after = after_keyframe_index; - *progress = (double)(frame-before_keyframe_time)/(double)(after_keyframe_time-before_keyframe_time); + before = before_keyframe_index; + after = after_keyframe_index; + progress = (double)(frame-before_keyframe_time)/(double)(after_keyframe_time-before_keyframe_time); // TODO routines for bezier - currently this is purely linear } else if (before_keyframe_index > -1) { - *before = before_keyframe_index; - *after = before_keyframe_index; + before = before_keyframe_index; + after = before_keyframe_index; } else if (after_keyframe_index > -1) { - *before = after_keyframe_index; - *after = after_keyframe_index; + before = after_keyframe_index; + after = after_keyframe_index; } } else { if (before_keyframe_index > -1) { - *before = before_keyframe_index; - *after = before_keyframe_index; + before = before_keyframe_index; + after = before_keyframe_index; } else { - *before = after_keyframe_time; - *after = after_keyframe_time; + before = after_keyframe_index; + after = after_keyframe_index; } } } void EffectField::validate_keyframe_data(long frame) { - if (parent_row->keyframing) { + if (parent_row->isKeyframing()) { int before_keyframe; int after_keyframe; double progress; - get_keyframe_data(frame, &before_keyframe, &after_keyframe, &progress); + get_keyframe_data(frame, before_keyframe, after_keyframe, progress); const QVariant& before_data = keyframe_data.at(before_keyframe); switch (type) { @@ -524,14 +556,12 @@ void EffectField::validate_keyframe_data(long frame) { static_cast(ui_element)->setCurrentTextEx(before_data.toString()); break; } - - } } void EffectField::uiElementChange() { - if (parent_row->keyframing) { - parent_row->set_keyframe_now(); + if (parent_row->isKeyframing()) { + parent_row->set_keyframe_now(!(type == EFFECT_FIELD_DOUBLE && static_cast(ui_element)->is_dragging())); } emit changed(); } diff --git a/effects/effect.h b/effects/effect.h index 5a7b90cdd..60b9adcda 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -17,6 +17,7 @@ class QXmlStreamWriter; class Effect; class EffectRow; class CheckboxEx; +class KeyframeDelete; enum VideoEffects { VIDEO_TRANSFORM_EFFECT, @@ -81,9 +82,8 @@ public: int type; QVariant get_current_data(); - void set_current_data(const QVariant&); - void set_keyframe_data(int i); - void get_keyframe_data(long frame, int* before, int *after, double* d); + void set_current_data(const QVariant&); + void get_keyframe_data(long frame, int& before, int& after, double& d); void validate_keyframe_data(long frame); // QVariant get_keyframe_data(long p); // bool is_keyframed(long p); @@ -116,8 +116,8 @@ public: QWidget* get_ui_element(); void set_enabled(bool e); QVector keyframe_data; + QWidget* ui_element; private: - QWidget* ui_element; private slots: void uiElementChange(); signals: @@ -133,24 +133,29 @@ public: EffectField* add_field(int type, int colspan = 1); EffectField* field(int i); int fieldCount(); - void set_keyframe(long time); - void move_keyframe(long from, long to); - void delete_keyframe(long time); + void set_keyframe_now(bool undoable); + void delete_keyframe(KeyframeDelete *kd, int index); + void delete_keyframe_at_time(KeyframeDelete* kd, long time); QLabel* label; Effect* parent_effect; - bool keyframing; + bool isKeyframing(); + void setKeyframing(bool); + QVector keyframe_times; QVector keyframe_types; -public slots: - void set_keyframe_now(); +private slots: + void set_keyframe_enabled(bool); private: + bool keyframing; QGridLayout* ui; QString name; int ui_row; QVector fields; CheckboxEx* keyframe_enable; + + bool just_made_unsafe_keyframe; }; class Effect : public QObject { diff --git a/effects/video/shakeeffect.cpp b/effects/video/shakeeffect.cpp index 6800d82d5..73a40b649 100644 --- a/effects/video/shakeeffect.cpp +++ b/effects/video/shakeeffect.cpp @@ -49,7 +49,7 @@ void ShakeEffect::refresh() { } } -void ShakeEffect::process_gl(long frame, QOpenGLShaderProgram& shaders, GLTextureCoords& coords) { +void ShakeEffect::process_gl(long frame, QOpenGLShaderProgram&, GLTextureCoords&) { if (shake_progress > shake_limit) { double ival = intensity_val->get_double_value(frame); if ((int)ival > 0) { diff --git a/effects/video/transformeffect.cpp b/effects/video/transformeffect.cpp index 1d5c21737..01f9de90c 100644 --- a/effects/video/transformeffect.cpp +++ b/effects/video/transformeffect.cpp @@ -128,14 +128,14 @@ void TransformEffect::process_gl(long frame, QOpenGLShaderProgram&, GLTextureCoo // anchor point int anchor_x_offset = (anchor_x_box->get_double_value(frame)-default_anchor_x); int anchor_y_offset = (anchor_y_box->get_double_value(frame)-default_anchor_y); - coords.vertexTopLeftX += anchor_x_offset; - coords.vertexTopRightX += anchor_x_offset; - coords.vertexBottomLeftX += anchor_x_offset; - coords.vertexBottomRightX += anchor_x_offset; - coords.vertexTopLeftY += anchor_y_offset; - coords.vertexTopRightY += anchor_y_offset; - coords.vertexBottomLeftY += anchor_y_offset; - coords.vertexBottomRightY += anchor_y_offset; + coords.vertexTopLeftX -= anchor_x_offset; + coords.vertexTopRightX -= anchor_x_offset; + coords.vertexBottomLeftX -= anchor_x_offset; + coords.vertexBottomRightX -= anchor_x_offset; + coords.vertexTopLeftY -= anchor_y_offset; + coords.vertexTopRightY -= anchor_y_offset; + coords.vertexBottomLeftY -= anchor_y_offset; + coords.vertexBottomRightY -= anchor_y_offset; // rotation glRotatef(rotation->get_double_value(frame), 0, 0, 1); diff --git a/icons/icons.qrc b/icons/icons.qrc index 4c80d9b23..4e55ea1b1 100644 --- a/icons/icons.qrc +++ b/icons/icons.qrc @@ -38,5 +38,6 @@ zoomout-disabled.png add-transition.png add-effect.png + olive-splash.png diff --git a/icons/olive-splash.png b/icons/olive-splash.png new file mode 100644 index 000000000..008682ac9 Binary files /dev/null and b/icons/olive-splash.png differ diff --git a/mainwindow.cpp b/mainwindow.cpp index 7bb462e35..94388a015 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -186,7 +186,9 @@ void MainWindow::on_actionDelete_triggered() panel_effect_controls->delete_effects(); } else if (panel_project->is_focused()) { panel_project->delete_selected_media(); - } + } else if (panel_effect_controls->keyframe_focus()) { + panel_effect_controls->delete_selected_keyframes(); + } } void MainWindow::on_actionSelect_All_triggered() diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index adced628a..caa1a4f5f 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -89,6 +89,10 @@ void EffectControls::update_keyframes() { ui->keyframeView->update(); } +void EffectControls::delete_selected_keyframes() { + ui->keyframeView->delete_selected_keyframes(); +} + void EffectControls::show_effect_menu(bool video, bool transitions) { video_menu = video; transition_menu = transitions; diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index c2b871c99..6d9df4aca 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -38,6 +38,7 @@ public: void reload_clips(); void set_zoom(bool in); bool keyframe_focus(); + void delete_selected_keyframes(); double zoom; public slots: diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 772f230ca..9c5fb5c92 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -22,12 +22,19 @@ #include #include #include +#include long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) { if (source_frame_rate == target_frame_rate) return framenumber; return qRound(((double)framenumber/source_frame_rate)*target_frame_rate); } +void draw_selection_rectangle(QPainter& painter, const QRect& rect) { + painter.setPen(QColor(204, 204, 204)); + painter.setBrush(QColor(0, 0, 0, 32)); + painter.drawRect(rect); +} + Timeline::Timeline(QWidget *parent) : QDockWidget(parent), playhead(0), diff --git a/panels/timeline.h b/panels/timeline.h index 4152aaab2..f3172f839 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -24,6 +24,7 @@ int lerp(int a, int b, double t); 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); struct Ghost { int clip; diff --git a/project/undo.cpp b/project/undo.cpp index 4e5994d38..2ccb4fc13 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -787,17 +787,146 @@ void MediaRename::redo() { project_changed = true; } -ValueChangeCommand::ValueChangeCommand() : done(true), old_project_changed(project_changed) {} +KeyframeMove::KeyframeMove() : old_project_changed(project_changed) {} -void ValueChangeCommand::undo() { - source->set_value(old_val, false); - done = false; +void KeyframeMove::undo() { + for (int i=0;ikeyframe_times[keyframes.at(i)] -= movement; + } project_changed = old_project_changed; } -void ValueChangeCommand::redo() { - if (!done) { - source->set_value(new_val, false); +void KeyframeMove::redo() { + for (int i=0;ikeyframe_times[keyframes.at(i)] += movement; } project_changed = true; } + +KeyframeDelete::KeyframeDelete() : old_project_changed(project_changed), disable_keyframes_on_row(NULL), sorted(false) {} + +void KeyframeDelete::undo() { + if (disable_keyframes_on_row != NULL) disable_keyframes_on_row->setKeyframing(true); + + int data_index = 0; + for (int i=0;ikeyframe_times.insert(keyframe_index, deleted_keyframe_times.at(i)); + row->keyframe_types.insert(keyframe_index, deleted_keyframe_types.at(i)); + + for (int j=0;jfieldCount();j++) { + row->field(j)->keyframe_data.insert(keyframe_index, deleted_keyframe_data.at(data_index)); + data_index++; + } + } + + project_changed = old_project_changed; +} + +void KeyframeDelete::redo() { + if (!sorted) { + for (int i=0;i keyframes.at(j)) { + int temp = keyframes.at(i); + EffectRow* temp_row = rows.at(i); + + keyframes[i] = keyframes.at(j); + rows[i] = rows.at(j); + + keyframes[j] = temp; + rows[j] = temp_row; + } + } + } + sorted = true; + } + + 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++) { + deleted_keyframe_data.append(row->field(j)->keyframe_data.at(keyframe_index)); + row->field(j)->keyframe_data.removeAt(keyframe_index); + } + } + + if (disable_keyframes_on_row != NULL) disable_keyframes_on_row->setKeyframing(false); + project_changed = true; +} + +KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : + old_project_changed(project_changed), + row(r), + index(i), + time(t), + just_made_keyframe(justMadeKeyframe), + done(true) +{ + enable_keyframes = !row->isKeyframing(); + if (index != -1) old_values.resize(row->fieldCount()); + new_values.resize(row->fieldCount()); + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (index != -1) { + if (field->type == EFFECT_FIELD_DOUBLE) { + old_values[i] = static_cast(field->ui_element)->get_drag_start_value(); + } else { + old_values[i] = field->keyframe_data.at(index); + } + } + new_values[i] = field->get_current_data(); + } +} + +void KeyframeSet::undo() { + if (enable_keyframes) row->setKeyframing(false); + + bool append = (index == -1 || just_made_keyframe); + if (append) { + row->keyframe_times.removeLast(); + row->keyframe_types.removeLast(); + } + for (int i=0;ifieldCount();i++) { + if (append) { + row->field(i)->keyframe_data.removeLast(); + } else { + row->field(i)->keyframe_data[index] = old_values.at(i); + } + } + + project_changed = old_project_changed; + done = false; +} + +void KeyframeSet::redo() { + bool append = (index == -1 || (just_made_keyframe && !done)); + if (append) { + row->keyframe_times.append(time); + row->keyframe_types.append((row->keyframe_types.size() > 0) ? row->keyframe_types.last() : EFFECT_KEYFRAME_LINEAR); + } + for (int i=0;ifieldCount();i++) { + if (append) { + row->field(i)->keyframe_data.append(new_values.at(i)); + } else { + row->field(i)->keyframe_data[index] = new_values.at(i); + } + } + row->setKeyframing(true); + + project_changed = true; + done = true; +} diff --git a/project/undo.h b/project/undo.h index 91621af35..ed6e5fee8 100644 --- a/project/undo.h +++ b/project/undo.h @@ -6,6 +6,7 @@ class QCheckBox; class LabelSlider; class Effect; class SourceTable; +class EffectRow; struct Clip; struct Sequence; struct Media; @@ -200,17 +201,50 @@ private: bool old_project_changed; }; -class ValueChangeCommand : public QUndoCommand { +class KeyframeMove : public QUndoCommand { public: - ValueChangeCommand(); - LabelSlider* source; - float old_val; - float new_val; + KeyframeMove(); + QVector rows; + QVector keyframes; + long movement; void undo(); void redo(); private: - bool done; bool old_project_changed; }; +class KeyframeDelete : public QUndoCommand { +public: + KeyframeDelete(); + QVector rows; + EffectRow* disable_keyframes_on_row; + QVector keyframes; + void undo(); + void redo(); +private: + bool old_project_changed; + QVector deleted_keyframe_times; + QVector deleted_keyframe_types; + QVector deleted_keyframe_data; + bool sorted; +}; + + +class KeyframeSet : public QUndoCommand { +public: + KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); + void undo(); + void redo(); + QVector old_values; + QVector new_values; +private: + bool old_project_changed; + EffectRow* row; + int index; + long time; + bool enable_keyframes; + bool just_made_keyframe; + bool done; +}; + #endif // UNDO_H diff --git a/ui/checkboxex.cpp b/ui/checkboxex.cpp index 9ec062d1a..c9134c321 100644 --- a/ui/checkboxex.cpp +++ b/ui/checkboxex.cpp @@ -3,7 +3,7 @@ #include "project/undo.h" CheckboxEx::CheckboxEx(QWidget* parent) : QCheckBox(parent) { - connect(this, SIGNAL(clicked(bool)), this, SLOT(checkbox_command())); +// connect(this, SIGNAL(clicked(bool)), this, SLOT(checkbox_command())); } void CheckboxEx::checkbox_command() { diff --git a/ui/colorbutton.cpp b/ui/colorbutton.cpp index e2062961a..2ddfafb22 100644 --- a/ui/colorbutton.cpp +++ b/ui/colorbutton.cpp @@ -29,8 +29,10 @@ void ColorButton::open_dialog() { QColor old_color = color; QColor new_color = QColorDialog::getColor(color, NULL); if (new_color.isValid() && old_color != new_color) { - ColorCommand* command = new ColorCommand(this, old_color, new_color); - undo_stack.push(command); + ColorCommand* command = new ColorCommand(this, old_color, new_color); +// undo_stack.push(command); + command->redo(); + delete command; set_button_color(); diff --git a/ui/comboboxex.cpp b/ui/comboboxex.cpp index 147dd27a3..6c1243561 100644 --- a/ui/comboboxex.cpp +++ b/ui/comboboxex.cpp @@ -45,7 +45,7 @@ void ComboBoxEx::setCurrentTextEx(const QString &text) { void ComboBoxEx::index_changed(int i) { if (index != i) { - undo_stack.push(new ComboBoxExCommand(this, index, i)); +// undo_stack.push(new ComboBoxExCommand(this, index, i)); index = i; } } diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index db91f4c65..84842d262 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -7,6 +7,9 @@ #include "project/clip.h" #include "panels/timeline.h" #include "ui/timelineheader.h" +#include "project/undo.h" +#include "panels/viewer.h" +#include "ui/viewerwidget.h" #include #include @@ -14,7 +17,11 @@ #define KEYFRAME_SIZE 6 #define KEYFRAME_POINT_COUNT 4 -KeyframeView::KeyframeView(QWidget *parent) : QWidget(parent), mousedown(false), visible_in(0), visible_out(0) { +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); +} + +KeyframeView::KeyframeView(QWidget *parent) : QWidget(parent), mousedown(false), dragging(false), keys_selected(false), select_rect(false), visible_in(0), visible_out(0) { setFocusPolicy(Qt::ClickFocus); setMouseTracking(true); } @@ -39,15 +46,10 @@ void KeyframeView::paintEvent(QPaintEvent*) { 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(); for (int k=0;kkeyframe_times.size();k++) { - bool keyframe_selected = false; - for (int l=0;lzoom, row->keyframe_times.at(k)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in)), keyframe_y, keyframe_selected); + 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, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame), keyframe_y, keyframe_selected); } rows.append(row); @@ -62,11 +64,43 @@ void KeyframeView::paintEvent(QPaintEvent*) { p.drawLine(playhead_x, 0, playhead_x, height()); } + if (select_rect) { + draw_selection_rectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h)); + } + /*if (mouseover && mouseover_row < rowY.size()) { draw_keyframe(p, getScreenPointFromFrame(panel_effect_controls->zoom, mouseover_frame - visible_in), rowY.at(mouseover_row), true); }*/ } +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); + + selected_keyframes.clear(); + selected_rows.clear(); + update(); + panel_viewer->viewer_widget->update(); + } else { + delete kd; + } +} + void KeyframeView::draw_keyframe(QPainter &p, int x, int y, bool darker) { 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)}; int color = (darker) ? 100 : 160; @@ -80,7 +114,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { int keyframe_index = -1; long frame_diff = 0; long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()-KEYFRAME_SIZE); - long frame_mid = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()); + drag_frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()); long frame_max = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()+KEYFRAME_SIZE); for (int i=0;iy() > rowY.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && event->y() < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) { @@ -88,7 +122,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { 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 - frame_mid); + 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; @@ -100,24 +134,26 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { } } bool already_selected = false; - if (keyframe_index > -1) { - for (int i=0;i -1) already_selected = keyframeIsSelected(rows.at(row_index), keyframe_index); if (!already_selected) { if (!(event->modifiers() & Qt::ShiftModifier)) { selected_rows.clear(); selected_keyframes.clear(); } if (keyframe_index > -1) { - selected_rows.append(row_index); + selected_rows.append(rows.at(row_index)); selected_keyframes.append(keyframe_index); } } + if (selected_rows.size() > 0) { + keys_selected = true; + } else { + rect_select_x = event->x(); + rect_select_y = event->y(); + } + update(); mousedown = true; } @@ -138,11 +174,70 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { update(); } mouseover = new_mo;*/ - if (mousedown) { + if (mousedown) { + if (keys_selected) { + frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()) - drag_frame_start; + // validate frame_diff + 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++; + } else { + frame_diff--; + } + } + } + } + + last_frame_diff = frame_diff; + + dragging = true; + } else { + rect_select_w = event->x() - rect_select_x; + rect_select_h = event->y() - rect_select_y; + + int min_row = qMin(rect_select_y, event->y())-KEYFRAME_SIZE; + int max_row = qMax(rect_select_y, event->y())+KEYFRAME_SIZE; + + long frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, rect_select_x); + long frame_end = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()); + long min_frame = qMin(frame_start, frame_end)-KEYFRAME_SIZE; + long max_frame = qMax(frame_start, frame_end)+KEYFRAME_SIZE; + + 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); + } + } + } + } + + select_rect = true; + } + update(); } } -void KeyframeView::mouseReleaseEvent(QMouseEvent* event) { +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); + } + + select_rect = false; + dragging = false; mousedown = false; + update(); } diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 10c9a9f63..ec62761c9 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -15,14 +15,18 @@ public: KeyframeView(QWidget* parent = 0); QVector effects; + void delete_selected_keyframes(); + TimelineHeader* header; long visible_in; long visible_out; private: - QVector selected_rows; + long adjust_row_keyframe(EffectRow* row, long time); + QVector selected_rows; QVector selected_keyframes; QVector rowY; + long frame_diff; QVector rows; void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); @@ -30,9 +34,18 @@ private: void paintEvent(QPaintEvent *event); void draw_keyframe(QPainter& p, int x, int y, bool darker); bool mousedown; + bool dragging; + bool keys_selected; + bool select_rect; - int drag_row_index; - int drag_keyframe_index; + bool keyframeIsSelected(EffectRow* row, int keyframe); + + long drag_frame_start; + long last_frame_diff; + int rect_select_x; + int rect_select_y; + int rect_select_w; + int rect_select_h; }; #endif // KEYFRAMEVIEW_H diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index 85a847903..c6ef7025a 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -8,6 +8,7 @@ LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) { drag_start = false; + drag_proc = false; min_enabled = false; max_enabled = false; setStyleSheet("QLabel{color:#ffc000;text-decoration:underline;}QLabel:disabled{color:#808080;}"); @@ -38,6 +39,10 @@ bool LabelSlider::is_set() { return set; } +bool LabelSlider::is_dragging() { + return drag_proc; +} + double LabelSlider::value() { return internal_value; } @@ -60,18 +65,16 @@ void LabelSlider::set_maximum_value(double v) { max_enabled = true; } -void LabelSlider::mousePressEvent(QMouseEvent *ev) { - if (ev->modifiers() & Qt::AltModifier) { - ValueChangeCommand* vcc = new ValueChangeCommand(); - vcc->source = this; - vcc->old_val = internal_value; - vcc->new_val = default_value; - undo_stack.push(vcc); +double LabelSlider::get_drag_start_value() { + return drag_start_value; +} +void LabelSlider::mousePressEvent(QMouseEvent *ev) { + drag_start_value = internal_value; + if (ev->modifiers() & Qt::AltModifier) { set_value(default_value, true); } else { qApp->setOverrideCursor(Qt::BlankCursor); - drag_start_value = internal_value; drag_start = true; drag_start_x = cursor().pos().x(); drag_start_y = cursor().pos().y(); @@ -80,9 +83,9 @@ void LabelSlider::mousePressEvent(QMouseEvent *ev) { void LabelSlider::mouseMoveEvent(QMouseEvent*) { if (drag_start) { + drag_proc = true; set_value(internal_value + (cursor().pos().x()-drag_start_x) + (drag_start_y-cursor().pos().y()), true); cursor().setPos(drag_start_x, drag_start_y); - drag_proc = true; } } @@ -91,25 +94,12 @@ void LabelSlider::mouseReleaseEvent(QMouseEvent*) { qApp->restoreOverrideCursor(); drag_start = false; if (drag_proc) { - // send undo event - ValueChangeCommand* vcc = new ValueChangeCommand(); - vcc->source = this; - vcc->old_val = drag_start_value; - vcc->new_val = internal_value; - undo_stack.push(vcc); - - drag_proc = false; + drag_proc = false; + emit valueChanged(); } else { double d = QInputDialog::getDouble(this, "Set Value", "New value:", internal_value); if (d != internal_value) { - ValueChangeCommand* vcc = new ValueChangeCommand(); - vcc->source = this; - vcc->old_val = internal_value; - set_value(d, true); - - vcc->new_val = internal_value; - undo_stack.push(vcc); } } } diff --git a/ui/labelslider.h b/ui/labelslider.h index 72e8c2120..04a9dbb05 100644 --- a/ui/labelslider.h +++ b/ui/labelslider.h @@ -15,6 +15,8 @@ public: void set_maximum_value(double v); double value(); bool is_set(); + double get_drag_start_value(); + bool is_dragging(); protected: void mousePressEvent(QMouseEvent *ev); void mouseMoveEvent(QMouseEvent *ev); @@ -36,7 +38,7 @@ private: bool set; signals: - void valueChanged(); + void valueChanged(); }; #endif // LABELSLIDER_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index fb8190bc0..f4fe68882 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -1793,10 +1793,8 @@ void TimelineWidget::paintEvent(QPaintEvent*) { if (bottom_align) { rsy += height(); } - QRect rect_select(panel_timeline->rect_select_x, rsy, panel_timeline->rect_select_w, rsh); - p.setPen(QColor(204, 204, 204)); - p.drawRect(rect_select); - p.fillRect(rect_select, QColor(0, 0, 0, 32)); + QRect rect_select(panel_timeline->rect_select_x, rsy, panel_timeline->rect_select_w, rsh); + draw_selection_rectangle(p, rect_select); } // Draw ghosts