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 };