]> git.sesse.net Git - kdenlive/blob - src/geometryval.cpp
cleanup composite transition UI
[kdenlive] / src / geometryval.cpp
1 /***************************************************************************
2                           geomeytrval.cpp  -  description
3                              -------------------
4     begin                : 03 Aug 2008
5     copyright            : (C) 2008 by Marco Gittler
6     email                : g.marco@freenet.de
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "geometryval.h"
19 #include "graphicsscenerectmove.h"
20 #include "kdenlivesettings.h"
21
22 #include <KDebug>
23
24 #include <QGraphicsView>
25 #include <QVBoxLayout>
26 #include <QGraphicsRectItem>
27 #include <QMenu>
28 #include <QInputDialog>
29
30
31 Geometryval::Geometryval(const MltVideoProfile profile, QPoint frame_size, int startPoint, QWidget* parent) :
32         QWidget(parent),
33         m_profile(profile),
34         m_paramRect(NULL),
35         m_geom(NULL),
36         m_path(NULL),
37         m_fixedMode(false),
38         m_frameSize(frame_size),
39         m_startPoint(startPoint)
40 {
41     setupUi(this);
42     QVBoxLayout* vbox = new QVBoxLayout(widget);
43     m_sceneview = new QGraphicsView(this);
44     m_sceneview->setBackgroundBrush(QBrush(Qt::black));
45     vbox->addWidget(m_sceneview);
46     vbox->setContentsMargins(0, 0, 0, 0);
47
48     QVBoxLayout* vbox2 = new QVBoxLayout(keyframeWidget);
49     m_helper = new KeyframeHelper(this);
50     vbox2->addWidget(m_helper);
51     vbox2->setContentsMargins(0, 0, 0, 0);
52
53     connect(m_helper, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int)));
54     connect(m_helper, SIGNAL(keyframeMoved(int)), this, SLOT(slotKeyframeMoved(int)));
55     connect(m_helper, SIGNAL(addKeyframe(int)), this, SLOT(slotAddFrame(int)));
56     connect(m_helper, SIGNAL(removeKeyframe(int)), this, SLOT(slotDeleteFrame(int)));
57
58     m_scene = new GraphicsSceneRectMove(this);
59     m_scene->setTool(TITLE_SELECT);
60     m_sceneview->setScene(m_scene);
61     m_dar = (m_profile.height * m_profile.display_aspect_num / (double) m_profile.display_aspect_den) / (double) m_profile.width;
62
63     m_realWidth = (int)(profile.height * profile.display_aspect_num / (double) profile.display_aspect_den);
64     QGraphicsRectItem *frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_realWidth, profile.height));
65     frameBorder->setZValue(-1100);
66     frameBorder->setBrush(QColor(255, 255, 0, 30));
67     frameBorder->setPen(QPen(QBrush(QColor(255, 255, 255, 255)), 1.0, Qt::DashLine));
68     m_scene->addItem(frameBorder);
69
70     buttonNext->setIcon(KIcon("media-skip-forward"));
71     buttonNext->setToolTip(i18n("Go to next keyframe"));
72     buttonPrevious->setIcon(KIcon("media-skip-backward"));
73     buttonPrevious->setToolTip(i18n("Go to previous keyframe"));
74     buttonAdd->setIcon(KIcon("document-new"));
75     buttonAdd->setToolTip(i18n("Add keyframe"));
76     buttonDelete->setIcon(KIcon("edit-delete"));
77     buttonDelete->setToolTip(i18n("Delete keyframe"));
78
79     m_configMenu = new QMenu(i18n("Misc..."), this);
80     buttonMenu->setMenu(m_configMenu);
81     buttonMenu->setPopupMode(QToolButton::MenuButtonPopup);
82
83     m_editOptions = m_configMenu->addAction(KIcon("system-run"), i18n("Show/Hide options"));
84     m_editOptions->setCheckable(true);
85     buttonMenu->setDefaultAction(m_editOptions);
86     connect(m_editOptions, SIGNAL(triggered()), this, SLOT(slotSwitchOptions()));
87     slotSwitchOptions();
88
89     m_reset = m_configMenu->addAction(KIcon("view-refresh"), i18n("Reset"), this, SLOT(slotResetPosition()));
90
91     m_syncAction = m_configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
92     m_syncAction->setCheckable(true);
93     m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
94
95     //scene->setSceneRect(0, 0, profile.width * 2, profile.height * 2);
96     //view->fitInView(m_frameBorder, Qt::KeepAspectRatio);
97     const double sc = 100.0 / profile.height * 0.8;
98     QRectF srect = m_sceneview->sceneRect();
99     m_sceneview->setSceneRect(srect.x(), -srect.height() / 3 + 10, srect.width(), srect.height() + srect.height() / 3 * 2 - 10);
100     m_scene->setZoom(sc);
101     m_sceneview->centerOn(frameBorder);
102     m_sceneview->setMouseTracking(true);
103     connect(buttonNext , SIGNAL(clicked()) , this , SLOT(slotNextFrame()));
104     connect(buttonPrevious , SIGNAL(clicked()) , this , SLOT(slotPreviousFrame()));
105     connect(buttonDelete , SIGNAL(clicked()) , this , SLOT(slotDeleteFrame()));
106     connect(buttonAdd , SIGNAL(clicked()) , this , SLOT(slotAddFrame()));
107     connect(m_scene, SIGNAL(actionFinished()), this, SLOT(slotUpdateTransitionProperties()));
108     connect(m_scene, SIGNAL(doubleClickEvent()), this, SLOT(slotGeometry()));
109
110     buttonhcenter->setIcon(KIcon("kdenlive-align-hor"));
111     buttonhcenter->setToolTip(i18n("Align item horizontally"));
112     buttonvcenter->setIcon(KIcon("kdenlive-align-vert"));
113     buttonvcenter->setToolTip(i18n("Align item vertically"));
114     buttontop->setIcon(KIcon("kdenlive-align-top"));
115     buttontop->setToolTip(i18n("Align item to top"));
116     buttonbottom->setIcon(KIcon("kdenlive-align-bottom"));
117     buttonbottom->setToolTip(i18n("Align item to bottom"));
118     buttonright->setIcon(KIcon("kdenlive-align-right"));
119     buttonright->setToolTip(i18n("Align item to right"));
120     buttonleft->setIcon(KIcon("kdenlive-align-left"));
121     buttonleft->setToolTip(i18n("Align item to left"));
122
123     connect(buttonhcenter, SIGNAL(clicked()), this, SLOT(slotAlignHCenter()));
124     connect(buttonvcenter, SIGNAL(clicked()), this, SLOT(slotAlignVCenter()));
125     connect(buttontop, SIGNAL(clicked()), this, SLOT(slotAlignTop()));
126     connect(buttonbottom, SIGNAL(clicked()), this, SLOT(slotAlignBottom()));
127     connect(buttonright, SIGNAL(clicked()), this, SLOT(slotAlignRight()));
128     connect(buttonleft, SIGNAL(clicked()), this, SLOT(slotAlignLeft()));
129     connect(spinX, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryX(int)));
130     connect(spinY, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryY(int)));
131     connect(spinWidth, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryWidth(int)));
132     connect(spinHeight, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryHeight(int)));
133     connect(spinResize, SIGNAL(editingFinished()), this, SLOT(slotResizeCustom()));
134     connect(buttonResize, SIGNAL(clicked()), this, SLOT(slotResizeOriginal()));
135
136     connect(this, SIGNAL(parameterChanged()), this, SLOT(slotUpdateGeometry()));
137 }
138
139
140 Geometryval::~Geometryval()
141 {
142     m_scene->disconnect();
143     delete m_syncAction;
144     delete m_configMenu;
145     delete m_paramRect;
146     delete m_path;
147     delete m_helper;
148     delete m_geom;
149     delete m_sceneview;
150     delete m_scene;
151 }
152
153
154 void Geometryval::slotAlignHCenter()
155 {
156     if (!keyframeSelected())
157         return;
158     m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, m_paramRect->pos().y());
159     slotUpdateTransitionProperties();
160 }
161
162 void Geometryval::slotAlignVCenter()
163 {
164     if (!keyframeSelected())
165         return;
166     m_paramRect->setPos(m_paramRect->pos().x(), (m_profile.height - m_paramRect->rect().height()) / 2);
167     slotUpdateTransitionProperties();
168 }
169
170 void Geometryval::slotAlignTop()
171 {
172     if (!keyframeSelected())
173         return;
174     m_paramRect->setPos(m_paramRect->pos().x(), 0);
175     slotUpdateTransitionProperties();
176 }
177
178 void Geometryval::slotAlignBottom()
179 {
180     if (!keyframeSelected())
181         return;
182     m_paramRect->setPos(m_paramRect->pos().x(), m_profile.height - m_paramRect->rect().height());
183     slotUpdateTransitionProperties();
184 }
185
186 void Geometryval::slotAlignLeft()
187 {
188     if (!keyframeSelected())
189         return;
190     m_paramRect->setPos(0, m_paramRect->pos().y());
191     slotUpdateTransitionProperties();
192 }
193
194 void Geometryval::slotAlignRight()
195 {
196     if (!keyframeSelected())
197         return;
198     m_paramRect->setPos(m_realWidth - m_paramRect->rect().width(), m_paramRect->pos().y());
199     slotUpdateTransitionProperties();
200 }
201
202 void Geometryval::slotResizeOriginal()
203 {
204     if (!keyframeSelected())
205         return;
206     if (m_frameSize.isNull())
207         m_paramRect->setRect(0, 0, m_realWidth, m_profile.height);
208     else
209         m_paramRect->setRect(0, 0, m_frameSize.x(), m_frameSize.y());
210     slotUpdateTransitionProperties();
211 }
212
213 void Geometryval::slotResizeCustom()
214 {
215     if (!keyframeSelected())
216         return;
217     int value = spinResize->value();
218     m_paramRect->setRect(0, 0, m_realWidth * value / 100, m_profile.height * value / 100);
219     slotUpdateTransitionProperties();
220 }
221
222 void Geometryval::slotTransparencyChanged(int transp)
223 {
224     int pos = spinPos->value();
225     Mlt::GeometryItem item;
226     int error = m_geom->fetch(&item, pos);
227     if (error || item.key() == false) {
228         // no keyframe under cursor
229         return;
230     }
231     item.mix(transp);
232     m_paramRect->setBrush(QColor(255, 0, 0, transp));
233     m_geom->insert(item);
234     emit parameterChanged();
235 }
236
237 void Geometryval::slotSyncCursor()
238 {
239     KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
240 }
241
242 void Geometryval::slotPositionChanged(int pos, bool seek)
243 {
244     if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos + m_startPoint);
245     spinPos->setValue(pos);
246     m_helper->setValue(pos);
247     Mlt::GeometryItem item;
248     int error = m_geom->fetch(&item, pos);
249     if (error || item.key() == false) {
250         // no keyframe under cursor, adjust buttons
251         buttonAdd->setEnabled(true);
252         buttonDelete->setEnabled(false);
253         widget->setEnabled(false);
254         spinTransp->setEnabled(false);
255         frameOptions->setEnabled(false);
256         m_reset->setEnabled(false);
257     } else {
258         buttonAdd->setEnabled(false);
259         buttonDelete->setEnabled(true);
260         widget->setEnabled(true);
261         spinTransp->setEnabled(true);
262         frameOptions->setEnabled(true);
263         m_reset->setEnabled(true);
264     }
265
266     m_paramRect->setPos(item.x() * m_dar, item.y());
267     m_paramRect->setRect(0, 0, item.w() * m_dar, item.h());
268     spinTransp->setValue(item.mix());
269     m_paramRect->setBrush(QColor(255, 0, 0, item.mix()));
270     slotUpdateGeometry();
271 }
272
273 void Geometryval::slotDeleteFrame(int pos)
274 {
275     // check there is more than one keyframe
276     Mlt::GeometryItem item;
277     if (pos == -1) pos = spinPos->value();
278     int error = m_geom->next_key(&item, pos + 1);
279     if (error) {
280         error = m_geom->prev_key(&item, pos - 1);
281         if (error || item.frame() == pos) return;
282     }
283
284     m_geom->remove(spinPos->value());
285     buttonAdd->setEnabled(true);
286     buttonDelete->setEnabled(false);
287     widget->setEnabled(false);
288     spinTransp->setEnabled(false);
289     frameOptions->setEnabled(false);
290     m_reset->setEnabled(false);
291     m_helper->update();
292     slotPositionChanged(pos, false);
293     updateTransitionPath();
294     emit parameterChanged();
295 }
296
297 void Geometryval::slotAddFrame(int pos)
298 {
299     if (pos == -1) pos = spinPos->value();
300     Mlt::GeometryItem item;
301     item.frame(pos);
302     QRectF r = m_paramRect->rect().normalized();
303     QPointF rectpos = m_paramRect->pos();
304     item.x(rectpos.x() / m_dar);
305     item.y(rectpos.y());
306     item.w(r.width() / m_dar);
307     item.h(r.height());
308     item.mix(spinTransp->value());
309     m_geom->insert(item);
310     buttonAdd->setEnabled(false);
311     buttonDelete->setEnabled(true);
312     widget->setEnabled(true);
313     spinTransp->setEnabled(true);
314     frameOptions->setEnabled(true);
315     m_reset->setEnabled(true);
316     m_helper->update();
317     emit parameterChanged();
318 }
319
320 void Geometryval::slotNextFrame()
321 {
322     Mlt::GeometryItem item;
323     int error = m_geom->next_key(&item, m_helper->value() + 1);
324     kDebug() << "// SEEK TO NEXT KFR: " << error;
325     if (error) {
326         // Go to end
327         spinPos->setValue(spinPos->maximum());
328         return;
329     }
330     int pos = item.frame();
331     spinPos->setValue(pos);
332 }
333
334 void Geometryval::slotPreviousFrame()
335 {
336     Mlt::GeometryItem item;
337     int error = m_geom->prev_key(&item, m_helper->value() - 1);
338     kDebug() << "// SEEK TO NEXT KFR: " << error;
339     if (error) return;
340     int pos = item.frame();
341     spinPos->setValue(pos);
342 }
343
344
345 QString Geometryval::getValue() const
346 {
347     return m_geom->serialise();
348 }
349
350 void Geometryval::setupParam(const QDomElement par, int minFrame, int maxFrame)
351 {
352     QString val = par.attribute("value");
353     if (par.attribute("fixed") == "1") {
354         m_fixedMode = true;
355         buttonPrevious->setHidden(true);
356         buttonNext->setHidden(true);
357         buttonDelete->setHidden(true);
358         buttonAdd->setHidden(true);
359         spinTransp->setMaximum(500);
360         label_pos->setHidden(true);
361         m_helper->setHidden(true);
362         spinPos->setHidden(true);
363     }
364     if (par.attribute("opacity") == "false") {
365         label_opacity->setHidden(true);
366         spinTransp->setHidden(true);
367     } else if (par.attribute("opacity") == "nonzero") {
368         spinTransp->setMinimum(1);
369     }
370     char *tmp = (char *) qstrdup(val.toUtf8().data());
371     if (m_geom) m_geom->parse(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
372     else m_geom = new Mlt::Geometry(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
373     delete[] tmp;
374
375     //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
376     //read param her and set rect
377     if (!m_fixedMode) {
378         m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
379         m_helper->update();
380         /*QDomDocument doc;
381         doc.appendChild(doc.importNode(par, true));
382         kDebug() << "IMPORTED TRANS: " << doc.toString();*/
383         spinPos->setMaximum(maxFrame - minFrame - 1);
384         if (m_path == NULL) {
385             m_path = new QGraphicsPathItem();
386             m_path->setPen(QPen(Qt::red));
387             m_scene->addItem(m_path);
388         }
389         updateTransitionPath();
390     }
391     Mlt::GeometryItem item;
392
393     m_geom->fetch(&item, 0);
394     delete m_paramRect;
395     m_paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w() * m_dar, item.h()));
396     m_paramRect->setPos(item.x() * m_dar, item.y());
397     m_paramRect->setZValue(0);
398     m_paramRect->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
399
400     m_paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
401     m_scene->addItem(m_paramRect);
402     slotPositionChanged(0, false);
403     slotUpdateGeometry();
404     if (!m_fixedMode) {
405         connect(spinPos, SIGNAL(valueChanged(int)), this , SLOT(slotPositionChanged(int)));
406     }
407     connect(spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
408 }
409
410 void Geometryval::updateTransitionPath()
411 {
412     if (m_fixedMode) return;
413     Mlt::GeometryItem item;
414     int pos = 0;
415     int counter = 0;
416     QPainterPath path;
417     while (true) {
418         if (m_geom->next_key(&item, pos) == 1) break;
419         pos = item.frame();
420         if (counter == 0) {
421             path.moveTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
422         } else {
423             path.lineTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
424         }
425         counter++;
426         pos++;
427     }
428     m_path->setPath(path);
429 }
430
431 void Geometryval::slotUpdateTransitionProperties()
432 {
433     int pos = spinPos->value();
434     Mlt::GeometryItem item;
435     int error = m_geom->next_key(&item, pos);
436     if (error || item.frame() != pos) {
437         // no keyframe under cursor
438         return;
439     }
440     QRectF r = m_paramRect->rect().normalized();
441     QPointF rectpos = m_paramRect->pos();
442     item.x(rectpos.x() / m_dar);
443     item.y(rectpos.y());
444     item.w(r.width() / m_dar);
445     item.h(r.height());
446     m_geom->insert(item);
447     updateTransitionPath();
448     emit parameterChanged();
449 }
450
451 void Geometryval::slotResetPosition()
452 {
453     spinX->setValue(0);
454     spinY->setValue(0);
455
456     if (m_frameSize.isNull()) {
457         spinWidth->setValue(m_realWidth);
458         spinHeight->setValue(m_profile.height);
459     } else {
460         spinWidth->setValue(m_frameSize.x());
461         spinHeight->setValue(m_frameSize.y());
462     }
463 }
464
465 void Geometryval::setFrameSize(QPoint p)
466 {
467     m_frameSize = p;
468 }
469
470
471 void Geometryval::slotKeyframeMoved(int pos)
472 {
473     slotPositionChanged(pos);
474     slotUpdateTransitionProperties();
475 }
476
477 void Geometryval::slotSwitchOptions()
478 {
479     if (frameOptions->isHidden()) {
480         frameOptions->setHidden(false);
481         m_editOptions->setChecked(true);
482     } else {
483         frameOptions->setHidden(true);
484         m_editOptions->setChecked(false);
485     }
486     adjustSize();
487 }
488
489 void Geometryval::slotGeometryX(int value)
490 {
491     if (!keyframeSelected())
492         return;
493     m_paramRect->setPos(value, spinY->value());
494     slotUpdateTransitionProperties();
495 }
496
497 void Geometryval::slotGeometryY(int value)
498 {
499     if (!keyframeSelected())
500         return;
501     m_paramRect->setPos(spinX->value(), value);
502     slotUpdateTransitionProperties();
503 }
504
505 void Geometryval::slotGeometryWidth(int value)
506 {
507     if (!keyframeSelected())
508         return;
509     m_paramRect->setRect(0, 0, value, spinHeight->value());
510     slotUpdateTransitionProperties();
511 }
512
513 void Geometryval::slotGeometryHeight(int value)
514 {
515     if (!keyframeSelected())
516         return;
517     m_paramRect->setRect(0, 0, spinWidth->value(), value);
518     slotUpdateTransitionProperties();
519 }
520
521 void Geometryval::slotUpdateGeometry()
522 {
523     QRectF r = m_paramRect->rect().normalized();
524
525     spinX->blockSignals(true);
526     spinY->blockSignals(true);
527     spinWidth->blockSignals(true);
528     spinHeight->blockSignals(true);
529     spinResize->blockSignals(true);
530
531     spinX->setValue(m_paramRect->pos().x());
532     spinY->setValue(m_paramRect->pos().y());
533     spinWidth->setValue(r.width());
534     spinHeight->setValue(r.height());
535     spinResize->setValue(m_paramRect->rect().width() * 100 / m_realWidth);
536
537     spinX->blockSignals(false);
538     spinY->blockSignals(false);
539     spinWidth->blockSignals(false);
540     spinHeight->blockSignals(false);
541     spinResize->blockSignals(false);
542 }
543
544 bool Geometryval::keyframeSelected()
545 {
546     Mlt::GeometryItem item;
547     if (m_geom->fetch(&item, spinPos->value()) || item.key() == false) return false;
548     return true;
549 }
550
551
552