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 SUBTITLELINE_H
9 #define SUBTITLELINE_H
10 
11 #include "core/time.h"
12 #include "core/formatdata.h"
13 #include "core/subtitletarget.h"
14 #include "helpers/objectref.h"
15 
16 #include <QExplicitlySharedDataPointer>
17 #include <QObject>
18 #include <QString>
19 
20 class QUndoCommand;
21 
22 namespace SubtitleComposer {
23 class Subtitle;
24 class SString;
25 class RichDocument;
26 class UndoAction;
27 
28 class SubtitleLine : public QObject
29 {
30 	Q_OBJECT
31 
32 	friend class Subtitle;
33 	friend class SubtitleAction;
34 	friend class SwapLinesTextsAction;
35 	friend class SubtitleLineAction;
36 	friend class SetLinePrimaryTextAction;
37 	friend class SetLineSecondaryTextAction;
38 	friend class SetLineTextsAction;
39 	friend class SetLineShowTimeAction;
40 	friend class SetLineHideTimeAction;
41 	friend class SetLineTimesAction;
42 	friend class SetLineStyleFlagsAction;
43 	friend class SetLineErrorsAction;
44 	friend class ToggleLineMarkedAction;
45 	friend class Format;
46 	friend class ObjectRef<SubtitleLine>;
47 
48 public:
49 	typedef enum {
50 		EmptyPrimaryTextID = 0,         // Empty primary text
51 		EmptySecondaryTextID,           // Empty secondary text
52 		MaxPrimaryCharsID,              // Too many characters in primary text
53 		MaxSecondaryCharsID,            // Too many characters in secondary text
54 		MaxPrimaryLinesID,              // Too many line breaks in primary text
55 		MaxSecondaryLinesID,            // Too many line breaks in secondary text
56 		PrimaryUnneededSpacesID,                // Unnecessary spaces in primary text
57 		SecondaryUnneededSpacesID,              // Unnecessary spaces in secondary text
58 		PrimaryUnneededDashID,          // Unnecessary dash (-) in primary text
59 		SecondaryUnneededDashID,                // Unnecessary dash (-) in secondary text
60 		PrimaryCapitalAfterEllipsisID,          // Capital letter after ellipsis in primary text
61 		SecondaryCapitalAfterEllipsisID,                // Capital letter after ellipsis in secondary text
62 		MinDurationPerPrimaryCharID,            // Too short duration per primary text character
63 		MinDurationPerSecondaryCharID,          // Too short duration per secondary text character
64 		MaxDurationPerPrimaryCharID,            // Too long duration per primary text character
65 		MaxDurationPerSecondaryCharID,          // Too long duration per secondary text character
66 		MinDurationID,                  // Too short duration
67 		MaxDurationID,                  // Too long duration
68 		OverlapsWithNextID,             // Overlaps with next line
69 		UntranslatedTextID,             // Primary and translation text are the same
70 		UserMarkID,
71 		ErrorSIZE,
72 		ErrorUNKNOWN
73 	} ErrorID;
74 
75 	typedef enum {
76 		EmptyPrimaryText = 0x1 << EmptyPrimaryTextID,
77 		EmptySecondaryText = 0x1 << EmptySecondaryTextID,
78 		MaxPrimaryChars = 0x1 << MaxPrimaryCharsID,
79 		MaxSecondaryChars = 0x1 << MaxSecondaryCharsID,
80 		MaxPrimaryLines = 0x1 << MaxPrimaryLinesID,
81 		MaxSecondaryLines = 0x1 << MaxSecondaryLinesID,
82 		PrimaryUnneededSpaces = 0x1 << PrimaryUnneededSpacesID,
83 		SecondaryUnneededSpaces = 0x1 << SecondaryUnneededSpacesID,
84 		PrimaryUnneededDash = 0x1 << PrimaryUnneededDashID,
85 		SecondaryUnneededDash = 0x1 << SecondaryUnneededDashID,
86 		PrimaryCapitalAfterEllipsis = 0x1 << PrimaryCapitalAfterEllipsisID,
87 		SecondaryCapitalAfterEllipsis = 0x1 << SecondaryCapitalAfterEllipsisID,
88 		MinDurationPerPrimaryChar = 0x1 << MinDurationPerPrimaryCharID,
89 		MinDurationPerSecondaryChar = 0x1 << MinDurationPerSecondaryCharID,
90 		MaxDurationPerPrimaryChar = 0x1 << MaxDurationPerPrimaryCharID,
91 		MaxDurationPerSecondaryChar = 0x1 << MaxDurationPerSecondaryCharID,
92 		MinDuration = 0x1 << MinDurationID,
93 		MaxDuration = 0x1 << MaxDurationID,
94 		OverlapsWithNext = 0x1 << OverlapsWithNextID,
95 		UntranslatedText = 0x1 << UntranslatedTextID,
96 		UserMark = 0x1 << UserMarkID,
97 
98 		PrimaryOnlyErrors = EmptyPrimaryText | MaxPrimaryChars | MaxPrimaryLines | PrimaryUnneededSpaces | PrimaryUnneededDash | PrimaryCapitalAfterEllipsis | MinDurationPerPrimaryChar | MaxDurationPerPrimaryChar,
99 
100 		SecondaryOnlyErrors = EmptySecondaryText | MaxSecondaryChars | MaxSecondaryLines | SecondaryUnneededSpaces | SecondaryUnneededDash | SecondaryCapitalAfterEllipsis | MinDurationPerSecondaryChar | MaxDurationPerSecondaryChar | UntranslatedText,
101 
102 		SharedErrors = MinDuration | MaxDuration | OverlapsWithNext | UserMark,
103 
104 		AllErrors = PrimaryOnlyErrors | SecondaryOnlyErrors | SharedErrors,
105 
106 		TimesErrors = MinDuration | MaxDuration | MinDurationPerPrimaryChar | MinDurationPerSecondaryChar | MaxDurationPerPrimaryChar | MaxDurationPerSecondaryChar | OverlapsWithNext,
107 
108 		TextErrors = EmptyPrimaryText | EmptySecondaryText | MaxPrimaryChars | MaxSecondaryChars | MaxPrimaryLines | MaxSecondaryLines | PrimaryUnneededSpaces | SecondaryUnneededSpaces | PrimaryUnneededDash | SecondaryUnneededDash | PrimaryCapitalAfterEllipsis | SecondaryCapitalAfterEllipsis | MinDurationPerPrimaryChar | MinDurationPerSecondaryChar | MaxDurationPerPrimaryChar | MaxDurationPerSecondaryChar | UntranslatedText
109 	} ErrorFlag;
110 
111 	static int bitsCount(unsigned int bitFlags);
112 
113 	static ErrorFlag errorFlag(ErrorID id);
114 	static ErrorID errorID(ErrorFlag flag);
115 
116 	static const QString & simpleErrorText(SubtitleLine::ErrorFlag errorFlag);
117 	static const QString & simpleErrorText(SubtitleLine::ErrorID errorID);
118 
119 	QString fullErrorText(SubtitleLine::ErrorFlag errorFlag) const;
120 	QString fullErrorText(SubtitleLine::ErrorID errorID) const;
121 
122 	SubtitleLine();
123 	SubtitleLine(const Time &showTime, const Time &hideTime);
124 	SubtitleLine(const SubtitleLine &line) = delete;
125 	SubtitleLine & operator=(const SubtitleLine &line) = delete;
126 	virtual ~SubtitleLine();
127 
128 	int number() const;
129 	int index() const;
130 
subtitle()131 	inline Subtitle * subtitle() { return m_subtitle.data(); }
subtitle()132 	inline const Subtitle * subtitle() const { return m_subtitle.constData(); }
133 
134 	inline SubtitleLine * prevLine() const;
135 	inline SubtitleLine * nextLine() const;
136 
primaryDoc()137 	inline RichDocument * primaryDoc() const { return m_primaryDoc; }
secondaryDoc()138 	inline RichDocument * secondaryDoc() const { return m_secondaryDoc; }
139 
140 	void breakText(int minBreakLength, SubtitleTarget target);
141 	void unbreakText(SubtitleTarget target);
142 	void simplifyTextWhiteSpace(SubtitleTarget target);
143 
showTime()144 	inline Time showTime() const { return m_showTime; }
145 	void setShowTime(const Time &showTime);
146 
hideTime()147 	inline Time hideTime() const { return m_hideTime; }
148 	void setHideTime(const Time &hideTime);
149 
duration()150 	inline double duration() const { return m_hideTime.toMillis() - m_showTime.toMillis(); }
durationTime()151 	inline Time durationTime() const { return Time(duration()); }
setDurationTime(const Time & durationTime)152 	inline void setDurationTime(const Time &durationTime) { setHideTime(m_showTime + durationTime); }
153 	QColor durationColor(const QColor &textColor, bool usePrimary=true);
154 
pauseTime()155 	inline Time pauseTime() const { const SubtitleLine *p = prevLine(); return Time(showTime().toMillis() - (p ? p->hideTime().toMillis() : 0.)); }
156 
157 	void setTimes(const Time &showTime, const Time &hideTime);
158 
containsTime(const Time & time)159 	inline bool containsTime(const Time &time) const { return m_showTime <= time && time <= m_hideTime; }
intersectsTimespan(const Time & start,const Time & end)160 	inline bool intersectsTimespan(const Time &start, const Time &end) const { return m_showTime <= end && start <= m_hideTime; }
161 
162 	int primaryCharacters() const;
163 	int primaryWords() const;
164 	int primaryLines() const;
165 
166 	int secondaryCharacters() const;
167 	int secondaryWords() const;
168 	int secondaryLines() const;
169 
170 	static Time autoDuration(const QString &text, int msecsPerChar, int msecsPerWord, int msecsPerLine);
171 	Time autoDuration(int msecsPerChar, int msecsPerWord, int msecsPerLine, SubtitleTarget calculationTarget);
172 
173 	void shiftTimes(long mseconds);
174 	void adjustTimes(double shiftMseconds, double scaleFactor);
175 
176 	int errorFlags() const;
177 	int errorCount() const;
178 	void setErrorFlags(int errorFlags);
179 	void setErrorFlags(int errorFlags, bool value);
180 
181 	bool checkEmptyPrimaryText(bool update = true);
182 	bool checkEmptySecondaryText(bool update = true);
183 	bool checkUntranslatedText(bool update = true);
184 	bool checkOverlapsWithNext(bool update = true);
185 
186 	bool checkMinDuration(int minMsecs, bool update = true);
187 	bool checkMaxDuration(int maxMsecs, bool update = true);
188 
189 	bool checkMinDurationPerPrimaryChar(int minMsecsPerChar, bool update = true);
190 	bool checkMinDurationPerSecondaryChar(int minMsecsPerChar, bool update = true);
191 	bool checkMaxDurationPerPrimaryChar(int maxMsecsPerChar, bool update = true);
192 	bool checkMaxDurationPerSecondaryChar(int maxMsecsPerChar, bool update = true);
193 
194 	bool checkMaxPrimaryChars(int maxCharacters, bool update = true);
195 	bool checkMaxSecondaryChars(int maxCharacters, bool update = true);
196 	bool checkMaxPrimaryLines(int maxLines, bool update = true);
197 	bool checkMaxSecondaryLines(int maxLines, bool update = true);
198 
199 	bool checkPrimaryUnneededSpaces(bool update = true);
200 	bool checkSecondaryUnneededSpaces(bool update = true);
201 	bool checkPrimaryCapitalAfterEllipsis(bool update = true);
202 	bool checkSecondaryCapitalAfterEllipsis(bool update = true);
203 	bool checkPrimaryUnneededDash(bool update = true);
204 	bool checkSecondaryUnneededDash(bool update = true);
205 
206 	int check(int errorFlagsToCheck, bool update = true);
207 
208 signals:
209 	void primaryTextChanged();
210 	void secondaryTextChanged();
211 	void showTimeChanged(const Time &showTime);
212 	void hideTimeChanged(const Time &hideTime);
213 	void errorFlagsChanged(int errorFlags);
214 
215 private:
216 	FormatData * formatData() const;
217 	void setFormatData(const FormatData *formatData);
218 
219 	void processAction(UndoAction *action);
220 	void processShowTimeSort(const Time &showTime);
221 
222 	void setPrimaryDoc(RichDocument *doc);
223 	void setSecondaryDoc(RichDocument *doc);
224 	void setTexts(RichDocument *pText, RichDocument *sText);
225 	void primaryDocumentChanged();
226 	void secondaryDocumentChanged();
227 
228 	void setupSignals();
229 
ignoreDocChanges(bool ignore)230 	inline bool ignoreDocChanges(bool ignore) {
231 		bool r = m_ignoreDocChanges;
232 		m_ignoreDocChanges = ignore;
233 		return r;
234 	}
235 
236 private:
237 	QExplicitlySharedDataPointer<Subtitle> m_subtitle;
238 	RichDocument *m_primaryDoc;
239 	RichDocument *m_secondaryDoc;
240 	Time m_showTime;
241 	Time m_hideTime;
242 	int m_errorFlags;
243 	bool m_ignoreDocChanges = false;
244 
245 	FormatData *m_formatData;
246 
247 	mutable ObjectRef<SubtitleLine> *m_ref = nullptr;
248 	const QVector<ObjectRef<SubtitleLine>> * refContainer();
249 };
250 }
251 
252 #include "core/subtitle.h"
253 
254 namespace SubtitleComposer {
255 
256 inline SubtitleLine *
prevLine()257 SubtitleLine::prevLine() const
258 {
259 	return m_subtitle ? m_subtitle->line(index() - 1) : nullptr;
260 }
261 
262 inline SubtitleLine *
nextLine()263 SubtitleLine::nextLine() const
264 {
265 	return m_subtitle ? m_subtitle->line(index() + 1) : nullptr;
266 }
267 
268 }
269 
270 #endif
271