1 /***************************************************************************
2 geomeytrval.cpp - description
5 copyright : (C) 2008 by Marco Gittler
6 email : g.marco@freenet.de
7 ***************************************************************************/
9 /***************************************************************************
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. *
16 ***************************************************************************/
18 #include "geometryval.h"
19 #include "graphicsscenerectmove.h"
20 #include "kdenlivesettings.h"
24 #include <QGraphicsView>
25 #include <QVBoxLayout>
26 #include <QGraphicsRectItem>
28 #include <QInputDialog>
31 Geometryval::Geometryval(const MltVideoProfile profile, QPoint frame_size, int startPoint, QWidget* parent) :
38 m_frameSize(frame_size),
39 m_startPoint(startPoint)
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);
48 QVBoxLayout* vbox2 = new QVBoxLayout(keyframeWidget);
49 m_helper = new KeyframeHelper(this);
50 vbox2->addWidget(m_helper);
51 vbox2->setContentsMargins(0, 0, 0, 0);
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)));
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;
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);
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"));
79 m_configMenu = new QMenu(i18n("Misc..."), this);
80 buttonMenu->setMenu(m_configMenu);
81 buttonMenu->setPopupMode(QToolButton::MenuButtonPopup);
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()));
89 m_reset = m_configMenu->addAction(KIcon("view-refresh"), i18n("Reset"), this, SLOT(slotResetPosition()));
91 m_syncAction = m_configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
92 m_syncAction->setCheckable(true);
93 m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
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()));
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"));
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(editingFinished()), this, SLOT(slotResizeCustom()));
134 connect(buttonResize, SIGNAL(clicked()), this, SLOT(slotResizeOriginal()));
136 connect(this, SIGNAL(parameterChanged()), this, SLOT(slotUpdateGeometry()));
140 Geometryval::~Geometryval()
142 m_scene->disconnect();
154 void Geometryval::slotAlignHCenter()
156 if (!keyframeSelected())
158 m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, m_paramRect->pos().y());
159 slotUpdateTransitionProperties();
162 void Geometryval::slotAlignVCenter()
164 if (!keyframeSelected())
166 m_paramRect->setPos(m_paramRect->pos().x(), (m_profile.height - m_paramRect->rect().height()) / 2);
167 slotUpdateTransitionProperties();
170 void Geometryval::slotAlignTop()
172 if (!keyframeSelected())
174 m_paramRect->setPos(m_paramRect->pos().x(), 0);
175 slotUpdateTransitionProperties();
178 void Geometryval::slotAlignBottom()
180 if (!keyframeSelected())
182 m_paramRect->setPos(m_paramRect->pos().x(), m_profile.height - m_paramRect->rect().height());
183 slotUpdateTransitionProperties();
186 void Geometryval::slotAlignLeft()
188 if (!keyframeSelected())
190 m_paramRect->setPos(0, m_paramRect->pos().y());
191 slotUpdateTransitionProperties();
194 void Geometryval::slotAlignRight()
196 if (!keyframeSelected())
198 m_paramRect->setPos(m_realWidth - m_paramRect->rect().width(), m_paramRect->pos().y());
199 slotUpdateTransitionProperties();
202 void Geometryval::slotResizeOriginal()
204 if (!keyframeSelected())
206 if (m_frameSize.isNull())
207 m_paramRect->setRect(0, 0, m_realWidth, m_profile.height);
209 m_paramRect->setRect(0, 0, m_frameSize.x(), m_frameSize.y());
210 slotUpdateTransitionProperties();
213 void Geometryval::slotResizeCustom()
215 if (!keyframeSelected())
217 int value = spinResize->value();
218 m_paramRect->setRect(0, 0, m_realWidth * value / 100, m_profile.height * value / 100);
219 slotUpdateTransitionProperties();
222 void Geometryval::slotTransparencyChanged(int transp)
224 int pos = spinPos->value();
225 Mlt::GeometryItem item;
226 int error = m_geom->fetch(&item, pos);
227 if (error || item.key() == false) {
228 // no keyframe under cursor
232 m_paramRect->setBrush(QColor(255, 0, 0, transp));
233 m_geom->insert(item);
234 emit parameterChanged();
237 void Geometryval::slotSyncCursor()
239 KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
242 void Geometryval::slotPositionChanged(int pos, bool seek)
244 if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos + m_startPoint);
245 spinPos->setValue(pos);
246 m_helper->setValue(pos);
247 Mlt::GeometryItem item;
248 int error = m_geom->fetch(&item, pos);
249 if (error || item.key() == false) {
250 // no keyframe under cursor, adjust buttons
251 buttonAdd->setEnabled(true);
252 buttonDelete->setEnabled(false);
253 widget->setEnabled(false);
254 spinTransp->setEnabled(false);
255 frameOptions->setEnabled(false);
256 m_reset->setEnabled(false);
258 buttonAdd->setEnabled(false);
259 buttonDelete->setEnabled(true);
260 widget->setEnabled(true);
261 spinTransp->setEnabled(true);
262 frameOptions->setEnabled(true);
263 m_reset->setEnabled(true);
266 m_paramRect->setPos(item.x() * m_dar, item.y());
267 m_paramRect->setRect(0, 0, item.w() * m_dar, item.h());
268 spinTransp->setValue(item.mix());
269 m_paramRect->setBrush(QColor(255, 0, 0, item.mix()));
270 slotUpdateGeometry();
273 void Geometryval::slotDeleteFrame(int pos)
275 // check there is more than one keyframe
276 Mlt::GeometryItem item;
277 if (pos == -1) pos = spinPos->value();
278 int error = m_geom->next_key(&item, pos + 1);
280 error = m_geom->prev_key(&item, pos - 1);
281 if (error || item.frame() == pos) return;
284 m_geom->remove(spinPos->value());
285 buttonAdd->setEnabled(true);
286 buttonDelete->setEnabled(false);
287 widget->setEnabled(false);
288 spinTransp->setEnabled(false);
289 frameOptions->setEnabled(false);
290 m_reset->setEnabled(false);
292 slotPositionChanged(pos, false);
293 updateTransitionPath();
294 emit parameterChanged();
297 void Geometryval::slotAddFrame(int pos)
299 if (pos == -1) pos = spinPos->value();
300 Mlt::GeometryItem item;
302 QRectF r = m_paramRect->rect().normalized();
303 QPointF rectpos = m_paramRect->pos();
304 item.x(rectpos.x() / m_dar);
306 item.w(r.width() / m_dar);
308 item.mix(spinTransp->value());
309 m_geom->insert(item);
310 buttonAdd->setEnabled(false);
311 buttonDelete->setEnabled(true);
312 widget->setEnabled(true);
313 spinTransp->setEnabled(true);
314 frameOptions->setEnabled(true);
315 m_reset->setEnabled(true);
317 emit parameterChanged();
320 void Geometryval::slotNextFrame()
322 Mlt::GeometryItem item;
323 int error = m_geom->next_key(&item, m_helper->value() + 1);
324 kDebug() << "// SEEK TO NEXT KFR: " << error;
327 spinPos->setValue(spinPos->maximum());
330 int pos = item.frame();
331 spinPos->setValue(pos);
334 void Geometryval::slotPreviousFrame()
336 Mlt::GeometryItem item;
337 int error = m_geom->prev_key(&item, m_helper->value() - 1);
338 kDebug() << "// SEEK TO NEXT KFR: " << error;
340 int pos = item.frame();
341 spinPos->setValue(pos);
345 QString Geometryval::getValue() const
347 return m_geom->serialise();
350 void Geometryval::setupParam(const QDomElement par, int minFrame, int maxFrame)
352 QString val = par.attribute("value");
353 if (par.attribute("fixed") == "1") {
355 buttonPrevious->setHidden(true);
356 buttonNext->setHidden(true);
357 buttonDelete->setHidden(true);
358 buttonAdd->setHidden(true);
359 spinTransp->setMaximum(500);
360 label_pos->setHidden(true);
361 m_helper->setHidden(true);
362 spinPos->setHidden(true);
364 if (par.attribute("opacity") == "false") {
365 label_opacity->setHidden(true);
366 spinTransp->setHidden(true);
367 } else if (par.attribute("opacity") == "nonzero") {
368 spinTransp->setMinimum(1);
370 char *tmp = (char *) qstrdup(val.toUtf8().data());
371 if (m_geom) m_geom->parse(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
372 else m_geom = new Mlt::Geometry(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
375 //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
376 //read param her and set rect
378 m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
381 doc.appendChild(doc.importNode(par, true));
382 kDebug() << "IMPORTED TRANS: " << doc.toString();*/
383 spinPos->setMaximum(maxFrame - minFrame - 1);
384 if (m_path == NULL) {
385 m_path = new QGraphicsPathItem();
386 m_path->setPen(QPen(Qt::red));
387 m_scene->addItem(m_path);
389 updateTransitionPath();
391 Mlt::GeometryItem item;
393 m_geom->fetch(&item, 0);
395 m_paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w() * m_dar, item.h()));
396 m_paramRect->setPos(item.x() * m_dar, item.y());
397 m_paramRect->setZValue(0);
398 m_paramRect->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
400 m_paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
401 m_scene->addItem(m_paramRect);
402 slotPositionChanged(0, false);
403 slotUpdateGeometry();
405 connect(spinPos, SIGNAL(valueChanged(int)), this , SLOT(slotPositionChanged(int)));
407 connect(spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
410 void Geometryval::updateTransitionPath()
412 if (m_fixedMode) return;
413 Mlt::GeometryItem item;
418 if (m_geom->next_key(&item, pos) == 1) break;
421 path.moveTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
423 path.lineTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
428 m_path->setPath(path);
431 void Geometryval::slotUpdateTransitionProperties()
433 int pos = spinPos->value();
434 Mlt::GeometryItem item;
435 int error = m_geom->next_key(&item, pos);
436 if (error || item.frame() != pos) {
437 // no keyframe under cursor
440 QRectF r = m_paramRect->rect().normalized();
441 QPointF rectpos = m_paramRect->pos();
442 item.x(rectpos.x() / m_dar);
444 item.w(r.width() / m_dar);
446 m_geom->insert(item);
447 updateTransitionPath();
448 emit parameterChanged();
451 void Geometryval::slotResetPosition()
456 if (m_frameSize.isNull()) {
457 spinWidth->setValue(m_realWidth);
458 spinHeight->setValue(m_profile.height);
460 spinWidth->setValue(m_frameSize.x());
461 spinHeight->setValue(m_frameSize.y());
465 void Geometryval::setFrameSize(QPoint p)
471 void Geometryval::slotKeyframeMoved(int pos)
473 slotPositionChanged(pos);
474 slotUpdateTransitionProperties();
477 void Geometryval::slotSwitchOptions()
479 if (frameOptions->isHidden()) {
480 frameOptions->setHidden(false);
481 m_editOptions->setChecked(true);
483 frameOptions->setHidden(true);
484 m_editOptions->setChecked(false);
489 void Geometryval::slotGeometryX(int value)
491 if (!keyframeSelected())
493 m_paramRect->setPos(value, spinY->value());
494 slotUpdateTransitionProperties();
497 void Geometryval::slotGeometryY(int value)
499 if (!keyframeSelected())
501 m_paramRect->setPos(spinX->value(), value);
502 slotUpdateTransitionProperties();
505 void Geometryval::slotGeometryWidth(int value)
507 if (!keyframeSelected())
509 m_paramRect->setRect(0, 0, value, spinHeight->value());
510 slotUpdateTransitionProperties();
513 void Geometryval::slotGeometryHeight(int value)
515 if (!keyframeSelected())
517 m_paramRect->setRect(0, 0, spinWidth->value(), value);
518 slotUpdateTransitionProperties();
521 void Geometryval::slotUpdateGeometry()
523 QRectF r = m_paramRect->rect().normalized();
525 spinX->blockSignals(true);
526 spinY->blockSignals(true);
527 spinWidth->blockSignals(true);
528 spinHeight->blockSignals(true);
529 spinResize->blockSignals(true);
531 spinX->setValue(m_paramRect->pos().x());
532 spinY->setValue(m_paramRect->pos().y());
533 spinWidth->setValue(r.width());
534 spinHeight->setValue(r.height());
535 spinResize->setValue(m_paramRect->rect().width() * 100 / m_realWidth);
537 spinX->blockSignals(false);
538 spinY->blockSignals(false);
539 spinWidth->blockSignals(false);
540 spinHeight->blockSignals(false);
541 spinResize->blockSignals(false);
544 bool Geometryval::keyframeSelected()
546 Mlt::GeometryItem item;
547 if (m_geom->fetch(&item, spinPos->value()) || item.key() == false) return false;