File indexing completed on 2024-04-28 08:43:24

0001 /*
0002     SPDX-FileCopyrightText: 2020 Sashmita Raghav
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #pragma once
0007 
0008 #include "bin/bin.h"
0009 #include "definitions.h"
0010 #include "undohelper.hpp"
0011 #include "utils/gentime.h"
0012 
0013 #include <QAbstractListModel>
0014 #include <QReadWriteLock>
0015 
0016 #include <array>
0017 #include <map>
0018 #include <memory>
0019 #include <mlt++/Mlt.h>
0020 #include <mlt++/MltProperties.h>
0021 #include <unordered_set>
0022 
0023 class DocUndoStack;
0024 class SnapInterface;
0025 class AssetParameterModel;
0026 class TimelineItemModel;
0027 
0028 /** @class SubtitleModel
0029     @brief This class is the model for a list of subtitles.
0030 */
0031 class SubtitleModel : public QAbstractListModel
0032 {
0033     Q_OBJECT
0034 
0035 public:
0036     static const int RAZOR_MODE_DUPLICATE = 0;
0037     static const int RAZOR_MODE_AFTER_FIRST_LINE = 1;
0038 
0039     /** @brief Construct a subtitle list bound to the timeline */
0040     explicit SubtitleModel(std::shared_ptr<TimelineItemModel> timeline = nullptr,
0041                            const std::weak_ptr<SnapInterface> &snapModel = std::weak_ptr<SnapInterface>(), QObject *parent = nullptr);
0042 
0043     enum { SubtitleRole = Qt::UserRole + 1, StartPosRole, EndPosRole, StartFrameRole, EndFrameRole, IdRole, SelectedRole, GrabRole };
0044     /** @brief Function that parses through a subtitle file */
0045     bool addSubtitle(int id, GenTime start, GenTime end, const QString &str, bool temporary = false, bool updateFilter = true);
0046     bool addSubtitle(GenTime start, GenTime end, const QString &str, Fun &undo, Fun &redo, bool updateFilter = true);
0047     /** @brief Converts string of time to GenTime */
0048     GenTime stringtoTime(QString &str, const double factor = 1.);
0049     /** @brief Return model data item according to the role passed */
0050     QVariant data(const QModelIndex &index, int role) const override;
0051     QHash<int, QByteArray> roleNames() const override; // override the same function of QAbstractListModel
0052     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
0053 
0054     /** @brief Returns all subtitles in the model */
0055     QList<SubtitledTime> getAllSubtitles() const;
0056 
0057     /** @brief Get subtitle at position */
0058     SubtitledTime getSubtitle(GenTime startFrame) const;
0059     /** @brief Returns all subtitle ids in a range */
0060     std::unordered_set<int> getItemsInRange(int startFrame, int endFrame) const;
0061 
0062     /** @brief Registers a snap model to the subtitle model */
0063     void registerSnap(const std::weak_ptr<SnapInterface> &snapModel);
0064 
0065     /** @brief Edit subtitle end timing
0066         @param startPos is start timing position of subtitles
0067         @param oldPos is the old position of the end time
0068         @param pos defines the new position of the end time
0069     */
0070     void editEndPos(GenTime startPos, GenTime newEndPos, bool refreshModel = true);
0071     bool requestResize(int id, int size, bool right);
0072     bool requestResize(int id, int size, bool right, Fun &undo, Fun &redo, bool logUndo);
0073 
0074     /** @brief Edit subtitle text
0075         @param id the model id of the subtitle
0076         @param newSubtitleText is (new) subtitle text
0077     */
0078     bool editSubtitle(int id, const QString &newSubtitleText);
0079 
0080     /** @brief Remove subtitle at start position (pos) */
0081     bool removeSubtitle(int id, bool temporary = false, bool updateFilter = true);
0082 
0083     /** @brief Remove all subtitles from subtitle model */
0084     void removeAllSubtitles();
0085 
0086     /** @brief Update some properties in the view */
0087     void updateSub(int id, const QVector<int> &roles);
0088 
0089     /** @brief Move an existing subtitle
0090         @param subId is the subtitle's ID
0091         @param newPos is new start position of subtitle
0092     */
0093     bool moveSubtitle(int subId, GenTime newPos, bool updateModel, bool updateView);
0094     void requestSubtitleMove(int clipId, GenTime position);
0095 
0096     /** @brief Guess the text encoding of the file at the provided path
0097      * @param file The path to the text file
0098      * @return The name of the text encoding, as guessed by KEncodingProber, or
0099      * "" if an error occurred
0100     */
0101     static QByteArray guessFileEncoding(const QString &file, bool *confidence);
0102     /** @brief Function that imports a subtitle file */
0103     void importSubtitle(const QString &filePath, int offset = 0, bool externalImport = false, float startFramerate = 30.00, float targetFramerate = 30.00, const QByteArray &encoding = "UTF-8");
0104 
0105     /** @brief Exports the subtitle model to json */
0106     QString toJson();
0107     /** @brief Returns the path to sub file */
0108     const QString getUrl();
0109     /** @brief Get a subtitle Id from its start position*/
0110     int getIdForStartPos(GenTime startTime) const;
0111     /** @brief Get the duration of a subtitle by id*/
0112     int getSubtitlePlaytime(int id) const;
0113     int getSubtitleEnd(int id) const;
0114     /** @brief Mark the subtitle item as selected or not*/
0115     void setSelected(int id, bool select);
0116     bool isSelected(int id) const;
0117     /** @brief Cut a subtitle */
0118     bool cutSubtitle(int position);
0119     /** @brief Cut a subtitle, return the id of newly created subtitle */
0120     int cutSubtitle(int position, Fun &undo, Fun &redo);
0121     QString getText(int id) const;
0122     int getRowForId(int id) const;
0123     GenTime getStartPosForId(int id) const;
0124     int getPreviousSub(int id) const;
0125     int getNextSub(int id) const;
0126     /** @brief Copy subtitle file to a new path
0127      * @param path the new subtitle path
0128      * @param checkOverwrite if true, warn before overwriting an existing subtitle file
0129      * @param updateFilter if true, the subtitle filter will be updated to us the new name, useful when saving the project file */
0130     void copySubtitle(const QString &path, int ix, bool checkOverwrite, bool updateFilter = false);
0131     /** @brief Use the tmp work file for the subtitle filter after saving the project */
0132     void restoreTmpFile(int ix);
0133     int trackDuration() const;
0134     void switchDisabled();
0135     bool isDisabled() const;
0136     void switchLocked();
0137     bool isLocked() const;
0138     /** @brief Load some subtitle filter properties from file */
0139     void loadProperties(const QMap<QString, QString> &subProperties);
0140     /** @brief Add all subtitle items to snaps */
0141     void allSnaps(std::vector<int> &snaps);
0142     /** @brief Returns an xml representation of the subtitle with id \@sid */
0143     QDomElement toXml(int sid, QDomDocument &document);
0144     /** @brief Returns the position of the first blank frame before a position */
0145     int getBlankStart(int pos) const;
0146     /** @brief Returns the position of the first subtitle after the blank at @position */
0147     int getBlankEnd(int pos) const;
0148     /** @brief Returns the duration of the blank at @position */
0149     int getBlankSizeAtPos(int frame) const;
0150     /** @brief If pos is blank, returns the position of the blank start. Otherwise returns the position of the next blank frame */
0151     int getNextBlankStart(int pos) const;
0152     /** @brief Returns true is track is empty at pos */
0153     bool isBlankAt(int pos) const;
0154     /** @brief Switch a subtitle's grab state */
0155     void switchGrab(int sid);
0156     /** @brief Ungrab all items */
0157     void clearGrab();
0158     /** @brief Release timeline model pointer */
0159     void unsetModel();
0160     /** @brief Get in/out of a subtitle item */
0161     QPair<int, int> getInOut(int sid) const;
0162     /** @brief Set subtitle style (font, color, etc) */
0163     void setStyle(const QString &style);
0164     const QString getStyle() const;
0165     void subtitleFileFromZone(int in, int out, const QString &outFile);
0166     /** @brief Edit the subtitle text*/
0167     Q_INVOKABLE void editSubtitle(int id, const QString &newText, const QString &oldText);
0168     /** @brief Edit the subtitle end */
0169     Q_INVOKABLE void resizeSubtitle(int startFrame, int endFrame, int oldEndFrame, bool refreshModel);
0170     /** @brief Add subtitle clip at cursor's position in timeline */
0171     Q_INVOKABLE void addSubtitle(int startframe = -1, QString text = QString());
0172     /** @brief Delete subtitle clip with frame as start position*/
0173     Q_INVOKABLE void deleteSubtitle(int frameframe, int endframe, const QString &text);
0174     /** @brief Cut a subtitle and split the text at \@param pos */
0175     void doCutSubtitle(int id, int cursorPos);
0176     /** @brief Return a list of the existing subtitles in this model */
0177     QMap<std::pair<int, QString>, QString> getSubtitlesList() const;
0178     /** @brief Load the current subtitles list in this model */
0179     void setSubtitlesList(QMap<std::pair<int, QString>, QString> list);
0180     /** @brief Update a subtitle name */
0181     void updateModelName(int ix, const QString &name);
0182     /** @brief Create a new subtitle track and return its subtitle index */
0183     int createNewSubtitle(const QString subtitleName = QString(), int id = -1);
0184     bool deleteSubtitle(int ix);
0185     void activateSubtitle(int ix);
0186     /** @brief Get JSON data for all subtitles in this model */
0187     const QString subtitlesFilesToJson();
0188 
0189 public Q_SLOTS:
0190     /** @brief Function that parses through a subtitle file */
0191     void parseSubtitle(const QString &workPath);
0192 
0193     /** @brief Import model to a temporary subtitle file to which the Subtitle effect is applied*/
0194     void jsontoSubtitle(const QString &data);
0195     /** @brief Update a subtitle text*/
0196     bool setText(int id, const QString &text);
0197 
0198 private:
0199     std::shared_ptr<TimelineItemModel> m_timeline;
0200     std::weak_ptr<DocUndoStack> m_undoStack;
0201     /** @brief A list of subtitles as: start time, text, end time */
0202     std::map<GenTime, std::pair<QString, GenTime>> m_subtitleList;
0203     /** @brief A list of all available subtitle files for this timeline
0204      *  in the form: ({id, name}, path) where id for a subtitle never changes
0205      */
0206     QMap<std::pair<int, QString>, QString> m_subtitlesList;
0207     QString scriptInfoSection, styleSection, eventSection;
0208     QString styleName;
0209 
0210     // To get subtitle file from effects parameter:
0211     // std::unique_ptr<Mlt::Properties> m_asset;
0212     // std::shared_ptr<AssetParameterModel> m_model;
0213 
0214     std::vector<std::weak_ptr<SnapInterface>> m_regSnaps;
0215     mutable QReadWriteLock m_lock;
0216     std::unique_ptr<Mlt::Filter> m_subtitleFilter;
0217     QVector<int> m_selected;
0218     QVector<int> m_grabbedIds;
0219     int saveSubtitleData(const QString &data, const QString &outFile);
0220 
0221 Q_SIGNALS:
0222     void modelChanged();
0223     void updateSubtitleStyle(const QString);
0224 
0225 protected:
0226     /** @brief Add time as snap in the registered snap model */
0227     void addSnapPoint(GenTime startpos);
0228     /** @brief Remove time as snap in the registered snap model */
0229     void removeSnapPoint(GenTime startpos);
0230     /** @brief Connect changes in model with signal */
0231     void setup();
0232 };
0233 Q_DECLARE_METATYPE(SubtitleModel *)