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