File indexing completed on 2024-05-12 04:52:47

0001 /*
0002     SPDX-FileCopyrightText: 2017 Nicolas Carion
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "keyframemodellist.hpp"
0007 #include "assets/model/assetcommand.hpp"
0008 #include "assets/model/assetparametermodel.hpp"
0009 #include "core.h"
0010 #include "doc/docundostack.hpp"
0011 #include "keyframemodel.hpp"
0012 #include "klocalizedstring.h"
0013 #include "macros.hpp"
0014 #include <kdenlivesettings.h>
0015 
0016 #include <QDebug>
0017 #include <utility>
0018 KeyframeModelList::KeyframeModelList(std::weak_ptr<AssetParameterModel> model, const QModelIndex &index, std::weak_ptr<DocUndoStack> undo_stack, int in,
0019                                      int out)
0020     : m_model(std::move(model))
0021     , m_undoStack(std::move(undo_stack))
0022     , m_lock(QReadWriteLock::Recursive)
0023 {
0024     qDebug() << "Construct keyframemodellist. Checking model:" << m_model.expired();
0025     addParameter(index, in, out);
0026 }
0027 
0028 ObjectId KeyframeModelList::getOwnerId() const
0029 {
0030     if (auto ptr = m_model.lock()) {
0031         return ptr->getOwnerId();
0032     }
0033     return ObjectId();
0034 }
0035 
0036 const QString KeyframeModelList::getAssetId()
0037 {
0038     if (auto ptr = m_model.lock()) {
0039         return ptr->getAssetId();
0040     }
0041     return {};
0042 }
0043 
0044 QVector<int> KeyframeModelList::selectedKeyframes() const
0045 {
0046     if (auto ptr = m_model.lock()) {
0047         return ptr->m_selectedKeyframes;
0048     }
0049     return {};
0050 }
0051 
0052 int KeyframeModelList::activeKeyframe() const
0053 {
0054     if (auto ptr = m_model.lock()) {
0055         return ptr->m_activeKeyframe;
0056     }
0057     return -1;
0058 }
0059 
0060 void KeyframeModelList::setActiveKeyframe(int ix)
0061 {
0062     m_parameters.begin()->second->setActiveKeyframe(ix);
0063 }
0064 
0065 void KeyframeModelList::removeFromSelected(int ix)
0066 {
0067     m_parameters.begin()->second->setSelectedKeyframe(ix, true);
0068 }
0069 
0070 void KeyframeModelList::setSelectedKeyframes(const QVector<int> &list)
0071 {
0072     m_parameters.begin()->second->setSelectedKeyframes(list);
0073 }
0074 
0075 void KeyframeModelList::appendSelectedKeyframe(int ix)
0076 {
0077     m_parameters.begin()->second->setSelectedKeyframe(ix, true);
0078 }
0079 
0080 const QString KeyframeModelList::getAssetRow()
0081 {
0082     if (auto ptr = m_model.lock()) {
0083         return ptr->getAssetMltId();
0084     }
0085     return QString();
0086 }
0087 
0088 void KeyframeModelList::addParameter(const QModelIndex &index, int in, int out)
0089 {
0090     std::shared_ptr<KeyframeModel> parameter(new KeyframeModel(m_model, index, m_undoStack, in, out));
0091     connect(parameter.get(), &KeyframeModel::modelChanged, this, &KeyframeModelList::modelChanged);
0092     connect(parameter.get(), &KeyframeModel::requestModelUpdate, this, &KeyframeModelList::slotUpdateModels);
0093     m_parameters.insert({index, std::move(parameter)});
0094 }
0095 
0096 void KeyframeModelList::slotUpdateModels(const QModelIndex &ix1, const QModelIndex &ix2, const QVector<int> &roles)
0097 {
0098     // Propagate change to all keyframe models
0099     for (const auto &param : m_parameters) {
0100         Q_EMIT param.second->dataChanged(ix1, ix2, roles);
0101     }
0102     Q_EMIT modelDisplayChanged();
0103 }
0104 
0105 bool KeyframeModelList::applyOperation(const std::function<bool(std::shared_ptr<KeyframeModel>, Fun &, Fun &)> &op, const QString &undoString)
0106 {
0107     QWriteLocker locker(&m_lock);
0108     Q_ASSERT(m_parameters.size() > 0);
0109     Fun undo = []() { return true; };
0110     Fun redo = []() { return true; };
0111 
0112     bool res = true;
0113     for (const auto &param : m_parameters) {
0114         res = op(param.second, undo, redo);
0115         if (!res) {
0116             bool undone = undo();
0117             Q_ASSERT(undone);
0118             return res;
0119         }
0120     }
0121     if (res && !undoString.isEmpty()) {
0122         PUSH_UNDO(undo, redo, undoString);
0123     }
0124     return res;
0125 }
0126 
0127 bool KeyframeModelList::addKeyframe(GenTime pos, KeyframeType type)
0128 {
0129     QWriteLocker locker(&m_lock);
0130     Q_ASSERT(m_parameters.size() > 0);
0131     bool update = (m_parameters.begin()->second->hasKeyframe(pos) > 0);
0132     auto op = [pos, type](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) {
0133         QVariant value = param->getInterpolatedValue(pos);
0134         return param->addKeyframe(pos, type, value, true, undo, redo);
0135     };
0136     return applyOperation(op, update ? i18n("Change keyframe type") : i18n("Add keyframe"));
0137 }
0138 
0139 bool KeyframeModelList::addKeyframe(int frame, double val)
0140 {
0141     QWriteLocker locker(&m_lock);
0142     GenTime pos(frame, pCore->getCurrentFps());
0143     Q_ASSERT(m_parameters.size() > 0);
0144     bool update = (m_parameters.begin()->second->hasKeyframe(pos) > 0);
0145     bool isRectParam = false;
0146     if (m_inTimelineIndex.isValid()) {
0147         if (auto ptr = m_model.lock()) {
0148             auto tp = ptr->data(m_inTimelineIndex, AssetParameterModel::TypeRole).value<ParamType>();
0149             if (tp == ParamType::AnimatedRect) {
0150                 isRectParam = true;
0151             }
0152         }
0153     }
0154     auto op = [this, pos, val, isRectParam](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) {
0155         QVariant value;
0156         if (m_inTimelineIndex.isValid()) {
0157             if (m_parameters.at(m_inTimelineIndex) == param) {
0158                 if (isRectParam) {
0159                     value = param->getInterpolatedValue(pos);
0160                     value = param->updateInterpolated(value, val);
0161                 } else {
0162                     value = param->getNormalizedValue(val);
0163                 }
0164             } else {
0165                 value = param->getInterpolatedValue(pos);
0166             }
0167         } else if (m_parameters.begin()->second == param) {
0168             value = param->getNormalizedValue(val);
0169         } else {
0170             value = param->getInterpolatedValue(pos);
0171         }
0172         return param->addKeyframe(pos, KeyframeType(KdenliveSettings::defaultkeyframeinterp()), value, true, undo, redo);
0173     };
0174     return applyOperation(op, update ? i18n("Change keyframe type") : i18n("Add keyframe"));
0175 }
0176 
0177 bool KeyframeModelList::removeKeyframe(GenTime pos)
0178 {
0179     QWriteLocker locker(&m_lock);
0180     Q_ASSERT(m_parameters.size() > 0);
0181     auto op = [pos](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) { return param->removeKeyframe(pos, undo, redo); };
0182     return applyOperation(op, i18n("Delete keyframe"));
0183 }
0184 
0185 bool KeyframeModelList::removeKeyframeWithUndo(GenTime pos, Fun &undo, Fun &redo)
0186 {
0187     bool result = true;
0188     for (const auto &param : m_parameters) {
0189         result = result && param.second->removeKeyframe(pos, undo, redo);
0190     }
0191     return result;
0192 }
0193 
0194 bool KeyframeModelList::duplicateKeyframeWithUndo(GenTime srcPos, GenTime destPos, Fun &undo, Fun &redo)
0195 {
0196     bool result = true;
0197     for (const auto &param : m_parameters) {
0198         result = result && param.second->duplicateKeyframe(srcPos, destPos, undo, redo);
0199     }
0200     return result;
0201 }
0202 
0203 bool KeyframeModelList::removeAllKeyframes()
0204 {
0205     QWriteLocker locker(&m_lock);
0206     Q_ASSERT(m_parameters.size() > 0);
0207     auto op = [](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) { return param->removeAllKeyframes(undo, redo); };
0208     return applyOperation(op, i18n("Delete all keyframes"));
0209 }
0210 
0211 bool KeyframeModelList::removeNextKeyframes(GenTime pos)
0212 {
0213     QWriteLocker locker(&m_lock);
0214     Q_ASSERT(m_parameters.size() > 0);
0215     auto op = [pos](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) { return param->removeNextKeyframes(pos, undo, redo); };
0216     return applyOperation(op, i18n("Delete keyframes"));
0217 }
0218 
0219 bool KeyframeModelList::moveKeyframe(GenTime oldPos, GenTime pos, bool logUndo, bool updateView)
0220 {
0221     QWriteLocker locker(&m_lock);
0222     Q_ASSERT(m_parameters.size() > 0);
0223     auto op = [oldPos, pos, updateView](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) {
0224         return param->moveKeyframe(oldPos, pos, QVariant(), undo, redo, updateView);
0225     };
0226     return applyOperation(op, logUndo ? i18nc("@action", "Move keyframe") : QString());
0227 }
0228 
0229 bool KeyframeModelList::moveKeyframeWithUndo(GenTime oldPos, GenTime pos, Fun &undo, Fun &redo)
0230 {
0231     bool result = true;
0232     for (const auto &param : m_parameters) {
0233         result = result && param.second->moveKeyframe(oldPos, pos, QVariant(), undo, redo);
0234     }
0235     return result;
0236 }
0237 
0238 bool KeyframeModelList::updateKeyframe(GenTime oldPos, GenTime pos, const QVariant &normalizedVal, bool logUndo)
0239 {
0240     QWriteLocker locker(&m_lock);
0241     Q_ASSERT(m_parameters.size() > 0);
0242     bool isRectParam = false;
0243     if (m_inTimelineIndex.isValid()) {
0244         if (auto ptr = m_model.lock()) {
0245             auto tp = ptr->data(m_inTimelineIndex, AssetParameterModel::TypeRole).value<ParamType>();
0246             if (tp == ParamType::AnimatedRect) {
0247                 isRectParam = true;
0248             }
0249         }
0250     }
0251     auto op = [this, oldPos, pos, normalizedVal, isRectParam](std::shared_ptr<KeyframeModel> param, Fun &undo, Fun &redo) {
0252         QVariant value;
0253         if (m_inTimelineIndex.isValid()) {
0254             if (m_parameters.at(m_inTimelineIndex) == param) {
0255                 if (isRectParam) {
0256                     if (normalizedVal.isValid()) {
0257                         double newValue = normalizedVal.toDouble();
0258                         value = param->getInterpolatedValue(oldPos);
0259                         value = param->updateInterpolated(value, newValue);
0260                     }
0261                 } else {
0262                     value = normalizedVal;
0263                 }
0264             }
0265         } else if (m_parameters.begin()->second == param) {
0266             value = normalizedVal;
0267         }
0268         return param->moveKeyframe(oldPos, pos, value, undo, redo);
0269     };
0270     return applyOperation(op, logUndo ? i18nc("@action", "Move keyframe") : QString());
0271 }
0272 
0273 bool KeyframeModelList::updateKeyframe(GenTime pos, const QVariant &value, const QPersistentModelIndex &index, QUndoCommand *parentCommand)
0274 {
0275     if (singleKeyframe()) {
0276         bool ok = false;
0277         Keyframe kf = m_parameters.begin()->second->getNextKeyframe(GenTime(-1), &ok);
0278         pos = kf.first;
0279     }
0280     if (auto ptr = m_model.lock()) {
0281         auto *command = new AssetKeyframeCommand(ptr, index, value, pos, parentCommand);
0282         if (parentCommand == nullptr) {
0283             pCore->pushUndo(command);
0284         } // clang-tidy: else "command" is leaked? no because is was pushed to parentCommand
0285     }
0286     return true; // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
0287 }
0288 
0289 bool KeyframeModelList::updateMultiKeyframe(GenTime pos, const QStringList &sourceValues, const QStringList &values, const QList<QModelIndex> &indexes,
0290                                             QUndoCommand *parentCommand)
0291 {
0292     if (singleKeyframe()) {
0293         bool ok = false;
0294         Keyframe kf = m_parameters.begin()->second->getNextKeyframe(GenTime(-1), &ok);
0295         pos = kf.first;
0296     }
0297     if (auto ptr = m_model.lock()) {
0298         auto *command = new AssetMultiKeyframeCommand(ptr, indexes, sourceValues, values, pos, parentCommand);
0299         if (parentCommand == nullptr) {
0300             pCore->pushUndo(command);
0301         } // clang-tidy: else "command" is leaked? no because is was pushed to parentCommand
0302     }
0303     return true; // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
0304 }
0305 
0306 bool KeyframeModelList::updateKeyframeType(GenTime pos, int type, const QPersistentModelIndex &index)
0307 {
0308     QWriteLocker locker(&m_lock);
0309     Q_ASSERT(m_parameters.count(index) > 0);
0310     Fun undo = []() { return true; };
0311     Fun redo = []() { return true; };
0312     if (singleKeyframe()) {
0313         bool ok = false;
0314         Keyframe kf = m_parameters.begin()->second->getNextKeyframe(GenTime(-1), &ok);
0315         pos = kf.first;
0316     }
0317     // Update kf type in all parameters
0318     bool res = true;
0319     for (const auto &param : m_parameters) {
0320         res = res && param.second->updateKeyframeType(pos, type, undo, redo);
0321     }
0322     if (res) {
0323         PUSH_UNDO(undo, redo, i18n("Update keyframe"));
0324     }
0325     return res;
0326 }
0327 
0328 KeyframeType KeyframeModelList::keyframeType(GenTime pos) const
0329 {
0330     QWriteLocker locker(&m_lock);
0331     if (singleKeyframe()) {
0332         bool ok = false;
0333         Keyframe kf = m_parameters.begin()->second->getNextKeyframe(GenTime(-1), &ok);
0334         return kf.second;
0335     }
0336     bool ok = false;
0337     Keyframe kf = m_parameters.begin()->second->getKeyframe(pos, &ok);
0338     return kf.second;
0339 }
0340 
0341 Keyframe KeyframeModelList::getKeyframe(const GenTime &pos, bool *ok) const
0342 {
0343     READ_LOCK();
0344     Q_ASSERT(m_parameters.size() > 0);
0345     return m_parameters.begin()->second->getKeyframe(pos, ok);
0346 }
0347 
0348 bool KeyframeModelList::singleKeyframe() const
0349 {
0350     READ_LOCK();
0351     Q_ASSERT(m_parameters.size() > 0);
0352     return m_parameters.begin()->second->singleKeyframe();
0353 }
0354 
0355 bool KeyframeModelList::isEmpty() const
0356 {
0357     READ_LOCK();
0358     return (m_parameters.size() == 0 || m_parameters.begin()->second->rowCount() == 0);
0359 }
0360 
0361 int KeyframeModelList::count() const
0362 {
0363     READ_LOCK();
0364     if (m_parameters.size() > 0) return m_parameters.begin()->second->rowCount();
0365     return 0;
0366 }
0367 
0368 Keyframe KeyframeModelList::getNextKeyframe(const GenTime &pos, bool *ok) const
0369 {
0370     READ_LOCK();
0371     Q_ASSERT(m_parameters.size() > 0);
0372     return m_parameters.begin()->second->getNextKeyframe(pos, ok);
0373 }
0374 
0375 Keyframe KeyframeModelList::getPrevKeyframe(const GenTime &pos, bool *ok) const
0376 {
0377     READ_LOCK();
0378     Q_ASSERT(m_parameters.size() > 0);
0379     return m_parameters.begin()->second->getPrevKeyframe(pos, ok);
0380 }
0381 
0382 Keyframe KeyframeModelList::getClosestKeyframe(const GenTime &pos, bool *ok) const
0383 {
0384     READ_LOCK();
0385     Q_ASSERT(m_parameters.size() > 0);
0386     return m_parameters.begin()->second->getClosestKeyframe(pos, ok);
0387 }
0388 
0389 bool KeyframeModelList::hasKeyframe(int frame) const
0390 {
0391     READ_LOCK();
0392     Q_ASSERT(m_parameters.size() > 0);
0393     return m_parameters.begin()->second->hasKeyframe(frame);
0394 }
0395 
0396 void KeyframeModelList::refresh()
0397 {
0398     QWriteLocker locker(&m_lock);
0399     for (const auto &param : m_parameters) {
0400         param.second->refresh();
0401     }
0402 }
0403 
0404 void KeyframeModelList::setParametersFromTask(const paramVector &params)
0405 {
0406     Fun redo = []() { return true; };
0407     Fun undo = []() { return true; };
0408     paramVector previousParams;
0409     // Collect all parameters previous values for undo
0410     if (auto ptr = m_model.lock()) {
0411         READ_LOCK();
0412         for (const auto &param : m_parameters) {
0413             const QString paramName = ptr->data(param.first, AssetParameterModel::NameRole).toString();
0414             QPair<QString, QVariant> val(paramName, ptr->getParamFromName(paramName));
0415             previousParams.append(val);
0416         }
0417     }
0418     QStringList updatedNames;
0419     paramVector currentValues;
0420     if (auto ptr = m_model.lock()) {
0421         for (auto &p : params) {
0422             currentValues.append({p.first, ptr->getParamFromName(p.first)});
0423             updatedNames << p.first;
0424         }
0425     }
0426     Fun local_redo = [this, params]() {
0427         if (auto ptr = m_model.lock()) {
0428             ptr->setParameters(params);
0429         }
0430         return true;
0431     };
0432     Fun local_undo = [this, currentValues]() {
0433         if (auto ptr = m_model.lock()) {
0434             ptr->setParameters(currentValues);
0435         }
0436         return true;
0437     };
0438     local_redo();
0439     if (auto ptr = m_model.lock()) {
0440         const QModelIndex ix = ptr->getParamIndexFromName(params.first().first);
0441         if (ix.isValid()) {
0442             QList<GenTime> kfrs = m_parameters.at(ix)->getKeyframePos();
0443             auto type = KeyframeType(KdenliveSettings::defaultkeyframeinterp());
0444             for (const auto &p : m_parameters) {
0445                 const QString paramName = ptr->data(p.first, AssetParameterModel::NameRole).toString();
0446                 if (!updatedNames.contains(paramName)) {
0447                     //  We need to sync this parameter keyframes.
0448                     // Remove all kfrs first
0449                     p.second->removeAllKeyframes(undo, redo);
0450                     // Now, sync
0451                     for (auto &k : kfrs) {
0452                         if (!p.second->hasKeyframe(k)) {
0453                             QVariant value = p.second->getInterpolatedValue(k);
0454                             p.second->addKeyframe(k, type, value, false, undo, redo);
0455                         }
0456                     }
0457                 }
0458             }
0459         }
0460         refresh();
0461         UPDATE_UNDO_REDO_NOLOCK(local_redo, local_undo, undo, redo);
0462     }
0463     pCore->pushUndo(undo, redo, i18n("Update effect"));
0464 }
0465 
0466 void KeyframeModelList::reset()
0467 {
0468     QWriteLocker locker(&m_lock);
0469     for (const auto &param : m_parameters) {
0470         param.second->reset();
0471     }
0472 }
0473 
0474 QVariant KeyframeModelList::getInterpolatedValue(int pos, const QPersistentModelIndex &index) const
0475 {
0476     READ_LOCK();
0477     Q_ASSERT(m_parameters.count(index) > 0);
0478     return m_parameters.at(index)->getInterpolatedValue(pos);
0479 }
0480 
0481 QVariant KeyframeModelList::getInterpolatedValue(const GenTime &pos, const QPersistentModelIndex &index) const
0482 {
0483     READ_LOCK();
0484     Q_ASSERT(m_parameters.count(index) > 0);
0485     return m_parameters.at(index)->getInterpolatedValue(pos);
0486 }
0487 
0488 KeyframeModel *KeyframeModelList::getKeyModel()
0489 {
0490     if (m_inTimelineIndex.isValid()) {
0491         return m_parameters.at(m_inTimelineIndex).get();
0492     }
0493     if (auto ptr = m_model.lock()) {
0494         for (const auto &param : m_parameters) {
0495             auto tp = ptr->data(param.first, AssetParameterModel::TypeRole).value<ParamType>();
0496             if (tp == ParamType::AnimatedRect) {
0497                 // Check if we have an opacity
0498                 if (ptr->data(param.first, AssetParameterModel::OpacityRole).toBool() == false) {
0499                     // Rect with no opacity, don't show timeline keyframes
0500                     continue;
0501                 }
0502             }
0503             if (ptr->data(param.first, AssetParameterModel::ShowInTimelineRole) == true) {
0504                 m_inTimelineIndex = param.first;
0505                 return param.second.get();
0506             }
0507         }
0508     }
0509     return nullptr;
0510 }
0511 
0512 KeyframeModel *KeyframeModelList::getKeyModel(const QPersistentModelIndex &index)
0513 {
0514     if (m_parameters.size() > 0 && m_parameters.find(index) != m_parameters.end()) {
0515         return m_parameters.at(index).get();
0516     }
0517     return nullptr;
0518 }
0519 
0520 void KeyframeModelList::moveKeyframes(int oldIn, int in, Fun &undo, Fun &redo)
0521 {
0522     // Get list of keyframes positions
0523     QList<GenTime> positions = m_parameters.begin()->second->getKeyframePos();
0524     GenTime offset(in - oldIn, pCore->getCurrentFps());
0525     if (in > oldIn) {
0526         // Moving to the right, process in reverse order to prevent collisions
0527         std::sort(positions.begin(), positions.end(), std::greater<>());
0528     } else {
0529         std::sort(positions.begin(), positions.end());
0530     }
0531     for (const auto &param : m_parameters) {
0532         for (auto frame : qAsConst(positions)) {
0533             param.second->moveKeyframe(frame, frame + offset, QVariant(), undo, redo);
0534         }
0535     }
0536 }
0537 
0538 void KeyframeModelList::resizeKeyframes(int oldIn, int oldOut, int in, int out, int offset, bool adjustFromEnd, Fun &undo, Fun &redo)
0539 {
0540     bool ok = false, ok2 = false;
0541     QList<GenTime> positions;
0542     if (!adjustFromEnd) {
0543         if (offset != 0) {
0544             // this is an endless resize clip
0545             GenTime old_in(oldIn, pCore->getCurrentFps());
0546             GenTime new_in(in + offset, pCore->getCurrentFps());
0547             getKeyframe(new_in, &ok2);
0548             positions = m_parameters.begin()->second->getKeyframePos();
0549             std::sort(positions.begin(), positions.end());
0550             for (const auto &param : m_parameters) {
0551                 if (offset > 0) {
0552                     QVariant value = param.second->getInterpolatedValue(new_in);
0553                     param.second->updateKeyframe(old_in, value, undo, redo);
0554                 }
0555                 for (auto frame : qAsConst(positions)) {
0556                     if (new_in > GenTime()) {
0557                         if (frame > new_in) {
0558                             param.second->moveKeyframe(frame, frame - new_in, QVariant(), undo, redo);
0559                             continue;
0560                         }
0561                     } else if (frame > GenTime()) {
0562                         param.second->moveKeyframe(frame, frame - new_in, QVariant(), undo, redo);
0563                         continue;
0564                     }
0565                     if (frame != GenTime()) {
0566                         param.second->removeKeyframe(frame, undo, redo);
0567                     }
0568                 }
0569             }
0570         } else if (oldIn != in) {
0571             GenTime old_in(oldIn, pCore->getCurrentFps());
0572             GenTime new_in(in, pCore->getCurrentFps());
0573             Keyframe kf = getKeyframe(old_in, &ok);
0574             KeyframeType type = kf.second;
0575             getKeyframe(new_in, &ok2);
0576             if (!ok2) {
0577                 // Add new in point
0578                 for (const auto &param : m_parameters) {
0579                     QVariant value = param.second->getInterpolatedValue(new_in);
0580                     param.second->addKeyframe(new_in, type, value, true, undo, redo);
0581                 }
0582             }
0583             if (ok) {
0584                 // Remove previous in point
0585                 for (const auto &param : m_parameters) {
0586                     param.second->removeKeyframe(old_in, undo, redo);
0587                 }
0588             }
0589             // Remove all keyframes before in
0590             bool nextOk = false;
0591             kf = m_parameters.begin()->second->getNextKeyframe(GenTime(-1), &nextOk);
0592             GenTime pos;
0593             while (nextOk) {
0594                 pos = kf.first;
0595                 if (pos < new_in) {
0596                     for (const auto &param : m_parameters) {
0597                         param.second->removeKeyframe(pos, undo, redo);
0598                     }
0599                     kf = m_parameters.begin()->second->getNextKeyframe(pos, &nextOk);
0600                 } else {
0601                     break;
0602                 }
0603             }
0604             // qDebug()<<"/// \n\nKEYS TO DELETE: "<<positions<<"\n------------------------";
0605         }
0606     } else {
0607         // Adjusting clip end
0608         GenTime old_out(oldOut, pCore->getCurrentFps());
0609         GenTime new_out(out, pCore->getCurrentFps());
0610         Keyframe kf = getKeyframe(old_out, &ok);
0611         KeyframeType type = kf.second;
0612         getKeyframe(new_out, &ok2);
0613         // Check keyframes after last position
0614         bool ok3;
0615         Keyframe toDel = getNextKeyframe(new_out, &ok3);
0616         if (ok && !ok2) {
0617             // Check if we have only 2 keyframes (in/out), in which case we move the out keyframe to new position
0618             bool ok4;
0619             kf = getPrevKeyframe(old_out, &ok4);
0620             if (ok4) {
0621                 GenTime current_in(oldIn, pCore->getCurrentFps());
0622                 qDebug() << " = = = = = = = \n\nGOT 2 KF SITUATION: " << current_in.frames(25) << " = " << kf.first.frames(25);
0623                 if (kf.first == current_in) {
0624                     // We have a 2 keyframes situation, move last one to new out
0625                     for (const auto &param : m_parameters) {
0626                         param.second->moveKeyframe(old_out, new_out, QVariant(), undo, redo);
0627                     }
0628                     return;
0629                 }
0630             }
0631             positions << old_out;
0632         }
0633         if (toDel.first == GenTime()) {
0634             // No keyframes
0635             return;
0636         }
0637         while (ok3) {
0638             if (!positions.contains(toDel.first)) {
0639                 positions << toDel.first;
0640             }
0641             toDel = getNextKeyframe(toDel.first, &ok3);
0642         }
0643         if ((ok || positions.size() > 0) && !ok2 && !singleKeyframe()) {
0644             for (const auto &param : m_parameters) {
0645                 QVariant value = param.second->getInterpolatedValue(new_out);
0646                 param.second->addKeyframe(new_out, type, value, true, undo, redo);
0647                 for (auto frame : qAsConst(positions)) {
0648                     param.second->removeKeyframe(frame, undo, redo);
0649                 }
0650             }
0651         }
0652     }
0653 }
0654 
0655 void KeyframeModelList::checkConsistency()
0656 {
0657     if (m_parameters.size() < 2) {
0658         return;
0659     }
0660     // Check keyframes in all parameters
0661     QList<GenTime> fullList;
0662     for (const auto &param : m_parameters) {
0663         QList<GenTime> list = param.second->getKeyframePos();
0664         for (auto &time : list) {
0665             if (!fullList.contains(time)) {
0666                 fullList << time;
0667             }
0668         }
0669     }
0670     Fun local_update = []() { return true; };
0671     auto type = KeyframeType(KdenliveSettings::defaultkeyframeinterp());
0672     for (const auto &param : m_parameters) {
0673         QList<GenTime> list = param.second->getKeyframePos();
0674         for (auto &time : fullList) {
0675             if (!list.contains(time)) {
0676                 qDebug() << " = = = \n\n = = = = \n\nWARNING; MISSING KF DETECTED AT: " << time.seconds() << "\n\n= = = \n\n= = =";
0677                 pCore->displayMessage(i18n("Missing keyframe detected at %1, automatically re-added", time.seconds()), ErrorMessage);
0678                 QVariant missingVal = param.second->getInterpolatedValue(time);
0679                 local_update = param.second->addKeyframe_lambda(time, type, missingVal, false);
0680                 local_update();
0681             }
0682         }
0683     }
0684 }
0685 
0686 GenTime KeyframeModelList::getPosAtIndex(int ix)
0687 {
0688     return m_parameters.begin()->second->getPosAtIndex(ix);
0689 }
0690 
0691 int KeyframeModelList::getIndexForPos(GenTime pos)
0692 {
0693     return m_parameters.begin()->second->getIndexForPos(pos);
0694 }
0695 
0696 QModelIndex KeyframeModelList::getIndexAtRow(int row)
0697 {
0698     for (auto &w : m_parameters) {
0699         if (w.first.row() == row) {
0700             return w.first;
0701         }
0702     }
0703     return QModelIndex();
0704 }
0705 
0706 std::vector<QPersistentModelIndex> KeyframeModelList::getIndexes()
0707 {
0708     std::vector<QPersistentModelIndex> keys;
0709     keys.reserve(m_parameters.size());
0710 
0711     for (auto kv : m_parameters) {
0712         keys.push_back(kv.first);
0713     }
0714     return keys;
0715 }