File indexing completed on 2024-05-19 04:53:15

0001 /*
0002  * dvbepg.h
0003  *
0004  * Copyright (C) 2009-2011 Christoph Pfister <christophpfister@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License along
0017  * with this program; if not, write to the Free Software Foundation, Inc.,
0018  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0019  */
0020 
0021 #ifndef DVBEPG_H
0022 #define DVBEPG_H
0023 
0024 #include "dvbrecording.h"
0025 
0026 class AtscEpgFilter;
0027 class DvbDevice;
0028 class DvbEpgFilter;
0029 
0030 #define FIRST_LANG "first"
0031 
0032 class DvbEpgLangEntry
0033 {
0034 public:
0035     QString title;
0036     QString subheading;
0037     QString details;
0038 };
0039 
0040 class DvbEpgEntry : public SharedData
0041 {
0042 public:
0043     enum EitType {
0044         EitActualTsPresentFollowing = 0,
0045         EitOtherTsPresentFollowing = 1,
0046         EitActualTsSchedule = 2,
0047         EitOtherTsSchedule = 3,
0048 
0049         EitLast = 3
0050     };
0051     DvbEpgEntry(): type(EitActualTsSchedule) { }
0052     explicit DvbEpgEntry(const DvbSharedChannel &channel_) : channel(channel_) { }
0053     ~DvbEpgEntry() { }
0054 
0055     // checks that all variables are ok
0056     bool validate() const;
0057 
0058     DvbSharedChannel channel;
0059     EitType type;
0060     QDateTime begin; // UTC
0061     QTime duration;
0062     QString content;
0063     QString parental;
0064 
0065     // ISO 639-2 language-dependent entries
0066     QHash<QString, DvbEpgLangEntry> langEntry;
0067 
0068     DvbSharedRecording recording;
0069 
0070     QString title(QString lang = QString()) const {
0071         QString s;
0072 
0073         if (!lang.isEmpty()) {
0074             /*
0075              * Only return the user requested data
0076              * ISO-639-2 code if the title is filled.
0077              *
0078              * If it isn't, show first language
0079              */
0080             if (langEntry[lang].title.isEmpty())
0081                 lang = FIRST_LANG;
0082             else if (lang != FIRST_LANG)
0083                 return langEntry[lang].title;
0084         }
0085 
0086         QHashIterator<QString, DvbEpgLangEntry> i(langEntry);
0087         bool first = true;
0088 
0089         while (i.hasNext()) {
0090             i.next();
0091 
0092             QString code = i.key();
0093             DvbEpgLangEntry entry = i.value();
0094 
0095             if (!entry.title.isEmpty()) {
0096                 if (first)
0097                     first = false;
0098                 else
0099                     s += "/";
0100 
0101                 if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) {
0102                     s += code;
0103                     s += ": ";
0104                 }
0105                 s += entry.title;
0106             }
0107 
0108             if (lang == FIRST_LANG)
0109                 break;
0110         }
0111         return s;
0112     }
0113 
0114     QString subheading(QString lang = QString()) const {
0115         QString s;
0116 
0117         if (!lang.isEmpty()) {
0118             /*
0119              * Only return the user requested data
0120              * ISO-639-2 code if the subheading is filled.
0121              *
0122              * If it isn't, show first language
0123              */
0124             if (langEntry[lang].subheading.isEmpty())
0125                 lang = FIRST_LANG;
0126             else if (lang != FIRST_LANG)
0127                 return langEntry[lang].subheading;
0128         }
0129 
0130         QHashIterator<QString, DvbEpgLangEntry> i(langEntry);
0131         bool first = true;
0132 
0133         while (i.hasNext()) {
0134             i.next();
0135 
0136             QString code = i.key();
0137             DvbEpgLangEntry entry = i.value();
0138 
0139             if (!entry.subheading.isEmpty()) {
0140                 if (first)
0141                     first = false;
0142                 else
0143                     s += "/";
0144 
0145                 if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) {
0146                     s += code;
0147                     s += ": ";
0148                 }
0149                 s += entry.subheading;
0150             }
0151 
0152             if (lang == FIRST_LANG)
0153                 break;
0154         }
0155         return s;
0156     }
0157 
0158     QString details(QString lang = QString()) const {
0159         QString s;
0160 
0161         if (!lang.isEmpty()) {
0162             /*
0163              * Only return the user requested data
0164              * ISO-639-2 code if the details are filled.
0165              *
0166              * If it isn't, show first language
0167              */
0168             if (langEntry[lang].details.isEmpty())
0169                 lang = FIRST_LANG;
0170             else if (lang != FIRST_LANG)
0171                 return langEntry[lang].details;
0172         }
0173 
0174         QHashIterator<QString, DvbEpgLangEntry> i(langEntry);
0175         bool first = true;
0176 
0177         while (i.hasNext()) {
0178             i.next();
0179 
0180             QString code = i.key();
0181             DvbEpgLangEntry entry = i.value();
0182 
0183             if (!entry.details.isEmpty()) {
0184                 if (first)
0185                     first = false;
0186                 else
0187                     s += "\n\n";
0188 
0189                 if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) {
0190                     s += code;
0191                     s += ": ";
0192                 }
0193                 s += entry.details;
0194             }
0195 
0196             if (lang == FIRST_LANG)
0197                 break;
0198         }
0199         return s;
0200     }
0201 
0202     // Check only the user-visible elements
0203     bool operator==(const DvbEpgEntry &other) const
0204     {
0205         if (channel != other.channel)
0206             return false;
0207         if (begin != other.begin)
0208             return false;
0209         if (duration != other.duration)
0210             return false;
0211         if (content != other.content)
0212             return false;
0213 
0214         QHashIterator<QString, DvbEpgLangEntry> i(langEntry);
0215         while (i.hasNext()) {
0216             i.next();
0217 
0218             QString code = i.key();
0219 
0220             if (!other.langEntry.contains(code))
0221                 return false;
0222 
0223             DvbEpgLangEntry thisEntry = i.value();
0224             DvbEpgLangEntry otherEntry = other.langEntry[code];
0225 
0226             if (thisEntry.title != otherEntry.title)
0227                 return false;
0228             if (thisEntry.subheading != otherEntry.subheading)
0229                 return false;
0230             if (thisEntry.details != otherEntry.details)
0231                 return false;
0232 
0233             // If first language matches, assume entries are identical
0234             return true;
0235         }
0236 
0237         return true;
0238     }
0239 };
0240 
0241 typedef ExplicitlySharedDataPointer<const DvbEpgEntry> DvbSharedEpgEntry;
0242 Q_DECLARE_TYPEINFO(DvbSharedEpgEntry, Q_MOVABLE_TYPE);
0243 
0244 class DvbEpgEntryId
0245 {
0246 public:
0247     explicit DvbEpgEntryId(const DvbEpgEntry *entry_) : entry(entry_) { }
0248     explicit DvbEpgEntryId(const DvbSharedEpgEntry &entry_) : entry(entry_.constData()) { }
0249     ~DvbEpgEntryId() { }
0250 
0251     // compares entries, 'recording' is ignored
0252     // if one 'details' is empty, 'details' is ignored
0253 
0254     bool operator<(const DvbEpgEntryId &other) const;
0255 
0256 private:
0257     const DvbEpgEntry *entry;
0258 };
0259 
0260 class DvbEpgModel : public QObject
0261 {
0262     Q_OBJECT
0263     typedef QMap<DvbEpgEntryId, DvbSharedEpgEntry>::Iterator Iterator;
0264     typedef QMap<DvbEpgEntryId, DvbSharedEpgEntry>::ConstIterator ConstIterator;
0265 public:
0266     DvbEpgModel(DvbManager *manager_, QObject *parent);
0267     ~DvbEpgModel();
0268 
0269     QMap<DvbEpgEntryId, DvbSharedEpgEntry> getEntries() const;
0270     QMap<DvbSharedRecording, DvbSharedEpgEntry> getRecordings() const;
0271     void setRecordings(const QMap<DvbSharedRecording, DvbSharedEpgEntry> map);
0272     QHash<DvbSharedChannel, int> getEpgChannels() const;
0273     QList<DvbSharedEpgEntry> getCurrentNext(const DvbSharedChannel &channel) const;
0274 
0275     DvbSharedEpgEntry addEntry(const DvbEpgEntry &entry);
0276     void scheduleProgram(const DvbSharedEpgEntry &entry, int extraSecondsBefore,
0277         int extraSecondsAfter, bool checkForRecursion=false, int priority=10);
0278 
0279     void startEventFilter(DvbDevice *device, const DvbSharedChannel &channel);
0280     void stopEventFilter(DvbDevice *device, const DvbSharedChannel &channel);
0281 
0282 signals:
0283     void entryAdded(const DvbSharedEpgEntry &entry);
0284     // updating doesn't change the entry pointer (modifies existing content)
0285     void entryAboutToBeUpdated(const DvbSharedEpgEntry &entry);
0286     void entryUpdated(const DvbSharedEpgEntry &entry);
0287     void entryRemoved(const DvbSharedEpgEntry &entry);
0288     void epgChannelAdded(const DvbSharedChannel &channel);
0289     void epgChannelRemoved(const DvbSharedChannel &channel);
0290     void languageAdded(const QString lang);
0291 
0292 private slots:
0293     void channelAboutToBeUpdated(const DvbSharedChannel &channel);
0294     void channelUpdated(const DvbSharedChannel &channel);
0295     void channelRemoved(const DvbSharedChannel &channel);
0296     void recordingRemoved(const DvbSharedRecording &recording);
0297 
0298 private:
0299     void timerEvent(QTimerEvent *event) override;
0300     void Debug(QString text, const DvbSharedEpgEntry &entry);
0301 
0302     Iterator removeEntry(Iterator it);
0303 
0304     DvbManager *manager;
0305     QDateTime currentDateTimeUtc;
0306     QMap<DvbEpgEntryId, DvbSharedEpgEntry> entries;
0307     QMap<DvbSharedRecording, DvbSharedEpgEntry> recordings;
0308     QHash<DvbSharedChannel, int> epgChannels;
0309     QList<QExplicitlySharedDataPointer<DvbEpgFilter> > dvbEpgFilters;
0310     QList<QExplicitlySharedDataPointer<AtscEpgFilter> > atscEpgFilters;
0311     DvbChannel updatingChannel;
0312     bool hasPendingOperation;
0313 };
0314 
0315 #endif /* DVBEPG_H */