parametercontainer.cpp 63.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/***************************************************************************
 *   Copyright (C) 2012 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 *   This program 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 this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

Vincent Pinon's avatar
Vincent Pinon committed
20 21
#include "parametercontainer.h"

Vincent Pinon's avatar
Vincent Pinon committed
22 23 24 25 26 27 28 29 30 31
#include "geometryval.h"
#include "positionedit.h"
#include "dragvalue.h"
#include "widgets/kis_curve_widget.h"
#include "widgets/kis_cubic_curve.h"
#include "widgets/choosecolorwidget.h"
#include "widgets/geometrywidget.h"
#include "widgets/doubleparameterwidget.h"
#include "widgets/cornerswidget.h"
#include "widgets/bezier/beziersplinewidget.h"
32
#include "effectstack/widgets/lumaliftgain.h"
33
#include "effectstack/widgets/animationwidget.h"
Vincent Pinon's avatar
Vincent Pinon committed
34 35 36 37

#include "kdenlivesettings.h"
#include "mainwindow.h"
#include "colortools.h"
38
#include "dialogs/clipcreationdialog.h"
39
#include "mltcontroller/effectscontroller.h"
40
#include "utils/KoIconUtils.h"
Vincent Pinon's avatar
Vincent Pinon committed
41 42
#include "onmonitoritems/rotoscoping/rotowidget.h"

43 44 45 46 47 48 49 50
#include "ui_listval_ui.h"
#include "ui_boolval_ui.h"
#include "ui_wipeval_ui.h"
#include "ui_urlval_ui.h"
#include "ui_keywordval_ui.h"
#include "ui_fontval_ui.h"

#include <KUrlRequester>
51
#include "klocalizedstring.h"
52 53 54 55

#include <QMap>
#include <QString>
#include <QImage>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
56
#include <QDebug>
57 58 59 60 61 62 63
#include <QClipboard>
#include <QDrag>
#include <QMimeData>


DraggableLabel::DraggableLabel(const QString &text, QWidget *parent):
    QLabel(text, parent)
64
    , m_dragStarted(false)
65 66 67 68 69 70 71 72 73 74
{
    setContextMenuPolicy(Qt::NoContextMenu);
    setToolTip(i18n("Click to copy data to clipboard"));
}

void DraggableLabel::mousePressEvent(QMouseEvent *ev)
{
    QLabel::mousePressEvent(ev);
    if (ev->button() == Qt::LeftButton) {
        m_clickStart = ev->pos();
75
        m_dragStarted = false;
76 77 78 79 80
    }
}

void DraggableLabel::mouseReleaseEvent(QMouseEvent *ev)
{
81 82 83
    // Don't call mouserelease in cas of drag because label might be deleted by a drop
    if (!m_dragStarted)
        QLabel::mouseReleaseEvent(ev);
84
    else
85
        ev->ignore();
86 87 88 89 90
    m_clickStart = QPoint();
}

void DraggableLabel::mouseMoveEvent(QMouseEvent *ev)
{
91 92 93 94
    if (m_dragStarted) {
        ev->ignore();
        return;
    }
95 96 97
    QLabel::mouseMoveEvent(ev);
    if (!m_clickStart.isNull() && (m_clickStart - ev->pos()).manhattanLength() >= QApplication::startDragDistance()) {
        emit startDrag(objectName());
98
        m_dragStarted = true;
99 100 101
        m_clickStart = QPoint();
    }
}
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

MySpinBox::MySpinBox(QWidget * parent):
    QSpinBox(parent)
{
    setFocusPolicy(Qt::StrongFocus);
}

void MySpinBox::focusInEvent(QFocusEvent *e)
{
     setFocusPolicy(Qt::WheelFocus);
     e->accept();
}

void MySpinBox::focusOutEvent(QFocusEvent *e)
{
     setFocusPolicy(Qt::StrongFocus);
     e->accept();
}

class Boolval: public QWidget, public Ui::Boolval_UI
{
};

class Listval: public QWidget, public Ui::Listval_UI
{
};

class Wipeval: public QWidget, public Ui::Wipeval_UI
{
};

class Urlval: public QWidget, public Ui::Urlval_UI
{
};

class Keywordval: public QWidget, public Ui::Keywordval_UI
{
};

class Fontval: public QWidget, public Ui::Fontval_UI
{
};


146
ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo &info, EffectMetaInfo *metaInfo, QWidget * parent) :
147
        m_info(info),
148 149
        m_keyframeEditor(NULL),
        m_geometryWidget(NULL),
150
        m_animationWidget(NULL),
151
        m_metaInfo(metaInfo),
152
        m_effect(effect),
153
        m_acceptDrops(false),
154 155
        m_monitorEffectScene(MonitorSceneDefault),
        m_conditionParameter(false)
156
{
157 158
    QLocale locale;
    locale.setNumberOptions(QLocale::OmitGroupSeparator);
159
    setObjectName(QStringLiteral("ParameterContainer"));
160

161 162 163
    m_in = info.cropStart.frames(KdenliveSettings::project_fps());
    m_out = (info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1;

164
    QDomNodeList namenode = effect.childNodes();
165
    QDomElement e = effect.toElement();
166

167 168
    int minFrame = e.attribute(QStringLiteral("start")).toInt();
    int maxFrame = e.attribute(QStringLiteral("end")).toInt();
169 170 171
    // In transitions, maxFrame is in fact one frame after the end of transition
    if (maxFrame > 0) maxFrame --;

172
    bool disable = effect.attribute(QStringLiteral("disable")) == QLatin1String("1") && KdenliveSettings::disable_effect_parameters();
173 174 175 176 177 178
    parent->setEnabled(!disable);

    bool stretch = true;
    m_vbox = new QVBoxLayout(parent);
    m_vbox->setContentsMargins(4, 0, 4, 0);
    m_vbox->setSpacing(2);
179 180 181 182 183
    /*
    QDomElement clone = effect.cloneNode(true).toElement();
    QDomDocument doc;
    doc.appendChild(doc.importNode(clone, true));
    qDebug()<<"-------------------------------------\n"<<"LOADED TRANS: "<<doc.toString()<<"\n-------------------";*/
184 185 186 187 188 189 190 191

    // Conditional effect (display / enable some parameters only if a parameter is present
    if (effect.hasAttribute(QStringLiteral("condition"))) {
        QString condition = effect.attribute(QStringLiteral("condition"));
        QString conditionParam = EffectsList::parameter(effect, condition);
        m_conditionParameter = !conditionParam.isEmpty();
    }

192
    if (effect.attribute(QStringLiteral("id")) == QLatin1String("movit.lift_gamma_gain") || effect.attribute(QStringLiteral("id")) == QLatin1String("lift_gamma_gain") ) {
193 194 195
        // We use a special custom widget here
        LumaLiftGain *gainWidget = new LumaLiftGain(namenode, parent);
        m_vbox->addWidget(gainWidget);
196
        m_valueItems[effect.attribute(QStringLiteral("id"))] = gainWidget;
197 198 199
        connect(gainWidget, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
    }
    else for (int i = 0; i < namenode.count() ; ++i) {
200
        QDomElement pa = namenode.item(i).toElement();
201 202 203 204 205 206 207 208 209 210 211
        if (pa.tagName() == QLatin1String("conditionalinfo")) {
            // Conditional info
            KMessageWidget *mw = new KMessageWidget;
            mw->setWordWrap(true);
            mw->setMessageType(KMessageWidget::Information);
            mw->setText(i18n(pa.text().toUtf8().data()));
            mw->setCloseButtonVisible(false);
            mw->setVisible(!m_conditionParameter);
            m_vbox->addWidget(mw);
            m_conditionalWidgets << mw;
        }
212 213 214 215 216
        if (pa.tagName() != QLatin1String("parameter")) continue;
        QDomElement na = pa.firstChildElement(QStringLiteral("name"));
        QDomElement commentElem = pa.firstChildElement(QStringLiteral("comment"));
        QString type = pa.attribute(QStringLiteral("type"));
        QString paramName = na.isNull() ? pa.attribute(QStringLiteral("name")) : i18n(na.text().toUtf8().data());
217 218 219 220
        QString comment;
        if (!commentElem.isNull())
            comment = i18n(commentElem.text().toUtf8().data());
        QWidget * toFillin = new QWidget(parent);
221 222
        QString value = pa.attribute(QStringLiteral("value")).isNull() ?
                        pa.attribute(QStringLiteral("default")) : pa.attribute(QStringLiteral("value"));
223 224 225


        /** See effects/README for info on the different types */
226
        if (type == QLatin1String("double") || type == QLatin1String("constant")) {
227 228
            double min;
            double max;
229
            m_acceptDrops = true;
230 231
            if (pa.attribute(QStringLiteral("min")).contains('%'))
                min = EffectsController::getStringEval(m_metaInfo->monitor->profileInfo(), pa.attribute(QStringLiteral("min")), m_metaInfo->frameSize);
232
            else
233 234 235
                min = locale.toDouble(pa.attribute(QStringLiteral("min")));
            if (pa.attribute(QStringLiteral("max")).contains('%'))
                max = EffectsController::getStringEval(m_metaInfo->monitor->profileInfo(), pa.attribute(QStringLiteral("max")), m_metaInfo->frameSize);
236
            else
237
                max = locale.toDouble(pa.attribute(QStringLiteral("max")));
238

239
            DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, locale.toDouble(value), min, max,
240
                    locale.toDouble(pa.attribute(QStringLiteral("default"))), comment, -1, pa.attribute(QStringLiteral("suffix")), pa.attribute(QStringLiteral("decimals")).toInt(), false, parent);
241
	    doubleparam->setFocusPolicy(Qt::StrongFocus);
242 243 244 245
            if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                doubleparam->setEnabled(false);
                m_conditionalWidgets << doubleparam;
            }
246 247 248 249
            m_vbox->addWidget(doubleparam);
            m_valueItems[paramName] = doubleparam;
            connect(doubleparam, SIGNAL(valueChanged(double)), this, SLOT(slotCollectAllParameters()));
            connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
250
        } else if (type == QLatin1String("list")) {
251 252 253
            Listval *lsval = new Listval;
            lsval->setupUi(toFillin);
	    lsval->list->setFocusPolicy(Qt::StrongFocus);
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
            QString items = pa.attribute(QStringLiteral("paramlist"));
            QStringList listitems;
            if (items == QLatin1String("%lumaPaths")) {
                // Special case: Luma files
                // Create thumbnails
                lsval->list->setIconSize(QSize(50, 30));
                if (m_metaInfo->monitor->profileInfo().profileSize.width() > 1000) {
                    // HD project
                    listitems = MainWindow::m_lumaFiles.value(QStringLiteral("HD"));
                } else {
                    listitems = MainWindow::m_lumaFiles.value(QStringLiteral("PAL"));
                }
                lsval->list->addItem(i18n("None (Dissolve)"));
                for (int i = 0; i < listitems.count(); ++i) {
                    QString entry = listitems.at(i);
                    lsval->list->addItem(listitems.at(i).section(QStringLiteral("/"), -1), entry);
                    if (!entry.isEmpty() && (entry.endsWith(QLatin1String(".png")) || entry.endsWith(QLatin1String(".pgm")))) {
                        if (!MainWindow::m_lumacache.contains(entry)) {
                            QImage pix(entry);
                            MainWindow::m_lumacache.insert(entry, pix.scaled(50, 30, Qt::KeepAspectRatio, Qt::SmoothTransformation));
                        }
                        lsval->list->setItemIcon(i + 1, QPixmap::fromImage(MainWindow::m_lumacache.value(entry)));
276 277
                    }
                }
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
                lsval->list->setCurrentText(pa.attribute(QStringLiteral("default")));
                if (!value.isEmpty() && listitems.contains(value)) lsval->list->setCurrentIndex(listitems.indexOf(value) + 1);
            } else {
                listitems = items.split(';');
                if (listitems.count() == 1) {
                    // probably custom effect created before change to ';' as separator
                    listitems = pa.attribute(QStringLiteral("paramlist")).split(',');
                }
                QDomElement list = pa.firstChildElement(QStringLiteral("paramlistdisplay"));
                QStringList listitemsdisplay;
                if (!list.isNull()) {
                    listitemsdisplay = i18n(list.text().toUtf8().data()).split(',');
                } else {
                    listitemsdisplay = i18n(pa.attribute("paramlistdisplay").toUtf8().data()).split(',');
                }
                if (listitemsdisplay.count() != listitems.count())
                    listitemsdisplay = listitems;
295 296 297
                for (int i = 0; i < listitems.count(); ++i) {
                    lsval->list->addItem(listitemsdisplay.at(i), listitems.at(i));
                }
298
                if (!value.isEmpty() && listitems.contains(value)) lsval->list->setCurrentIndex(listitems.indexOf(value));
299 300
            }
            lsval->name->setText(paramName);
301
	    lsval->setToolTip(comment);
302 303
            lsval->labelComment->setText(comment);
            lsval->widgetComment->setHidden(true);
304 305 306 307
            if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                lsval->list->setEnabled(false);
                m_conditionalWidgets << lsval->list;
            }
308 309 310 311 312
            m_valueItems[paramName] = lsval;
            connect(lsval->list, SIGNAL(currentIndexChanged(int)) , this, SLOT(slotCollectAllParameters()));
            if (!comment.isEmpty())
                connect(this, SIGNAL(showComments(bool)), lsval->widgetComment, SLOT(setVisible(bool)));
            m_uiItems.append(lsval);
313
        } else if (type == QLatin1String("bool")) {
314 315
            Boolval *bval = new Boolval;
            bval->setupUi(toFillin);
316
            bval->checkBox->setCheckState(value == QLatin1String("0") ? Qt::Unchecked : Qt::Checked);
317
            bval->name->setText(paramName);
318
	    bval->name->setToolTip(comment);
319 320
            bval->labelComment->setText(comment);
            bval->widgetComment->setHidden(true);
321 322 323 324
            if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                bval->setEnabled(false);
                m_conditionalWidgets << bval;
            }
325 326 327 328 329
            m_valueItems[paramName] = bval;
            connect(bval->checkBox, SIGNAL(stateChanged(int)) , this, SLOT(slotCollectAllParameters()));
            if (!comment.isEmpty())
                connect(this, SIGNAL(showComments(bool)), bval->widgetComment, SLOT(setVisible(bool)));
            m_uiItems.append(bval);
330 331 332 333 334 335 336 337
        } else if (type == QLatin1String("switch")) {
            Boolval *bval = new Boolval;
            bval->setupUi(toFillin);
            bval->checkBox->setCheckState(value == pa.attribute("min") ? Qt::Unchecked : Qt::Checked);
            bval->name->setText(paramName);
            bval->name->setToolTip(comment);
            bval->labelComment->setText(comment);
            bval->widgetComment->setHidden(true);
338 339 340 341
            if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                bval->setEnabled(false);
                m_conditionalWidgets << bval;
            }
342 343 344 345 346
            m_valueItems[paramName] = bval;
            connect(bval->checkBox, SIGNAL(stateChanged(int)) , this, SLOT(slotCollectAllParameters()));
            if (!comment.isEmpty())
                connect(this, SIGNAL(showComments(bool)), bval->widgetComment, SLOT(setVisible(bool)));
            m_uiItems.append(bval);
347
        } else if (type.startsWith(QLatin1String("animated"))) {
348
            m_acceptDrops = true;
349 350 351
            if (type == QLatin1String("animatedrect")) {
                m_monitorEffectScene = MonitorSceneGeometry;
            }
352 353 354
            if (m_animationWidget) {
                m_animationWidget->addParameter(pa);
            } else {
355
                m_animationWidget = new AnimationWidget(m_metaInfo, info.startPos.frames(KdenliveSettings::project_fps()), m_in, m_out, effect.attribute(QStringLiteral("in")).toInt(), effect.attribute(QStringLiteral("id")), pa, parent);
356 357
                connect(m_animationWidget, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
                connect(this, SIGNAL(syncEffectsPos(int)), m_animationWidget, SLOT(slotSyncPosition(int)));
358
                connect(this, SIGNAL(initScene(int)), m_animationWidget, SLOT(slotPositionChanged(int)));
359 360
                connect(m_animationWidget, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
                m_vbox->addWidget(m_animationWidget);
361 362 363 364
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                    m_animationWidget->setEnabled(false);
                    m_conditionalWidgets << m_animationWidget;
                }
365
            }
366
        } else if (type == QLatin1String("geometry")) {
367
            m_acceptDrops = true;
368
            if (true) {
369
                m_monitorEffectScene = MonitorSceneGeometry;
370 371 372 373 374
                bool useOffset = false;
                if (effect.tagName() == QLatin1String("effect") && effect.hasAttribute(QStringLiteral("kdenlive:sync_in_out")) && effect.attribute(QStringLiteral("kdenlive:sync_in_out")).toInt() == 0) {
                    useOffset = true;
                }
                m_geometryWidget = new GeometryWidget(m_metaInfo, info.startPos.frames(KdenliveSettings::project_fps()), effect.hasAttribute(QStringLiteral("showrotation")), useOffset, parent);
375 376 377 378
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                    m_geometryWidget->setEnabled(false);
                    m_conditionalWidgets << m_geometryWidget;
                }
379
                connect(m_geometryWidget, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
380
                if (minFrame == maxFrame) {
381
                    m_geometryWidget->setupParam(pa, m_in, m_out);
382 383
                    connect(this, SIGNAL(updateRange(int,int)), m_geometryWidget, SLOT(slotUpdateRange(int,int)));
                }
384 385 386 387 388
                else
                    m_geometryWidget->setupParam(pa, minFrame, maxFrame);
                m_vbox->addWidget(m_geometryWidget);
                m_valueItems[paramName+"geometry"] = m_geometryWidget;
                connect(m_geometryWidget, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
389
                connect(m_geometryWidget, SIGNAL(importClipKeyframes()), this, SIGNAL(importClipKeyframes()));
390
                connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
391
                connect(this, SIGNAL(initScene(int)), m_geometryWidget, SLOT(slotInitScene(int)));
392
                connect(this, &ParameterContainer::updateFrameInfo, m_geometryWidget, &GeometryWidget::setFrameSize);
393
            } else {
394
                Geometryval *geo = new Geometryval(m_metaInfo->monitor->profile(), m_metaInfo->monitor->timecode(), m_metaInfo->frameSize, 0);
395
                if (minFrame == maxFrame) {
396
                    geo->setupParam(pa, m_in, m_out);
397 398
		    connect(this, SIGNAL(updateRange(int,int)), geo, SLOT(slotUpdateRange(int,int)));
		}
399 400
                else
                    geo->setupParam(pa, minFrame, maxFrame);
401 402 403 404
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                    geo->setEnabled(false);
                    m_conditionalWidgets << geo;
                }
405 406 407 408 409 410
                m_vbox->addWidget(geo);
                m_valueItems[paramName+"geometry"] = geo;
                connect(geo, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
                connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
                connect(this, SIGNAL(syncEffectsPos(int)), geo, SLOT(slotSyncPosition(int)));
            }
411
        } else if (type == QLatin1String("addedgeometry")) {
412
            // this is a parameter that should be linked to the geometry widget, for example rotation, shear, ...
413 414 415
            if (m_geometryWidget) {
                m_geometryWidget->addParameter(pa);
            }
416
        } else if (type == QLatin1String("keyframe") || type == QLatin1String("simplekeyframe")) {
417 418 419
            // keyframe editor widget
            if (m_keyframeEditor == NULL) {
                KeyframeEdit *geo;
420
                if (pa.attribute(QStringLiteral("widget")) == QLatin1String("corners")) {
421
                    // we want a corners-keyframe-widget
422 423
                    int relativePos = (m_metaInfo->monitor->position() - info.startPos).frames(KdenliveSettings::project_fps());
                    CornersWidget *corners = new CornersWidget(m_metaInfo->monitor, pa, m_in, m_out, relativePos, m_metaInfo->monitor->timecode(), e.attribute(QStringLiteral("active_keyframe"), QStringLiteral("-1")).toInt(), parent);
424
		    connect(this, SIGNAL(updateRange(int,int)), corners, SLOT(slotUpdateRange(int,int)));
425
		    m_monitorEffectScene = MonitorSceneCorners;
426
                    connect(this, &ParameterContainer::updateFrameInfo, corners, &CornersWidget::setFrameSize);
427 428 429
                    connect(this, SIGNAL(syncEffectsPos(int)), corners, SLOT(slotSyncPosition(int)));
                    geo = static_cast<KeyframeEdit *>(corners);
                } else {
430
                    geo = new KeyframeEdit(pa, m_in, m_out, m_metaInfo->monitor->timecode(), e.attribute(QStringLiteral("active_keyframe"), QStringLiteral("-1")).toInt());
431
		    connect(this, SIGNAL(updateRange(int,int)), geo, SLOT(slotUpdateRange(int,int)));
432
                }
433 434 435 436
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                    geo->setEnabled(false);
                    m_conditionalWidgets << geo;
                }
437 438 439 440 441 442 443 444 445 446
                m_vbox->addWidget(geo);
                m_valueItems[paramName+"keyframe"] = geo;
                m_keyframeEditor = geo;
                connect(geo, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
                connect(geo, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
                connect(this, SIGNAL(showComments(bool)), geo, SIGNAL(showComments(bool)));
            } else {
                // we already have a keyframe editor, so just add another column for the new param
                m_keyframeEditor->addParameter(pa);
            }
447 448
        } else if (type == QLatin1String("color")) {
	    if (pa.hasAttribute(QStringLiteral("paramprefix"))) value.remove(0, pa.attribute(QStringLiteral("paramprefix")).size());
449
            if (value.startsWith('#'))
450 451
                value = value.replace('#', QLatin1String("0x"));
            ChooseColorWidget *choosecolor = new ChooseColorWidget(paramName, value, pa.hasAttribute(QStringLiteral("alpha")), parent);
452
	    choosecolor->setToolTip(comment);
453
            m_vbox->addWidget(choosecolor);
454 455 456 457
            if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                choosecolor->setEnabled(false);
                m_conditionalWidgets << choosecolor;
            }
458
            m_valueItems[paramName] = choosecolor;
Laurent Montel's avatar
Laurent Montel committed
459
            connect(choosecolor, SIGNAL(displayMessage(QString,int)), this, SIGNAL(displayMessage(QString,int)));
460
            connect(choosecolor, SIGNAL(modified()) , this, SLOT(slotCollectAllParameters()));
461
	    connect(choosecolor, SIGNAL(disableCurrentFilter(bool)) , this, SIGNAL(disableCurrentFilter(bool)));
462
        } else if (type == QLatin1String("position")) {
463
            int pos = value.toInt();
464
            if (effect.attribute(QStringLiteral("id")) == QLatin1String("fadein") || effect.attribute(QStringLiteral("id")) == QLatin1String("fade_from_black")) {
465
                pos = pos - m_in;
466
            } else if (effect.attribute(QStringLiteral("id")) == QLatin1String("fadeout") || effect.attribute(QStringLiteral("id")) == QLatin1String("fade_to_black")) {
467 468 469
                // fadeout position starts from clip end
                pos = m_out - pos;
            }
470
            PositionEdit *posedit = new PositionEdit(paramName, pos, 0, m_out - m_in, m_metaInfo->monitor->timecode());
471
	    posedit->setToolTip(comment);
472
	    connect(this, SIGNAL(updateRange(int,int)), posedit, SLOT(setRange(int,int)));
473 474 475 476
            if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                posedit->setEnabled(false);
                m_conditionalWidgets << posedit;
            }
477 478 479
            m_vbox->addWidget(posedit);
            m_valueItems[paramName+"position"] = posedit;
            connect(posedit, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
480
        } else if (type == QLatin1String("curve")) {
481
            KisCurveWidget *curve = new KisCurveWidget(parent);
482
            curve->setMaxPoints(pa.attribute(QStringLiteral("max")).toInt());
483 484
            QList<QPointF> points;
            int number;
485
            double version = 0;
486
            QDomElement namenode = effect.firstChildElement(QStringLiteral("version"));
487 488 489 490
            if (!namenode.isNull()) {
                version = locale.toDouble(namenode.text());
            }
	    if (version > 0.2) {
491
		// Rounding gives really weird results. (int) (10 * 0.3) gives 2! So for now, add 0.5 to get correct result
492
                number = locale.toDouble(EffectsList::parameter(e, pa.attribute(QStringLiteral("number")))) * 10 + 0.5;
493
            } else {
494
                number = EffectsList::parameter(e, pa.attribute(QStringLiteral("number"))).toInt();
495
            }
496 497 498
            QString inName = pa.attribute(QStringLiteral("inpoints"));
            QString outName = pa.attribute(QStringLiteral("outpoints"));
            int start = pa.attribute(QStringLiteral("min")).toInt();
499
            for (int j = start; j <= number; ++j) {
500
                QString in = inName;
501
                in.replace(QLatin1String("%i"), QString::number(j));
502
                QString out = outName;
503
                out.replace(QLatin1String("%i"), QString::number(j));
504
                points << QPointF(locale.toDouble(EffectsList::parameter(e, in)), locale.toDouble(EffectsList::parameter(e, out)));
505
            }
506
            if (!points.isEmpty()) {
507
                curve->setCurve(KisCubicCurve(points));
508
            }
509 510 511 512 513
            MySpinBox *spinin = new MySpinBox();
            spinin->setRange(0, 1000);
            MySpinBox *spinout = new MySpinBox();
            spinout->setRange(0, 1000);
            curve->setupInOutControls(spinin, spinout, 0, 1000);
514 515 516 517 518
            if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                curve->setEnabled(false);
                spinin->setEnabled(false);
                spinout->setEnabled(false);
            }
519 520 521 522 523 524 525
            m_vbox->addWidget(curve);
            m_vbox->addWidget(spinin);
            m_vbox->addWidget(spinout);

            connect(curve, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
            m_valueItems[paramName] = curve;

526
            QString depends = pa.attribute(QStringLiteral("depends"));
527 528
            if (!depends.isEmpty())
                meetDependency(paramName, type, EffectsList::parameter(e, depends));
529
        } else if (type == QLatin1String("bezier_spline")) {
530 531 532 533 534
            BezierSplineWidget *widget = new BezierSplineWidget(value, parent);
            stretch = false;
            m_vbox->addWidget(widget);
            m_valueItems[paramName] = widget;
            connect(widget, SIGNAL(modified()), this, SLOT(slotCollectAllParameters()));
535
            QString depends = pa.attribute(QStringLiteral("depends"));
536 537
            if (!depends.isEmpty())
                meetDependency(paramName, type, EffectsList::parameter(e, depends));
538
        } else if (type == QLatin1String("roto-spline")) {
539
            m_monitorEffectScene = MonitorSceneRoto;
540
            RotoWidget *roto = new RotoWidget(value.toLatin1(), m_metaInfo->monitor, info, m_metaInfo->monitor->timecode(), parent);
541 542 543 544 545
            connect(roto, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
            connect(roto, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
            connect(this, SIGNAL(syncEffectsPos(int)), roto, SLOT(slotSyncPosition(int)));
            m_vbox->addWidget(roto);
            m_valueItems[paramName] = roto;
546
        } else if (type == QLatin1String("wipe")) {
547 548
            Wipeval *wpval = new Wipeval;
            wpval->setupUi(toFillin);
549 550
            // Make sure button shows its pressed state even if widget loses focus
            QColor bg = QPalette().highlight().color();
551
            toFillin->setStyleSheet(QStringLiteral("QPushButton:checked {background-color:rgb(%1,%2,%3);}").arg(bg.red()).arg(bg.green()).arg(bg.blue()));
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
            wipeInfo w = getWipeInfo(value);
            switch (w.start) {
            case UP:
                wpval->start_up->setChecked(true);
                break;
            case DOWN:
                wpval->start_down->setChecked(true);
                break;
            case RIGHT:
                wpval->start_right->setChecked(true);
                break;
            case LEFT:
                wpval->start_left->setChecked(true);
                break;
            default:
                wpval->start_center->setChecked(true);
                break;
            }
            switch (w.end) {
            case UP:
                wpval->end_up->setChecked(true);
                break;
            case DOWN:
                wpval->end_down->setChecked(true);
                break;
            case RIGHT:
                wpval->end_right->setChecked(true);
                break;
            case LEFT:
                wpval->end_left->setChecked(true);
                break;
            default:
                wpval->end_center->setChecked(true);
                break;
            }
            wpval->start_transp->setValue(w.startTransparency);
            wpval->end_transp->setValue(w.endTransparency);
            m_valueItems[paramName] = wpval;
            connect(wpval->end_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->end_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->end_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->end_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->end_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->start_up, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->start_down, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->start_left, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->start_right, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->start_center, SIGNAL(clicked()), this, SLOT(slotCollectAllParameters()));
            connect(wpval->start_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
            connect(wpval->end_transp, SIGNAL(valueChanged(int)), this, SLOT(slotCollectAllParameters()));
            //wpval->title->setTitle(na.toElement().text());
            m_uiItems.append(wpval);
604
        } else if (type == QLatin1String("url")) {
605 606 607
            Urlval *cval = new Urlval;
            cval->setupUi(toFillin);
            cval->label->setText(paramName);
608
	    cval->setToolTip(comment);
609
            cval->urlwidget->setFilter(ClipCreationDialog::getExtensions().join(' '));
610
            m_valueItems[paramName] = cval;
611
            cval->urlwidget->setUrl(QUrl(value));
612
            connect(cval->urlwidget, SIGNAL(returnPressed()) , this, SLOT(slotCollectAllParameters()));
613
            connect(cval->urlwidget, SIGNAL(urlSelected(QUrl)) , this, SLOT(slotCollectAllParameters()));
614 615 616 617
            if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                cval->setEnabled(false);
                m_conditionalWidgets << cval;
            }
618
            m_uiItems.append(cval);
619
	} else if (type == QLatin1String("keywords")) {
620 621 622 623
            Keywordval* kval = new Keywordval;
            kval->setupUi(toFillin);
            kval->label->setText(paramName);
            kval->lineeditwidget->setText(value);
624
	    kval->setToolTip(comment);
625 626
            QDomElement klistelem = pa.firstChildElement(QStringLiteral("keywords"));
            QDomElement kdisplaylistelem = pa.firstChildElement(QStringLiteral("keywordsdisplay"));
627 628 629 630 631 632 633 634 635
            QStringList keywordlist;
            QStringList keyworddisplaylist;
            if (!klistelem.isNull()) {
                keywordlist = klistelem.text().split(';');
                keyworddisplaylist = i18n(kdisplaylistelem.text().toUtf8().data()).split(';');
            }
            if (keyworddisplaylist.count() != keywordlist.count()) {
                keyworddisplaylist = keywordlist;
            }
636
            for (int i = 0; i < keywordlist.count(); ++i) {
637 638 639 640 641 642 643
                kval->comboboxwidget->addItem(keyworddisplaylist.at(i), keywordlist.at(i));
            }
            // Add disabled user prompt at index 0
            kval->comboboxwidget->insertItem(0, i18n("<select a keyword>"), "");
            kval->comboboxwidget->model()->setData( kval->comboboxwidget->model()->index(0,0), QVariant(Qt::NoItemFlags), Qt::UserRole -1);
            kval->comboboxwidget->setCurrentIndex(0);
            m_valueItems[paramName] = kval;
644
            connect(kval->lineeditwidget, SIGNAL(editingFinished()) , this, SLOT(slotCollectAllParameters()));
Laurent Montel's avatar
Laurent Montel committed
645
            connect(kval->comboboxwidget, SIGNAL(activated(QString)), this, SLOT(slotCollectAllParameters()));
646
            m_uiItems.append(kval);
647
        } else if (type == QLatin1String("fontfamily")) {
648 649 650 651 652
            Fontval* fval = new Fontval;
            fval->setupUi(toFillin);
            fval->name->setText(paramName);
            fval->fontfamilywidget->setCurrentFont(QFont(value));
            m_valueItems[paramName] = fval;
Laurent Montel's avatar
Laurent Montel committed
653
            connect(fval->fontfamilywidget, SIGNAL(currentFontChanged(QFont)), this, SLOT(slotCollectAllParameters())) ;
654
            m_uiItems.append(fval);
655
        } else if (type == QLatin1String("filterjob")) {
656 657 658 659 660 661 662 663 664 665 666 667 668 669
            QVBoxLayout *l= new QVBoxLayout(toFillin);
            QPushButton *button = new QPushButton(toFillin);
            button->setProperty("realName", paramName);
	    if (effect.hasAttribute(QStringLiteral("condition"))) {
                if (m_conditionParameter) {
                    QDomElement na = pa.firstChildElement(QStringLiteral("name"));
                    QString conditionalName = na.attribute(QStringLiteral("conditional"));
                    if (!conditionalName.isEmpty()) {
                        paramName = conditionalName;
                    }
                }
            }
            button->setText(paramName);
            l->addWidget(button);
670
            m_valueItems[paramName] = button;
671
            connect(button, SIGNAL(pressed()), this, SLOT(slotStartFilterJobAction()));
672 673 674 675 676 677 678 679 680 681 682
        } else if (type == QLatin1String("readonly")) {
            QHBoxLayout *lay= new QHBoxLayout(toFillin);
            DraggableLabel *lab = new DraggableLabel(QString("<a href=\"%1\">").arg(pa.attribute(QStringLiteral("name"))) + paramName + QStringLiteral("</a>"));
            lab->setObjectName(pa.attribute(QStringLiteral("name")));
            connect(lab, &QLabel::linkActivated, this, &ParameterContainer::copyData);
            connect(lab, &DraggableLabel::startDrag, this, &ParameterContainer::makeDrag);
            lay->setContentsMargins(0, 0, 0, 0);
            lay->addWidget(lab);
            lab->setVisible(m_conditionParameter);
            lab->setProperty("revert", 1);
            m_conditionalWidgets << lab;
683 684 685 686 687 688 689 690 691
        } else {
            delete toFillin;
            toFillin = NULL;
        }

        if (toFillin)
            m_vbox->addWidget(toFillin);
    }

692
    if (effect.hasAttribute(QStringLiteral("sync_in_out"))) {
693
        QCheckBox *cb = new QCheckBox(i18n("sync keyframes with clip start"));
694
        cb->setChecked(effect.attribute(QStringLiteral("sync_in_out")) == QLatin1String("1"));
695 696 697
        m_vbox->addWidget(cb);
        connect(cb, &QCheckBox::toggled, this, &ParameterContainer::toggleSync);
    }
698 699 700 701 702
    if (stretch)
        m_vbox->addStretch();

    if (m_keyframeEditor)
        m_keyframeEditor->checkVisibleParam();
703

704 705 706
    if (m_animationWidget) {
        m_animationWidget->finishSetup();
    }
707 708 709 710

    // Make sure all doubleparam spinboxes have the same width, looks much better
    QList<DoubleParameterWidget *> allWidgets = findChildren<DoubleParameterWidget *>();
    int minSize = 0;
711
    for (int i = 0; i < allWidgets.count(); ++i) {
712 713
        if (minSize < allWidgets.at(i)->spinSize()) minSize = allWidgets.at(i)->spinSize();
    }
714
    for (int i = 0; i < allWidgets.count(); ++i) {
715 716 717 718 719 720 721 722 723 724
        allWidgets.at(i)->setSpinSize(minSize);
    }
}

ParameterContainer::~ParameterContainer()
{
    clearLayout(m_vbox);
    delete m_vbox;
}

725 726 727 728 729 730 731 732 733 734 735 736 737 738
void ParameterContainer::copyData(const QString &name)
{
    QString value = EffectsList::parameter(m_effect, name);
    if (value.isEmpty())
        return;
    QClipboard *clipboard = QApplication::clipboard();
    clipboard->setText(value);
}

void ParameterContainer::makeDrag(const QString &name)
{
    QString value = EffectsList::parameter(m_effect, name);
    if (value.isEmpty())
        return;
739
    QDrag *dr = new QDrag(qApp);
740 741 742 743 744 745 746 747 748 749 750
    // The data to be transferred by the drag and drop operation is contained in a QMimeData object
    QMimeData *mimeData = new QMimeData;
    QByteArray data;
    data.append(value.toUtf8());
    mimeData->setData(QStringLiteral("kdenlive/geometry"),  data);
    // Assign ownership of the QMimeData object to the QDrag object.
    dr->setMimeData(mimeData);
    // Start the drag and drop operation
    dr->exec(Qt::CopyAction);
}

751 752 753
void ParameterContainer::toggleSync(bool enable)
{
    const QDomElement oldparam = m_effect.cloneNode().toElement();
754
    m_effect.setAttribute(QStringLiteral("sync_in_out"), enable ? "1" : "0");
755 756
    if (!enable) {
        int oldIn = m_effect.attribute(QStringLiteral("in")).toInt();
757
        // geometry / animation attached to 0
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
        if (oldIn > 0) {
            if (m_animationWidget) {
                // Switch keyframes offset
                m_animationWidget->offsetAnimation(oldIn);
                // Save updated animation to xml effect
                QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
                QMap <QString, QString> values = m_animationWidget->getAnimation();
                for (int i = 0; i < namenode.count() ; ++i) {
                    QDomElement pa = namenode.item(i).toElement();
                    QString paramName = pa.attribute(QStringLiteral("name"));
                    if (values.count() > 1) {
                        pa.setAttribute(QStringLiteral("intimeline"), m_animationWidget->isActive(paramName) ? "1" : "0");
                    }
                    if (values.contains(paramName)) {
                        pa.setAttribute(QStringLiteral("value"), values.value(paramName));
                    }
                }
            }
            if (m_geometryWidget) {
                // Switch keyframes offset
778 779 780 781 782 783 784 785
                QString updated = m_geometryWidget->offsetAnimation(oldIn, true);
                QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
                for (int i = 0; i < namenode.count() ; ++i) {
                    QDomElement pa = namenode.item(i).toElement();
                    if (pa.attribute(QStringLiteral("type")) == QLatin1String("geometry")) {
                        pa.setAttribute(QStringLiteral("value"), updated);
                    }
                }
786 787 788 789 790
            }
        }
        m_effect.removeAttribute(QStringLiteral("in"));
        m_effect.removeAttribute(QStringLiteral("out"));
    } else {
791
        // geometry / animation attached to clip's crop start
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
        if (m_in > 0) {
            if (m_animationWidget) {
                // Switch keyframes offset
                m_animationWidget->offsetAnimation(-m_in);
                // Save updated animation to xml effect
                QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
                QMap <QString, QString> values = m_animationWidget->getAnimation();
                for (int i = 0; i < namenode.count() ; ++i) {
                    QDomElement pa = namenode.item(i).toElement();
                    QString paramName = pa.attribute(QStringLiteral("name"));
                    if (values.count() > 1) {
                        pa.setAttribute(QStringLiteral("intimeline"), m_animationWidget->isActive(paramName) ? "1" : "0");
                    }
                    if (values.contains(paramName)) {
                        pa.setAttribute(QStringLiteral("value"), values.value(paramName));
                    }
                }
            }
            if (m_geometryWidget) {
                // Switch keyframes offset
812 813 814 815 816 817 818 819
                QString updated = m_geometryWidget->offsetAnimation(-m_in, false);
                QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
                for (int i = 0; i < namenode.count() ; ++i) {
                    QDomElement pa = namenode.item(i).toElement();
                    if (pa.attribute(QStringLiteral("type")) == QLatin1String("geometry")) {
                        pa.setAttribute(QStringLiteral("value"), updated);
                    }
                }
820 821 822 823 824
            }
        }
        m_effect.setAttribute(QStringLiteral("in"), QString::number(m_in));
        m_effect.setAttribute(QStringLiteral("out"), QString::number(m_out));
    }
825 826 827
    emit parameterChanged(oldparam, m_effect, m_effect.attribute(QStringLiteral("kdenlive_ix")).toInt());
}

Laurent Montel's avatar
Laurent Montel committed
828
void ParameterContainer::meetDependency(const QString& name, const QString &type, const QString &value)
829
{
830
    if (type == QLatin1String("curve")) {
831
        KisCurveWidget *curve = static_cast<KisCurveWidget*>(m_valueItems[name]);
832
        if (curve) {
Laurent Montel's avatar
Laurent Montel committed
833
            const int color = value.toInt();
834 835
            curve->setPixmap(QPixmap::fromImage(ColorTools::rgbCurvePlane(curve->size(), (ColorTools::ColorsRGB)(color == 3 ? 4 : color), 0.8)));
        }
836
    } else if (type == QLatin1String("bezier_spline")) {
837
        BezierSplineWidget *widget = static_cast<BezierSplineWidget*>(m_valueItems[name]);
838
        if (widget) {
839 840
            QLocale locale;
            widget->setMode((BezierSplineWidget::CurveModes)((int)(locale.toDouble(value) * 10 + 0.5)));
841 842 843 844 845 846 847 848 849 850 851
        }
    }
}

wipeInfo ParameterContainer::getWipeInfo(QString value)
{
    wipeInfo info;
    // Convert old geometry values that used a comma as separator
    if (value.contains(',')) value.replace(',','/');
    QString start = value.section(';', 0, 0);
    QString end = value.section(';', 1, 1).section('=', 1, 1);
852
    if (start.startsWith(QLatin1String("-100%/0")))
853
        info.start = LEFT;
854
    else if (start.startsWith(QLatin1String("100%/0")))
855
        info.start = RIGHT;
856
    else if (start.startsWith(QLatin1String("0%/100%")))
857
        info.start = DOWN;
858
    else if (start.startsWith(QLatin1String("0%/-100%")))
859 860 861 862 863 864 865 866 867
        info.start = UP;
    else
        info.start = CENTER;

    if (start.count(':') == 2)
        info.startTransparency = start.section(':', -1).toInt();
    else
        info.startTransparency = 100;

868
    if (end.startsWith(QLatin1String("-100%/0")))
869
        info.end = LEFT;
870
    else if (end.startsWith(QLatin1String("100%/0")))
871
        info.end = RIGHT;
872
    else if (end.startsWith(QLatin1String("0%/100%")))
873
        info.end = DOWN;
874
    else if (end.startsWith(QLatin1String("0%/-100%")))
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
        info.end = UP;
    else
        info.end = CENTER;

    if (end.count(':') == 2)
        info.endTransparency = end.section(':', -1).toInt();
    else
        info.endTransparency = 100;

    return info;
}

void ParameterContainer::updateTimecodeFormat()
{
    if (m_keyframeEditor)
        m_keyframeEditor->updateTimecodeFormat();

892 893 894
    if (m_animationWidget) 
        m_animationWidget->updateTimecodeFormat();

895
    QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
896
    for (int i = 0; i < namenode.count() ; ++i) {
897
        QDomNode pa = namenode.item(i);
898 899 900
        QDomElement na = pa.firstChildElement(QStringLiteral("name"));
        QString type = pa.attributes().namedItem(QStringLiteral("type")).nodeValue();
        QString paramName = na.isNull() ? pa.attributes().namedItem(QStringLiteral("name")).nodeValue() : i18n(na.text().toUtf8().data());
901

902
        if (type == QLatin1String("geometry")) {
903
            if (m_geometryWidget) m_geometryWidget->updateTimecodeFormat();
904
            break;
905
        } else if (type == QLatin1String("position")) {
906
            PositionEdit *posi = static_cast<PositionEdit*>(m_valueItems[paramName+"position"]);
907 908
            posi->updateTimecodeFormat();
            break;
909
        } else if (type == QLatin1String("roto-spline")) {
910 911 912 913 914 915 916 917
            RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems[paramName]);
            widget->updateTimecodeFormat();
        }
    }
}

void ParameterContainer::slotCollectAllParameters()
{
918
    if ((m_valueItems.isEmpty() && !m_animationWidget && !m_geometryWidget) || m_effect.isNull()) return;
919 920 921 922
    QLocale locale;
    locale.setNumberOptions(QLocale::OmitGroupSeparator);
    const QDomElement oldparam = m_effect.cloneNode().toElement();
    //QDomElement newparam = oldparam.cloneNode().toElement();
923

924 925
    if (m_effect.attribute(QStringLiteral("id")) == QLatin1String("movit.lift_gamma_gain") || m_effect.attribute(QStringLiteral("id")) == QLatin1String("lift_gamma_gain") ) {
        LumaLiftGain *gainWidget = static_cast<LumaLiftGain*>(m_valueItems.value(m_effect.attribute(QStringLiteral("id"))));
926
        gainWidget->updateEffect(m_effect);
927
        emit parameterChanged(oldparam, m_effect, m_effect.attribute(QStringLiteral("kdenlive_ix")).toInt());
928 929
        return;
    }
930

931
    QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
932

933 934 935 936 937 938
    // special case, m_animationWidget can hold several parameters
    if (m_animationWidget) {
        QMap <QString, QString> values = m_animationWidget->getAnimation();
        for (int i = 0; i < namenode.count() ; ++i) {
            QDomElement pa = namenode.item(i).toElement();
            QString paramName = pa.attribute(QStringLiteral("name"));
939 940 941
            if (values.count() > 1) {
                pa.setAttribute(QStringLiteral("intimeline"), m_animationWidget->isActive(paramName) ? "1" : "0");
            }
942 943 944 945 946 947
            if (values.contains(paramName)) {
                pa.setAttribute(QStringLiteral("value"), values.value(paramName));
            }
        }
    }

948
    for (int i = 0; i < namenode.count() ; ++i) {
949
        QDomElement pa = namenode.item(i).toElement();
950 951 952 953
        QDomElement na = pa.firstChildElement(QStringLiteral("name"));
        QString type = pa.attribute(QStringLiteral("type"));
        QString paramName = na.isNull() ? pa.attribute(QStringLiteral("name")) : i18n(na.text().toUtf8().data());
        if (type == QLatin1String("complex"))
954
            paramName.append("complex");
955
        else if (type == QLatin1String("position"))
956
            paramName.append("position");
957
        else if (type == QLatin1String("geometry"))
958
            paramName.append("geometry");
959
        else if (type == QLatin1String("keyframe"))
960
            paramName.append("keyframe");
961
        else if (type == QLatin1String("animated"))
962
            continue;
963
        if (type != QLatin1String("simplekeyframe") && type != QLatin1String("fixed") && type != QLatin1String("addedgeometry") && !m_valueItems.contains(paramName)) {
964
            qDebug() << "// Param: " << paramName << " NOT FOUND";