1 /***
2
3 Olive - Non-Linear Video Editor
4 Copyright (C) 2019 Olive Team
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 ***/
20
21 #include "effectrow.h"
22
23 #include <QHBoxLayout>
24 #include <QPushButton>
25 #include <QMessageBox>
26
27 #include "undo/undo.h"
28 #include "undo/undostack.h"
29 #include "timeline/clip.h"
30 #include "timeline/sequence.h"
31 #include "panels/panels.h"
32 #include "panels/effectcontrols.h"
33 #include "panels/viewer.h"
34 #include "panels/grapheditor.h"
35 #include "effect.h"
36 #include "ui/viewerwidget.h"
37 #include "ui/keyframenavigator.h"
38 #include "ui/clickablelabel.h"
39
EffectRow(Effect * parent,const QString & n,bool savable,bool keyframable)40 EffectRow::EffectRow(Effect *parent, const QString &n, bool savable, bool keyframable) :
41 QObject(parent),
42 name_(n),
43 keyframable_(keyframable),
44 keyframing_(false),
45 savable_(savable)
46 {
47 Q_ASSERT(parent != nullptr);
48
49 parent->AddRow(this);
50 }
51
AddField(EffectField * field)52 void EffectRow::AddField(EffectField *field)
53 {
54 field->setParent(this);
55 fields_.append(field);
56 }
57
IsKeyframing()58 bool EffectRow::IsKeyframing() {
59 return keyframing_;
60 }
61
SetKeyframingInternal(bool b)62 void EffectRow::SetKeyframingInternal(bool b) {
63 if (GetParentEffect()->meta->type != EFFECT_TYPE_TRANSITION) {
64 keyframing_ = b;
65 emit KeyframingSetChanged(keyframing_);
66 }
67 }
68
IsSavable()69 bool EffectRow::IsSavable()
70 {
71 return savable_;
72 }
73
IsKeyframable()74 bool EffectRow::IsKeyframable()
75 {
76 return keyframable_;
77 }
78
SetKeyframingEnabled(bool enabled)79 void EffectRow::SetKeyframingEnabled(bool enabled) {
80 if (enabled == keyframing_) {
81 return;
82 }
83
84 if (enabled) {
85
86 ComboAction* ca = new ComboAction();
87
88 // Enable keyframing setting on this row
89 ca->append(new SetIsKeyframing(this, true));
90
91 // Prepare each field's data to start keyframing
92 for (int i=0;i<FieldCount();i++) {
93 Field(i)->PrepareDataForKeyframing(true, ca);
94 }
95
96 olive::UndoStack.push(ca);
97
98 update_ui(false);
99
100 } else {
101
102 // Confirm with the user whether they really want to disable keyframing
103 if (QMessageBox::question(panel_effect_controls,
104 tr("Disable Keyframes"),
105 tr("Disabling keyframes will delete all current keyframes. "
106 "Are you sure you want to do this?"),
107 QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
108
109 ComboAction* ca = new ComboAction();
110
111 // Prepare each field's data to stop keyframing
112 for (int i=0;i<FieldCount();i++) {
113 Field(i)->PrepareDataForKeyframing(false, ca);
114 }
115
116 // Disable keyframing setting on this row
117 ca->append(new SetIsKeyframing(this, false));
118
119 olive::UndoStack.push(ca);
120
121 update_ui(false);
122
123 } else {
124
125 SetKeyframingInternal(true);
126
127 }
128 }
129 }
130
GoToPreviousKeyframe()131 void EffectRow::GoToPreviousKeyframe() {
132 long key = LONG_MIN;
133 Clip* c = GetParentEffect()->parent_clip;
134 long sequence_playhead = c->sequence->playhead;
135
136 // Used to convert clip frame number to sequence frame number
137 long time_adjustment = c->timeline_in() - c->clip_in();
138
139 // Loop through all of this row's fields
140 for (int i=0;i<FieldCount();i++) {
141
142 EffectField* f = Field(i);
143
144 // Loop through all of this field's keyframes for a keyframe EARLIER than the playhead
145 for (int j=0;j<f->keyframes.size();j++) {
146 long comp = f->keyframes.at(j).time + time_adjustment;
147
148 // Get the closest keyframe
149 if (comp < sequence_playhead) {
150 key = qMax(comp, key);
151 }
152 }
153 }
154
155 // If we found a keyframe less than the playhead, jump to it
156 if (key != LONG_MIN) panel_sequence_viewer->seek(key);
157 }
158
ToggleKeyframe()159 void EffectRow::ToggleKeyframe() {
160 Clip* c = GetParentEffect()->parent_clip;
161 long sequence_playhead = c->sequence->playhead;
162
163 // Used to convert clip frame number to sequence frame number
164 long time_adjustment = c->timeline_in() - c->clip_in();
165
166 QVector<EffectField*> key_fields;
167 QVector<int> key_field_index;
168
169 // See if any keyframes on any fields are at the current time
170 for (int j=0;j<FieldCount();j++) {
171 EffectField* f = Field(j);
172 for (int i=0;i<f->keyframes.size();i++) {
173 long comp = f->keyframes.at(i).time + time_adjustment;
174
175 if (comp == sequence_playhead) {
176
177 // Cache the keyframes if they are at the current time
178 key_fields.append(f);
179 key_field_index.append(i);
180
181 }
182 }
183 }
184
185 ComboAction* ca = new ComboAction();
186
187 if (key_fields.isEmpty()) {
188
189 // If we didn't find any current keyframes, create one for each field
190 SetKeyframeOnAllFields(ca);
191
192 } else {
193
194 // If we DID find keyframes at this time, delete them
195
196 QVector<EffectField*> sorted_key_fields;
197 QVector<int> sorted_key_field_index;
198
199 // Since QVectors shift themselves when removing items, we need to sort these in reverse order
200 for (int i=0;i<key_field_index.size();i++) {
201 bool found = false;
202
203 for (int j=0;j<sorted_key_field_index.size();j++) {
204 if (sorted_key_field_index.at(j) < key_field_index.at(i)) {
205 sorted_key_fields.insert(j, key_fields.at(i));
206 sorted_key_field_index.insert(j, key_field_index.at(i));
207
208 found = true;
209 break;
210 }
211 }
212
213 if (!found) {
214 sorted_key_fields.append(key_fields.at(i));
215 sorted_key_field_index.append(key_field_index.at(i));
216 }
217 }
218
219 for (int i=0;i<sorted_key_fields.size();i++) {
220 ca->append(new KeyframeDelete(sorted_key_fields.at(i), sorted_key_field_index.at(i)));
221 }
222
223 }
224
225 olive::UndoStack.push(ca);
226 update_ui(false);
227 }
228
GoToNextKeyframe()229 void EffectRow::GoToNextKeyframe() {
230 long key = LONG_MAX;
231 Clip* c = GetParentEffect()->parent_clip;
232 for (int i=0;i<FieldCount();i++) {
233 EffectField* f = Field(i);
234 for (int j=0;j<f->keyframes.size();j++) {
235 long comp = f->keyframes.at(j).time - c->clip_in() + c->timeline_in();
236 if (comp > olive::ActiveSequence->playhead) {
237 key = qMin(comp, key);
238 }
239 }
240 }
241 if (key != LONG_MAX) panel_sequence_viewer->seek(key);
242 }
243
FocusRow()244 void EffectRow::FocusRow() {
245 panel_graph_editor->set_row(this);
246 }
247
SetKeyframeOnAllFields(ComboAction * ca)248 void EffectRow::SetKeyframeOnAllFields(ComboAction* ca) {
249 for (int i=0;i<FieldCount();i++) {
250 EffectField* field = Field(i);
251
252 KeyframeDataChange* kdc = new KeyframeDataChange(field);
253
254 field->SetValueAt(field->Now(), field->GetValueAt(field->Now()));
255
256 kdc->SetNewKeyframes();
257 ca->append(kdc);
258 }
259
260 panel_effect_controls->update_keyframes();
261 }
262
GetParentEffect()263 Effect *EffectRow::GetParentEffect()
264 {
265 return static_cast<Effect*>(parent());
266 }
267
name()268 const QString &EffectRow::name() {
269 return name_;
270 }
271
Field(int i)272 EffectField* EffectRow::Field(int i) {
273 return fields_.at(i);
274 }
275
FieldCount()276 int EffectRow::FieldCount() {
277 return fields_.size();
278 }
279