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

0001 /*
0002 SPDX-FileCopyrightText: 2012 Till Theato <root@ttill.de>
0003 SPDX-FileCopyrightText: 2014 Jean-Baptiste Mardelle <jb@kdenlive.org>
0004 SPDX-FileCopyrightText: 2017 Nicolas Carion
0005 This file is part of Kdenlive. See www.kdenlive.org.
0006 
0007 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0008 */
0009 
0010 #pragma once
0011 
0012 #include "abstractmodel/abstracttreemodel.hpp"
0013 #include "bin/abstractprojectitem.h"
0014 #include "definitions.h"
0015 #include "undohelper.hpp"
0016 #include <QDomElement>
0017 #include <QFileInfo>
0018 #include <QIcon>
0019 #include <QReadWriteLock>
0020 #include <QSize>
0021 #include <QTimer>
0022 #include <QUuid>
0023 
0024 class BinPlaylist;
0025 class FileWatcher;
0026 class MarkerListModel;
0027 class ProjectClip;
0028 class ProjectFolder;
0029 class EffectStackModel;
0030 class QProgressDialog;
0031 
0032 namespace Mlt {
0033 class Producer;
0034 class Properties;
0035 class Tractor;
0036 class Service;
0037 } // namespace Mlt
0038 
0039 /**
0040  * @class ProjectItemModel
0041  * @brief Acts as an adaptor to be able to use BinModel with item views.
0042  */
0043 class ProjectItemModel : public AbstractTreeModel
0044 {
0045     Q_OBJECT
0046 
0047 protected:
0048     explicit ProjectItemModel(QObject *parent);
0049 
0050 public:
0051     static std::shared_ptr<ProjectItemModel> construct(QObject *parent = nullptr);
0052     ~ProjectItemModel() override;
0053 
0054     friend class ProjectClip;
0055     friend class ThumbnailCache;
0056     /** @brief Timer checking if we have missing clips in the project */
0057     QTimer missingClipTimer;
0058 
0059     /** @brief Builds the MLT playlist, can only be done after MLT is correctly initialized */
0060     void buildPlaylist(const QUuid uuid);
0061 
0062     /** @brief Returns a clip from the hierarchy, given its id */
0063     std::shared_ptr<ProjectClip> getClipByBinID(const QString &binId);
0064     /** @brief Returns audio levels for a clip from its id */
0065     const QVector <uint8_t>getAudioLevelsByBinID(const QString &binId, int stream);
0066     double getAudioMaxLevel(const QString &binId, int stream);
0067 
0068     /** @brief Returns a list of clips using the given url */
0069     QStringList getClipByUrl(const QFileInfo &url) const;
0070 
0071     /** @brief Helper to check whether a clip with a given id exists */
0072     bool hasClip(const QString &binId);
0073 
0074     /** @brief Gets a folder by its id. If none is found, nullptr is returned */
0075     std::shared_ptr<ProjectFolder> getFolderByBinId(const QString &binId);
0076     /** @brief Gets a list of all folders in this project */
0077     QList <std::shared_ptr<ProjectFolder> > getFolders();
0078     /** @brief Gets a id folder by its name. If none is found, empty string returned */
0079     const QString getFolderIdByName(const QString &folderName);
0080 
0081     /** @brief Gets any item by its id. */
0082     std::shared_ptr<AbstractProjectItem> getItemByBinId(const QString &binId);
0083 
0084     /** @brief This function change the global enabled state of the bin effects */
0085     void setBinEffectsEnabled(bool enabled);
0086 
0087     /** @brief Returns some info about the folder containing the given index */
0088     QStringList getEnclosingFolderInfo(const QModelIndex &index) const;
0089 
0090     /** @brief Deletes all element and start a fresh model */
0091     void clean();
0092 
0093     /** @brief Returns the id of all the clips (excluding folders) */
0094     std::vector<QString> getAllClipIds() const;
0095     
0096     /** @brief Updates the list of all created bin thumbnails */
0097     void updateCacheThumbnail(std::unordered_map<QString, std::vector<int>> &thumbData);
0098 
0099     /** @brief Convenience method to access root folder */
0100     std::shared_ptr<ProjectFolder> getRootFolder() const;
0101 
0102     void loadSubClips(const QString &id, const QString &dataMap, Fun &undo, Fun &redo);
0103 
0104     /** @brief Convenience method to retrieve a pointer to an element given its index */
0105     std::shared_ptr<AbstractProjectItem> getBinItemByIndex(const QModelIndex &index) const;
0106 
0107     /** @brief Load the folders given the property containing them */
0108     bool loadFolders(Mlt::Properties &folders, std::unordered_map<QString, QString> &binIdCorresp);
0109 
0110     /** @brief Parse a bin playlist from the document tractor and reconstruct the tree
0111      *  @return A list of invalid sequence clips found in Project Bin (can be caused by 23.04.0 bug)
0112      */
0113     QList<QUuid> loadBinPlaylist(Mlt::Service *documentTractor, std::unordered_map<QString, QString> &binIdCorresp, QStringList &expandedFolders,
0114                                  const QUuid &activeUuid, int &zoomLevel);
0115     void loadTractorPlaylist(Mlt::Tractor documentTractor, std::unordered_map<QString, QString> &binIdCorresp);
0116 
0117     /** @brief Save document properties in MLT's bin playlist */
0118     void saveDocumentProperties(const QMap<QString, QString> &props, const QMap<QString, QString> &metadata);
0119 
0120     /** @brief Save a property to main bin */
0121     void saveProperty(const QString &name, const QString &value);
0122 
0123     /** @brief Returns item data depending on role requested */
0124     QVariant data(const QModelIndex &index, int role) const override;
0125     /** @brief Called when user edits an item */
0126     bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
0127     /** @brief Allow selection and drag & drop */
0128     Qt::ItemFlags flags(const QModelIndex &index) const override;
0129     /** @brief Returns column names in case we want to use columns in QTreeView */
0130     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
0131     /** @brief Mandatory reimplementation from QAbstractItemModel */
0132     int columnCount(const QModelIndex &parent = QModelIndex()) const override;
0133     /** @brief Returns the MIME type used for Drag actions */
0134     QStringList mimeTypes() const override;
0135     /** @brief Create data that will be used for Drag events */
0136     QMimeData *mimeData(const QModelIndexList &indices) const override;
0137     /** @brief Set size for thumbnails */
0138     void setIconSize(QSize s);
0139     bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
0140     Qt::DropActions supportedDropActions() const override;
0141 
0142     /** @brief Request deletion of a bin clip from the project bin
0143        @param clip : pointer to the clip to delete
0144        @param undo,redo: lambdas that are updated to accumulate operation.
0145      */
0146     bool requestBinClipDeletion(const std::shared_ptr<AbstractProjectItem> &clip, Fun &undo, Fun &redo);
0147 
0148     /** @brief Request creation of a bin folder
0149        @param id Id of the requested bin. If this is empty or invalid (already used, for example), it will be used as a return parameter to give the automatic
0150        bin id used.
0151        @param name Name of the folder
0152        @param parentId Bin id of the parent folder
0153        @param undo,redo: lambdas that are updated to accumulate operation.
0154     */
0155     bool requestAddFolder(QString &id, const QString &name, const QString &parentId, Fun &undo, Fun &redo);
0156     /** @brief Request creation of a bin clip
0157        @param id Id of the requested bin. If this is empty, it will be used as a return parameter to give the automatic bin id used.
0158        @param description Xml description of the clip
0159        @param parentId Bin id of the parent folder
0160        @param undo,redo: lambdas that are updated to accumulate operation.
0161        @param readyCallBack: lambda that will be executed when the clip becomes ready. It is given the binId as parameter
0162     */
0163     bool requestAddBinClip(QString &id, const QDomElement &description, const QString &parentId, Fun &undo, Fun &redo,
0164                            const std::function<void(const QString &)> &readyCallBack = [](const QString &) {});
0165     bool requestAddBinClip(QString &id, const QDomElement &description, const QString &parentId, const QString &undoText = QString(), const std::function<void(const QString &)> &readyCallBack = [](const QString &) {});
0166 
0167     /** @brief This is the addition function when we already have a producer for the clip*/
0168     bool requestAddBinClip(
0169         QString &id, std::shared_ptr<Mlt::Producer> &producer, const QString &parentId, Fun &undo, Fun &redo,
0170         const std::function<void(const QString &)> &readyCallBack = [](const QString &) {});
0171 
0172     /** @brief Create a subClip
0173        @param id Id of the requested bin. If this is empty, it will be used as a return parameter to give the automatic bin id used.
0174        @param parentId Bin id of the parent clip
0175        @param in,out : zone that corresponds to the subclip
0176        @param undo,redo: lambdas that are updated to accumulate operation.
0177     */
0178     bool requestAddBinSubClip(QString &id, int in, int out, const QMap<QString, QString> &zoneProperties, const QString &parentId, Fun &undo, Fun &redo);
0179     bool requestAddBinSubClip(QString &id, int in, int out, const QMap<QString, QString> &zoneProperties, const QString &parentId);
0180 
0181     /** @brief Request that a folder's name is changed
0182        @param clip : pointer to the folder to rename
0183        @param name: new name of the folder
0184        @param undo,redo: lambdas that are updated to accumulate operation.
0185     */
0186     bool requestRenameFolder(const std::shared_ptr<AbstractProjectItem> &folder, const QString &name, Fun &undo, Fun &redo);
0187     /* Same functions but pushes the undo object directly */
0188     bool requestRenameFolder(std::shared_ptr<AbstractProjectItem> folder, const QString &name);
0189 
0190     /** @brief Request that the unused clips are deleted */
0191     bool requestCleanupUnused();
0192 
0193     /** @brief Request that all clips using one of the given urls are removed from the project and deleted from the hard disk*/
0194     bool requestTrashClips(QStringList &ids, QStringList &urls);
0195 
0196     /** @brief Retrieves the next id available for attribution to a folder */
0197     int getFreeFolderId();
0198 
0199     /** @brief Retrieves the next id available for attribution to a clip */
0200     int getFreeClipId();
0201 
0202     /** @brief Check whether a given id is currently used or not*/
0203     bool isIdFree(const QString &id) const;
0204 
0205     /** @brief Retrieve a list of proxy/original urls */
0206     QMap<QString, QString> getProxies(const QString &root);
0207 
0208     /** @brief Request that the producer of a given clip is reloaded */
0209     void reloadClip(const QString &binId);
0210 
0211     /** @brief Set the status of the clip to "waiting". This happens when the corresponding file has changed*/
0212     void setClipWaiting(const QString &binId);
0213     void setClipInvalid(const QString &binId);
0214 
0215     /** @brief Returns true if current project has a clip with id \@clipId and a hash of \@clipHash */
0216     bool validateClip(const QString &binId, const QString &clipHash);
0217     /** @brief Returns clip id if folder "folderId" has a clip with hash of "clipHash" or empty if not found */
0218     QString validateClipInFolder(const QString &folderId, const QString &clipHash);
0219 
0220     /** @brief Number of clips in the bin playlist */
0221     int clipsCount() const;
0222     /** @brief Returns true if we have proxied clips in the project */
0223     bool hasProxies() const;
0224     /** @brief Get a secondary timeline tractor by its uuid */
0225     std::shared_ptr<Mlt::Tractor> getExtraTimeline(const QString &uuid);
0226     void setExtraTimelineSaved(const QString &uuid);
0227     /** @brief Check if  a file is already in Bin */
0228     bool urlExists(const QString &path) const;
0229     /** @brief Returns the unique uuid for this project item model */
0230     QUuid uuid() const { return m_uuid; };
0231     /** @brief Retrieve the Bin clip id from a sequence uuid */
0232     const QString getSequenceId(const QUuid &uuid);
0233     /** @brief Check if we already have a sequence with this uuid */
0234     bool hasSequenceId(const QUuid &uuid) const;
0235     /** @brief Return a project sequence clip from its uuid */
0236     std::shared_ptr<ProjectClip> getSequenceClip(const QUuid &uuid);
0237     /** @brief Returns uuid / bin id of all sequence clips in the project */
0238     QMap<QUuid, QString> getAllSequenceClips() const;
0239     /** @brief Return the main project tractor (container of all playlists) */
0240     std::shared_ptr<Mlt::Tractor> projectTractor();
0241     const QString sceneList(const QString &root, const QString &fullPath, const QString &filterData, Mlt::Tractor *activeTractor, int duration);
0242     /** @brief Ensure that sequence @destUuid is not embedded in any dependency of sequence @srcUuid */
0243     bool canBeEmbeded(const QUuid destUuid, const QUuid srcUuid);
0244     /** @brief Store a newly created sequence tractor for reuse */
0245     void storeSequence(const QString uuid, std::shared_ptr<Mlt::Tractor> tractor, bool internalSave = true);
0246     /** @brief Returns the count of sequences in this project */
0247     int sequenceCount() const;
0248     /** @brief The id of the folder where new sequences will be created, -1 if none */
0249     int defaultSequencesFolder() const;
0250     void setSequencesFolder(int id);
0251     /** @brief The id of the folder where new audio captures will be created, -1 if none */
0252     int defaultAudioCaptureFolder() const;
0253     void setAudioCaptureFolder(int id);
0254     /** @brief Remove clip references for a timeline. */
0255     void removeReferencedClips(const QUuid &uuid, bool onDeletion);
0256     /** @brief Check that all sequences are correctly stored in the model */
0257     void checkSequenceIntegrity(const QString activeSequenceId);
0258     std::shared_ptr<EffectStackModel> getClipEffectStack(int itemId);
0259 
0260 protected:
0261     bool closing;
0262     /** @brief Register the existence of a new element
0263      */
0264     void registerItem(const std::shared_ptr<TreeItem> &item) override;
0265     /** @brief Deregister the existence of a new element*/
0266     void deregisterItem(int id, TreeItem *item) override;
0267 
0268     /** @brief Helper function to generate a lambda that rename a folder */
0269     Fun requestRenameFolder_lambda(const std::shared_ptr<AbstractProjectItem> &folder, const QString &newName);
0270 
0271     /** @brief Helper function to add a given item to the tree */
0272     bool addItem(const std::shared_ptr<AbstractProjectItem> &item, const QString &parentId, Fun &undo, Fun &redo);
0273 
0274     /** @brief Function to be called when the url of a clip changes */
0275     void updateWatcher(const std::shared_ptr<ProjectClip> &item);
0276 
0277 public Q_SLOTS:
0278     /** @brief An item in the list was modified, notify */
0279     void onItemUpdated(const std::shared_ptr<AbstractProjectItem> &item, const QVector<int> &roles);
0280     void onItemUpdated(const QString &binId, int role);
0281 
0282     void setDragType(PlaylistState::ClipState type);
0283     /** @brief Create the subclips defined in the parent clip.
0284     @param id is the id of the parent clip
0285     @param data is a definition of the subclips (keys are subclips' names, value are "in:out")*/
0286     void loadSubClips(const QString &id, const QString &clipData, bool logUndo);
0287 
0288 private Q_SLOTS:
0289     /** @brief Check how many invalid clips we have. */
0290     void slotUpdateInvalidCount();
0291 
0292 private:
0293     /** @brief Return reference to column specific data */
0294     int mapToColumn(int column) const;
0295     /** @brief Return column number(s) responsible for a specific data type*/
0296     QList<int> mapDataToColumn(AbstractProjectItem::DataType type) const;
0297 
0298     mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
0299 
0300     std::unique_ptr<BinPlaylist> m_binPlaylist;
0301 
0302     std::unique_ptr<FileWatcher> m_fileWatcher;
0303     std::unordered_map<QString, std::shared_ptr<Mlt::Tractor>> m_extraPlaylists;
0304     std::shared_ptr<Mlt::Tractor> m_projectTractor;
0305     std::map<int, std::shared_ptr<ProjectClip>> m_allClipItems;
0306     QList<int> m_allIds;
0307 
0308     int m_nextId;
0309     QIcon m_blankThumb;
0310     PlaylistState::ClipState m_dragType;
0311     QUuid m_uuid;
0312     /** @brief The id of the folder where new sequences will be created, -1 if none */
0313     int m_sequenceFolderId;
0314     /** @brief The id of the folder where new audio captures will be created, -1 if none */
0315     int m_audioCaptureFolderId;
0316     /** @brief Remove an item from the project */
0317     Fun removeProjectItem_lambda(int binId, int id);
0318 
0319 Q_SIGNALS:
0320     /** @brief thumbs of the given clip were modified, request update of the monitor if need be */
0321     void refreshAudioThumbs(const QString &id);
0322     void refreshClip(const QString &id);
0323     void emitMessage(const QString &, int, MessageType);
0324     void refreshPanel(const QString &id);
0325     void requestAudioThumbs(const QString &id, long duration);
0326     // TODO
0327     void markersNeedUpdate(const QString &id, const QList<int> &);
0328     void itemDropped(const QStringList, const QModelIndex);
0329     void urlsDropped(const QList<QUrl>, const QModelIndex);
0330     void effectDropped(const QStringList &, const QModelIndex &);
0331     void addTag(const QString &, const QModelIndex &);
0332     void addClipCut(const QString &, int, int);
0333     void resetPlayOrLoopZone(const QString &id);
0334 };