]> git.sesse.net Git - kdenlive/blob - src/geometryval.cpp
Fix label
[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 #include <QTimer>
30
31
32 Geometryval::Geometryval(const MltVideoProfile &profile, const Timecode &t, const QPoint &frame_size, int startPoint, QWidget* parent) :
33         QWidget(parent),
34         m_profile(profile),
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     toolbarlayout->addWidget(&m_timePos);
45     toolbarlayout->insertStretch(-1);
46
47     QVBoxLayout* vbox = new QVBoxLayout(widget);
48     m_sceneview = new QGraphicsView(this);
49     m_sceneview->setBackgroundBrush(QBrush(Qt::black));
50     vbox->addWidget(m_sceneview);
51     vbox->setContentsMargins(0, 0, 0, 0);
52
53     QVBoxLayout* vbox2 = new QVBoxLayout(keyframeWidget);
54     m_helper = new KeyframeHelper(this);
55     vbox2->addWidget(m_helper);
56     vbox2->setContentsMargins(0, 0, 0, 0);
57
58     connect(m_helper, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int)));
59     connect(m_helper, SIGNAL(keyframeMoved(int)), this, SLOT(slotKeyframeMoved(int)));
60     connect(m_helper, SIGNAL(addKeyframe(int)), this, SLOT(slotAddFrame(int)));
61     connect(m_helper, SIGNAL(removeKeyframe(int)), this, SLOT(slotDeleteFrame(int)));
62
63     m_scene = new GraphicsSceneRectMove(this);
64     m_scene->setTool(TITLE_SELECT);
65     m_sceneview->setScene(m_scene);
66     m_dar = (m_profile.height * m_profile.display_aspect_num / (double) m_profile.display_aspect_den) / (double) m_profile.width;
67
68     m_realWidth = (int)(profile.height * profile.display_aspect_num / (double) profile.display_aspect_den + 0.5);
69     QGraphicsRectItem *frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_realWidth, profile.height));
70     frameBorder->setZValue(-1100);
71     frameBorder->setBrush(QColor(255, 255, 0, 30));
72     frameBorder->setPen(QPen(QBrush(QColor(255, 255, 255, 255)), 1.0, Qt::DashLine));
73     m_scene->addItem(frameBorder);
74
75     buttonNext->setIcon(KIcon("media-skip-forward"));
76     buttonNext->setToolTip(i18n("Go to next keyframe"));
77     buttonPrevious->setIcon(KIcon("media-skip-backward"));
78     buttonPrevious->setToolTip(i18n("Go to previous keyframe"));
79     buttonAdd->setIcon(KIcon("document-new"));
80     buttonAdd->setToolTip(i18n("Add keyframe"));
81     buttonDelete->setIcon(KIcon("edit-delete"));
82     buttonDelete->setToolTip(i18n("Delete keyframe"));
83
84     m_configMenu = new QMenu(i18n("Misc..."), this);
85     buttonMenu->setMenu(m_configMenu);
86     buttonMenu->setPopupMode(QToolButton::MenuButtonPopup);
87
88     m_editOptions = m_configMenu->addAction(KIcon("system-run"), i18n("Show/Hide options"));
89     m_editOptions->setCheckable(true);
90     buttonMenu->setDefaultAction(m_editOptions);
91     connect(m_editOptions, SIGNAL(triggered()), this, SLOT(slotSwitchOptions()));
92     slotSwitchOptions();
93
94     m_reset = m_configMenu->addAction(KIcon("view-refresh"), i18n("Reset"), this, SLOT(slotResetPosition()));
95
96     m_syncAction = m_configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
97     m_syncAction->setCheckable(true);
98     m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
99
100     //scene->setSceneRect(0, 0, profile.width * 2, profile.height * 2);
101     //view->fitInView(m_frameBorder, Qt::KeepAspectRatio);
102     const double sc = 100.0 / profile.height * 0.8;
103     QRectF srect = m_sceneview->sceneRect();
104     m_sceneview->setSceneRect(srect.x(), -srect.height() / 3 + 10, srect.width(), srect.height() + srect.height() / 3 * 2 - 10);
105     m_scene->setZoom(sc);
106     m_sceneview->centerOn(frameBorder);
107     m_sceneview->setMouseTracking(true);
108     connect(buttonNext , SIGNAL(clicked()) , this , SLOT(slotNextFrame()));
109     connect(buttonPrevious , SIGNAL(clicked()) , this , SLOT(slotPreviousFrame()));
110     connect(buttonDelete , SIGNAL(clicked()) , this , SLOT(slotDeleteFrame()));
111     connect(buttonAdd , SIGNAL(clicked()) , this , SLOT(slotAddFrame()));
112     connect(m_scene, SIGNAL(actionFinished()), this, SLOT(slotUpdateTransitionProperties()));
113
114     buttonhcenter->setIcon(KIcon("kdenlive-align-hor"));
115     buttonhcenter->setToolTip(i18n("Align item horizontally"));
116     buttonvcenter->setIcon(KIcon("kdenlive-align-vert"));
117     buttonvcenter->setToolTip(i18n("Align item vertically"));
118     buttontop->setIcon(KIcon("kdenlive-align-top"));
119     buttontop->setToolTip(i18n("Align item to top"));
120     buttonbottom->setIcon(KIcon("kdenlive-align-bottom"));
121     buttonbottom->setToolTip(i18n("Align item to bottom"));
122     buttonright->setIcon(KIcon("kdenlive-align-right"));
123     buttonright->setToolTip(i18n("Align item to right"));
124     buttonleft->setIcon(KIcon("kdenlive-align-left"));
125     buttonleft->setToolTip(i18n("Align item to left"));
126
127     connect(buttonhcenter, SIGNAL(clicked()), this, SLOT(slotAlignHCenter()));
128     connect(buttonvcenter, SIGNAL(clicked()), this, SLOT(slotAlignVCenter()));
129     connect(buttontop, SIGNAL(clicked()), this, SLOT(slotAlignTop()));
130     connect(buttonbottom, SIGNAL(clicked()), this, SLOT(slotAlignBottom()));
131     connect(buttonright, SIGNAL(clicked()), this, SLOT(slotAlignRight()));
132     connect(buttonleft, SIGNAL(clicked()), this, SLOT(slotAlignLeft()));
133     connect(spinX, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryX(int)));
134     connect(spinY, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryY(int)));
135     connect(spinWidth, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryWidth(int)));
136     connect(spinHeight, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryHeight(int)));
137     connect(spinResize, SIGNAL(editingFinished()), this, SLOT(slotResizeCustom()));
138     connect(buttonResize, SIGNAL(clicked()), this, SLOT(slotResizeOriginal()));
139
140     connect(this, SIGNAL(parameterChanged()), this, SLOT(slotUpdateGeometry()));
141 }
142
143
144 Geometryval::~Geometryval()
145 {
146     m_scene->disconnect();
147     delete m_syncAction;
148     delete m_configMenu;
149     delete m_paramRect;
150     delete m_path;
151     delete m_helper;
152     delete m_geom;
153     delete m_sceneview;
154     delete m_scene;
155 }
156
157
158 void Geometryval::slotAlignHCenter()
159 {
160     if (!keyframeSelected())
161         return;
162     m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, m_paramRect->pos().y());
163     slotUpdateTransitionProperties();
164 }
165
166 void Geometryval::slotAlignVCenter()
167 {
168     if (!keyframeSelected())
169         return;
170     m_paramRect->setPos(m_paramRect->pos().x(), (m_profile.height - m_paramRect->rect().height()) / 2);
171     slotUpdateTransitionProperties();
172 }
173
174 void Geometryval::slotAlignTop()
175 {
176     if (!keyframeSelected())
177         return;
178     m_paramRect->setPos(m_paramRect->pos().x(), 0);
179     slotUpdateTransitionProperties();
180 }
181
182 void Geometryval::slotAlignBottom()
183 {
184     if (!keyframeSelected())
185         return;
186     m_paramRect->setPos(m_paramRect->pos().x(), m_profile.height - m_paramRect->rect().height());
187     slotUpdateTransitionProperties();
188 }
189
190 void Geometryval::slotAlignLeft()
191 {
192     if (!keyframeSelected())
193         return;
194     m_paramRect->setPos(0, m_paramRect->pos().y());
195     slotUpdateTransitionProperties();
196 }
197
198 void Geometryval::slotAlignRight()
199 {
200     if (!keyframeSelected())
201         return;
202     m_paramRect->setPos(m_realWidth - m_paramRect->rect().width(), m_paramRect->pos().y());
203     slotUpdateTransitionProperties();
204 }
205
206 void Geometryval::slotResizeOriginal()
207 {
208     if (!keyframeSelected())
209         return;
210     if (m_frameSize.isNull())
211         m_paramRect->setRect(0, 0, m_realWidth, m_profile.height);
212     else
213         m_paramRect->setRect(0, 0, m_frameSize.x(), m_frameSize.y());
214     slotUpdateTransitionProperties();
215 }
216
217 void Geometryval::slotResizeCustom()
218 {
219     if (!keyframeSelected())
220         return;
221     int value = spinResize->value();
222     m_paramRect->setRect(0, 0, m_realWidth * value / 100, m_profile.height * value / 100);
223     slotUpdateTransitionProperties();
224 }
225
226 void Geometryval::slotTransparencyChanged(int transp)
227 {
228     int pos = m_timePos.getValue();
229     Mlt::GeometryItem item;
230     int error = m_geom->fetch(&item, pos);
231     if (error || item.key() == false) {
232         // no keyframe under cursor
233         return;
234     }
235     item.mix(transp);
236     m_paramRect->setBrush(QColor(255, 0, 0, transp));
237     m_geom->insert(item);
238     emit parameterChanged();
239 }
240
241 void Geometryval::slotSyncCursor()
242 {
243     KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
244 }
245
246 void Geometryval::updateTimecodeFormat()
247 {
248     m_timePos.slotUpdateTimeCodeFormat();
249 }
250
251 void Geometryval::slotPositionChanged(int pos, bool seek)
252 {
253     if (pos == -1) {
254         pos = m_timePos.getValue();
255     }
256     if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos + m_startPoint);
257     m_timePos.setValue(pos);
258     //spinPos->setValue(pos);
259     m_helper->blockSignals(true);
260     m_helper->setValue(pos);
261     m_helper->blockSignals(false);
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.getValue();
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.getValue();
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     }
387     if (m_geom)
388         m_geom->parse(val.toUtf8().data(), maxFrame - minFrame, m_profile.width, m_profile.height);
389     else
390         m_geom = new Mlt::Geometry(val.toUtf8().data(), maxFrame - minFrame, m_profile.width, m_profile.height);
391
392     //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
393     //read param her and set rect
394     if (!m_fixedMode) {
395         m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
396         m_helper->update();
397         /*QDomDocument doc;
398         doc.appendChild(doc.importNode(par, true));
399         kDebug() << "IMPORTED TRANS: " << doc.toString();*/
400         if (m_path == NULL) {
401             m_path = new QGraphicsPathItem();
402             m_path->setPen(QPen(Qt::red));
403             m_scene->addItem(m_path);
404         }
405         updateTransitionPath();
406     }
407     Mlt::GeometryItem item;
408
409     m_geom->fetch(&item, 0);
410     delete m_paramRect;
411     m_paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w() * m_dar, item.h()));
412     m_paramRect->setPos(item.x() * m_dar, item.y());
413     m_paramRect->setZValue(0);
414     m_paramRect->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
415
416     m_paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
417     m_scene->addItem(m_paramRect);
418     slotPositionChanged(0, false);
419     slotUpdateGeometry();
420     if (!m_fixedMode) {
421         m_timePos.setRange(0, maxFrame - minFrame - 1);
422         connect(&m_timePos, SIGNAL(timeCodeEditingFinished()), this , SLOT(slotPositionChanged()));
423     }
424     connect(spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
425 }
426
427 void Geometryval::slotSyncPosition(int relTimelinePos)
428 {
429     if (m_timePos.maximum() > 0 && KdenliveSettings::transitionfollowcursor()) {
430         relTimelinePos = qMax(0, relTimelinePos);
431         relTimelinePos = qMin(relTimelinePos, m_timePos.maximum());
432         if (relTimelinePos != m_timePos.getValue())
433             slotPositionChanged(relTimelinePos, false);
434     }
435 }
436
437 void Geometryval::updateTransitionPath()
438 {
439     if (m_fixedMode) return;
440     Mlt::GeometryItem item;
441     int pos = 0;
442     int counter = 0;
443     QPainterPath path;
444     while (true) {
445         if (m_geom->next_key(&item, pos) == 1) break;
446         pos = item.frame();
447         if (counter == 0) {
448             path.moveTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
449         } else {
450             path.lineTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
451         }
452         counter++;
453         pos++;
454     }
455     m_path->setPath(path);
456 }
457
458 void Geometryval::slotUpdateTransitionProperties()
459 {
460     int pos = m_timePos.getValue();
461     Mlt::GeometryItem item;
462     int error = m_geom->next_key(&item, pos);
463     if (error || item.frame() != pos) {
464         // no keyframe under cursor
465         return;
466     }
467     QRectF r = m_paramRect->rect().normalized();
468     QPointF rectpos = m_paramRect->pos();
469     item.x(rectpos.x() / m_dar);
470     item.y(rectpos.y());
471     item.w(r.width() / m_dar);
472     item.h(r.height());
473     m_geom->insert(item);
474     updateTransitionPath();
475     emit parameterChanged();
476 }
477
478 void Geometryval::slotResetPosition()
479 {
480     spinX->setValue(0);
481     spinY->setValue(0);
482
483     if (m_frameSize.isNull()) {
484         spinWidth->setValue(m_realWidth);
485         spinHeight->setValue(m_profile.height);
486     } else {
487         spinWidth->setValue(m_frameSize.x());
488         spinHeight->setValue(m_frameSize.y());
489     }
490 }
491
492 void Geometryval::setFrameSize(const QPoint &p)
493 {
494     m_frameSize = p;
495 }
496
497
498 void Geometryval::slotKeyframeMoved(int pos)
499 {
500     slotPositionChanged(pos);
501     slotUpdateTransitionProperties();
502     QTimer::singleShot(100, this, SIGNAL(parameterChanged()));
503 }
504
505 void Geometryval::slotSwitchOptions()
506 {
507     if (frameOptions->isHidden()) {
508         frameOptions->setHidden(false);
509         m_editOptions->setChecked(true);
510     } else {
511         frameOptions->setHidden(true);
512         m_editOptions->setChecked(false);
513     }
514     //adjustSize();
515 }
516
517 void Geometryval::slotGeometryX(int value)
518 {
519     if (!keyframeSelected())
520         return;
521     m_paramRect->setPos(value, spinY->value());
522     slotUpdateTransitionProperties();
523 }
524
525 void Geometryval::slotGeometryY(int value)
526 {
527     if (!keyframeSelected())
528         return;
529     m_paramRect->setPos(spinX->value(), value);
530     slotUpdateTransitionProperties();
531 }
532
533 void Geometryval::slotGeometryWidth(int value)
534 {
535     if (!keyframeSelected())
536         return;
537     m_paramRect->setRect(0, 0, value, spinHeight->value());
538     slotUpdateTransitionProperties();
539 }
540
541 void Geometryval::slotGeometryHeight(int value)
542 {
543     if (!keyframeSelected())
544         return;
545     m_paramRect->setRect(0, 0, spinWidth->value(), value);
546     slotUpdateTransitionProperties();
547 }
548
549 void Geometryval::slotUpdateGeometry()
550 {
551     QRectF r = m_paramRect->rect().normalized();
552
553     spinX->blockSignals(true);
554     spinY->blockSignals(true);
555     spinWidth->blockSignals(true);
556     spinHeight->blockSignals(true);
557     spinResize->blockSignals(true);
558
559     spinX->setValue(m_paramRect->pos().x());
560     spinY->setValue(m_paramRect->pos().y());
561     spinWidth->setValue(r.width());
562     spinHeight->setValue(r.height());
563     spinResize->setValue(m_paramRect->rect().width() * 100 / m_realWidth);
564
565     spinX->blockSignals(false);
566     spinY->blockSignals(false);
567     spinWidth->blockSignals(false);
568     spinHeight->blockSignals(false);
569     spinResize->blockSignals(false);
570 }
571
572 bool Geometryval::keyframeSelected()
573 {
574     Mlt::GeometryItem item;
575     int pos = m_timePos.getValue();
576     if (m_geom->fetch(&item, pos) || item.key() == false) return false;
577     return true;
578 }
579
580
581 void Geometryval::slotUpdateRange(int inPoint, int outPoint)
582 {
583     m_helper->setKeyGeometry(m_geom, outPoint - inPoint - 1);
584     m_helper->update();
585     m_timePos.setRange(0, outPoint - inPoint - 1);
586 }
587
588 #include "geometryval.moc"