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