keyframewidget.cpp 26.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/***************************************************************************
 *   Copyright (C) 2011 by Till Theato (root@ttill.de)                     *
 *   Copyright (C) 2017 by Nicolas Carion                                  *
 *   This file is part of Kdenlive (www.kdenlive.org).                     *
 *                                                                         *
 *   Kdenlive is free software: you can redistribute it and/or modify      *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation, either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   Kdenlive is distributed in the hope that it will be useful,           *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with Kdenlive.  If not, see <http://www.gnu.org/licenses/>.     *
 ***************************************************************************/

#include "keyframewidget.hpp"
21
#include "assets/keyframes/model/corners/cornershelper.hpp"
22
#include "assets/keyframes/model/keyframemodellist.hpp"
23
#include "assets/keyframes/model/rotoscoping/rotohelper.hpp"
24
#include "assets/keyframes/model/keyframemonitorhelper.hpp"
Nicolas Carion's avatar
Nicolas Carion committed
25
#include "assets/keyframes/view/keyframeview.hpp"
26
#include "assets/model/assetparametermodel.hpp"
27
#include "assets/view/widgets/keyframeimport.h"
28
#include "core.h"
29
#include "kdenlivesettings.h"
30
31
#include "monitor/monitor.h"
#include "timecode.h"
32
#include "timecodedisplay.h"
33

34
#include "widgets/doublewidget.h"
35
#include "widgets/geometrywidget.h"
36

37
#include <KSelectAction>
38
39
40
#include <QApplication>
#include <QClipboard>
#include <QJsonDocument>
41
#include <QMenu>
42
#include <QPointer>
43
#include <QToolButton>
44
#include <QStyle>
45
#include <QVBoxLayout>
46
#include <klocalizedstring.h>
Nicolas Carion's avatar
Nicolas Carion committed
47
#include <utility>
48

49
KeyframeWidget::KeyframeWidget(std::shared_ptr<AssetParameterModel> model, QModelIndex index, QSize frameSize, QWidget *parent)
Nicolas Carion's avatar
Nicolas Carion committed
50
    : AbstractParamWidget(std::move(model), index, parent)
51
52
    , m_monitorHelper(nullptr)
    , m_neededScene(MonitorSceneType::MonitorSceneDefault)
53
    , m_sourceFrameSize(frameSize.isValid() && !frameSize.isNull() ? frameSize : pCore->getCurrentFrameSize())
54
55
    , m_baseHeight(0)
    , m_addedHeight(0)
56
{
57
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
58
    m_lay = new QVBoxLayout(this);
59
    m_lay->setSpacing(0);
60

61
62
63
    bool ok = false;
    int duration = m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(&ok);
    Q_ASSERT(ok);
64
65
66
    m_model->prepareKeyframes();
    m_keyframes = m_model->getKeyframeModel();
    m_keyframeview = new KeyframeView(m_keyframes, duration, this);
67
68
69

    m_buttonAddDelete = new QToolButton(this);
    m_buttonAddDelete->setAutoRaise(true);
70
    m_buttonAddDelete->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
71
72
73
74
    m_buttonAddDelete->setToolTip(i18n("Add keyframe"));

    m_buttonPrevious = new QToolButton(this);
    m_buttonPrevious->setAutoRaise(true);
75
    m_buttonPrevious->setIcon(QIcon::fromTheme(QStringLiteral("media-skip-backward")));
76
77
78
79
    m_buttonPrevious->setToolTip(i18n("Go to previous keyframe"));

    m_buttonNext = new QToolButton(this);
    m_buttonNext->setAutoRaise(true);
80
    m_buttonNext->setIcon(QIcon::fromTheme(QStringLiteral("media-skip-forward")));
81
    m_buttonNext->setToolTip(i18n("Go to next keyframe"));
82
83
84
85
86
87
88
    
    // Move keyframe to cursor
    m_buttonCenter = new QToolButton(this);
    m_buttonCenter->setAutoRaise(true);
    m_buttonCenter->setIcon(QIcon::fromTheme(QStringLiteral("align-horizontal-center")));
    m_buttonCenter->setToolTip(i18n("Move selected keyframe to cursor"));
    
89
90
91
92
93
94
    // Duplicate selected keyframe at cursor pos
    m_buttonCopy = new QToolButton(this);
    m_buttonCopy->setAutoRaise(true);
    m_buttonCopy->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
    m_buttonCopy->setToolTip(i18n("Duplicate selected keyframe"));
    
95
96
97
98
99
100
    // Apply current value to selected keyframes
    m_buttonApply = new QToolButton(this);
    m_buttonApply->setAutoRaise(true);
    m_buttonApply->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste")));
    m_buttonApply->setToolTip(i18n("Apply value to selected keyframes"));
    m_buttonApply->setFocusPolicy(Qt::StrongFocus);
101
    m_focusConnection = connect(qApp, &QApplication::focusChanged, [this](QWidget *old, QWidget *now) {
102
        if (now == m_buttonApply) {
103
            if (old && old->parentWidget() && isAncestorOf(old->parentWidget())) {
104
105
106
107
108
109
                m_lastFocusedParam = old->parentWidget()->objectName();
                qDebug()<<"======= FROM PARENT: "<<old->parentWidget()->objectName();
            }
        }
    });
    
110
    // Keyframe type widget
111
112
    m_selectType = new KSelectAction(QIcon::fromTheme(QStringLiteral("keyframes")), i18n("Keyframe interpolation"), this);
    QAction *linear = new QAction(QIcon::fromTheme(QStringLiteral("linear")), i18n("Linear"), this);
113
114
115
    linear->setData((int)mlt_keyframe_linear);
    linear->setCheckable(true);
    m_selectType->addAction(linear);
116
    QAction *discrete = new QAction(QIcon::fromTheme(QStringLiteral("discrete")), i18n("Discrete"), this);
117
118
119
    discrete->setData((int)mlt_keyframe_discrete);
    discrete->setCheckable(true);
    m_selectType->addAction(discrete);
120
    QAction *curve = new QAction(QIcon::fromTheme(QStringLiteral("smooth")), i18n("Smooth"), this);
121
122
123
124
    curve->setData((int)mlt_keyframe_smooth);
    curve->setCheckable(true);
    m_selectType->addAction(curve);
    m_selectType->setCurrentAction(linear);
Nicolas Carion's avatar
Nicolas Carion committed
125
    connect(m_selectType, static_cast<void (KSelectAction::*)(QAction *)>(&KSelectAction::triggered), this, &KeyframeWidget::slotEditKeyframeType);
126
    m_selectType->setToolBarMode(KSelectAction::ComboBoxMode);
127
    m_toolbar = new QToolBar(this);
128
129
130
    m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
    int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
    m_toolbar->setIconSize(QSize(size, size));
131

132
    Monitor *monitor = pCore->getMonitor(m_model->monitorId);
133
    connect(monitor, &Monitor::seekPosition, this, &KeyframeWidget::monitorSeek, Qt::UniqueConnection);
134
    m_time = new TimecodeDisplay(pCore->timecode(), this);
135
    m_time->setRange(0, duration - 1);
136

137
138
139
    m_toolbar->addWidget(m_buttonPrevious);
    m_toolbar->addWidget(m_buttonAddDelete);
    m_toolbar->addWidget(m_buttonNext);
140
    m_toolbar->addWidget(m_buttonCenter);
141
    m_toolbar->addWidget(m_buttonCopy);
142
    m_toolbar->addWidget(m_buttonApply);
143
    m_toolbar->addAction(m_selectType);
144

145
146
147
148
149
150
    QAction *seekKeyframe = new QAction(i18n("Seek to keyframe on select"), this);
    seekKeyframe->setCheckable(true);
    seekKeyframe->setChecked(KdenliveSettings::keyframeseek());
    connect(seekKeyframe, &QAction::triggered, [&](bool selected) {
        KdenliveSettings::setKeyframeseek(selected);
    });
151
152
153
154
155
    // copy/paste keyframes from clipboard
    QAction *copy = new QAction(i18n("Copy keyframes to clipboard"), this);
    connect(copy, &QAction::triggered, this, &KeyframeWidget::slotCopyKeyframes);
    QAction *paste = new QAction(i18n("Import keyframes from clipboard"), this);
    connect(paste, &QAction::triggered, this, &KeyframeWidget::slotImportKeyframes);
156
    // Remove keyframes
157
158
    QAction *removeNext = new QAction(i18n("Remove all keyframes after cursor"), this);
    connect(removeNext, &QAction::triggered, this, &KeyframeWidget::slotRemoveNextKeyframes);
159
160

    // Default kf interpolation
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
    KSelectAction *kfType = new KSelectAction(i18n("Default keyframe type"), this);
    QAction *discrete2 = new QAction(QIcon::fromTheme(QStringLiteral("discrete")), i18n("Discrete"), this);
    discrete2->setData((int)mlt_keyframe_discrete);
    discrete2->setCheckable(true);
    kfType->addAction(discrete2);
    QAction *linear2 = new QAction(QIcon::fromTheme(QStringLiteral("linear")), i18n("Linear"), this);
    linear2->setData((int)mlt_keyframe_linear);
    linear2->setCheckable(true);
    kfType->addAction(linear2);
    QAction *curve2 = new QAction(QIcon::fromTheme(QStringLiteral("smooth")), i18n("Smooth"), this);
    curve2->setData((int)mlt_keyframe_smooth);
    curve2->setCheckable(true);
    kfType->addAction(curve2);
    switch (KdenliveSettings::defaultkeyframeinterp()) {
    case mlt_keyframe_discrete:
        kfType->setCurrentAction(discrete2);
        break;
    case mlt_keyframe_smooth:
        kfType->setCurrentAction(curve2);
        break;
    default:
        kfType->setCurrentAction(linear2);
        break;
    }
185
    connect(kfType, static_cast<void (KSelectAction::*)(QAction *)>(&KSelectAction::triggered),
Vincent Pinon's avatar
Vincent Pinon committed
186
            this, [&](QAction *ac) { KdenliveSettings::setDefaultkeyframeinterp(ac->data().toInt()); });
187
    auto *container = new QMenu(this);
188
    container->addAction(seekKeyframe);
189
190
    container->addAction(copy);
    container->addAction(paste);
191
    container->addSeparator();
192
193
    container->addAction(kfType);
    container->addAction(removeNext);
194

195
196
197
198
199
200
201
202
    // rotoscoping only supports linear keyframes
    if (m_model->getAssetId() == QLatin1String("rotoscoping")) {
        m_selectType->setVisible(false);
        m_selectType->setCurrentAction(linear);
        kfType->setVisible(false);
        kfType->setCurrentAction(linear2);
    }

203
204
205
206
207
208
209
    // Menu toolbutton
    auto *menuButton = new QToolButton(this);
    menuButton->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-menu")));
    menuButton->setToolTip(i18n("Options"));
    menuButton->setMenu(container);
    menuButton->setPopupMode(QToolButton::InstantPopup);
    m_toolbar->addWidget(menuButton);
210
    m_toolbar->addWidget(m_time);
211
212

    m_lay->addWidget(m_keyframeview);
213
    m_lay->addWidget(m_toolbar);
214

Vincent Pinon's avatar
Vincent Pinon committed
215
216
    connect(m_time, &TimecodeDisplay::timeCodeEditingFinished, this, [&]() { slotSetPosition(-1, true); });
    connect(m_keyframeview, &KeyframeView::seekToPos, this, [&](int p) { slotSetPosition(p, true); });
217
    connect(m_keyframeview, &KeyframeView::atKeyframe, this, &KeyframeWidget::slotAtKeyframe);
218
    connect(m_keyframeview, &KeyframeView::modified, this, &KeyframeWidget::slotRefreshParams);
219
    connect(m_keyframeview, &KeyframeView::activateEffect, this, &KeyframeWidget::activateEffect);
220
221
222
223

    connect(m_buttonAddDelete, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotAddRemove);
    connect(m_buttonPrevious, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotGoToPrev);
    connect(m_buttonNext, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotGoToNext);
224
    connect(m_buttonCenter, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotCenterKeyframe);
225
    connect(m_buttonCopy, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotDuplicateKeyframe);
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
    connect(m_buttonApply, &QAbstractButton::pressed, [this]() {
        if (!m_lastFocusedParam.isEmpty()) {
            qDebug()<<"=== ADJUSTING KF PARAM: "<<m_lastFocusedParam;
            if (m_lastFocusedParam.startsWith(QLatin1String("spin"))) {
                for (const auto &w : m_parameters) {
                    auto type = m_model->data(w.first, AssetParameterModel::TypeRole).value<ParamType>();
                    if (type != ParamType::AnimatedRect) {
                        continue;
                    }
                    QModelIndex ix = w.first;
                    m_keyframeview->copyCurrentValue(ix, m_lastFocusedParam);
                }
            } else {
                m_keyframeview->copyCurrentValue(m_keyframes->getIndexAtRow(m_lastFocusedParam.toInt()), QString());
            }
        }
    });
243
    //m_baseHeight = m_keyframeview->height() + m_selectType->defaultWidget()->sizeHint().height();
244
    QMargins mrg = m_lay->contentsMargins();
245
246
247
    m_baseHeight = m_keyframeview->height() + m_toolbar->sizeHint().height();
    m_addedHeight = mrg.top() + mrg.bottom();
    setFixedHeight(m_baseHeight + m_addedHeight);
248
    addParameter(index);
249
250
251
252
}

KeyframeWidget::~KeyframeWidget()
{
253
    QObject::disconnect( m_focusConnection );
254
255
256
257
258
259
260
    delete m_keyframeview;
    delete m_buttonAddDelete;
    delete m_buttonPrevious;
    delete m_buttonNext;
    delete m_time;
}

261
262
void KeyframeWidget::monitorSeek(int pos)
{
263
    int in = pCore->getItemPosition(m_model->getOwnerId());
264
    int out = in + pCore->getItemDuration(m_model->getOwnerId());
265
    bool isInRange = pos >= in && pos < out;
266
    connectMonitor(isInRange && m_model->isActive());
267
    m_buttonAddDelete->setEnabled(isInRange && pos > in);
268
    int framePos = qBound(in, pos, out) - in;
269
    if (isInRange && framePos != m_time->getValue()) {
270
271
        slotSetPosition(framePos, false);
    }
272
273
}

274
275
276
277
void KeyframeWidget::slotEditKeyframeType(QAction *action)
{
    int type = action->data().toInt();
    m_keyframeview->slotEditType(type, m_index);
Vincent Pinon's avatar
Vincent Pinon committed
278
    emit activateEffect();
279
280
}

281
282
283
void KeyframeWidget::slotRefreshParams()
{
    int pos = getPosition();
284
    KeyframeType keyType = m_keyframes->keyframeType(GenTime(pos, pCore->getCurrentFps()));
285
286
    int i = 0;
    while (auto ac = m_selectType->action(i)) {
287
        if (ac->data().toInt() == (int)keyType) {
288
289
290
291
292
            m_selectType->setCurrentItem(i);
            break;
        }
        i++;
    }
Nicolas Carion's avatar
Nicolas Carion committed
293
    for (const auto &w : m_parameters) {
Nicolas Carion's avatar
Nicolas Carion committed
294
        auto type = m_model->data(w.first, AssetParameterModel::TypeRole).value<ParamType>();
295
        if (type == ParamType::KeyframeParam) {
Nicolas Carion's avatar
Nicolas Carion committed
296
            ((DoubleWidget *)w.second)->setValue(m_keyframes->getInterpolatedValue(pos, w.first).toDouble());
297
298
299
300
        } else if (type == ParamType::AnimatedRect) {
            const QString val = m_keyframes->getInterpolatedValue(pos, w.first).toString();
            const QStringList vals = val.split(QLatin1Char(' '));
            QRect rect;
301
            double opacity = -1;
302
303
            if (vals.count() >= 4) {
                rect = QRect(vals.at(0).toInt(), vals.at(1).toInt(), vals.at(2).toInt(), vals.at(3).toInt());
304
                if (vals.count() > 4) {
Simon Eugster's avatar
Simon Eugster committed
305
                    opacity = vals.at(4).toDouble();
306
                }
307
            }
Nicolas Carion's avatar
Nicolas Carion committed
308
            ((GeometryWidget *)w.second)->setValue(rect, opacity);
309
        }
310
    }
311
    if (m_monitorHelper && m_model->isActive()) {
312
313
314
        m_monitorHelper->refreshParams(pos);
        return;
    }
315
}
316
317
318
319
void KeyframeWidget::slotSetPosition(int pos, bool update)
{
    if (pos < 0) {
        pos = m_time->getValue();
320
        m_keyframeview->slotSetPosition(pos, true);
321
322
    } else {
        m_time->setValue(pos);
323
        m_keyframeview->slotSetPosition(pos, true);
324
    }
325
    m_buttonAddDelete->setEnabled(pos > 0);
326
327
    slotRefreshParams();

328
    if (update) {
329
        emit seekToPos(pos);
330
331
332
333
334
    }
}

int KeyframeWidget::getPosition() const
{
335
    return m_time->getValue() + pCore->getItemIn(m_model->getOwnerId());
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
}

void KeyframeWidget::addKeyframe(int pos)
{
    blockSignals(true);
    m_keyframeview->slotAddKeyframe(pos);
    blockSignals(false);
    setEnabled(true);
}

void KeyframeWidget::updateTimecodeFormat()
{
    m_time->slotUpdateTimeCodeFormat();
}

351
void KeyframeWidget::slotAtKeyframe(bool atKeyframe, bool singleKeyframe)
352
353
{
    if (atKeyframe) {
354
        m_buttonAddDelete->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
355
356
        m_buttonAddDelete->setToolTip(i18n("Delete keyframe"));
    } else {
357
        m_buttonAddDelete->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
358
359
        m_buttonAddDelete->setToolTip(i18n("Add keyframe"));
    }
360
    m_buttonCenter->setEnabled(!atKeyframe);
361
    emit updateEffectKeyframe(atKeyframe || singleKeyframe);
362
    m_selectType->setEnabled(atKeyframe || singleKeyframe);
363
    for (const auto &w : m_parameters) {
364
        w.second->setEnabled(atKeyframe || singleKeyframe);
365
    }
366
}
367

368
void KeyframeWidget::slotRefresh()
369
{
370
    // update duration
371
372
373
    bool ok = false;
    int duration = m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(&ok);
    Q_ASSERT(ok);
374
    // m_model->dataChanged(QModelIndex(), QModelIndex());
375
376
377
378
379
380
381
382
383
384
385
386
387
    //->getKeyframeModel()->getKeyModel(m_index)->dataChanged(QModelIndex(), QModelIndex());
    m_keyframeview->setDuration(duration);
    m_time->setRange(0, duration - 1);
    slotRefreshParams();
}

void KeyframeWidget::resetKeyframes()
{
    // update duration
    bool ok = false;
    int duration = m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(&ok);
    Q_ASSERT(ok);
    // reset keyframes
388
    m_keyframes->refresh();
389
    // m_model->dataChanged(QModelIndex(), QModelIndex());
390
391
    m_keyframeview->setDuration(duration);
    m_time->setRange(0, duration - 1);
392
    slotRefreshParams();
393
}
394

Nicolas Carion's avatar
Nicolas Carion committed
395
void KeyframeWidget::addParameter(const QPersistentModelIndex &index)
396
397
{
    // Retrieve parameters from the model
398
399
400
    QString name = m_model->data(index, Qt::DisplayRole).toString();
    QString comment = m_model->data(index, AssetParameterModel::CommentRole).toString();
    QString suffix = m_model->data(index, AssetParameterModel::SuffixRole).toString();
401

Nicolas Carion's avatar
Nicolas Carion committed
402
    auto type = m_model->data(index, AssetParameterModel::TypeRole).value<ParamType>();
403
404
405
    // Construct object
    QWidget *paramWidget = nullptr;
    if (type == ParamType::AnimatedRect) {
406
        m_neededScene = MonitorSceneType::MonitorSceneGeometry;
407
        int inPos = m_model->data(index, AssetParameterModel::ParentInRole).toInt();
Nicolas Carion's avatar
Nicolas Carion committed
408
        QPair<int, int> range(inPos, inPos + m_model->data(index, AssetParameterModel::ParentDurationRole).toInt());
409
        const QString value = m_keyframes->getInterpolatedValue(getPosition(), index).toString();
410
        m_monitorHelper = new KeyframeMonitorHelper(pCore->getMonitor(m_model->monitorId), m_model, index, this);
411
        QRect rect;
412
        double opacity = 0;
413
        QStringList vals = value.split(QLatin1Char(' '));
414
        if (vals.count() > 3) {
415
            rect = QRect(vals.at(0).toInt(), vals.at(1).toInt(), vals.at(2).toInt(), vals.at(3).toInt());
416
            if (vals.count() > 4) {
Simon Eugster's avatar
Simon Eugster committed
417
                opacity = vals.at(4).toDouble();
418
            }
419
        }
420
421
        // qtblend uses an opacity value in the (0-1) range, while older geometry effects use (0-100)
        bool integerOpacity = m_model->getAssetId() != QLatin1String("qtblend");
422
        GeometryWidget *geomWidget = new GeometryWidget(pCore->getMonitor(m_model->monitorId), range, rect, opacity, m_sourceFrameSize, false,
423
                                                        m_model->data(m_index, AssetParameterModel::OpacityRole).toBool(), integerOpacity, this);
Nicolas Carion's avatar
Nicolas Carion committed
424
        connect(geomWidget, &GeometryWidget::valueChanged,
Vincent Pinon's avatar
Vincent Pinon committed
425
426
                this, [this, index](const QString v) {
                    emit activateEffect();
427
                    m_keyframes->updateKeyframe(GenTime(getPosition(), pCore->getCurrentFps()), QVariant(v), index); });
428
        connect(geomWidget, &GeometryWidget::updateMonitorGeometry, [this](const QRect r) {
429
430
431
432
                    if (m_model->isActive()) {
                        pCore->getMonitor(m_model->monitorId)->setUpEffectGeometry(r);
                    }
        });
433
        paramWidget = geomWidget;
434
    } else if (type == ParamType::Roto_spline) {
435
436
437
        m_monitorHelper = new RotoHelper(pCore->getMonitor(m_model->monitorId), m_model, index, this);
        connect(m_monitorHelper, &KeyframeMonitorHelper::updateKeyframeData, this, &KeyframeWidget::slotUpdateKeyframesFromMonitor, Qt::UniqueConnection);
        m_neededScene = MonitorSceneType::MonitorSceneRoto;
438
    } else {
439
440
441
442
        if (m_model->getAssetId() == QLatin1String("frei0r.c0rners")) {
            if (m_neededScene == MonitorSceneDefault && !m_monitorHelper) {
                m_neededScene = MonitorSceneType::MonitorSceneCorners;
                m_monitorHelper = new CornersHelper(pCore->getMonitor(m_model->monitorId), m_model, index, this);
443
444
                connect(m_monitorHelper, &KeyframeMonitorHelper::updateKeyframeData, this, &KeyframeWidget::slotUpdateKeyframesFromMonitor,
                        Qt::UniqueConnection);
445
446
447
448
449
450
451
452
453
                connect(this, &KeyframeWidget::addIndex, m_monitorHelper, &CornersHelper::addIndex);
            } else {
                if (type == ParamType::KeyframeParam) {
                    int paramName = m_model->data(index, AssetParameterModel::NameRole).toInt();
                    if (paramName < 8) {
                        emit addIndex(index);
                    }
                }
            }
454
        }
455
        double value = m_keyframes->getInterpolatedValue(getPosition(), index).toDouble();
Simon Eugster's avatar
Simon Eugster committed
456
457
        double min = m_model->data(index, AssetParameterModel::MinRole).toDouble();
        double max = m_model->data(index, AssetParameterModel::MaxRole).toDouble();
458
        double defaultValue = m_model->data(index, AssetParameterModel::DefaultRole).toDouble();
459
        int decimals = m_model->data(index, AssetParameterModel::DecimalsRole).toInt();
Simon Eugster's avatar
Simon Eugster committed
460
        double factor = m_model->data(index, AssetParameterModel::FactorRole).toDouble();
Vincent Pinon's avatar
Vincent Pinon committed
461
        factor = qFuzzyIsNull(factor) ? 1 : factor;
462
        auto doubleWidget = new DoubleWidget(name, value, min, max, factor, defaultValue, comment, -1, suffix, decimals, m_model->data(index, AssetParameterModel::OddRole).toBool(), this);
Nicolas Carion's avatar
Nicolas Carion committed
463
        connect(doubleWidget, &DoubleWidget::valueChanged,
Vincent Pinon's avatar
Vincent Pinon committed
464
465
                this, [this, index](double v) {
            emit activateEffect();
466
467
            m_keyframes->updateKeyframe(GenTime(getPosition(), pCore->getCurrentFps()), QVariant(v), index);
        });
468
        doubleWidget->setDragObjectName(QString::number(index.row()));
469
470
471
472
        paramWidget = doubleWidget;
    }
    if (paramWidget) {
        m_parameters[index] = paramWidget;
473
        m_lay->addWidget(paramWidget);
474
475
        m_addedHeight += paramWidget->minimumHeight();
        setFixedHeight(m_baseHeight + m_addedHeight);
476
    }
477
}
478
479
480

void KeyframeWidget::slotInitMonitor(bool active)
{
481
    connectMonitor(active);
482
    Monitor *monitor = pCore->getMonitor(m_model->monitorId);
483
484
    if (m_keyframeview) {
        m_keyframeview->initKeyframePos();
485
        connect(monitor, &Monitor::updateScene, m_keyframeview, &KeyframeView::slotModelChanged, Qt::UniqueConnection);
486
487
488
489
490
    }
}

void KeyframeWidget::connectMonitor(bool active)
{
491
    if (m_monitorHelper) {
492
        if (m_monitorHelper->connectMonitor(active) && m_model->isActive()) {
493
494
495
            slotRefreshParams();
        }
    }
496
497
498
499
500
    Monitor *monitor = pCore->getMonitor(m_model->monitorId);
    if (active) {
        connect(monitor, &Monitor::seekToNextKeyframe, m_keyframeview, &KeyframeView::slotGoToNext, Qt::UniqueConnection);
        connect(monitor, &Monitor::seekToPreviousKeyframe, m_keyframeview, &KeyframeView::slotGoToPrev, Qt::UniqueConnection);
        connect(monitor, &Monitor::addRemoveKeyframe, m_keyframeview, &KeyframeView::slotAddRemove, Qt::UniqueConnection);
501
502
        connect(this, &KeyframeWidget::updateEffectKeyframe, monitor, &Monitor::setEffectKeyframe, Qt::DirectConnection);
        connect(monitor, &Monitor::seekToKeyframe, this, &KeyframeWidget::slotSeekToKeyframe, Qt::UniqueConnection);
503
504
505
506
    } else {
        disconnect(monitor, &Monitor::seekToNextKeyframe, m_keyframeview, &KeyframeView::slotGoToNext);
        disconnect(monitor, &Monitor::seekToPreviousKeyframe, m_keyframeview, &KeyframeView::slotGoToPrev);
        disconnect(monitor, &Monitor::addRemoveKeyframe, m_keyframeview, &KeyframeView::slotAddRemove);
507
508
        disconnect(this, &KeyframeWidget::updateEffectKeyframe, monitor, &Monitor::setEffectKeyframe);
        disconnect(monitor, &Monitor::seekToKeyframe, this, &KeyframeWidget::slotSeekToKeyframe);
509
    }
510
    for (const auto &w : m_parameters) {
Nicolas Carion's avatar
Nicolas Carion committed
511
        auto type = m_model->data(w.first, AssetParameterModel::TypeRole).value<ParamType>();
512
513
514
515
        if (type == ParamType::AnimatedRect) {
            ((GeometryWidget *)w.second)->connectMonitor(active);
            break;
        }
516
517
518
    }
}

Nicolas Carion's avatar
Nicolas Carion committed
519
void KeyframeWidget::slotUpdateKeyframesFromMonitor(const QPersistentModelIndex &index, const QVariant &res)
520
{
Vincent Pinon's avatar
Vincent Pinon committed
521
    emit activateEffect();
522
    if (m_keyframes->isEmpty()) {
523
524
525
526
527
528
        GenTime pos(pCore->getItemIn(m_model->getOwnerId()) + m_time->getValue(), pCore->getCurrentFps());
        if (m_time->getValue() > 0) {
            GenTime pos0(pCore->getItemIn(m_model->getOwnerId()), pCore->getCurrentFps());
            m_keyframes->addKeyframe(pos0, KeyframeType::Linear);
            m_keyframes->updateKeyframe(pos0, res, index);
        }
529
530
        m_keyframes->addKeyframe(pos, KeyframeType::Linear);
        m_keyframes->updateKeyframe(pos, res, index);
531
    } else if (m_keyframes->hasKeyframe(getPosition()) || m_keyframes->singleKeyframe()) {
532
        GenTime pos(getPosition(), pCore->getCurrentFps());
533
534
535
        if (m_keyframes->singleKeyframe() && KdenliveSettings::autoKeyframe() && m_neededScene == MonitorSceneType::MonitorSceneRoto) {
            m_keyframes->addKeyframe(pos, KeyframeType::Linear);
        }
536
        m_keyframes->updateKeyframe(pos, res, index);
537
538
    }
}
539
540
541

MonitorSceneType KeyframeWidget::requiredScene() const
{
542
    qDebug() << "// // // RESULTING REQUIRED SCENE: " << m_neededScene;
543
544
    return m_neededScene;
}
545
546
547
548
549
550
551
552

bool KeyframeWidget::keyframesVisible() const
{
    return m_keyframeview->isVisible();
}

void KeyframeWidget::showKeyframes(bool enable)
{
553
554
555
    if (enable && m_toolbar->isVisible()) {
        return;
    }
556
557
    m_toolbar->setVisible(enable);
    m_keyframeview->setVisible(enable);
558
    setFixedHeight(m_addedHeight + (enable ? m_baseHeight : 0));
559
}
560
561
562

void KeyframeWidget::slotCopyKeyframes()
{
563
    QJsonDocument effectDoc = m_model->toJson(false);
564
    if (effectDoc.isEmpty()) {
565
566
567
        return;
    }
    QClipboard *clipboard = QApplication::clipboard();
568
    clipboard->setText(QString(effectDoc.toJson()));
569
570
571
572
573
574
}

void KeyframeWidget::slotImportKeyframes()
{
    QClipboard *clipboard = QApplication::clipboard();
    QString values = clipboard->text();
575
    QList<QPersistentModelIndex> indexes;
576
577
578
    for (const auto &w : m_parameters) {
        indexes << w.first;
    }
579
    QPointer<KeyframeImport> import = new KeyframeImport(values, m_model, indexes, m_model->data(m_index, AssetParameterModel::ParentInRole).toInt(), m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(), this);
580
581
582
583
    if (import->exec() != QDialog::Accepted) {
        delete import;
        return;
    }
584
    import->importSelectedData();
585

586
587
    /*m_model->getKeyframeModel()->getKeyModel()->dataChanged(QModelIndex(), QModelIndex());*/
    /*m_model->modelChanged();
588
589
590
591
    qDebug()<<"//// UPDATING KEYFRAMES CORE---------";
    pCore->updateItemKeyframes(m_model->getOwnerId());*/
    delete import;
}
592
593
594

void KeyframeWidget::slotRemoveNextKeyframes()
{
595
596
    int pos = m_time->getValue() + m_model->data(m_index, AssetParameterModel::ParentInRole).toInt();
    m_keyframes->removeNextKeyframes(GenTime(pos, pCore->getCurrentFps()));
597
}
598
599
600
601
602
603
604


void KeyframeWidget::slotSeekToKeyframe(int ix)
{
    int pos = m_keyframes->getPosAtIndex(ix).frames(pCore->getCurrentFps());
    slotSetPosition(pos, true);
}