From: Till Theato Date: Thu, 12 Aug 2010 20:03:50 +0000 (+0000) Subject: Add keyframe support to new geometry widget X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=8090077b4f8f547244e76988eb553ac5c5993d33;p=kdenlive Add keyframe support to new geometry widget svn path=/trunk/kdenlive/; revision=4708 --- diff --git a/src/effectstackedit.cpp b/src/effectstackedit.cpp index 87818765..b3bda144 100644 --- a/src/effectstackedit.cpp +++ b/src/effectstackedit.cpp @@ -124,9 +124,14 @@ void EffectStackEdit::updateTimecodeFormat() QString type = pa.attributes().namedItem("type").nodeValue(); QString paramName = i18n(na.toElement().text().toUtf8().data()); - if (type == "geometry" && !KdenliveSettings::on_monitor_effects()) { - Geometryval *geom = ((Geometryval*)m_valueItems[paramName+"geometry"]); - geom->updateTimecodeFormat(); + if (type == "geometry") { + if (KdenliveSettings::on_monitor_effects()) { + GeometryWidget *geom = (GeometryWidget*)m_valueItems[paramName+"geometry"]; + geom->updateTimecodeFormat(); + } else { + Geometryval *geom = ((Geometryval*)m_valueItems[paramName+"geometry"]); + geom->updateTimecodeFormat(); + } break; } if (type == "position") { @@ -260,7 +265,7 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in connect(pl, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters())); } else if (type == "geometry") { if (KdenliveSettings::on_monitor_effects()) { - GeometryWidget *geometry = new GeometryWidget(m_monitor, pos, isEffect, this); + GeometryWidget *geometry = new GeometryWidget(m_monitor, m_timecode, pos, isEffect, this); if (minFrame == maxFrame) geometry->setupParam(pa, m_in, m_out); else @@ -269,6 +274,7 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in m_valueItems[paramName+"geometry"] = geometry; connect(geometry, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters())); connect(geometry, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int))); + connect(geometry, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int))); } else { Geometryval *geo = new Geometryval(m_profile, m_timecode, m_frameSize, pos); if (minFrame == maxFrame) diff --git a/src/geometrywidget.cpp b/src/geometrywidget.cpp index 6e3fdc91..72572e44 100644 --- a/src/geometrywidget.cpp +++ b/src/geometrywidget.cpp @@ -21,15 +21,21 @@ #include "geometrywidget.h" #include "monitor.h" #include "renderer.h" +#include "keyframehelper.h" +#include "timecodedisplay.h" #include "monitorscene.h" +#include "kdenlivesettings.h" #include #include #include +#include +#include -GeometryWidget::GeometryWidget(Monitor* monitor, int clipPos, bool isEffect, QWidget* parent ): +GeometryWidget::GeometryWidget(Monitor* monitor, Timecode timecode, int clipPos, bool isEffect, QWidget* parent ): QWidget(parent), m_monitor(monitor), + m_timePos(new TimecodeDisplay(timecode)), m_clipPos(clipPos), m_inPoint(0), m_outPoint(1), @@ -38,7 +44,39 @@ GeometryWidget::GeometryWidget(Monitor* monitor, int clipPos, bool isEffect, QWi m_geometry(NULL) { m_ui.setupUi(this); - m_scene = monitor->getEffectScene(); + + + /* + Setup of timeline and keyframe controls + */ + + ((QGridLayout *)(m_ui.widgetTimeWrapper->layout()))->addWidget(m_timePos, 1, 4); + + QVBoxLayout *layout = new QVBoxLayout(m_ui.frameTimeline); + m_timeline = new KeyframeHelper(m_ui.frameTimeline); + layout->addWidget(m_timeline); + layout->setContentsMargins(0, 0, 0, 0); + + m_ui.buttonPrevious->setIcon(KIcon("media-skip-backward")); + m_ui.buttonPrevious->setToolTip(i18n("Go to previous keyframe")); + m_ui.buttonNext->setIcon(KIcon("media-skip-forward")); + m_ui.buttonNext->setToolTip(i18n("Go to next keyframe")); + m_ui.buttonAddDelete->setIcon(KIcon("document-new")); + m_ui.buttonAddDelete->setToolTip(i18n("Add keyframe")); + + connect(m_timeline, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int))); + connect(m_timeline, SIGNAL(keyframeMoved(int)), this, SLOT(slotKeyframeMoved(int))); + connect(m_timeline, SIGNAL(addKeyframe(int)), this, SLOT(slotAddKeyframe(int))); + connect(m_timeline, SIGNAL(removeKeyframe(int)), this, SLOT(slotDeleteKeyframe(int))); + connect(m_timePos, SIGNAL(editingFinished()), this , SLOT(slotPositionChanged())); + connect(m_ui.buttonPrevious, SIGNAL(clicked()), this, SLOT(slotPreviousKeyframe())); + connect(m_ui.buttonNext, SIGNAL(clicked()), this, SLOT(slotNextKeyframe())); + connect(m_ui.buttonAddDelete, SIGNAL(clicked()), this, SLOT(slotAddDeleteKeyframe())); + + + /* + Setup of geometry controls + */ m_ui.buttonMoveLeft->setIcon(KIcon("kdenlive-align-left")); m_ui.buttonMoveLeft->setToolTip(i18n("Move to left")); @@ -53,7 +91,6 @@ GeometryWidget::GeometryWidget(Monitor* monitor, int clipPos, bool isEffect, QWi m_ui.buttonMoveBottom->setIcon(KIcon("kdenlive-align-bottom")); m_ui.buttonMoveBottom->setToolTip(i18n("Move to bottom")); - connect(m_ui.spinX, SIGNAL(valueChanged(int)), this, SLOT(slotSetX(int))); connect(m_ui.spinY, SIGNAL(valueChanged(int)), this, SLOT(slotSetY(int))); connect(m_ui.spinWidth, SIGNAL(valueChanged(int)), this, SLOT(slotSetWidth(int))); @@ -68,18 +105,27 @@ GeometryWidget::GeometryWidget(Monitor* monitor, int clipPos, bool isEffect, QWi connect(m_ui.buttonCenterV, SIGNAL(clicked()), this, SLOT(slotCenterV())); connect(m_ui.buttonMoveBottom, SIGNAL(clicked()), this, SLOT(slotMoveBottom())); + + m_scene = monitor->getEffectScene(); connect(m_scene, SIGNAL(actionFinished()), this, SLOT(slotUpdateGeometry())); - connect(m_monitor->render, SIGNAL(rendererPosition(int)), this, SLOT(slotCheckPosition(int))); + connect(m_monitor->render, SIGNAL(rendererPosition(int)), this, SLOT(slotCheckMonitorPosition(int))); connect(this, SIGNAL(parameterChanged()), this, SLOT(slotUpdateProperties())); } GeometryWidget::~GeometryWidget() { + delete m_timePos; + delete m_timeline; m_scene->removeItem(m_rect); delete m_geometry; m_monitor->slotEffectScene(false); } +void GeometryWidget::updateTimecodeFormat() +{ + m_timePos->slotUpdateTimeCodeFormat(); +} + QString GeometryWidget::getValue() const { return m_geometry->serialise(); @@ -89,15 +135,23 @@ void GeometryWidget::setupParam(const QDomElement elem, int minframe, int maxfra { m_inPoint = minframe; m_outPoint = maxframe; - QString value = elem.attribute("value"); - char *tmp = (char *) qstrdup(value.toUtf8().data()); + char *tmp = (char *) qstrdup(elem.attribute("value").toUtf8().data()); if (m_geometry) m_geometry->parse(tmp, maxframe - minframe, m_monitor->render->renderWidth(), m_monitor->render->renderHeight()); else m_geometry = new Mlt::Geometry(tmp, maxframe - minframe, m_monitor->render->renderWidth(), m_monitor->render->renderHeight()); delete[] tmp; + if (elem.attribute("fixed") == "1") { + // Keyframes are disabled + m_ui.widgetTimeWrapper->setHidden(true); + } else { + m_timeline->setKeyGeometry(m_geometry, m_outPoint - m_inPoint - 1); + m_timeline->update(); + m_timePos->setRange(0, m_outPoint - m_inPoint - 1); + } + Mlt::GeometryItem item; m_geometry->fetch(&item, 0); @@ -113,11 +167,120 @@ void GeometryWidget::setupParam(const QDomElement elem, int minframe, int maxfra m_rect->setBrush(Qt::transparent); m_scene->addItem(m_rect); + slotPositionChanged(0, false); slotUpdateProperties(); - slotCheckPosition(m_monitor->render->seekFramePosition()); + slotCheckMonitorPosition(m_monitor->render->seekFramePosition()); +} + + +void GeometryWidget::slotPositionChanged(int pos, bool seek) +{ + if (pos == -1) + pos = m_timePos->getValue(); + + m_timePos->setValue(pos); + m_timeline->blockSignals(true); + m_timeline->setValue(pos); + m_timeline->blockSignals(false); + + Mlt::GeometryItem item; + if (m_geometry->fetch(&item, pos) || item.key() == false) { + // no keyframe + m_scene->setEnabled(false); + m_ui.widgetGeometry->setEnabled(false); + m_ui.buttonAddDelete->setIcon(KIcon("document-new")); + m_ui.buttonAddDelete->setToolTip(i18n("Add keyframe")); + } else { + // keyframe + m_scene->setEnabled(true); + m_ui.widgetGeometry->setEnabled(true); + m_ui.buttonAddDelete->setIcon(KIcon("edit-delete")); + m_ui.buttonAddDelete->setToolTip(i18n("Delete keyframe")); + } + + m_rect->setPos(item.x(), item.y()); + m_rect->setRect(0, 0, item.w(), item.h()); + slotUpdateProperties(); + + if (seek && KdenliveSettings::transitionfollowcursor()) + emit seekToPos(m_clipPos + pos); } -void GeometryWidget::slotCheckPosition(int renderPos) +void GeometryWidget::slotKeyframeMoved(int pos) +{ + slotPositionChanged(pos); + slotUpdateGeometry(); +} + +void GeometryWidget::slotAddKeyframe(int pos) +{ + Mlt::GeometryItem item; + if (pos == -1) + pos = m_timePos->getValue(); + item.frame(pos); + QRectF r = m_rect->rect().normalized(); + QPointF rectpos = m_rect->pos(); + item.x(rectpos.x()); + item.y(rectpos.y()); + item.w(r.width()); + item.h(r.height()); + m_geometry->insert(item); + + m_timeline->update(); + slotPositionChanged(pos, false); + emit parameterChanged(); +} + +void GeometryWidget::slotDeleteKeyframe(int pos) +{ + Mlt::GeometryItem item; + if (pos == -1) + pos = m_timePos->getValue(); + // check there is more than one keyframe, do not allow to delete last one + if (m_geometry->next_key(&item, pos + 1)) { + if (m_geometry->prev_key(&item, pos - 1) || item.frame() == pos) + return; + } + m_geometry->remove(pos); + + m_timeline->update(); + slotPositionChanged(pos, false); + emit parameterChanged(); +} + +void GeometryWidget::slotPreviousKeyframe() +{ + Mlt::GeometryItem item; + // Go to start if no keyframe is found + int pos = 0; + if(!m_geometry->prev_key(&item, m_timeline->value() - 1)) + pos = item.frame(); + + slotPositionChanged(pos); +} + +void GeometryWidget::slotNextKeyframe() +{ + Mlt::GeometryItem item; + // Go to end if no keyframe is found + int pos = m_timeline->frameLength; + if (!m_geometry->next_key(&item, m_timeline->value() + 1)) + pos = item.frame(); + + slotPositionChanged(pos); +} + +void GeometryWidget::slotAddDeleteKeyframe() +{ + Mlt::GeometryItem item; + if (m_geometry->fetch(&item, m_timePos->getValue()) || item.key() == false) + slotAddKeyframe(); + else + slotDeleteKeyframe(); +} + + +void GeometryWidget::slotCheckMonitorPosition(int renderPos) { /* We do only get the position in timeline if this geometry belongs to a transition, @@ -135,10 +298,14 @@ void GeometryWidget::slotCheckPosition(int renderPos) } } + void GeometryWidget::slotUpdateGeometry() { Mlt::GeometryItem item; - m_geometry->next_key(&item, 0); + int pos = m_timePos->getValue(); + // get keyframe and make sure it is the correct one + if (m_geometry->next_key(&item, pos) || item.frame() != pos) + return; QRectF rectSize = m_rect->rect().normalized(); QPointF rectPos = m_rect->pos(); diff --git a/src/geometrywidget.h b/src/geometrywidget.h index 79e5baf7..35369f43 100644 --- a/src/geometrywidget.h +++ b/src/geometrywidget.h @@ -22,6 +22,7 @@ #define GEOMETRYWIDGET_H #include "ui_geometrywidget_ui.h" +#include "timecode.h" #include #include @@ -30,6 +31,8 @@ class QDomElement; class QGraphicsRectItem; class Monitor; class MonitorScene; +class KeyframeHelper; +class TimecodeDisplay; class GeometryWidget : public QWidget { @@ -39,10 +42,12 @@ public: * @param monitor Project monitor * @param clipPos Position of the clip in timeline * @param parent (optional) Parent widget */ - GeometryWidget(Monitor *monitor, int clipPos, bool isEffect, QWidget* parent = 0); + GeometryWidget(Monitor *monitor, Timecode timecode, int clipPos, bool isEffect, QWidget* parent = 0); virtual ~GeometryWidget(); /** @brief Gets the geometry as a serialized string. */ QString getValue() const; + /** @brief Updates the timecode display according to settings (frame number or hh:mm:ss:ff) */ + void updateTimecodeFormat(); public slots: /** @brief Sets up the rect and the geometry object. @@ -54,6 +59,7 @@ public slots: private: Ui::GeometryWidget_UI m_ui; Monitor *m_monitor; + TimecodeDisplay *m_timePos; /** Position of the clip in timeline. */ int m_clipPos; /** In point of the clip (crop from start). */ @@ -63,12 +69,37 @@ private: bool m_isEffect; MonitorScene *m_scene; QGraphicsRectItem *m_rect; + KeyframeHelper *m_timeline; + /** Stores the different settings in the MLT geometry format. */ Mlt::Geometry *m_geometry; private slots: + /** @brief Updates controls according to position. + * @param pos (optional) Position to update to + * @param seek (optional, default = true) Whether to seek timleine & project monitor to pos + * If pos = -1 (default) the value of m_timePos is used. */ + void slotPositionChanged(int pos = -1, bool seek = true); + /** @brief Updates settings after a keyframe was moved to @param pos. */ + void slotKeyframeMoved(int pos); + /** @brief Adds a keyframe. + * @param pos (optional) Position where the keyframe should be added + * If pos = -1 (default) the value of m_timePos is used. */ + void slotAddKeyframe(int pos = -1); + /** @brief Deletes a keyframe. + * @param pos (optional) Position of the keyframe which should be deleted + * If pos = -1 (default) the value of m_timePos is used. */ + void slotDeleteKeyframe(int pos = -1); + /** @brief Goes to the next keyframe or to the end if none is available. */ + void slotNextKeyframe(); + /** @brief Goes to the previous keyframe or to the beginning if none is available. */ + void slotPreviousKeyframe(); + /** @brief Adds or deletes a keyframe depending on whether there is already a keyframe at the current position. */ + void slotAddDeleteKeyframe(); + /** @brief Makes sure the monitor effect scene is only visible if the clip this geometry belongs to is visible. * @param renderPos Postion of the Monitor / Timeline cursor */ - void slotCheckPosition(int renderPos); + void slotCheckMonitorPosition(int renderPos); + /** @brief Updates the Mlt::Geometry object. */ void slotUpdateGeometry(); /** @brief Updates the spinBoxes according to the rect. */ @@ -102,6 +133,7 @@ private slots: signals: void parameterChanged(); void checkMonitorPosition(int); + void seekToPos(int); }; #endif diff --git a/src/monitorscene.cpp b/src/monitorscene.cpp index fc80d705..7048f12b 100644 --- a/src/monitorscene.cpp +++ b/src/monitorscene.cpp @@ -33,7 +33,8 @@ MonitorScene::MonitorScene(Render *renderer, QObject* parent) : m_selectedItem(NULL), m_resizeMode(NoResize), m_clickPoint(0, 0), - m_backgroundImage(QImage()) + m_backgroundImage(QImage()), + m_enabled(true) { setBackgroundBrush(QBrush(QColor(KdenliveSettings::window_background().name()))); @@ -74,6 +75,11 @@ void MonitorScene::setUp() slotUpdateBackground(true); } +void MonitorScene::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + void MonitorScene::slotUpdateBackground(bool fit) { if (m_view && m_view->isVisible()) { @@ -137,6 +143,9 @@ resizeModes MonitorScene::getResizeMode(QGraphicsRectItem *item, QPoint pos) void MonitorScene::mousePressEvent(QGraphicsSceneMouseEvent *event) { + if (!m_enabled) + return; + m_resizeMode = NoResize; m_selectedItem = NULL; @@ -159,6 +168,9 @@ void MonitorScene::mousePressEvent(QGraphicsSceneMouseEvent *event) void MonitorScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + if (!m_enabled) + return; + QPointF mousePos = event->scenePos(); if (m_selectedItem && event->buttons() & Qt::LeftButton) { @@ -276,6 +288,9 @@ void MonitorScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void MonitorScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + if (!m_enabled) + return; + QGraphicsScene::mouseReleaseEvent(event); emit actionFinished(); } diff --git a/src/monitorscene.h b/src/monitorscene.h index 87c36968..ac26f4e5 100644 --- a/src/monitorscene.h +++ b/src/monitorscene.h @@ -34,6 +34,7 @@ class MonitorScene : public QGraphicsScene public: MonitorScene(Render *renderer, QObject* parent = 0); void setUp(); + void setEnabled(bool enabled = true); protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); @@ -58,6 +59,7 @@ private: resizeModes m_resizeMode; QPointF m_clickPoint; QImage m_backgroundImage; + bool m_enabled; signals: void actionFinished(); diff --git a/src/timecodedisplay.cpp b/src/timecodedisplay.cpp index 77b49599..72bbd863 100644 --- a/src/timecodedisplay.cpp +++ b/src/timecodedisplay.cpp @@ -39,7 +39,7 @@ TimecodeDisplay::TimecodeDisplay(Timecode t, QWidget *parent) lineedit->setFont(KGlobalSettings::toolBarFont()); QFontMetrics fm = lineedit->fontMetrics(); lineedit->setMaximumWidth(fm.width("88:88:88:888")); - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); + setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); setTimeCodeFormat(KdenliveSettings::frametimecode(), true); diff --git a/src/widgets/geometrywidget_ui.ui b/src/widgets/geometrywidget_ui.ui index 264b4d61..fac79bf9 100644 --- a/src/widgets/geometrywidget_ui.ui +++ b/src/widgets/geometrywidget_ui.ui @@ -6,167 +6,180 @@ 0 0 - 274 - 133 + 285 + 187 Form - - - - X - - - - - - - -10000 - - - 10000 - - - - - - Y - - - - - - - Width - - - - - - - Height - - - - - - - -10000 - - - 10000 - - - - - - - 1 - - - 10000 - - - - - - - 1 - - - 10000 - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - + + - + - ... + X - - - - ... + + + + -10000 + + + 10000 - - - ... + + + -10000 + + + 10000 - - + + - ... + Y - - + + - ... + Width - - - - ... + + + + 1 + + + 10000 - - - - - - - - + + - Resize: + Height - - - - % - + + 1 - 500 + 10000 - - 100 + + + + + + + + + Resize: + + + + + + + % + + + 1 + + + 500 + + + 100 + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + + + + Qt::Vertical - - + + Qt::Horizontal @@ -178,15 +191,8 @@ - - - - Qt::Vertical - - - - - + + Qt::Horizontal @@ -198,6 +204,74 @@ + + + + + 0 + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 15 + + + + + 16777215 + 15 + + + + + + + + + +