1 /*
2     SPDX-FileCopyrightText: 2017 Nicolas Carion
3     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4 */
5 
6 #ifndef KEYFRAMELISTMODELLIST_H
7 #define KEYFRAMELISTMODELLIST_H
8 
9 #include "definitions.h"
10 #include "gentime.h"
11 #include "keyframemodel.hpp"
12 #include "undohelper.hpp"
13 
14 #include <QReadWriteLock>
15 
16 #include <QObject>
17 #include <map>
18 #include <memory>
19 #include <unordered_map>
20 
21 class AssetParameterModel;
22 class DocUndoStack;
23 
24 /** @class KeyframeModelList
25     @brief This class is a container for the keyframe models.
26    If an asset has several keyframable parameters, each one has its own keyframeModel,
27    but we regroup all of these in a common class to provide unified access.
28  */
29 class KeyframeModelList : public QObject
30 {
31     Q_OBJECT
32 
33 public:
34     /** @brief Construct a keyframe list bound to the given asset
35        @param init_value and index correspond to the first parameter
36      */
37     explicit KeyframeModelList(std::weak_ptr<AssetParameterModel> model, const QModelIndex &index, std::weak_ptr<DocUndoStack> undo_stack);
38 
39     /** @brief Add a keyframable parameter to be managed by this model */
40     void addParameter(const QModelIndex &index);
41 
42     /** @brief Adds a keyframe at the given position. If there is already one then we update it.
43        @param pos defines the position of the keyframe, relative to the clip
44        @param type is the type of the keyframe.
45      */
46     bool addKeyframe(GenTime pos, KeyframeType type);
47     bool addKeyframe(int frame, double val);
48 
49     /** @brief Removes the keyframe at the given position. */
50     bool removeKeyframe(GenTime pos);
51     bool removeKeyframeWithUndo(GenTime pos, Fun &undo, Fun &redo);
52 
53     /** @brief Duplicate a keyframe at the given position. */
54     bool duplicateKeyframeWithUndo(GenTime srcPos, GenTime destPos, Fun &undo, Fun &redo);
55     /** @brief Delete all the keyframes of the model (except first) */
56     bool removeAllKeyframes();
57     /** @brief Delete all the keyframes after a certain position (except first) */
58     bool removeNextKeyframes(GenTime pos);
59 
60     /** @brief moves a keyframe
61        @param oldPos is the old position of the keyframe
62        @param pos defines the new position of the keyframe, relative to the clip
63        @param logUndo if true, then an undo object is created
64     */
65     bool moveKeyframe(GenTime oldPos, GenTime pos, bool logUndo, bool updateView = true);
66     bool moveKeyframeWithUndo(GenTime oldPos, GenTime pos, Fun &undo, Fun &redo);
67 
68     /** @brief updates the value of a keyframe
69        @param old is the position of the keyframe
70        @param value is the new value of the param
71        @param index is the index of the wanted keyframe
72     */
73     bool updateKeyframe(GenTime pos, const QVariant &value, const QPersistentModelIndex &index, QUndoCommand *parentCommand = nullptr);
74     bool updateKeyframeType(GenTime pos, int type, const QPersistentModelIndex &index);
75     bool updateKeyframe(GenTime oldPos, GenTime pos, const QVariant &normalizedVal, bool logUndo = true);
76     KeyframeType keyframeType(GenTime pos) const;
77     /** @brief Returns a keyframe data at given pos
78        ok is a return parameter, set to true if everything went good
79      */
80     Keyframe getKeyframe(const GenTime &pos, bool *ok) const;
81 
82     /** @brief Returns true if we only have 1 keyframe
83      */
84     bool singleKeyframe() const;
85     /** @brief Returns true if we only have no keyframe
86      */
87     bool isEmpty() const;
88     /** @brief Returns the number of keyframes
89      */
90     int count() const;
91 
92     /** @brief Returns the keyframe located after given position.
93        If there is a keyframe at given position it is ignored.
94        @param ok is a return parameter to tell if a keyframe was found.
95     */
96     Keyframe getNextKeyframe(const GenTime &pos, bool *ok) const;
97 
98     /** @brief Returns the keyframe located before given position.
99        If there is a keyframe at given position it is ignored.
100        @param ok is a return parameter to tell if a keyframe was found.
101     */
102     Keyframe getPrevKeyframe(const GenTime &pos, bool *ok) const;
103 
104     /** @brief Returns the closest keyframe from given position.
105        @param ok is a return parameter to tell if a keyframe was found.
106     */
107     Keyframe getClosestKeyframe(const GenTime &pos, bool *ok) const;
108 
109     /** @brief Returns true if a keyframe exists at given pos
110        Notice that add/remove queries are done in real time (gentime), but this request is made in frame
111      */
112     Q_INVOKABLE bool hasKeyframe(int frame) const;
113 
114     /** @brief Return the interpolated value of a parameter.
115        @param pos is the position where we interpolate
116        @param index is the index of the queried parameter. */
117     QVariant getInterpolatedValue(int pos, const QPersistentModelIndex &index) const;
118     /** @brief Return the interpolated value of a parameter.
119        @param pos is the position where we interpolate
120        @param index is the index of the queried parameter. */
121     QVariant getInterpolatedValue(const GenTime &pos, const QPersistentModelIndex &index) const;
122 
123 
124     /** @brief Load keyframes from the current parameter value. */
125     void refresh();
126     /** @brief Reset all keyframes and add a default one */
127     void reset();
128     Q_INVOKABLE KeyframeModel *getKeyModel();
129     KeyframeModel *getKeyModel(const QPersistentModelIndex &index);
130     /** @brief Returns parent asset owner id*/
131     ObjectId getOwnerId() const;
132     /** @brief Returns parent asset id*/
133     const QString getAssetId();
134     const QString getAssetRow();
135 
136     /** @brief Returns the list of selected keyframes */
137     QVector<int> selectedKeyframes() const;
138     /** @brief Remove a position from selected keyframes */
139     void removeFromSelected(int pos);
140     /** @brief Replace list of selected keyframes */
141     void setSelectedKeyframes(QVector<int> list);
142     /** @brief Append a keyframe to selection */
143     void appendSelectedKeyframe(int frame);
144 
145     /** @brief Get the currently active keyframe */
146     int activeKeyframe() const;
147     /** @brief Set the currently active keyframe */
148     void setActiveKeyframe(int pos);
149 
150     /** @brief Parent item size change, update keyframes*/
151     void resizeKeyframes(int oldIn, int oldOut, int in, int out, int offset, bool adjustFromEnd, Fun &undo, Fun &redo);
152 
153     /** @brief Parent item size change, update keyframes*/
154     void moveKeyframes(int oldIn, int in, Fun &undo, Fun &redo);
155 
156     /** @brief Return position of the nth keyframe (ix = nth)*/
157     GenTime getPosAtIndex(int ix);
158     int getIndexForPos(GenTime pos);
159     QModelIndex getIndexAtRow(int row);
160 
161     /** @brief Check that all keyframable parameters have the same keyframes on loading
162      *  (that's how our model works) */
163     void checkConsistency();
164 
165 protected:
166     /** @brief Helper function to apply a given operation on all parameters */
167     bool applyOperation(const std::function<bool(std::shared_ptr<KeyframeModel>, Fun &, Fun &)> &op, const QString &undoString);
168 
169 signals:
170     void modelChanged();
171     void modelDisplayChanged();
172 
173 private:
174     std::weak_ptr<AssetParameterModel> m_model;
175     std::weak_ptr<DocUndoStack> m_undoStack;
176     std::unordered_map<QPersistentModelIndex, std::shared_ptr<KeyframeModel>> m_parameters;
177     /** @brief Index of the parameter that is displayed in timeline */
178     QModelIndex m_inTimelineIndex;
179     mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
180 
181 private slots:
182     void slotUpdateModels(const QModelIndex &ix1, const QModelIndex &ix2, const QVector<int> &roles);
183 
184 public:
185     // this is to enable for range loops
begin()186     auto begin() -> decltype(m_parameters.begin()->second->begin()) { return m_parameters.begin()->second->begin(); }
end()187     auto end() -> decltype(m_parameters.begin()->second->end()) { return m_parameters.begin()->second->end(); }
188 };
189 
190 #endif
191