]> git.sesse.net Git - kdenlive/blob - src/geometryval.cpp
- Add ability to disable opacity in geometryval
[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(valueChanged(int)), this, SLOT(slotResizeCustom(int)));
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(int value)
214 {
215     if (!keyframeSelected())
216         return;
217     m_paramRect->setRect(0, 0, m_realWidth * value / 100, m_profile.height * value / 100);
218     slotUpdateTransitionProperties();
219 }
220
221 void Geometryval::slotTransparencyChanged(int transp)
222 {
223     int pos = spinPos->value();
224     Mlt::GeometryItem item;
225     int error = m_geom->fetch(&item, pos);
226     if (error || item.key() == false) {
227         // no keyframe under cursor
228         return;
229     }
230     item.mix(transp);
231     m_paramRect->setBrush(QColor(255, 0, 0, transp));
232     m_geom->insert(item);
233     emit parameterChanged();
234 }
235
236 void Geometryval::slotSyncCursor()
237 {
238     KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
239 }
240
241 void Geometryval::slotPositionChanged(int pos, bool seek)
242 {
243     if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos + m_startPoint);
244     spinPos->setValue(pos);
245     m_helper->setValue(pos);
246     Mlt::GeometryItem item;
247     int error = m_geom->fetch(&item, pos);
248     if (error || item.key() == false) {
249         // no keyframe under cursor, adjust buttons
250         buttonAdd->setEnabled(true);
251         buttonDelete->setEnabled(false);
252         widget->setEnabled(false);
253         spinTransp->setEnabled(false);
254         frameOptions->setEnabled(false);
255         m_reset->setEnabled(false);
256     } else {
257         buttonAdd->setEnabled(false);
258         buttonDelete->setEnabled(true);
259         widget->setEnabled(true);
260         spinTransp->setEnabled(true);
261         frameOptions->setEnabled(true);
262         m_reset->setEnabled(true);
263     }
264
265     m_paramRect->setPos(item.x() * m_dar, item.y());
266     m_paramRect->setRect(0, 0, item.w() * m_dar, item.h());
267     spinTransp->setValue(item.mix());
268     m_paramRect->setBrush(QColor(255, 0, 0, item.mix()));
269     slotUpdateGeometry();
270 }
271
272 void Geometryval::slotDeleteFrame(int pos)
273 {
274     // check there is more than one keyframe
275     Mlt::GeometryItem item;
276     if (pos == -1) pos = spinPos->value();
277     int error = m_geom->next_key(&item, pos + 1);
278     if (error) {
279         error = m_geom->prev_key(&item, pos - 1);
280         if (error || item.frame() == pos) return;
281     }
282
283     m_geom->remove(spinPos->value());
284     buttonAdd->setEnabled(true);
285     buttonDelete->setEnabled(false);
286     widget->setEnabled(false);
287     spinTransp->setEnabled(false);
288     frameOptions->setEnabled(false);
289     m_reset->setEnabled(false);
290     m_helper->update();
291     slotPositionChanged(pos, false);
292     updateTransitionPath();
293     emit parameterChanged();
294 }
295
296 void Geometryval::slotAddFrame(int pos)
297 {
298     if (pos == -1) pos = spinPos->value();
299     Mlt::GeometryItem item;
300     item.frame(pos);
301     QRectF r = m_paramRect->rect().normalized();
302     QPointF rectpos = m_paramRect->pos();
303     item.x(rectpos.x() / m_dar);
304     item.y(rectpos.y());
305     item.w(r.width() / m_dar);
306     item.h(r.height());
307     item.mix(spinTransp->value());
308     m_geom->insert(item);
309     buttonAdd->setEnabled(false);
310     buttonDelete->setEnabled(true);
311     widget->setEnabled(true);
312     spinTransp->setEnabled(true);
313     frameOptions->setEnabled(true);
314     m_reset->setEnabled(true);
315     m_helper->update();
316     emit parameterChanged();
317 }
318
319 void Geometryval::slotNextFrame()
320 {
321     Mlt::GeometryItem item;
322     int error = m_geom->next_key(&item, m_helper->value() + 1);
323     kDebug() << "// SEEK TO NEXT KFR: " << error;
324     if (error) {
325         // Go to end
326         spinPos->setValue(spinPos->maximum());
327         return;
328     }
329     int pos = item.frame();
330     spinPos->setValue(pos);
331 }
332
333 void Geometryval::slotPreviousFrame()
334 {
335     Mlt::GeometryItem item;
336     int error = m_geom->prev_key(&item, m_helper->value() - 1);
337     kDebug() << "// SEEK TO NEXT KFR: " << error;
338     if (error) return;
339     int pos = item.frame();
340     spinPos->setValue(pos);
341 }
342
343
344 QString Geometryval::getValue() const
345 {
346     return m_geom->serialise();
347 }
348
349 void Geometryval::setupParam(const QDomElement par, int minFrame, int maxFrame)
350 {
351     QString val = par.attribute("value");
352     if (par.attribute("fixed") == "1") {
353         m_fixedMode = true;
354         buttonPrevious->setHidden(true);
355         buttonNext->setHidden(true);
356         buttonDelete->setHidden(true);
357         buttonAdd->setHidden(true);
358         spinTransp->setMaximum(500);
359         label_pos->setHidden(true);
360         m_helper->setHidden(true);
361         spinPos->setHidden(true);
362     }
363     if (par.attribute("opacity") == "false") {
364         label_opacity->setHidden(true);
365         spinTransp->setHidden(true);
366     }
367     char *tmp = (char *) qstrdup(val.toUtf8().data());
368     if (m_geom) m_geom->parse(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
369     else m_geom = new Mlt::Geometry(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
370     delete[] tmp;
371
372     //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
373     //read param her and set rect
374     if (!m_fixedMode) {
375         m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
376         m_helper->update();
377         /*QDomDocument doc;
378         doc.appendChild(doc.importNode(par, true));
379         kDebug() << "IMPORTED TRANS: " << doc.toString();*/
380         spinPos->setMaximum(maxFrame - minFrame - 1);
381         if (m_path == NULL) {
382             m_path = new QGraphicsPathItem();
383             m_path->setPen(QPen(Qt::red));
384             m_scene->addItem(m_path);
385         }
386         updateTransitionPath();
387     }
388     Mlt::GeometryItem item;
389
390     m_geom->fetch(&item, 0);
391     delete m_paramRect;
392     m_paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w() * m_dar, item.h()));
393     m_paramRect->setPos(item.x() * m_dar, item.y());
394     m_paramRect->setZValue(0);
395     m_paramRect->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
396
397     m_paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
398     m_scene->addItem(m_paramRect);
399     slotPositionChanged(0, false);
400     slotUpdateGeometry();
401     if (!m_fixedMode) {
402         connect(spinPos, SIGNAL(valueChanged(int)), this , SLOT(slotPositionChanged(int)));
403     }
404     connect(spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
405 }
406
407 void Geometryval::updateTransitionPath()
408 {
409     if (m_fixedMode) return;
410     Mlt::GeometryItem item;
411     int pos = 0;
412     int counter = 0;
413     QPainterPath path;
414     while (true) {
415         if (m_geom->next_key(&item, pos) == 1) break;
416         pos = item.frame();
417         if (counter == 0) {
418             path.moveTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
419         } else {
420             path.lineTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
421         }
422         counter++;
423         pos++;
424     }
425     m_path->setPath(path);
426 }
427
428 void Geometryval::slotUpdateTransitionProperties()
429 {
430     int pos = spinPos->value();
431     Mlt::GeometryItem item;
432     int error = m_geom->next_key(&item, pos);
433     if (error || item.frame() != pos) {
434         // no keyframe under cursor
435         return;
436     }
437     QRectF r = m_paramRect->rect().normalized();
438     QPointF rectpos = m_paramRect->pos();
439     item.x(rectpos.x() / m_dar);
440     item.y(rectpos.y());
441     item.w(r.width() / m_dar);
442     item.h(r.height());
443     m_geom->insert(item);
444     updateTransitionPath();
445     emit parameterChanged();
446 }
447
448 void Geometryval::slotResetPosition()
449 {
450     spinX->setValue(0);
451     spinY->setValue(0);
452
453     if (m_frameSize.isNull()) {
454         spinWidth->setValue(m_realWidth);
455         spinHeight->setValue(m_profile.height);
456     } else {
457         spinWidth->setValue(m_frameSize.x());
458         spinHeight->setValue(m_frameSize.y());
459     }
460 }
461
462 void Geometryval::setFrameSize(QPoint p)
463 {
464     m_frameSize = p;
465 }
466
467
468 void Geometryval::slotKeyframeMoved(int pos)
469 {
470     slotPositionChanged(pos);
471     slotUpdateTransitionProperties();
472 }
473
474 void Geometryval::slotSwitchOptions()
475 {
476     if (frameOptions->isHidden()) {
477         frameOptions->setHidden(false);
478         m_editOptions->setChecked(true);
479     } else {
480         frameOptions->setHidden(true);
481         m_editOptions->setChecked(false);
482     }
483     adjustSize();
484 }
485
486 void Geometryval::slotGeometryX(int value)
487 {
488     if (!keyframeSelected())
489         return;
490     m_paramRect->setPos(value, spinY->value());
491     slotUpdateTransitionProperties();
492 }
493
494 void Geometryval::slotGeometryY(int value)
495 {
496     if (!keyframeSelected())
497         return;
498     m_paramRect->setPos(spinX->value(), value);
499     slotUpdateTransitionProperties();
500 }
501
502 void Geometryval::slotGeometryWidth(int value)
503 {
504     if (!keyframeSelected())
505         return;
506     m_paramRect->setRect(0, 0, value, spinHeight->value());
507     slotUpdateTransitionProperties();
508 }
509
510 void Geometryval::slotGeometryHeight(int value)
511 {
512     if (!keyframeSelected())
513         return;
514     m_paramRect->setRect(0, 0, spinWidth->value(), value);
515     slotUpdateTransitionProperties();
516 }
517
518 void Geometryval::slotUpdateGeometry()
519 {
520     QRectF r = m_paramRect->rect().normalized();
521
522     spinX->blockSignals(true);
523     spinY->blockSignals(true);
524     spinWidth->blockSignals(true);
525     spinHeight->blockSignals(true);
526     spinResize->blockSignals(true);
527
528     spinX->setValue(m_paramRect->pos().x());
529     spinY->setValue(m_paramRect->pos().y());
530     spinWidth->setValue(r.width());
531     spinHeight->setValue(r.height());
532     spinResize->setValue(m_paramRect->rect().width() * 100 / m_realWidth);
533
534     spinX->blockSignals(false);
535     spinY->blockSignals(false);
536     spinWidth->blockSignals(false);
537     spinHeight->blockSignals(false);
538     spinResize->blockSignals(false);
539 }
540
541 bool Geometryval::keyframeSelected()
542 {
543     Mlt::GeometryItem item;
544     if (m_geom->fetch(&item, spinPos->value()) || item.key() == false) return false;
545     return true;
546 }
547
548
549