From 263bf090b4401063f382e8838aafa2176ba2a854 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 20 Aug 2018 23:35:56 +1000 Subject: [PATCH] made all keyframes editable and undoable --- dialogs/aboutdialog.ui | 8 +- effects/effect.cpp | 152 ++++++++++++++++++------------ effects/effect.h | 25 +++-- effects/video/shakeeffect.cpp | 2 +- effects/video/transformeffect.cpp | 16 ++-- icons/icons.qrc | 1 + icons/olive-splash.png | Bin 0 -> 8924 bytes mainwindow.cpp | 4 +- panels/effectcontrols.cpp | 4 + panels/effectcontrols.h | 1 + panels/timeline.cpp | 7 ++ panels/timeline.h | 1 + project/undo.cpp | 143 ++++++++++++++++++++++++++-- project/undo.h | 46 +++++++-- ui/checkboxex.cpp | 2 +- ui/colorbutton.cpp | 6 +- ui/comboboxex.cpp | 2 +- ui/keyframeview.cpp | 139 ++++++++++++++++++++++----- ui/keyframeview.h | 19 +++- ui/labelslider.cpp | 38 +++----- ui/labelslider.h | 4 +- ui/timelinewidget.cpp | 6 +- 22 files changed, 470 insertions(+), 156 deletions(-) create mode 100644 icons/olive-splash.png 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 0000000000000000000000000000000000000000..008682ac91e453c5099913cb6edba10781463455 GIT binary patch literal 8924 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 } 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