File indexing completed on 2025-11-09 08:26:33

0001 /*
0002     SPDX-FileCopyrightText: 2017 Nicolas Carion
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #pragma once
0007 
0008 #include "assets/model/assetparametermodel.hpp"
0009 #include "definitions.h"
0010 #include "undohelper.hpp"
0011 #include "utils/gentime.h"
0012 
0013 #include <QAbstractListModel>
0014 #include <QReadWriteLock>
0015 #include <QtGlobal>
0016 
0017 #include <framework/mlt_version.h>
0018 
0019 #include <map>
0020 #include <memory>
0021 
0022 class AssetParameterModel;
0023 class DocUndoStack;
0024 class EffectItemModel;
0025 
0026 #define mltMinVersion QT_VERSION_CHECK(7, 20, 0)
0027 #define currentVersion QT_VERSION_CHECK(LIBMLT_VERSION_MAJOR, LIBMLT_VERSION_MINOR, 0)
0028 
0029 #if currentVersion > mltMinVersion
0030 #define USE_MLT_NEW_KEYFRAMES
0031 enum class KeyframeType {
0032     Linear = mlt_keyframe_linear,
0033     Discrete = mlt_keyframe_discrete,
0034     Curve = mlt_keyframe_smooth,
0035     CurveSmooth = mlt_keyframe_smooth_natural,
0036     BounceIn = mlt_keyframe_bounce_in,
0037     BounceOut = mlt_keyframe_bounce_out,
0038     CubicIn = mlt_keyframe_cubic_in,
0039     CubicOut = mlt_keyframe_cubic_out,
0040     ExponentialIn = mlt_keyframe_exponential_in,
0041     ExponentialOut = mlt_keyframe_exponential_out,
0042     CircularIn = mlt_keyframe_circular_in,
0043     CircularOut = mlt_keyframe_circular_out,
0044     ElasticIn = mlt_keyframe_elastic_in,
0045     ElasticOut = mlt_keyframe_elastic_out
0046 };
0047 #else
0048 enum class KeyframeType { Linear = mlt_keyframe_linear, Discrete = mlt_keyframe_discrete, Curve = mlt_keyframe_smooth };
0049 #endif
0050 Q_DECLARE_METATYPE(KeyframeType)
0051 using Keyframe = std::pair<GenTime, KeyframeType>;
0052 
0053 /** @class KeyframeModel
0054     @brief This class is the model for a list of keyframes.
0055    A keyframe is defined by a time, a type and a value
0056    We store them in a sorted fashion using a std::map
0057  */
0058 class KeyframeModel : public QAbstractListModel
0059 {
0060     Q_OBJECT
0061 
0062 public:
0063     /** @brief Construct a keyframe list bound to the given effect
0064        @param init_value is the value taken by the param at time 0.
0065        @param model is the asset this parameter belong to
0066        @param index is the index of this parameter in its model
0067      */
0068     explicit KeyframeModel(std::weak_ptr<AssetParameterModel> model, const QModelIndex &index, std::weak_ptr<DocUndoStack> undo_stack, int in = -1,
0069                            int out = -1, QObject *parent = nullptr);
0070 
0071     enum { TypeRole = Qt::UserRole + 1, PosRole, FrameRole, ValueRole, NormalizedValueRole, SelectedRole, ActiveRole, MoveOnlyRole };
0072     friend class KeyframeModelList;
0073     friend class KeyframeWidget;
0074     friend class KeyframeImport;
0075     friend class AssetMultiKeyframeCommand;
0076 
0077 protected:
0078     /** @brief These methods should ONLY be called by keyframemodellist to ensure synchronisation
0079      *  with keyframes from other parameters */
0080     /** @brief Adds a keyframe at the given position. If there is already one then we update it.
0081        @param pos defines the position of the keyframe, relative to the clip
0082        @param type is the type of the keyframe.
0083      */
0084     bool addKeyframe(GenTime pos, KeyframeType type, QVariant value);
0085     bool addKeyframe(int frame, double normalizedValue);
0086     /** @brief Same function but accumulates undo/redo
0087        @param notify: if true, send a signal to model
0088      */
0089     bool addKeyframe(GenTime pos, KeyframeType type, QVariant value, bool notify, Fun &undo, Fun &redo);
0090 
0091     /** @brief Removes the keyframe at the given position. */
0092     bool removeKeyframe(int frame);
0093     bool moveKeyframe(int oldPos, int pos, QVariant newVal);
0094     /** @brief Duplicate a keyframe at the given position. */
0095     bool duplicateKeyframe(GenTime srcPos, GenTime dstPos, Fun &undo, Fun &redo);
0096     bool removeKeyframe(GenTime pos);
0097     /** @brief Delete all the keyframes of the model */
0098     bool removeAllKeyframes();
0099     bool removeAllKeyframes(Fun &undo, Fun &redo);
0100     bool removeNextKeyframes(GenTime pos, Fun &undo, Fun &redo);
0101     QList<GenTime> getKeyframePos() const;
0102 
0103 protected:
0104     /** @brief Same function but accumulates undo/redo */
0105     bool removeKeyframe(GenTime pos, Fun &undo, Fun &redo, bool notify = true, bool updateSelection = true);
0106 
0107 public:
0108     /** @brief moves a keyframe
0109        @param oldPos is the old position of the keyframe
0110        @param pos defines the new position of the keyframe, relative to the clip
0111        @param logUndo if true, then an undo object is created
0112     */
0113     bool moveKeyframe(int oldPos, int pos, bool logUndo);
0114     bool offsetKeyframes(int oldPos, int pos, bool logUndo);
0115     bool moveKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, bool logUndo);
0116     bool moveKeyframe(GenTime oldPos, GenTime pos, const QVariant &newVal, Fun &undo, Fun &redo, bool updateView = true);
0117 
0118     /** @brief updates the value of a keyframe
0119        @param old is the position of the keyframe
0120        @param value is the new value of the param
0121     */
0122     Q_INVOKABLE bool updateKeyframe(int pos, double newVal);
0123     bool updateKeyframe(GenTime pos, QVariant value);
0124     bool updateKeyframeType(GenTime pos, int type, Fun &undo, Fun &redo);
0125     bool updateKeyframe(GenTime pos, const QVariant &value, Fun &undo, Fun &redo, bool update = true);
0126     /** @brief updates the value of a keyframe, without any management of undo/redo
0127        @param pos is the position of the keyframe
0128        @param value is the new value of the param
0129     */
0130     bool directUpdateKeyframe(GenTime pos, QVariant value, bool notify = true);
0131 
0132     /** @brief Returns a keyframe data at given pos
0133        ok is a return parameter, set to true if everything went good
0134      */
0135     Keyframe getKeyframe(const GenTime &pos, bool *ok) const;
0136 
0137     /** @brief Returns true if we only have 1 keyframe
0138      */
0139     bool singleKeyframe() const;
0140     /** @brief Returns the keyframe located after given position.
0141        If there is a keyframe at given position it is ignored.
0142        @param ok is a return parameter to tell if a keyframe was found.
0143     */
0144     Keyframe getNextKeyframe(const GenTime &pos, bool *ok) const;
0145 
0146     /** @brief Returns the keyframe located before given position.
0147        If there is a keyframe at given position it is ignored.
0148        @param ok is a return parameter to tell if a keyframe was found.
0149     */
0150     Keyframe getPrevKeyframe(const GenTime &pos, bool *ok) const;
0151 
0152     /** @brief Returns the closest keyframe from given position.
0153        @param ok is a return parameter to tell if a keyframe was found.
0154     */
0155     Keyframe getClosestKeyframe(const GenTime &pos, bool *ok) const;
0156 
0157     /** @brief Returns true if a keyframe exists at given pos
0158        Notice that add/remove queries are done in real time (gentime), but this request is made in frame
0159      */
0160     Q_INVOKABLE bool hasKeyframe(int frame) const;
0161     Q_INVOKABLE bool hasKeyframe(const GenTime &pos) const;
0162     Q_INVOKABLE QString realValue(double normalizedValue) const;
0163 
0164     /** @brief Read the value from the model and update itself accordingly */
0165     void refresh(int in = -1, int out = -1);
0166     /** @brief Reset all values to their default */
0167     void reset();
0168 
0169     /** @brief Return the interpolated value at given pos */
0170     QVariant getInterpolatedValue(int pos) const;
0171     QVariant getInterpolatedValue(const GenTime &pos) const;
0172     QVariant updateInterpolated(const QVariant &interpValue, double val);
0173     /** @brief Return the real value from a normalized one */
0174     QVariant getNormalizedValue(double newVal) const;
0175     /** @brief Set or add a keyframe to selection */
0176     Q_INVOKABLE void setSelectedKeyframe(int ix, bool add);
0177     void setSelectedKeyframes(QVector<int> selection);
0178 
0179     Q_INVOKABLE int activeKeyframe() const;
0180     Q_INVOKABLE void setActiveKeyframe(int ix);
0181     int getIndexForPos(const GenTime pos) const;
0182     GenTime getPosAtIndex(int ix) const;
0183 
0184     // Mandatory overloads
0185     Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override;
0186     QHash<int, QByteArray> roleNames() const override;
0187     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
0188     static QList<QPoint> getRanges(const QString &animData, const std::shared_ptr<AssetParameterModel> &model);
0189     static std::shared_ptr<Mlt::Properties> getAnimation(std::shared_ptr<AssetParameterModel> model, const QString &animData, int duration = 0);
0190     static const QString getAnimationStringWithOffset(std::shared_ptr<AssetParameterModel> model, const QString &animData, int offset, int duration,
0191                                                       ParamType paramType, bool useOpacity = true);
0192 
0193 protected:
0194     /** @brief Helper function that generate a lambda to change type / value of given keyframe */
0195     Fun updateKeyframe_lambda(GenTime pos, KeyframeType type, const QVariant &value, bool notify);
0196 
0197     /** @brief Helper function that generate a lambda to add given keyframe */
0198     Fun addKeyframe_lambda(GenTime pos, KeyframeType type, const QVariant &value, bool notify);
0199 
0200     /** @brief Helper function that generate a lambda to remove given keyframe */
0201     Fun deleteKeyframe_lambda(GenTime pos, bool notify);
0202 
0203     /** @brief Connects the signals of this object */
0204     void setup();
0205 
0206     /** @brief Commit the modification to the model */
0207     void sendModification();
0208 
0209     /** @brief returns the keyframes as a Mlt Anim Property string.
0210         It is defined as pairs of frame and value, separated by ;
0211         Example : "0|=50; 50|=100; 100=200; 200~=60;"
0212         Spaces are ignored by Mlt.
0213         |= represents a discrete keyframe, = a linear one and ~= a Catmull-Rom spline
0214     */
0215     QString getAnimProperty() const;
0216     QString getRotoProperty() const;
0217 
0218     /** @brief this function clears all existing keyframes, and reloads its data from the string passed */
0219     void resetAnimProperty(const QString &prop);
0220     /** @brief this function does the opposite of getAnimProperty: given a MLT representation of an animation, build the corresponding model */
0221     void parseAnimProperty(const QString &prop, int in = -1, int out = -1);
0222     void parseRotoProperty(const QString &prop);
0223 
0224 private:
0225     std::weak_ptr<AssetParameterModel> m_model;
0226     std::weak_ptr<DocUndoStack> m_undoStack;
0227     QPersistentModelIndex m_index;
0228     QString m_lastData;
0229     ParamType m_paramType;
0230     /** @brief This is a lock that ensures safety in case of concurrent access */
0231     mutable QReadWriteLock m_lock;
0232 
0233     std::map<GenTime, std::pair<KeyframeType, QVariant>> m_keyframeList;
0234     bool moveOneKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, Fun &undo, Fun &redo, bool updateView = true);
0235 
0236 Q_SIGNALS:
0237     void modelChanged();
0238     void requestModelUpdate(const QModelIndex &, const QModelIndex &, const QVector<int> &);
0239 
0240 public:
0241     // this is to enable for range loops
0242     auto begin() -> decltype(m_keyframeList.begin()) { return m_keyframeList.begin(); }
0243     auto end() -> decltype(m_keyframeList.end()) { return m_keyframeList.end(); }
0244 };
0245 // Q_DECLARE_METATYPE(KeyframeModel *)