1 /*
2     SPDX-FileCopyrightText: 2017 Nicolas Carion
3     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4 */
5 
6 #ifndef KEYFRAMELISTMODEL_H
7 #define KEYFRAMELISTMODEL_H
8 
9 #include "assets/model/assetparametermodel.hpp"
10 #include "definitions.h"
11 #include "gentime.h"
12 #include "undohelper.hpp"
13 
14 #include <QAbstractListModel>
15 #include <QReadWriteLock>
16 
17 #include <map>
18 #include <memory>
19 
20 class AssetParameterModel;
21 class DocUndoStack;
22 class EffectItemModel;
23 
24 
25 enum class KeyframeType { Linear = mlt_keyframe_linear, Discrete = mlt_keyframe_discrete, Curve = mlt_keyframe_smooth };
26 Q_DECLARE_METATYPE(KeyframeType)
27 using Keyframe = std::pair<GenTime, KeyframeType>;
28 
29 /** @class KeyframeModel
30     @brief This class is the model for a list of keyframes.
31    A keyframe is defined by a time, a type and a value
32    We store them in a sorted fashion using a std::map
33  */
34 class KeyframeModel : public QAbstractListModel
35 {
36     Q_OBJECT
37 
38 public:
39     /** @brief Construct a keyframe list bound to the given effect
40        @param init_value is the value taken by the param at time 0.
41        @param model is the asset this parameter belong to
42        @param index is the index of this parameter in its model
43      */
44     explicit KeyframeModel(std::weak_ptr<AssetParameterModel> model, const QModelIndex &index, std::weak_ptr<DocUndoStack> undo_stack,
45                            QObject *parent = nullptr);
46 
47     enum { TypeRole = Qt::UserRole + 1, PosRole, FrameRole, ValueRole, NormalizedValueRole, SelectedRole, ActiveRole };
48     friend class KeyframeModelList;
49     friend class KeyframeWidget;
50     friend class KeyframeImport;
51 
52 protected:
53     /** @brief These methods should ONLY be called by keyframemodellist to ensure synchronisation
54      *  with keyframes from other parameters */
55     /** @brief Adds a keyframe at the given position. If there is already one then we update it.
56        @param pos defines the position of the keyframe, relative to the clip
57        @param type is the type of the keyframe.
58      */
59     bool addKeyframe(GenTime pos, KeyframeType type, QVariant value);
60     bool addKeyframe(int frame, double normalizedValue);
61     /** @brief Same function but accumulates undo/redo
62        @param notify: if true, send a signal to model
63      */
64     bool addKeyframe(GenTime pos, KeyframeType type, QVariant value, bool notify, Fun &undo, Fun &redo);
65 
66     /** @brief Removes the keyframe at the given position. */
67     bool removeKeyframe(int frame);
68     bool moveKeyframe(int oldPos, int pos, QVariant newVal);
69     /** @brief Duplicate a keyframe at the given position. */
70     bool duplicateKeyframe(GenTime srcPos, GenTime dstPos, Fun &undo, Fun &redo);
71     bool removeKeyframe(GenTime pos);
72     /** @brief Delete all the keyframes of the model */
73     bool removeAllKeyframes();
74     bool removeAllKeyframes(Fun &undo, Fun &redo);
75     bool removeNextKeyframes(GenTime pos, Fun &undo, Fun &redo);
76     QList<GenTime> getKeyframePos() const;
77 
78 protected:
79     /** @brief Same function but accumulates undo/redo */
80     bool removeKeyframe(GenTime pos, Fun &undo, Fun &redo, bool notify = true, bool updateSelection = true);
81 
82 public:
83     /** @brief moves a keyframe
84        @param oldPos is the old position of the keyframe
85        @param pos defines the new position of the keyframe, relative to the clip
86        @param logUndo if true, then an undo object is created
87     */
88     bool moveKeyframe(int oldPos, int pos, bool logUndo);
89     bool offsetKeyframes(int oldPos, int pos, bool logUndo);
90     bool moveKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, bool logUndo);
91     bool moveKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, Fun &undo, Fun &redo, bool updateView = true);
92 
93     /** @brief updates the value of a keyframe
94        @param old is the position of the keyframe
95        @param value is the new value of the param
96     */
97     Q_INVOKABLE bool updateKeyframe(int pos, double newVal);
98     bool updateKeyframe(GenTime pos, QVariant value);
99     bool updateKeyframeType(GenTime pos, int type, Fun &undo, Fun &redo);
100     bool updateKeyframe(GenTime pos, const QVariant &value, Fun &undo, Fun &redo, bool update = true);
101     /** @brief updates the value of a keyframe, without any management of undo/redo
102        @param pos is the position of the keyframe
103        @param value is the new value of the param
104     */
105     bool directUpdateKeyframe(GenTime pos, QVariant value);
106 
107     /** @brief Returns a keyframe data at given pos
108        ok is a return parameter, set to true if everything went good
109      */
110     Keyframe getKeyframe(const GenTime &pos, bool *ok) const;
111 
112     /** @brief Returns true if we only have 1 keyframe
113      */
114     bool singleKeyframe() const;
115     /** @brief Returns the keyframe located after given position.
116        If there is a keyframe at given position it is ignored.
117        @param ok is a return parameter to tell if a keyframe was found.
118     */
119     Keyframe getNextKeyframe(const GenTime &pos, bool *ok) const;
120 
121     /** @brief Returns the keyframe located before given position.
122        If there is a keyframe at given position it is ignored.
123        @param ok is a return parameter to tell if a keyframe was found.
124     */
125     Keyframe getPrevKeyframe(const GenTime &pos, bool *ok) const;
126 
127     /** @brief Returns the closest keyframe from given position.
128        @param ok is a return parameter to tell if a keyframe was found.
129     */
130     Keyframe getClosestKeyframe(const GenTime &pos, bool *ok) const;
131 
132     /** @brief Returns true if a keyframe exists at given pos
133        Notice that add/remove queries are done in real time (gentime), but this request is made in frame
134      */
135     Q_INVOKABLE bool hasKeyframe(int frame) const;
136     Q_INVOKABLE bool hasKeyframe(const GenTime &pos) const;
137     Q_INVOKABLE QString realValue(double normalizedValue) const;
138 
139     /** @brief Read the value from the model and update itself accordingly */
140     void refresh();
141     /** @brief Reset all values to their default */
142     void reset();
143 
144     /** @brief Return the interpolated value at given pos */
145     QVariant getInterpolatedValue(int pos) const;
146     QVariant getInterpolatedValue(const GenTime &pos) const;
147     QVariant updateInterpolated(const QVariant &interpValue, double val);
148     /** @brief Return the real value from a normalized one */
149     QVariant getNormalizedValue(double newVal) const;
150     /** @brief Set or add a keyframe to selection */
151     Q_INVOKABLE void setSelectedKeyframe(int ix, bool add);
152     void setSelectedKeyframes(QVector<int> selection);
153 
154     Q_INVOKABLE int activeKeyframe() const;
155     Q_INVOKABLE void setActiveKeyframe(int ix);
156     int getIndexForPos(const GenTime pos) const;
157     GenTime getPosAtIndex(int ix) const;
158 
159     // Mandatory overloads
160     Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override;
161     QHash<int, QByteArray> roleNames() const override;
162     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
163     static QList<QPoint> getRanges(const QString &animData, const std::shared_ptr<AssetParameterModel> &model);
164     static std::shared_ptr<Mlt::Properties> getAnimation(std::shared_ptr<AssetParameterModel> model, const QString &animData, int duration = 0);
165     static const QString getAnimationStringWithOffset(std::shared_ptr<AssetParameterModel> model, const QString &animData, int offset);
166 
167 protected:
168     /** @brief Helper function that generate a lambda to change type / value of given keyframe */
169     Fun updateKeyframe_lambda(GenTime pos, KeyframeType type, const QVariant &value, bool notify);
170 
171     /** @brief Helper function that generate a lambda to add given keyframe */
172     Fun addKeyframe_lambda(GenTime pos, KeyframeType type, const QVariant &value, bool notify);
173 
174     /** @brief Helper function that generate a lambda to remove given keyframe */
175     Fun deleteKeyframe_lambda(GenTime pos, bool notify);
176 
177     /** @brief Connects the signals of this object */
178     void setup();
179 
180     /** @brief Commit the modification to the model */
181     void sendModification();
182 
183     /** @brief returns the keyframes as a Mlt Anim Property string.
184         It is defined as pairs of frame and value, separated by ;
185         Example : "0|=50; 50|=100; 100=200; 200~=60;"
186         Spaces are ignored by Mlt.
187         |= represents a discrete keyframe, = a linear one and ~= a Catmull-Rom spline
188     */
189     QString getAnimProperty() const;
190     QString getRotoProperty() const;
191 
192     /** @brief this function clears all existing keyframes, and reloads its data from the string passed */
193     void resetAnimProperty(const QString &prop);
194     /** @brief this function does the opposite of getAnimProperty: given a MLT representation of an animation, build the corresponding model */
195     void parseAnimProperty(const QString &prop);
196     void parseRotoProperty(const QString &prop);
197 
198 private:
199     std::weak_ptr<AssetParameterModel> m_model;
200     std::weak_ptr<DocUndoStack> m_undoStack;
201     QPersistentModelIndex m_index;
202     QString m_lastData;
203     ParamType m_paramType;
204     /** @brief This is a lock that ensures safety in case of concurrent access */
205     mutable QReadWriteLock m_lock;
206 
207     std::map<GenTime, std::pair<KeyframeType, QVariant>> m_keyframeList;
208     bool moveOneKeyframe(GenTime oldPos, GenTime pos, QVariant newVal, Fun &undo, Fun &redo, bool updateView = true);
209 
210 signals:
211     void modelChanged();
212     void requestModelUpdate(const QModelIndex &, const QModelIndex &, const QVector<int>&);
213 
214 public:
215     // this is to enable for range loops
begin()216     auto begin() -> decltype(m_keyframeList.begin()) { return m_keyframeList.begin(); }
end()217     auto end() -> decltype(m_keyframeList.end()) { return m_keyframeList.end(); }
218 };
219 // Q_DECLARE_METATYPE(KeyframeModel *)
220 
221 #endif
222