1 /*
2  * dvbepg.h
3  *
4  * Copyright (C) 2009-2011 Christoph Pfister <christophpfister@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #ifndef DVBEPG_H
22 #define DVBEPG_H
23 
24 #include "dvbrecording.h"
25 
26 class AtscEpgFilter;
27 class DvbDevice;
28 class DvbEpgFilter;
29 
30 #define FIRST_LANG "first"
31 
32 class DvbEpgLangEntry
33 {
34 public:
35 	QString title;
36 	QString subheading;
37 	QString details;
38 };
39 
40 class DvbEpgEntry : public SharedData
41 {
42 public:
43 	enum EitType {
44 		EitActualTsPresentFollowing = 0,
45 		EitOtherTsPresentFollowing = 1,
46 		EitActualTsSchedule = 2,
47 		EitOtherTsSchedule = 3,
48 
49 		EitLast = 3
50 	};
DvbEpgEntry()51 	DvbEpgEntry(): type(EitActualTsSchedule) { }
DvbEpgEntry(const DvbSharedChannel & channel_)52 	explicit DvbEpgEntry(const DvbSharedChannel &channel_) : channel(channel_) { }
~DvbEpgEntry()53 	~DvbEpgEntry() { }
54 
55 	// checks that all variables are ok
56 	bool validate() const;
57 
58 	DvbSharedChannel channel;
59 	EitType type;
60 	QDateTime begin; // UTC
61 	QTime duration;
62 	QString content;
63 	QString parental;
64 
65 	// ISO 639-2 language-dependent entries
66 	QHash<QString, DvbEpgLangEntry> langEntry;
67 
68 	DvbSharedRecording recording;
69 
70 	QString title(QString lang = QString()) const {
71 		QString s;
72 
73 		if (!lang.isEmpty()) {
74 			/*
75 			 * Only return the user requested data
76 			 * ISO-639-2 code if the title is filled.
77 			 *
78 			 * If it isn't, show first language
79 			 */
80 			if (langEntry[lang].title.isEmpty())
81 				lang = FIRST_LANG;
82 			else if (lang != FIRST_LANG)
83 				return langEntry[lang].title;
84 		}
85 
86 		QHashIterator<QString, DvbEpgLangEntry> i(langEntry);
87 		bool first = true;
88 
89 		while (i.hasNext()) {
90 			i.next();
91 
92 			QString code = i.key();
93 			DvbEpgLangEntry entry = i.value();
94 
95 			if (!entry.title.isEmpty()) {
96 				if (first)
97 					first = false;
98 				else
99 					s += "/";
100 
101 				if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) {
102 					s += code;
103 					s += ": ";
104 				}
105 				s += entry.title;
106 			}
107 
108 			if (lang == FIRST_LANG)
109 				break;
110 		}
111 		return s;
112 	}
113 
114 	QString subheading(QString lang = QString()) const {
115 		QString s;
116 
117 		if (!lang.isEmpty()) {
118 			/*
119 			 * Only return the user requested data
120 			 * ISO-639-2 code if the subheading is filled.
121 			 *
122 			 * If it isn't, show first language
123 			 */
124 			if (langEntry[lang].subheading.isEmpty())
125 				lang = FIRST_LANG;
126 			else if (lang != FIRST_LANG)
127 				return langEntry[lang].subheading;
128 		}
129 
130 		QHashIterator<QString, DvbEpgLangEntry> i(langEntry);
131 		bool first = true;
132 
133 		while (i.hasNext()) {
134 			i.next();
135 
136 			QString code = i.key();
137 			DvbEpgLangEntry entry = i.value();
138 
139 			if (!entry.subheading.isEmpty()) {
140 				if (first)
141 					first = false;
142 				else
143 					s += "/";
144 
145 				if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) {
146 					s += code;
147 					s += ": ";
148 				}
149 				s += entry.subheading;
150 			}
151 
152 			if (lang == FIRST_LANG)
153 				break;
154 		}
155 		return s;
156 	}
157 
158 	QString details(QString lang = QString()) const {
159 		QString s;
160 
161 		if (!lang.isEmpty()) {
162 			/*
163 			 * Only return the user requested data
164 			 * ISO-639-2 code if the details are filled.
165 			 *
166 			 * If it isn't, show first language
167 			 */
168 			if (langEntry[lang].details.isEmpty())
169 				lang = FIRST_LANG;
170 			else if (lang != FIRST_LANG)
171 				return langEntry[lang].details;
172 		}
173 
174 		QHashIterator<QString, DvbEpgLangEntry> i(langEntry);
175 		bool first = true;
176 
177 		while (i.hasNext()) {
178 			i.next();
179 
180 			QString code = i.key();
181 			DvbEpgLangEntry entry = i.value();
182 
183 			if (!entry.details.isEmpty()) {
184 				if (first)
185 					first = false;
186 				else
187 					s += "\n\n";
188 
189 				if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) {
190 					s += code;
191 					s += ": ";
192 				}
193 				s += entry.details;
194 			}
195 
196 			if (lang == FIRST_LANG)
197 				break;
198 		}
199 		return s;
200 	}
201 
202 	// Check only the user-visible elements
203 	bool operator==(const DvbEpgEntry &other) const
204 	{
205 		if (channel != other.channel)
206 			return false;
207 		if (begin != other.begin)
208 			return false;
209 		if (duration != other.duration)
210 			return false;
211 		if (content != other.content)
212 			return false;
213 
214 		QHashIterator<QString, DvbEpgLangEntry> i(langEntry);
215 		while (i.hasNext()) {
216 			i.next();
217 
218 			QString code = i.key();
219 
220 			if (!other.langEntry.contains(code))
221 				return false;
222 
223 			DvbEpgLangEntry thisEntry = i.value();
224 			DvbEpgLangEntry otherEntry = other.langEntry[code];
225 
226 			if (thisEntry.title != otherEntry.title)
227 				return false;
228 			if (thisEntry.subheading != otherEntry.subheading)
229 				return false;
230 			if (thisEntry.details != otherEntry.details)
231 				return false;
232 
233 			// If first language matches, assume entries are identical
234 			return true;
235 		}
236 
237 		return true;
238 	}
239 };
240 
241 typedef ExplicitlySharedDataPointer<const DvbEpgEntry> DvbSharedEpgEntry;
242 Q_DECLARE_TYPEINFO(DvbSharedEpgEntry, Q_MOVABLE_TYPE);
243 
244 class DvbEpgEntryId
245 {
246 public:
DvbEpgEntryId(const DvbEpgEntry * entry_)247 	explicit DvbEpgEntryId(const DvbEpgEntry *entry_) : entry(entry_) { }
DvbEpgEntryId(const DvbSharedEpgEntry & entry_)248 	explicit DvbEpgEntryId(const DvbSharedEpgEntry &entry_) : entry(entry_.constData()) { }
~DvbEpgEntryId()249 	~DvbEpgEntryId() { }
250 
251 	// compares entries, 'recording' is ignored
252 	// if one 'details' is empty, 'details' is ignored
253 
254 	bool operator<(const DvbEpgEntryId &other) const;
255 
256 private:
257 	const DvbEpgEntry *entry;
258 };
259 
260 class DvbEpgModel : public QObject
261 {
262 	Q_OBJECT
263 	typedef QMap<DvbEpgEntryId, DvbSharedEpgEntry>::Iterator Iterator;
264 	typedef QMap<DvbEpgEntryId, DvbSharedEpgEntry>::ConstIterator ConstIterator;
265 public:
266 	DvbEpgModel(DvbManager *manager_, QObject *parent);
267 	~DvbEpgModel();
268 
269 	QMap<DvbEpgEntryId, DvbSharedEpgEntry> getEntries() const;
270 	QMap<DvbSharedRecording, DvbSharedEpgEntry> getRecordings() const;
271 	void setRecordings(const QMap<DvbSharedRecording, DvbSharedEpgEntry> map);
272 	QHash<DvbSharedChannel, int> getEpgChannels() const;
273 	QList<DvbSharedEpgEntry> getCurrentNext(const DvbSharedChannel &channel) const;
274 
275 	DvbSharedEpgEntry addEntry(const DvbEpgEntry &entry);
276 	void scheduleProgram(const DvbSharedEpgEntry &entry, int extraSecondsBefore,
277 		int extraSecondsAfter, bool checkForRecursion=false, int priority=10);
278 
279 	void startEventFilter(DvbDevice *device, const DvbSharedChannel &channel);
280 	void stopEventFilter(DvbDevice *device, const DvbSharedChannel &channel);
281 
282 signals:
283 	void entryAdded(const DvbSharedEpgEntry &entry);
284 	// updating doesn't change the entry pointer (modifies existing content)
285 	void entryAboutToBeUpdated(const DvbSharedEpgEntry &entry);
286 	void entryUpdated(const DvbSharedEpgEntry &entry);
287 	void entryRemoved(const DvbSharedEpgEntry &entry);
288 	void epgChannelAdded(const DvbSharedChannel &channel);
289 	void epgChannelRemoved(const DvbSharedChannel &channel);
290 	void languageAdded(const QString lang);
291 
292 private slots:
293 	void channelAboutToBeUpdated(const DvbSharedChannel &channel);
294 	void channelUpdated(const DvbSharedChannel &channel);
295 	void channelRemoved(const DvbSharedChannel &channel);
296 	void recordingRemoved(const DvbSharedRecording &recording);
297 
298 private:
299 	void timerEvent(QTimerEvent *event);
300 	void Debug(QString text, const DvbSharedEpgEntry &entry);
301 
302 	Iterator removeEntry(Iterator it);
303 
304 	DvbManager *manager;
305 	QDateTime currentDateTimeUtc;
306 	QMap<DvbEpgEntryId, DvbSharedEpgEntry> entries;
307 	QMap<DvbSharedRecording, DvbSharedEpgEntry> recordings;
308 	QHash<DvbSharedChannel, int> epgChannels;
309 	QList<QExplicitlySharedDataPointer<DvbEpgFilter> > dvbEpgFilters;
310 	QList<QExplicitlySharedDataPointer<AtscEpgFilter> > atscEpgFilters;
311 	DvbChannel updatingChannel;
312 	bool hasPendingOperation;
313 };
314 
315 #endif /* DVBEPG_H */
316