File indexing completed on 2024-04-21 04:52:15

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 "trackmodel.hpp"
0010 #include "undohelper.hpp"
0011 #include <QAbstractItemModel>
0012 #include <QReadWriteLock>
0013 #include <QUuid>
0014 #include <cassert>
0015 #include <memory>
0016 #include <mlt++/MltTractor.h>
0017 
0018 #include <unordered_map>
0019 #include <unordered_set>
0020 #include <vector>
0021 
0022 class AssetParameterModel;
0023 class EffectStackModel;
0024 class ClipModel;
0025 class CompositionModel;
0026 class DocUndoStack;
0027 class GroupsModel;
0028 class SnapModel;
0029 class SubtitleModel;
0030 class TimelineItemModel;
0031 class TrackModel;
0032 class ProfileModel;
0033 class MarkerListModel;
0034 class MarkerSortModel;
0035 class PreviewManager;
0036 
0037 /** @brief This class represents a Timeline object, as viewed by the backend.
0038    In general, the Gui associated with it will send modification queries (such as resize or move), and this class authorize them or not depending on the
0039    validity of the modifications.
0040 
0041    This class also serves to keep track of all objects. It holds pointers to all tracks and clips, and gives them unique IDs on creation. These Ids are used in
0042    any interactions with the objects and have nothing to do with Melt IDs.
0043 
0044    This is the entry point for any modifications that has to be made on an element. The dataflow beyond this entry point may vary, for example when the user
0045    request a clip resize, the call is deferred to the clip itself, that check if there is enough data to extend by the requested amount, compute the new in and
0046    out, and then asks the track if there is enough room for extension. To avoid any confusion on which function to call first, remember to always call the
0047    version in timeline. This is also required to generate the Undo/Redo operators
0048 
0049    The undo/redo system is designed around lambda functions. Each time a function executes an elementary change to the model, it writes the corresponding
0050    operation and its reverse, respectively in the redo and the undo lambdas. This way, if an operation fails for some reason, we can easily cancel the steps
0051    that have been done so far without corrupting anything. The other advantage is that operations are easy to compose, and you get a undo/redo pair for free no
0052    matter in which way you combine them.
0053 
0054    Most of the modification functions are named requestObjectAction. Eg, if the object is a clip and we want to move it, we call requestClipMove. These
0055    functions always return a bool indicating success, and when they return false they should guarantee than nothing has been modified. Most of the time, these
0056    functions come in two versions: the first one is the entry point if you want to perform only the action (and not compose it with other actions). This version
0057    will generally automatically push and Undo object on the Application stack, in case the user later wants to cancel the operation. It also generally goes the
0058    extra mile to ensure the operation is done in a way that match the user's expectation: for example requestClipMove checks whether the clip belongs to a group
0059    and in that case actually mouves the full group. The other version of the function, if it exists, is intended for composition (using the action as part of a
0060    complex operation). It takes as input the undo/redo lambda corresponding to the action that is being performed and accumulates on them. Note that this
0061    version does the minimal job: in the example of the requestClipMove, it will not move the full group if the clip is in a group.
0062 
0063    Generally speaking, we don't check ahead of time if an action is going to succeed or not before applying it.
0064    We just apply it naively, and if it fails at some point, we use the undo operator that we are constructing on the fly to revert what we have done so far.
0065    For example, when we move a group of clips, we apply the move operation to all the clips inside this group (in the right order). If none fails, we are good,
0066    otherwise we revert what we've already done.
0067    This kind of behaviour frees us from the burden of simulating the actions before actually applying theme. This is a good thing because this simulation step
0068    would be very sensitive to corruptions and small discrepancies, which we try to avoid at all cost.
0069 
0070 
0071    It derives from AbstractItemModel (indirectly through TimelineItemModel) to provide the model to the QML interface. An itemModel is organized with row and
0072    columns that contain the data. It can be hierarchical, meaning that a given index (row,column) can contain another level of rows and column.
0073    Our organization is as follows: at the top level, each row contains a track. These rows are in the same order as in the actual timeline.
0074    Then each of this row contains itself sub-rows that correspond to the clips.
0075    Here the order of these sub-rows is unrelated to the chronological order of the clips,
0076    but correspond to their Id order. For example, if you have three clips, with ids 12, 45 and 150, they will receive row index 0,1 and 2.
0077    This is because the order actually doesn't matter since the clips are rendered based on their positions rather than their row order.
0078    The id order has been chosen because it is consistent with a valid ordering of the clips.
0079    The columns are never used, so the data is always in column 0
0080 
0081    An ModelIndex in the ItemModel consists of a row number, a column number, and a parent index. In our case, tracks have always an empty parent, and the clip
0082    have a track index as parent.
0083    A ModelIndex can also store one additional integer, and we exploit this feature to store the unique ID of the object it corresponds to.
0084 
0085 */
0086 class TimelineModel : public QAbstractItemModel_shared_from_this<TimelineModel>
0087 {
0088     Q_OBJECT
0089 
0090 protected:
0091     /** @brief this constructor should not be called. Call the static construct instead
0092      */
0093     TimelineModel(const QUuid &uuid, std::weak_ptr<DocUndoStack> undo_stack);
0094 
0095 public:
0096     friend class TrackModel;
0097     friend class TimelineTabs;
0098     friend class ProjectManager;
0099     template <typename T> friend class MoveableItem;
0100     friend class ClipModel;
0101     friend class CompositionModel;
0102     friend class GroupsModel;
0103     friend class TimelineController;
0104     friend class SubtitleModel;
0105     friend class MarkerListModel;
0106     friend class TimeRemap;
0107     friend struct TimelineFunctions;
0108 
0109     bool isClosed{true};
0110     Q_PROPERTY(QString visibleSequenceName MEMBER m_visibleSequenceName NOTIFY visibleSequenceNameChanged)
0111 
0112     /// Two level model: tracks and clips on track
0113     enum {
0114         NameRole = Qt::UserRole + 1,
0115         ResourceRole, /// clip only
0116         IsProxyRole,  /// clip only
0117         ServiceRole,  /// clip only
0118         StartRole,    /// clip only
0119         MixRole,      /// clip only, the duration of the mix
0120         MixCutRole,   /// The original cut position for the mix
0121         BinIdRole,    /// clip only
0122         TrackIdRole,
0123         FakeTrackIdRole,
0124         FakePositionRole,
0125         MarkersRole,       /// clip only
0126         PlaylistStateRole, /// clip only
0127         StatusRole,        /// clip only
0128         TypeRole,          /// clip only
0129         KeyframesRole,
0130         DurationRole,
0131         FinalMoveRole,
0132         MaxDurationRole,
0133         InPointRole,    /// clip only
0134         OutPointRole,   /// clip only
0135         FramerateRole,  /// clip only
0136         GroupedRole,    /// clip only
0137         HasAudio,       /// clip only
0138         CanBeAudioRole, /// clip only
0139         CanBeVideoRole, /// clip only
0140         IsDisabledRole, /// track only
0141         IsAudioRole,
0142         SortRole,
0143         TagRole, /// clip only
0144         ShowKeyframesRole,
0145         AudioLevelsRole,      /// clip only
0146         AudioChannelsRole,    /// clip only
0147         AudioStreamRole,      /// clip only
0148         AudioMultiStreamRole, /// clip only
0149         AudioStreamIndexRole, /// clip only
0150         IsCompositeRole,      /// track only
0151         IsLockedRole,         /// track only
0152         HeightRole,           /// track only
0153         TrackTagRole,         /// track only
0154         FadeInRole,           /// clip only
0155         FadeOutRole,          /// clip only
0156         FileHashRole,         /// clip only
0157         SpeedRole,            /// clip only
0158         ClipThumbRole,        /// clip only
0159         ReloadAudioThumbRole, /// clip only
0160         PositionOffsetRole,   /// clip only
0161         TimeRemapRole,        /// clip only
0162         ItemATrack,           /// composition only
0163         ItemIdRole,
0164         ThumbsFormatRole,   /// track only
0165         EffectNamesRole,    /// track and clip only
0166         EffectsEnabledRole, /// track and clip only
0167         GrabbedRole,        /// clip+composition only
0168         SelectedRole,       /// clip+composition only
0169         TrackActiveRole,    /// track only
0170         AudioRecordRole,    /// track only
0171         EffectZonesRole     /// track only
0172     };
0173 
0174     ~TimelineModel() override;
0175     Mlt::Tractor *tractor() const { return m_tractor.get(); }
0176     /** @brief Load tracks from the current tractor, used on project opening
0177      */
0178     void loadTractor();
0179 
0180     /** @brief Returns the current tractor's producer, useful for control seeking, playing, etc
0181      */
0182     std::shared_ptr<Mlt::Producer> producer();
0183     Mlt::Profile &getProfile();
0184 
0185     /** @brief returns the number of tracks */
0186     int getTracksCount() const;
0187     /** @brief returns the number of {audio, video} tracks */
0188     QPair<int, int> getAVtracksCount() const;
0189     /** @brief returns the ids of all audio or video tracks */
0190     QList<int> getTracksIds(bool audio) const;
0191 
0192     /** @brief returns the ids of all the tracks */
0193     std::unordered_set<int> getAllTracksIds() const;
0194 
0195     /** @brief returns the track index (id) from its position */
0196     int getTrackIndexFromPosition(int pos) const;
0197 
0198     /** @brief @returns true if the track is a audio track */
0199     Q_INVOKABLE bool isAudioTrack(int trackId) const;
0200 
0201     /** @brief @returns true if the track is a subtitle track */
0202     Q_INVOKABLE bool isSubtitleTrack(int trackId) const;
0203 
0204     /** @brief returns the number of clips */
0205     int getClipsCount() const;
0206 
0207     /** @brief returns the number of compositions */
0208     int getCompositionsCount() const;
0209 
0210     /** @brief Returns the id of the track containing clip (-1 if it is not inserted)
0211        @param clipId Id of the clip to test */
0212     Q_INVOKABLE int getClipTrackId(int clipId) const;
0213 
0214     /** @brief Returns the id of the track containing composition (-1 if it is not inserted)
0215        @param clipId Id of the composition to test */
0216     Q_INVOKABLE int getCompositionTrackId(int compoId) const;
0217 
0218     /** @brief Convenience function that calls either of the previous ones based on item type*/
0219     Q_INVOKABLE int getItemTrackId(int itemId) const;
0220 
0221     Q_INVOKABLE int getCompositionPosition(int compoId) const;
0222     int getSubtitlePosition(int subId) const;
0223     int getCompositionPlaytime(int compoId) const;
0224     int getCompositionEnd(int compoId) const;
0225     std::pair<int, int> getMixInOut(int cid) const;
0226     int getMixDuration(int cid) const;
0227 
0228     /** @brief Returns an item position, item can be clip, subtitle or composition */
0229     Q_INVOKABLE int getItemPosition(int itemId) const;
0230     /** @brief Returns an item duration, item can be clip, subtitle or composition */
0231     int getItemPlaytime(int itemId) const;
0232     /** @brief Returns an item's out point on its track, item can be clip, subtitle or composition */
0233     int getItemEnd(int itemId) const;
0234 
0235     /** @brief Returns the subplaylist index of a clip in a track */
0236     int getClipSubPlaylistIndex(int cid) const;
0237     /** @brief Returns the name of a timeline clip */
0238     const QString getClipName(int cid) const;
0239 
0240     /** @brief Returns the current speed of a clip */
0241     double getClipSpeed(int clipId) const;
0242 
0243     /** @brief Helper function to query the amount of free space around a clip
0244      * @param clipId: the queried clip. If it is not inserted on a track, this functions returns 0
0245      * @param after: if true, we return the blank after the clip, otherwise, before.
0246      */
0247     int getBlankSizeNearClip(int clipId, bool after) const;
0248 
0249     /** @brief if the clip belongs to a AVSplit group, then return the id of the other corresponding clip. Otherwise, returns -1 */
0250     int getClipSplitPartner(int clipId) const;
0251 
0252     /** @brief Helper function that returns true if the given ID corresponds to a clip */
0253     Q_INVOKABLE bool isClip(int id) const;
0254 
0255     /** @brief Helper function that returns true if the given ID corresponds to a composition */
0256     Q_INVOKABLE bool isComposition(int id) const;
0257 
0258     Q_INVOKABLE bool isSubTitle(int id) const;
0259 
0260     /** @brief Helper function that returns true if the given ID corresponds to a timeline item (composition or clip) */
0261     Q_INVOKABLE bool isItem(int id) const;
0262 
0263     /** @brief Helper function that returns true if the given ID corresponds to a track */
0264     Q_INVOKABLE bool isTrack(int id) const;
0265 
0266     /** @brief Helper function that returns true if the given ID corresponds to a group */
0267     Q_INVOKABLE bool isGroup(int id) const;
0268     /** @brief Helper function that returns true if the given ID is in a group */
0269     Q_INVOKABLE bool isInGroup(int id) const;
0270 
0271     /** @brief Given a composition Id, returns its underlying parameter model */
0272     std::shared_ptr<AssetParameterModel> getCompositionParameterModel(int compoId) const;
0273     /** @brief Given a clip Id, returns its underlying effect stack model */
0274     std::shared_ptr<EffectStackModel> getClipEffectStackModel(int clipId) const;
0275     /** @brief Given a clip Id, returns its mix transition stack model */
0276     std::shared_ptr<EffectStackModel> getClipMixStackModel(int clipId) const;
0277 
0278     /** @brief Returns the position of clip (-1 if it is not inserted)
0279        @param clipId Id of the clip to test
0280     */
0281     Q_INVOKABLE int getClipPosition(int clipId) const;
0282     Q_INVOKABLE QVariantList addClipEffect(int clipId, const QString &effectId, bool notify = true);
0283     Q_INVOKABLE bool addTrackEffect(int trackId, const QString &effectId);
0284     bool removeFade(int clipId, bool fromStart);
0285     Q_INVOKABLE bool copyTrackEffect(int trackId, const QString &sourceId);
0286     bool adjustEffectLength(int clipId, const QString &effectId, int duration, int initialDuration);
0287 
0288     /** @brief Returns the closest snap point within snapDistance
0289      */
0290     Q_INVOKABLE int suggestSnapPoint(int pos, int snapDistance);
0291 
0292     /** @brief Return the previous track of same type as source trackId, or trackId if no track found */
0293     Q_INVOKABLE int getPreviousTrackId(int trackId);
0294     /** @brief Return the next track of same type as source trackId, or trackId if no track found */
0295     Q_INVOKABLE int getNextTrackId(int trackId);
0296 
0297     /** @brief Returns true if the clip has a mix composition at the end
0298        @param clipId Id of the clip to test
0299     */
0300     Q_INVOKABLE bool hasClipEndMix(int clipId) const;
0301 
0302     /** @brief Returns the in cut position of a clip
0303        @param clipId Id of the clip to test
0304     */
0305     int getClipIn(int clipId) const;
0306     /** @brief Returns the in and playtime of a clip
0307        @param clipId Id of the clip to test
0308     */
0309     QPoint getClipInDuration(int clipId) const;
0310 
0311     /** @brief Returns the clip state (audio/video only)
0312      */
0313     PlaylistState::ClipState getClipState(int clipId) const;
0314 
0315     /** @brief Returns the bin id of the clip master
0316        @param clipId Id of the clip to test
0317     */
0318     const QString getClipBinId(int clipId) const;
0319 
0320     /** @brief Returns the duration of a clip
0321        @param clipId Id of the clip to test
0322     */
0323     int getClipPlaytime(int clipId) const;
0324 
0325     /** @brief Returns the out point of a clip in its track
0326        @param clipId Id of the clip to test
0327     */
0328     int getClipEnd(int clipId) const;
0329 
0330     /** @brief Returns the size of the clip's frame (widthxheight)
0331        @param clipId Id of the clip to test
0332     */
0333     QSize getClipFrameSize(int clipId) const;
0334     /** @brief Returns the number of clips in a given track
0335        @param trackId Id of the track to test
0336     */
0337     int getTrackClipsCount(int trackId) const;
0338 
0339     /** @brief Returns the number of compositions in a given track
0340        @param trackId Id of the track to test
0341     */
0342     int getTrackCompositionsCount(int trackId) const;
0343 
0344     /** @brief Returns the position of the track in the order of the tracks
0345        @param trackId Id of the track to test
0346     */
0347     int getTrackPosition(int trackId) const;
0348 
0349     /** @brief Returns the track's index in terms of mlt's internal representation
0350      */
0351     int getTrackMltIndex(int trackId) const;
0352     /** @brief Returns a sort position for tracks.
0353      * @param separated: if true, the tracks will be sorted like: V2,V1,A1,A2
0354      * Otherwise, the tracks will be sorted like V2,A2,V1,A1
0355      */
0356     int getTrackSortValue(int trackId, int separated) const;
0357 
0358     /** @brief Returns the ids of the tracks below the given track in the order of the tracks
0359        Returns an empty list if no track available
0360        @param trackId Id of the track to test
0361     */
0362     QList<int> getLowerTracksId(int trackId, TrackType type = TrackType::AnyTrack) const;
0363 
0364     /** @brief Returns the MLT track index of the video track just below the given track
0365        @param trackId Id of the track to test
0366     */
0367     int getPreviousVideoTrackPos(int trackId) const;
0368     /** @brief Returns the Track id of the video track just below the given track
0369        @param trackId Id of the track to test
0370     */
0371     int getPreviousVideoTrackIndex(int trackId) const;
0372 
0373     /** @brief Set the marker model on this timeline (usually the marker model from its Bin Sequence clip.
0374      */
0375     void setMarkerModel(std::shared_ptr<MarkerListModel> markerModel);
0376 
0377     /** @brief Returns the Id of the corresponding audio track. If trackId corresponds to video1, this will return audio 1 and so on */
0378     int getMirrorAudioTrackId(int trackId) const;
0379     int getMirrorVideoTrackId(int trackId) const;
0380     int getMirrorTrackId(int trackId) const;
0381     /** @brief Returns true if a clip cid is on an audio track */
0382     bool clipIsAudio(int cid) const;
0383 
0384     /** @brief Sets a track in a given lock state
0385        Locked tracks can't receive any operations (resize, move, insertion, deletion...)
0386        @param trackId is of the track to alter
0387        @param lock if true, the track will be locked, otherwise unlocked.
0388     */
0389     Q_INVOKABLE void setTrackLockedState(int trackId, bool lock);
0390 
0391     /** @brief Move a clip to a specific position
0392        This action is undoable
0393        Returns true on success. If it fails, nothing is modified.
0394        If the clip is not in inserted in a track yet, it gets inserted for the first time.
0395        If the clip is in a group, the call is deferred to requestGroupMove
0396        @param clipId is the ID of the clip
0397        @param trackId is the ID of the target track
0398        @param position is the position where we want to move
0399        @param updateView if set to false, no signal is sent to qml
0400        @param logUndo if set to false, no undo object is stored
0401     */
0402     Q_INVOKABLE bool requestClipMove(int clipId, int trackId, int position, bool moveMirrorTracks = true, bool updateView = true, bool logUndo = true,
0403                                      bool invalidateTimeline = false, bool revertMove = false);
0404     Q_INVOKABLE bool requestSubtitleMove(int clipId, int position, bool updateView = true, bool logUndo = true, bool finalMove = false);
0405     bool requestSubtitleMove(int clipId, int position, bool updateView, bool first, bool last, bool finalMove, Fun &undo, Fun &redo);
0406     int cutSubtitle(int position, Fun &undo, Fun &redo);
0407     bool requestClipMix(const QString &mixId, std::pair<int, int> clipIds, std::pair<int, int> mixDurations, int trackId, int position, bool updateView,
0408                         bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove);
0409 
0410     /** @brief Move a composition to a specific position This action is undoable
0411        Returns true on success. If it fails, nothing is modified. If the clip is
0412        not in inserted in a track yet, it gets inserted for the first time. If
0413        the clip is in a group, the call is deferred to requestGroupMove @param
0414        transid is the ID of the composition @param trackId is the ID of the
0415        track */
0416     Q_INVOKABLE bool requestCompositionMove(int compoId, int trackId, int position, bool updateView = true, bool logUndo = true);
0417 
0418     /* Same function, but accumulates undo and redo, and doesn't check
0419        for group*/
0420     bool requestClipMove(int clipId, int trackId, int position, bool moveMirrorTracks, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo,
0421                          Fun &redo, bool revertMove = false, bool groupMove = false, const QMap<int, int> &moving_clips = QMap<int, int>(),
0422                          std::pair<MixInfo, MixInfo> mixData = {});
0423     bool requestCompositionMove(int transid, int trackId, int compositionTrack, int position, bool updateView, bool finalMove, Fun &undo, Fun &redo);
0424 
0425     /** @brief When timeline edit mode is insert or overwrite, we fake the move (as it will overlap existing clips, and only process the real move on drop */
0426     bool requestFakeClipMove(int clipId, int trackId, int position, bool updateView, bool invalidateTimeline, Fun &undo, Fun &redo);
0427     bool requestFakeClipMove(int clipId, int trackId, int position, bool updateView, bool logUndo, bool invalidateTimeline);
0428     bool requestFakeGroupMove(int clipId, int groupId, int delta_track, int delta_pos, bool updateView = true, bool logUndo = true);
0429     bool requestFakeGroupMove(int clipId, int groupId, int delta_track, int delta_pos, bool updateView, bool finalMove, Fun &undo, Fun &redo,
0430                               bool allowViewRefresh = true);
0431 
0432     /** @brief Given an intended move, try to suggest a more valid one
0433        (accounting for snaps and missing UI calls)
0434        @param clipId id of the clip to
0435        move
0436        @param trackId id of the target track
0437        @param position target position
0438        @param snapDistance the maximum distance for a snap result, -1 for no snapping
0439         of the clip
0440        @param dontRefreshMasterClip when false, no view refresh is attempted
0441        @returns  a list in the form {position, trackId}
0442         */
0443     Q_INVOKABLE QVariantList suggestItemMove(int itemId, int trackId, int position, int cursorPosition, int snapDistance = -1);
0444     Q_INVOKABLE QVariantList suggestClipMove(int clipId, int trackId, int position, int cursorPosition, int snapDistance = -1, bool moveMirrorTracks = true);
0445     Q_INVOKABLE int suggestSubtitleMove(int subId, int position, int cursorPosition, int snapDistance);
0446     Q_INVOKABLE QVariantList suggestCompositionMove(int compoId, int trackId, int position, int cursorPosition, int snapDistance = -1);
0447     /** @brief returns the frame pos adjusted to edit mode
0448      */
0449     Q_INVOKABLE int adjustFrame(int frame, int trackId);
0450 
0451     /** @brief Request clip insertion at given position. This action is undoable
0452        Returns true on success. If it fails, nothing is modified.
0453        @param binClipId id of the clip in the bin
0454        @param track Id of the track where to insert
0455        @param position Requested position
0456        @param ID return parameter of the id of the inserted clip
0457        @param logUndo if set to false, no undo object is stored
0458        @param refreshView whether the view should be refreshed
0459        @param useTargets: if true, the Audio/video split will occur on the set targets. Otherwise, they will be computed as an offset from the middle line
0460     */
0461     bool requestClipInsertion(const QString &binClipId, int trackId, int position, int &id, bool logUndo = true, bool refreshView = false,
0462                               bool useTargets = true);
0463     /* Same function, but accumulates undo and redo*/
0464     bool requestClipInsertion(const QString &binClipId, int trackId, int position, int &id, bool logUndo, bool refreshView, bool useTargets, Fun &undo,
0465                               Fun &redo, const QVector<int> &allowedTracks = QVector<int>());
0466 
0467     /** @brief Switch current composition type
0468      *  @param cid the id of the composition we want to change
0469      *  @param compoId the name of the new composition we want to insert
0470      */
0471     void switchComposition(int cid, const QString &compoId);
0472     /**  @brief Plant a same track composition in track tid
0473      */
0474     bool plantMix(int tid, Mlt::Transition *t);
0475     bool removeMixWithUndo(int cid, Fun &undo, Fun &redo);
0476     bool removeMix(int cid);
0477     /**  @brief Returns a list of the master effects zones
0478      */
0479     QVariantList getMasterEffectZones() const;
0480     /**  @brief Returns a list of proxied clips at position pos
0481      */
0482     QStringList getProxiesAt(int position);
0483     /**  @brief Returns the current project xml playlist for saving
0484      */
0485     const QString sceneList(const QString &root, const QString &fullPath = QString(), const QString &filterData = QString());
0486 
0487     /**  @brief Lock or unlock a track
0488      */
0489     void lockTrack(int trackId, bool lock);
0490     /**  @brief Returns this timeline's uuid
0491      */
0492     const QUuid uuid() const;
0493     /**  @brief Initialize the preview manager, responsible for timeline preview
0494      */
0495     void initializePreviewManager();
0496     void resetPreviewManager();
0497     bool hasTimelinePreview() const;
0498     /**  @brief Enable/disable timeline preview
0499      */
0500     void updatePreviewConnection(bool enable);
0501     bool buildPreviewTrack();
0502     void setOverlayTrack(Mlt::Playlist *overlay);
0503     void removeOverlayTrack();
0504     void deletePreviewTrack();
0505     std::shared_ptr<PreviewManager> previewManager();
0506     /**  @brief We want to delete the timelineModel without removing clips from tractor
0507      */
0508     void prepareShutDown();
0509     /**  @brief True if we are selecting a single item in a group
0510      */
0511     bool singleSelectionMode() const;
0512 
0513 protected:
0514     /** @brief Creates a new clip instance without inserting it.
0515        This action is undoable, returns true on success
0516        @param binClipId: Bin id of the clip to insert
0517        @param id: return parameter for the id of the newly created clip.
0518        @param state: The desired clip state (original, audio/video only).
0519      */
0520     bool requestClipCreation(const QString &binClipId, int &id, PlaylistState::ClipState state, int audioStream, double speed, bool warp_pitch, Fun &undo,
0521                              Fun &redo);
0522 
0523     /** @brief Switch item selection status */
0524     void setSelected(int itemId, bool sel);
0525 
0526 public:
0527     /** @brief Deletes the given clip or composition from the timeline.
0528        This action is undoable.
0529        Returns true on success. If it fails, nothing is modified.
0530        If the clip/composition is in a group, the call is deferred to requestGroupDeletion
0531        @param clipId is the ID of the clip/composition
0532        @param logUndo if set to false, no undo object is stored */
0533     Q_INVOKABLE bool requestItemDeletion(int itemId, bool logUndo = true);
0534     /* Same function, but accumulates undo and redo*/
0535     bool requestItemDeletion(int itemId, Fun &undo, Fun &redo, bool logUndo = false);
0536 
0537     /** @brief Move a group to a specific position
0538        This action is undoable
0539        Returns true on success. If it fails, nothing is modified.
0540        If the clips in the group are not in inserted in a track yet, they get inserted for the first time.
0541        @param clipId is the id of the clip that triggers the group move
0542        @param groupId is the id of the group
0543        @param delta_track is the delta applied to the track index
0544        @param delta_pos is the requested position change
0545        @param updateView if set to false, no signal is sent to qml for the clip clipId
0546        @param logUndo if set to true, an undo object is created
0547        @param allowViewRefresh if false, the view will never get updated (useful for suggestMove)
0548     */
0549     bool requestGroupMove(int itemId, int groupId, int delta_track, int delta_pos, bool moveMirrorTracks = true, bool updateView = true, bool logUndo = true,
0550                           bool revertMove = false);
0551     bool requestGroupMove(int itemId, int groupId, int delta_track, int delta_pos, bool updateView, bool finalMove, Fun &undo, Fun &redo,
0552                           bool revertMove = false, bool moveMirrorTracks = true, bool allowViewRefresh = true,
0553                           const QVector<int> &allowedTracks = QVector<int>());
0554 
0555     /** @brief Deletes all clips inside the group that contains the given clip.
0556        This action is undoable
0557        Note that if their is a hierarchy of groups, all of them will be deleted.
0558        Returns true on success. If it fails, nothing is modified.
0559        @param clipId is the id of the clip that triggers the group deletion
0560     */
0561     Q_INVOKABLE bool requestGroupDeletion(int clipId, bool logUndo = true);
0562     bool requestGroupDeletion(int clipId, Fun &undo, Fun &redo);
0563 
0564     /** @brief Change the duration of an item (clip or composition)
0565      *  This action is undoable
0566      *  Returns the real size reached (can be different, if snapping occurs).
0567      *  If it fails, nothing is modified, and -1 is returned
0568      *  @param itemId is the ID of the item
0569      *  @param size is the new size of the item
0570      *  @param right is true if we change the right side of the item, false otherwise
0571      *  @param logUndo if set to true, an undo object is created
0572      *  @param snap if set to true, the resize order will be coerced to use the snapping grid
0573      *  if @param allowSingleResize is false, then the resize will also be applied to any clip in the same AV group (allow resizing audio and video at the same
0574      *  time)
0575      */
0576     Q_INVOKABLE int requestItemResize(int itemId, int size, bool right, bool logUndo = true, int snapDistance = -1, bool allowSingleResize = false);
0577 
0578     /** @brief Same function, but accumulates undo and redo and doesn't deal with snapping*/
0579     bool requestItemResize(int itemId, int &size, bool right, bool logUndo, Fun &undo, Fun &redo, bool blockUndo = false);
0580 
0581     /** @brief @todo TODO */
0582     int requestItemRippleResize(const std::shared_ptr<TimelineItemModel> &timeline, int itemId, int size, bool right, bool logUndo = true,
0583                                 bool moveGuides = false, int snapDistance = -1, bool allowSingleResize = false);
0584     /** @brief @todo TODO */
0585     bool requestItemRippleResize(const std::shared_ptr<TimelineItemModel> &timeline, int itemId, int size, bool right, bool logUndo, bool moveGuides, Fun &undo,
0586                                  Fun &redo, bool blockUndo = false);
0587 
0588     /** @brief Move ("slip") in and out point of a clip by the given offset
0589        This action is undoable
0590        @param itemId is the ID of the clip
0591        @param offset is how many frames in and out point should be slipped
0592        @param logUndo if set to true, an undo object is created
0593        @param allowSingleResize is false, then the resize will also be applied to any clip in the same group
0594        @return The request offset (can be different from real offset). If it fails, nothing is modified, and 0 is returned
0595     */
0596     Q_INVOKABLE int requestClipSlip(int itemId, int offset, bool logUndo = true, bool allowSingleResize = false);
0597 
0598     /** @brief Same function, but accumulates undo and redo
0599      *  @return If it fails, nothing is modified, and false is returned
0600      */
0601     bool requestClipSlip(int itemId, int offset, bool logUndo, Fun &undo, Fun &redo, bool blockUndo = false);
0602 
0603     /** @brief Slip all the clips of the current timeline selection
0604      * @see requestClipSlip
0605      */
0606     Q_INVOKABLE int requestSlipSelection(int offset, bool logUndo);
0607     /** @brief Return true if multiple items are selected in timeline
0608      */
0609     Q_INVOKABLE bool hasMultipleSelection() const;
0610 
0611     /** @brief Returns a proposed size for clip resize, checking for collisions */
0612     Q_INVOKABLE int requestItemSpeedChange(int itemId, int size, bool right, int snapDistance);
0613     /** @brief Returns a list of {id, position duration} for all elements in the group*/
0614     Q_INVOKABLE const QVariantList getGroupData(int itemId);
0615     Q_INVOKABLE void processGroupResize(QVariantList startPosList, QVariantList endPosList, bool right);
0616 
0617     Q_INVOKABLE int requestClipResizeAndTimeWarp(int itemId, int size, bool right, int snapDistance, bool allowSingleResize, double speed);
0618 
0619     /** @brief Group together a set of ids
0620        The ids are either a group ids or clip ids. The involved clip must already be inserted in a track
0621        This action is undoable
0622        Returns the group id on success, -1 if it fails and nothing is modified.
0623        Typically, ids would be ids of clips, but for convenience, some of them can be ids of groups as well.
0624        @param ids Set of ids to group
0625     */
0626     int requestClipsGroup(const std::unordered_set<int> &ids, bool logUndo = true, GroupType type = GroupType::Normal);
0627     int requestClipsGroup(const std::unordered_set<int> &ids, Fun &undo, Fun &redo, GroupType type = GroupType::Normal);
0628 
0629     /** @brief Destruct the topmost group containing clip
0630        This action is undoable
0631        Returns true on success. If it fails, nothing is modified.
0632        @param id of the clip to degroup (all clips belonging to the same group will be ungrouped as well)
0633     */
0634     bool requestClipUngroup(int itemId, bool logUndo = true);
0635     /** Same function, but accumulates undo and redo @see requestClipUngroup*/
0636     bool requestClipUngroup(int itemId, Fun &undo, Fun &redo);
0637     /** Remove an item from a group*/
0638     bool requestRemoveFromGroup(int itemId, Fun &undo, Fun &redo);
0639     /** @brief convenience functions for several ids at the same time */
0640     bool requestClipsUngroup(const std::unordered_set<int> &itemIds, bool logUndo = true);
0641 
0642     /** @brief Create a track at given position
0643        This action is undoable
0644        Returns true on success. If it fails, nothing is modified.
0645        @param Requested position (order). If set to -1, the track is inserted last.
0646        @param id is a return parameter that holds the id of the resulting track (-1 on failure)
0647     */
0648     bool requestTrackInsertion(int pos, int &id, const QString &trackName = QString(), bool audioTrack = false);
0649     /* Same function, but accumulates undo and redo*/
0650     bool requestTrackInsertion(int pos, int &id, const QString &trackName, bool audioTrack, Fun &undo, Fun &redo, bool addCompositing = true);
0651 
0652     /** @brief Delete track with given id
0653        This also deletes all the clips contained in the track.
0654        This action is undoable
0655        Returns true on success. If it fails, nothing is modified.
0656        @param trackId id of the track to delete
0657     */
0658     bool requestTrackDeletion(int trackId);
0659     /** @brief Same function, but accumulates undo and redo*/
0660     bool requestTrackDeletion(int trackId, Fun &undo, Fun &redo);
0661 
0662     /** @brief Get project duration
0663        Returns the duration in frames
0664     */
0665     int duration() const;
0666     static int seekDuration; /// Duration after project end where seeking is allowed
0667     /** @brief True until the timeline has all tracks and clips loaded
0668      */
0669     bool isLoading{true};
0670 
0671     /** @brief Get all the elements of the same group as the given clip.
0672        If there is a group hierarchy, only the topmost group is considered.
0673        @param clipId id of the clip to test
0674     */
0675     std::unordered_set<int> getGroupElements(int clipId);
0676 
0677     /** @brief Removes all the elements on the timeline (tracks and clips)
0678      */
0679     bool requestReset(Fun &undo, Fun &redo);
0680     /** @brief Updates the current the pointer to the current undo_stack
0681        Must be called for example when the doc change
0682     */
0683     void setUndoStack(std::weak_ptr<DocUndoStack> undo_stack);
0684     /** @brief Calculate timeline hash based on clips, mixes and compositions
0685      */
0686     QByteArray timelineHash();
0687     /** @brief Make the background track transparent (or opaque black) - this affects compositing.
0688      */
0689     void makeTransparentBg(bool transparent);
0690 
0691 protected:
0692     /** @brief Requests the best snapped position for a clip
0693        @param pos is the clip's requested position
0694        @param length is the clip's duration
0695        @param pts snap points to ignore (for example currently moved clip)
0696        @param snapDistance the maximum distance for a snap result, -1 for no snapping
0697        @returns best snap position or -1 if no snap point is near
0698      */
0699     int getBestSnapPos(int referencePos, int diff, std::vector<int> pts = std::vector<int>(), int cursorPosition = 0, int snapDistance = -1);
0700 
0701     /** @brief Returns the best possible size for a clip on resize
0702      */
0703     int requestItemResizeInfo(int itemId, int in, int out, int size, bool right, int snapDistance);
0704 
0705     /** @brief Returns a list of in/out of all items in the group of itemId
0706      */
0707     const std::vector<int> getBoundaries(int itemId);
0708 
0709 public:
0710     /** @brief Requests the next snapped point
0711        @param pos is the current position
0712      */
0713     int getNextSnapPos(int pos, std::vector<int> &snaps, std::vector<int> &ignored);
0714 
0715     /** @brief Requests the previous snapped point
0716        @param pos is the current position
0717      */
0718     int getPreviousSnapPos(int pos, std::vector<int> &snaps, std::vector<int> &ignored);
0719 
0720     /** @brief Add a new snap point
0721        @param pos is the current position
0722      */
0723     void addSnap(int pos);
0724 
0725     /** @brief Remove snap point
0726        @param pos is the current position
0727      */
0728     void removeSnap(int pos);
0729     /** @brief Create a composition.
0730        This action is undoable
0731        Returns true on success. If it fails, nothing is modified.
0732        @param transitionId Identifier of the Mlt transition to insert (as given by repository)
0733        @param length Requested initial length.
0734        @param transProps The properties for the transition.
0735        @param id return parameter of the id of the inserted composition
0736        @param logUndo if set to false, no undo object is stored
0737     */
0738     bool requestCompositionCreation(const QString &transitionId, int length, std::unique_ptr<Mlt::Properties> transProps, int &id, Fun &undo, Fun &redo,
0739                                     bool finalMove = false, const QString &originalDecimalPoint = QString());
0740 
0741     /** @brief Request composition insertion at given position.
0742        This action is undoable
0743        Returns true on success. If it fails, nothing is modified.
0744        @param transitionId Identifier of the Mlt transition to insert (as given by repository)
0745        @param track Id of the track where to insert
0746        @param position Requested position
0747        @param length Requested initial length.
0748        @param transProps The properties for the transition.
0749        @param id return parameter of the id of the inserted composition
0750        @param logUndo if set to false, no undo object is stored
0751     */
0752     bool requestCompositionInsertion(const QString &transitionId, int trackId, int position, int length, std::unique_ptr<Mlt::Properties> transProps, int &id,
0753                                      bool logUndo = true);
0754     /* Same function, but accumulates undo and redo*/
0755     bool requestCompositionInsertion(const QString &transitionId, int trackId, int compositionTrack, int position, int length,
0756                                      std::unique_ptr<Mlt::Properties> transProps, int &id, Fun &undo, Fun &redo, bool finalMove = false,
0757                                      const QString &originalDecimalPoint = QString());
0758 
0759     /** @brief This function change the global (timeline-wise) enabled state of the effects
0760        It disables/enables track and clip effects (recursively)
0761      */
0762     void setTimelineEffectsEnabled(bool enabled);
0763 
0764     /** @brief Get a timeline clip id by its position or -1 if not found
0765      */
0766     int getClipByPosition(int trackId, int position, int playlist = -1) const;
0767     int getClipByStartPosition(int trackId, int position) const;
0768 
0769     /** @brief Get a timeline composition id by its starting position or -1 if not found
0770      */
0771     int getCompositionByPosition(int trackId, int position) const;
0772     /** @brief Get a timeline subtitle id by its starting position or -1 if not found
0773      */
0774     int getSubtitleByStartPosition(int position) const;
0775     int getSubtitleByPosition(int position) const;
0776 
0777     /** @brief Returns a list of all items that are intersect with a given range.
0778      * @param trackId is the id of the track for concerned items. Setting trackId to -1 returns items on all tracks
0779      * @param start is the position where we the items should start
0780      * @param end is the position after which items will not be selected, set to -1 to get all clips on track
0781      * @param listCompositions if enabled, the list will also contains composition ids
0782      */
0783     std::unordered_set<int> getItemsInRange(int trackId, int start, int end = -1, bool listCompositions = true);
0784 
0785     /** @brief Returns a list of all luma files used in the project
0786      */
0787     QStringList extractCompositionLumas() const;
0788     /** @brief Returns a list of all external files used by effects in the timeline
0789      */
0790     QStringList extractExternalEffectFiles() const;
0791     /** @brief Inform asset view of duration change
0792      */
0793     virtual void adjustAssetRange(int clipId, int in, int out);
0794     /** @brief Reload a timeline clip occurrence from its bin clip.
0795      *  @returns true if the timeline clip was shortened by the reload operation
0796      */
0797     bool requestClipReload(int clipId, int forceDuration, Fun &local_undo, Fun &local_redo);
0798     void requestClipUpdate(int clipId, const QVector<int> &roles);
0799     /** @brief define current edit mode (normal, insert, overwrite */
0800     void setEditMode(TimelineMode::EditMode mode);
0801     TimelineMode::EditMode editMode() const;
0802     Q_INVOKABLE bool normalEdit() const;
0803 
0804     /** @brief Returns the effectstack of a given clip. */
0805     std::shared_ptr<EffectStackModel> getClipEffectStack(int itemId);
0806     std::shared_ptr<EffectStackModel> getTrackEffectStackModel(int trackId);
0807     std::shared_ptr<EffectStackModel> getMasterEffectStackModel();
0808 
0809     /** @brief Add slowmotion effect to clip in timeline.
0810      @param clipId id of the target clip
0811     @param speed: speed in percentage. 100 corresponds to original speed, 50 to half the speed
0812     This functions create an undo object and also apply the effect to the corresponding audio if there is any.
0813     Returns true on success, false otherwise (and nothing is modified)
0814     */
0815     Q_INVOKABLE bool requestClipTimeWarp(int clipId, double speed, bool pitchCompensate, bool changeDuration);
0816     /** @brief Same function as above, but doesn't check for paired audio and accumulate undo/redo
0817      */
0818     bool requestClipTimeWarp(int clipId, double speed, bool pitchCompensate, bool changeDuration, Fun &undo, Fun &redo);
0819     bool requestClipTimeRemap(int clipId, bool enable = true);
0820     bool requestClipTimeRemap(int clipId, bool enable, Fun &undo, Fun &redo);
0821     std::shared_ptr<Mlt::Producer> getClipProducer(int clipId);
0822 
0823     void replugClip(int clipId);
0824 
0825     /** @brief Refresh the tractor profile in case a change was requested. */
0826     // void updateProfile(Mlt::Profile profile);
0827 
0828     /** @brief Add, remove or refresh the internal added avfilter.fieldorder effect based on the given profile*/
0829     void updateFieldOrderFilter(std::unique_ptr<ProfileModel> &ptr);
0830 
0831     /** @brief Clear the current selection
0832         @param onDeletion is true when the selection is cleared as a result of a deletion
0833      */
0834     Q_INVOKABLE bool requestClearSelection(bool onDeletion = false);
0835 
0836     /** @brief On groups deletion, ensure the groups were not selected, clear selection otherwise
0837         @param groups The group ids
0838      */
0839     void clearGroupSelectionOnDelete(std::vector<int> groups);
0840     // same function with undo/redo accumulation
0841     void requestClearSelection(bool onDeletion, Fun &undo, Fun &redo);
0842 
0843     /** @brief Select a given mix in timeline
0844         @param cid clip id
0845      */
0846     Q_INVOKABLE void requestMixSelection(int cid);
0847 
0848     /** @brief Add the given item to the selection
0849         If @param clear is true, the selection is first cleared
0850      */
0851     Q_INVOKABLE void requestAddToSelection(int itemId, bool clear = false, bool singleSelect = false);
0852 
0853     /** @brief Remove the given item from the selection */
0854     Q_INVOKABLE void requestRemoveFromSelection(int itemId);
0855 
0856     /** @brief Set the selection to the set of given ids */
0857     bool requestSetSelection(const std::unordered_set<int> &ids);
0858     // same function with undo/redo
0859     bool requestSetSelection(const std::unordered_set<int> &ids, Fun &undo, Fun &redo);
0860 
0861     /** @brief Returns a set containing all the items in the selection */
0862     std::unordered_set<int> getCurrentSelection() const;
0863 
0864     /** @brief Do some cleanup before closing */
0865     void prepareClose(bool softDelete = false);
0866     /** @brief Import project's master effects */
0867     void importMasterEffects(std::weak_ptr<Mlt::Service> service);
0868     /** @brief Create a mix selection with currently selected clip. If delta = -1, mix with previous clip, +1 with next clip and 0 will check cursor position*/
0869     bool mixClip(int idToMove = -1, const QString &mixId = QStringLiteral("luma"), int delta = 0);
0870     Q_INVOKABLE bool resizeStartMix(int cid, int duration, bool singleResize);
0871     void requestResizeMix(int cid, int duration, MixAlignment align, int leftFrames = -1);
0872     /** @brief Get Mix cut pos (the duration of the mix on the right clip) */
0873     int getMixCutPos(int cid) const;
0874     MixAlignment getMixAlign(int cid) const;
0875     bool hasSubtitleModel();
0876     /** @brief Get the frame size of the clip above a composition */
0877     const QSize getCompositionSizeOnTrack(const ObjectId &id);
0878     /** @brief Get a track tag (A1, V1, V2,...) through its id */
0879     const QString getTrackTagById(int trackId) const;
0880     /** @brief returns true if track is empty at position on playlist */
0881     bool trackIsBlankAt(int tid, int pos, int playlist) const;
0882     /** @brief returns true if track is empty at position on playlist */
0883     bool trackIsAvailable(int tid, int pos, int duration, int playlist) const;
0884     /** @brief returns the position of the clip start on a playlist */
0885     int getClipStartAt(int tid, int pos, int playlist) const;
0886     int getClipEndAt(int tid, int pos, int playlist) const;
0887     /** @brief returns true if the track trackId is Locked */
0888     bool trackIsLocked(int trackid) const;
0889     /** @brief returns this timeline's subtitle model */
0890     std::shared_ptr<SubtitleModel> getSubtitleModel();
0891     /** @brief returns this timeline's guide model */
0892     std::shared_ptr<MarkerListModel> getGuideModel();
0893     std::shared_ptr<MarkerSortModel> getFilteredGuideModel();
0894     /** @brief The sequence name displayed in master effec button needs an update */
0895     void updateVisibleSequenceName(const QString displayName);
0896     /** @brief Register all clips in this sequence to Bin */
0897     void registerTimeline();
0898     /** @brief Load timeline preview on project opening */
0899     void loadPreview(const QString &chunks, const QString &dirty, bool enable, Mlt::Playlist &playlist);
0900 
0901 protected:
0902     /** @brief Register a new track. This is a call-back meant to be called from TrackModel
0903        @param pos indicates the number of the track we are adding. If this is -1, then we add at the end.
0904      */
0905     void registerTrack(std::shared_ptr<TrackModel> track, int pos = -1, bool doInsert = true, bool singleOperation = true);
0906 
0907     /** @brief Register a new clip. This is a call-back meant to be called from ClipModel
0908      */
0909     void registerClip(const std::shared_ptr<ClipModel> &clip, bool registerProducer = false);
0910 
0911     /** @brief Register a new composition. This is a call-back meant to be called from CompositionModel
0912      */
0913     void registerComposition(const std::shared_ptr<CompositionModel> &composition);
0914 
0915     void registerSubtitle(int id, GenTime startTime, bool temporary = false);
0916     void deregisterSubtitle(int id, bool temporary = false);
0917     /** @brief Returns the index for a subtitle's id (it's position in the list
0918      */
0919     int positionForIndex(int id);
0920 
0921     /** @brief Register a new group. This is a call-back meant to be called from GroupsModel
0922      */
0923     void registerGroup(int groupId);
0924 
0925     /** @brief Deregister and destruct the track with given id.
0926        @param updateView Whether to send updates to the model. Must be false when called from a constructor/destructor
0927      */
0928     Fun deregisterTrack_lambda(int id);
0929 
0930     /** @brief Return a lambda that deregisters and destructs the clip with given id.
0931        Note that the clip must already be deleted from its track and groups.
0932      */
0933     Fun deregisterClip_lambda(int id);
0934 
0935     /** @brief Return a lambda that deregisters and destructs the composition with given id.
0936      */
0937     Fun deregisterComposition_lambda(int compoId);
0938 
0939     /** @brief Deregister a group with given id
0940      */
0941     void deregisterGroup(int id);
0942 
0943     /** @brief Helper function to get a pointer to the track, given its id
0944      */
0945     std::shared_ptr<TrackModel> getTrackById(int trackId);
0946     const std::shared_ptr<TrackModel> getTrackById_const(int trackId) const;
0947 
0948     /** @brief Helper function to get a pointer to a clip, given its id*/
0949     std::shared_ptr<ClipModel> getClipPtr(int clipId) const;
0950 
0951     /** @brief Helper function to get a pointer to a composition, given its id*/
0952     std::shared_ptr<CompositionModel> getCompositionPtr(int compoId) const;
0953 
0954     /** @brief Returns next valid unique id to create an object
0955      */
0956     static int getNextId();
0957 
0958     /** @brief unplant and the replant all the compositions in the correct order
0959        @param currentCompo is the id of a compo that have not yet been planted, if any. Otherwise send -1
0960      */
0961     bool replantCompositions(int currentCompo, bool updateView);
0962 
0963     /** @brief Unplant the composition with given Id */
0964     bool unplantComposition(int compoId);
0965 
0966     /** @brief Internal functions to delete a clip or a composition. In general, you should call requestItemDeletion */
0967     bool requestClipDeletion(int clipId, Fun &undo, Fun &redo, bool logUndo = true);
0968     bool requestCompositionDeletion(int compositionId, Fun &undo, Fun &redo);
0969     bool requestSubtitleDeletion(int clipId, Fun &undo, Fun &redo, bool first, bool last);
0970 
0971     /** @brief Check tracks duration and update black track accordingly */
0972     void updateDuration();
0973 
0974     /** @brief Attempt to make a clip move without ever updating the view */
0975     bool requestClipMoveAttempt(int clipId, int trackId, int position);
0976 
0977     int getSubtitleIndex(int subId) const;
0978     std::pair<int, GenTime> getSubtitleIdFromIndex(int index) const;
0979 
0980 public:
0981     /** @brief Debugging function that checks consistency with Mlt objects */
0982     bool checkConsistency(const std::vector<int> &guideSnaps = {});
0983 
0984 protected:
0985     /** @brief Refresh project monitor if cursor was inside range */
0986     void checkRefresh(int start, int end);
0987 
0988     bool m_blockRefresh;
0989 
0990 Q_SIGNALS:
0991     /** @brief signal triggered by clearAssetView */
0992     void requestClearAssetView(int);
0993     void requestMonitorRefresh();
0994     /** @brief signal triggered by track operations */
0995     void invalidateZone(int in, int out);
0996     /** @brief signal triggered when a track duration changed (insertion/deletion) */
0997     void durationUpdated(const QUuid &uuid);
0998 
0999     /** @brief Signal sent whenever the selection changes */
1000     void selectionChanged();
1001     /** @brief Signal sent whenever the selected mix changes */
1002     void selectedMixChanged(int cid, const std::shared_ptr<AssetParameterModel> &asset, bool refreshOnly = false);
1003     /** @brief Signal when a track is deleted so we make sure we don't store its id */
1004     void checkTrackDeletion(int tid);
1005     /** @brief Emitted when a clip is deleted to check if it was not used in timeline qml */
1006     void checkItemDeletion(int cid);
1007     /** @brief request animation of the track tid lock icon */
1008     void flashLock(int tid);
1009     /** @brief Save guide categories in document properties */
1010     void saveGuideCategories();
1011     /** @brief Highlight a subtitle item in timeline */
1012     void highlightSub(int index);
1013     /** @brief The visible sequence name has to be changed */
1014     void visibleSequenceNameChanged();
1015     /** @brief Connect the preview manager with timelinecontroller */
1016     void connectPreviewManager();
1017     /** @brief An editable clip action changed, refresh menus */
1018     void refreshClipActions();
1019     /** @brief We switched from single to normal selection mode */
1020     void selectionModeChanged();
1021 
1022 protected:
1023     QUuid m_uuid;
1024     std::unique_ptr<Mlt::Tractor> m_tractor;
1025     std::shared_ptr<EffectStackModel> m_masterStack;
1026     std::shared_ptr<Mlt::Service> m_masterService;
1027     std::list<std::shared_ptr<TrackModel>> m_allTracks;
1028     std::shared_ptr<PreviewManager> m_timelinePreview;
1029 
1030     std::unordered_map<int, std::list<std::shared_ptr<TrackModel>>::iterator>
1031         m_iteratorTable; // this logs the iterator associated which each track id. This allows easy access of a track based on its id.
1032 
1033     std::unordered_map<int, std::shared_ptr<ClipModel>> m_allClips; // the keys are the clip id, and the values are the corresponding pointers
1034 
1035     std::unordered_map<int, std::shared_ptr<CompositionModel>>
1036         m_allCompositions; // the keys are the composition id, and the values are the corresponding pointers
1037 
1038     // TODO: move this in subtitlemodel.h
1039     std::map<int, GenTime> m_allSubtitles;
1040 
1041     std::unique_ptr<GroupsModel> m_groups;
1042     std::shared_ptr<SnapModel> m_snaps;
1043     std::shared_ptr<SubtitleModel> m_subtitleModel{nullptr};
1044 
1045     std::unordered_set<int> m_allGroups; /// ids of all the groups
1046 
1047     std::weak_ptr<DocUndoStack> m_undoStack;
1048 
1049     // The black track producer. Its length / out should always be adjusted to the projects's length
1050     std::unique_ptr<Mlt::Producer> m_blackClip;
1051 
1052     mutable QReadWriteLock m_lock; // This is a lock that ensures safety in case of concurrent access
1053 
1054     bool m_timelineEffectsEnabled;
1055 
1056     bool m_id; // id of the timeline itself
1057 
1058     /** @brief id of the selection. If -1, there is no selection, if positive, then it might either be the id of the selection group, or the id of an individual
1059      *  item, or, finally, the id of a group which is not of type selection. The last case happens when the selection exactly matches an existing group
1060      *  (in that case we cannot further group it because the selection would have only one child, which is prohibited by design) */
1061     std::unordered_set<int> m_currentSelection;
1062     int m_selectedMix = -1;
1063 
1064     /// The index of the temporary overlay track in tractor, or -1 if not connected
1065     int m_overlayTrackCount;
1066 
1067     /// The preferred audio target for clip insertion in the form {timeline track id, bin clip stream index}
1068     QMap<int, int> m_audioTarget;
1069     /** @brief The list of audio streams available from the selected bin clip, in the form: {stream index, stream description} */
1070     QMap<int, QString> m_binAudioTargets;
1071     /// The preferred video target for clip insertion or -1 if not defined
1072     int m_videoTarget;
1073     /// Timeline editing mode
1074     TimelineMode::EditMode m_editMode;
1075     bool m_closing;
1076     bool m_softDelete;
1077     std::shared_ptr<MarkerSortModel> m_guidesFilterModel;
1078     std::shared_ptr<MarkerListModel> m_guidesModel;
1079     QString m_visibleSequenceName;
1080     /** @brief True if we are selecting a single item in a group */
1081     bool m_singleSelectionMode{false};
1082 
1083     // what follows are some virtual function that corresponds to the QML. They are implemented in TimelineItemModel
1084 protected:
1085     /** @brief Rebuild track compositing */
1086     virtual void buildTrackCompositing(bool rebuild = false) = 0;
1087     virtual void _beginRemoveRows(const QModelIndex &, int, int) = 0;
1088     virtual void _beginInsertRows(const QModelIndex &, int, int) = 0;
1089     virtual void _endRemoveRows() = 0;
1090     virtual void _endInsertRows() = 0;
1091     virtual void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, bool start, bool duration, bool updateThumb) = 0;
1092     virtual void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, const QVector<int> &roles) = 0;
1093     virtual void notifyChange(const QModelIndex &topleft, const QModelIndex &bottomright, int role) = 0;
1094     virtual QModelIndex makeClipIndexFromID(int) const = 0;
1095     virtual QModelIndex makeCompositionIndexFromID(int) const = 0;
1096     virtual QModelIndex makeTrackIndexFromID(int) const = 0;
1097     virtual void _resetView() = 0;
1098 };