diff --git a/CONTRIBUTORS b/CONTRIBUTORS index e04e477b..5a5bc701 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -639,6 +639,7 @@ Teemu Rantanen for fixing faulty calculation of section length in eit.c for reporting a problem in calculation of channel ids for tv stations that use the undefined NID value 0 + for adding EPG preferred languages Jan Ekholm for adding/improving some Swedish language OSD texts diff --git a/HISTORY b/HISTORY index e2033377..5d4e291c 100644 --- a/HISTORY +++ b/HISTORY @@ -2555,3 +2555,5 @@ Video Disk Recorder Revision History - Changed calculation of channel ids to make it work for tv stations that use the undefined NID value 0 (thanks to Teemu Rantanen for reporting this one). - Enhanced the SDT filter to handle multi part sections. +- Added support for selecting preferred EPG languages (based upon a patch by + Teemu Rantanen). diff --git a/MANUAL b/MANUAL index 9b08914e..4584f161 100644 --- a/MANUAL +++ b/MANUAL @@ -482,6 +482,19 @@ Version 1.2 be taken. Note that in order to set the system time from the transponder data the option "Set system time" must also be enabled. + Preferred languages = 0 + Some tv stations broadcast their EPG data in various + different languages. This option allows you to define + which language(s) you prefer in such cases. By default, + or if none of the preferred languages is broadcast, any + language will be accepted and the EPG data will be + displayed in the first language received from the data + stream. If this option is set to a non-zero value, the + menu page will contain that many "Preferred language" + options which allow you to select the individual preferred + languages. If an actual EPG data record is received in + different languages, the preferred languages are checked + in the given order to decide which one to take. DVB: diff --git a/config.c b/config.c index 12172900..094d632a 100644 --- a/config.c +++ b/config.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.118 2004/01/05 11:45:40 kls Exp $ + * $Id: config.c 1.119 2004/01/06 17:09:54 kls Exp $ */ #include "config.h" @@ -259,6 +259,7 @@ cSetup::cSetup(void) TimeTransponder = 0; MarginStart = 2; MarginStop = 10; + EPGLanguages[0] = -1; EPGScanTimeout = 5; EPGBugfixLevel = 2; SVDRPTimeout = 300; @@ -395,6 +396,39 @@ bool cSetup::ParseCaCaps(const char *Value) return false; } +void cSetup::StoreLanguages(const char *Name, int *Values) +{ + char buffer[I18nNumLanguages * 4]; + char *q = buffer; + for (int i = 0; i < I18nNumLanguages; i++) { + if (Values[i] < 0) + break; + const char *s = I18nLanguageAbbreviation(Values[i]); + if (s) { + if (q > buffer) + *q++ = ' '; + strncpy(q, s, 3); + q += 3; + } + } + *q = 0; + Store(Name, buffer); +} + +bool cSetup::ParseLanguages(const char *Value, int *Values) +{ + int n = 0; + while (Value && *Value && n < I18nNumLanguages) { + int i = I18nLanguageIndex(Value); + if (i >= 0) + Values[n++] = i; + if ((Value = strchr(Value, ' ')) != NULL) + Value++; + } + Values[n] = -1; + return true; +} + bool cSetup::Parse(const char *Name, const char *Value) { if (!strcasecmp(Name, "OSDLanguage")) OSDLanguage = atoi(Value); @@ -412,6 +446,7 @@ bool cSetup::Parse(const char *Name, const char *Value) else if (!strcasecmp(Name, "TimeTransponder")) TimeTransponder = atoi(Value); else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value); else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value); + else if (!strcasecmp(Name, "EPGLanguages")) return ParseLanguages(Value, EPGLanguages); else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value); else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value); else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value); @@ -463,6 +498,7 @@ bool cSetup::Save(void) Store("TimeTransponder", TimeTransponder); Store("MarginStart", MarginStart); Store("MarginStop", MarginStop); + StoreLanguages("EPGLanguages", EPGLanguages); Store("EPGScanTimeout", EPGScanTimeout); Store("EPGBugfixLevel", EPGBugfixLevel); Store("SVDRPTimeout", SVDRPTimeout); diff --git a/config.h b/config.h index db4769cd..22623e73 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.181 2004/01/05 11:31:54 kls Exp $ + * $Id: config.h 1.182 2004/01/06 16:47:41 kls Exp $ */ #ifndef __CONFIG_H @@ -17,6 +17,7 @@ #include #include #include "device.h" +#include "i18n.h" #include "tools.h" #define VDRVERSION "1.3.1" @@ -195,6 +196,8 @@ class cSetup : public cConfig { private: void StoreCaCaps(const char *Name); bool ParseCaCaps(const char *Value); + void StoreLanguages(const char *Name, int *Values); + bool ParseLanguages(const char *Value, int *Values); bool Parse(const char *Name, const char *Value); cSetupLine *Get(const char *Name, const char *Plugin = NULL); void Store(const char *Name, const char *Value, const char *Plugin = NULL, bool AllowMultiple = false); @@ -216,6 +219,7 @@ public: int SetSystemTime; int TimeTransponder; int MarginStart, MarginStop; + int EPGLanguages[I18nNumLanguages + 1]; int EPGScanTimeout; int EPGBugfixLevel; int SVDRPTimeout; diff --git a/eit.c b/eit.c index 45a4d800..d71cad82 100644 --- a/eit.c +++ b/eit.c @@ -8,11 +8,12 @@ * Robert Schneider and Rolf Hakenes . * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg . * - * $Id: eit.c 1.84 2004/01/02 22:27:29 kls Exp $ + * $Id: eit.c 1.85 2004/01/09 15:44:43 kls Exp $ */ #include "eit.h" #include "epg.h" +#include "i18n.h" #include "libsi/section.h" #include "libsi/descriptor.h" @@ -88,19 +89,36 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) pEvent->SetTableID(Tid); pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-( + int LanguagePreferenceShort = -1; + int LanguagePreferenceExt = -1; + bool UseExtendedEventDescriptor = false; SI::Descriptor *d; - SI::ExtendedEventDescriptors exGroup; - char text[256]; + SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL; + SI::ShortEventDescriptor *ShortEventDescriptor = NULL; for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) { switch (d->getDescriptorTag()) { - case SI::ExtendedEventDescriptorTag: - exGroup.Add((SI::ExtendedEventDescriptor *)d); - d = NULL; //so that it is not deleted + case SI::ExtendedEventDescriptorTag: { + SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d; + if (I18nIsPreferredLanguage(Setup.EPGLanguages, I18nLanguageIndex(eed->languageCode), LanguagePreferenceExt) || !ExtendedEventDescriptors) { + delete ExtendedEventDescriptors; + ExtendedEventDescriptors = new SI::ExtendedEventDescriptors; + UseExtendedEventDescriptor = true; + } + if (UseExtendedEventDescriptor) { + ExtendedEventDescriptors->Add(eed); + d = NULL; // so that it is not deleted + } + if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber()) + UseExtendedEventDescriptor = false; + } break; case SI::ShortEventDescriptorTag: { SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d; - pEvent->SetTitle(sed->name.getText(text)); - pEvent->SetShortText(sed->text.getText(text)); + if (I18nIsPreferredLanguage(Setup.EPGLanguages, I18nLanguageIndex(sed->languageCode), LanguagePreferenceShort) || !ShortEventDescriptor) { + delete ShortEventDescriptor; + ShortEventDescriptor = sed; + d = NULL; // so that it is not deleted + } } break; case SI::ContentDescriptorTag: @@ -126,9 +144,18 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) } if (!rEvent) { - char buffer[exGroup.getMaximumTextLength()]; - pEvent->SetDescription(exGroup.getText(buffer)); + if (ShortEventDescriptor) { + char buffer[256]; + pEvent->SetTitle(ShortEventDescriptor->name.getText(buffer)); + pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer)); + } + if (ExtendedEventDescriptors) { + char buffer[ExtendedEventDescriptors->getMaximumTextLength()]; + pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer)); + } } + delete ExtendedEventDescriptors; + delete ShortEventDescriptor; pEvent->SetStartTime(SiEitEvent.getStartTime()); pEvent->SetDuration(SiEitEvent.getDuration()); diff --git a/epg.c b/epg.c index 69130145..19f1c7f4 100644 --- a/epg.c +++ b/epg.c @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $Id: epg.c 1.3 2004/01/04 15:20:42 kls Exp $ + * $Id: epg.c 1.4 2004/01/09 15:22:18 kls Exp $ */ #include "epg.h" @@ -486,6 +486,12 @@ bool cSchedule::SetFollowingEvent(cEvent *Event) return true; } +void cSchedule::ResetVersions(void) +{ + for (cEvent *p = events.First(); p; p = events.Next(p)) + p->SetVersion(0xFF); +} + void cSchedule::Cleanup(void) { Cleanup(time(NULL)); @@ -613,6 +619,16 @@ void cSchedules::Cleanup(bool Force) } } +void cSchedules::ResetVersions(void) +{ + cSchedulesLock SchedulesLock(true); + cSchedules *s = (cSchedules *)Schedules(SchedulesLock); + if (s) { + for (cSchedule *Schedule = s->First(); Schedule; Schedule = s->Next(Schedule)) + Schedule->ResetVersions(); + } +} + bool cSchedules::ClearAll(void) { cSchedulesLock SchedulesLock(true, 1000); diff --git a/epg.h b/epg.h index 19332a59..409968ac 100644 --- a/epg.h +++ b/epg.h @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $Id: epg.h 1.3 2004/01/03 17:00:25 kls Exp $ + * $Id: epg.h 1.4 2004/01/09 15:21:05 kls Exp $ */ #ifndef __EPG_H @@ -84,6 +84,7 @@ public: tChannelID ChannelID(void) const { return channelID; } bool SetPresentEvent(cEvent *Event); bool SetFollowingEvent(cEvent *Event); + void ResetVersions(void); void Cleanup(time_t Time); void Cleanup(void); cEvent *AddEvent(cEvent *Event); @@ -122,6 +123,7 @@ public: ///< time the returned cSchedules is accessed. Once the cSchedules is no ///< longer used, the cSchedulesLock must be destroyed. static void Cleanup(bool Force = false); + static void ResetVersions(void); static bool ClearAll(void); static bool Dump(FILE *f, const char *Prefix = ""); static bool Read(FILE *f = NULL); diff --git a/i18n.c b/i18n.c index 3b6c0bcf..a9009446 100644 --- a/i18n.c +++ b/i18n.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.138 2004/01/05 11:56:24 kls Exp $ + * $Id: i18n.c 1.139 2004/01/09 15:48:03 kls Exp $ * * Translations provided by: * @@ -111,6 +111,24 @@ const tI18nPhrase Phrases[] = { "iso8859-1", "iso8859-1", }, + // The 3-letter names of the language (this MUST be the third phrase!): + { "eng", + "deu,ger", + "slv", + "ita", + "dut,nla", + "por", + "fra,fre", + "nor", + "fin", + "pol", + "esl,spa", + "ell,gre", + "sve,swe", + "ron,rum", + "hun", + "cat,cln", + }, // Menu titles: { "VDR", "VDR", @@ -2227,6 +2245,40 @@ const tI18nPhrase Phrases[] = { "Idöhöz tartozó Transponder", "Usar el temps del múltiplex", }, + { "Setup.EPG$Preferred languages", + "Bevorzugte Sprachen", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "Suosikkikielet", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Setup.EPG$Preferred language", + "Bevorzugte Sprache", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "Suosikkikieli", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { "Setup.DVB$Primary DVB interface", "Primäres DVB Interface", "Primarna naprava", @@ -3920,3 +3972,41 @@ const char * const * I18nCharSets(void) { return &Phrases[1][0]; } + +const char * I18nLanguageAbbreviation(int Index) +{ + return Index < I18nNumLanguages ? Phrases[2][Index] : NULL; +} + +int I18nLanguageIndex(const char Code[3]) +{ + char s[4]; + memcpy(s, Code, 3); + s[3] = 0; + for (int i = 0; i < I18nNumLanguages; i++) { + if (strcasestr(Phrases[2][i], s)) + return i; + } + //dsyslog("unknown language code: '%s'", s); + return -1; +} + +bool I18nIsPreferredLanguage(int *PreferredLanguages, int LanguageIndex, int &OldPreference) +{ + for (int i = 0; i < I18nNumLanguages; i++) { + if (PreferredLanguages[i] < 0) + break; // the language is not a preferred one + if (PreferredLanguages[i] == LanguageIndex) { + if (OldPreference < 0 || i < OldPreference) { + OldPreference = i; + return true; + } + break; + } + } + if (OldPreference < 0) { + OldPreference = I18nNumLanguages; // higher than the maximum possible value + return true; // if we don't find a preferred one, we take the first one + } + return false; +} diff --git a/i18n.h b/i18n.h index 44cf1905..8c882140 100644 --- a/i18n.h +++ b/i18n.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.h 1.7 2003/10/19 15:02:05 kls Exp $ + * $Id: i18n.h 1.8 2004/01/06 15:56:53 kls Exp $ */ #ifndef __I18N_H @@ -22,6 +22,9 @@ const char *I18nTranslate(const char *s, const char *Plugin = NULL); const char * const * I18nLanguages(void); const char * const * I18nCharSets(void); +const char * I18nLanguageAbbreviation(int Index); +int I18nLanguageIndex(const char Code[3]); +bool I18nIsPreferredLanguage(int *PreferredLanguages, int LanguageIndex, int &OldPreference); #ifdef PLUGIN_NAME_I18N #define tr(s) I18nTranslate(s, PLUGIN_NAME_I18N) diff --git a/menu.c b/menu.c index 6e2c17be..275544f4 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.277 2004/01/05 11:51:33 kls Exp $ + * $Id: menu.c 1.278 2004/01/09 15:42:59 kls Exp $ */ #include "menu.h" @@ -2029,17 +2029,83 @@ eOSState cMenuSetupOSD::ProcessKey(eKeys Key) // --- cMenuSetupEPG --------------------------------------------------------- class cMenuSetupEPG : public cMenuSetupBase { +private: + int originalNumLanguages; + int numLanguages; + void Setup(void); public: cMenuSetupEPG(void); + virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupEPG::cMenuSetupEPG(void) { + for (numLanguages = 0; numLanguages < I18nNumLanguages && data.EPGLanguages[numLanguages] >= 0; numLanguages++) + ; + originalNumLanguages = numLanguages; SetSection(tr("EPG")); + Setup(); +} + +void cMenuSetupEPG::Setup(void) +{ + int current = Current(); + + Clear(); + Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan timeout (h)"), &data.EPGScanTimeout)); Add(new cMenuEditIntItem( tr("Setup.EPG$EPG bugfix level"), &data.EPGBugfixLevel, 0, MAXEPGBUGFIXLEVEL)); Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"), &data.SetSystemTime)); - Add(new cMenuEditTranItem(tr("Setup.EPG$Use time from transponder"), &data.TimeTransponder)); + if (data.SetSystemTime) + Add(new cMenuEditTranItem(tr("Setup.EPG$Use time from transponder"), &data.TimeTransponder)); + Add(new cMenuEditIntItem( tr("Setup.EPG$Preferred languages"), &numLanguages, 0, I18nNumLanguages)); + for (int i = 0; i < numLanguages; i++) + Add(new cMenuEditStraItem(tr("Setup.EPG$Preferred language"), &data.EPGLanguages[i], I18nNumLanguages, I18nLanguages())); + + SetCurrent(Get(current)); + Display(); +} + +eOSState cMenuSetupEPG::ProcessKey(eKeys Key) +{ + int oldnumLanguages = numLanguages; + int oldSetSystemTime = data.SetSystemTime; + + eOSState state = cMenuSetupBase::ProcessKey(Key); + if (Key == kOk) { + bool Modified = numLanguages != originalNumLanguages; + if (!Modified) { + for (int i = 0; i < numLanguages; i++) { + if (data.EPGLanguages[i] != ::Setup.EPGLanguages[i]) { + Modified = true; + break; + } + } + } + if (Modified) + cSchedules::ResetVersions(); + } + else if (Key != kNone) { + if (numLanguages != oldnumLanguages || data.SetSystemTime != oldSetSystemTime) { + for (int i = oldnumLanguages; i < numLanguages; i++) { + data.EPGLanguages[i] = 0; + for (int l = 0; l < I18nNumLanguages; l++) { + int k; + for (k = 0; k < oldnumLanguages; k++) { + if (data.EPGLanguages[k] == l) + break; + } + if (k >= oldnumLanguages) { + data.EPGLanguages[i] = l; + break; + } + } + } + data.EPGLanguages[numLanguages] = -1; + Setup(); + } + } + return state; } // --- cMenuSetupDVB ---------------------------------------------------------