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