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, Timecode t, QPoint frame_size, int startPoint, QWidget* parent) :
39 m_frameSize(frame_size),
40 m_startPoint(startPoint),
44 m_timePos.setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
45 toolbarlayout->addWidget(&m_timePos);
46 toolbarlayout->insertStretch(-1);
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);
54 QVBoxLayout* vbox2 = new QVBoxLayout(keyframeWidget);
55 m_helper = new KeyframeHelper(this);
56 vbox2->addWidget(m_helper);
57 vbox2->setContentsMargins(0, 0, 0, 0);
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)));
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;
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);
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"));
85 m_configMenu = new QMenu(i18n("Misc..."), this);
86 buttonMenu->setMenu(m_configMenu);
87 buttonMenu->setPopupMode(QToolButton::MenuButtonPopup);
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()));
95 m_reset = m_configMenu->addAction(KIcon("view-refresh"), i18n("Reset"), this, SLOT(slotResetPosition()));
97 m_syncAction = m_configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
98 m_syncAction->setCheckable(true);
99 m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
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()));
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"));
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()));
142 connect(this, SIGNAL(parameterChanged()), this, SLOT(slotUpdateGeometry()));
146 Geometryval::~Geometryval()
148 m_scene->disconnect();
160 void Geometryval::slotAlignHCenter()
162 if (!keyframeSelected())
164 m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, m_paramRect->pos().y());
165 slotUpdateTransitionProperties();
168 void Geometryval::slotAlignVCenter()
170 if (!keyframeSelected())
172 m_paramRect->setPos(m_paramRect->pos().x(), (m_profile.height - m_paramRect->rect().height()) / 2);
173 slotUpdateTransitionProperties();
176 void Geometryval::slotAlignTop()
178 if (!keyframeSelected())
180 m_paramRect->setPos(m_paramRect->pos().x(), 0);
181 slotUpdateTransitionProperties();
184 void Geometryval::slotAlignBottom()
186 if (!keyframeSelected())
188 m_paramRect->setPos(m_paramRect->pos().x(), m_profile.height - m_paramRect->rect().height());
189 slotUpdateTransitionProperties();
192 void Geometryval::slotAlignLeft()
194 if (!keyframeSelected())
196 m_paramRect->setPos(0, m_paramRect->pos().y());
197 slotUpdateTransitionProperties();
200 void Geometryval::slotAlignRight()
202 if (!keyframeSelected())
204 m_paramRect->setPos(m_realWidth - m_paramRect->rect().width(), m_paramRect->pos().y());
205 slotUpdateTransitionProperties();
208 void Geometryval::slotResizeOriginal()
210 if (!keyframeSelected())
212 if (m_frameSize.isNull())
213 m_paramRect->setRect(0, 0, m_realWidth, m_profile.height);
215 m_paramRect->setRect(0, 0, m_frameSize.x(), m_frameSize.y());
216 slotUpdateTransitionProperties();
219 void Geometryval::slotResizeCustom()
221 if (!keyframeSelected())
223 int value = spinResize->value();
224 m_paramRect->setRect(0, 0, m_realWidth * value / 100, m_profile.height * value / 100);
225 slotUpdateTransitionProperties();
228 void Geometryval::slotTransparencyChanged(int transp)
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
238 m_paramRect->setBrush(QColor(255, 0, 0, transp));
239 m_geom->insert(item);
240 emit parameterChanged();
243 void Geometryval::slotSyncCursor()
245 KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
248 void Geometryval::updateTimecodeFormat()
250 m_timePos.slotPrepareTimeCodeFormat(m_timecode);
253 void Geometryval::slotPositionChanged(int pos, bool seek)
256 pos = m_timePos.value();
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);
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);
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();
288 void Geometryval::slotDeleteFrame(int pos)
290 // check there is more than one keyframe
291 Mlt::GeometryItem item;
292 int frame = m_timePos.value();
294 if (pos == -1) pos = frame;
295 int error = m_geom->next_key(&item, pos + 1);
297 error = m_geom->prev_key(&item, pos - 1);
298 if (error || item.frame() == pos) return;
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);
309 slotPositionChanged(pos, false);
310 updateTransitionPath();
311 emit parameterChanged();
314 void Geometryval::slotAddFrame(int pos)
316 int frame = m_timePos.value();
317 if (pos == -1) pos = frame;
318 Mlt::GeometryItem item;
320 QRectF r = m_paramRect->rect().normalized();
321 QPointF rectpos = m_paramRect->pos();
322 item.x(rectpos.x() / m_dar);
324 item.w(r.width() / m_dar);
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);
335 emit parameterChanged();
338 void Geometryval::slotNextFrame()
340 Mlt::GeometryItem item;
341 int error = m_geom->next_key(&item, m_helper->value() + 1);
343 kDebug() << "// SEEK TO NEXT KFR: " << error;
346 pos = m_helper->frameLength;
347 } else pos = item.frame();
348 m_timePos.setValue(pos);
349 slotPositionChanged();
352 void Geometryval::slotPreviousFrame()
354 Mlt::GeometryItem item;
355 int error = m_geom->prev_key(&item, m_helper->value() - 1);
356 kDebug() << "// SEEK TO NEXT KFR: " << error;
358 int pos = item.frame();
359 m_timePos.setValue(pos);
360 slotPositionChanged();
364 QString Geometryval::getValue() const
366 return m_geom->serialise();
369 void Geometryval::setupParam(const QDomElement par, int minFrame, int maxFrame)
371 QString val = par.attribute("value");
372 if (par.attribute("fixed") == "1") {
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);
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);
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);
394 //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
395 //read param her and set rect
397 m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
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);
407 updateTransitionPath();
409 Mlt::GeometryItem item;
411 m_geom->fetch(&item, 0);
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);
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();
423 m_timePos.setMaximum(maxFrame - minFrame - 1);
424 connect(&m_timePos, SIGNAL(editingFinished()), this , SLOT(slotPositionChanged()));
426 connect(spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
429 void Geometryval::updateTransitionPath()
431 if (m_fixedMode) return;
432 Mlt::GeometryItem item;
437 if (m_geom->next_key(&item, pos) == 1) break;
440 path.moveTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
442 path.lineTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
447 m_path->setPath(path);
450 void Geometryval::slotUpdateTransitionProperties()
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
459 QRectF r = m_paramRect->rect().normalized();
460 QPointF rectpos = m_paramRect->pos();
461 item.x(rectpos.x() / m_dar);
463 item.w(r.width() / m_dar);
465 m_geom->insert(item);
466 updateTransitionPath();
467 emit parameterChanged();
470 void Geometryval::slotResetPosition()
475 if (m_frameSize.isNull()) {
476 spinWidth->setValue(m_realWidth);
477 spinHeight->setValue(m_profile.height);
479 spinWidth->setValue(m_frameSize.x());
480 spinHeight->setValue(m_frameSize.y());
484 void Geometryval::setFrameSize(QPoint p)
490 void Geometryval::slotKeyframeMoved(int pos)
492 slotPositionChanged(pos);
493 slotUpdateTransitionProperties();
496 void Geometryval::slotSwitchOptions()
498 if (frameOptions->isHidden()) {
499 frameOptions->setHidden(false);
500 m_editOptions->setChecked(true);
502 frameOptions->setHidden(true);
503 m_editOptions->setChecked(false);
508 void Geometryval::slotGeometryX(int value)
510 if (!keyframeSelected())
512 m_paramRect->setPos(value, spinY->value());
513 slotUpdateTransitionProperties();
516 void Geometryval::slotGeometryY(int value)
518 if (!keyframeSelected())
520 m_paramRect->setPos(spinX->value(), value);
521 slotUpdateTransitionProperties();
524 void Geometryval::slotGeometryWidth(int value)
526 if (!keyframeSelected())
528 m_paramRect->setRect(0, 0, value, spinHeight->value());
529 slotUpdateTransitionProperties();
532 void Geometryval::slotGeometryHeight(int value)
534 if (!keyframeSelected())
536 m_paramRect->setRect(0, 0, spinWidth->value(), value);
537 slotUpdateTransitionProperties();
540 void Geometryval::slotUpdateGeometry()
542 QRectF r = m_paramRect->rect().normalized();
544 spinX->blockSignals(true);
545 spinY->blockSignals(true);
546 spinWidth->blockSignals(true);
547 spinHeight->blockSignals(true);
548 spinResize->blockSignals(true);
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);
556 spinX->blockSignals(false);
557 spinY->blockSignals(false);
558 spinWidth->blockSignals(false);
559 spinHeight->blockSignals(false);
560 spinResize->blockSignals(false);
563 bool Geometryval::keyframeSelected()
565 Mlt::GeometryItem item;
566 int pos = m_timePos.value();
567 if (m_geom->fetch(&item, pos) || item.key() == false) return false;