]> git.sesse.net Git - kdenlive/commitdiff
rotoscoping:
authorTill Theato <root@ttill.de>
Sun, 13 Feb 2011 15:15:39 +0000 (15:15 +0000)
committerTill Theato <root@ttill.de>
Sun, 13 Feb 2011 15:15:39 +0000 (15:15 +0000)
- keyframe support (WIP)
- obey to direct update setting for on-monitor items

svn path=/trunk/kdenlive/; revision=5400

13 files changed:
src/CMakeLists.txt
src/effectstackedit.cpp
src/geometrywidget.cpp
src/onmonitoritems/rotoscoping/bpointitem.cpp
src/onmonitoritems/rotoscoping/splineitem.cpp
src/onmonitoritems/rotoscoping/splineitem.h
src/rotoscoping/rotowidget.cpp
src/rotoscoping/rotowidget.h
src/simplekeyframes/CMakeLists.txt [new file with mode: 0644]
src/simplekeyframes/simplekeyframewidget.cpp [new file with mode: 0644]
src/simplekeyframes/simplekeyframewidget.h [new file with mode: 0644]
src/simplekeyframes/simpletimelinewidget.cpp [new file with mode: 0644]
src/simplekeyframes/simpletimelinewidget.h [new file with mode: 0644]

index ef477b7ed9ccc85451e95dec47519b8322ff7385..817fb835807743246d354e955c247d1f60577398 100644 (file)
@@ -278,6 +278,8 @@ set(kdenlive_SRCS
   beziercurve/cubicbezierspline.cpp
   dragvalue.cpp
   monitoreditwidget.cpp
+  simplekeyframes/simpletimelinewidget.cpp
+  simplekeyframes/simplekeyframewidget.cpp
 )
 
 add_definitions(${KDE4_DEFINITIONS})
index fe62a739fc22442e33af06d48da7464c87a0382e..c1f81bee94d2726a6ca68615a727039268d415f1 100644 (file)
@@ -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);
index bb9fa8a24e8d385d2e15c14bed405b45153deb31..75c4d0fd45ee94f58a8a41f7f2cc63435485d725 100644 (file)
@@ -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);
     }
index 4129a17d7a925758dcd57b22218ce5a88f2c7733..0b29097df8af22111aff187d771ef0137ee456ad 100644 (file)
@@ -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<SplineItem*>(parentItem());
         if (parent)
-            parent->updateSpline();
+            parent->updateSpline(true);
     }
 }
 
 void BPointItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
 {
-QGraphicsItem::mouseReleaseEvent(event);
+    if (parentItem()) {
+        SplineItem *parent = qgraphicsitem_cast<SplineItem*>(parentItem());
+        if (parent)
+            parent->updateSpline(false);
+    }
+    QGraphicsItem::mouseReleaseEvent(event);
 }
 
 void BPointItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
index 64eedb03c347190cd293bef18b15b3f8abd87f5b..5dbe00fe9534240828761499becd89419486a38c 100644 (file)
@@ -19,6 +19,7 @@
 #include "splineitem.h"
 #include "bpointitem.h"
 #include "nearestpoint.h"
+#include "kdenlivesettings.h"
 
 #include <QGraphicsScene>
 #include <QCursor>
@@ -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<BPointItem *>(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 <BPoint> SplineItem::getPoints()
@@ -108,6 +104,29 @@ QList <BPoint> 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) {
index f2e7562a40a7d6cd7ce2951600e948a789264b80..4b3599d4b4792ecf6688197461d498967e23d83a 100644 (file)
@@ -35,8 +35,11 @@ public:
 
     virtual int type() const;
 
-    void updateSpline();
+    bool editing();
+
+    void updateSpline(bool editing = false);
     QList <BPoint> getPoints();
+    void setPoints(const QList <BPoint> &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
index e755dd23e46cb5b3324ebf11c575fdb185b80c24..b58d370f3908301d99e9f15e70c393c3997c934e 100644 (file)
 #include "monitoreditwidget.h"
 #include "onmonitoritems/rotoscoping/bpointitem.h"
 #include "onmonitoritems/rotoscoping/splineitem.h"
+#include "simplekeyframes/simplekeyframewidget.h"
+#include "kdenlivesettings.h"
 
 #include <qjson/parser.h>
 #include <qjson/serializer.h>
 
+#include <QVBoxLayout>
 
-RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, QWidget* parent) :
+#include <KDebug>
+
+
+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 <BPoint> points;
-    foreach (const QVariant &bpoint, m_data.toList()) {
-        QList <QVariant> 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 <int> keyframes;
+        QMap <QString, QVariant> map = m_data.toMap();
+        QMap <QString, QVariant>::const_iterator i = map.constBegin();
+        while (i != map.constEnd()) {
+            keyframes.append(i.key().toInt());
+            ++i;
+        }
+        m_keyframeWidget->setKeyframes(keyframes);
+    } else {
+        m_keyframeWidget->setKeyframes(QList <int>() << 0);
     }
 
-    m_item = new SplineItem(points, NULL, m_scene);
+    m_item = new SplineItem(QList <BPoint>(), 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>() << QVariant(point[i].x() / width) << QVariant(point[i].y() / height));
         vlist << QVariant(pl);
     }
-    m_data = QVariant(vlist);
+
+    if (m_data.canConvert(QVariant::Map)) {
+        QMap <QString, QVariant> 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 <QString, QVariant> map = m_data.toMap();
+        QMap <QString, QVariant>::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 <BPoint> p1 = getPoints(keyframe1);
+            QList <BPoint> p2 = getPoints(keyframe2);
+            QList <BPoint> 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 <BPoint> RotoWidget::getPoints(int keyframe)
+{
+    int width = m_monitor->render->frameRenderWidth();
+    int height = m_monitor->render->renderHeight();
+    QList <BPoint> points;
+    QList <QVariant> data;
+    if (keyframe >= 0)
+        data = m_data.toMap()[QString::number(keyframe)].toList();
+    else
+        data = m_data.toList();
+    foreach (const QVariant &bpoint, data) {
+        QList <QVariant> 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<QString, QVariant> 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"
index 3ac9fa0ac15550be39bb004985cd5d580fa34646..668e3e21734fb7e26d9deb7c69a7a2a0367c88ef 100644 (file)
 #define ROTOWIDGET_H
 
 #include "bpoint.h"
+#include "timecode.h"
 
 #include <QWidget>
 
 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 <BPoint> 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 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/simplekeyframes/simplekeyframewidget.cpp b/src/simplekeyframes/simplekeyframewidget.cpp
new file mode 100644 (file)
index 0000000..47ad20e
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.     *
+ ***************************************************************************/
+
+#include "simplekeyframes/simplekeyframewidget.h"
+#include "simpletimelinewidget.h"
+#include "timecodedisplay.h"
+
+#include <QToolButton>
+#include <QGridLayout>
+
+#include <KIcon>
+#include <KLocale>
+
+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 (file)
index 0000000..f4c45bb
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.     *
+ ***************************************************************************/
+
+#ifndef SIMPLEKEYFRAMEWIDGET_H
+#define SIMPLEKEYFRAMEWIDGET_H
+
+#include "timecode.h"
+
+#include <QtCore>
+#include <QWidget>
+
+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 <int> &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 (file)
index 0000000..f562186
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.     *
+ ***************************************************************************/
+
+#include "simpletimelinewidget.h"
+
+#include <QPainter>
+#include <QMouseEvent>
+
+
+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 <int> 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 (file)
index 0000000..864b028
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.     *
+ ***************************************************************************/
+
+#ifndef SIMPLETIMELINEWIDGET_H
+#define SIMPLETIMELINEWIDGET_H
+
+#include <QtCore>
+#include <QWidget>
+
+class SimpleTimelineWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    SimpleTimelineWidget(QWidget* parent = 0);
+    void setKeyframes(QList <int> 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 <int> 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