]> git.sesse.net Git - kdenlive/blobdiff - src/effectstackedit.cpp
Fix last keyframe for composite transition (and other geometry effects) inserted...
[kdenlive] / src / effectstackedit.cpp
index a7a220b53118c3671a9787771701bcbef6d9cab7..3d4a481f81791c64b5c73849b51168b5d46a0774 100644 (file)
@@ -82,7 +82,7 @@ EffectStackEdit::EffectStackEdit(Monitor *monitor, QWidget *parent) :
     m_geometryWidget(NULL)
 {
     m_baseWidget = new QWidget(this);
-    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
     setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
     setFrameStyle(QFrame::NoFrame);
     setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding));
@@ -122,9 +122,9 @@ void EffectStackEdit::setFrameSize(QPoint p)
     QDomNodeList namenode = m_params.elementsByTagName("parameter");
     for (int i = 0; i < namenode.count() ; i++) {
         QDomNode pa = namenode.item(i);
-        QDomNode na = pa.firstChildElement("name");
+        QDomElement na = pa.firstChildElement("name");
         QString type = pa.attributes().namedItem("type").nodeValue();
-        QString paramName = i18n(na.toElement().text().toUtf8().data());
+        QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
 
         if (type == "geometry") {
             if (!KdenliveSettings::on_monitor_effects()) {
@@ -148,9 +148,9 @@ void EffectStackEdit::updateTimecodeFormat()
     QDomNodeList namenode = m_params.elementsByTagName("parameter");
     for (int i = 0; i < namenode.count() ; i++) {
         QDomNode pa = namenode.item(i);
-        QDomNode na = pa.firstChildElement("name");
+        QDomElement na = pa.firstChildElement("name");
         QString type = pa.attributes().namedItem("type").nodeValue();
-        QString paramName = i18n(na.toElement().text().toUtf8().data());
+        QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
 
         if (type == "geometry") {
             if (KdenliveSettings::on_monitor_effects()) {
@@ -160,11 +160,13 @@ void EffectStackEdit::updateTimecodeFormat()
                 geom->updateTimecodeFormat();
             }
             break;
-        }
-        if (type == "position") {
+        } else if (type == "position") {
             PositionEdit *posi = ((PositionEdit*)m_valueItems[paramName+"position"]);
             posi->updateTimecodeFormat();
             break;
+        } else if (type == "roto-spline") {
+            RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems[paramName]);
+            widget->updateTimecodeFormat();
         }
     }
 }
@@ -203,14 +205,14 @@ void EffectStackEdit::updateParameter(const QString &name, const QString &value)
     }
 }
 
-void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, int out, bool isEffect)
+void EffectStackEdit::transferParamDesc(const QDomElement d, ItemInfo info, bool isEffect)
 {
     clearAllItems();
     if (m_keyframeEditor) delete m_keyframeEditor;
     m_keyframeEditor = NULL;
     m_params = d;
-    m_in = in;
-    m_out = out;
+    m_in = isEffect ? info.cropStart.frames(KdenliveSettings::project_fps()) : info.startPos.frames(KdenliveSettings::project_fps());
+    m_out = isEffect ? (info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1 : info.endPos.frames(KdenliveSettings::project_fps());
     if (m_params.isNull()) {
 //         kDebug() << "// EMPTY EFFECT STACK";
         return;
@@ -231,8 +233,10 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in
     }
 #endif
     QDomElement e = m_params.toElement();
-    const int minFrame = e.attribute("start").toInt();
-    const int maxFrame = e.attribute("end").toInt();
+    int minFrame = e.attribute("start").toInt();
+    int maxFrame = e.attribute("end").toInt();
+    // In transitions, maxFrame is in fact one frame after the end of transition
+    if (maxFrame > 0) maxFrame --;
 
     bool disable = d.attribute("disable") == "1" && KdenliveSettings::disable_effect_parameters();
     setEnabled(!disable);
@@ -245,7 +249,7 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in
         QDomElement na = pa.firstChildElement("name");
         QDomElement commentElem = pa.firstChildElement("comment");
         QString type = pa.attribute("type");
-        QString paramName = i18n(na.text().toUtf8().data());
+        QString paramName = na.isNull() ? pa.attribute("name") : i18n(na.text().toUtf8().data());
         QString comment;
         if (!commentElem.isNull())
             comment = i18n(commentElem.text().toUtf8().data());
@@ -253,36 +257,26 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in
         QString value = pa.attribute("value").isNull() ?
                         pa.attribute("default") : pa.attribute("value");
 
-        /** Currently supported parameter types are:
-            * constant (=double): a slider with an integer value (use the "factor" attribute to divide the value so that you can get a double
-            * list: a combobox containing a list of values to choose
-            * bool: a checkbox
-            * complex: designed for keyframe parameters, but old and not finished, do not use
-            * geometry: a rectangle that can be moved & resized, with possible keyframes, used in composite transition
-            * keyframe: a list widget with a list of entries (position and value)
-            * color: a color chooser button
-            * position: a slider representing the position of a frame in the current clip
-            * curve: a single curve representing multiple points
-            * wipe: a widget designed for the wipe transition, allowing to choose a position (left, right, top,...)
-        */
+
+        /** See effects/README for info on the different types */
 
         if (type == "double" || type == "constant") {
-            int min;
-            int max;
-            if (pa.attribute("min").startsWith('%'))
-                min = (int) ProfilesDialog::getStringEval(m_profile, pa.attribute("min"));
+            double min;
+            double max;
+            if (pa.attribute("min").contains('%'))
+                min = ProfilesDialog::getStringEval(m_profile, pa.attribute("min"), m_frameSize);
             else
-                min = pa.attribute("min").toInt();
-            if (pa.attribute("max").startsWith('%'))
-                max = (int) ProfilesDialog::getStringEval(m_profile, pa.attribute("max"));
+                min = pa.attribute("min").toDouble();
+            if (pa.attribute("max").contains('%'))
+                max = ProfilesDialog::getStringEval(m_profile, pa.attribute("max"), m_frameSize);
             else
-                max = pa.attribute("max").toInt();
+                max = pa.attribute("max").toDouble();
 
-            DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, (int)(value.toDouble() + 0.5), min, max,
-                    pa.attribute("default").toInt(), comment, -1, pa.attribute("suffix"), this);
+            DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, value.toDouble(), min, max,
+                    pa.attribute("default").toDouble(), comment, -1, pa.attribute("suffix"), pa.attribute("decimals").toInt(), this);
             m_vbox->addWidget(doubleparam);
             m_valueItems[paramName] = doubleparam;
-            connect(doubleparam, SIGNAL(valueChanged(int)), this, SLOT(collectAllParameters()));
+            connect(doubleparam, SIGNAL(valueChanged(double)), this, SLOT(collectAllParameters()));
             connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
         } else if (type == "list") {
             Listval *lsval = new Listval;
@@ -335,7 +329,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()) {
-                m_geometryWidget = new GeometryWidget(m_monitor, m_timecode, pos, isEffect, m_params.hasAttribute("showrotation"), this);
+                m_geometryWidget = new GeometryWidget(m_monitor, m_timecode, isEffect ? 0 : qMax(0, (int)info.startPos.frames(KdenliveSettings::project_fps())), isEffect, m_params.hasAttribute("showrotation"), this);
                 m_geometryWidget->setFrameSize(m_frameSize);
                 m_geometryWidget->slotShowScene(!disable);
                 // connect this before setupParam to make sure the monitor scene shows up at startup
@@ -351,7 +345,7 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in
                 connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
                 connect(this, SIGNAL(effectStateChanged(bool)), m_geometryWidget, SLOT(slotShowScene(bool)));
             } else {
-                Geometryval *geo = new Geometryval(m_profile, m_timecode, m_frameSize, pos);
+                Geometryval *geo = new Geometryval(m_profile, m_timecode, m_frameSize, isEffect ? 0 : qMax(0, (int)info.startPos.frames(KdenliveSettings::project_fps())));
                 if (minFrame == maxFrame)
                     geo->setupParam(pa, m_in, m_out);
                 else
@@ -371,14 +365,14 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in
                 KeyframeEdit *geo;
                 if (pa.attribute("widget") == "corners") {
                     // we want a corners-keyframe-widget
-                    CornersWidget *corners = new CornersWidget(m_monitor, pa, m_in, m_in + m_out, m_timecode, e.attribute("active_keyframe", "-1").toInt(), this);
+                    CornersWidget *corners = new CornersWidget(m_monitor, pa, m_in, m_out, m_timecode, e.attribute("active_keyframe", "-1").toInt(), this);
                     corners->slotShowScene(!disable);
                     connect(corners, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
                     connect(this, SIGNAL(effectStateChanged(bool)), corners, SLOT(slotShowScene(bool)));
                     connect(this, SIGNAL(syncEffectsPos(int)), corners, SLOT(slotSyncPosition(int)));
                     geo = static_cast<KeyframeEdit *>(corners);
                 } else {
-                    geo = new KeyframeEdit(pa, m_in, m_in + m_out, m_timecode, e.attribute("active_keyframe", "-1").toInt());
+                    geo = new KeyframeEdit(pa, m_in, m_out, m_timecode, e.attribute("active_keyframe", "-1").toInt());
                 }
                 m_vbox->addWidget(geo);
                 m_valueItems[paramName+"keyframe"] = geo;
@@ -454,7 +448,7 @@ 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, m_timecode, this);
+            RotoWidget *roto = new RotoWidget(value, m_monitor, info, m_timecode, this);
             roto->slotShowScene(!disable);
             connect(roto, SIGNAL(valueChanged()), this, SLOT(collectAllParameters()));
             connect(roto, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
@@ -650,9 +644,9 @@ void EffectStackEdit::collectAllParameters()
 
     for (int i = 0; i < namenode.count() ; i++) {
         QDomNode pa = namenode.item(i);
-        QDomNode na = pa.firstChildElement("name");
+        QDomElement na = pa.firstChildElement("name");
         QString type = pa.attributes().namedItem("type").nodeValue();
-        QString paramName = i18n(na.toElement().text().toUtf8().data());
+        QString paramName = na.isNull() ? pa.attributes().namedItem("name").nodeValue() : i18n(na.text().toUtf8().data());
         if (type == "complex")
             paramName.append("complex");
         else if (type == "position")