File indexing completed on 2024-06-23 04:26:36
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Saurabh Kumar <saurabhk660@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #ifndef STORYBOARD_MODEL 0007 #define STORYBOARD_MODEL 0008 0009 #include "StoryboardItem.h" 0010 #include "CommentModel.h" 0011 0012 #include <QAbstractListModel> 0013 #include <QItemSelection> 0014 0015 #include <kis_keyframe_channel.h> 0016 #include "kis_idle_watcher.h" 0017 #include <kritastoryboarddocker_export.h> 0018 #include <kis_image.h> 0019 #include <kis_signal_compressor.h> 0020 0021 class StoryboardView; 0022 class KisTimeSpan; 0023 class KisStoryboardThumbnailRenderScheduler; 0024 class KUndo2Command; 0025 0026 /** 0027 * @class StoryboardModel 0028 * @brief The main storyboard model. This class manages a @c StoryboardItemList 0029 * which is a list of @c StoryboardItem objects. It provides the interface to 0030 * manipulate and access the data. 0031 */ 0032 class KRITASTORYBOARDDOCKER_EXPORT StoryboardModel : public QAbstractItemModel 0033 { 0034 Q_OBJECT 0035 0036 public: 0037 enum AdditionalRoles { 0038 TotalSceneDurationInFrames = Qt::UserRole + 1, 0039 TotalSceneDurationInSeconds = Qt::UserRole + 2, 0040 }; 0041 0042 class KeyframeReorderLock { 0043 public: 0044 KeyframeReorderLock(StoryboardModel* model) 0045 : m_model(model) 0046 , m_originalLock(!model->m_reorderingKeyframes) { 0047 m_model->m_reorderingKeyframes = true; 0048 } 0049 0050 ~KeyframeReorderLock() { 0051 m_model->m_reorderingKeyframes = !m_originalLock; 0052 } 0053 0054 private: 0055 StoryboardModel* m_model; 0056 bool m_originalLock = false; 0057 }; 0058 0059 StoryboardModel(QObject *parent); 0060 ~StoryboardModel() override; 0061 0062 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; 0063 QModelIndex parent(const QModelIndex &index) const override; 0064 0065 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 0066 int columnCount(const QModelIndex &parent = QModelIndex()) const override; 0067 0068 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 0069 bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; 0070 0071 /** 0072 * @brief Sets the @c scrollValue of the CommentBox object 0073 * @param index The index of the CommentBox object whose @c scrollValue is changed 0074 * @param value The new @c scrollValue 0075 * @return @c True if data was set 0076 * @sa CommentBox 0077 */ 0078 bool setCommentScrollData(const QModelIndex & index, const QVariant & value); 0079 0080 /** 0081 * @brief Sets the Pixmap data. 0082 * @param parentIndex The index of item whose thumbnail changed. 0083 * @param dev Projection of the new pixmap. 0084 * @return @c True if data was set 0085 * @sa ThumbnailData 0086 */ 0087 bool setThumbnailPixmapData(const QModelIndex & parentIndex, const KisPaintDeviceSP & dev); 0088 0089 /** 0090 * @brief updates the duration data of item at @c parentIndex to the number 0091 * of frame to the next @c keyframe in any @c layer. 0092 * @param parentIndex The index whose duration is to be updated. 0093 * @return @c True if data was set 0094 * @note If there are no keyframes after this index's frame duration is set to 0s 0f 0095 */ 0096 bool updateDurationData(const QModelIndex & parentIndex); 0097 0098 Qt::ItemFlags flags(const QModelIndex &index) const override; 0099 0100 //for removing and inserting rows 0101 bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; 0102 bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex())override; 0103 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, 0104 const QModelIndex &destinationParent, int destinationChild) override; 0105 0106 //for drag and drop 0107 QStringList mimeTypes() const override; 0108 QMimeData *mimeData(const QModelIndexList &indexes) const override; 0109 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; 0110 Qt::DropActions supportedDropActions() const override; 0111 Qt::DropActions supportedDragActions() const override; 0112 0113 //these function access the value from the comment model 0114 /** 0115 * @brief Used in @c StoryboardDelegate and @c StoryboardView to get size of one storyboard item. 0116 * @return Number of visible comments. 0117 * @sa StoryboardDelegate::sizeHint(QStyleOptionViewItem,QModelIndex) 0118 * @sa StoryboardView::visualRect(QModelIndex) 0119 */ 0120 int visibleCommentCount() const; 0121 0122 /** 0123 * @brief Get total number of comments. 0124 * @return Number of total comments. 0125 */ 0126 int totalCommentCount(); 0127 0128 /** 0129 * @brief Used in @c StoryboardView to design the layout of storyboard item. 0130 * @return Number of visible comments upto index. 0131 * @sa StoryboardView::visualRect(QModelIndex) 0132 */ 0133 int visibleCommentsUpto(QModelIndex index) const; 0134 0135 /** 0136 * @brief Sets the commentModel in StoryboardModel and creates connections 0137 * to keep the local copy of comments in sync with the commentModel's. 0138 * @sa slotCommentDataChanged(); 0139 * @sa slotCommentRowInserted(QModelIndex,int,int) 0140 * @sa slotCommentRowRemoved(QModelIndex,int,int) 0141 * @sa slotCommentRowMoved(); 0142 */ 0143 void setCommentModel(StoryboardCommentModel *commentModel); 0144 0145 /** 0146 * @param row The row of the comment. 0147 * @return The Comment object at row in comment's list. 0148 * @note The Comment object contains the name of the comment. 0149 * Not to be confused with CommentBox. 0150 * @sa Comment 0151 */ 0152 StoryboardComment getComment(int row) const; 0153 0154 void setFreeze(bool); 0155 bool isFrozen() const; 0156 void setLocked(bool); 0157 bool isLocked() const; 0158 int getFramesPerSecond() const; 0159 void setView(StoryboardView *view); 0160 void setImage(KisImageWSP image); 0161 0162 /** 0163 * @brief Returns the index of the item corresponding the frame, 0164 * if there is an item with that frame 0165 * @param frame The frame whose index is needed. 0166 * @param framePerfect Whether query will return a valid scene 0167 * even when a scene contains the frame, not just if it starts 0168 * on said frame. Default == true 0169 * @return The index corresponding to frame, if exists. 0170 */ 0171 QModelIndex indexFromFrame(int frame, bool framePerfect = true) const; 0172 0173 /** 0174 * @brief Returns the index of the item with largest frame smaller 0175 * than argument frame 0176 * @param frame 0177 * @return The index with largest frame less than argument frame. 0178 */ 0179 QModelIndex lastIndexBeforeFrame(int frame) const; 0180 0181 /** 0182 * @brief Returns a list of index of items that have frame in between 0183 * argument range 0184 * @param range The range of frames 0185 * @return The list of index corresponding to the range. 0186 */ 0187 QModelIndexList affectedIndexes(KisTimeSpan range) const; 0188 0189 /** 0190 * @brief the next time at which there is a keyframe in any layer after @c keyframeTime 0191 * @param keyframeTime The time after which keyframe is wanted. 0192 * @return The time of the next keyframe in any layer. 0193 */ 0194 int nextKeyframeGlobal(int keyframeTime) const; 0195 0196 /** 0197 * @brief Gets the last keyframe that exists within an index's duration. 0198 * Used to prevent duration from overwriting keyframes that exist internal 0199 * to an existing scene. 0200 */ 0201 int lastKeyframeWithin(QModelIndex index); 0202 0203 /** 0204 * @brief reorders all keyframes to reflect storyboard docker's arrangement. 0205 * typically used after drag and drop to keep storyboard timing accurate 0206 * to timeline timing. 0207 */ 0208 void reorderKeyframes(); 0209 0210 /** 0211 * @brief moves all keyframes in all layers after the frame of the parent of @c durationIndex 0212 * Keyframes are moved to the left or right based on the difference (newDuration-oldDuration) 0213 * @param oldDuration The old duration in frames assigned to item 0214 * @param itemIndex The storyboard item index. 0215 * @return True if keyframes were moved, otherwise False 0216 */ 0217 bool changeSceneHoldLength(int oldDuration, QModelIndex itemIndex); 0218 0219 /** 0220 * @brief inserts item after or before @c index based on @c after parameter 0221 * @param index The index at which right click was clicked or the plus button belonged to. 0222 * @param after If True item is added after index, otherwise before 0223 * @return True if item was inserted, otherwise false 0224 */ 0225 bool insertItem(QModelIndex index, bool after); 0226 0227 /** 0228 * @brief removes item, deletes keyframes within and shifts keyframe after 0229 * the scene to fill in the gap 0230 * @param index The index of the item to be removed 0231 * @return true if item was removed 0232 */ 0233 bool removeItem(QModelIndex index, KUndo2Command *command = nullptr); 0234 0235 0236 /** 0237 * @brief resets @c m_items to @c list 0238 * @param list The new list of StoryboardItem* 0239 */ 0240 void resetData(StoryboardItemList list); 0241 0242 /** 0243 * @return The list of StoryboardItem* stored in the model. 0244 */ 0245 StoryboardItemList getData(); 0246 0247 void pushUndoCommand(KUndo2Command *command); 0248 0249 void shiftKeyframes(KisTimeSpan affected, int offset, KUndo2Command *cmd = nullptr); 0250 0251 int lastKeyframeGlobal() const; 0252 void slotUpdateThumbnailsForItems(QModelIndexList indices); 0253 0254 /** 0255 * @brief must be called after a first level index is inserted. Adds child nodes to the 0256 * first level indices 0257 * @param position Index of the first level node. 0258 */ 0259 void insertChildRows(int position, KUndo2Command* cmd = nullptr); 0260 0261 /** 0262 * @brief Adds child nodes from the item provided 0263 * @param position Index of the first level node. 0264 */ 0265 void insertChildRows(int position, StoryboardItemSP item); 0266 0267 void visualizeScene(const QModelIndex& index, bool useUndo = true); 0268 0269 void createDuplicateKeyframes(const QModelIndex& index, KUndo2Command* cmd = nullptr); 0270 void createBlankKeyframes(const QModelIndex& index, KUndo2Command* cmd = nullptr); 0271 0272 private: 0273 // For now, board is structured as a tree, with each board element being the top level 0274 // and storyboard components (numbers, comments, etc.) being children/leaves. 0275 inline bool isValidBoard(const QModelIndex &index) const {return index.isValid() && !index.parent().isValid();} 0276 0277 bool moveRowsImpl(const QModelIndex &sourceParent, int sourceRow, int count, 0278 const QModelIndex &destinationParent, int destinationChild, KUndo2Command *parentCMD = nullptr); 0279 0280 private Q_SLOTS: 0281 /** 0282 * @brief called when currentUiTime changes 0283 * @sa KisImageAnimationInterface::sigUiTimeChanged(int) 0284 */ 0285 void slotCurrentFrameChanged(int frameId); 0286 void slotKeyframeAdded(const KisKeyframeChannel *channel, int time); 0287 void slotKeyframeRemoved(const KisKeyframeChannel *channel, int time); 0288 void slotNodeRemoved(KisNodeSP node); 0289 0290 void slotFramerateChanged(); 0291 0292 /** 0293 * @brief calls regeneration of @c frame in the background i.e. in another thread. 0294 * @param frame The frame to be regenerated. 0295 * @param delay Update thumbnail with delay if true 0296 */ 0297 void slotUpdateThumbnailForFrame(int frame, bool delay = true); 0298 0299 /** 0300 * @brief calls regeneration of the currentUiTime() and all frames in @c affectedIndexes(KisTimeSpan) 0301 */ 0302 void slotUpdateThumbnails(); 0303 0304 /** 0305 * @brief called @c KisStoryboardThumbnailRenderScheduler when frame render is complete 0306 * @param frame The frame whose regeneration was requested 0307 * @param dev The projection of the frame 0308 */ 0309 void slotFrameRenderCompleted(int frame, KisPaintDeviceSP dev); 0310 0311 /** 0312 * @brief called @c KisStoryboardThumbnailRenderScheduler when frame render is cancelled. 0313 */ 0314 void slotFrameRenderCancelled(int frame); 0315 0316 void slotCommentDataChanged(); 0317 void slotCommentRowInserted(const QModelIndex, int, int); 0318 void slotCommentRowRemoved(const QModelIndex, int, int); 0319 void slotCommentRowMoved(const QModelIndex &sourceParent, int sourceRow, int count, 0320 const QModelIndex &destinationParent, int destinationChild); 0321 0322 0323 public Q_SLOTS: 0324 void slotSetActiveNode(KisNodeSP); 0325 0326 Q_SIGNALS: 0327 /** 0328 * @brief This signal is emitted whenever m_items is changed. 0329 * it is used to keep the StoryboardItemList in KisDocument 0330 * in sync with m_items 0331 */ 0332 void sigStoryboardItemListChanged(); 0333 0334 private: 0335 friend class KisMoveStoryboardCommand; 0336 0337 StoryboardItemList m_items; 0338 QVector<StoryboardComment> m_commentList; 0339 StoryboardCommentModel *m_commentModel {0}; 0340 bool m_freezeKeyframePositions {false}; 0341 bool m_lockBoards {false}; 0342 bool m_reorderingKeyframes {false}; 0343 int m_lastScene {0}; 0344 KisIdleWatcher m_imageIdleWatcher; 0345 KisImageWSP m_image; 0346 StoryboardView *m_view {0}; 0347 KisNodeWSP m_activeNode; 0348 KisStoryboardThumbnailRenderScheduler *m_renderScheduler {0}; 0349 KisSignalCompressor m_renderSchedulingCompressor; 0350 }; 0351 0352 #endif