File indexing completed on 2024-04-28 04:51:21

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 "definitions.h"
0009 #include "undohelper.hpp"
0010 #include "utils/gentime.h"
0011 
0012 #include <QAbstractListModel>
0013 #include <QReadWriteLock>
0014 
0015 #include <array>
0016 #include <map>
0017 #include <memory>
0018 
0019 class ProjectClip;
0020 class DocUndoStack;
0021 class SnapInterface;
0022 
0023 /** @class MarkerListModel
0024     @brief This class is the model for a list of markers.
0025     A marker is defined by a time, a type (the color used to represent it) and a comment string.
0026     We store them in a sorted fashion using a std::map
0027 
0028     A marker is essentially bound to a clip.
0029  */
0030 class MarkerListModel : public QAbstractListModel, public enable_shared_from_this_virtual<MarkerListModel>
0031 {
0032     Q_OBJECT
0033 
0034     friend class ClipController;
0035 
0036 public:
0037     /** @brief Construct a marker list bound to the bin clip with given id */
0038     explicit MarkerListModel(QString clipId, std::weak_ptr<DocUndoStack> undo_stack, QObject *parent = nullptr);
0039 
0040     enum { CommentRole = Qt::UserRole + 1, PosRole, FrameRole, ColorRole, TypeRole, IdRole, TCRole };
0041 
0042     /** @brief Adds a marker at the given position. If there is already one, the comment will be overridden
0043        @param pos defines the position of the marker, relative to the clip
0044        @param comment is the text associated with the marker
0045        @param type is the type (color) associated with the marker. If -1 is passed, then the value is pulled from kdenlive's defaults
0046      */
0047     bool addMarker(GenTime pos, const QString &comment, int type = -1);
0048     bool addMarkers(const QMap<GenTime, QString> &markers, int type = -1);
0049 
0050 protected:
0051     /** @brief Same function but accumulates undo/redo */
0052     bool addMarker(GenTime pos, const QString &comment, int type, Fun &undo, Fun &redo);
0053 
0054 public:
0055     /** @brief Removes the marker at the given position.
0056        Returns false if no marker was found at given pos
0057      */
0058     bool removeMarker(GenTime pos);
0059     /** @brief Delete all the markers of the model */
0060     bool removeAllMarkers();
0061 
0062     /** @brief Same function but accumulates undo/redo */
0063     bool removeMarker(GenTime pos, Fun &undo, Fun &redo);
0064 
0065 public:
0066     /** @brief Edit a marker
0067        @param oldPos is the old position of the marker
0068        @param pos defines the new position of the marker, relative to the clip
0069        @param comment is the text associated with the marker
0070        @param type is the type (color) associated with the marker. If -1 is passed, then the value is pulled from kdenlive's defaults
0071     */
0072     bool editMarker(GenTime oldPos, GenTime pos, QString comment = QString(), int type = -1);
0073 
0074     /** @brief Moves all markers from on to another position
0075        @param markers list of markers to move
0076        @param fromPos
0077        @param toPos
0078        @param undo
0079        @param redo
0080     */
0081     bool moveMarkers(const QList<CommentedTime> &markers, GenTime fromPos, GenTime toPos, Fun &undo, Fun &redo);
0082     bool moveMarker(int mid, GenTime pos);
0083     void moveMarkersWithoutUndo(const QVector<int> &markersId, int offset, bool updateView = true);
0084 
0085     /** @brief Returns a marker data at given pos */
0086     CommentedTime getMarker(const GenTime &pos, bool *ok) const;
0087     CommentedTime getMarker(int frame, bool *ok) const;
0088 
0089     /** @brief Returns all markers in model or – if a type is given – all markers of the given type */
0090     QList<CommentedTime> getAllMarkers(int type = -1) const;
0091 
0092     /** @brief Returns all markers of model that are intersect with a given range.
0093      * @param start is the position where start to search for markers
0094      * @param end is the position after which markers will not be returned, set to -1 to get all markers after start
0095      */
0096     QList<CommentedTime> getMarkersInRange(int start, int end) const;
0097     QVector<int> getMarkersIdInRange(int start, int end) const;
0098 
0099     /** @brief Returns a marker position in frames given it's id */
0100     int getMarkerPos(int mid) const;
0101 
0102     /** @brief Returns all markers positions in model */
0103     std::vector<int> getSnapPoints() const;
0104 
0105     /** @brief Returns true if a marker exists at given pos
0106        Notice that add/remove queries are done in real time (gentime), but this request is made in frame
0107      */
0108     Q_INVOKABLE bool hasMarker(int frame) const;
0109     /** @brief Returns a marker id at frame pos. Returns -1 if no marker exists at that position
0110      */
0111     int markerIdAtFrame(int pos) const;
0112     bool hasMarker(GenTime pos) const;
0113     CommentedTime marker(GenTime pos) const;
0114     CommentedTime marker(int frame) const;
0115     CommentedTime markerById(int mid) const;
0116 
0117     /** @brief Registers a snapModel to the marker model.
0118        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
0119        on the clip monitor to keep tracking the clip markers
0120        The snap logic for clips is managed from the Timeline
0121        Note that no deregistration is necessary, the weak_ptr will be discarded as soon as it becomes invalid.
0122     */
0123     void registerSnapModel(const std::weak_ptr<SnapInterface> &snapModel);
0124 
0125     /** @brief Exports the model to json using format above
0126      *  @param categories will only export selected categories. If param is empty, all categories will be exported
0127      */
0128     QString toJson(QList<int> categories = {}) const;
0129 
0130     /** @brief Shows a dialog to edit a marker/guide
0131        @param pos: position of the marker to edit, or new position for a marker
0132        @param widget: qt widget that will be the parent of the dialog
0133        @param createIfNotFound: if true, we create a marker if none is found at pos
0134        @param clip: pointer to the clip if we are editing a marker
0135        @return true if dialog was accepted and modification successful
0136      */
0137     bool editMarkerGui(const GenTime &pos, QWidget *parent, bool createIfNotFound, ProjectClip *clip = nullptr, bool createOnly = false);
0138     /** @brief Shows a dialog to change the category of multiple markers/guides
0139        @param positions: List of the markers positions to edit
0140        @param widget: qt widget that will be the parent of the dialog
0141        @return true if dialog was accepted and modification successful
0142      */
0143     bool editMultipleMarkersGui(const QList<GenTime> positions, QWidget *parent);
0144     /** @brief Shows a dialog to add multiple markers/guide
0145        @param pos: position of the marker to edit, or new position for a marker
0146        @param widget: qt widget that will be the parent of the dialog
0147        @param createIfNotFound: if true, we create a marker if none is found at pos
0148        @param clip: pointer to the clip if we are editing a marker
0149        @return true if dialog was accepted and modification successful
0150      */
0151     bool addMultipleMarkersGui(const GenTime &pos, QWidget *parent, bool createIfNotFound, ProjectClip *clip = nullptr);
0152     void exportGuidesGui(QWidget *parent, GenTime projectDuration) const;
0153     /** @brief Load the marker categories from a stringList
0154      *  @return the list of deleted categories ids (if any)
0155      */
0156     void loadCategoriesWithUndo(const QStringList &categories, const QStringList &currentCategories, const QMap<int, int> remapCategories = {});
0157     QList<int> loadCategories(const QStringList &categories, bool notify = true);
0158     QStringList guideCategoriesToStringList(const QString &categoriesData);
0159     /** @brief Returns the marker categories in the form of a stringList for saving */
0160     const QStringList categoriesToStringList() const;
0161     const QString categoriesToJSon() const;
0162     static const QString categoriesListToJSon(const QStringList categories);
0163 
0164     // Mandatory overloads
0165     QVariant data(const QModelIndex &index, int role) const override;
0166     QHash<int, QByteArray> roleNames() const override;
0167     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
0168 
0169 public Q_SLOTS:
0170     /** @brief Imports a list of markers from json data
0171    The data should be formatted as follows:
0172    [{"pos":0.2, "comment":"marker 1", "type":1}, {...}, ...]
0173    return true on success and logs undo object
0174    @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,
0175    such markers are overridden silently
0176    @param pushUndo: if true, create an undo object
0177  */
0178     bool importFromJson(const QString &data, bool ignoreConflicts, bool pushUndo = true);
0179     bool importFromJson(const QString &data, bool ignoreConflicts, Fun &undo, Fun &redo);
0180     bool importFromTxt(const QString &fileName, Fun &undo, Fun &redo);
0181     bool importFromFile(const QString &fileData, bool ignoreConflicts);
0182 
0183 protected:
0184     /** @brief Adds a snap point at marker position in the registered snap models
0185      (those that are still valid)*/
0186     void addSnapPoint(GenTime pos);
0187 
0188     /** @brief Deletes a snap point at marker position in the registered snap models
0189        (those that are still valid)*/
0190     void removeSnapPoint(GenTime pos);
0191 
0192     /** @brief Helper function that generate a lambda to change comment / type of given marker */
0193     Fun changeComment_lambda(GenTime pos, const QString &comment, int type);
0194 
0195     /** @brief Helper function that generate a lambda to add given marker */
0196     Fun addMarker_lambda(GenTime pos, const QString &comment, int type);
0197 
0198     /** @brief Helper function that generate a lambda to remove given marker */
0199     Fun deleteMarker_lambda(GenTime pos);
0200 
0201     /** @brief Helper function that retrieves a pointer to the markermodel, given whether it's a guide model and its clipId*/
0202     std::shared_ptr<MarkerListModel> getModel(const QString &clipId);
0203 
0204     /** @brief Connects the signals of this object */
0205     void setup();
0206 
0207 private:
0208     std::weak_ptr<DocUndoStack> m_undoStack;
0209     /** @brief the Id of the clip this model corresponds to. */
0210     QString m_clipId;
0211 
0212     /** @brief This is a lock that ensures safety in case of concurrent access */
0213     mutable QReadWriteLock m_lock;
0214 
0215     std::map<int, CommentedTime> m_markerList;
0216     /** @brief A list of {marker frame,marker id}, useful to quickly find a marker */
0217     QMap<int, int> m_markerPositions;
0218 
0219     std::vector<std::weak_ptr<SnapInterface>> m_registeredSnaps;
0220     int getRowfromId(int mid) const;
0221     int getIdFromPos(const GenTime &pos) const;
0222     int getIdFromPos(int frame) const;
0223 
0224 Q_SIGNALS:
0225     void modelChanged();
0226     void categoriesChanged();
0227 };
0228 Q_DECLARE_METATYPE(MarkerListModel *)