1 #include "effectui.h"
2
3 #include <QPoint>
4
5 #include "timeline/clip.h"
6 #include "ui/menuhelper.h"
7 #include "ui/keyframenavigator.h"
8 #include "ui/clickablelabel.h"
9 #include "ui/menu.h"
10 #include "panels/panels.h"
11
EffectUI(Effect * e)12 EffectUI::EffectUI(Effect* e) :
13 effect_(e)
14 {
15 Q_ASSERT(e != nullptr);
16
17 QString effect_name;
18
19 // If this effect is actually a transition
20 if (e->meta->type == EFFECT_TYPE_TRANSITION) {
21
22 Transition* t = static_cast<Transition*>(e);
23
24 // Since effects can have two clip attachments, find out which one is selected
25 Clip* selected_clip = t->parent_clip;
26 bool both_selected = false;
27
28 // Check if this is a shared transition
29 if (t->secondary_clip != nullptr) {
30
31 // Check which clips are selected
32 if (t->secondary_clip->IsSelected()) {
33
34 selected_clip = t->secondary_clip;
35
36 if (t->parent_clip->IsSelected()) {
37 // Both clips are selected
38 both_selected = true;
39 }
40
41 } else if (!t->parent_clip->IsSelected()) {
42
43 // Neither are selected, but the naming scheme (no "opening" or "closing" modifier) will be the same
44 both_selected = true;
45
46 }
47
48 }
49
50 // See if the transition is the clip's opening or closing transition and label it accordingly
51 if (both_selected) {
52 effect_name = t->name;
53 } else if (selected_clip->opening_transition.get() == t) {
54 effect_name = tr("%1 (Opening)").arg(t->name);
55 } else {
56 effect_name = tr("%1 (Closing)").arg(t->name);
57 }
58
59 } else {
60
61 // Otherwise just set the title normally
62 effect_name = e->name;
63
64 }
65
66 SetTitle(effect_name);
67
68 QWidget* ui = new QWidget(this);
69 ui->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
70 SetContents(ui);
71
72 SetExpanded(e->IsExpanded());
73 connect(this, SIGNAL(visibleChanged(bool)), e, SLOT(SetExpanded(bool)));
74
75 layout_ = new QGridLayout(ui);
76 layout_->setSpacing(4);
77
78 connect(title_bar,
79 SIGNAL(customContextMenuRequested(const QPoint&)),
80 this,
81 SLOT(show_context_menu(const QPoint&)));
82
83 int maximum_column = 0;
84
85 widgets_.resize(e->row_count());
86
87 for (int i=0;i<e->row_count();i++) {
88 EffectRow* row = e->row(i);
89
90 ClickableLabel* row_label = new ClickableLabel(row->name());
91 connect(row_label, SIGNAL(clicked()), row, SLOT(FocusRow()));
92
93 labels_.append(row_label);
94
95 layout_->addWidget(row_label, i, 0);
96
97 widgets_[i].resize(row->FieldCount());
98
99 int column = 1;
100 for (int j=0;j<row->FieldCount();j++) {
101 EffectField* field = row->Field(j);
102
103 QWidget* widget = field->CreateWidget();
104
105 widgets_[i][j] = widget;
106
107 layout_->addWidget(widget, i, column, 1, field->GetColumnSpan());
108
109 column += field->GetColumnSpan();
110 }
111
112 // Find maximum column to place keyframe controls
113 maximum_column = qMax(row->FieldCount(), maximum_column);
114 }
115
116 // Create keyframe controls
117 maximum_column++;
118
119 keyframe_navigators_.resize(e->row_count());
120
121 for (int i=0;i<e->row_count();i++) {
122 EffectRow* row = e->row(i);
123
124 KeyframeNavigator* nav;
125
126 if (row->IsKeyframable()) {
127
128 nav = new KeyframeNavigator();
129
130 nav->enable_keyframes(row->IsKeyframing());
131
132 AttachKeyframeNavigationToRow(row, nav);
133
134 layout_->addWidget(nav, i, maximum_column);
135
136 } else {
137
138 nav = nullptr;
139
140 }
141
142 keyframe_navigators_[i] = nav;
143
144 }
145
146 enabled_check->setChecked(e->IsEnabled());
147 connect(enabled_check, SIGNAL(toggled(bool)), e, SLOT(SetEnabled(bool)));
148 connect(enabled_check, SIGNAL(toggled(bool)), e, SLOT(FieldChanged()));
149 }
150
AddAdditionalEffect(Effect * e)151 void EffectUI::AddAdditionalEffect(Effect *e)
152 {
153 // Ensure this is the same kind of effect and will be fully compatible
154 Q_ASSERT(e->meta == effect_->meta);
155
156 // Add 'multiple' modifier to header label (but only once)
157 if (additional_effects_.isEmpty()) {
158 QString new_title = tr("%1 (multiple)").arg(Title());
159
160 SetTitle(new_title);
161 }
162
163 // Add effect to list
164 additional_effects_.append(e);
165
166 // Attach this UI's widgets to the additional effect
167 for (int i=0;i<effect_->row_count();i++) {
168
169 EffectRow* row = effect_->row(i);
170
171 // Attach existing keyframe navigator to this effect's row
172 AttachKeyframeNavigationToRow(e->row(i), keyframe_navigators_.at(i));
173
174 for (int j=0;j<row->FieldCount();j++) {
175
176 // Attach existing field widget to this effect's field
177 e->row(i)->Field(j)->CreateWidget(Widget(i, j));
178
179 }
180
181 }
182 }
183
GetEffect()184 Effect *EffectUI::GetEffect()
185 {
186 return effect_;
187 }
188
GetRowY(int row,QWidget * mapToWidget)189 int EffectUI::GetRowY(int row, QWidget* mapToWidget) {
190
191 // Currently to get a Y value in the context of `mapToWidget`, we use `panel_effect_controls` as the base. Mapping
192 // to global doesn't work for some reason, so this is the best reference point we have.
193
194 QLabel* row_label = labels_.at(row);
195
196 // Get center point of label (label->rect()->center()->y() - instead of y()+height/2 - produces an inaccurate result)
197 return row_label->y()
198 + row_label->height() / 2
199 + mapToWidget->mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y()
200 - title_bar->height();
201 }
202
UpdateFromEffect()203 void EffectUI::UpdateFromEffect()
204 {
205 Effect* effect = GetEffect();
206
207 for (int j=0;j<effect->row_count();j++) {
208
209 EffectRow* row = effect->row(j);
210
211 for (int k=0;k<row->FieldCount();k++) {
212 EffectField* field = row->Field(k);
213
214 // Check if this UI object is attached to one effect or many
215 if (additional_effects_.isEmpty()) {
216
217 field->UpdateWidgetValue(Widget(j, k), field->Now());
218
219 } else {
220
221 bool same_value = true;
222
223 for (int i=0;i<additional_effects_.size();i++) {
224 EffectField* previous_field = i > 0 ? additional_effects_.at(i-1)->row(j)->Field(k) : field;
225 EffectField* additional_field = additional_effects_.at(i)->row(j)->Field(k);
226
227 if (additional_field->GetValueAt(additional_field->Now()) != previous_field->GetValueAt(previous_field->Now())) {
228 same_value = false;
229 break;
230 }
231 }
232
233 if (same_value) {
234 field->UpdateWidgetValue(Widget(j, k), field->Now());
235 } else {
236 field->UpdateWidgetValue(Widget(j, k), qSNaN());
237 }
238
239 }
240 }
241 }
242 }
243
IsAttachedToClip(Clip * c)244 bool EffectUI::IsAttachedToClip(Clip *c)
245 {
246 if (GetEffect()->parent_clip == c) {
247 return true;
248 }
249
250 for (int i=0;i<additional_effects_.size();i++) {
251 if (additional_effects_.at(i)->parent_clip == c) {
252 return true;
253 }
254 }
255
256 return false;
257 }
258
Widget(int row,int field)259 QWidget *EffectUI::Widget(int row, int field)
260 {
261 return widgets_.at(row).at(field);
262 }
263
AttachKeyframeNavigationToRow(EffectRow * row,KeyframeNavigator * nav)264 void EffectUI::AttachKeyframeNavigationToRow(EffectRow *row, KeyframeNavigator *nav)
265 {
266 if (nav == nullptr) {
267 return;
268 }
269
270 connect(nav, SIGNAL(goto_previous_key()), row, SLOT(GoToPreviousKeyframe()));
271 connect(nav, SIGNAL(toggle_key()), row, SLOT(ToggleKeyframe()));
272 connect(nav, SIGNAL(goto_next_key()), row, SLOT(GoToNextKeyframe()));
273 connect(nav, SIGNAL(keyframe_enabled_changed(bool)), row, SLOT(SetKeyframingEnabled(bool)));
274 connect(nav, SIGNAL(clicked()), row, SLOT(FocusRow()));
275 connect(row, SIGNAL(KeyframingSetChanged(bool)), nav, SLOT(enable_keyframes(bool)));
276 }
277
show_context_menu(const QPoint & pos)278 void EffectUI::show_context_menu(const QPoint& pos) {
279 if (effect_->meta->type == EFFECT_TYPE_EFFECT) {
280 Menu menu;
281
282 Clip* c = effect_->parent_clip;
283
284 int index = c->IndexOfEffect(effect_);
285
286 QAction* cut_action = menu.addAction(tr("Cu&t"));
287 connect(cut_action, SIGNAL(triggered(bool)), this, SIGNAL(CutRequested()));
288
289 QAction* copy_action = menu.addAction(tr("&Copy"));
290 connect(copy_action, SIGNAL(triggered(bool)), this, SIGNAL(CopyRequested()));
291
292 olive::MenuHelper.create_effect_paste_action(&menu);
293
294 menu.addSeparator();
295
296 QAction* move_up_action = nullptr;
297 QAction* move_down_action = nullptr;
298
299 if (index > 0) {
300 move_up_action = menu.addAction(tr("Move &Up"), GetEffect(), SLOT(move_up()));
301 }
302
303 if (index < c->effects.size() - 1) {
304 move_down_action = menu.addAction(tr("Move &Down"), GetEffect(), SLOT(move_down()));
305 }
306
307 menu.addSeparator();
308
309 QAction* delete_action = menu.addAction(tr("D&elete"), GetEffect(), SLOT(delete_self()));
310
311 // Loop through additional effects and link these too
312 for (int i=0;i<additional_effects_.size();i++) {
313 if (move_up_action != nullptr) {
314 connect(move_up_action, SIGNAL(triggered(bool)), additional_effects_.at(i), SLOT(move_up()));
315 }
316
317 if (move_down_action != nullptr) {
318 connect(move_down_action, SIGNAL(triggered(bool)), additional_effects_.at(i), SLOT(move_down()));
319 }
320
321 connect(delete_action, SIGNAL(triggered(bool)), additional_effects_.at(i), SLOT(delete_self()));
322 }
323
324 menu.addSeparator();
325
326 menu.addAction(tr("Load Settings From File"), GetEffect(), SLOT(load_from_file()));
327
328 menu.addAction(tr("Save Settings to File"), GetEffect(), SLOT(save_to_file()));
329
330 menu.exec(title_bar->mapToGlobal(pos));
331 }
332 }
333