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