]> git.sesse.net Git - kdenlive/blob - src/geometryval.cpp
Fix seek to pos in composite transition:
[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->setIcon(KIcon("system-run"));
81     buttonMenu->setMenu(m_configMenu);
82     buttonMenu->setPopupMode(QToolButton::QToolButton::InstantPopup);
83
84
85     m_editGeom = m_configMenu->addAction(i18n("Edit keyframe"), this, SLOT(slotGeometry()));
86
87     m_scaleMenu = new QMenu(i18n("Resize..."), this);
88     m_configMenu->addMenu(m_scaleMenu);
89     m_scaleMenu->addAction(i18n("50%"), this, SLOT(slotResize50()));
90     m_scaleMenu->addAction(i18n("100%"), this, SLOT(slotResize100()));
91     m_scaleMenu->addAction(i18n("200%"), this, SLOT(slotResize200()));
92     m_scaleMenu->addAction(i18n("Original size"), this, SLOT(slotResizeOriginal()));
93     m_scaleMenu->addAction(i18n("Custom"), this, SLOT(slotResizeCustom()));
94
95     m_alignMenu = new QMenu(i18n("Align..."), this);
96     m_configMenu->addMenu(m_alignMenu);
97     m_alignMenu->addAction(i18n("Center"), this, SLOT(slotAlignCenter()));
98     m_alignMenu->addAction(i18n("Hor. Center"), this, SLOT(slotAlignHCenter()));
99     m_alignMenu->addAction(i18n("Vert. Center"), this, SLOT(slotAlignVCenter()));
100     m_alignMenu->addAction(i18n("Right"), this, SLOT(slotAlignRight()));
101     m_alignMenu->addAction(i18n("Left"), this, SLOT(slotAlignLeft()));
102     m_alignMenu->addAction(i18n("Top"), this, SLOT(slotAlignTop()));
103     m_alignMenu->addAction(i18n("Bottom"), this, SLOT(slotAlignBottom()));
104
105
106     m_syncAction = m_configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
107     m_syncAction->setCheckable(true);
108     m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
109
110     //scene->setSceneRect(0, 0, profile.width * 2, profile.height * 2);
111     //view->fitInView(m_frameBorder, Qt::KeepAspectRatio);
112     const double sc = 100.0 / profile.height * 0.8;
113     QRectF srect = m_sceneview->sceneRect();
114     m_sceneview->setSceneRect(srect.x(), -srect.height() / 3 + 10, srect.width(), srect.height() + srect.height() / 3 * 2 - 10);
115     m_scene->setZoom(sc);
116     m_sceneview->centerOn(frameBorder);
117     m_sceneview->setMouseTracking(true);
118     connect(buttonNext , SIGNAL(clicked()) , this , SLOT(slotNextFrame()));
119     connect(buttonPrevious , SIGNAL(clicked()) , this , SLOT(slotPreviousFrame()));
120     connect(buttonDelete , SIGNAL(clicked()) , this , SLOT(slotDeleteFrame()));
121     connect(buttonAdd , SIGNAL(clicked()) , this , SLOT(slotAddFrame()));
122     connect(m_scene, SIGNAL(actionFinished()), this, SLOT(slotUpdateTransitionProperties()));
123     connect(m_scene, SIGNAL(doubleClickEvent()), this, SLOT(slotGeometry()));
124
125 }
126
127
128 Geometryval::~Geometryval()
129 {
130     m_scene->disconnect();
131     delete m_scaleMenu;
132     delete m_alignMenu;
133     delete m_editGeom;
134     delete m_syncAction;
135     delete m_configMenu;
136     delete m_paramRect;
137     delete m_path;
138     delete m_helper;
139     delete m_geom;
140     delete m_sceneview;
141     delete m_scene;
142 }
143
144
145 void Geometryval::slotAlignCenter()
146 {
147     int pos = spinPos->value();
148     Mlt::GeometryItem item;
149     int error = m_geom->fetch(&item, pos);
150     if (error || item.key() == false) {
151         // no keyframe under cursor
152         return;
153     }
154     m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, (m_profile.height - m_paramRect->rect().height()) / 2);
155     slotUpdateTransitionProperties();
156 }
157
158 void Geometryval::slotAlignHCenter()
159 {
160     int pos = spinPos->value();
161     Mlt::GeometryItem item;
162     int error = m_geom->fetch(&item, pos);
163     if (error || item.key() == false) {
164         // no keyframe under cursor
165         return;
166     }
167     m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, m_paramRect->pos().y());
168     slotUpdateTransitionProperties();
169 }
170
171 void Geometryval::slotAlignVCenter()
172 {
173     int pos = spinPos->value();
174     Mlt::GeometryItem item;
175     int error = m_geom->fetch(&item, pos);
176     if (error || item.key() == false) {
177         // no keyframe under cursor
178         return;
179     }
180     m_paramRect->setPos(m_paramRect->pos().x(), (m_profile.height - m_paramRect->rect().height()) / 2);
181     slotUpdateTransitionProperties();
182 }
183
184 void Geometryval::slotAlignTop()
185 {
186     int pos = spinPos->value();
187     Mlt::GeometryItem item;
188     int error = m_geom->fetch(&item, pos);
189     if (error || item.key() == false) {
190         // no keyframe under cursor
191         return;
192     }
193     m_paramRect->setPos(m_paramRect->pos().x(), 0);
194     slotUpdateTransitionProperties();
195 }
196
197 void Geometryval::slotAlignBottom()
198 {
199     int pos = spinPos->value();
200     Mlt::GeometryItem item;
201     int error = m_geom->fetch(&item, pos);
202     if (error || item.key() == false) {
203         // no keyframe under cursor
204         return;
205     }
206     m_paramRect->setPos(m_paramRect->pos().x(), m_profile.height - m_paramRect->rect().height());
207     slotUpdateTransitionProperties();
208 }
209
210 void Geometryval::slotAlignLeft()
211 {
212     int pos = spinPos->value();
213     Mlt::GeometryItem item;
214     int error = m_geom->fetch(&item, pos);
215     if (error || item.key() == false) {
216         // no keyframe under cursor
217         return;
218     }
219     m_paramRect->setPos(0, m_paramRect->pos().y());
220     slotUpdateTransitionProperties();
221 }
222
223 void Geometryval::slotAlignRight()
224 {
225     int pos = spinPos->value();
226     Mlt::GeometryItem item;
227     int error = m_geom->fetch(&item, pos);
228     if (error || item.key() == false) {
229         // no keyframe under cursor
230         return;
231     }
232     m_paramRect->setPos(m_realWidth - m_paramRect->rect().width(), m_paramRect->pos().y());
233     slotUpdateTransitionProperties();
234 }
235
236 void Geometryval::slotResize50()
237 {
238     int pos = spinPos->value();
239     Mlt::GeometryItem item;
240     int error = m_geom->fetch(&item, pos);
241     if (error || item.key() == false) {
242         // no keyframe under cursor
243         return;
244     }
245     m_paramRect->setRect(0, 0, m_realWidth / 2, m_profile.height / 2);
246     slotUpdateTransitionProperties();
247 }
248
249 void Geometryval::slotResize100()
250 {
251     int pos = spinPos->value();
252     Mlt::GeometryItem item;
253     int error = m_geom->fetch(&item, pos);
254     if (error || item.key() == false) {
255         // no keyframe under cursor
256         return;
257     }
258     m_paramRect->setRect(0, 0, m_realWidth, m_profile.height);
259     slotUpdateTransitionProperties();
260 }
261
262 void Geometryval::slotResize200()
263 {
264     int pos = spinPos->value();
265     Mlt::GeometryItem item;
266     int error = m_geom->fetch(&item, pos);
267     if (error || item.key() == false) {
268         // no keyframe under cursor
269         return;
270     }
271     m_paramRect->setRect(0, 0, m_realWidth * 2, m_profile.height * 2);
272     slotUpdateTransitionProperties();
273 }
274
275 void Geometryval::slotResizeOriginal()
276 {
277     if (m_frameSize.isNull()) slotResize100();
278     int pos = spinPos->value();
279     Mlt::GeometryItem item;
280     int error = m_geom->fetch(&item, pos);
281     if (error || item.key() == false) {
282         // no keyframe under cursor
283         return;
284     }
285     m_paramRect->setRect(0, 0, m_frameSize.x(), m_frameSize.y());
286     slotUpdateTransitionProperties();
287 }
288
289 void Geometryval::slotResizeCustom()
290 {
291     int pos = spinPos->value();
292     Mlt::GeometryItem item;
293     int error = m_geom->fetch(&item, pos);
294     if (error || item.key() == false) {
295         // no keyframe under cursor
296         return;
297     }
298     int scale = m_paramRect->rect().width() * 100 / m_realWidth;
299     bool ok;
300     scale =  QInputDialog::getInteger(this, i18n("Resize..."), i18n("Scale"), scale, 1, 2147483647, 10, &ok);
301     if (!ok) return;
302     m_paramRect->setRect(0, 0, m_realWidth * scale / 100, m_profile.height * scale / 100);
303     slotUpdateTransitionProperties();
304 }
305
306 void Geometryval::slotTransparencyChanged(int transp)
307 {
308     int pos = spinPos->value();
309     Mlt::GeometryItem item;
310     int error = m_geom->fetch(&item, pos);
311     if (error || item.key() == false) {
312         // no keyframe under cursor
313         return;
314     }
315     item.mix(transp);
316     m_paramRect->setBrush(QColor(255, 0, 0, transp));
317     m_geom->insert(item);
318     emit parameterChanged();
319 }
320
321 void Geometryval::slotSyncCursor()
322 {
323     KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
324 }
325
326 void Geometryval::slotPositionChanged(int pos, bool seek)
327 {
328     if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos + m_startPoint);
329     spinPos->setValue(pos);
330     m_helper->setValue(pos);
331     Mlt::GeometryItem item;
332     int error = m_geom->fetch(&item, pos);
333     if (error || item.key() == false) {
334         // no keyframe under cursor, adjust buttons
335         buttonAdd->setEnabled(true);
336         buttonDelete->setEnabled(false);
337         widget->setEnabled(false);
338         spinTransp->setEnabled(false);
339         m_scaleMenu->setEnabled(false);
340         m_alignMenu->setEnabled(false);
341         m_editGeom->setEnabled(false);
342     } else {
343         buttonAdd->setEnabled(false);
344         buttonDelete->setEnabled(true);
345         widget->setEnabled(true);
346         spinTransp->setEnabled(true);
347         m_scaleMenu->setEnabled(true);
348         m_alignMenu->setEnabled(true);
349         m_editGeom->setEnabled(true);
350     }
351
352     m_paramRect->setPos(item.x() * m_dar, item.y());
353     m_paramRect->setRect(0, 0, item.w() * m_dar, item.h());
354     spinTransp->setValue(item.mix());
355     m_paramRect->setBrush(QColor(255, 0, 0, item.mix()));
356 }
357
358 void Geometryval::slotDeleteFrame(int pos)
359 {
360     // check there is more than one keyframe
361     Mlt::GeometryItem item;
362     if (pos == -1) pos = spinPos->value();
363     int error = m_geom->next_key(&item, pos + 1);
364     if (error) {
365         error = m_geom->prev_key(&item, pos - 1);
366         if (error || item.frame() == pos) return;
367     }
368
369     m_geom->remove(spinPos->value());
370     buttonAdd->setEnabled(true);
371     buttonDelete->setEnabled(false);
372     widget->setEnabled(false);
373     spinTransp->setEnabled(false);
374     m_scaleMenu->setEnabled(false);
375     m_alignMenu->setEnabled(false);
376     m_editGeom->setEnabled(false);
377     m_helper->update();
378     slotPositionChanged(pos, false);
379     updateTransitionPath();
380     emit parameterChanged();
381 }
382
383 void Geometryval::slotAddFrame(int pos)
384 {
385     if (pos == -1) pos = spinPos->value();
386     Mlt::GeometryItem item;
387     item.frame(pos);
388     QRectF r = m_paramRect->rect().normalized();
389     QPointF rectpos = m_paramRect->pos();
390     item.x(rectpos.x() / m_dar);
391     item.y(rectpos.y());
392     item.w(r.width() / m_dar);
393     item.h(r.height());
394     item.mix(spinTransp->value());
395     m_geom->insert(item);
396     buttonAdd->setEnabled(false);
397     buttonDelete->setEnabled(true);
398     widget->setEnabled(true);
399     spinTransp->setEnabled(true);
400     m_scaleMenu->setEnabled(true);
401     m_alignMenu->setEnabled(true);
402     m_editGeom->setEnabled(true);
403     m_helper->update();
404     emit parameterChanged();
405 }
406
407 void Geometryval::slotNextFrame()
408 {
409     Mlt::GeometryItem item;
410     int error = m_geom->next_key(&item, m_helper->value() + 1);
411     kDebug() << "// SEEK TO NEXT KFR: " << error;
412     if (error) {
413         // Go to end
414         spinPos->setValue(spinPos->maximum());
415         return;
416     }
417     int pos = item.frame();
418     spinPos->setValue(pos);
419 }
420
421 void Geometryval::slotPreviousFrame()
422 {
423     Mlt::GeometryItem item;
424     int error = m_geom->prev_key(&item, m_helper->value() - 1);
425     kDebug() << "// SEEK TO NEXT KFR: " << error;
426     if (error) return;
427     int pos = item.frame();
428     spinPos->setValue(pos);
429 }
430
431
432 QString Geometryval::getValue() const
433 {
434     return m_geom->serialise();
435 }
436
437 void Geometryval::setupParam(const QDomElement par, int minFrame, int maxFrame)
438 {
439     QString val = par.attribute("value");
440     if (par.attribute("fixed") == "1") {
441         m_fixedMode = true;
442         buttonPrevious->setHidden(true);
443         buttonNext->setHidden(true);
444         buttonDelete->setHidden(true);
445         buttonAdd->setHidden(true);
446         spinTransp->setMaximum(500);
447         label_pos->setHidden(true);
448         m_helper->setHidden(true);
449         spinPos->setHidden(true);
450
451     }
452     char *tmp = (char *) qstrdup(val.toUtf8().data());
453     if (m_geom) m_geom->parse(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
454     else m_geom = new Mlt::Geometry(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
455     delete[] tmp;
456
457     //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
458     //read param her and set rect
459     if (!m_fixedMode) {
460         m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
461         m_helper->update();
462         /*QDomDocument doc;
463         doc.appendChild(doc.importNode(par, true));
464         kDebug() << "IMPORTED TRANS: " << doc.toString();*/
465         spinPos->setMaximum(maxFrame - minFrame - 1);
466         if (m_path == NULL) {
467             m_path = new QGraphicsPathItem();
468             m_path->setPen(QPen(Qt::red));
469             m_scene->addItem(m_path);
470         }
471         updateTransitionPath();
472     }
473     Mlt::GeometryItem item;
474
475     m_geom->fetch(&item, 0);
476     delete m_paramRect;
477     m_paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w() * m_dar, item.h()));
478     m_paramRect->setPos(item.x() * m_dar, item.y());
479     m_paramRect->setZValue(0);
480     m_paramRect->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
481
482     m_paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
483     m_scene->addItem(m_paramRect);
484     slotPositionChanged(0, false);
485     if (!m_fixedMode) {
486         connect(spinPos, SIGNAL(valueChanged(int)), this , SLOT(slotPositionChanged(int)));
487     }
488     connect(spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
489 }
490
491 void Geometryval::updateTransitionPath()
492 {
493     if (m_fixedMode) return;
494     Mlt::GeometryItem item;
495     int pos = 0;
496     int counter = 0;
497     QPainterPath path;
498     while (true) {
499         if (m_geom->next_key(&item, pos) == 1) break;
500         pos = item.frame();
501         if (counter == 0) {
502             path.moveTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
503         } else {
504             path.lineTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
505         }
506         counter++;
507         pos++;
508     }
509     m_path->setPath(path);
510 }
511
512 void Geometryval::slotUpdateTransitionProperties()
513 {
514     int pos = spinPos->value();
515     Mlt::GeometryItem item;
516     int error = m_geom->next_key(&item, pos);
517     if (error || item.frame() != pos) {
518         // no keyframe under cursor
519         return;
520     }
521     QRectF r = m_paramRect->rect().normalized();
522     QPointF rectpos = m_paramRect->pos();
523     item.x(rectpos.x() / m_dar);
524     item.y(rectpos.y());
525     item.w(r.width() / m_dar);
526     item.h(r.height());
527     m_geom->insert(item);
528     updateTransitionPath();
529     emit parameterChanged();
530 }
531
532 void Geometryval::slotGeometry()
533 {
534     int pos = spinPos->value();
535     Mlt::GeometryItem item;
536     int error = m_geom->fetch(&item, pos);
537     if (error || item.key() == false) {
538         // no keyframe under cursor
539         return;
540     }
541     QRectF r = m_paramRect->rect().normalized();
542
543     QDialog d(this);
544     m_view.setupUi(&d);
545     d.setWindowTitle(i18n("Frame Geometry"));
546     m_view.value_x->setMaximum(10000);
547     m_view.value_x->setMinimum(-10000);
548     m_view.value_y->setMaximum(10000);
549     m_view.value_y->setMinimum(-10000);
550     m_view.value_width->setMaximum(500000);
551     m_view.value_width->setMinimum(1);
552     m_view.value_height->setMaximum(500000);
553     m_view.value_height->setMinimum(1);
554
555     m_view.value_x->setValue(m_paramRect->pos().x());
556     m_view.value_y->setValue(m_paramRect->pos().y());
557     m_view.value_width->setValue(r.width());
558     m_view.value_height->setValue(r.height());
559     connect(m_view.button_reset , SIGNAL(clicked()) , this , SLOT(slotResetPosition()));
560
561     if (d.exec() == QDialog::Accepted) {
562         m_paramRect->setPos(m_view.value_x->value(), m_view.value_y->value());
563         m_paramRect->setRect(0, 0, m_view.value_width->value(), m_view.value_height->value());
564         slotUpdateTransitionProperties();
565     }
566 }
567
568 void Geometryval::slotResetPosition()
569 {
570     m_view.value_x->setValue(0);
571     m_view.value_y->setValue(0);
572
573     if (m_frameSize.isNull()) {
574         m_view.value_width->setValue(m_realWidth);
575         m_view.value_height->setValue(m_profile.height);
576     } else {
577         m_view.value_width->setValue(m_frameSize.x());
578         m_view.value_height->setValue(m_frameSize.y());
579     }
580 }
581
582 void Geometryval::setFrameSize(QPoint p)
583 {
584     m_frameSize = p;
585 }
586
587
588 void Geometryval::slotKeyframeMoved(int pos)
589 {
590     slotPositionChanged(pos);
591     slotUpdateTransitionProperties();
592 }
593