1 /*
2     SPDX-FileCopyrightText: 2007-2009 Sergio Pistone <sergio_pistone@yahoo.com.ar>
3     SPDX-FileCopyrightText: 2010-2018 Mladen Milinkovic <max@smoothware.net>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #ifndef SUBTITLE_H
9 #define SUBTITLE_H
10 
11 #include "core/range.h"
12 #include "core/rangelist.h"
13 #include "core/time.h"
14 #include "core/sstring.h"
15 #include "core/subtitletarget.h"
16 #include "core/undo/undostack.h"
17 #include "helpers/objectref.h"
18 #include "formatdata.h"
19 
20 #include <QObject>
21 #include <QString>
22 #include <QStringList>
23 #include <QList>
24 
QT_FORWARD_DECLARE_CLASS(QUndoCommand)25 QT_FORWARD_DECLARE_CLASS(QUndoCommand)
26 
27 namespace SubtitleComposer {
28 class RichDocument;
29 class SubtitleLine;
30 class UndoAction;
31 
32 class Subtitle : public QObject, public QSharedData
33 {
34 	Q_OBJECT
35 
36 	friend class UndoStack;
37 
38 	friend class SubtitleLine;
39 	friend class SubtitleIterator;
40 
41 	friend class UndoAction;
42 
43 	friend class SubtitleAction;
44 	friend class SetFramesPerSecondAction;
45 	friend class InsertLinesAction;
46 	friend class RemoveLinesAction;
47 	friend class MoveLineAction;
48 
49 	friend class SubtitleLineAction;
50 	friend class SetLinePrimaryTextAction;
51 	friend class SetLineSecondaryTextAction;
52 	friend class SetLineTextsAction;
53 	friend class SetLineShowTimeAction;
54 	friend class SetLineHideTimeAction;
55 	friend class SetLineTimesAction;
56 	friend class SetLineErrorsAction;
57 	friend class ToggleLineMarkedAction;
58 
59 	friend class SubtitleCompositeActionExecutor;
60 
61 	friend class Format;
62 	friend class InputFormat;
63 
64 public:
65 	static double defaultFramesPerSecond();
66 	static void setDefaultFramesPerSecond(double framesPerSecond);
67 
68 	Subtitle(double framesPerSecond = defaultFramesPerSecond());
69 	virtual ~Subtitle();
70 
71 /// primary data includes primary text, timing information, format data and all errors except secondary only errors
72 	void setPrimaryData(const Subtitle &from, bool usePrimaryData);
73 	void clearPrimaryTextData();
74 
75 /// secondary data includes secondary text and secondary only errors
76 	void setSecondaryData(const Subtitle &from, bool usePrimaryData);
77 	void clearSecondaryTextData();
78 
79 	inline bool isPrimaryDirty() const { return m_primaryDirtyState; }
80 	void clearPrimaryDirty();
81 
82 	inline bool isSecondaryDirty() const { return m_secondaryDirtyState; }
83 	void clearSecondaryDirty();
84 
85 	double framesPerSecond() const;
86 	void setFramesPerSecond(double framesPerSecond);
87 	void changeFramesPerSecond(double toFramesPerSecond, double fromFramesPerSecond = -1.0);
88 
89 	bool isEmpty() const { return m_lines.empty(); }
90 	int linesCount() const { return m_lines.count(); }
91 	int lastIndex() const { return m_lines.count() - 1; }
92 
93 	SubtitleLine * line(int index);
94 	const SubtitleLine * line(int index) const;
95 
96 	inline SubtitleLine * firstLine() { return m_lines.isEmpty() ? nullptr : m_lines.first().obj(); }
97 	inline const SubtitleLine * firstLine() const { return m_lines.isEmpty() ? nullptr : m_lines.first().obj(); }
98 
99 	inline SubtitleLine * lastLine() { return m_lines.isEmpty() ? nullptr : m_lines.last().obj(); }
100 	inline const SubtitleLine * lastLine() const { return m_lines.isEmpty() ? nullptr : m_lines.last().obj(); }
101 
102 	inline int count() const { return m_lines.size(); }
103 	inline const SubtitleLine * at(const int i) const { return m_lines.at(i).obj(); }
104 	inline SubtitleLine * at(const int i) { return m_lines.at(i).obj(); }
105 	inline const SubtitleLine * operator[](const int i) const { return m_lines.at(i).obj(); }
106 	inline SubtitleLine * operator[](const int i) { return m_lines.at(i).obj(); }
107 
108 //	inline const QVector<ObjectRef<SubtitleLine>> & allLines() const { return m_lines; }
109 
110 //	inline const QList<const SubtitleLine *> & anchoredLines() const { return m_anchoredLines; }
111 
112 	bool hasAnchors() const;
113 	bool isLineAnchored(int index) const;
114 	bool isLineAnchored(const SubtitleLine *line) const;
115 	void toggleLineAnchor(int index);
116 	void toggleLineAnchor(const SubtitleLine *line);
117 	void removeAllAnchors();
118 
119 	void insertLine(SubtitleLine *line);
120 	SubtitleLine * insertNewLine(int index, bool timeAfter, SubtitleTarget target);
121 	void removeLines(const RangeList &ranges, SubtitleTarget target);
122 
123 	void swapTexts(const RangeList &ranges);
124 
125 	void splitLines(const RangeList &ranges);
126 	void joinLines(const RangeList &ranges);
127 
128 	void shiftAnchoredLine(SubtitleLine *anchoredLine, const Time &newShowTime);
129 
130 	void shiftLines(const RangeList &ranges, long msecs);
131 	void adjustLines(const Range &range, long firstTime, long lastTime);
132 	void sortLines(const Range &range);
133 
134 	void applyDurationLimits(const RangeList &ranges, const Time &minDuration, const Time &maxDuration, bool canOverlap);
135 	void setMaximumDurations(const RangeList &ranges);
136 	void setAutoDurations(const RangeList &ranges, int msecsPerChar, int msecsPerWord, int msecsPerLine, bool canOverlap, SubtitleTarget calculationTarget);
137 
138 	void fixOverlappingLines(const RangeList &ranges, const Time &minInterval = 100);
139 
140 	void fixPunctuation(const RangeList &ranges, bool spaces, bool quotes, bool englishI, bool ellipsis, SubtitleTarget target);
141 
142 	void lowerCase(const RangeList &ranges, SubtitleTarget target);
143 	void upperCase(const RangeList &ranges, SubtitleTarget target);
144 	void titleCase(const RangeList &ranges, bool lowerFirst, SubtitleTarget target);
145 	void sentenceCase(const RangeList &ranges, bool lowerFirst, SubtitleTarget target);
146 
147 	void breakLines(const RangeList &ranges, unsigned minLengthForLineBreak, SubtitleTarget target);
148 	void unbreakTexts(const RangeList &ranges, SubtitleTarget target);
149 	void simplifyTextWhiteSpace(const RangeList &ranges, SubtitleTarget target);
150 
151 	void syncWithSubtitle(const Subtitle &refSubtitle);
152 	void appendSubtitle(const Subtitle &srcSubtitle, double shiftMsecsBeforeAppend);
153 	void splitSubtitle(Subtitle &dstSubtitle, const Time &splitTime, bool shiftSplitLines);
154 
155 	void toggleStyleFlag(const RangeList &ranges, SString::StyleFlag styleFlag);
156 	void changeTextColor(const RangeList &ranges, QRgb color);
157 
158 	void setMarked(const RangeList &ranges, bool value);
159 	void toggleMarked(const RangeList &ranges);
160 
161 	void clearErrors(const RangeList &ranges, int errorFlags);
162 	void checkErrors(const RangeList &ranges, int errorFlags);
163 	void recheckErrors(const RangeList &ranges);
164 
165 signals:
166 	void primaryChanged();
167 	void secondaryChanged();
168 
169 	void primaryDirtyStateChanged(bool dirty);
170 	void secondaryDirtyStateChanged(bool dirty);
171 
172 	void framesPerSecondChanged(double fps);
173 	void linesAboutToBeInserted(int firstIndex, int lastIndex);
174 	void linesInserted(int firstIndex, int lastIndex);
175 	void linesAboutToBeRemoved(int firstIndex, int lastIndex);
176 	void linesRemoved(int firstIndex, int lastIndex);
177 
178 	void compositeActionStart();
179 	void compositeActionEnd();
180 
181 	void lineAnchorChanged(const SubtitleLine *line, bool anchored);
182 
183 /// forwarded line signals
184 	void linePrimaryTextChanged(SubtitleLine *line);
185 	void lineSecondaryTextChanged(SubtitleLine *line);
186 	void lineShowTimeChanged(SubtitleLine *line);
187 	void lineHideTimeChanged(SubtitleLine *line);
188 	void lineErrorFlagsChanged(SubtitleLine *line);
189 	void lineMarkChanged(SubtitleLine *line);
190 
191 private:
192 	inline int insertIndex(const Time &showTime) const { return insertIndex(showTime, 0, m_lines.isEmpty() ? 0 : m_lines.count() - 1); }
193 	int insertIndex(const Time &showTime, int start, int end) const;
194 	void insertLine(SubtitleLine *line, int index);
195 
196 	FormatData * formatData() const;
197 	void setFormatData(const FormatData *formatData);
198 
199 	void beginCompositeAction(const QString &title) const;
200 	void endCompositeAction(UndoStack::DirtyMode dirtyOverride = UndoStack::Invalid) const;
201 	void processAction(UndoAction *action) const;
202 
203 	bool isPrimaryDirty(int index) const;
204 	bool isSecondaryDirty(int index) const;
205 	void updateState();
206 
207 	inline int normalizeRangeIndex(int index) const { return index >= m_lines.count() ? m_lines.count() - 1 : index; }
208 
209 	inline SubtitleLine * takeAt(const int i) { SubtitleLine *s = m_lines.at(i).obj(); m_lines.remove(i); return s; }
210 
211 private:
212 	bool m_primaryDirtyState;
213 	int m_primaryCleanIndex;
214 	bool m_secondaryDirtyState;
215 	int m_secondaryCleanIndex;
216 
217 	double m_framesPerSecond;
218 	mutable QVector<ObjectRef<SubtitleLine>> m_lines;
219 	QList<const SubtitleLine *> m_anchoredLines;
220 
221 	FormatData *m_formatData;
222 
223 	static double s_defaultFramesPerSecond;
224 };
225 
226 class SubtitleCompositeActionExecutor
227 {
228 public:
229 	SubtitleCompositeActionExecutor(const Subtitle *subtitle, const QString &title);
230 	~SubtitleCompositeActionExecutor();
231 
232 private:
233 	const Subtitle *m_subtitle;
234 };
235 }
236 
237 #include "subtitleline.h"
238 
239 #endif
240