From: Till Theato Date: Sun, 13 Feb 2011 15:15:39 +0000 (+0000) Subject: rotoscoping: X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=802c8ece2ae6a16819222247a90841717bb366ec;p=kdenlive rotoscoping: - keyframe support (WIP) - obey to direct update setting for on-monitor items svn path=/trunk/kdenlive/; revision=5400 --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ef477b7e..817fb835 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -278,6 +278,8 @@ set(kdenlive_SRCS beziercurve/cubicbezierspline.cpp dragvalue.cpp monitoreditwidget.cpp + simplekeyframes/simpletimelinewidget.cpp + simplekeyframes/simplekeyframewidget.cpp ) add_definitions(${KDE4_DEFINITIONS}) diff --git a/src/effectstackedit.cpp b/src/effectstackedit.cpp index fe62a739..c1f81bee 100644 --- a/src/effectstackedit.cpp +++ b/src/effectstackedit.cpp @@ -447,10 +447,11 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in meetDependency(paramName, type, EffectsList::parameter(e, depends)); #ifdef QJSON } else if (type == "roto-spline") { - RotoWidget *roto = new RotoWidget(value, m_monitor, m_in, m_out, this); + RotoWidget *roto = new RotoWidget(value, m_monitor, m_in, m_out, m_timecode, this); roto->slotShowScene(!disable); connect(roto, SIGNAL(valueChanged()), this, SLOT(collectAllParameters())); connect(roto, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int))); + connect(roto, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int))); connect(this, SIGNAL(syncEffectsPos(int)), roto, SLOT(slotSyncPosition(int))); connect(this, SIGNAL(effectStateChanged(bool)), roto, SLOT(slotShowScene(bool))); m_vbox->addWidget(roto); diff --git a/src/geometrywidget.cpp b/src/geometrywidget.cpp index bb9fa8a2..75c4d0fd 100644 --- a/src/geometrywidget.cpp +++ b/src/geometrywidget.cpp @@ -248,8 +248,7 @@ void GeometryWidget::slotSyncPosition(int relTimelinePos) { // do only sync if this effect is keyframable if (m_timePos->maximum() > 0 && KdenliveSettings::transitionfollowcursor()) { - relTimelinePos = qMax(0, relTimelinePos); - relTimelinePos = qMin(relTimelinePos, m_timePos->maximum()); + relTimelinePos = qBound(0, relTimelinePos, m_timePos->maximum()); if (relTimelinePos != m_timePos->getValue()) slotPositionChanged(relTimelinePos, false); } diff --git a/src/onmonitoritems/rotoscoping/bpointitem.cpp b/src/onmonitoritems/rotoscoping/bpointitem.cpp index 4129a17d..0b29097d 100644 --- a/src/onmonitoritems/rotoscoping/bpointitem.cpp +++ b/src/onmonitoritems/rotoscoping/bpointitem.cpp @@ -73,8 +73,13 @@ void BPointItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option Q_UNUSED(option); Q_UNUSED(widget); - painter->setPen(QPen(Qt::yellow, 1, Qt::SolidLine)); - painter->setBrush(QBrush(isSelected() ? Qt::red : Qt::yellow)); + if (isEnabled()) { + painter->setPen(QPen(Qt::yellow, 1, Qt::SolidLine)); + painter->setBrush(QBrush(isSelected() ? Qt::red : Qt::yellow)); + } else { + painter->setPen(QPen(Qt::gray, 1, Qt::SolidLine)); + painter->setBrush(QBrush(Qt::gray)); + } painter->setRenderHint(QPainter::Antialiasing); double handleSize = 6 / painter->matrix().m11(); @@ -152,13 +157,18 @@ void BPointItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) if (parentItem()) { SplineItem *parent = qgraphicsitem_cast(parentItem()); if (parent) - parent->updateSpline(); + parent->updateSpline(true); } } void BPointItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { -QGraphicsItem::mouseReleaseEvent(event); + if (parentItem()) { + SplineItem *parent = qgraphicsitem_cast(parentItem()); + if (parent) + parent->updateSpline(false); + } + QGraphicsItem::mouseReleaseEvent(event); } void BPointItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event) diff --git a/src/onmonitoritems/rotoscoping/splineitem.cpp b/src/onmonitoritems/rotoscoping/splineitem.cpp index 64eedb03..5dbe00fe 100644 --- a/src/onmonitoritems/rotoscoping/splineitem.cpp +++ b/src/onmonitoritems/rotoscoping/splineitem.cpp @@ -19,6 +19,7 @@ #include "splineitem.h" #include "bpointitem.h" #include "nearestpoint.h" +#include "kdenlivesettings.h" #include #include @@ -52,7 +53,9 @@ void deCasteljau(BPoint *p1, BPoint *p2, BPoint *res, double t) SplineItem::SplineItem(const QList< BPoint >& points, QGraphicsItem* parent, QGraphicsScene *scene) : - QGraphicsPathItem(parent, scene) + QGraphicsPathItem(parent, scene), + m_closed(false), + m_editing(false) { QPen framepen(Qt::SolidLine); framepen.setColor(Qt::yellow); @@ -60,21 +63,7 @@ SplineItem::SplineItem(const QList< BPoint >& points, QGraphicsItem* parent, QGr setBrush(Qt::NoBrush); setAcceptHoverEvents(true); - m_closed = true; - if (points.isEmpty()) { - m_closed = false; - grabMouse(); - return; - } - - QPainterPath path(points.at(0).p); - int j; - for (int i = 0; i < points.count(); ++i) { - new BPointItem(points.at(i), this); - j = (i + 1) % points.count(); - path.cubicTo(points.at(i).h2, points.at(j).h1, points.at(j).p); - } - setPath(path); + setPoints(points); } int SplineItem::type() const @@ -82,7 +71,12 @@ int SplineItem::type() const return Type; } -void SplineItem::updateSpline() +bool SplineItem::editing() +{ + return m_editing; +} + +void SplineItem::updateSpline(bool editing) { QPainterPath path(qgraphicsitem_cast(childItems().at(0))->getPoint().p); @@ -96,8 +90,10 @@ void SplineItem::updateSpline() } setPath(path); - if (m_closed) - emit changed(); + m_editing = editing; + + if (m_closed && (!editing || KdenliveSettings::monitorscene_directupdate())) + emit changed(editing); } QList SplineItem::getPoints() @@ -108,6 +104,29 @@ QList SplineItem::getPoints() return points; } +void SplineItem::setPoints(const QList< BPoint >& points) +{ + if (points.count() < 2) { + m_closed = false; + grabMouse(); + return; + } else { + m_closed = true; + } + + qDeleteAll(childItems()); + childItems().clear(); + + QPainterPath path(points.at(0).p); + int j; + for (int i = 0; i < points.count(); ++i) { + new BPointItem(points.at(i), this); + j = (i + 1) % points.count(); + path.cubicTo(points.at(i).h2, points.at(j).h1, points.at(j).p); + } + setPath(path); +} + void SplineItem::removeChild(QGraphicsItem* child) { if (childItems().count() > 2) { diff --git a/src/onmonitoritems/rotoscoping/splineitem.h b/src/onmonitoritems/rotoscoping/splineitem.h index f2e7562a..4b3599d4 100644 --- a/src/onmonitoritems/rotoscoping/splineitem.h +++ b/src/onmonitoritems/rotoscoping/splineitem.h @@ -35,8 +35,11 @@ public: virtual int type() const; - void updateSpline(); + bool editing(); + + void updateSpline(bool editing = false); QList getPoints(); + void setPoints(const QList &points); void removeChild(QGraphicsItem *child); @@ -50,9 +53,10 @@ private: int getClosestPointOnCurve(QPointF point, double *tFinal); bool m_closed; + bool m_editing; signals: - void changed(); + void changed(bool editing); }; #endif diff --git a/src/rotoscoping/rotowidget.cpp b/src/rotoscoping/rotowidget.cpp index e755dd23..b58d370f 100644 --- a/src/rotoscoping/rotowidget.cpp +++ b/src/rotoscoping/rotowidget.cpp @@ -23,12 +23,18 @@ #include "monitoreditwidget.h" #include "onmonitoritems/rotoscoping/bpointitem.h" #include "onmonitoritems/rotoscoping/splineitem.h" +#include "simplekeyframes/simplekeyframewidget.h" +#include "kdenlivesettings.h" #include #include +#include -RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, QWidget* parent) : +#include + + +RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode t, QWidget* parent) : QWidget(parent), m_monitor(monitor), m_showScene(true), @@ -36,6 +42,10 @@ RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, QWidget* m_out(out), m_pos(0) { + QVBoxLayout *l = new QVBoxLayout(this); + m_keyframeWidget = new SimpleKeyframeWidget(t, in, out, this); + l->addWidget(m_keyframeWidget); + MonitorEditWidget *edit = monitor->getEffectEdit(); edit->showVisibilityButton(true); m_scene = edit->getScene(); @@ -47,27 +57,37 @@ RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, QWidget* // :( } - int width = m_monitor->render->frameRenderWidth(); - int height = m_monitor->render->renderHeight(); - QList points; - foreach (const QVariant &bpoint, m_data.toList()) { - QList l = bpoint.toList(); - BPoint p; - p.h1 = QPointF(l.at(0).toList().at(0).toDouble() * width, l.at(0).toList().at(1).toDouble() * height); - p.p = QPointF(l.at(1).toList().at(0).toDouble() * width, l.at(1).toList().at(1).toDouble() * height); - p.h2 = QPointF(l.at(2).toList().at(0).toDouble() * width, l.at(2).toList().at(1).toDouble() * height); - points << p; + + if (m_data.canConvert(QVariant::Map)) { + QList keyframes; + QMap map = m_data.toMap(); + QMap ::const_iterator i = map.constBegin(); + while (i != map.constEnd()) { + keyframes.append(i.key().toInt()); + ++i; + } + m_keyframeWidget->setKeyframes(keyframes); + } else { + m_keyframeWidget->setKeyframes(QList () << 0); } - m_item = new SplineItem(points, NULL, m_scene); + m_item = new SplineItem(QList (), NULL, m_scene); - connect(m_item, SIGNAL(changed()), this, SLOT(slotUpdateData())); + connect(m_item, SIGNAL(changed(bool)), this, SLOT(slotUpdateData(bool))); connect(edit, SIGNAL(showEdit(bool)), this, SLOT(slotShowScene(bool))); connect(m_monitor, SIGNAL(renderPosition(int)), this, SLOT(slotCheckMonitorPosition(int))); + connect(m_keyframeWidget, SIGNAL(positionChanged(int)), this, SLOT(slotPositionChanged(int))); + connect(m_keyframeWidget, SIGNAL(keyframeAdded(int)), this, SLOT(slotAddKeyframe(int))); + connect(m_keyframeWidget, SIGNAL(keyframeRemoved(int)), this, SLOT(slotRemoveKeyframe(int))); + connect(m_scene, SIGNAL(addKeyframe()), this, SLOT(slotAddKeyframe())); + + slotPositionChanged(0, false); } RotoWidget::~RotoWidget() { + delete m_keyframeWidget; + m_scene->removeItem(m_item); delete m_item; @@ -87,7 +107,9 @@ void RotoWidget::slotCheckMonitorPosition(int renderPos) void RotoWidget::slotSyncPosition(int relTimelinePos) { - Q_UNUSED(relTimelinePos); + relTimelinePos = qBound(0, relTimelinePos, m_out); + m_keyframeWidget->slotSetPosition(relTimelinePos, false); + slotPositionChanged(relTimelinePos, false); } void RotoWidget::slotShowScene(bool show) @@ -99,8 +121,10 @@ void RotoWidget::slotShowScene(bool show) slotCheckMonitorPosition(m_monitor->render->seekFramePosition()); } -void RotoWidget::slotUpdateData() +void RotoWidget::slotUpdateData(int pos, bool editing) { + Q_UNUSED(editing) + int width = m_monitor->render->frameRenderWidth(); int height = m_monitor->render->renderHeight(); @@ -112,15 +136,133 @@ void RotoWidget::slotUpdateData() pl << QVariant(QList () << QVariant(point[i].x() / width) << QVariant(point[i].y() / height)); vlist << QVariant(pl); } - m_data = QVariant(vlist); + + if (m_data.canConvert(QVariant::Map)) { + QMap map = m_data.toMap(); + map[QString::number(pos < 0 ? m_keyframeWidget->getPosition() : pos)] = QVariant(vlist); + m_data = QVariant(map); + } else { + m_data = QVariant(vlist); + } emit valueChanged(); } +void RotoWidget::slotUpdateData(bool editing) +{ + slotUpdateData(-1, editing); +} + QString RotoWidget::getSpline() { QJson::Serializer serializer; return QString(serializer.serialize(m_data)); } +void RotoWidget::slotPositionChanged(int pos, bool seek) +{ + if (m_item->editing()) + return; + + m_keyframeWidget->slotSetPosition(pos, false); + + if (m_data.canConvert(QVariant::Map)) { + QMap map = m_data.toMap(); + QMap ::const_iterator i = map.constBegin(); + int keyframe1, keyframe2; + keyframe1 = keyframe2 = i.key().toInt(); + while (i.key().toInt() < pos && ++i != map.constEnd()) { + keyframe1 = keyframe2; + keyframe2 = i.key().toInt(); + } + + if (keyframe1 != keyframe2 && pos < keyframe2) { + QList p1 = getPoints(keyframe1); + QList p2 = getPoints(keyframe2); + QList p; + qreal relPos = (pos - keyframe1) / (qreal)(keyframe2 - keyframe1 + 1); + + for (int i = 0; i < p1.count(); ++i) { + BPoint bp; + for (int j = 0; j < 3; ++j) { + if (p1.at(i)[j] != p2.at(i)[j]) + bp[j] = QLineF(p1.at(i)[j], p2.at(i)[j]).pointAt(relPos); + else + bp[j] = p1.at(i)[j]; + } + p.append(bp); + } + + m_item->setPoints(p); + m_item->setEnabled(false); + m_scene->setEnabled(false); + } else { + m_item->setPoints(getPoints(keyframe2)); + m_item->setEnabled(pos == keyframe2); + m_scene->setEnabled(pos == keyframe2); + } + } else { + m_item->setPoints(getPoints(-1)); + m_item->setEnabled(true); + m_scene->setEnabled(true); + } + + if (seek) + emit seekToPos(pos); +} + +QList RotoWidget::getPoints(int keyframe) +{ + int width = m_monitor->render->frameRenderWidth(); + int height = m_monitor->render->renderHeight(); + QList points; + QList data; + if (keyframe >= 0) + data = m_data.toMap()[QString::number(keyframe)].toList(); + else + data = m_data.toList(); + foreach (const QVariant &bpoint, data) { + QList l = bpoint.toList(); + BPoint p; + p.h1 = QPointF(l.at(0).toList().at(0).toDouble() * width, l.at(0).toList().at(1).toDouble() * height); + p.p = QPointF(l.at(1).toList().at(0).toDouble() * width, l.at(1).toList().at(1).toDouble() * height); + p.h2 = QPointF(l.at(2).toList().at(0).toDouble() * width, l.at(2).toList().at(1).toDouble() * height); + points << p; + } + return points; +} + +void RotoWidget::slotAddKeyframe(int pos) +{ + if (!m_data.canConvert(QVariant::Map)) { + QVariant data = m_data; + QMap map; + map[QString::number(m_in)] = data; + m_data = QVariant(map); + } + + if (pos < 0) + m_keyframeWidget->addKeyframe(); + + slotUpdateData(pos); + m_item->setEnabled(true); + m_scene->setEnabled(true); +} + +void RotoWidget::slotRemoveKeyframe(int pos) +{ + if (pos < 0) + pos = m_keyframeWidget->getPosition(); + + if (!m_data.canConvert(QVariant::Map) || m_data.toMap().count() < 2) + return; + + m_data.toMap().remove(QString::number(pos)); + + if (m_data.toMap().count() == 1) + m_data = m_data.toMap().begin().value(); + + slotPositionChanged(m_keyframeWidget->getPosition(), false); +} + #include "rotowidget.moc" diff --git a/src/rotoscoping/rotowidget.h b/src/rotoscoping/rotowidget.h index 3ac9fa0a..668e3e21 100644 --- a/src/rotoscoping/rotowidget.h +++ b/src/rotoscoping/rotowidget.h @@ -20,19 +20,21 @@ #define ROTOWIDGET_H #include "bpoint.h" +#include "timecode.h" #include class Monitor; class MonitorScene; class SplineItem; +class SimpleKeyframeWidget; class RotoWidget : public QWidget { Q_OBJECT public: - RotoWidget(QString data, Monitor *monitor, int in, int out, QWidget* parent = 0); + RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode t, QWidget* parent = 0); virtual ~RotoWidget(); QString getSpline(); @@ -46,9 +48,11 @@ public slots: signals: void valueChanged(); void checkMonitorPosition(int); + void seekToPos(int pos); private: + SimpleKeyframeWidget *m_keyframeWidget; Monitor *m_monitor; MonitorScene *m_scene; bool m_showScene; @@ -58,12 +62,20 @@ private: int m_out; int m_pos; + QList getPoints(int keyframe); + private slots: /** @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 slotCheckMonitorPosition(int renderPos); - void slotUpdateData(); + void slotUpdateData(int pos = -1, bool editing = false); + void slotUpdateData(bool editing); + + void slotPositionChanged(int pos, bool seek = true); + + void slotAddKeyframe(int pos = -1); + void slotRemoveKeyframe(int pos = -1); }; #endif diff --git a/src/simplekeyframes/CMakeLists.txt b/src/simplekeyframes/CMakeLists.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/simplekeyframes/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/src/simplekeyframes/simplekeyframewidget.cpp b/src/simplekeyframes/simplekeyframewidget.cpp new file mode 100644 index 00000000..47ad20e8 --- /dev/null +++ b/src/simplekeyframes/simplekeyframewidget.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + * Copyright (C) 2011 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#include "simplekeyframes/simplekeyframewidget.h" +#include "simpletimelinewidget.h" +#include "timecodedisplay.h" + +#include +#include + +#include +#include + +SimpleKeyframeWidget::SimpleKeyframeWidget(Timecode t, int in, int out, QWidget *parent) : + QWidget(parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QGridLayout *l = new QGridLayout(this); + + m_timeline = new SimpleTimelineWidget(this); + m_timeline->setRange(in ,out); + + m_buttonAddDelete = new QToolButton(this); + m_buttonAddDelete->setAutoRaise(true); + m_buttonAddDelete->setIcon(KIcon("document-new")); + m_buttonAddDelete->setToolTip(i18n("Add keyframe")); + + m_buttonPrevious = new QToolButton(this); + m_buttonPrevious->setAutoRaise(true); + m_buttonPrevious->setIcon(KIcon("media-skip-backward")); + m_buttonPrevious->setToolTip(i18n("Go to previous keyframe")); + + m_buttonNext = new QToolButton(this); + m_buttonNext->setAutoRaise(true); + m_buttonNext->setIcon(KIcon("media-skip-forward")); + m_buttonNext->setToolTip(i18n("Go to next keyframe")); + + m_time = new TimecodeDisplay(t, this); + m_time->setRange(in, out); + + l->addWidget(m_timeline, 0, 0, 1, -1); + l->addWidget(m_buttonPrevious, 1, 0); + l->addWidget(m_buttonAddDelete, 1, 1); + l->addWidget(m_buttonNext, 1, 2); + l->addWidget(m_time, 1, 3, Qt::AlignRight); + + connect(m_time, SIGNAL(editingFinished()), this, SLOT(slotSetPosition())); + connect(m_timeline, SIGNAL(positionChanged(int)), this, SLOT(slotSetPosition(int))); + connect(m_timeline, SIGNAL(keyframeAdded(int)), this, SIGNAL(keyframeAdded(int))); + connect(m_timeline, SIGNAL(keyframeRemoved(int)), this, SIGNAL(keyframeRemoved(int))); + + connect(m_buttonAddDelete, SIGNAL(pressed()), m_timeline, SLOT(slotAddRemove())); + connect(m_buttonPrevious, SIGNAL(pressed()), m_timeline, SLOT(slotGoToPrev())); + connect(m_buttonNext, SIGNAL(pressed()), m_timeline, SLOT(slotGoToNext())); +} + +SimpleKeyframeWidget::~SimpleKeyframeWidget() +{ + delete m_timeline; + delete m_buttonAddDelete; + delete m_buttonPrevious; + delete m_buttonNext; + //delete m_buttonSync; + delete m_time; +} + +void SimpleKeyframeWidget::slotSetPosition(int pos, bool update) +{ + if (pos < 0) { + pos = m_time->getValue(); + m_timeline->slotSetPosition(pos); + } else { + m_time->setValue(pos); + m_timeline->slotSetPosition(pos); + } + + if (update) + emit positionChanged(pos); +} + +int SimpleKeyframeWidget::getPosition() +{ + return m_time->getValue(); +} + +void SimpleKeyframeWidget::setKeyframes(const QList< int >& keyframes) +{ + m_timeline->setKeyframes(keyframes); +} + +void SimpleKeyframeWidget::addKeyframe(int pos) +{ + blockSignals(true); + m_timeline->slotAddKeyframe(pos); + blockSignals(false); +} + + +#include "simplekeyframewidget.moc" diff --git a/src/simplekeyframes/simplekeyframewidget.h b/src/simplekeyframes/simplekeyframewidget.h new file mode 100644 index 00000000..f4c45bb8 --- /dev/null +++ b/src/simplekeyframes/simplekeyframewidget.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2011 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#ifndef SIMPLEKEYFRAMEWIDGET_H +#define SIMPLEKEYFRAMEWIDGET_H + +#include "timecode.h" + +#include +#include + +class SimpleTimelineWidget; +class TimecodeDisplay; +class QToolButton; + + +class SimpleKeyframeWidget : public QWidget +{ + Q_OBJECT + +public: + SimpleKeyframeWidget(Timecode t, int in, int out, QWidget* parent = 0); + virtual ~SimpleKeyframeWidget(); + + int getPosition(); + void setKeyframes(const QList &keyframes); + void addKeyframe(int pos = -1); + +public slots: + void slotSetPosition(int pos = -1, bool update = true); + +signals: + void positionChanged(int pos); + void keyframeAdded(int pos); + void keyframeRemoved(int pos); + +private: + SimpleTimelineWidget *m_timeline; + QToolButton *m_buttonAddDelete; + QToolButton *m_buttonPrevious; + QToolButton *m_buttonNext; + //QToolButton *m_buttonSync; + TimecodeDisplay *m_time; +}; + + +#endif diff --git a/src/simplekeyframes/simpletimelinewidget.cpp b/src/simplekeyframes/simpletimelinewidget.cpp new file mode 100644 index 00000000..f5621869 --- /dev/null +++ b/src/simplekeyframes/simpletimelinewidget.cpp @@ -0,0 +1,218 @@ +/*************************************************************************** + * Copyright (C) 2011 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#include "simpletimelinewidget.h" + +#include +#include + + +SimpleTimelineWidget::SimpleTimelineWidget(QWidget* parent) : + QWidget(parent), + m_min(0), + m_max(1), + m_position(0), + m_currentKeyframe(-1), + m_currentKeyframeOriginal(-1), + m_lineHeight(10), + m_scale(1) +{ + setMouseTracking(true); + setMinimumSize(QSize(150, 20)); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); +} + +void SimpleTimelineWidget::setKeyframes(QList keyframes) +{ + m_keyframes = keyframes; + m_currentKeyframe = m_currentKeyframeOriginal = -1; + update(); +} + +void SimpleTimelineWidget::slotSetPosition(int pos) +{ + m_position = pos; + update(); +} + +void SimpleTimelineWidget::slotAddKeyframe(int pos, int select) +{ + if (pos < 0) + pos = m_position; + + m_keyframes.append(pos); + if (select) + m_currentKeyframe = m_currentKeyframeOriginal = pos; + update(); + + emit keyframeAdded(pos); +} + +void SimpleTimelineWidget::slotAddRemove() +{ + if (m_keyframes.contains(m_position)) + slotRemoveKeyframe(m_position); + else + slotAddKeyframe(m_position); +} + +void SimpleTimelineWidget::slotRemoveKeyframe(int pos) +{ + m_keyframes.removeAll(pos); + if (m_currentKeyframe == pos) + m_currentKeyframe = m_currentKeyframeOriginal = -1; + update(); + emit keyframeRemoved(pos); +} + +void SimpleTimelineWidget::setRange(int min, int max) +{ + m_min = min; + m_max = max; +} + +void SimpleTimelineWidget::slotGoToNext() +{ + foreach (const int &keyframe, m_keyframes) { + if (keyframe > m_position) { + slotSetPosition(keyframe); + emit positionChanged(keyframe); + return; + } + } + + // no keyframe after current position + slotSetPosition(m_max); + emit positionChanged(m_max); +} + +void SimpleTimelineWidget::slotGoToPrev() +{ + for (int i = m_keyframes.count() - 1; i >= 0; --i) { + if (m_keyframes.at(i) < m_position) { + slotSetPosition(m_keyframes.at(i)); + emit positionChanged(m_keyframes.at(i)); + return; + } + } + + // no keyframe before current position + slotSetPosition(m_min); + emit positionChanged(m_min); +} + +void SimpleTimelineWidget::mousePressEvent(QMouseEvent* event) +{ + if (qAbs(event->y() - m_lineHeight) < m_lineHeight / 5. && event->button() == Qt::LeftButton) { + int pos = (event->x() - 5) / m_scale; + foreach(const int &keyframe, m_keyframes) { + if (qAbs(keyframe - pos) < 5) { + m_currentKeyframeOriginal = keyframe; + m_currentKeyframe = pos; + update(); + return; + } + } + } + + // no keyframe next to mouse + m_currentKeyframe = m_currentKeyframeOriginal = -1; + m_position = (event->x() - 5) / m_scale; + emit positionChanged(m_position); + update(); +} + +void SimpleTimelineWidget::mouseMoveEvent(QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) { + int pos = qBound(m_min, (int)((event->x() - 5) / m_scale), m_max); + if (m_currentKeyframe >= 0) { + m_currentKeyframe = pos; + emit keyframeMoving(m_currentKeyframeOriginal, m_currentKeyframe); + } else { + m_position = pos; + emit positionChanged(pos); + } + update(); + return; + } + + // cursor +} + +void SimpleTimelineWidget::mouseReleaseEvent(QMouseEvent* event) +{ + if (m_currentKeyframe > 0) { + emit keyframeMoved(m_currentKeyframeOriginal, m_currentKeyframe); + } +} + +void SimpleTimelineWidget::wheelEvent(QWheelEvent* event) +{ + int change = event->delta() < 0 ? -1 : 1; + if (m_currentKeyframe > 0) { + m_currentKeyframe = qBound(m_min, m_currentKeyframe + change, m_max); + emit keyframeMoved(m_currentKeyframeOriginal, m_currentKeyframe); + } else { + m_position = qBound(m_min, m_position + change, m_max); + emit positionChanged(m_position); + } + update(); +} + +void SimpleTimelineWidget::paintEvent(QPaintEvent* event) +{ + QPainter p(this); + int min = 5; + int max = width() - 6; + m_scale = (max - min) / (double)(m_max - m_min); + p.translate(min, m_lineHeight); + + p.setPen(QPen(palette().foreground().color(), 1, Qt::SolidLine)); + + /* + * Time-"line" + */ + p.drawLine(0, 0, max, 0); + + /* + * current position + */ + p.fillRect(QRectF(-1, -10, 3, 20).translated(m_position * m_scale, 0), QBrush(palette().foreground().color(), Qt::SolidPattern)); + + /* + * keyframes + */ + p.setPen(QPen(palette().highlight().color(), 1, Qt::SolidLine)); + p.setBrush(Qt::NoBrush); + QPolygonF keyframe = QPolygonF() << QPointF(0, -4) << QPointF(-4, 0) << QPointF(0, 4) << QPointF(4, 0); + QPolygonF tmp; + foreach (const int &pos, m_keyframes) { + tmp = keyframe; + tmp.translate(pos * m_scale, 0); + if (pos == m_currentKeyframe) + p.setBrush(QBrush(palette().highlight().color(), Qt::SolidPattern)); + + p.drawConvexPolygon(tmp); + + if (pos == m_currentKeyframe) + p.setBrush(Qt::NoBrush); + } +} + +#include "simpletimelinewidget.moc" diff --git a/src/simplekeyframes/simpletimelinewidget.h b/src/simplekeyframes/simpletimelinewidget.h new file mode 100644 index 00000000..864b0283 --- /dev/null +++ b/src/simplekeyframes/simpletimelinewidget.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2011 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#ifndef SIMPLETIMELINEWIDGET_H +#define SIMPLETIMELINEWIDGET_H + +#include +#include + +class SimpleTimelineWidget : public QWidget +{ + Q_OBJECT + +public: + SimpleTimelineWidget(QWidget* parent = 0); + void setKeyframes(QList keyframes); + void setRange(int min, int max); + +public slots: + void slotSetPosition(int pos); + void slotRemoveKeyframe(int pos); + void slotAddKeyframe(int pos = - 1, int select = false); + void slotAddRemove(); + void slotGoToNext(); + void slotGoToPrev(); + +protected: + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void wheelEvent(QWheelEvent *event); + +private: + int m_min; + int m_max; + int m_position; + int m_currentKeyframe; + int m_currentKeyframeOriginal; + QList m_keyframes; + int m_lineHeight; + double m_scale; + +signals: + void positionChanged(int pos); + + void keyframeSelected(); + void keyframeMoving(int oldPos, int currentPos); + void keyframeMoved(int oldPos, int newPos); + void keyframeAdded(int pos); + void keyframeRemoved(int pos); +}; + +#endif