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