made all keyframes editable and undoable

This commit is contained in:
itsmattkc
2018-08-20 23:35:56 +10:00
parent 33d3cc34c8
commit 263bf090b4
22 changed files with 470 additions and 156 deletions
+4 -4
View File
@@ -6,18 +6,18 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>220</height>
<width>358</width>
<height>422</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>About Olive</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Olive is a non-linear video editor. This software is free and protected by the GNU GPL.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/olive-splash.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.olivevideoeditor.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;https://www.olivevideoeditor.org/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Olive is a non-linear video editor. This software is free and protected by the GNU GPL.&lt;/p&gt;&lt;p&gt;Olive Team is obliged to inform users that Olive source code is available for download from its website.&lt;/p&gt;&lt;p&gt;Olive uses (at least) the following libraries in accordance with the GNU GPL/LGPL:&lt;/p&gt;&lt;p&gt;Qt, FFmpeg, libass, libfreetype, libmp3lame, libopenjpeg, libopus, libtheora, libtwolame, libvpx, libwavpack, libwebp, libx264, libx265, lzma, bzlib, zlib, libvidstab, libvorbis.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
+91 -61
View File
@@ -29,6 +29,7 @@
#include <QTextEdit>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QMessageBox>
QVector<QString> video_effect_names;
QVector<QString> audio_effect_names;
@@ -101,7 +102,7 @@ Effect::~Effect() {
void Effect::copy_field_keyframes(Effect* e) {
for (int i=0;i<rows.size();i++) {
EffectRow* row = rows.at(i);
e->rows.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;j<row->fieldCount();j++) {
@@ -178,11 +179,11 @@ void Effect::load(QXmlStreamReader& stream) {
for (int k=0;k<stream.attributes().size();k++) {
const QXmlStreamAttribute& attr = stream.attributes().at(k);
if (attr.name() == "enabled") {
row->keyframing = (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;j<row->keyframe_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;i<keyframe_times.size();i++) {
if (keyframe_times.at(i) == time) {
index = i;
break;
}
}
KeyframeSet* ks = new KeyframeSet(this, index, time, just_made_unsafe_keyframe);
if (undoable) {
just_made_unsafe_keyframe = false;
undo_stack.push(ks);
} else {
if (index == -1) just_made_unsafe_keyframe = true;
ks->redo();
delete ks;
}
panel_effect_controls->update_keyframes();
}
void EffectRow::set_keyframe(long time) {
keyframing = true;
int index = -1;
for (int i=0;i<keyframe_times.size();i++) {
if (keyframe_times.at(i) == time) {
index = i;
break;
}
}
if (index == -1) {
keyframe_times.append(time);
keyframe_types.append((keyframe_types.size() > 0) ? keyframe_types.last() : EFFECT_KEYFRAME_LINEAR);
}
for (int i=0;i<fields.size();i++) {
fields.at(i)->set_keyframe_data(index);
}
panel_effect_controls->update_keyframes();
void EffectRow::delete_keyframe_at_time(KeyframeDelete* kd, long time) {
for (int i=0;i<keyframe_times.size();i++) {
if (keyframe_times.at(i) == time) {
delete_keyframe(kd, i);
break;
}
}
}
void EffectRow::delete_keyframe(KeyframeDelete* kd, int index) {
kd->rows.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;i<parent_row->keyframe_times.size();i++) {
long eval_keyframe_time = parent_row->keyframe_times.at(i);
if (eval_keyframe_time == frame) {
*before = i;
*after = i;
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<FontCombobox*>(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<LabelSlider*>(ui_element)->is_dragging()));
}
emit changed();
}
+15 -10
View File
@@ -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<QVariant> 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<long> keyframe_times;
QVector<int> 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<EffectField*> fields;
CheckboxEx* keyframe_enable;
bool just_made_unsafe_keyframe;
};
class Effect : public QObject {
+1 -1
View File
@@ -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) {
+8 -8
View File
@@ -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);
+1
View File
@@ -38,5 +38,6 @@
<file>zoomout-disabled.png</file>
<file>add-transition.png</file>
<file>add-effect.png</file>
<file>olive-splash.png</file>
</qresource>
</RCC>
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

+3 -1
View File
@@ -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()
+4
View File
@@ -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;
+1
View File
@@ -38,6 +38,7 @@ public:
void reload_clips();
void set_zoom(bool in);
bool keyframe_focus();
void delete_selected_keyframes();
double zoom;
public slots:
+7
View File
@@ -22,12 +22,19 @@
#include <QtMath>
#include <QGuiApplication>
#include <QScreen>
#include <QPainter>
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),
+1
View File
@@ -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;
+136 -7
View File
@@ -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;i<rows.size();i++) {
rows.at(i)->keyframe_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;i<rows.size();i++) {
rows.at(i)->keyframe_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;i<rows.size();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=0;j<row->fieldCount();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.size();i++) {
for (int j=i+1;j<keyframes.size();j++) {
if (keyframes.at(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;i<rows.size();i++) {
EffectRow* row = rows.at(i);
int keyframe_index = keyframes.at(i) - i;
deleted_keyframe_times[i] = (row->keyframe_times.at(keyframe_index));
deleted_keyframe_types[i] = (row->keyframe_types.at(keyframe_index));
row->keyframe_times.removeAt(keyframe_index);
row->keyframe_types.removeAt(keyframe_index);
for (int j=0;j<row->fieldCount();j++) {
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;i<row->fieldCount();i++) {
EffectField* field = row->field(i);
if (index != -1) {
if (field->type == EFFECT_FIELD_DOUBLE) {
old_values[i] = static_cast<LabelSlider*>(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;i<row->fieldCount();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;i<row->fieldCount();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;
}
+40 -6
View File
@@ -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<EffectRow*> rows;
QVector<int> keyframes;
long movement;
void undo();
void redo();
private:
bool done;
bool old_project_changed;
};
class KeyframeDelete : public QUndoCommand {
public:
KeyframeDelete();
QVector<EffectRow*> rows;
EffectRow* disable_keyframes_on_row;
QVector<int> keyframes;
void undo();
void redo();
private:
bool old_project_changed;
QVector<long> deleted_keyframe_times;
QVector<int> deleted_keyframe_types;
QVector<QVariant> deleted_keyframe_data;
bool sorted;
};
class KeyframeSet : public QUndoCommand {
public:
KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe);
void undo();
void redo();
QVector<QVariant> old_values;
QVector<QVariant> 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
+1 -1
View File
@@ -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() {
+4 -2
View File
@@ -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();
+1 -1
View File
@@ -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;
}
}
+117 -22
View File
@@ -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 <QLabel>
#include <QMouseEvent>
@@ -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;k<row->keyframe_times.size();k++) {
bool keyframe_selected = false;
for (int l=0;l<selected_rows.size();l++) {
if (selected_rows.at(l) == rows.size() && selected_keyframes.at(l) == k) {
keyframe_selected = true;
break;
}
}
draw_keyframe(p, getScreenPointFromFrame(panel_effect_controls->zoom, 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;i<selected_rows.size();i++) {
if (selected_rows.at(i) == row && selected_keyframes.at(i) == keyframe) {
return true;
}
}
return false;
}
void KeyframeView::delete_selected_keyframes() {
KeyframeDelete* kd = new KeyframeDelete();
bool del = false;
for (int i=0;i<selected_keyframes.size();i++) {
selected_rows.at(i)->delete_keyframe(kd, selected_keyframes.at(i));
del = true;
}
if (del) {
undo_stack.push(kd);
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;i<rowY.size();i++) {
if (event->y() > 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;j<row->keyframe_times.size();j++) {
long eval_keyframe_time = row->keyframe_times.at(j)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in);
if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) {
long eval_frame_diff = qAbs(eval_keyframe_time - 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<selected_rows.size();i++) {
if (selected_rows.at(i) == row_index && selected_keyframes.at(i) == keyframe_index) {
already_selected = true;
}
}
}
keys_selected = false;
if (keyframe_index > -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;i<selected_keyframes.size();i++) {
EffectRow* row = selected_rows.at(i);
long eval_key = row->keyframe_times.at(selected_keyframes.at(i));
for (int j=0;j<row->keyframe_times.size();j++) {
while (!keyframeIsSelected(row, j) && row->keyframe_times.at(j) == eval_key + frame_diff) {
if (last_frame_diff > frame_diff) {
frame_diff++;
} 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<rowY.size();i++) {
if (rowY.at(i) >= min_row && rowY.at(i) <= max_row) {
EffectRow* row = rows.at(i);
for (int j=0;j<row->keyframe_times.size();j++) {
long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(j));
if (!keyframeIsSelected(row, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) {
selected_rows.append(rows.at(i));
selected_keyframes.append(j);
}
}
}
}
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();
}
+16 -3
View File
@@ -15,14 +15,18 @@ public:
KeyframeView(QWidget* parent = 0);
QVector<Effect*> effects;
void delete_selected_keyframes();
TimelineHeader* header;
long visible_in;
long visible_out;
private:
QVector<int> selected_rows;
long adjust_row_keyframe(EffectRow* row, long time);
QVector<EffectRow*> selected_rows;
QVector<int> selected_keyframes;
QVector<int> rowY;
long frame_diff;
QVector<EffectRow*> 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
+14 -24
View File
@@ -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);
}
}
}
+3 -1
View File
@@ -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
+2 -4
View File
@@ -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