parametercontainer.cpp 65.8 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
#include "dragvalue.h"
Nicolas Carion's avatar
Nicolas Carion committed
23 24

#include "widgets/animationwidget.h"
25 26
#include "widgets/curves/bezier/beziersplineeditor.h"
#include "widgets/curves/curveparamwidget.h"
Nicolas Carion's avatar
Nicolas Carion committed
27
#include "widgets/boolparamwidget.h"
Vincent Pinon's avatar
Vincent Pinon committed
28 29
#include "widgets/choosecolorwidget.h"
#include "widgets/cornerswidget.h"
Nicolas Carion's avatar
Nicolas Carion committed
30 31 32
#include "widgets/doubleparameterwidget.h"
#include "widgets/draggablelabel.h"
#include "widgets/geometrywidget.h"
33
#include "widgets/keyframeedit.h"
34 35
#include "widgets/curves/cubic/kis_cubic_curve.h"
#include "widgets/curves/cubic/kis_curve_widget.h"
Nicolas Carion's avatar
Nicolas Carion committed
36
#include "widgets/listparamwidget.h"
37
#include "widgets/lumaliftgain.h"
38
#include "widgets/positionwidget.h"
39
#include "widgets/selectivecolor.h"
Vincent Pinon's avatar
Vincent Pinon committed
40 41 42 43

#include "kdenlivesettings.h"
#include "mainwindow.h"
#include "colortools.h"
44
#include "dialogs/clipcreationdialog.h"
45
#include "mltcontroller/effectscontroller.h"
46
#include "utils/KoIconUtils.h"
Vincent Pinon's avatar
Vincent Pinon committed
47 48
#include "onmonitoritems/rotoscoping/rotowidget.h"

49 50 51 52 53 54
#include "ui_wipeval_ui.h"
#include "ui_urlval_ui.h"
#include "ui_keywordval_ui.h"
#include "ui_fontval_ui.h"

#include <KUrlRequester>
55
#include "klocalizedstring.h"
56 57 58 59

#include <QMap>
#include <QString>
#include <QImage>
Laurent Montel's avatar
Laurent Montel committed
60
#include "kdenlive_debug.h"
61 62 63 64
#include <QClipboard>
#include <QDrag>
#include <QMimeData>

65

66
MySpinBox::MySpinBox(QWidget *parent):
67 68 69 70 71 72 73
    QSpinBox(parent)
{
    setFocusPolicy(Qt::StrongFocus);
}

void MySpinBox::focusInEvent(QFocusEvent *e)
{
74 75
    setFocusPolicy(Qt::WheelFocus);
    e->accept();
76 77 78 79
}

void MySpinBox::focusOutEvent(QFocusEvent *e)
{
80 81
    setFocusPolicy(Qt::StrongFocus);
    e->accept();
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
}


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
{
};

101 102
ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo &info, EffectMetaInfo *metaInfo, QWidget *parent) :
    m_info(info),
Laurent Montel's avatar
Laurent Montel committed
103 104 105
    m_keyframeEditor(nullptr),
    m_geometryWidget(nullptr),
    m_animationWidget(nullptr),
106 107 108 109 110
    m_metaInfo(metaInfo),
    m_effect(effect),
    m_acceptDrops(false),
    m_monitorEffectScene(MonitorSceneDefault),
    m_conditionParameter(false)
111
{
112 113
    QLocale locale;
    locale.setNumberOptions(QLocale::OmitGroupSeparator);
114
    setObjectName(QStringLiteral("ParameterContainer"));
115

116 117 118
    m_in = info.cropStart.frames(KdenliveSettings::project_fps());
    m_out = (info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1;

119
    QDomNodeList namenode = effect.childNodes();
120
    QDomElement e = effect.toElement();
121

122 123
    int minFrame = e.attribute(QStringLiteral("start")).toInt();
    int maxFrame = e.attribute(QStringLiteral("end")).toInt();
124
    // In transitions, maxFrame is in fact one frame after the end of transition
125 126 127
    if (maxFrame > 0) {
        maxFrame --;
    }
128

129
    bool disable = effect.attribute(QStringLiteral("disable")) == QLatin1String("1") && KdenliveSettings::disable_effect_parameters();
130 131 132 133 134 135
    parent->setEnabled(!disable);

    bool stretch = true;
    m_vbox = new QVBoxLayout(parent);
    m_vbox->setContentsMargins(4, 0, 4, 0);
    m_vbox->setSpacing(2);
136 137 138 139
    /*
    QDomElement clone = effect.cloneNode(true).toElement();
    QDomDocument doc;
    doc.appendChild(doc.importNode(clone, true));
Laurent Montel's avatar
Laurent Montel committed
140
    qCDebug(KDENLIVE_LOG)<<"-------------------------------------\n"<<"LOADED TRANS: "<<doc.toString()<<"\n-------------------";*/
141 142 143 144 145 146 147 148

    // 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();
    }

149
    if (effect.attribute(QStringLiteral("id")) == QLatin1String("movit.lift_gamma_gain") || effect.attribute(QStringLiteral("id")) == QLatin1String("lift_gamma_gain")) {
150 151 152
        // We use a special custom widget here
        LumaLiftGain *gainWidget = new LumaLiftGain(namenode, parent);
        m_vbox->addWidget(gainWidget);
153
        m_valueItems[effect.attribute(QStringLiteral("id"))] = gainWidget;
Nicolas Carion's avatar
Nicolas Carion committed
154

Laurent Montel's avatar
Laurent Montel committed
155
        connect(gainWidget, &LumaLiftGain::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
156 157
    } else if (effect.attribute(QStringLiteral("id")) == QLatin1String("avfilter.selectivecolor")) {
        SelectiveColor *cmykAdjust = new SelectiveColor(effect);
Laurent Montel's avatar
Laurent Montel committed
158
        connect(cmykAdjust, &SelectiveColor::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
159 160
        m_vbox->addWidget(cmykAdjust);
        m_valueItems[effect.attribute(QStringLiteral("id"))] = cmykAdjust;
161 162 163 164 165 166 167 168 169 170 171 172
    } else for (int i = 0; i < namenode.count(); ++i) {
            QDomElement pa = namenode.item(i).toElement();
            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;
173
            }
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
            if (pa.tagName() != QLatin1String("parameter")) {
                continue;
            }
            QString type = pa.attribute(QStringLiteral("type"));
            if (type == QLatin1String("fixed")) {
                // Fixed parameters are not exposed in the UI
                continue;
            }
            QDomElement na = pa.firstChildElement(QStringLiteral("name"));
            QDomElement commentElem = pa.firstChildElement(QStringLiteral("comment"));
            QString paramName = na.isNull() ? pa.attribute(QStringLiteral("name")) : i18n(na.text().toUtf8().data());
            QString comment;
            if (!commentElem.isNull()) {
                comment = i18n(commentElem.text().toUtf8().data());
            }
            QWidget *toFillin = new QWidget(parent);
            QString value = pa.attribute(QStringLiteral("value")).isNull() ?
                            pa.attribute(QStringLiteral("default")) : pa.attribute(QStringLiteral("value"));

            /** See effects/README for info on the different types */
            if (type == QLatin1String("double") || type == QLatin1String("constant")) {
                double min;
                double max;
                m_acceptDrops = true;
Laurent Montel's avatar
Laurent Montel committed
198
                if (pa.attribute(QStringLiteral("min")).contains(QLatin1Char('%'))) {
199
                    min = EffectsController::getStringEval(m_metaInfo->monitor->profileInfo(), pa.attribute(QStringLiteral("min")), m_metaInfo->frameSize);
200
                } else {
201
                    min = locale.toDouble(pa.attribute(QStringLiteral("min")));
202
                }
Laurent Montel's avatar
Laurent Montel committed
203
                if (pa.attribute(QStringLiteral("max")).contains(QLatin1Char('%'))) {
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
                    max = EffectsController::getStringEval(m_metaInfo->monitor->profileInfo(), pa.attribute(QStringLiteral("max")), m_metaInfo->frameSize);
                } else {
                    max = locale.toDouble(pa.attribute(QStringLiteral("max")));
                }

                DoubleParameterWidget *doubleparam = new DoubleParameterWidget(paramName, locale.toDouble(value), min, max,
                        locale.toDouble(pa.attribute(QStringLiteral("default"))), comment, -1, pa.attribute(QStringLiteral("suffix")), pa.attribute(QStringLiteral("decimals")).toInt(), false, parent);
                doubleparam->setFocusPolicy(Qt::StrongFocus);
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                    doubleparam->setEnabled(false);
                    m_conditionalWidgets << doubleparam;
                }
                m_vbox->addWidget(doubleparam);
                m_valueItems[paramName] = doubleparam;
                connect(doubleparam, &DoubleParameterWidget::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
                connect(this, SIGNAL(showComments(bool)), doubleparam, SLOT(slotShowComment(bool)));
            } else if (type == QLatin1String("list")) {
Nicolas Carion's avatar
Nicolas Carion committed
221 222
                ListParamWidget *lswid = new ListParamWidget(paramName, comment, parent);
                lswid->setFocusPolicy(Qt::StrongFocus);
223 224 225 226 227
                QString items = pa.attribute(QStringLiteral("paramlist"));
                QStringList listitems;
                if (items == QLatin1String("%lumaPaths")) {
                    // Special case: Luma files
                    // Create thumbnails
Nicolas Carion's avatar
Nicolas Carion committed
228
                    lswid->setIconSize(QSize(50, 30));
229 230 231 232 233 234
                    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"));
                    }
Nicolas Carion's avatar
Nicolas Carion committed
235
                    lswid->addItem(i18n("None (Dissolve)"));
236 237
                    for (int j = 0; j < listitems.count(); ++j) {
                        QString entry = listitems.at(j);
Laurent Montel's avatar
Laurent Montel committed
238
                        lswid->addItem(listitems.at(j).section(QLatin1Char('/'), -1), entry);
239 240 241 242 243
                        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));
                            }
244
                            lswid->setItemIcon(j + 1, QPixmap::fromImage(MainWindow::m_lumacache.value(entry)));
245
                        }
246
                    }
Nicolas Carion's avatar
Nicolas Carion committed
247
                    lswid->setCurrentText(pa.attribute(QStringLiteral("default")));
248
                    if (!value.isEmpty() && listitems.contains(value)) {
Nicolas Carion's avatar
Nicolas Carion committed
249
                        lswid->setCurrentIndex(listitems.indexOf(value) + 1);
250 251
                    }
                } else {
Laurent Montel's avatar
Laurent Montel committed
252
                    listitems = items.split(QLatin1Char(';'));
253 254
                    if (listitems.count() == 1) {
                        // probably custom effect created before change to ';' as separator
Laurent Montel's avatar
Laurent Montel committed
255
                        listitems = pa.attribute(QStringLiteral("paramlist")).split(QLatin1Char(','));
256 257 258 259
                    }
                    QDomElement list = pa.firstChildElement(QStringLiteral("paramlistdisplay"));
                    QStringList listitemsdisplay;
                    if (!list.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
260
                        listitemsdisplay = i18n(list.text().toUtf8().data()).split(QLatin1Char(','));
261
                    } else {
Laurent Montel's avatar
Laurent Montel committed
262
                        listitemsdisplay = i18n(pa.attribute("paramlistdisplay").toUtf8().data()).split(QLatin1Char(','));
263 264 265 266
                    }
                    if (listitemsdisplay.count() != listitems.count()) {
                        listitemsdisplay = listitems;
                    }
267 268
                    for (int j = 0; j < listitems.count(); ++j) {
                        lswid->addItem(listitemsdisplay.at(j), listitems.at(j));
269 270
                    }
                    if (!value.isEmpty() && listitems.contains(value)) {
Nicolas Carion's avatar
Nicolas Carion committed
271
                        lswid->setCurrentIndex(listitems.indexOf(value));
272 273
                    }
                }
274
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
Nicolas Carion's avatar
Nicolas Carion committed
275 276
                    lswid->setEnabled(false);
                    m_conditionalWidgets << lswid;
277
                }
Nicolas Carion's avatar
Nicolas Carion committed
278 279
                m_valueItems[paramName] = lswid;
                connect(lswid, SIGNAL(valueChanged()) , this, SLOT(slotCollectAllParameters()));
280
                if (!comment.isEmpty()) {
Nicolas Carion's avatar
Nicolas Carion committed
281
                    connect(this, SIGNAL(showComments(bool)), lswid, SLOT(slotShowComment(bool)));
282
                }
Nicolas Carion's avatar
Nicolas Carion committed
283
                m_vbox->addWidget(lswid);
284
            } else if (type == QLatin1String("bool")) {
285
                bool checked = (value == QLatin1String("1"));
Nicolas Carion's avatar
Nicolas Carion committed
286
                BoolParamWidget *bwid = new BoolParamWidget(paramName, comment, checked, parent);
287
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
Nicolas Carion's avatar
Nicolas Carion committed
288 289
                    bwid->setEnabled(false);
                    m_conditionalWidgets << bwid;
290
                }
Nicolas Carion's avatar
Nicolas Carion committed
291 292 293 294
                m_valueItems[paramName] = bwid;
                connect(bwid, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
                connect(this, SIGNAL(showComments(bool)), bwid, SLOT(slotShowComment(bool)));
                m_vbox->addWidget(bwid);
295
            } else if (type == QLatin1String("switch")) {
Nicolas Carion's avatar
Nicolas Carion committed
296 297
                bool checked = (value == pa.attribute("min"));
                BoolParamWidget *bwid = new BoolParamWidget(paramName, comment, checked, parent);
298
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
Nicolas Carion's avatar
Nicolas Carion committed
299 300
                    bwid->setEnabled(false);
                    m_conditionalWidgets << bwid;
301
                }
Nicolas Carion's avatar
Nicolas Carion committed
302 303 304 305
                m_valueItems[paramName] = bwid;
                connect(bwid, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
                connect(this, SIGNAL(showComments(bool)), bwid, SLOT(slotShowComment(bool)));
                m_vbox->addWidget(bwid);
306 307 308 309 310 311 312 313 314 315 316 317 318
            } else if (type.startsWith(QLatin1String("animated"))) {
                m_acceptDrops = true;
                if (type == QLatin1String("animatedrect")) {
                    m_monitorEffectScene = MonitorSceneGeometry;
                }
                if (m_animationWidget) {
                    m_animationWidget->addParameter(pa);
                } else {
                    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);
                    connect(m_animationWidget, &AnimationWidget::seekToPos, this, &ParameterContainer::seekTimeline);
                    connect(m_animationWidget, &AnimationWidget::setKeyframes, this, &ParameterContainer::importKeyframes);
                    connect(this, &ParameterContainer::syncEffectsPos, m_animationWidget, &AnimationWidget::slotSyncPosition);
                    connect(this, SIGNAL(initScene(int)), m_animationWidget, SLOT(slotPositionChanged(int)));
319 320
                    connect(m_animationWidget, &AnimationWidget::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
                    connect(this, SIGNAL(showComments(bool)), m_animationWidget, SLOT(slotShowComment(bool)));
321 322 323 324 325 326 327 328
                    m_vbox->addWidget(m_animationWidget);
                    if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                        m_animationWidget->setEnabled(false);
                        m_conditionalWidgets << m_animationWidget;
                    }
                }
            } else if (type == QLatin1String("geometry")) {
                m_acceptDrops = true;
329 330
                m_monitorEffectScene = MonitorSceneGeometry;
                bool useOffset = false;
331
                if (	effect.tagName() == QLatin1String("effect") && effect.hasAttribute(QStringLiteral("kdenlive:sync_in_out")) && effect.attribute(QStringLiteral("kdenlive:sync_in_out")).toInt() == 0) {
332 333 334 335 336 337 338
                    useOffset = true;
                }
                m_geometryWidget = new GeometryWidget(m_metaInfo, info.startPos.frames(KdenliveSettings::project_fps()), effect.hasAttribute(QStringLiteral("showrotation")), useOffset, parent);
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                    m_geometryWidget->setEnabled(false);
                    m_conditionalWidgets << m_geometryWidget;
                }
339
                connect(m_geometryWidget, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
340 341 342
                if (minFrame == maxFrame) {
                    m_geometryWidget->setupParam(pa, m_in, m_out);
                    connect(this, SIGNAL(updateRange(int,int)), m_geometryWidget, SLOT(slotUpdateRange(int,int)));
343
                } else {
344
                    m_geometryWidget->setupParam(pa, minFrame, maxFrame);
345
                }
346
                m_vbox->addWidget(m_geometryWidget);
Laurent Montel's avatar
Laurent Montel committed
347
                m_valueItems[paramName+QStringLiteral("geometry")] = m_geometryWidget;
348 349 350 351 352
                connect(m_geometryWidget, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
                connect(m_geometryWidget, SIGNAL(importClipKeyframes()), this, SIGNAL(importClipKeyframes()));
                connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
                connect(this, SIGNAL(initScene(int)), m_geometryWidget, SLOT(slotInitScene(int)));
                connect(this, &ParameterContainer::updateFrameInfo, m_geometryWidget, &GeometryWidget::setFrameSize);
353 354 355 356 357 358 359
            } else if (type == QLatin1String("addedgeometry")) {
                // this is a parameter that should be linked to the geometry widget, for example rotation, shear, ...
                if (m_geometryWidget) {
                    m_geometryWidget->addParameter(pa);
                }
            } else if (type == QLatin1String("keyframe") || type == QLatin1String("simplekeyframe")) {
                // keyframe editor widget
Laurent Montel's avatar
Laurent Montel committed
360
                if (m_keyframeEditor == nullptr) {
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
                    KeyframeEdit *geo;
                    if (pa.attribute(QStringLiteral("widget")) == QLatin1String("corners")) {
                        // we want a corners-keyframe-widget
                        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);
                        connect(this, &ParameterContainer::updateRange, corners, &KeyframeEdit::slotUpdateRange);
                        m_monitorEffectScene = MonitorSceneCorners;
                        connect(this, &ParameterContainer::updateFrameInfo, corners, &CornersWidget::setFrameSize);
                        connect(this, &ParameterContainer::syncEffectsPos, corners, &CornersWidget::slotSyncPosition);
                        geo = static_cast<KeyframeEdit *>(corners);
                    } else {
                        geo = new KeyframeEdit(pa, m_in, m_out, m_metaInfo->monitor->timecode(), e.attribute(QStringLiteral("active_keyframe"), QStringLiteral("-1")).toInt());
                        connect(this, &ParameterContainer::updateRange, geo, &KeyframeEdit::slotUpdateRange);
                    }
                    if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                        geo->setEnabled(false);
                        m_conditionalWidgets << geo;
                    }
                    m_vbox->addWidget(geo);
Laurent Montel's avatar
Laurent Montel committed
380
                    m_valueItems[paramName + QStringLiteral("keyframe")] = geo;
381
                    m_keyframeEditor = geo;
382
                    connect(geo, &KeyframeEdit::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
383
                    connect(geo, &KeyframeEdit::seekToPos, this, &ParameterContainer::seekTimeline);
384
                    connect(this, &ParameterContainer::showComments, geo, &KeyframeEdit::slotShowComment);
385 386 387 388 389 390 391 392 393 394
                } else {
                    // we already have a keyframe editor, so just add another column for the new param
                    m_keyframeEditor->addParameter(pa);
                }
            } else if (type == QLatin1String("color")) {
                if (pa.hasAttribute(QStringLiteral("paramprefix"))) {
                    value.remove(0, pa.attribute(QStringLiteral("paramprefix")).size());
                }
                if (value.startsWith('#')) {
                    value = value.replace('#', QLatin1String("0x"));
395
                }
396
                ChooseColorWidget *choosecolor = new ChooseColorWidget(paramName, value, comment, pa.hasAttribute(QStringLiteral("alpha")), parent);
397
                m_vbox->addWidget(choosecolor);
398
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
399 400
                    choosecolor->setEnabled(false);
                    m_conditionalWidgets << choosecolor;
401
                }
402
                m_valueItems[paramName] = choosecolor;
403
                connect(choosecolor, &ChooseColorWidget::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
404 405 406
                connect(choosecolor, &ChooseColorWidget::disableCurrentFilter, this, &ParameterContainer::disableCurrentFilter);
            } else if (type == QLatin1String("position")) {
                int pos = value.toInt();
407 408 409
                auto id = effect.attribute(QStringLiteral("id"));
                if (id == QLatin1String("fadein") ||
                    id == QLatin1String("fade_from_black")) {
410
                    pos = pos - m_in;
411 412
                } else if (id == QLatin1String("fadeout") ||
                           id == QLatin1String("fade_to_black")) {
413 414
                    // fadeout position starts from clip end
                    pos = m_out - pos;
415 416
                } else {
                    qCDebug(KDENLIVE_LOG) << "Error: Invalid position effect id" << endl;
417
                }
418
                PositionWidget *posedit = new PositionWidget(paramName, pos, 0, m_out - m_in, m_metaInfo->monitor->timecode(), comment);
Laurent Montel's avatar
Laurent Montel committed
419
                connect(this, SIGNAL(updateRange(int, int)), posedit, SLOT(setRange(int, int)));
420
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
421 422
                    posedit->setEnabled(false);
                    m_conditionalWidgets << posedit;
423
                }
424
                m_vbox->addWidget(posedit);
Laurent Montel's avatar
Laurent Montel committed
425
                m_valueItems[paramName + QStringLiteral("position")] = posedit;
426 427
                connect(posedit, &PositionWidget::valueChanged,
                        this,    &ParameterContainer::slotCollectAllParameters);
428 429 430 431
            } else if (type == QLatin1String("curve")) {
                QList<QPointF> points;
                int number;
                double version = 0;
432 433 434
                QDomElement versionnode = effect.firstChildElement(QStringLiteral("version"));
                if (!versionnode.isNull()) {
                    version = locale.toDouble(versionnode.text());
435 436 437 438
                }
                if (version > 0.2) {
                    // Rounding gives really weird results. (int) (10 * 0.3) gives 2! So for now, add 0.5 to get correct result
                    number = locale.toDouble(EffectsList::parameter(e, pa.attribute(QStringLiteral("number")))) * 10 + 0.5;
439
                } else {
440
                    number = EffectsList::parameter(e, pa.attribute(QStringLiteral("number"))).toInt();
441
                }
442 443 444 445 446 447 448 449 450 451
                QString inName = pa.attribute(QStringLiteral("inpoints"));
                QString outName = pa.attribute(QStringLiteral("outpoints"));
                int start = pa.attribute(QStringLiteral("min")).toInt();
                for (int j = start; j <= number; ++j) {
                    QString in = inName;
                    in.replace(QLatin1String("%i"), QString::number(j));
                    QString out = outName;
                    out.replace(QLatin1String("%i"), QString::number(j));
                    points << QPointF(locale.toDouble(EffectsList::parameter(e, in)), locale.toDouble(EffectsList::parameter(e, out)));
                }
452
                QString curve_value = "";
453
                if (!points.isEmpty()) {
454
                    curve_value = KisCubicCurve(points).toString();
455
                }
456 457 458
                using Widget_t = CurveParamWidget<KisCurveWidget>;
                Widget_t *curve = new Widget_t(curve_value,parent);
                curve->setMaxPoints(pa.attribute(QStringLiteral("max")).toInt());
459
                m_vbox->addWidget(curve);
460 461
                connect(curve, &Widget_t::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
                connect(this, &ParameterContainer::showComments, curve, &Widget_t::slotShowComment);
462
                m_valueItems[paramName] = curve;
463

464 465 466 467 468
                QString depends = pa.attribute(QStringLiteral("depends"));
                if (!depends.isEmpty()) {
                    meetDependency(paramName, type, EffectsList::parameter(e, depends));
                }
            } else if (type == QLatin1String("bezier_spline")) {
469 470 471
                // BezierSplineWidget *widget = new BezierSplineWidget(value, parent);
                using Widget_t = CurveParamWidget<BezierSplineEditor>;
                Widget_t *widget = new Widget_t(value,parent);
472 473 474
                stretch = false;
                m_vbox->addWidget(widget);
                m_valueItems[paramName] = widget;
475
                connect(widget, &Widget_t::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
                QString depends = pa.attribute(QStringLiteral("depends"));
                if (!depends.isEmpty()) {
                    meetDependency(paramName, type, EffectsList::parameter(e, depends));
                }
            } else if (type == QLatin1String("roto-spline")) {
                m_monitorEffectScene = MonitorSceneRoto;
                RotoWidget *roto = new RotoWidget(value.toLatin1(), m_metaInfo->monitor, info, m_metaInfo->monitor->timecode(), parent);
                connect(roto, &RotoWidget::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
                connect(roto, &RotoWidget::seekToPos, this, &ParameterContainer::seekTimeline);
                connect(this, &ParameterContainer::syncEffectsPos, roto, &RotoWidget::slotSyncPosition);
                m_vbox->addWidget(roto);
                m_valueItems[paramName] = roto;
            } else if (type == QLatin1String("wipe")) {
                Wipeval *wpval = new Wipeval;
                wpval->setupUi(toFillin);
                // Make sure button shows its pressed state even if widget loses focus
                QColor bg = QPalette().highlight().color();
                toFillin->setStyleSheet(QStringLiteral("QPushButton:checked {background-color:rgb(%1,%2,%3);}").arg(bg.red()).arg(bg.green()).arg(bg.blue()));
                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, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->end_down, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->end_left, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->end_right, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->end_center, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->start_up, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->start_down, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->start_left, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->start_right, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->start_center, &QAbstractButton::clicked, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->start_transp, &QAbstractSlider::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
                connect(wpval->end_transp, &QAbstractSlider::valueChanged, this, &ParameterContainer::slotCollectAllParameters);
                //wpval->title->setTitle(na.toElement().text());
                m_uiItems.append(wpval);
            } else if (type == QLatin1String("url")) {
                Urlval *cval = new Urlval;
                cval->setupUi(toFillin);
                cval->label->setText(paramName);
                cval->setToolTip(comment);
                cval->widgetComment->setHidden(true);
                QString filter = pa.attribute(QStringLiteral("filter"));
Laurent Montel's avatar
Laurent Montel committed
553
                if (!filter.isEmpty()) {
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
                    cval->urlwidget->setFilter(filter);
                }
                m_valueItems[paramName] = cval;
                cval->urlwidget->setUrl(QUrl(value));
                connect(cval->urlwidget, SIGNAL(returnPressed()), this, SLOT(slotCollectAllParameters()));
                connect(cval->urlwidget, &KUrlRequester::urlSelected, this, &ParameterContainer::slotCollectAllParameters);
                if (m_conditionParameter && pa.hasAttribute(QStringLiteral("conditional"))) {
                    cval->setEnabled(false);
                    m_conditionalWidgets << cval;
                }
                m_uiItems.append(cval);
            } else if (type == QLatin1String("keywords")) {
                Keywordval *kval = new Keywordval;
                kval->setupUi(toFillin);
                kval->label->setText(paramName);
                kval->lineeditwidget->setText(value);
                kval->setToolTip(comment);
                QDomElement klistelem = pa.firstChildElement(QStringLiteral("keywords"));
                QDomElement kdisplaylistelem = pa.firstChildElement(QStringLiteral("keywordsdisplay"));
                QStringList keywordlist;
                QStringList keyworddisplaylist;
                if (!klistelem.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
576 577
                    keywordlist = klistelem.text().split(QLatin1Char(';'));
                    keyworddisplaylist = i18n(kdisplaylistelem.text().toUtf8().data()).split(QLatin1Char(';'));
578 579 580 581
                }
                if (keyworddisplaylist.count() != keywordlist.count()) {
                    keyworddisplaylist = keywordlist;
                }
582 583
                for (int j = 0; j < keywordlist.count(); ++j) {
                    kval->comboboxwidget->addItem(keyworddisplaylist.at(j), keywordlist.at(j));
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
                }
                // 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;
                connect(kval->lineeditwidget, &QLineEdit::editingFinished, this, &ParameterContainer::slotCollectAllParameters);
                connect(kval->comboboxwidget, SIGNAL(activated(QString)), this, SLOT(slotCollectAllParameters()));
                m_uiItems.append(kval);
            } else if (type == QLatin1String("fontfamily")) {
                Fontval *fval = new Fontval;
                fval->setupUi(toFillin);
                fval->name->setText(paramName);
                fval->fontfamilywidget->setCurrentFont(QFont(value));
                m_valueItems[paramName] = fval;
                connect(fval->fontfamilywidget, &QFontComboBox::currentFontChanged, this, &ParameterContainer::slotCollectAllParameters);
                m_uiItems.append(fval);
            } else if (type == QLatin1String("filterjob")) {
                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;
                        }
612 613
                    }
                }
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
                button->setText(paramName);
                l->addWidget(button);
                m_valueItems[paramName] = button;
                connect(button, &QAbstractButton::pressed, this, &ParameterContainer::slotStartFilterJobAction);
            } else if (type == QLatin1String("readonly")) {
                QHBoxLayout *lay = new QHBoxLayout(toFillin);
                DraggableLabel *lab = new DraggableLabel(QStringLiteral("<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;
            } else {
                delete toFillin;
Laurent Montel's avatar
Laurent Montel committed
631
                toFillin = nullptr;
632
            }
633

634 635 636 637
            if (toFillin) {
                m_vbox->addWidget(toFillin);
            }
        }
638

639
    if (effect.hasAttribute(QStringLiteral("sync_in_out"))) {
640
        QCheckBox *cb = new QCheckBox(i18n("sync keyframes with clip start"));
641
        cb->setChecked(effect.attribute(QStringLiteral("sync_in_out")) == QLatin1String("1"));
642 643 644
        m_vbox->addWidget(cb);
        connect(cb, &QCheckBox::toggled, this, &ParameterContainer::toggleSync);
    }
645
    if (stretch) {
646
        m_vbox->addStretch(2);
647
    }
648

649
    if (m_keyframeEditor) {
650
        m_keyframeEditor->checkVisibleParam();
651
    }
652

653 654 655
    if (m_animationWidget) {
        m_animationWidget->finishSetup();
    }
656 657 658 659

    // Make sure all doubleparam spinboxes have the same width, looks much better
    QList<DoubleParameterWidget *> allWidgets = findChildren<DoubleParameterWidget *>();
    int minSize = 0;
660
    for (int i = 0; i < allWidgets.count(); ++i) {
661 662 663
        if (minSize < allWidgets.at(i)->spinSize()) {
            minSize = allWidgets.at(i)->spinSize();
        }
664
    }
665
    for (int i = 0; i < allWidgets.count(); ++i) {
666 667 668 669 670 671 672 673 674 675
        allWidgets.at(i)->setSpinSize(minSize);
    }
}

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

676 677 678
void ParameterContainer::copyData(const QString &name)
{
    QString value = EffectsList::parameter(m_effect, name);
679
    if (value.isEmpty()) {
680
        return;
681
    }
682 683 684 685 686 687 688
    QClipboard *clipboard = QApplication::clipboard();
    clipboard->setText(value);
}

void ParameterContainer::makeDrag(const QString &name)
{
    QString value = EffectsList::parameter(m_effect, name);
689
    if (value.isEmpty()) {
690
        return;
691
    }
Laurent Montel's avatar
Laurent Montel committed
692
    value.prepend(name + QLatin1Char('='));
693
    QDrag *dr = new QDrag(qApp);
694 695 696 697 698 699 700 701 702 703 704
    // 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);
}

705 706 707
void ParameterContainer::toggleSync(bool enable)
{
    const QDomElement oldparam = m_effect.cloneNode().toElement();
708
    m_effect.setAttribute(QStringLiteral("sync_in_out"), enable ? "1" : "0");
709 710
    if (!enable) {
        int oldIn = m_effect.attribute(QStringLiteral("in")).toInt();
711
        // geometry / animation attached to 0
712 713 714 715 716 717
        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"));
Laurent Montel's avatar
Laurent Montel committed
718
                QMap<QString, QString> values = m_animationWidget->getAnimation();
719
                for (int i = 0; i < namenode.count(); ++i) {
720 721 722 723 724
                    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");
                    }
Laurent Montel's avatar
Laurent Montel committed
725 726 727
                    const QString val = values.value(paramName);
                    if (!val.isEmpty()) {
                        pa.setAttribute(QStringLiteral("value"), val);
728 729 730 731 732
                    }
                }
            }
            if (m_geometryWidget) {
                // Switch keyframes offset
733 734
                QString updated = m_geometryWidget->offsetAnimation(oldIn, true);
                QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
735
                for (int i = 0; i < namenode.count(); ++i) {
736 737 738 739 740
                    QDomElement pa = namenode.item(i).toElement();
                    if (pa.attribute(QStringLiteral("type")) == QLatin1String("geometry")) {
                        pa.setAttribute(QStringLiteral("value"), updated);
                    }
                }
741 742 743 744 745
            }
        }
        m_effect.removeAttribute(QStringLiteral("in"));
        m_effect.removeAttribute(QStringLiteral("out"));
    } else {
746
        // geometry / animation attached to clip's crop start
747 748 749 750 751 752
        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"));
Laurent Montel's avatar
Laurent Montel committed
753
                QMap<QString, QString> values = m_animationWidget->getAnimation();
754
                for (int i = 0; i < namenode.count(); ++i) {
755 756 757 758 759
                    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");
                    }
Laurent Montel's avatar
Laurent Montel committed
760 761 762
                    const QString val = values.value(paramName);
                    if (!val.isEmpty()) {
                        pa.setAttribute(QStringLiteral("value"), val);
763 764 765 766 767
                    }
                }
            }
            if (m_geometryWidget) {
                // Switch keyframes offset
768 769
                QString updated = m_geometryWidget->offsetAnimation(-m_in, false);
                QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
770
                for (int i = 0; i < namenode.count(); ++i) {
771 772 773 774 775
                    QDomElement pa = namenode.item(i).toElement();
                    if (pa.attribute(QStringLiteral("type")) == QLatin1String("geometry")) {
                        pa.setAttribute(QStringLiteral("value"), updated);
                    }
                }
776 777 778 779 780
            }
        }
        m_effect.setAttribute(QStringLiteral("in"), QString::number(m_in));
        m_effect.setAttribute(QStringLiteral("out"), QString::number(m_out));
    }
781 782 783
    emit parameterChanged(oldparam, m_effect, m_effect.attribute(QStringLiteral("kdenlive_ix")).toInt());
}

784
void ParameterContainer::meetDependency(const QString &name, const QString &type, const QString &value)
785
{
786
    if (type == QLatin1String("curve")) {
787 788
        using Widget_t = CurveParamWidget<KisCurveWidget>;
        Widget_t *curve = static_cast<Widget_t *>(m_valueItems[name]);
789
        if (curve) {
790
            QLocale locale;
791 792 793 794 795
            double mode = locale.toDouble(value);
            if (mode < 1.) {
                mode *= 10; //hack to deal with new versions of the effect that set mode in [0,1]
            }
            curve->setMode((Widget_t::CurveModes)int(mode + 0.5));
796
        }
797
    } else if (type == QLatin1String("bezier_spline")) {
798 799
        using Widget_t = CurveParamWidget<BezierSplineEditor>;
        Widget_t *widget = static_cast<Widget_t *>(m_valueItems[name]);
800
        if (widget) {
801
            QLocale locale;
802
            widget->setMode((Widget_t::CurveModes)((int)(locale.toDouble(value) * 10 + 0.5)));
803 804 805 806 807 808 809 810
        }
    }
}

wipeInfo ParameterContainer::getWipeInfo(QString value)
{
    wipeInfo info;
    // Convert old geometry values that used a comma as separator
Laurent Montel's avatar
Laurent Montel committed
811
    if (value.contains(QLatin1Char(','))) {
812 813
        value.replace(',', '/');
    }
814 815
    QString start = value.section(QLatin1Char(';'), 0, 0);
    QString end = value.section(QLatin1Char(';'), 1, 1).section(QLatin1Char('='), 1, 1);
816
    if (start.startsWith(QLatin1String("-100%/0"))) {
817
        info.start = LEFT;
818
    } else if (start.startsWith(QLatin1String("100%/0"))) {
819
        info.start = RIGHT;
820
    } else if (start.startsWith(QLatin1String("0%/100%"))) {
821
        info.start = DOWN;
822
    } else if (start.startsWith(QLatin1String("0%/-100%"))) {
823
        info.start = UP;
824
    } else {
825
        info.start = CENTER;
826
    }
827

828
    if (start.count(':') == 2) {
Laurent Montel's avatar
Laurent Montel committed
829
        info.startTransparency = start.section(QLatin1Char(':'), -1).toInt();
830
    } else {
831
        info.startTransparency = 100;
832
    }
833

834
    if (end.startsWith(QLatin1String("-100%/0"))) {
835
        info.end = LEFT;
836
    } else if (end.startsWith(QLatin1String("100%/0"))) {
837
        info.end = RIGHT;
838
    } else if (end.startsWith(QLatin1String("0%/100%"))) {
839
        info.end = DOWN;
840
    } else if (end.startsWith(QLatin1String("0%/-100%"))) {
841
        info.end = UP;
842
    } else {
843
        info.end = CENTER;
844
    }
845

846
    if (end.count(':') == 2) {
Laurent Montel's avatar
Laurent Montel committed
847
        info.endTransparency = end.section(QLatin1Char(':'), -1).toInt();
848
    } else {
849
        info.endTransparency = 100;
850
    }
851 852 853 854 855 856

    return info;
}

void ParameterContainer::updateTimecodeFormat()
{
857
    if (m_keyframeEditor) {
858
        m_keyframeEditor->updateTimecodeFormat();
859
    }
860

861
    if (m_animationWidget) {
862
        m_animationWidget->updateTimecodeFormat();
863
    }
864

865
    QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
866
    for (int i = 0; i < namenode.count(); ++i) {
867
        QDomNode pa = namenode.item(i);
868 869 870
        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());
871

872
        if (type == QLatin1String("geometry")) {
873 874 875
            if (m_geometryWidget) {
                m_geometryWidget->updateTimecodeFormat();
            }
876
            break;
877
        } else if (type == QLatin1String("position")) {
878
            PositionWidget *posi = qobject_cast<PositionWidget *>(m_valueItems[paramName]);
879 880
            posi->updateTimecodeFormat();
            break;
881
        } else if (type == QLatin1String("roto-spline")) {
882 883 884 885 886 887 888 889
            RotoWidget *widget = static_cast<RotoWidget *>(m_valueItems[paramName]);
            widget->updateTimecodeFormat();
        }
    }
}

void ParameterContainer::slotCollectAllParameters()
{
890 891 892
    if ((m_valueItems.isEmpty() && !m_animationWidget && !m_geometryWidget) || m_effect.isNull()) {
        return;
    }
893 894 895 896
    QLocale locale;
    locale.setNumberOptions(QLocale::OmitGroupSeparator);
    const QDomElement oldparam = m_effect.cloneNode().toElement();
    //QDomElement newparam = oldparam.cloneNode().toElement();
897

898 899
    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"))));
900
        gainWidget->updateEffect(m_effect);
901
        emit parameterChanged(oldparam, m_effect, m_effect.attribute(QStringLiteral("kdenlive_ix")).toInt());
902 903
        return;
    }
904
    if (m_effect.attribute(QStringLiteral("tag")) == QLatin1String("avfilter.selectivecolor")) {
905
        SelectiveColor *cmykAdjust = static_cast<SelectiveColor *>(m_valueItems.value(m_effect.attribute(QStringLiteral("id"))));
906 907 908 909
        cmykAdjust->updateEffect(m_effect);
        emit parameterChanged(oldparam, m_effect, m_effect.attribute(QStringLiteral("kdenlive_ix")).toInt());
        return;
    }
910

911
    QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
912

913 914
    // special case, m_animationWidget can hold several parameters
    if (m_animationWidget) {
Laurent Montel's avatar
Laurent Montel committed
915
        QMap<QString, QString> values = m_animationWidget->getAnimation();
916
        for (int i = 0; i < namenode.count(); ++i) {
917 918
            QDomElement pa = namenode.item(i).toElement();
            QString paramName = pa.attribute(QStringLiteral("name"));
919 920 921
            if (values.count() > 1) {
                pa.setAttribute(QStringLiteral("intimeline"), m_animationWidget->isActive(paramName) ? "1" : "0");
            }
Laurent Montel's avatar
Laurent Montel committed
922 923 924
            const QString val = values.value(paramName);
            if (!val.isEmpty()) {
                pa.setAttribute(QStringLiteral("value"), val);
925 926 927 928
            }
        }
    }

929
    for (int i = 0; i < namenode.count(); ++i) {
930
        QDomElement pa = namenode.item(i).toElement();
931 932 933
        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());