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, QWidget* parent) :
38 m_frameSize(frame_size)
41 QVBoxLayout* vbox = new QVBoxLayout(widget);
42 QGraphicsView *view = new QGraphicsView(this);
43 view->setBackgroundBrush(QBrush(Qt::black));
44 vbox->addWidget(view);
45 vbox->setContentsMargins(0, 0, 0, 0);
47 QVBoxLayout* vbox2 = new QVBoxLayout(keyframeWidget);
48 m_helper = new KeyframeHelper(this);
49 vbox2->addWidget(m_helper);
50 vbox2->setContentsMargins(0, 0, 0, 0);
52 connect(m_helper, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int)));
53 connect(m_helper, SIGNAL(keyframeMoved(int)), this, SLOT(slotKeyframeMoved(int)));
54 connect(m_helper, SIGNAL(addKeyframe(int)), this, SLOT(slotAddFrame(int)));
55 connect(m_helper, SIGNAL(removeKeyframe(int)), this, SLOT(slotDeleteFrame(int)));
57 m_scene = new GraphicsSceneRectMove(this);
58 m_scene->setTool(TITLE_SELECT);
59 view->setScene(m_scene);
60 m_dar = (m_profile.height * m_profile.display_aspect_num / (double) m_profile.display_aspect_den) / (double) m_profile.width;
62 m_realWidth = (int)(profile.height * profile.display_aspect_num / (double) profile.display_aspect_den);
63 QGraphicsRectItem *frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_realWidth, profile.height));
64 frameBorder->setZValue(-1100);
65 frameBorder->setBrush(QColor(255, 255, 0, 30));
66 frameBorder->setPen(QPen(QBrush(QColor(255, 255, 255, 255)), 1.0, Qt::DashLine));
67 m_scene->addItem(frameBorder);
69 buttonNext->setIcon(KIcon("media-skip-forward"));
70 buttonNext->setToolTip(i18n("Go to next keyframe"));
71 buttonPrevious->setIcon(KIcon("media-skip-backward"));
72 buttonPrevious->setToolTip(i18n("Go to previous keyframe"));
73 buttonAdd->setIcon(KIcon("document-new"));
74 buttonAdd->setToolTip(i18n("Add keyframe"));
75 buttonDelete->setIcon(KIcon("edit-delete"));
76 buttonDelete->setToolTip(i18n("Delete keyframe"));
78 m_configMenu = new QMenu(i18n("Misc..."), this);
79 buttonMenu->setIcon(KIcon("system-run"));
80 buttonMenu->setMenu(m_configMenu);
81 buttonMenu->setPopupMode(QToolButton::QToolButton::InstantPopup);
84 m_editGeom = m_configMenu->addAction(i18n("Edit keyframe"), this, SLOT(slotGeometry()));
86 m_scaleMenu = new QMenu(i18n("Resize..."), this);
87 m_configMenu->addMenu(m_scaleMenu);
88 m_scaleMenu->addAction(i18n("50%"), this, SLOT(slotResize50()));
89 m_scaleMenu->addAction(i18n("100%"), this, SLOT(slotResize100()));
90 m_scaleMenu->addAction(i18n("200%"), this, SLOT(slotResize200()));
91 m_scaleMenu->addAction(i18n("Original size"), this, SLOT(slotResizeOriginal()));
92 m_scaleMenu->addAction(i18n("Custom"), this, SLOT(slotResizeCustom()));
94 m_alignMenu = new QMenu(i18n("Align..."), this);
95 m_configMenu->addMenu(m_alignMenu);
96 m_alignMenu->addAction(i18n("Center"), this, SLOT(slotAlignCenter()));
97 m_alignMenu->addAction(i18n("Hor. Center"), this, SLOT(slotAlignHCenter()));
98 m_alignMenu->addAction(i18n("Vert. Center"), this, SLOT(slotAlignVCenter()));
99 m_alignMenu->addAction(i18n("Right"), this, SLOT(slotAlignRight()));
100 m_alignMenu->addAction(i18n("Left"), this, SLOT(slotAlignLeft()));
101 m_alignMenu->addAction(i18n("Top"), this, SLOT(slotAlignTop()));
102 m_alignMenu->addAction(i18n("Bottom"), this, SLOT(slotAlignBottom()));
105 m_syncAction = m_configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
106 m_syncAction->setCheckable(true);
107 m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
109 //scene->setSceneRect(0, 0, profile.width * 2, profile.height * 2);
110 //view->fitInView(m_frameBorder, Qt::KeepAspectRatio);
111 const double sc = 100.0 / profile.height * 0.8;
112 QRectF srect = view->sceneRect();
113 view->setSceneRect(srect.x(), -srect.height() / 3 + 10, srect.width(), srect.height() + srect.height() / 3 * 2 - 10);
114 m_scene->setZoom(sc);
115 view->centerOn(frameBorder);
116 connect(buttonNext , SIGNAL(clicked()) , this , SLOT(slotNextFrame()));
117 connect(buttonPrevious , SIGNAL(clicked()) , this , SLOT(slotPreviousFrame()));
118 connect(buttonDelete , SIGNAL(clicked()) , this , SLOT(slotDeleteFrame()));
119 connect(buttonAdd , SIGNAL(clicked()) , this , SLOT(slotAddFrame()));
120 connect(m_scene, SIGNAL(actionFinished()), this, SLOT(slotUpdateTransitionProperties()));
121 connect(m_scene, SIGNAL(doubleClickEvent()), this, SLOT(slotGeometry()));
126 Geometryval::~Geometryval()
128 m_scene->disconnect();
142 void Geometryval::slotAlignCenter()
144 int pos = spinPos->value();
145 Mlt::GeometryItem item;
146 int error = m_geom->fetch(&item, pos);
147 if (error || item.key() == false) {
148 // no keyframe under cursor
151 m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, (m_profile.height - m_paramRect->rect().height()) / 2);
152 slotUpdateTransitionProperties();
155 void Geometryval::slotAlignHCenter()
157 int pos = spinPos->value();
158 Mlt::GeometryItem item;
159 int error = m_geom->fetch(&item, pos);
160 if (error || item.key() == false) {
161 // no keyframe under cursor
164 m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, m_paramRect->pos().y());
165 slotUpdateTransitionProperties();
168 void Geometryval::slotAlignVCenter()
170 int pos = spinPos->value();
171 Mlt::GeometryItem item;
172 int error = m_geom->fetch(&item, pos);
173 if (error || item.key() == false) {
174 // no keyframe under cursor
177 m_paramRect->setPos(m_paramRect->pos().x(), (m_profile.height - m_paramRect->rect().height()) / 2);
178 slotUpdateTransitionProperties();
181 void Geometryval::slotAlignTop()
183 int pos = spinPos->value();
184 Mlt::GeometryItem item;
185 int error = m_geom->fetch(&item, pos);
186 if (error || item.key() == false) {
187 // no keyframe under cursor
190 m_paramRect->setPos(m_paramRect->pos().x(), 0);
191 slotUpdateTransitionProperties();
194 void Geometryval::slotAlignBottom()
196 int pos = spinPos->value();
197 Mlt::GeometryItem item;
198 int error = m_geom->fetch(&item, pos);
199 if (error || item.key() == false) {
200 // no keyframe under cursor
203 m_paramRect->setPos(m_paramRect->pos().x(), m_profile.height - m_paramRect->rect().height());
204 slotUpdateTransitionProperties();
207 void Geometryval::slotAlignLeft()
209 int pos = spinPos->value();
210 Mlt::GeometryItem item;
211 int error = m_geom->fetch(&item, pos);
212 if (error || item.key() == false) {
213 // no keyframe under cursor
216 m_paramRect->setPos(0, m_paramRect->pos().y());
217 slotUpdateTransitionProperties();
220 void Geometryval::slotAlignRight()
222 int pos = spinPos->value();
223 Mlt::GeometryItem item;
224 int error = m_geom->fetch(&item, pos);
225 if (error || item.key() == false) {
226 // no keyframe under cursor
229 m_paramRect->setPos(m_realWidth - m_paramRect->rect().width(), m_paramRect->pos().y());
230 slotUpdateTransitionProperties();
233 void Geometryval::slotResize50()
235 int pos = spinPos->value();
236 Mlt::GeometryItem item;
237 int error = m_geom->fetch(&item, pos);
238 if (error || item.key() == false) {
239 // no keyframe under cursor
242 m_paramRect->setRect(0, 0, m_realWidth / 2, m_profile.height / 2);
243 slotUpdateTransitionProperties();
246 void Geometryval::slotResize100()
248 int pos = spinPos->value();
249 Mlt::GeometryItem item;
250 int error = m_geom->fetch(&item, pos);
251 if (error || item.key() == false) {
252 // no keyframe under cursor
255 m_paramRect->setRect(0, 0, m_realWidth, m_profile.height);
256 slotUpdateTransitionProperties();
259 void Geometryval::slotResize200()
261 int pos = spinPos->value();
262 Mlt::GeometryItem item;
263 int error = m_geom->fetch(&item, pos);
264 if (error || item.key() == false) {
265 // no keyframe under cursor
268 m_paramRect->setRect(0, 0, m_realWidth * 2, m_profile.height * 2);
269 slotUpdateTransitionProperties();
272 void Geometryval::slotResizeOriginal()
274 if (m_frameSize.isNull()) slotResize100();
275 int pos = spinPos->value();
276 Mlt::GeometryItem item;
277 int error = m_geom->fetch(&item, pos);
278 if (error || item.key() == false) {
279 // no keyframe under cursor
282 m_paramRect->setRect(0, 0, m_frameSize.x(), m_frameSize.y());
283 slotUpdateTransitionProperties();
286 void Geometryval::slotResizeCustom()
288 int pos = spinPos->value();
289 Mlt::GeometryItem item;
290 int error = m_geom->fetch(&item, pos);
291 if (error || item.key() == false) {
292 // no keyframe under cursor
295 int scale = m_paramRect->rect().width() * 100 / m_realWidth;
297 scale = QInputDialog::getInteger(this, i18n("Resize..."), i18n("Scale"), scale, 1, 2147483647, 10, &ok);
299 m_paramRect->setRect(0, 0, m_realWidth * scale / 100, m_profile.height * scale / 100);
300 slotUpdateTransitionProperties();
303 void Geometryval::slotTransparencyChanged(int transp)
305 int pos = spinPos->value();
306 Mlt::GeometryItem item;
307 int error = m_geom->fetch(&item, pos);
308 if (error || item.key() == false) {
309 // no keyframe under cursor
313 m_paramRect->setBrush(QColor(255, 0, 0, transp));
314 m_geom->insert(item);
315 emit parameterChanged();
318 void Geometryval::slotSyncCursor()
320 KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
323 void Geometryval::slotPositionChanged(int pos, bool seek)
325 if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos);
326 spinPos->setValue(pos);
327 m_helper->setValue(pos);
328 Mlt::GeometryItem item;
329 int error = m_geom->fetch(&item, pos);
330 if (error || item.key() == false) {
331 // no keyframe under cursor, adjust buttons
332 buttonAdd->setEnabled(true);
333 buttonDelete->setEnabled(false);
334 widget->setEnabled(false);
335 spinTransp->setEnabled(false);
336 m_scaleMenu->setEnabled(false);
337 m_alignMenu->setEnabled(false);
338 m_editGeom->setEnabled(false);
340 buttonAdd->setEnabled(false);
341 buttonDelete->setEnabled(true);
342 widget->setEnabled(true);
343 spinTransp->setEnabled(true);
344 m_scaleMenu->setEnabled(true);
345 m_alignMenu->setEnabled(true);
346 m_editGeom->setEnabled(true);
349 m_paramRect->setPos(item.x() * m_dar, item.y());
350 m_paramRect->setRect(0, 0, item.w() * m_dar, item.h());
351 spinTransp->setValue(item.mix());
352 m_paramRect->setBrush(QColor(255, 0, 0, item.mix()));
355 void Geometryval::slotDeleteFrame(int pos)
357 // check there is more than one keyframe
358 Mlt::GeometryItem item;
359 if (pos == -1) pos = spinPos->value();
360 int error = m_geom->next_key(&item, pos + 1);
362 error = m_geom->prev_key(&item, pos - 1);
363 if (error || item.frame() == pos) return;
366 m_geom->remove(spinPos->value());
367 buttonAdd->setEnabled(true);
368 buttonDelete->setEnabled(false);
369 widget->setEnabled(false);
370 spinTransp->setEnabled(false);
371 m_scaleMenu->setEnabled(false);
372 m_alignMenu->setEnabled(false);
373 m_editGeom->setEnabled(false);
375 slotPositionChanged(pos, false);
376 updateTransitionPath();
377 emit parameterChanged();
380 void Geometryval::slotAddFrame(int pos)
382 if (pos == -1) pos = spinPos->value();
383 Mlt::GeometryItem item;
385 QRectF r = m_paramRect->sceneBoundingRect().normalized();
386 item.x(r.left() / m_dar);
388 item.w(r.width() / m_dar);
390 item.mix(spinTransp->value());
391 m_geom->insert(item);
392 buttonAdd->setEnabled(false);
393 buttonDelete->setEnabled(true);
394 widget->setEnabled(true);
395 spinTransp->setEnabled(true);
396 m_scaleMenu->setEnabled(true);
397 m_alignMenu->setEnabled(true);
398 m_editGeom->setEnabled(true);
400 emit parameterChanged();
403 void Geometryval::slotNextFrame()
405 Mlt::GeometryItem item;
406 int error = m_geom->next_key(&item, m_helper->value() + 1);
407 kDebug() << "// SEEK TO NEXT KFR: " << error;
410 spinPos->setValue(spinPos->maximum());
413 int pos = item.frame();
414 spinPos->setValue(pos);
417 void Geometryval::slotPreviousFrame()
419 Mlt::GeometryItem item;
420 int error = m_geom->prev_key(&item, m_helper->value() - 1);
421 kDebug() << "// SEEK TO NEXT KFR: " << error;
423 int pos = item.frame();
424 spinPos->setValue(pos);
428 QString Geometryval::getValue() const
430 return m_geom->serialise();
433 void Geometryval::setupParam(const QDomElement par, int minFrame, int maxFrame)
435 QString val = par.attribute("value");
436 if (par.attribute("fixed") == "1") {
438 buttonPrevious->setHidden(true);
439 buttonNext->setHidden(true);
440 buttonDelete->setHidden(true);
441 buttonAdd->setHidden(true);
442 spinTransp->setMaximum(500);
443 label_pos->setHidden(true);
444 m_helper->setHidden(true);
445 spinPos->setHidden(true);
448 char *tmp = (char *) qstrdup(val.toUtf8().data());
449 if (m_geom) m_geom->parse(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
450 else m_geom = new Mlt::Geometry(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
453 //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
454 //read param her and set rect
456 m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
459 doc.appendChild(doc.importNode(par, true));
460 kDebug() << "IMPORTED TRANS: " << doc.toString();*/
461 spinPos->setMaximum(maxFrame - minFrame - 1);
462 if (m_path == NULL) {
463 m_path = new QGraphicsPathItem();
464 m_path->setPen(QPen(Qt::red));
465 m_scene->addItem(m_path);
467 updateTransitionPath();
469 Mlt::GeometryItem item;
471 m_geom->fetch(&item, 0);
473 m_paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w() * m_dar, item.h()));
474 m_paramRect->setPos(item.x() * m_dar, item.y());
475 m_paramRect->setZValue(0);
476 m_paramRect->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
478 m_paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
479 m_scene->addItem(m_paramRect);
480 slotPositionChanged(0, false);
482 connect(spinPos, SIGNAL(valueChanged(int)), this , SLOT(slotPositionChanged(int)));
484 connect(spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
487 void Geometryval::updateTransitionPath()
489 if (m_fixedMode) return;
490 Mlt::GeometryItem item;
495 if (m_geom->next_key(&item, pos) == 1) break;
498 path.moveTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
500 path.lineTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
505 m_path->setPath(path);
508 void Geometryval::slotUpdateTransitionProperties()
510 int pos = spinPos->value();
511 Mlt::GeometryItem item;
512 int error = m_geom->next_key(&item, pos);
513 if (error || item.frame() != pos) {
514 // no keyframe under cursor
517 QRectF r = m_paramRect->sceneBoundingRect().normalized();
518 item.x(r.left() / m_dar);
520 item.w(r.width() / m_dar);
522 m_geom->insert(item);
523 updateTransitionPath();
524 emit parameterChanged();
527 void Geometryval::slotGeometry()
529 int pos = spinPos->value();
530 Mlt::GeometryItem item;
531 int error = m_geom->fetch(&item, pos);
532 if (error || item.key() == false) {
533 // no keyframe under cursor
536 QRectF r = m_paramRect->sceneBoundingRect().normalized();
540 d.setWindowTitle(i18n("Frame Geometry"));
541 m_view.value_x->setMaximum(10000);
542 m_view.value_x->setMinimum(-10000);
543 m_view.value_y->setMaximum(10000);
544 m_view.value_y->setMinimum(-10000);
545 m_view.value_width->setMaximum(500000);
546 m_view.value_width->setMinimum(1);
547 m_view.value_height->setMaximum(500000);
548 m_view.value_height->setMinimum(1);
550 m_view.value_x->setValue(m_paramRect->pos().x());
551 m_view.value_y->setValue(m_paramRect->pos().y());
552 m_view.value_width->setValue(r.width());
553 m_view.value_height->setValue(r.height());
554 connect(m_view.button_reset , SIGNAL(clicked()) , this , SLOT(slotResetPosition()));
556 if (d.exec() == QDialog::Accepted) {
557 m_paramRect->setPos(m_view.value_x->value(), m_view.value_y->value());
558 m_paramRect->setRect(0, 0, m_view.value_width->value(), m_view.value_height->value());
559 slotUpdateTransitionProperties();
563 void Geometryval::slotResetPosition()
565 m_view.value_x->setValue(0);
566 m_view.value_y->setValue(0);
568 if (m_frameSize.isNull()) {
569 m_view.value_width->setValue(m_realWidth);
570 m_view.value_height->setValue(m_profile.height);
572 m_view.value_width->setValue(m_frameSize.x());
573 m_view.value_height->setValue(m_frameSize.y());
577 void Geometryval::setFrameSize(QPoint p)
583 void Geometryval::slotKeyframeMoved(int pos)
585 slotPositionChanged(pos);
586 slotUpdateTransitionProperties();