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)
43 QVBoxLayout* vbox = new QVBoxLayout(widget);
44 m_sceneview = new QGraphicsView(this);
45 m_sceneview->setBackgroundBrush(QBrush(Qt::black));
46 vbox->addWidget(m_sceneview);
47 vbox->setContentsMargins(0, 0, 0, 0);
49 QVBoxLayout* vbox2 = new QVBoxLayout(keyframeWidget);
50 m_helper = new KeyframeHelper(this);
51 vbox2->addWidget(m_helper);
52 vbox2->setContentsMargins(0, 0, 0, 0);
54 connect(m_helper, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int)));
55 connect(m_helper, SIGNAL(keyframeMoved(int)), this, SLOT(slotKeyframeMoved(int)));
56 connect(m_helper, SIGNAL(addKeyframe(int)), this, SLOT(slotAddFrame(int)));
57 connect(m_helper, SIGNAL(removeKeyframe(int)), this, SLOT(slotDeleteFrame(int)));
59 m_scene = new GraphicsSceneRectMove(this);
60 m_scene->setTool(TITLE_SELECT);
61 m_sceneview->setScene(m_scene);
62 m_dar = (m_profile.height * m_profile.display_aspect_num / (double) m_profile.display_aspect_den) / (double) m_profile.width;
64 m_realWidth = (int)(profile.height * profile.display_aspect_num / (double) profile.display_aspect_den);
65 QGraphicsRectItem *frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_realWidth, profile.height));
66 frameBorder->setZValue(-1100);
67 frameBorder->setBrush(QColor(255, 255, 0, 30));
68 frameBorder->setPen(QPen(QBrush(QColor(255, 255, 255, 255)), 1.0, Qt::DashLine));
69 m_scene->addItem(frameBorder);
71 buttonNext->setIcon(KIcon("media-skip-forward"));
72 buttonNext->setToolTip(i18n("Go to next keyframe"));
73 buttonPrevious->setIcon(KIcon("media-skip-backward"));
74 buttonPrevious->setToolTip(i18n("Go to previous keyframe"));
75 buttonAdd->setIcon(KIcon("document-new"));
76 buttonAdd->setToolTip(i18n("Add keyframe"));
77 buttonDelete->setIcon(KIcon("edit-delete"));
78 buttonDelete->setToolTip(i18n("Delete keyframe"));
80 m_configMenu = new QMenu(i18n("Misc..."), this);
81 buttonMenu->setMenu(m_configMenu);
82 buttonMenu->setPopupMode(QToolButton::MenuButtonPopup);
84 m_editOptions = m_configMenu->addAction(KIcon("system-run"), i18n("Show/Hide options"));
85 m_editOptions->setCheckable(true);
86 buttonMenu->setDefaultAction(m_editOptions);
87 connect(m_editOptions, SIGNAL(triggered()), this, SLOT(slotSwitchOptions()));
90 m_reset = m_configMenu->addAction(KIcon("view-refresh"), i18n("Reset"), this, SLOT(slotResetPosition()));
92 m_syncAction = m_configMenu->addAction(i18n("Sync timeline cursor"), this, SLOT(slotSyncCursor()));
93 m_syncAction->setCheckable(true);
94 m_syncAction->setChecked(KdenliveSettings::transitionfollowcursor());
96 //scene->setSceneRect(0, 0, profile.width * 2, profile.height * 2);
97 //view->fitInView(m_frameBorder, Qt::KeepAspectRatio);
98 const double sc = 100.0 / profile.height * 0.8;
99 QRectF srect = m_sceneview->sceneRect();
100 m_sceneview->setSceneRect(srect.x(), -srect.height() / 3 + 10, srect.width(), srect.height() + srect.height() / 3 * 2 - 10);
101 m_scene->setZoom(sc);
102 m_sceneview->centerOn(frameBorder);
103 m_sceneview->setMouseTracking(true);
104 connect(buttonNext , SIGNAL(clicked()) , this , SLOT(slotNextFrame()));
105 connect(buttonPrevious , SIGNAL(clicked()) , this , SLOT(slotPreviousFrame()));
106 connect(buttonDelete , SIGNAL(clicked()) , this , SLOT(slotDeleteFrame()));
107 connect(buttonAdd , SIGNAL(clicked()) , this , SLOT(slotAddFrame()));
108 connect(m_scene, SIGNAL(actionFinished()), this, SLOT(slotUpdateTransitionProperties()));
109 connect(m_scene, SIGNAL(doubleClickEvent()), this, SLOT(slotGeometry()));
110 connect(pos_up, SIGNAL(clicked()), this, SLOT(slotPosUp()));
111 connect(pos_down, SIGNAL(clicked()), this, SLOT(slotPosDown()));
113 buttonhcenter->setIcon(KIcon("kdenlive-align-hor"));
114 buttonhcenter->setToolTip(i18n("Align item horizontally"));
115 buttonvcenter->setIcon(KIcon("kdenlive-align-vert"));
116 buttonvcenter->setToolTip(i18n("Align item vertically"));
117 buttontop->setIcon(KIcon("kdenlive-align-top"));
118 buttontop->setToolTip(i18n("Align item to top"));
119 buttonbottom->setIcon(KIcon("kdenlive-align-bottom"));
120 buttonbottom->setToolTip(i18n("Align item to bottom"));
121 buttonright->setIcon(KIcon("kdenlive-align-right"));
122 buttonright->setToolTip(i18n("Align item to right"));
123 buttonleft->setIcon(KIcon("kdenlive-align-left"));
124 buttonleft->setToolTip(i18n("Align item to left"));
126 connect(buttonhcenter, SIGNAL(clicked()), this, SLOT(slotAlignHCenter()));
127 connect(buttonvcenter, SIGNAL(clicked()), this, SLOT(slotAlignVCenter()));
128 connect(buttontop, SIGNAL(clicked()), this, SLOT(slotAlignTop()));
129 connect(buttonbottom, SIGNAL(clicked()), this, SLOT(slotAlignBottom()));
130 connect(buttonright, SIGNAL(clicked()), this, SLOT(slotAlignRight()));
131 connect(buttonleft, SIGNAL(clicked()), this, SLOT(slotAlignLeft()));
132 connect(spinX, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryX(int)));
133 connect(spinY, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryY(int)));
134 connect(spinWidth, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryWidth(int)));
135 connect(spinHeight, SIGNAL(valueChanged(int)), this, SLOT(slotGeometryHeight(int)));
136 connect(spinResize, SIGNAL(editingFinished()), this, SLOT(slotResizeCustom()));
137 connect(buttonResize, SIGNAL(clicked()), this, SLOT(slotResizeOriginal()));
139 connect(this, SIGNAL(parameterChanged()), this, SLOT(slotUpdateGeometry()));
143 Geometryval::~Geometryval()
145 m_scene->disconnect();
157 void Geometryval::slotAlignHCenter()
159 if (!keyframeSelected())
161 m_paramRect->setPos((m_realWidth - m_paramRect->rect().width()) / 2, m_paramRect->pos().y());
162 slotUpdateTransitionProperties();
165 void Geometryval::slotAlignVCenter()
167 if (!keyframeSelected())
169 m_paramRect->setPos(m_paramRect->pos().x(), (m_profile.height - m_paramRect->rect().height()) / 2);
170 slotUpdateTransitionProperties();
173 void Geometryval::slotAlignTop()
175 if (!keyframeSelected())
177 m_paramRect->setPos(m_paramRect->pos().x(), 0);
178 slotUpdateTransitionProperties();
181 void Geometryval::slotAlignBottom()
183 if (!keyframeSelected())
185 m_paramRect->setPos(m_paramRect->pos().x(), m_profile.height - m_paramRect->rect().height());
186 slotUpdateTransitionProperties();
189 void Geometryval::slotAlignLeft()
191 if (!keyframeSelected())
193 m_paramRect->setPos(0, m_paramRect->pos().y());
194 slotUpdateTransitionProperties();
197 void Geometryval::slotAlignRight()
199 if (!keyframeSelected())
201 m_paramRect->setPos(m_realWidth - m_paramRect->rect().width(), m_paramRect->pos().y());
202 slotUpdateTransitionProperties();
205 void Geometryval::slotResizeOriginal()
207 if (!keyframeSelected())
209 if (m_frameSize.isNull())
210 m_paramRect->setRect(0, 0, m_realWidth, m_profile.height);
212 m_paramRect->setRect(0, 0, m_frameSize.x(), m_frameSize.y());
213 slotUpdateTransitionProperties();
216 void Geometryval::slotResizeCustom()
218 if (!keyframeSelected())
220 int value = spinResize->value();
221 m_paramRect->setRect(0, 0, m_realWidth * value / 100, m_profile.height * value / 100);
222 slotUpdateTransitionProperties();
225 void Geometryval::slotTransparencyChanged(int transp)
228 if (KdenliveSettings::frametimecode()) pos = timePos->text().toInt();
229 else pos = m_timecode.getFrameCount(timePos->text());
230 Mlt::GeometryItem item;
231 int error = m_geom->fetch(&item, pos);
232 if (error || item.key() == false) {
233 // no keyframe under cursor
237 m_paramRect->setBrush(QColor(255, 0, 0, transp));
238 m_geom->insert(item);
239 emit parameterChanged();
242 void Geometryval::slotSyncCursor()
244 KdenliveSettings::setTransitionfollowcursor(m_syncAction->isChecked());
247 void Geometryval::updateTimecodeFormat()
249 timePos->setInputMask("");
250 if (KdenliveSettings::frametimecode()) {
251 int frames = m_timecode.getFrameCount(timePos->text());
252 QIntValidator *valid = new QIntValidator(this);
254 timePos->setValidator(valid);
255 timePos->setText(QString::number(frames));
257 int pos = timePos->text().toInt();
258 timePos->setValidator(m_timecode.validator());
259 timePos->setText(m_timecode.getTimecodeFromFrames(pos));
263 void Geometryval::slotPosUp()
266 if (KdenliveSettings::frametimecode()) pos = timePos->text().toInt();
267 else pos = m_timecode.getFrameCount(timePos->text());
269 if (pos > m_helper->frameLength) pos = m_helper->frameLength;
270 if (KdenliveSettings::frametimecode()) {
271 timePos->setText(QString::number(pos));
272 } else timePos->setText(m_timecode.getTimecodeFromFrames(pos));
273 slotPositionChanged();
276 void Geometryval::slotPosDown()
279 if (KdenliveSettings::frametimecode()) pos = timePos->text().toInt();
280 else pos = m_timecode.getFrameCount(timePos->text());
282 if (pos < 0) pos = 0;
283 if (KdenliveSettings::frametimecode()) {
284 timePos->setText(QString::number(pos));
285 } else timePos->setText(m_timecode.getTimecodeFromFrames(pos));
286 slotPositionChanged();
289 void Geometryval::slotPositionChanged(int pos, bool seek)
292 if (KdenliveSettings::frametimecode()) pos = timePos->text().toInt();
293 else pos = m_timecode.getFrameCount(timePos->text());
295 if (seek && KdenliveSettings::transitionfollowcursor()) emit seekToPos(pos + m_startPoint);
296 if (KdenliveSettings::frametimecode()) {
297 timePos->setText(QString::number(pos));
298 } else timePos->setText(m_timecode.getTimecodeFromFrames(pos));
300 //spinPos->setValue(pos);
301 m_helper->setValue(pos);
302 Mlt::GeometryItem item;
303 int error = m_geom->fetch(&item, pos);
304 if (error || item.key() == false) {
305 // no keyframe under cursor, adjust buttons
306 buttonAdd->setEnabled(true);
307 buttonDelete->setEnabled(false);
308 widget->setEnabled(false);
309 spinTransp->setEnabled(false);
310 frameOptions->setEnabled(false);
311 m_reset->setEnabled(false);
313 buttonAdd->setEnabled(false);
314 buttonDelete->setEnabled(true);
315 widget->setEnabled(true);
316 spinTransp->setEnabled(true);
317 frameOptions->setEnabled(true);
318 m_reset->setEnabled(true);
321 m_paramRect->setPos(item.x() * m_dar, item.y());
322 m_paramRect->setRect(0, 0, item.w() * m_dar, item.h());
323 spinTransp->setValue(item.mix());
324 m_paramRect->setBrush(QColor(255, 0, 0, item.mix()));
325 slotUpdateGeometry();
328 void Geometryval::slotDeleteFrame(int pos)
330 // check there is more than one keyframe
331 Mlt::GeometryItem item;
333 if (KdenliveSettings::frametimecode()) frame = timePos->text().toInt();
334 else frame = m_timecode.getFrameCount(timePos->text());
336 if (pos == -1) pos = frame;
337 int error = m_geom->next_key(&item, pos + 1);
339 error = m_geom->prev_key(&item, pos - 1);
340 if (error || item.frame() == pos) return;
343 m_geom->remove(frame);
344 buttonAdd->setEnabled(true);
345 buttonDelete->setEnabled(false);
346 widget->setEnabled(false);
347 spinTransp->setEnabled(false);
348 frameOptions->setEnabled(false);
349 m_reset->setEnabled(false);
351 slotPositionChanged(pos, false);
352 updateTransitionPath();
353 emit parameterChanged();
356 void Geometryval::slotAddFrame(int pos)
359 if (KdenliveSettings::frametimecode()) frame = timePos->text().toInt();
360 else frame = m_timecode.getFrameCount(timePos->text());
361 if (pos == -1) pos = frame;
362 Mlt::GeometryItem item;
364 QRectF r = m_paramRect->rect().normalized();
365 QPointF rectpos = m_paramRect->pos();
366 item.x(rectpos.x() / m_dar);
368 item.w(r.width() / m_dar);
370 item.mix(spinTransp->value());
371 m_geom->insert(item);
372 buttonAdd->setEnabled(false);
373 buttonDelete->setEnabled(true);
374 widget->setEnabled(true);
375 spinTransp->setEnabled(true);
376 frameOptions->setEnabled(true);
377 m_reset->setEnabled(true);
379 emit parameterChanged();
382 void Geometryval::slotNextFrame()
384 Mlt::GeometryItem item;
385 int error = m_geom->next_key(&item, m_helper->value() + 1);
387 kDebug() << "// SEEK TO NEXT KFR: " << error;
390 pos = m_helper->frameLength;
391 } else pos = item.frame();
393 if (KdenliveSettings::frametimecode()) {
394 timePos->setText(QString::number(pos));
395 } else timePos->setText(m_timecode.getTimecodeFromFrames(pos));
396 slotPositionChanged();
399 void Geometryval::slotPreviousFrame()
401 Mlt::GeometryItem item;
402 int error = m_geom->prev_key(&item, m_helper->value() - 1);
403 kDebug() << "// SEEK TO NEXT KFR: " << error;
405 int pos = item.frame();
406 if (KdenliveSettings::frametimecode()) {
407 timePos->setText(QString::number(pos));
408 } else timePos->setText(m_timecode.getTimecodeFromFrames(pos));
409 slotPositionChanged();
413 QString Geometryval::getValue() const
415 return m_geom->serialise();
418 void Geometryval::setupParam(const QDomElement par, int minFrame, int maxFrame)
420 QString val = par.attribute("value");
421 if (par.attribute("fixed") == "1") {
423 buttonPrevious->setHidden(true);
424 buttonNext->setHidden(true);
425 buttonDelete->setHidden(true);
426 buttonAdd->setHidden(true);
427 spinTransp->setMaximum(500);
428 label_pos->setHidden(true);
429 m_helper->setHidden(true);
430 timePos->setHidden(true);
432 if (par.attribute("opacity") == "false") {
433 label_opacity->setHidden(true);
434 spinTransp->setHidden(true);
435 } else if (par.attribute("opacity") == "nonzero") {
436 spinTransp->setMinimum(1);
438 char *tmp = (char *) qstrdup(val.toUtf8().data());
439 if (m_geom) m_geom->parse(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
440 else m_geom = new Mlt::Geometry(tmp, maxFrame - minFrame, m_profile.width, m_profile.height);
443 //kDebug() << " / / UPDATING TRANSITION VALUE: " << m_geom->serialise();
444 //read param her and set rect
446 m_helper->setKeyGeometry(m_geom, maxFrame - minFrame - 1);
449 doc.appendChild(doc.importNode(par, true));
450 kDebug() << "IMPORTED TRANS: " << doc.toString();*/
451 if (m_path == NULL) {
452 m_path = new QGraphicsPathItem();
453 m_path->setPen(QPen(Qt::red));
454 m_scene->addItem(m_path);
456 updateTransitionPath();
458 Mlt::GeometryItem item;
460 m_geom->fetch(&item, 0);
462 m_paramRect = new QGraphicsRectItem(QRectF(0, 0, item.w() * m_dar, item.h()));
463 m_paramRect->setPos(item.x() * m_dar, item.y());
464 m_paramRect->setZValue(0);
465 m_paramRect->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
467 m_paramRect->setPen(QPen(QBrush(QColor(255, 0, 0, 255)), 1.0));
468 m_scene->addItem(m_paramRect);
469 slotPositionChanged(0, false);
470 slotUpdateGeometry();
472 connect(timePos, SIGNAL(editingFinished()), this , SLOT(slotPositionChanged()));
474 connect(spinTransp, SIGNAL(valueChanged(int)), this , SLOT(slotTransparencyChanged(int)));
477 void Geometryval::updateTransitionPath()
479 if (m_fixedMode) return;
480 Mlt::GeometryItem item;
485 if (m_geom->next_key(&item, pos) == 1) break;
488 path.moveTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
490 path.lineTo(item.x() * m_dar + item.w() * m_dar / 2, item.y() + item.h() / 2);
495 m_path->setPath(path);
498 void Geometryval::slotUpdateTransitionProperties()
501 if (KdenliveSettings::frametimecode()) pos = timePos->text().toInt();
502 else pos = m_timecode.getFrameCount(timePos->text());
503 Mlt::GeometryItem item;
504 int error = m_geom->next_key(&item, pos);
505 if (error || item.frame() != pos) {
506 // no keyframe under cursor
509 QRectF r = m_paramRect->rect().normalized();
510 QPointF rectpos = m_paramRect->pos();
511 item.x(rectpos.x() / m_dar);
513 item.w(r.width() / m_dar);
515 m_geom->insert(item);
516 updateTransitionPath();
517 emit parameterChanged();
520 void Geometryval::slotResetPosition()
525 if (m_frameSize.isNull()) {
526 spinWidth->setValue(m_realWidth);
527 spinHeight->setValue(m_profile.height);
529 spinWidth->setValue(m_frameSize.x());
530 spinHeight->setValue(m_frameSize.y());
534 void Geometryval::setFrameSize(QPoint p)
540 void Geometryval::slotKeyframeMoved(int pos)
542 slotPositionChanged(pos);
543 slotUpdateTransitionProperties();
546 void Geometryval::slotSwitchOptions()
548 if (frameOptions->isHidden()) {
549 frameOptions->setHidden(false);
550 m_editOptions->setChecked(true);
552 frameOptions->setHidden(true);
553 m_editOptions->setChecked(false);
558 void Geometryval::slotGeometryX(int value)
560 if (!keyframeSelected())
562 m_paramRect->setPos(value, spinY->value());
563 slotUpdateTransitionProperties();
566 void Geometryval::slotGeometryY(int value)
568 if (!keyframeSelected())
570 m_paramRect->setPos(spinX->value(), value);
571 slotUpdateTransitionProperties();
574 void Geometryval::slotGeometryWidth(int value)
576 if (!keyframeSelected())
578 m_paramRect->setRect(0, 0, value, spinHeight->value());
579 slotUpdateTransitionProperties();
582 void Geometryval::slotGeometryHeight(int value)
584 if (!keyframeSelected())
586 m_paramRect->setRect(0, 0, spinWidth->value(), value);
587 slotUpdateTransitionProperties();
590 void Geometryval::slotUpdateGeometry()
592 QRectF r = m_paramRect->rect().normalized();
594 spinX->blockSignals(true);
595 spinY->blockSignals(true);
596 spinWidth->blockSignals(true);
597 spinHeight->blockSignals(true);
598 spinResize->blockSignals(true);
600 spinX->setValue(m_paramRect->pos().x());
601 spinY->setValue(m_paramRect->pos().y());
602 spinWidth->setValue(r.width());
603 spinHeight->setValue(r.height());
604 spinResize->setValue(m_paramRect->rect().width() * 100 / m_realWidth);
606 spinX->blockSignals(false);
607 spinY->blockSignals(false);
608 spinWidth->blockSignals(false);
609 spinHeight->blockSignals(false);
610 spinResize->blockSignals(false);
613 bool Geometryval::keyframeSelected()
615 Mlt::GeometryItem item;
617 if (KdenliveSettings::frametimecode()) pos = timePos->text().toInt();
618 else pos = m_timecode.getFrameCount(timePos->text());
619 if (m_geom->fetch(&item, pos) || item.key() == false) return false;