1 /*
2     SPDX-FileCopyrightText: 2017 Nicolas Carion
3     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4 */
5 
6 #ifndef MARKERLISTMODEL_H
7 #define MARKERLISTMODEL_H
8 
9 #include "definitions.h"
10 #include "gentime.h"
11 #include "undohelper.hpp"
12 
13 #include <QAbstractListModel>
14 #include <QReadWriteLock>
15 
16 #include <array>
17 #include <map>
18 #include <memory>
19 
20 class ClipController;
21 class DocUndoStack;
22 class SnapInterface;
23 
24 /** @class MarkerListModel
25     @brief This class is the model for a list of markers.
26     A marker is defined by a time, a type (the color used to represent it) and a comment string.
27     We store them in a sorted fashion using a std::map
28 
29     A marker is essentially bound to a clip. We can also define guides, that are timeline-wise markers. For that, use the constructors without clipId
30  */
31 class MarkerListModel : public QAbstractListModel
32 {
33     Q_OBJECT
34 
35 public:
36     /** @brief Construct a marker list bound to the bin clip with given id */
37     explicit MarkerListModel(QString clipId, std::weak_ptr<DocUndoStack> undo_stack, QObject *parent = nullptr);
38 
39     /** @brief Construct a guide list (bound to the timeline) */
40     MarkerListModel(std::weak_ptr<DocUndoStack> undo_stack, QObject *parent = nullptr);
41 
42     enum { CommentRole = Qt::UserRole + 1, PosRole, FrameRole, ColorRole, TypeRole, IdRole };
43 
44     /** @brief Adds a marker at the given position. If there is already one, the comment will be overridden
45        @param pos defines the position of the marker, relative to the clip
46        @param comment is the text associated with the marker
47        @param type is the type (color) associated with the marker. If -1 is passed, then the value is pulled from kdenlive's defaults
48      */
49     bool addMarker(GenTime pos, const QString &comment, int type = -1);
50     bool addMarkers(QMap <GenTime, QString> markers, int type = -1);
51 
52 protected:
53     /** @brief Same function but accumulates undo/redo */
54     bool addMarker(GenTime pos, const QString &comment, int type, Fun &undo, Fun &redo);
55 
56 public:
57     /** @brief Removes the marker at the given position.
58        Returns false if no marker was found at given pos
59      */
60     bool removeMarker(GenTime pos);
61     /** @brief Delete all the markers of the model */
62     bool removeAllMarkers();
63 
64     /** @brief Same function but accumulates undo/redo */
65     bool removeMarker(GenTime pos, Fun &undo, Fun &redo);
66 
67 public:
68     /** @brief Edit a marker
69        @param oldPos is the old position of the marker
70        @param pos defines the new position of the marker, relative to the clip
71        @param comment is the text associated with the marker
72        @param type is the type (color) associated with the marker. If -1 is passed, then the value is pulled from kdenlive's defaults
73     */
74     bool editMarker(GenTime oldPos, GenTime pos, QString comment = QString(), int type = -1);
75 
76     /** @brief Moves all markers from on to another position
77        @param markers list of markers to move
78        @param fromPos
79        @param toPos
80        @param undo
81        @param redo
82     */
83     bool moveMarkers(QList<CommentedTime> markers, GenTime fromPos, GenTime toPos, Fun &undo, Fun &redo);
84     bool moveMarker(int mid, GenTime pos);
85     void moveMarkersWithoutUndo(QVector<int> markersId, int offset, bool updateView = true);
86 
87     /** @brief This describes the available markers type and their corresponding colors */
88     static std::array<QColor, 9> markerTypes;
89 
90     /** @brief Returns a marker data at given pos */
91     CommentedTime getMarker(const GenTime &pos, bool *ok) const;
92 
93     /** @brief Returns all markers in model */
94     QList<CommentedTime> getAllMarkers() const;
95 
96     /** @brief Returns all markers of model that are intersect with a given range.
97      * @param start is the position where start to search for markers
98      * @param end is the position after which markers will not be returned, set to -1 to get all markers after start
99     */
100     QList<CommentedTime> getMarkersInRange(int start, int end) const;
101     QVector<int> getMarkersIdInRange(int start, int end) const;
102 
103     /** @brief Returns a marker position in frames given it's id */
104     int getMarkerPos(int mid) const;
105 
106     /** @brief Returns all markers positions in model */
107     std::vector<int> getSnapPoints() const;
108 
109     /** @brief Returns true if a marker 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 hasMarker(int frame) const;
113     bool hasMarker(GenTime pos) const;
114     CommentedTime marker(GenTime pos) const;
115 
116     /** @brief Registers a snapModel to the marker model.
117        This is intended to be used for a guide model, so that the timelines can register their snapmodel to be updated when the guide moves. This is also used
118        on the clip monitor to keep tracking the clip markers
119        The snap logic for clips is managed from the Timeline
120        Note that no deregistration is necessary, the weak_ptr will be discarded as soon as it becomes invalid.
121     */
122     void registerSnapModel(const std::weak_ptr<SnapInterface> &snapModel);
123 
124     /** @brief Exports the model to json using format above */
125     QString toJson() const;
126 
127     /** @brief Shows a dialog to edit a marker/guide
128        @param pos: position of the marker to edit, or new position for a marker
129        @param widget: qt widget that will be the parent of the dialog
130        @param createIfNotFound: if true, we create a marker if none is found at pos
131        @param clip: pointer to the clip if we are editing a marker
132        @return true if dialog was accepted and modification successful
133      */
134     bool editMarkerGui(const GenTime &pos, QWidget *parent, bool createIfNotFound, ClipController *clip = nullptr, bool createOnly = false);
135 
136     // Mandatory overloads
137     QVariant data(const QModelIndex &index, int role) const override;
138     QHash<int, QByteArray> roleNames() const override;
139     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
140 
141 public slots:
142     /** @brief Imports a list of markers from json data
143    The data should be formatted as follows:
144    [{"pos":0.2, "comment":"marker 1", "type":1}, {...}, ...]
145    return true on success and logs undo object
146    @param ignoreConflicts: if set to false, it aborts if the data contains a marker with same position but different comment and/or type. If set to true,
147    such markers are overridden silently
148    @param pushUndo: if true, create an undo object
149  */
150     bool importFromJson(const QString &data, bool ignoreConflicts, bool pushUndo = true);
151     bool importFromJson(const QString &data, bool ignoreConflicts, Fun &undo, Fun &redo);
152 
153 protected:
154     /** @brief Adds a snap point at marker position in the registered snap models
155      (those that are still valid)*/
156     void addSnapPoint(GenTime pos);
157 
158     /** @brief Deletes a snap point at marker position in the registered snap models
159        (those that are still valid)*/
160     void removeSnapPoint(GenTime pos);
161 
162     /** @brief Helper function that generate a lambda to change comment / type of given marker */
163     Fun changeComment_lambda(GenTime pos, const QString &comment, int type);
164 
165     /** @brief Helper function that generate a lambda to add given marker */
166     Fun addMarker_lambda(GenTime pos, const QString &comment, int type);
167 
168     /** @brief Helper function that generate a lambda to remove given marker */
169     Fun deleteMarker_lambda(GenTime pos);
170 
171     /** @brief Helper function that retrieves a pointer to the markermodel, given whether it's a guide model and its clipId*/
172     static std::shared_ptr<MarkerListModel> getModel(bool guide, const QString &clipId);
173 
174     /** @brief Connects the signals of this object */
175     void setup();
176 
177 private:
178     std::weak_ptr<DocUndoStack> m_undoStack;
179     /** @brief whether this model represents timeline-wise guides */
180     bool m_guide;
181     /** @brief the Id of the clip this model corresponds to, if any. */
182     QString m_clipId;
183 
184     /** @brief This is a lock that ensures safety in case of concurrent access */
185     mutable QReadWriteLock m_lock;
186 
187     std::map<int, CommentedTime> m_markerList;
188     std::vector<std::weak_ptr<SnapInterface>> m_registeredSnaps;
189     int getRowfromId(int mid) const;
190     int getIdFromPos(const GenTime &pos) const;
191 
192 signals:
193     void modelChanged();
194 
195 };
196 Q_DECLARE_METATYPE(MarkerListModel *)
197 
198 #endif
199