File indexing completed on 2024-12-22 04:40:05

0001 /*
0002     SPDX-FileCopyrightText: 2007-2009 Sergio Pistone <sergio_pistone@yahoo.com.ar>
0003     SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #ifndef SUBTITLE_H
0009 #define SUBTITLE_H
0010 
0011 #include "core/range.h"
0012 #include "core/rangelist.h"
0013 #include "core/time.h"
0014 #include "core/richstring.h"
0015 #include "core/subtitletarget.h"
0016 #include "core/undo/undostack.h"
0017 #include "helpers/objectref.h"
0018 #include "formatdata.h"
0019 
0020 #include <QObject>
0021 #include <QString>
0022 #include <QStringList>
0023 #include <QList>
0024 #include <QMap>
0025 
0026 QT_FORWARD_DECLARE_CLASS(QUndoCommand)
0027 QT_FORWARD_DECLARE_CLASS(QTextEdit)
0028 
0029 namespace SubtitleComposer {
0030 class RichDocument;
0031 class SubtitleLine;
0032 class UndoAction;
0033 class RichCSS;
0034 
0035 class Subtitle : public QObject, public QSharedData
0036 {
0037     Q_OBJECT
0038 
0039     friend class UndoStack;
0040 
0041     friend class SubtitleLine;
0042     friend class SubtitleIterator;
0043 
0044     friend class UndoAction;
0045 
0046     friend class SubtitleAction;
0047     friend class SetFramesPerSecondAction;
0048     friend class InsertLinesAction;
0049     friend class RemoveLinesAction;
0050     friend class MoveLineAction;
0051     friend class EditStylesheetAction;
0052 
0053     friend class SubtitleLineAction;
0054     friend class SetLinePrimaryTextAction;
0055     friend class SetLineSecondaryTextAction;
0056     friend class SetLineTextsAction;
0057     friend class SetLineShowTimeAction;
0058     friend class SetLineHideTimeAction;
0059     friend class SetLineTimesAction;
0060     friend class SetLineErrorsAction;
0061     friend class ToggleLineMarkedAction;
0062 
0063     friend class SubtitleCompositeActionExecutor;
0064 
0065     friend class Format;
0066     friend class InputFormat;
0067 
0068 public:
0069     static double defaultFramesPerSecond();
0070     static void setDefaultFramesPerSecond(double framesPerSecond);
0071 
0072     Subtitle(double framesPerSecond = defaultFramesPerSecond());
0073     virtual ~Subtitle();
0074 
0075 /// primary data includes primary text, timing information, format data and all errors except secondary only errors
0076     void setPrimaryData(const Subtitle &from, bool usePrimaryData);
0077     void clearPrimaryTextData();
0078 
0079 /// secondary data includes secondary text and secondary only errors
0080     void setSecondaryData(const Subtitle &from, bool usePrimaryData);
0081     void clearSecondaryTextData();
0082 
0083     inline bool isPrimaryDirty() const { return m_primaryDirtyState; }
0084     void clearPrimaryDirty();
0085 
0086     inline bool isSecondaryDirty() const { return m_secondaryDirtyState; }
0087     void clearSecondaryDirty();
0088 
0089     double framesPerSecond() const;
0090     void setFramesPerSecond(double framesPerSecond);
0091     void changeFramesPerSecond(double toFramesPerSecond, double fromFramesPerSecond = -1.0);
0092 
0093     bool isEmpty() const { return m_lines.empty(); }
0094     int linesCount() const { return m_lines.count(); }
0095     int lastIndex() const { return m_lines.count() - 1; }
0096 
0097     SubtitleLine * line(int index);
0098     const SubtitleLine * line(int index) const;
0099 
0100     inline SubtitleLine * firstLine() { return m_lines.isEmpty() ? nullptr : m_lines.first().obj(); }
0101     inline const SubtitleLine * firstLine() const { return m_lines.isEmpty() ? nullptr : m_lines.first().obj(); }
0102 
0103     inline SubtitleLine * lastLine() { return m_lines.isEmpty() ? nullptr : m_lines.last().obj(); }
0104     inline const SubtitleLine * lastLine() const { return m_lines.isEmpty() ? nullptr : m_lines.last().obj(); }
0105 
0106     inline int count() const { return m_lines.size(); }
0107     inline const SubtitleLine * at(const int i) const { return m_lines.at(i).obj(); }
0108     inline SubtitleLine * at(const int i) { return m_lines.at(i).obj(); }
0109     inline const SubtitleLine * operator[](const int i) const { return m_lines.at(i).obj(); }
0110     inline SubtitleLine * operator[](const int i) { return m_lines.at(i).obj(); }
0111 
0112 //  inline const QVector<ObjectRef<SubtitleLine>> & allLines() const { return m_lines; }
0113 
0114 //  inline const QList<const SubtitleLine *> & anchoredLines() const { return m_anchoredLines; }
0115 
0116     bool hasAnchors() const;
0117     bool isLineAnchored(int index) const;
0118     bool isLineAnchored(const SubtitleLine *line) const;
0119     void toggleLineAnchor(int index);
0120     void toggleLineAnchor(const SubtitleLine *line);
0121     void removeAllAnchors();
0122 
0123     void insertLine(SubtitleLine *line);
0124     SubtitleLine * insertNewLine(int index, bool timeAfter, SubtitleTarget target);
0125     void removeLines(const RangeList &ranges, SubtitleTarget target);
0126 
0127     void swapTexts(const RangeList &ranges);
0128 
0129     void splitLines(const RangeList &ranges);
0130     void joinLines(const RangeList &ranges);
0131 
0132     void shiftAnchoredLine(SubtitleLine *anchoredLine, const Time &newShowTime);
0133 
0134     void shiftLines(const RangeList &ranges, long msecs);
0135     void adjustLines(const Range &range, long firstTime, long lastTime);
0136     void sortLines(const Range &range);
0137 
0138     void applyDurationLimits(const RangeList &ranges, const Time &minDuration, const Time &maxDuration, bool canOverlap);
0139     void setMaximumDurations(const RangeList &ranges);
0140     void setAutoDurations(const RangeList &ranges, int msecsPerChar, int msecsPerWord, int msecsPerLine, bool canOverlap, SubtitleTarget calculationTarget);
0141 
0142     void fixOverlappingLines(const RangeList &ranges, const Time &minInterval = 100);
0143 
0144     void fixPunctuation(const RangeList &ranges, bool spaces, bool quotes, bool englishI, bool ellipsis, SubtitleTarget target);
0145 
0146     void lowerCase(const RangeList &ranges, SubtitleTarget target);
0147     void upperCase(const RangeList &ranges, SubtitleTarget target);
0148     void titleCase(const RangeList &ranges, bool lowerFirst, SubtitleTarget target);
0149     void sentenceCase(const RangeList &ranges, bool lowerFirst, SubtitleTarget target);
0150 
0151     void breakLines(const RangeList &ranges, unsigned minLengthForLineBreak, SubtitleTarget target);
0152     void unbreakTexts(const RangeList &ranges, SubtitleTarget target);
0153     void simplifyTextWhiteSpace(const RangeList &ranges, SubtitleTarget target);
0154 
0155     void syncWithSubtitle(const Subtitle &refSubtitle);
0156     void appendSubtitle(const Subtitle &srcSubtitle, double shiftMsecsBeforeAppend);
0157     void splitSubtitle(Subtitle &dstSubtitle, const Time &splitTime, bool shiftSplitLines);
0158 
0159     void toggleStyleFlag(const RangeList &ranges, RichString::StyleFlag styleFlag);
0160     void changeTextColor(const RangeList &ranges, QRgb color);
0161 
0162     void setMarked(const RangeList &ranges, bool value);
0163     void toggleMarked(const RangeList &ranges);
0164 
0165     void clearErrors(const RangeList &ranges, int errorFlags);
0166     void checkErrors(const RangeList &ranges, int errorFlags);
0167     void recheckErrors(const RangeList &ranges);
0168 
0169     inline bool metaExists(const QByteArray &key) const { return m_metaData.contains(key); }
0170     inline int metaRemove(const QByteArray &key) { return m_metaData.remove(key); }
0171     inline const QString meta(const QByteArray &key) const { return m_metaData.value(key); }
0172     inline void meta(const QByteArray &key, const QString &value) { m_metaData.insert(key, value); }
0173 
0174     void stylesheetEdit(QTextEdit *textEdit);
0175     void stylesheetAppend(const QString &css);
0176     void stylesheetClear();
0177     inline const RichCSS *stylesheet() const { return m_stylesheet; }
0178 
0179 signals:
0180     void primaryChanged();
0181     void secondaryChanged();
0182 
0183     void primaryDirtyStateChanged(bool dirty);
0184     void secondaryDirtyStateChanged(bool dirty);
0185 
0186     void framesPerSecondChanged(double fps);
0187     void linesAboutToBeInserted(int firstIndex, int lastIndex);
0188     void linesInserted(int firstIndex, int lastIndex);
0189     void linesAboutToBeRemoved(int firstIndex, int lastIndex);
0190     void linesRemoved(int firstIndex, int lastIndex);
0191 
0192     void compositeActionStart();
0193     void compositeActionEnd();
0194 
0195     void lineAnchorChanged(const SubtitleLine *line, bool anchored);
0196 
0197 /// forwarded line signals
0198     void linePrimaryTextChanged(SubtitleLine *line);
0199     void lineSecondaryTextChanged(SubtitleLine *line);
0200     void lineShowTimeChanged(SubtitleLine *line);
0201     void lineHideTimeChanged(SubtitleLine *line);
0202     void lineErrorFlagsChanged(SubtitleLine *line);
0203     void lineMarkChanged(SubtitleLine *line);
0204 
0205 private:
0206     inline int insertIndex(const Time &showTime) const { return insertIndex(showTime, 0, m_lines.isEmpty() ? 0 : m_lines.count() - 1); }
0207     int insertIndex(const Time &showTime, int start, int end) const;
0208     void insertLine(SubtitleLine *line, int index);
0209 
0210     FormatData * formatData() const;
0211     void setFormatData(const FormatData *formatData);
0212 
0213     void beginCompositeAction(const QString &title) const;
0214     void endCompositeAction(UndoStack::DirtyMode dirtyOverride = UndoStack::Invalid) const;
0215     void processAction(UndoAction *action) const;
0216 
0217     bool isPrimaryDirty(int index) const;
0218     bool isSecondaryDirty(int index) const;
0219     void updateState();
0220 
0221     inline int normalizeRangeIndex(int index) const { return index >= m_lines.count() ? m_lines.count() - 1 : index; }
0222 
0223     inline SubtitleLine * takeAt(const int i) { SubtitleLine *s = m_lines.at(i).obj(); m_lines.remove(i); return s; }
0224 
0225     inline bool ignoreDocChanges(bool ignore) {
0226         bool r = m_ignoreDocChanges;
0227         m_ignoreDocChanges = ignore;
0228         return r;
0229     }
0230 
0231 private:
0232     bool m_primaryDirtyState;
0233     int m_primaryCleanIndex;
0234     bool m_secondaryDirtyState;
0235     int m_secondaryCleanIndex;
0236 
0237     bool m_ignoreDocChanges = false;
0238 
0239     double m_framesPerSecond;
0240     mutable QVector<ObjectRef<SubtitleLine>> m_lines;
0241     QList<const SubtitleLine *> m_anchoredLines;
0242 
0243     QMap<QByteArray, QString> m_metaData;
0244 
0245     RichCSS *m_stylesheet;
0246 
0247     FormatData *m_formatData;
0248 
0249     static double s_defaultFramesPerSecond;
0250 };
0251 
0252 class SubtitleCompositeActionExecutor
0253 {
0254 public:
0255     SubtitleCompositeActionExecutor(const Subtitle *subtitle, const QString &title);
0256     ~SubtitleCompositeActionExecutor();
0257 
0258 private:
0259     const Subtitle *m_subtitle;
0260 };
0261 }
0262 
0263 #include "subtitleline.h"
0264 
0265 #endif