summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkwacker <vdr@w-i-r.com>2010-04-11 13:17:33 +0200
committerkwacker <vdr@w-i-r.com>2010-04-11 13:17:33 +0200
commit9cd931834ecadbf5efefdf484abb966e9248ebbb (patch)
tree2678592d4252483a59c6d3c37b5d5925c875c387
parentaf740943fe298b8cc4391f171cc5a13f7863b56f (diff)
downloadx-vdr-9cd931834ecadbf5efefdf484abb966e9248ebbb.tar.gz
x-vdr-9cd931834ecadbf5efefdf484abb966e9248ebbb.tar.bz2
aktuellen Extensionpatch NG hinzugefügt
-rw-r--r--vdr/extensions/ExtP-NG-vdr-1.7.14-V2.diff14724
-rwxr-xr-xvdr/extensions/vdr-1.7.14_extensions.diff465
2 files changed, 14976 insertions, 213 deletions
diff --git a/vdr/extensions/ExtP-NG-vdr-1.7.14-V2.diff b/vdr/extensions/ExtP-NG-vdr-1.7.14-V2.diff
new file mode 100644
index 0000000..023d712
--- /dev/null
+++ b/vdr/extensions/ExtP-NG-vdr-1.7.14-V2.diff
@@ -0,0 +1,14724 @@
+diff -ruN vdr-1.7.14/Make.config.template vdr-1.7.14.ExtP_NG/Make.config.template
+--- vdr-1.7.14/Make.config.template 2010-02-06 15:50:03.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/Make.config.template 2010-04-10 15:45:10.488736701 +0200
+@@ -41,8 +41,185 @@
+ ## Define if you want vdr to not run as root
+ #VDR_USER = vdr
+
++### VDR-Extensions:
++# Uncomment the patches you need
++# SORTRECORDS needs LIEMIEXT enabled
++# you can only enable MENUORG or SETUP
++
++#ALTERNATECHANNEL = 1
++#ATSC = 1
++#CHANNELBIND = 1
++#CHANNELPROVIDE = 1
++#CUTTERLIMIT = 1
++#CUTTERQUEUE = 1
++#CUTTIME = 1
++#DDEPGENTRY = 1
++#DVLFRIENDLYNAMES = 1
++#DVLSCRIPTADDON = 1
++#DVLVIDPREFER = 1
++#GRAPHTFT = 1
++#HARDLINKCUTTER = 1
++#JUMPINGSECONDS = 1
++#JUMPPLAY = 1
++#LIEMIEXT = 1
++#LIRCSETTINGS = 1
++#LNBSHARE = 1
++#MAINMENUHOOKS = 1
++#MCLI = 1
++#MENUORG = 1
++#NOEPG = 1
++#PINPLUGIN = 1
++#PLUGINMISSING = 1
++#ROTOR = 1
++#SETUP = 1
++#SORTRECORDS = 1
++#STATUS_EXTENSION = 1
++#TIMERINFO = 1
++#TTXTSUBS = 1
++#VALIDINPUT = 1
++#VOLCTRL = 1
++#WAREAGLEICON = 1
++#YAEPG = 1
+ ### You don't need to touch the following:
+
+ ifdef DVBDIR
+ INCLUDES += -I$(DVBDIR)/include
+ endif
++
++ifdef ALTERNATECHANNEL
++DEFINES += -DUSE_ALTERNATECHANNEL
++endif
++
++ifdef ATSC
++DEFINES += -DUSE_ATSC
++endif
++
++ifdef CHANNELBIND
++DEFINES += -DUSE_CHANNELBIND
++endif
++
++ifdef CHANNELPROVIDE
++DEFINES += -DUSE_CHANNELPROVIDE
++endif
++
++ifdef CUTTERLIMIT
++DEFINES += -DUSE_CUTTERLIMIT
++endif
++
++ifdef CUTTERQUEUE
++DEFINES += -DUSE_CUTTERQUEUE
++endif
++
++ifdef CUTTIME
++DEFINES += -DUSE_CUTTIME
++endif
++
++ifdef DDEPGENTRY
++DEFINES += -DUSE_DDEPGENTRY
++endif
++
++ifdef DVLFRIENDLYNAMES
++DEFINES += -DUSE_DVLFRIENDLYNAMES
++endif
++
++ifdef DVLSCRIPTADDON
++DEFINES += -DUSE_DVLSCRIPTADDON
++endif
++
++ifdef DVLVIDPREFER
++DEFINES += -DUSE_DVLVIDPREFER
++endif
++
++ifdef GRAPHTFT
++DEFINES += -DUSE_GRAPHTFT
++endif
++
++ifdef HARDLINKCUTTER
++DEFINES += -DUSE_HARDLINKCUTTER
++endif
++
++ifdef JUMPINGSECONDS
++DEFINES += -DUSE_JUMPINGSECONDS
++endif
++
++ifdef JUMPPLAY
++DEFINES += -DUSE_JUMPPLAY
++endif
++
++ifdef LIEMIEXT
++DEFINES += -DUSE_LIEMIEXT
++endif
++
++ifdef LIRCSETTINGS
++DEFINES += -DUSE_LIRCSETTINGS
++endif
++
++ifdef LNBSHARE
++DEFINES += -DUSE_LNBSHARE
++endif
++
++ifdef MAINMENUHOOKS
++DEFINES += -DUSE_MAINMENUHOOKS
++endif
++
++ifdef MCLI
++DEFINES += -DUSE_MCLI
++endif
++
++ifdef MENUORG
++DEFINES += -DUSE_MENUORG
++else
++ifdef SETUP
++DEFINES += -DUSE_SETUP
++endif
++endif
++
++ifdef NOEPG
++DEFINES += -DUSE_NOEPG
++endif
++
++ifdef PINPLUGIN
++DEFINES += -DUSE_PINPLUGIN
++endif
++
++ifdef PLUGINMISSING
++DEFINES += -DUSE_PLUGINMISSING
++endif
++
++ifdef ROTOR
++DEFINES += -DUSE_ROTOR
++endif
++
++ifdef SORTRECORDS
++ifdef LIEMIEXT
++DEFINES += -DUSE_SORTRECORDS
++endif
++endif
++
++ifdef STATUS_EXTENSION
++DEFINES += -DUSE_STATUS_EXTENSION
++endif
++
++ifdef TIMERINFO
++DEFINES += -DUSE_TIMERINFO
++endif
++
++ifdef TTXTSUBS
++DEFINES += -DUSE_TTXTSUBS
++endif
++
++ifdef VALIDINPUT
++DEFINES += -DUSE_VALIDINPUT
++endif
++
++ifdef VOLCTRL
++DEFINES += -DUSE_VOLCTRL
++endif
++
++ifdef WAREAGLEICON
++DEFINES += -DUSE_WAREAGLEICON
++endif
++
++ifdef YAEPG
++DEFINES += -DUSE_YAEPG
++endif
+diff -ruN vdr-1.7.14/Makefile vdr-1.7.14.ExtP_NG/Makefile
+--- vdr-1.7.14/Makefile 2010-02-21 12:44:38.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/Makefile 2010-04-10 15:45:10.520736498 +0200
+@@ -44,6 +44,18 @@
+ skinclassic.o skins.o skinsttng.o sourceparams.o sources.o spu.o status.o svdrp.o themes.o thread.o\
+ timers.o tools.o transfer.o vdr.o videodir.o
+
++ifdef SETUP
++OBJS += tinystr.o tinyxml.o tinyxmlerror.o tinyxmlparser.o submenu.o
++endif
++
++ifdef TTXTSUBS
++OBJS += vdrttxtsubshooks.o
++endif
++
++ifdef WAREAGLEICON
++OBJS += iconpatch.o
++endif
++
+ ifndef NO_KBD
+ DEFINES += -DREMOTE_KBD
+ endif
+diff -ruN vdr-1.7.14/channels.c vdr-1.7.14.ExtP_NG/channels.c
+--- vdr-1.7.14/channels.c 2010-02-21 14:36:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/channels.c 2010-04-10 15:45:09.286739173 +0200
+@@ -12,6 +12,9 @@
+ #include "device.h"
+ #include "epg.h"
+ #include "timers.h"
++#ifdef USE_ALTERNATECHANNEL
++#include "tools.h"
++#endif /* ALTERNATECHANNEL */
+
+ // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
+ // format characters in order to allow any number of blanks after a numeric
+@@ -140,6 +143,18 @@
+ return tf;
+ }
+
++#ifdef USE_LNBSHARE
++char cChannel::Polarization(void) const
++{
++ if (IsSat()) {
++ const char *p = strpbrk(parameters, "HVLRhvlr"); // lowercase for backwards compatibility
++ if (p)
++ return *p;
++ }
++ return NULL;
++}
++#endif /* LNBSHARE */
++
+ bool cChannel::HasTimer(void) const
+ {
+ for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
+@@ -253,6 +268,14 @@
+ }
+ }
+
++#ifdef USE_ALTERNATECHANNEL
++void cChannel::SetAlternativeChannelID(const char *AlternativeChannelID)
++{
++ if (!isempty(AlternativeChannelID))
++ alternativeChannelID = tChannelID::FromString(AlternativeChannelID);
++}
++#endif /* ALTERNATECHANNEL */
++
+ #define STRDIFF 0x01
+ #define VALDIFF 0x02
+
+@@ -365,6 +388,29 @@
+ }
+ }
+
++#ifdef USE_TTXTSUBS
++void cChannel::SetTeletextSubtitlePages(tTeletextSubtitlePage pages[], int numberOfPages)
++{
++ int mod = CHANNELMOD_NONE;
++ if (totalTtxtSubtitlePages != numberOfPages)
++ mod |= CHANNELMOD_PIDS;
++ totalTtxtSubtitlePages = fixedTtxtSubtitlePages;
++ for (int i = 0; (i < numberOfPages) && (totalTtxtSubtitlePages < MAXTXTPAGES); i++) {
++ if (teletextSubtitlePages[totalTtxtSubtitlePages].ttxtMagazine != pages[i].ttxtMagazine ||
++ teletextSubtitlePages[totalTtxtSubtitlePages].ttxtPage != pages[i].ttxtPage ||
++ teletextSubtitlePages[totalTtxtSubtitlePages].ttxtType != pages[i].ttxtType ||
++ strcmp(teletextSubtitlePages[totalTtxtSubtitlePages].ttxtLanguage, pages[i].ttxtLanguage)) {
++ mod |= CHANNELMOD_PIDS;
++ teletextSubtitlePages[totalTtxtSubtitlePages] = pages[i];
++ }
++ totalTtxtSubtitlePages++;
++ }
++ modification |= mod;
++ Channels.SetModified();
++}
++
++#endif /* TTXTSUBS */
++
+ void cChannel::SetCaIds(const int *CaIds)
+ {
+ if (caids[0] && caids[0] <= CA_USER_MAX)
+@@ -493,11 +539,28 @@
+ q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs);
+ }
+ *q = 0;
++#ifdef USE_TTXTSUBS
++ const int TBufferSize = 5 + 1 + (MAXTXTPAGES * (3 + 1 + MAXLANGCODE1 + 1)) + 10; // '12345;150=deu,151=fin,...', +10: paranoia
++ char tpidbuf[TBufferSize];
++ q = tpidbuf;
++ q += snprintf(q, sizeof(tpidbuf), "%d", Channel->tpid);
++ if (Channel->fixedTtxtSubtitlePages > 0) {
++ q += snprintf(q, sizeof(tpidbuf) - (q - tpidbuf), ";");
++ for (int i = 0; i < Channel->fixedTtxtSubtitlePages; ++i) {
++ tTeletextSubtitlePage page = Channel->teletextSubtitlePages[i];
++ q += snprintf(q, sizeof(tpidbuf) - (q - tpidbuf), "%d=%s", page.PageNumber(), page.ttxtLanguage);
++ }
++ }
++#endif /* TTXTSUBS */
+ char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia
+ q = caidbuf;
+ q += IntArrayToString(q, Channel->caids, 16);
+ *q = 0;
++#ifdef USE_TTXTSUBS
++ buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%s:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, tpidbuf, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid);
++#else
+ buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid);
++#endif /* TTXTSUBS */
+ }
+ return buffer;
+ }
+@@ -531,8 +594,15 @@
+ char *parambuf = NULL;
+ char *vpidbuf = NULL;
+ char *apidbuf = NULL;
++#ifdef USE_TTXTSUBS
++ char *tpidbuf = NULL;
++#endif /* TTXTSUBS */
+ char *caidbuf = NULL;
++#ifdef USE_TTXTSUBS
++ int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%a[^:]:%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, &parambuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpidbuf, &caidbuf, &sid, &nid, &tid, &rid);
++#else
+ int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, &parambuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid);
++#endif /* TTXTSUBS */
+ if (fields >= 9) {
+ if (fields == 9) {
+ // allow reading of old format
+@@ -616,6 +686,39 @@
+ dpids[NumDpids] = 0;
+ }
+
++#ifdef USE_TTXTSUBS
++ if (tpidbuf) {
++ char *p;
++ fixedTtxtSubtitlePages = 0;
++ // 2001;150=deu,151=fin
++ if ((p = strchr(tpidbuf, ';')) != NULL) {
++ char *q, *strtok_next;
++ *p++ = 0;
++ while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
++ if (fixedTtxtSubtitlePages < MAXTXTPAGES) {
++ int page;
++ char *l = strchr(q, '=');
++ if (l)
++ *l++ = 0;
++ if (sscanf(q, "%d", &page) == 1) {
++ teletextSubtitlePages[fixedTtxtSubtitlePages] = tTeletextSubtitlePage(page);
++ if (l)
++ strn0cpy(teletextSubtitlePages[fixedTtxtSubtitlePages].ttxtLanguage, l, MAXLANGCODE1);
++ fixedTtxtSubtitlePages++;
++ }
++ else
++ esyslog("ERROR: invalid Teletext page!"); // no need to set ok to 'false'
++ }
++ else
++ esyslog("ERROR: too many Teletext pages!"); // no need to set ok to 'false'
++ p = NULL;
++ }
++ totalTtxtSubtitlePages = fixedTtxtSubtitlePages;
++ }
++ if (sscanf(tpidbuf, "%d", &tpid) != 1)
++ return false;
++ }
++#endif /* TTXTSUBS */
+ if (caidbuf) {
+ char *p = caidbuf;
+ char *q;
+@@ -652,6 +755,9 @@
+ free(sourcebuf);
+ free(vpidbuf);
+ free(apidbuf);
++#ifdef USE_TTXTSUBS
++ free(tpidbuf);
++#endif /* TTXTSUBS */
+ free(caidbuf);
+ free(namebuf);
+ if (!GetChannelID().Valid()) {
+@@ -725,6 +831,50 @@
+ return false;
+ }
+
++#ifdef USE_ALTERNATECHANNEL
++bool cChannels::LoadAlternativeChannels(const char *FileName)
++{
++ FILE *fp;
++ char *line;
++ cReadLine ReadLine;
++ cChannel *origChannel;
++ tChannelID channelID;
++ if ((fp = fopen(FileName,"r"))==NULL)
++ {
++ esyslog("Can't open Alternative Channels-File <%s>",FileName);
++ return false;
++ }
++ while ((line = ReadLine.Read(fp)) != NULL)
++ {
++ if (line[0] != '#')
++ {
++ line=strtok(line, ";");
++ if (line != NULL)
++ {
++ channelID = tChannelID::FromString(line);
++ if (channelID == tChannelID::InvalidID)
++ dsyslog("Skipping invalid channel ID <%s>",line);
++ else {
++ origChannel = Channels.GetByChannelID(channelID);
++ if (!origChannel)
++ dsyslog("Skipping unknown channel ID <%s>",line);
++ else {
++ line=strtok(NULL, ";");
++ channelID = tChannelID::FromString(line);
++ if (channelID == tChannelID::InvalidID || !Channels.GetByChannelID(channelID))
++ dsyslog("Skipping invalid/unknown alternative channel ID <%s>",line);
++ else
++ origChannel->SetAlternativeChannelID(line);
++ }
++ }
++ }
++ }
++ } while (line != NULL);
++ fclose(fp);
++ return true;
++}
++#endif /* ALTERNATECHANNEL */
++
+ void cChannels::HashChannel(cChannel *Channel)
+ {
+ channelsHashSid.Add(Channel, Channel->Sid());
+diff -ruN vdr-1.7.14/channels.h vdr-1.7.14.ExtP_NG/channels.h
+--- vdr-1.7.14/channels.h 2010-03-07 14:47:13.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/channels.h 2010-04-10 15:45:09.370737290 +0200
+@@ -35,6 +35,9 @@
+ #define MAXDPIDS 16 // dolby (AC3 + DTS)
+ #define MAXSPIDS 32 // subtitles
+ #define MAXCAIDS 8 // conditional access
++#ifdef USE_TTXTSUBS
++#define MAXTXTPAGES 8 // teletext pages
++#endif /* TTXTSUBS */
+
+ #define MAXLANGCODE1 4 // a 3 letter language code, zero terminated
+ #define MAXLANGCODE2 8 // up to two 3 letter language codes, separated by '+' and zero terminated
+@@ -71,6 +74,18 @@
+ static const tChannelID InvalidID;
+ };
+
++#ifdef USE_TTXTSUBS
++struct tTeletextSubtitlePage {
++ tTeletextSubtitlePage(void) { ttxtPage = ttxtMagazine = 0; ttxtType = 0x02; strcpy(ttxtLanguage, "und"); }
++ tTeletextSubtitlePage(int page) { ttxtMagazine = (page / 100) & 0x7; ttxtPage = (((page % 100) / 10) << 4) + (page % 10); ttxtType = 0x02; strcpy(ttxtLanguage, "und"); }
++ char ttxtLanguage[MAXLANGCODE1];
++ uchar ttxtPage;
++ uchar ttxtMagazine;
++ uchar ttxtType;
++ int PageNumber(void) const { return BCDCHARTOINT(ttxtMagazine) * 100 + BCDCHARTOINT(ttxtPage); }
++ };
++#endif /* TTXTSUBS */
++
+ class cChannel;
+
+ class cLinkChannel : public cListObject {
+@@ -96,6 +111,9 @@
+ char *shortName;
+ char *provider;
+ char *portalName;
++#ifdef USE_ALTERNATECHANNEL
++ tChannelID alternativeChannelID;
++#endif /* ALTERNATECHANNEL */
+ int __BeginData__;
+ int frequency; // MHz
+ int source;
+@@ -113,6 +131,11 @@
+ uint16_t compositionPageIds[MAXSPIDS];
+ uint16_t ancillaryPageIds[MAXSPIDS];
+ int tpid;
++#ifdef USE_TTXTSUBS
++ int fixedTtxtSubtitlePages;
++ int totalTtxtSubtitlePages;
++ tTeletextSubtitlePage teletextSubtitlePages[MAXTXTPAGES];
++#endif /* TTXTSUBS */
+ int caids[MAXCAIDS + 1]; // list is zero-terminated
+ int nid;
+ int tid;
+@@ -139,6 +162,9 @@
+ const char *ShortName(bool OrName = false) const { return (OrName && isempty(shortName)) ? name : shortName; }
+ const char *Provider(void) const { return provider; }
+ const char *PortalName(void) const { return portalName; }
++#ifdef USE_ALTERNATECHANNEL
++ const tChannelID AlternativeChannelID(void) const { return alternativeChannelID; }
++#endif /* ALTERNATECHANNEL */
+ int Frequency(void) const { return frequency; } ///< Returns the actual frequency, as given in 'channels.conf'
+ int Transponder(void) const; ///< Returns the transponder frequency in MHz, plus the polarization in case of sat
+ static int Transponder(int Frequency, char Polarization); ///< builds the transponder from the given Frequency and Polarization
+@@ -160,6 +186,10 @@
+ uint16_t CompositionPageId(int i) const { return (0 <= i && i < MAXSPIDS) ? compositionPageIds[i] : uint16_t(0); }
+ uint16_t AncillaryPageId(int i) const { return (0 <= i && i < MAXSPIDS) ? ancillaryPageIds[i] : uint16_t(0); }
+ int Tpid(void) const { return tpid; }
++#ifdef USE_TTXTSUBS
++ const tTeletextSubtitlePage *TeletextSubtitlePages() const { return teletextSubtitlePages; }
++ int TotalTeletextSubtitlePages() const { return totalTtxtSubtitlePages; }
++#endif /* TTXTSUBS */
+ const int *Caids(void) const { return caids; }
+ int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
+ int Nid(void) const { return nid; }
+@@ -170,6 +200,9 @@
+ void SetNumber(int Number) { number = Number; }
+ bool GroupSep(void) const { return groupSep; }
+ const char *Parameters(void) const { return parameters; }
++#ifdef USE_LNBSHARE
++ char Polarization(void) const;
++#endif /* LNBSHARE */
+ const cLinkChannels* LinkChannels(void) const { return linkChannels; }
+ const cChannel *RefChannel(void) const { return refChannel; }
+ bool IsAtsc(void) const { return cSource::IsAtsc(source); }
+@@ -185,7 +218,13 @@
+ void SetId(int Nid, int Tid, int Sid, int Rid = 0);
+ void SetName(const char *Name, const char *ShortName, const char *Provider);
+ void SetPortalName(const char *PortalName);
++#ifdef USE_ALTERNATECHANNEL
++ void SetAlternativeChannelID(const char *AlternativeChannelID);
++#endif /* ALTERNATECHANNEL */
+ void SetPids(int Vpid, int Ppid, int Vtype, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid);
++#ifdef USE_TTXTSUBS
++ void SetTeletextSubtitlePages(tTeletextSubtitlePage pages[], int numberOfPages);
++#endif /* TTXTSUBS */
+ void SetCaIds(const int *CaIds); // list must be zero-terminated
+ void SetCaDescriptors(int Level);
+ void SetLinkChannels(cLinkChannels *LinkChannels);
+@@ -203,6 +242,9 @@
+ public:
+ cChannels(void);
+ bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false);
++#ifdef USE_ALTERNATECHANNEL
++ bool LoadAlternativeChannels(const char *FileName);
++#endif /* ALTERNATECHANNEL */
+ void HashChannel(cChannel *Channel);
+ void UnhashChannel(cChannel *Channel);
+ int GetNextGroup(int Idx); // Get next channel group
+diff -ruN vdr-1.7.14/ci.c vdr-1.7.14.ExtP_NG/ci.c
+--- vdr-1.7.14/ci.c 2010-01-02 11:39:50.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/ci.c 2010-04-10 15:45:09.396741389 +0200
+@@ -1911,6 +1911,10 @@
+ AddPid(Channel->Sid(), *Apid, STREAM_TYPE_AUDIO);
+ for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
+ AddPid(Channel->Sid(), *Dpid, STREAM_TYPE_DOLBY);
++#ifdef USE_TTXTSUBS
++ if (Channel->Tpid() && Setup.SupportTeletext)
++ AddPid(Channel->Sid(), Channel->Tpid(), STREAM_TYPE_DOLBY);
++#endif /* TTXTSUBS */
+ }
+ }
+
+@@ -1932,6 +1936,11 @@
+ CaPmt.AddPid(*Apid, STREAM_TYPE_AUDIO);
+ for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
+ CaPmt.AddPid(*Dpid, STREAM_TYPE_DOLBY);
++#ifdef USE_TTXTSUBS
++ if (Channel->Tpid() && Setup.SupportTeletext) {
++ CaPmt.AddPid(Channel->Tpid(), STREAM_TYPE_DOLBY); // FIXME: STREAM_TYPE_DOLBY should probably be renamed STREAM_TYPE_PRIVATE
++ }
++#endif /* TTXTSUBS */
+ cas->SendPMT(&CaPmt);
+ cTimeMs Timeout(QUERY_REPLY_TIMEOUT);
+ do {
+diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
+--- vdr-1.7.14/config.c 2010-03-12 17:41:37.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/config.c 2010-04-10 15:45:09.424743485 +0200
+@@ -313,6 +313,12 @@
+ strcpy(OSDLanguage, ""); // default is taken from environment
+ strcpy(OSDSkin, "sttng");
+ strcpy(OSDTheme, "default");
++#ifdef USE_VALIDINPUT
++ ShowValidInput = 0;
++#endif /* VALIDINPUT */
++#ifdef USE_WAREAGLEICON
++ WarEagleIcons = 1;
++#endif /* WAREAGLEICON */
+ PrimaryDVB = 1;
+ ShowInfoOnChSwitch = 1;
+ TimeoutRequChInfo = 1;
+@@ -331,13 +337,27 @@
+ TimeTransponder = 0;
+ MarginStart = 2;
+ MarginStop = 10;
++#ifdef USE_JUMPINGSECONDS
++ JumpSeconds = 60;
++ JumpSecondsSlow = 10;
++ JumpSecondsRepeat = 300;
++#endif /* JUMPINGSECONDS */
+ AudioLanguages[0] = -1;
+ DisplaySubtitles = 0;
++#ifdef USE_TTXTSUBS
++ SupportTeletext = 0;
++#endif /* TTXTSUBS */
+ SubtitleLanguages[0] = -1;
+ SubtitleOffset = 0;
+ SubtitleFgTransparency = 0;
+ SubtitleBgTransparency = 0;
+ EPGLanguages[0] = -1;
++#ifdef USE_DDEPGENTRY
++ DoubleEpgTimeDelta = 15;
++ DoubleEpgAction = 0;
++ MixEpgAction = 0;
++ DisableVPS = 0;
++#endif /* DDEPGENTRY */
+ EPGScanTimeout = 5;
+ EPGBugfixLevel = 3;
+ EPGLinger = 0;
+@@ -358,6 +378,9 @@
+ VideoDisplayFormat = 1;
+ VideoFormat = 0;
+ UpdateChannels = 5;
++#ifdef USE_CHANNELBIND
++ ChannelBindingByRid = 0;
++#endif /* CHANNELBIND */
+ UseDolbyDigital = 1;
+ ChannelInfoPos = 0;
+ ChannelInfoTime = 5;
+@@ -383,6 +406,10 @@
+ FontSmlSize = 18;
+ FontFixSize = 20;
+ MaxVideoFileSize = MAXVIDEOFILESIZEDEFAULT;
++#ifdef USE_HARDLINKCUTTER
++ MaxRecordingSize = DEFAULTRECORDINGSIZE;
++ HardLinkCutter = 0;
++#endif /* HARDLINKCUTTER */
+ SplitEditedFiles = 0;
+ DelTimeshiftRec = 0;
+ MinEventTimeout = 30;
+@@ -391,18 +418,83 @@
+ MultiSpeedMode = 0;
+ ShowReplayMode = 0;
+ ResumeID = 0;
++#ifdef USE_JUMPPLAY
++ JumpPlay = 0;
++ PlayJump = 0;
++ PauseLastMark = 0;
++ ReloadMarks = 0;
++#endif /* JUMPPLAY */
+ CurrentChannel = -1;
+ CurrentVolume = MAXVOLUME;
+ CurrentDolby = 0;
++#ifdef USE_CHANNELPROVIDE
++ LocalChannelProvide = 1;
++#endif /* CHANNELPROVIDE */
+ InitialChannel = 0;
+ InitialVolume = -1;
++#ifdef USE_VOLCTRL
++ LRVolumeControl = 0;
++ LRChannelGroups = 1;
++ LRForwardRewind = 1;
++#endif /* VOLCTRL */
+ ChannelsWrap = 0;
+ EmergencyExit = 1;
++#ifdef USE_LIEMIEXT
++ ShowRecDate = 1;
++ ShowRecTime = 1;
++ ShowRecLength = 0;
++ ShowProgressBar = 0;
++ MenuCmdPosition = 0;
++#endif /* LIEMIEXT */
++#ifdef USE_LIRCSETTINGS
++ LircRepeatDelay = 350;
++ LircRepeatFreq = 100;
++ LircRepeatTimeout = 500;
++#endif /* LIRCSETTINGS */
++#ifdef USE_LNBSHARE
++ VerboseLNBlog = 0;
++ for (int i = 0; i < MAXDEVICES; i++) CardUsesLNBnr[i] = i + 1;
++#endif /* LNBSHARE */
++#ifdef USE_NOEPG
++ noEPGMode = 0;
++ noEPGList = strdup("");
++#endif /* NOEPG */
++#ifdef USE_SORTRECORDS
++ RecordingsSortMode = 0;
++ RecordingsSortDirsFirst = 0;
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ CutterAutoDelete = 0;
++#endif /* CUTTERQUEUE */
++#ifdef USE_DVLFRIENDLYFNAMES
++ UseFriendlyFNames = 0; // default = disabled
++#endif /* DVLFRIENDLYFNAMES */
++#ifdef USE_DVLVIDPREFER
++ UseVidPrefer = 0; // default = disabled
++ nVidPrefer = 1;
++ for (int zz = 1; zz < DVLVIDPREFER_MAX; zz++) {
++ VidPreferPrio[ zz ] = 50;
++ VidPreferSize[ zz ] = 100;
++ }
++ VidPreferSize[ 0 ] = 800;
++ VidPreferPrio[ 0 ] = 50;
++#endif /* DVLVIDPREFER */
++}
++
++#ifdef USE_NOEPG
++cSetup::~cSetup()
++{
++ free(noEPGList);
+ }
++#endif /* NOEPG */
+
+ cSetup& cSetup::operator= (const cSetup &s)
+ {
+ memcpy(&__BeginData__, &s.__BeginData__, (char *)&s.__EndData__ - (char *)&s.__BeginData__);
++#ifdef USE_NOEPG
++ free(noEPGList);
++ noEPGList = strdup(s.noEPGList);
++#endif /* NOEPG */
+ return *this;
+ }
+
+@@ -503,6 +595,12 @@
+ if (!strcasecmp(Name, "OSDLanguage")) { strn0cpy(OSDLanguage, Value, sizeof(OSDLanguage)); I18nSetLocale(OSDLanguage); }
+ else if (!strcasecmp(Name, "OSDSkin")) Utf8Strn0Cpy(OSDSkin, Value, MaxSkinName);
+ else if (!strcasecmp(Name, "OSDTheme")) Utf8Strn0Cpy(OSDTheme, Value, MaxThemeName);
++#ifdef USE_VALIDINPUT
++ else if (!strcasecmp(Name, "ShowValidInput")) ShowValidInput = atoi(Value);
++#endif /* VALIDINPUT */
++#ifdef USE_WAREAGLEICON
++ else if (!strcasecmp(Name, "WarEagleIcons")) WarEagleIcons = atoi(Value);
++#endif /* WAREAGLEICON */
+ else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value);
+ else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value);
+ else if (!strcasecmp(Name, "TimeoutRequChInfo")) TimeoutRequChInfo = atoi(Value);
+@@ -521,13 +619,27 @@
+ else if (!strcasecmp(Name, "TimeTransponder")) TimeTransponder = atoi(Value);
+ else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
+ else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
++#ifdef USE_JUMPINGSECONDS
++ else if (!strcasecmp(Name, "JumpSeconds")) JumpSeconds = atoi(Value);
++ else if (!strcasecmp(Name, "JumpSecondsSlow")) JumpSecondsSlow = atoi(Value);
++ else if (!strcasecmp(Name, "JumpSecondsRepeat")) JumpSecondsRepeat = atoi(Value);
++#endif /* JUMPINGSECONDS */
+ else if (!strcasecmp(Name, "AudioLanguages")) return ParseLanguages(Value, AudioLanguages);
+ else if (!strcasecmp(Name, "DisplaySubtitles")) DisplaySubtitles = atoi(Value);
++#ifdef USE_TTXTSUBS
++ else if (!strcasecmp(Name, "SupportTeletext")) SupportTeletext = atoi(Value);
++#endif /* TTXTSUBS */
+ else if (!strcasecmp(Name, "SubtitleLanguages")) return ParseLanguages(Value, SubtitleLanguages);
+ else if (!strcasecmp(Name, "SubtitleOffset")) SubtitleOffset = atoi(Value);
+ else if (!strcasecmp(Name, "SubtitleFgTransparency")) SubtitleFgTransparency = atoi(Value);
+ else if (!strcasecmp(Name, "SubtitleBgTransparency")) SubtitleBgTransparency = atoi(Value);
+ else if (!strcasecmp(Name, "EPGLanguages")) return ParseLanguages(Value, EPGLanguages);
++#ifdef USE_DDEPGENTRY
++ else if (!strcasecmp(Name, "DoubleEpgTimeDelta")) DoubleEpgTimeDelta = atoi(Value);
++ else if (!strcasecmp(Name, "DoubleEpgAction")) DoubleEpgAction = atoi(Value);
++ else if (!strcasecmp(Name, "MixEpgAction")) MixEpgAction = atoi(Value);
++ else if (!strcasecmp(Name, "DisableVPS")) DisableVPS = atoi(Value);
++#endif /* DDEPGENTRY */
+ else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
+ else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value);
+ else if (!strcasecmp(Name, "EPGLinger")) EPGLinger = atoi(Value);
+@@ -548,6 +660,9 @@
+ else if (!strcasecmp(Name, "VideoDisplayFormat")) VideoDisplayFormat = atoi(Value);
+ else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value);
+ else if (!strcasecmp(Name, "UpdateChannels")) UpdateChannels = atoi(Value);
++#ifdef USE_CHANNELBIND
++ else if (!strcasecmp(Name, "ChannelBindingByRid")) ChannelBindingByRid= atoi(Value);
++#endif /* CHANNELBIND */
+ else if (!strcasecmp(Name, "UseDolbyDigital")) UseDolbyDigital = atoi(Value);
+ else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value);
+ else if (!strcasecmp(Name, "ChannelInfoTime")) ChannelInfoTime = atoi(Value);
+@@ -573,6 +688,10 @@
+ else if (!strcasecmp(Name, "FontSmlSize")) FontSmlSize = atoi(Value);
+ else if (!strcasecmp(Name, "FontFixSize")) FontFixSize = atoi(Value);
+ else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value);
++#ifdef USE_HARDLINKCUTTER
++ else if (!strcasecmp(Name, "MaxRecordingSize")) MaxRecordingSize = atoi(Value);
++ else if (!strcasecmp(Name, "HardLinkCutter")) HardLinkCutter = atoi(Value);
++#endif /* HARDLINKCUTTER */
+ else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value);
+ else if (!strcasecmp(Name, "DelTimeshiftRec")) DelTimeshiftRec = atoi(Value);
+ else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value);
+@@ -581,15 +700,102 @@
+ else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value);
+ else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value);
+ else if (!strcasecmp(Name, "ResumeID")) ResumeID = atoi(Value);
++#ifdef USE_JUMPPLAY
++ else if (!strcasecmp(Name, "JumpPlay")) JumpPlay = atoi(Value);
++ else if (!strcasecmp(Name, "PlayJump")) PlayJump = atoi(Value);
++ else if (!strcasecmp(Name, "PauseLastMark")) PauseLastMark = atoi(Value);
++ else if (!strcasecmp(Name, "ReloadMarks")) ReloadMarks = atoi(Value);
++#endif /* JUMPPLAY */
+ else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
+ else if (!strcasecmp(Name, "CurrentDolby")) CurrentDolby = atoi(Value);
++#ifdef USE_CHANNELPROVIDE
++ else if (!strcasecmp(Name, "LocalChannelProvide")) LocalChannelProvide = atoi(Value);
++#endif /* CHANNELPROVIDE */
+ else if (!strcasecmp(Name, "InitialChannel")) InitialChannel = atoi(Value);
+ else if (!strcasecmp(Name, "InitialVolume")) InitialVolume = atoi(Value);
++#ifdef USE_VOLCTRL
++ else if (!strcasecmp(Name, "LRVolumeControl")) LRVolumeControl = atoi(Value);
++ else if (!strcasecmp(Name, "LRChannelGroups")) LRChannelGroups = atoi(Value);
++ else if (!strcasecmp(Name, "LRForwardRewind")) LRForwardRewind = atoi(Value);
++#endif /* VOLCTRL */
+ else if (!strcasecmp(Name, "ChannelsWrap")) ChannelsWrap = atoi(Value);
+ else if (!strcasecmp(Name, "EmergencyExit")) EmergencyExit = atoi(Value);
++#ifdef USE_LIEMIEXT
++ else if (!strcasecmp(Name, "ShowRecDate")) ShowRecDate = atoi(Value);
++ else if (!strcasecmp(Name, "ShowRecTime")) ShowRecTime = atoi(Value);
++ else if (!strcasecmp(Name, "ShowRecLength")) ShowRecLength = atoi(Value);
++ else if (!strcasecmp(Name, "ShowProgressBar")) ShowProgressBar = atoi(Value);
++ else if (!strcasecmp(Name, "MenuCmdPosition")) MenuCmdPosition = atoi(Value);
++#endif /* LIEMIEXT */
++#ifdef USE_LIRCSETTINGS
++ else if (!strcasecmp(Name, "LircRepeatDelay")) LircRepeatDelay = atoi(Value);
++ else if (!strcasecmp(Name, "LircRepeatFreq")) LircRepeatFreq = atoi(Value);
++ else if (!strcasecmp(Name, "LircRepeatTimeout")) LircRepeatTimeout = atoi(Value);
++#endif /* LIRCSETTINGS */
++#ifdef USE_NOEPG
++ else if (!strcasecmp(Name, "noEPGMode")) noEPGMode = atoi(Value);
++ else if (!strcasecmp(Name, "noEPGList")) {
++ free(noEPGList);
++ noEPGList = strdup(Value ? Value : "");
++ }
++#endif /* NOEPG */
++#ifdef USE_SORTRECORDS
++ else if (!strcasecmp(Name, "RecordingsSortMode")) RecordingsSortMode = atoi(Value);
++ else if (!strcasecmp(Name, "RecordingsSortDirsFirst")) RecordingsSortDirsFirst = atoi(Value);
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ else if (!strcasecmp(Name, "CutterAutoDelete")) CutterAutoDelete = atoi(Value);
++#endif /* CUTTERQUEUE */
++#ifdef USE_DVLFRIENDLYFNAMES
++ else if (strcasecmp(Name, "UseFriendlyFNames") == 0) UseFriendlyFNames = atoi(Value);
++#endif /* DVLFRIENDLYFNAMES */
++#ifdef USE_DVLVIDPREFER
++ else if (strcasecmp(Name, "UseVidPrefer") == 0) UseVidPrefer = atoi(Value);
++ else if (strcasecmp(Name, "nVidPrefer") == 0) nVidPrefer = atoi(Value);
++ else if (strstr(Name, "VidPrefer") == Name) {
++ char *x = (char *)&Name[ strlen(Name) - 1 ];
++ int vN;
++
++ if (isdigit(*x) != 0) {
++ while (isdigit(*x) != 0)
++ x--;
++ x++;
++ }
++
++ vN = atoi(x);
++ if (vN < DVLVIDPREFER_MAX) {
++ if (strstr(Name, "VidPreferPrio") == Name) {
++ VidPreferPrio[ vN ] = atoi(Value);
++ if (VidPreferPrio[ vN ] > 99)
++ VidPreferPrio[ vN ] = 99;
++ }
++ else if (strstr(Name, "VidPreferSize") == Name) {
++ VidPreferSize[ vN ] = atoi(Value);
++ }
++ else
++ return false;
++ }
++ }
++#endif /* DVLVIDPREFER */
+ else
++#ifdef USE_LNBSHARE
++ if (!strcasecmp(Name, "VerboseLNBlog")) VerboseLNBlog = atoi(Value);
++ else {
++ char tmp[20];
++ bool result = false;
++ for (int i = 1; i <= MAXDEVICES; i++) {
++ sprintf(tmp, "Card%dusesLNBnr", i);
++ if (!strcasecmp(Name, tmp)) {
++ CardUsesLNBnr[i - 1] = atoi(Value);
++ result = true;
++ }
++ }
++ return result;
++ }
++#else
+ return false;
++#endif /* LNBSHARE */
+ return true;
+ }
+
+@@ -598,6 +804,12 @@
+ Store("OSDLanguage", OSDLanguage);
+ Store("OSDSkin", OSDSkin);
+ Store("OSDTheme", OSDTheme);
++#ifdef USE_VALIDINPUT
++ Store("ShowValidInput", ShowValidInput);
++#endif /* VALIDINPUT */
++#ifdef USE_WAREAGLEICON
++ Store("WarEagleIcons", WarEagleIcons);
++#endif /* WAREAGLEICON */
+ Store("PrimaryDVB", PrimaryDVB);
+ Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch);
+ Store("TimeoutRequChInfo", TimeoutRequChInfo);
+@@ -616,13 +828,27 @@
+ Store("TimeTransponder", TimeTransponder);
+ Store("MarginStart", MarginStart);
+ Store("MarginStop", MarginStop);
++#ifdef USE_JUMPINGSECONDS
++ Store("JumpSeconds", JumpSeconds);
++ Store("JumpSecondsSlow", JumpSecondsSlow);
++ Store("JumpSecondsRepeat", JumpSecondsRepeat);
++#endif /* JUMPINGSECONDS */
+ StoreLanguages("AudioLanguages", AudioLanguages);
+ Store("DisplaySubtitles", DisplaySubtitles);
++#ifdef USE_TTXTSUBS
++ Store("SupportTeletext", SupportTeletext);
++#endif /* TTXTSUBS */
+ StoreLanguages("SubtitleLanguages", SubtitleLanguages);
+ Store("SubtitleOffset", SubtitleOffset);
+ Store("SubtitleFgTransparency", SubtitleFgTransparency);
+ Store("SubtitleBgTransparency", SubtitleBgTransparency);
+ StoreLanguages("EPGLanguages", EPGLanguages);
++#ifdef USE_DDEPGENTRY
++ Store("DoubleEpgTimeDelta", DoubleEpgTimeDelta);
++ Store("DoubleEpgAction", DoubleEpgAction);
++ Store("MixEpgAction", MixEpgAction);
++ Store("DisableVPS", DisableVPS);
++#endif /* DDEPGENTRY */
+ Store("EPGScanTimeout", EPGScanTimeout);
+ Store("EPGBugfixLevel", EPGBugfixLevel);
+ Store("EPGLinger", EPGLinger);
+@@ -643,6 +869,9 @@
+ Store("VideoDisplayFormat", VideoDisplayFormat);
+ Store("VideoFormat", VideoFormat);
+ Store("UpdateChannels", UpdateChannels);
++#ifdef USE_CHANNELBIND
++ Store("ChannelBindingByRid",ChannelBindingByRid);
++#endif /* CHANNELBIND */
+ Store("UseDolbyDigital", UseDolbyDigital);
+ Store("ChannelInfoPos", ChannelInfoPos);
+ Store("ChannelInfoTime", ChannelInfoTime);
+@@ -676,13 +905,75 @@
+ Store("MultiSpeedMode", MultiSpeedMode);
+ Store("ShowReplayMode", ShowReplayMode);
+ Store("ResumeID", ResumeID);
++#ifdef USE_JUMPPLAY
++ Store("JumpPlay", JumpPlay);
++ Store("PlayJump", PlayJump);
++ Store("PauseLastMark", PauseLastMark);
++ Store("ReloadMarks", ReloadMarks);
++#endif /* JUMPPLAY */
+ Store("CurrentChannel", CurrentChannel);
+ Store("CurrentVolume", CurrentVolume);
+ Store("CurrentDolby", CurrentDolby);
++#ifdef USE_CHANNELPROVIDE
++ Store("LocalChannelProvide",LocalChannelProvide);
++#endif /* CHANNELPROVIDE */
+ Store("InitialChannel", InitialChannel);
+ Store("InitialVolume", InitialVolume);
++#ifdef USE_VOLCTRL
++ Store("LRVolumeControl", LRVolumeControl);
++ Store("LRChannelGroups", LRChannelGroups);
++ Store("LRForwardRewind", LRForwardRewind);
++#endif /* VOLCTRL */
+ Store("ChannelsWrap", ChannelsWrap);
+ Store("EmergencyExit", EmergencyExit);
++#ifdef USE_LIEMIEXT
++ Store("ShowRecDate", ShowRecDate);
++ Store("ShowRecTime", ShowRecTime);
++ Store("ShowRecLength", ShowRecLength);
++ Store("ShowProgressBar", ShowProgressBar);
++ Store("MenuCmdPosition", MenuCmdPosition);
++#endif /* LIEMIEXT */
++#ifdef USE_LIRCSETTINGS
++ Store("LircRepeatDelay", LircRepeatDelay);
++ Store("LircRepeatFreq", LircRepeatFreq);
++ Store("LircRepeatTimeout", LircRepeatTimeout);
++#endif /* LIRCSETTINGS */
++#ifdef USE_LNBSHARE
++ Store("VerboseLNBlog", VerboseLNBlog);
++ char tmp[20];
++ if (cDevice::NumDevices() > 1) {
++ for (int i = 1; i <= cDevice::NumDevices(); i++) {
++ sprintf(tmp, "Card%dusesLNBnr", i);
++ Store(tmp, CardUsesLNBnr[i - 1]);
++ }
++ }
++#endif /* LNBSHARE */
++#ifdef USE_NOEPG
++ Store("noEPGMode", noEPGMode);
++ Store("noEPGList", noEPGList);
++#endif /* NOEPG */
++#ifdef USE_SORTRECORDS
++ Store("RecordingsSortMode", RecordingsSortMode);
++ Store("RecordingsSortDirsFirst", RecordingsSortDirsFirst);
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ Store("CutterAutoDelete", CutterAutoDelete);
++#endif /* CUTTERQUEUE */
++#ifdef USE_DVLFRIENDLYFNAMES
++ Store ("UseFriendlyFNames", UseFriendlyFNames);
++#endif /* DVLFRIENDLYFNAMES */
++#ifdef USE_DVLVIDPREFER
++ Store ("UseVidPrefer", UseVidPrefer);
++ Store ("nVidPrefer", nVidPrefer);
++
++ char vidBuf[32];
++ for (int zz = 0; zz < nVidPrefer; zz++) {
++ sprintf(vidBuf, "VidPreferPrio%d", zz);
++ Store (vidBuf, VidPreferPrio[zz]);
++ sprintf(vidBuf, "VidPreferSize%d", zz);
++ Store (vidBuf, VidPreferSize[zz]);
++ }
++#endif /* DVLVIDPREFER */
+
+ Sort();
+
+diff -ruN vdr-1.7.14/config.h vdr-1.7.14.ExtP_NG/config.h
+--- vdr-1.7.14/config.h 2010-03-12 17:02:53.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/config.h 2010-04-10 15:45:09.438738230 +0200
+@@ -30,15 +30,39 @@
+ #define APIVERSION "1.7.14"
+ #define APIVERSNUM 10714 // Version * 10000 + Major * 100 + Minor
+
++#ifdef USE_YAEPG
++#define YAEPGHDVERSNUM 1
++#endif /* YAEPG */
++
+ // When loading plugins, VDR searches them by their APIVERSION, which
+ // may be smaller than VDRVERSION in case there have been no changes to
+ // VDR header files since the last APIVERSION. This allows compiled
+ // plugins to work with newer versions of the core VDR as long as no
+ // VDR header files have changed.
+
++#ifdef USE_CHANNELBIND
++#define CHANNELBINDINGVERSNUM 2
++#endif /* CHANNELBIND */
++
++#ifdef USE_JUMPPLAY
++#define JUMPPLAYVERSNUM 100
++#endif /* JUMPPLAY */
++
++#ifdef USE_LIEMIEXT
++#define LIEMIKUUTIO 130
++#endif /* LIEMIEXT */
++
++#ifdef USE_MAINMENUHOOKS
++#define MAINMENUHOOKSVERSNUM 1.0
++#endif /* MAINMENUHOOKS */
++
+ #define MAXPRIORITY 99
+ #define MAXLIFETIME 99
+
++#ifdef USE_DVLVIDPREFER
++#define DVLVIDPREFER_MAX 12
++#endif /* DVLVIDPREFER */
++
+ #define MINOSDWIDTH 480
+ #define MAXOSDWIDTH 1920
+ #define MINOSDHEIGHT 324
+@@ -216,6 +240,12 @@
+ char OSDLanguage[I18N_MAX_LOCALE_LEN];
+ char OSDSkin[MaxSkinName];
+ char OSDTheme[MaxThemeName];
++#ifdef USE_VALIDINPUT
++ int ShowValidInput;
++#endif /* VALIDINPUT */
++#ifdef USE_WAREAGLEICON
++ int WarEagleIcons;
++#endif /* WAREAGLEICON */
+ int PrimaryDVB;
+ int ShowInfoOnChSwitch;
+ int TimeoutRequChInfo;
+@@ -233,12 +263,24 @@
+ int TimeSource;
+ int TimeTransponder;
+ int MarginStart, MarginStop;
++#ifdef USE_JUMPINGSECONDS
++ int JumpSeconds, JumpSecondsSlow, JumpSecondsRepeat;
++#endif /* JUMPINGSECONDS */
+ int AudioLanguages[I18N_MAX_LANGUAGES + 1];
+ int DisplaySubtitles;
++#ifdef USE_TTXTSUBS
++ int SupportTeletext;
++#endif /* TTXTSUBS */
+ int SubtitleLanguages[I18N_MAX_LANGUAGES + 1];
+ int SubtitleOffset;
+ int SubtitleFgTransparency, SubtitleBgTransparency;
+ int EPGLanguages[I18N_MAX_LANGUAGES + 1];
++#ifdef USE_DDEPGENTRY
++ int DoubleEpgTimeDelta;
++ int DoubleEpgAction;
++ int MixEpgAction;
++ int DisableVPS;
++#endif /* DDEPPGENTRY */
+ int EPGScanTimeout;
+ int EPGBugfixLevel;
+ int EPGLinger;
+@@ -257,6 +299,9 @@
+ int VideoDisplayFormat;
+ int VideoFormat;
+ int UpdateChannels;
++#ifdef USE_CHANNELBIND
++ int ChannelBindingByRid;
++#endif /* CHANNELBIND */
+ int UseDolbyDigital;
+ int ChannelInfoPos;
+ int ChannelInfoTime;
+@@ -276,6 +321,10 @@
+ int FontSmlSize;
+ int FontFixSize;
+ int MaxVideoFileSize;
++#ifdef USE_HARDLINKCUTTER
++ int MaxRecordingSize;
++ int HardLinkCutter;
++#endif /* HARDLINKCUTTER */
+ int SplitEditedFiles;
+ int DelTimeshiftRec;
+ int MinEventTimeout, MinUserInactivity;
+@@ -283,15 +332,70 @@
+ int MultiSpeedMode;
+ int ShowReplayMode;
+ int ResumeID;
++#ifdef USE_JUMPPLAY
++ int JumpPlay;
++ int PlayJump;
++ int PauseLastMark;
++ int ReloadMarks;
++#endif /* JUMPPLAY */
+ int CurrentChannel;
+ int CurrentVolume;
+ int CurrentDolby;
++#ifdef USE_CHANNELPROVIDE
++ int LocalChannelProvide;
++#endif /* CHANNELPROVIDE */
+ int InitialChannel;
+ int InitialVolume;
++#ifdef USE_VOLCTRL
++ int LRVolumeControl;
++ int LRChannelGroups;
++ int LRForwardRewind;
++#endif /* VOLCTRL */
+ int ChannelsWrap;
+ int EmergencyExit;
++#ifdef USE_LIRCSETTINGS
++ int LircRepeatDelay;
++ int LircRepeatFreq;
++ int LircRepeatTimeout;
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++ int ShowRecDate, ShowRecTime, ShowRecLength, ShowProgressBar, MenuCmdPosition;
++#endif /* LIEMIEXT */
++#ifdef USE_LNBSHARE
++ int VerboseLNBlog;
++ #define MAXDEVICES 16 // Since VDR 1.3.32 we can not #include "device.h" for MAXDEVICES anymore.
++ // With this workaround a warning will be shown during compilation if
++ // MAXDEVICES changes in device.h.
++ int CardUsesLNBnr[MAXDEVICES];
++#endif /* LNBShARE */
++#ifdef USE_NOEPG
++ int noEPGMode;
++#endif /* NOEPG */
++#ifdef USE_SORTRECORDS
++ int RecordingsSortMode;
++ int RecordingsSortDirsFirst;
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ int CutterAutoDelete;
++#endif /* CUTTERQUEUE */
++#ifdef USE_DVLFRIENDLYFNAMES
++ int UseFriendlyFNames;
++#endif /* DVLFRIENDLYFNAMES */
++#ifdef USE_DVLVIDPREFER
++ int UseVidPrefer; // 0 = VDR's default, 1 = use
++ int nVidPrefer;
++ int VidPreferPrio[DVLVIDPREFER_MAX];
++ int VidPreferSize[DVLVIDPREFER_MAX];
++#endif /* DVLVIDPREFER */
++
+ int __EndData__;
++#ifdef USE_NOEPG
++ char *noEPGList; // pointer not to be flat-copied
++#endif /* NOEPG */
+ cSetup(void);
++#ifdef USE_NOEPG
++ ~cSetup();
++#endif /* NOEPG */
+ cSetup& operator= (const cSetup &s);
+ bool Load(const char *FileName);
+ bool Save(void);
+diff -ruN vdr-1.7.14/cutter.c vdr-1.7.14.ExtP_NG/cutter.c
+--- vdr-1.7.14/cutter.c 2010-01-02 14:08:08.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/cutter.c 2010-04-10 15:45:09.627745454 +0200
+@@ -15,6 +15,19 @@
+
+ // --- cCuttingThread --------------------------------------------------------
+
++#ifdef USE_CUTTERLIMIT
++#ifndef CUTTER_MAX_BANDWIDTH
++#define CUTTER_MAX_BANDWIDTH MEGABYTE(10) // 10 MB/s
++#endif
++#ifndef CUTTER_REL_BANDWIDTH
++#define CUTTER_REL_BANDWIDTH 75 // %
++#endif
++#ifndef CUTTER_PRIORITY
++#define CUTTER_PRIORITY sched_get_priority_min(SCHED_OTHER)
++#endif
++#define CUTTER_TIMESLICE 100 // ms
++#endif /* CUTTERLIMIT */
++
+ class cCuttingThread : public cThread {
+ private:
+ const char *error;
+@@ -67,6 +80,22 @@
+
+ void cCuttingThread::Action(void)
+ {
++#ifdef USE_CUTTERLIMIT
++#ifdef USE_HARDLINKCUTTER
++ if (!Setup.HardLinkCutter)
++#endif /* HARDLINKCUTTER */
++ {
++ sched_param tmp;
++ tmp.sched_priority = CUTTER_PRIORITY;
++ if(!pthread_setschedparam(pthread_self(), SCHED_OTHER, &tmp))
++ printf("cCuttingThread::Action: cant set priority\n");
++ }
++
++ int bytes = 0;
++ int __attribute__((unused)) burst_size = CUTTER_MAX_BANDWIDTH * CUTTER_TIMESLICE / 1000; // max bytes/timeslice
++ cTimeMs __attribute__((unused)) t;
++#endif /* CUTTERLIMIT */
++
+ cMark *Mark = fromMarks.First();
+ if (Mark) {
+ fromFile = fromFileName->Open();
+@@ -78,6 +107,9 @@
+ Mark = fromMarks.Next(Mark);
+ off_t FileSize = 0;
+ int CurrentFileNumber = 0;
++#ifdef USE_HARDLINKCUTTER
++ bool SkipThisSourceFile = false;
++#endif /* HARDLINKCUTTER */
+ int LastIFrame = 0;
+ toMarks.Add(0);
+ toMarks.Save();
+@@ -96,12 +128,101 @@
+
+ // Read one frame:
+
++#ifdef USE_HARDLINKCUTTER
++ if (!fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
++ // Error, unless we're past last cut-in and there's no cut-out
++ if (Mark || LastMark)
++ error = "index";
++ break;
++ }
++
++ if (FileNumber != CurrentFileNumber) {
++ fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
++ fromFile->SetReadAhead(MEGABYTE(20));
++ CurrentFileNumber = FileNumber;
++ if (SkipThisSourceFile) {
++ // At end of fast forward: Always skip to next file
++ toFile = toFileName->NextFile();
++ if (!toFile) {
++ error = "toFile 4";
++ break;
++ }
++ FileSize = 0;
++ SkipThisSourceFile = false;
++ }
++
++
++ if (Setup.HardLinkCutter && FileOffset == 0) {
++ // We are at the beginning of a new source file.
++ // Do we need to copy the whole file?
++
++ // if !Mark && LastMark, then we're past the last cut-out and continue to next I-frame
++ // if !Mark && !LastMark, then there's just a cut-in, but no cut-out
++ // if Mark, then we're between a cut-in and a cut-out
++
++ uint16_t MarkFileNumber;
++ off_t MarkFileOffset;
++ // Get file number of next cut mark
++ if (!Mark && !LastMark
++ || Mark
++ && fromIndex->Get(Mark->position, &MarkFileNumber, &MarkFileOffset)
++ && (MarkFileNumber != CurrentFileNumber)) {
++ // The current source file will be copied completely.
++ // Start new output file unless we did that already
++ if (FileSize != 0) {
++ toFile = toFileName->NextFile();
++ if (!toFile) {
++ error = "toFile 3";
++ break;
++ }
++ FileSize = 0;
++ }
++
++ // Safety check that file has zero size
++ struct stat buf;
++ if (stat(toFileName->Name(), &buf) == 0) {
++ if (buf.st_size != 0) {
++ esyslog("cCuttingThread: File %s exists and has nonzero size", toFileName->Name());
++ error = "nonzero file exist";
++ break;
++ }
++ }
++ else if (errno != ENOENT) {
++ esyslog("cCuttingThread: stat failed on %s", toFileName->Name());
++ error = "stat";
++ break;
++ }
++
++ // Clean the existing 0-byte file
++ toFileName->Close();
++ cString ActualToFileName(ReadLink(toFileName->Name()), true);
++ unlink(ActualToFileName);
++ unlink(toFileName->Name());
++
++ // Try to create a hard link
++ if (HardLinkVideoFile(fromFileName->Name(), toFileName->Name())) {
++ // Success. Skip all data transfer for this file
++ SkipThisSourceFile = true;
++ cutIn = false;
++ toFile = NULL; // was deleted by toFileName->Close()
++ }
++ else {
++ // Fallback: Re-open the file if necessary
++ toFile = toFileName->Open();
++ }
++ }
++ }
++ }
++
++ if (!SkipThisSourceFile) {
++#else
+ if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
+ if (FileNumber != CurrentFileNumber) {
+ fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+ fromFile->SetReadAhead(MEGABYTE(20));
+ CurrentFileNumber = FileNumber;
+ }
++#endif /* HARDLINKCUTTER */
+ if (fromFile) {
+ int len = ReadFrame(fromFile, buffer, Length, sizeof(buffer));
+ if (len < 0) {
+@@ -118,19 +239,25 @@
+ break;
+ }
+ }
++#ifndef USE_HARDLINKCUTTER
+ else {
+ // Error, unless we're past the last cut-in and there's no cut-out
+ if (Mark || LastMark)
+ error = "index";
+ break;
+ }
++#endif /* HARDLINKCUTTER */
+
+ // Write one frame:
+
+ if (Independent) { // every file shall start with an independent frame
+ if (LastMark) // edited version shall end before next I-frame
+ break;
++#ifdef USE_HARDLINKCUTTER
++ if (!SkipThisSourceFile && FileSize > toFileName->MaxFileSize()) {
++#else
+ if (FileSize > maxVideoFileSize) {
++#endif /* HARDLINKCUTTER */
+ toFile = toFileName->NextFile();
+ if (!toFile) {
+ error = "toFile 1";
+@@ -140,7 +267,11 @@
+ }
+ LastIFrame = 0;
+
++#ifdef USE_HARDLINKCUTTER
++ if (!SkipThisSourceFile && cutIn) {
++#else
+ if (cutIn) {
++#endif /* HARDLINKCUTTER */
+ if (isPesRecording)
+ cRemux::SetBrokenLink(buffer, Length);
+ else
+@@ -148,7 +279,11 @@
+ cutIn = false;
+ }
+ }
++#ifdef USE_HARDLINKCUTTER
++ if (!SkipThisSourceFile && toFile->Write(buffer, Length) < 0) {
++#else
+ if (toFile->Write(buffer, Length) < 0) {
++#endif /* HARDLINKCUTTER */
+ error = "safe_write";
+ break;
+ }
+@@ -183,8 +318,45 @@
+ }
+ }
+ else
++#ifdef USE_HARDLINKCUTTER
++ LastMark = true; // After last cut-out: Write on until next I-frame, then exit
++#else
+ LastMark = true;
++#endif /* HARDLINKCUTTER */
+ }
++
++#ifdef USE_CUTTERLIMIT
++#ifdef USE_HARDLINKCUTTER
++ if (!Setup.HardLinkCutter) {
++#endif /* HARDLINKCUTTER */
++ bytes += Length;
++ if(bytes >= burst_size) {
++ int elapsed = t.Elapsed();
++ int sleep = 0;
++
++#if CUTTER_REL_BANDWIDTH > 0 && CUTTER_REL_BANDWIDTH < 100
++ // stay under max. relative bandwidth
++
++ sleep = (elapsed * 100 / CUTTER_REL_BANDWIDTH) - elapsed;
++ //if(sleep<=0 && elapsed<=2) sleep = 1;
++ //if(sleep) esyslog("cutter: relative bandwidth limit, sleep %d ms (chunk %dk / %dms)", sleep, burst_size/1024, CUTTER_TIMESLICE);
++#endif
++ // stay under max. absolute bandwidth
++ if(elapsed < CUTTER_TIMESLICE) {
++ sleep = max(CUTTER_TIMESLICE - elapsed, sleep);
++ //if(sleep) esyslog("cutter: absolute bandwidth limit, sleep %d ms (chunk %dk / %dms)", sleep, burst_size/1024, CUTTER_TIMESLICE);
++ }
++
++ if(sleep>0)
++ cCondWait::SleepMs(sleep);
++ t.Set();
++ bytes = 0;
++ }
++#ifdef USE_HARDLINKCUTTER
++ }
++#endif /* HARDLINKCUTTER */
++#endif /* CUTTERLIMIT */
++
+ }
+ Recordings.TouchUpdate();
+ }
+@@ -194,18 +366,87 @@
+
+ // --- cCutter ---------------------------------------------------------------
+
++#ifdef USE_CUTTERQUEUE
++#define WAIT_BEFORE_NEXT_CUT (10*1000) // 10 seconds
++
++class cStringListObject : public cListObject {
++ public:
++ cStringListObject(const char *s) { str = strdup(s); }
++ ~cStringListObject() { free(str); }
++
++ const char *Value() { return str; }
++ operator const char * () { return str; }
++
++ private:
++ char *str;
++};
++#endif /* CUTTERQUEUE */
++
+ char *cCutter::editedVersionName = NULL;
+ cCuttingThread *cCutter::cuttingThread = NULL;
+ bool cCutter::error = false;
+ bool cCutter::ended = false;
++#ifdef USE_CUTTERQUEUE
++cMutex *cCutter::cutterLock = new cMutex();
++
++static uint64_t lastCuttingEndTime = 0;
++static cList<cStringListObject> cutterQueue;
++#endif /* CUTTERQUEUE */
+
+ bool cCutter::Start(const char *FileName)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++
++ if(FileName) {
++ /* Add file to queue.
++ * If cutter is still active, next cutting will be started
++ * when vdr.c:main calls cCutter::Active and previous cutting has
++ * been stopped > 10 s before
++ */
++ cutterQueue.Add(new cStringListObject(FileName));
++ }
++
++ if (cuttingThread)
++ return true;
++
++ /* cut next file from queue */
++ if(!(cutterQueue.First()))
++ return false;
++ FileName = cutterQueue.First()->Value();
++#endif /* CUTTTERQUEUE */
++
+ if (!cuttingThread) {
+ error = false;
+ ended = false;
+ cRecording Recording(FileName);
++
++#ifdef USE_CUTTIME
++ cMarks FromMarks;
++ FromMarks.Load(FileName);
++ cMark *First=FromMarks.First();
++ if (First) Recording.SetStartTime(Recording.start+(int(First->position/Recording.FramesPerSecond()+30)/60)*60);
++#endif /* CUTTIME */
++
+ const char *evn = Recording.PrefixFileName('%');
++
++#ifdef USE_CUTTERQUEUE
++ if(!(Recordings.GetByName(FileName))) {
++ // Update Recordings, maybe its not initialized(if vdr --edit is used)
++ Recordings.Update(true);
++ if(!(Recordings.GetByName(FileName))) {
++ // Update Recordings, maybe its not initialized(if vdr --edit is used)
++ Recordings.Update(true);
++ if(!(Recordings.GetByName(FileName))) {
++ // Should _not_ remove any cutted recordings
++ // (original recording already deleted ?)
++ // so, just pop item from queue and return.
++ esyslog("can't cut non-existing recording %s", FileName);
++ cutterQueue.Del(cutterQueue.First());
++ return true; // might be already queued recording
++ }
++ }
++#endif /* CUTTERQUEUE */
+ if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
+ // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
+ // remove a possible deleted recording with the same name to avoid symlink mixups:
+@@ -231,6 +472,10 @@
+
+ void cCutter::Stop(void)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
++
+ bool Interrupted = cuttingThread && cuttingThread->Active();
+ const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
+ delete cuttingThread;
+@@ -242,11 +487,20 @@
+ esyslog("ERROR: '%s' during editing process", Error);
+ RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
+ Recordings.DelByName(editedVersionName);
++#ifdef USE_CUTTERQUEUE
++ cutterQueue.Del(cutterQueue.First());
++#endif /* CUTTERQUEUE */
+ }
++#ifdef USE_CUTTERQUEUE
++ lastCuttingEndTime = cTimeMs::Now();
++#endif /* CUTTERQUEUE */
+ }
+
+ bool cCutter::Active(void)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+ if (cuttingThread) {
+ if (cuttingThread->Active())
+ return true;
+@@ -257,12 +511,41 @@
+ free(editedVersionName);
+ editedVersionName = NULL;
+ ended = true;
++#ifdef USE_CUTTERQUEUE
++ if (Setup.CutterAutoDelete) {
++ /* Remove original (if cutting was successful) */
++ if(!error) {
++ cRecording *recording = Recordings.GetByName(*cutterQueue.First());
++ if (!recording)
++ esyslog("ERROR: Can't found '%s' after editing process", cutterQueue.First()->Value());
++ else {
++ if (recording->Delete())
++ Recordings.DelByName(recording->FileName());
++ else
++ esyslog("ERROR: Can't delete '%s' after editing process", cutterQueue.First()->Value());
++ }
++ }
++ lastCuttingEndTime = cTimeMs::Now();
++ }
++ cutterQueue.Del(cutterQueue.First());
++#endif /* CUTTERQUEUE */
+ }
++#ifdef USE_CUTTERQUEUE
++ if(!cuttingThread && cutterQueue.First()) {
++ /* start next cutting from queue*/
++ if(cTimeMs::Now() > lastCuttingEndTime + WAIT_BEFORE_NEXT_CUT)
++ Start(NULL);
++ }
++#endif /* CUTTERQUEUE */
++
+ return false;
+ }
+
+ bool cCutter::Error(void)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+ bool result = error;
+ error = false;
+ return result;
+@@ -270,6 +553,9 @@
+
+ bool cCutter::Ended(void)
+ {
++#ifdef USE_CUTTERQUEUE
++ cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+ bool result = ended;
+ ended = false;
+ return result;
+diff -ruN vdr-1.7.14/cutter.h vdr-1.7.14.ExtP_NG/cutter.h
+--- vdr-1.7.14/cutter.h 2010-01-02 13:09:54.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/cutter.h 2010-04-10 15:45:09.656737920 +0200
+@@ -11,6 +11,9 @@
+ #define __CUTTER_H
+
+ class cCuttingThread;
++#ifdef USE_CUTTERQUEUE
++class cMutex;
++#endif /* CUTTERQUEUE */
+
+ class cCutter {
+ private:
+@@ -18,6 +21,9 @@
+ static cCuttingThread *cuttingThread;
+ static bool error;
+ static bool ended;
++#ifdef USE_CUTTERQUEUE
++ static cMutex *cutterLock;
++#endif /* CUTTERQUEUE */
+ public:
+ static bool Start(const char *FileName);
+ static void Stop(void);
+diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
+--- vdr-1.7.14/device.c 2010-02-07 12:54:42.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/device.c 2010-04-10 15:45:09.684741026 +0200
+@@ -18,6 +18,12 @@
+ #include "receiver.h"
+ #include "status.h"
+ #include "transfer.h"
++#ifdef USE_LNBSHARE
++#include "diseqc.h"
++#endif /* LNBSHARE */
++#ifdef USE_TTXTSUBS
++#include "vdrttxtsubshooks.h"
++#endif /* TTXTSUBS */
+
+ // --- cLiveSubtitle ---------------------------------------------------------
+
+@@ -83,6 +89,12 @@
+
+ SetVideoFormat(Setup.VideoFormat);
+
++#ifdef USE_LNBSHARE
++ LNBstate = -1;
++ LNBnr = Setup.CardUsesLNBnr[cardIndex];
++ LNBsource = NULL;
++#endif /* LNBSHARE */
++
+ mute = false;
+ volume = Setup.CurrentVolume;
+
+@@ -144,6 +156,16 @@
+ useDevice |= (1 << n);
+ }
+
++#ifdef USE_LNBSHARE
++void cDevice::SetLNBnr(void)
++{
++ for (int i = 0; i < numDevices; i++) {
++ device[i]->LNBnr = Setup.CardUsesLNBnr[i];
++ isyslog("LNB-sharing: setting device %d to use LNB %d", i, device[i]->LNBnr);
++ }
++}
++#endif /* LNBSHARE */
++
+ int cDevice::NextCardIndex(int n)
+ {
+ if (n > 0) {
+@@ -208,6 +230,98 @@
+ return d;
+ }
+
++#ifdef USE_LNBSHARE
++cDevice *cDevice::GetBadDevice(const cChannel *Channel)
++{
++ if(!cSource::IsSat(Channel->Source())) return NULL;
++ if (Setup.DiSEqC) {
++ cDiseqc *diseqc;
++ diseqc = Diseqcs.Get(cardIndex + 1, Channel->Source(), Channel->Frequency(), Channel->Polarization());
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBsource() != (int*) diseqc) {
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Device check for channel %d on device %d. LNB or DiSEq conflict with device %d", LNBnr, Channel->Number(), this->DeviceNumber(), i);
++ }
++ return device[i];
++ }
++ }
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Device check for for channel %d on device %d. OK", LNBnr, Channel->Number(), this->DeviceNumber());
++ }
++ } else {
++ char requiredState;
++ if (Channel->Frequency() >= Setup.LnbSLOF) {
++ requiredState = 1 ;
++ } else {
++ requiredState = 0;
++ }
++ if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V') requiredState += 2;
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBconf() != requiredState) {
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Device check for channel %d, LNBstate %d on device %d, current LNBstate %d. Conflict with device %d, LNBstate %d", LNBnr, Channel->Number(), requiredState, this->DeviceNumber(), LNBstate, i, device[i]->GetLNBconf());
++ }
++ return device[i];
++ }
++ }
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Device check for channel %d, LNBstate %d on device %d, current LNBstate %d. No other devices affected", LNBnr, Channel->Number(), requiredState, this->DeviceNumber(), LNBstate);
++ }
++ }
++ return NULL;
++}
++
++int cDevice::GetMaxBadPriority(const cChannel *Channel)
++{
++ if(!cSource::IsSat(Channel->Source())) return -2;
++ bool PrimaryIsBad = false;
++ int maxBadPriority = -2;
++ if (Setup.DiSEqC) {
++ cDiseqc *diseqc;
++ diseqc = Diseqcs.Get(cardIndex + 1, Channel->Source(), Channel->Frequency(), Channel->Polarization());
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBsource() != (int*) diseqc) {
++ if (device[i]->Receiving() && device[i]->Priority() > maxBadPriority) {
++ maxBadPriority = device[i]->Priority();
++ }
++ if (i == ActualDevice()->CardIndex()) {
++ PrimaryIsBad = true;
++ }
++ }
++ }
++ } else {
++ char requiredState;
++ if (Channel->Frequency() >= Setup.LnbSLOF) {
++ requiredState = 1 ;
++ } else {
++ requiredState = 0;
++ }
++ if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V') requiredState += 2;
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBconf() != requiredState) {
++ if (device[i]->Receiving() && device[i]->Priority() > maxBadPriority) {
++ maxBadPriority = device[i]->Priority();
++ }
++ if (i == ActualDevice()->CardIndex()) {
++ PrimaryIsBad = true;
++ }
++ }
++ }
++ }
++ if (PrimaryIsBad && maxBadPriority == -2) {
++ maxBadPriority = -1;
++ }
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Request for channel %d on device %d. MaxBadPriority is %d", LNBnr, Channel->Number(), this->DeviceNumber(), maxBadPriority);
++ }
++ return maxBadPriority;
++}
++#endif /* LNBSHARE */
++
+ cDevice *cDevice::GetDevice(int Index)
+ {
+ return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
+@@ -236,6 +350,9 @@
+ int NumCamSlots = CamSlots.Count();
+ int SlotPriority[NumCamSlots];
+ int NumUsableSlots = 0;
++#ifdef USE_MCLI
++ bool InternalCamNeeded = false;
++#endif /* MCLI */
+ if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
+ for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
+ SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
+@@ -249,7 +366,11 @@
+ }
+ }
+ if (!NumUsableSlots)
++#ifdef USE_MCLI
++ InternalCamNeeded = true; // no CAM is able to decrypt this channel
++#else
+ return NULL; // no CAM is able to decrypt this channel
++#endif /* MCLI */
+ }
+
+ bool NeedsDetachReceivers = false;
+@@ -257,6 +378,10 @@
+ cCamSlot *s = NULL;
+
+ uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
++#ifdef USE_LNBSHARE
++ int badPriority;
++ uint32_t imp2;
++#endif /* LNBSHARE */
+ for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
+ if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
+ continue; // there is no CAM available in this slot
+@@ -265,11 +390,21 @@
+ continue; // this device shall be temporarily avoided
+ if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device[i]->CardIndex() + 1)
+ continue; // a specific card was requested, but not this one
++#ifdef USE_MCLI
++ if (InternalCamNeeded && !device[i]->HasInternalCam())
++ continue; // no CAM is able to decrypt this channel and the device uses vdr handled CAMs
++ if (NumUsableSlots && !device[i]->HasInternalCam() && !CamSlots.Get(j)->Assign(device[i], true))
++#else
+ if (NumUsableSlots && !CamSlots.Get(j)->Assign(device[i], true))
++#endif /* MCLI */
+ continue; // CAM slot can't be used with this device
+ bool ndr;
+ if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
++#ifdef USE_MCLI
++ if (NumUsableSlots && !device[i]->HasInternalCam() && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
++#else
+ if (NumUsableSlots && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
++#endif /* MCLI */
+ ndr = true; // using a different CAM slot requires detaching receivers
+ // Put together an integer number that reflects the "impact" using
+ // this device would have on the overall system. Each condition is represented
+@@ -284,18 +419,61 @@
+ imp <<= 2; imp |= GetClippedNumProvidedSystems(2, device[i]) - 1; // avoid cards which support multiple delivery systems
+ imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
+ imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
++#ifdef USE_MCLI
++ imp <<= 8; imp |= min(max(((NumUsableSlots && !device[i]->HasInternalCam()) ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
++#else
+ imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
++#endif /* MCLI */
+ imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
+ imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
++#ifdef USE_MCLI
++ imp <<= 1; imp |= (NumUsableSlots || InternalCamNeeded) ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
++#else
+ imp <<= 1; imp |= NumUsableSlots ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
++#endif /* MCLI */
+ imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
++#ifdef USE_MCLI
++ imp <<= 1; imp |= (NumUsableSlots && !device[i]->HasInternalCam()) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
++#else
+ imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
++#endif /* MCLI */
++#ifdef USE_LNBSHARE
++ badPriority = device[i]->GetMaxBadPriority(Channel);
++ if (badPriority >= Priority || (badPriority == -1 && Priority < Setup.PrimaryLimit)) {
++ // channel is not available for the requested prioity
++ imp = 0xFFFFFFFF;
++ } else {
++ switch (badPriority) {
++ case -2: // not affected by LNB-sharing
++ imp2 = 0;
++ break;
++ case -1: // the primary device would need a channel switch
++ if ( LiveView ) {
++ imp2 = 0; // this has no impact if the new channel is needed for LiveView
++ } else {
++ imp += 1 << 17;
++ imp2 = 0xFFFFFFFF | 1 << 17;
++ }
++ break;
++ default: // a device receiving with lower priority would need to be stopped
++ imp += badPriority << 8;
++ imp2 = 0xFFFFFFFF | badPriority << 8;
++ break;
++ }
++ }
++ if (imp < Impact && imp2 < Impact) {
++#else
+ if (imp < Impact) {
++#endif /* LNBSHARE */
+ // This device has less impact than any previous one, so we take it.
+ Impact = imp;
+ d = device[i];
+ NeedsDetachReceivers = ndr;
++#ifdef USE_MCLI
++ if (NumUsableSlots && !device[i]->HasInternalCam())
++#else
+ if (NumUsableSlots)
++#endif /* MCLI */
+ s = CamSlots.Get(j);
+ }
+ }
+@@ -561,6 +739,13 @@
+ return -1;
+ }
+
++#ifdef USE_MCLI
++int cDevice::ReadFilter(int Handle, void *Buffer, size_t Length)
++{
++ return safe_read(Handle, Buffer, Length);
++}
++#endif /* MCLI */
++
+ void cDevice::CloseFilter(int Handle)
+ {
+ close(Handle);
+@@ -602,7 +787,11 @@
+ bool cDevice::ProvidesTransponderExclusively(const cChannel *Channel) const
+ {
+ for (int i = 0; i < numDevices; i++) {
++#ifdef USE_LNBSHARE
++ if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel) && device[i]->GetLNBnr() != LNBnr)
++#else
+ if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel))
++#endif /* LNBSHARE */
+ return false;
+ }
+ return true;
+@@ -635,6 +824,25 @@
+
+ bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
+ {
++
++#ifdef USE_LNBSHARE
++ cDevice *tmpDevice;
++ if (this->GetMaxBadPriority(Channel) >= 0) {
++ Skins.Message(mtInfo, tr("Channel locked by LNB!"));
++ return false;
++ }
++ while ((tmpDevice = GetBadDevice(Channel)) != NULL) {
++ if ((tmpDevice->CardIndex() == ActualDevice()->CardIndex()) && LiveView)
++ tmpDevice->SwitchChannelForced(Channel, true);
++ else
++ tmpDevice->SwitchChannelForced(Channel, false);
++ }
++ return SwitchChannelForced(Channel, LiveView);
++}
++
++bool cDevice::SwitchChannelForced(const cChannel *Channel, bool LiveView)
++{
++#endif /* LNBSHARE */
+ if (LiveView) {
+ isyslog("switching to channel %d", Channel->Number());
+ cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
+@@ -665,6 +873,9 @@
+ cChannel *channel;
+ while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
+ // try only channels which are currently available
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgChannelProtected(0, channel) == false)
++#endif /* PINPLUGIN */
+ if (GetDevice(channel, 0, true))
+ break;
+ n = channel->Number() + Direction;
+@@ -686,6 +897,11 @@
+
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++#ifdef USE_PINPLUGIN
++ if (LiveView && cStatus::MsgChannelProtected(this, Channel) == true)
++ return scrNotAvailable;
++#endif /* PINPLUGIN */
++
+ if (LiveView) {
+ StopReplay();
+ DELETENULL(liveSubtitle);
+@@ -698,11 +914,39 @@
+
+ eSetChannelResult Result = scrOk;
+
++#ifdef USE_LNBSHARE
++ char requiredState;
++ if (Channel->Frequency() >= Setup.LnbSLOF) {
++ requiredState = 1;
++ } else {
++ requiredState = 0;
++ }
++ if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V') requiredState += 2;
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Switching device %d to channel %d", LNBnr, this->DeviceNumber(), Channel->Number());
++ }
++#endif /* LNBSHARE */
++
+ // If this DVB card can't receive this channel, let's see if we can
+ // use the card that actually can receive it and transfer data from there:
+
+ if (NeedsTransferMode) {
+ if (Device && CanReplay()) {
++
++#ifdef USE_LNBSHARE
++ if (Device->GetLNBnr() == LNBnr) {
++ if (LNBstate != requiredState || (Setup.DiSEqC && LNBsource != (int*) Diseqcs.Get(cardIndex + 1, Channel->Source(), Channel->Frequency(), Channel->Polarization())) ) {
++ if (CardIndex() == ActualDevice()->CardIndex()) {
++ SetChannelDevice(Channel, true);
++ } else {
++ SetChannelDevice(Channel, false);
++ }
++ LNBstate = requiredState;
++ LNBsource = (int*) Diseqcs.Get(cardIndex + 1, Channel->Source(), Channel->Frequency(), Channel->Polarization());
++ }
++ }
++#endif /* LNBSHARE */
++
+ cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
+ if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
+ cControl::Launch(new cTransferControl(Device, Channel));
+@@ -720,6 +964,12 @@
+ sectionHandler->SetStatus(false);
+ sectionHandler->SetChannel(NULL);
+ }
++
++#ifdef USE_LNBSHARE
++ LNBstate = requiredState;
++ LNBsource = (int*) Diseqcs.Get(cardIndex + 1, Channel->Source(), Channel->Frequency(), Channel->Polarization());
++#endif /* LNBSHARE */
++
+ // Tell the camSlot about the channel switch and add all PIDs of this
+ // channel to it, for possible later decryption:
+ if (camSlot)
+@@ -1012,6 +1262,10 @@
+ int LanguagePreference = INT_MAX; // higher than the maximum possible value
+ for (int i = ttSubtitleFirst; i <= ttSubtitleLast; i++) {
+ const tTrackId *TrackId = GetTrack(eTrackType(i));
++#ifdef USE_LIEMIEXT
++ if (TrackId && TrackId->id && (I18nIsPreferredLanguage(Setup.SubtitleLanguages, TrackId->language, LanguagePreference) ||
++ ((i == ttSubtitleFirst + 8) && !(*TrackId->language) && (LanguagePreference == INT_MAX))))
++#endif /* LIEMIEXT */
+ if (TrackId && TrackId->id && I18nIsPreferredLanguage(Setup.SubtitleLanguages, TrackId->language, LanguagePreference))
+ PreferredTrack = eTrackType(i);
+ }
+@@ -1223,6 +1477,15 @@
+ }
+ break;
+ case 0xBD: { // private stream 1
++#ifdef USE_TTXTSUBS
++ // EBU Teletext data, ETSI EN 300 472
++ // if PES data header length = 24 and data_identifier = 0x10..0x1F (EBU Data)
++ if (Data[8] == 0x24 && Data[45] >= 0x10 && Data[45] < 0x20) {
++ cVDRTtxtsubsHookListener::Hook()->PlayerTeletextData((uint8_t*)Data, Length);
++ break;
++ }
++#endif /* TTXTSUBS */
++
+ int PayloadOffset = Data[8] + 9;
+
+ // Compatibility mode for old subtitles plugin:
+@@ -1382,6 +1645,9 @@
+ tsToPesVideo.Reset();
+ tsToPesAudio.Reset();
+ tsToPesSubtitle.Reset();
++#ifdef USE_TTXTSUBS
++ tsToPesTeletext.Reset();
++#endif /* TTXTSUBS */
+ }
+ else if (Length < TS_SIZE) {
+ esyslog("ERROR: skipped %d bytes of TS fragment", Length);
+@@ -1427,6 +1693,19 @@
+ if (!VideoOnly || HasIBPTrickSpeed())
+ PlayTsSubtitle(Data, TS_SIZE);
+ }
++#ifdef USE_TTXTSUBS
++ else if (Pid == patPmtParser.Tpid()) {
++ if (!VideoOnly || HasIBPTrickSpeed()) {
++ int l;
++ tsToPesTeletext.PutTs(Data, Length);
++ if (const uchar *p = tsToPesTeletext.GetPes(l)) {
++ if ((l > 45) && (p[0] == 0x00) && (p[1] == 0x00) && (p[2] == 0x01) && (p[3] == 0xbd) && (p[8] == 0x24) && (p[45] >= 0x10) && (p[45] < 0x20))
++ cVDRTtxtsubsHookListener::Hook()->PlayerTeletextData((uchar *)p, l, false, patPmtParser.TeletextSubtitlePages(), patPmtParser.TotalTeletextSubtitlePages());
++ tsToPesTeletext.Reset();
++ }
++ }
++ }
++#endif /* TTXTSUBS */
+ }
+ }
+ else if (Pid == patPmtParser.Ppid()) {
+diff -ruN vdr-1.7.14/device.h vdr-1.7.14.ExtP_NG/device.h
+--- vdr-1.7.14/device.h 2010-02-06 15:34:41.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/device.h 2010-04-10 15:45:09.706728067 +0200
+@@ -24,6 +24,9 @@
+ #include "spu.h"
+ #include "thread.h"
+ #include "tools.h"
++#ifdef USE_ROTOR
++#include <linux/dvb/frontend.h>
++#endif /* ROTOR */
+
+ #define MAXDEVICES 16 // the maximum number of devices in the system
+ #define MAXPIDHANDLES 64 // the maximum number of different PIDs per device
+@@ -156,6 +159,31 @@
+ static void Shutdown(void);
+ ///< Closes down all devices.
+ ///< Must be called at the end of the program.
++#ifdef USE_LNBSHARE
++private:
++ char LNBstate; // Current frequency band and polarization of the DVB-tuner
++// cDiseqc *LNBsource; // can not #include "diseqc.h". A workaround follows:
++ int *LNBsource; // [DiSEqC] DiSEqC-Source
++ int LNBnr; // Number of LNB used
++public:
++ char GetLNBconf(void) { return LNBstate; }
++ int *GetLNBsource(void) { return LNBsource; }
++ int GetLNBnr(void) { return LNBnr; }
++ static void SetLNBnr(void);
++ cDevice *GetBadDevice(const cChannel *Channel);
++ ///< Returns NULL if there is no device which uses the same LNB or if
++ ///< all of those devices are tuned to the same frequency band and
++ ///< polarization as of the requested channel.
++ ///< Otherwise returns the first device found.
++ int GetMaxBadPriority(const cChannel *Channel);
++ ///< Returns the highest priority of all receiving devices which use
++ ///< the same LNB and are tuned to a different frequency band or
++ ///< polarization as of the requested channel.
++ ///< Returns -1 if there are no such devices, but the primary device
++ ///< would be affected by switching to the requested channel.
++ ///< Returns -2 if there are no such devices and the primary device
++ ///< would not be affected by switching to the requested channel.
++#endif /* LNBSHARE */
+ private:
+ static int nextCardIndex;
+ int cardIndex;
+@@ -261,17 +289,28 @@
+ bool SwitchChannel(const cChannel *Channel, bool LiveView);
+ ///< Switches the device to the given Channel, initiating transfer mode
+ ///< if necessary.
++#ifdef USE_LNBSHARE
++ bool SwitchChannelForced(const cChannel *Channel, bool LiveView);
++ ///< Switches the device to the given channel, initiating transfer mode
++ ///< if necessary. Forces the switch without taking care of the LNB configuration.
++#endif /* LNBSHARE */
+ static bool SwitchChannel(int Direction);
+ ///< Switches the primary device to the next available channel in the given
+ ///< Direction (only the sign of Direction is evaluated, positive values
+ ///< switch to higher channel numbers).
+ private:
++#ifndef USE_YAEPG
+ eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
+ ///< Sets the device to the given channel (general setup).
++#endif /* YAEPG */
+ protected:
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+ ///< Sets the device to the given channel (actual physical setup).
+ public:
++#ifdef USE_YAEPG
++ eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
++ ///< Sets the device to the given channel (general setup).
++#endif /* YAEPG */
+ static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
+ ///< Returns the number of the current channel on the primary device.
+ static void SetCurrentChannel(const cChannel *Channel) { currentChannel = Channel ? Channel->Number() : 0; }
+@@ -289,6 +328,9 @@
+ virtual bool HasProgramme(void);
+ ///< Returns true if the device is currently showing any programme to
+ ///< the user, either through replaying or live.
++#ifdef USE_ROTOR
++ virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd) {return false;}
++#endif /* ROTOR */
+
+ // PID handle facilities
+
+@@ -342,6 +384,12 @@
+ ///< Opens a file handle for the given filter data.
+ ///< A derived device that provides section data must
+ ///< implement this function.
++#ifdef USE_MCLI
++ virtual int ReadFilter(int Handle, void *Buffer, size_t Length);
++ ///< Read from a handle for the given filter data.
++ ///< a derived class need not implement this function, because this
++ ///< is done by the default implementation.
++#endif /* MCLI */
+ virtual void CloseFilter(int Handle);
+ ///< Closes a file handle that has previously been opened
+ ///< by OpenFilter(). If this is as simple as calling close(Handle),
+@@ -360,6 +408,14 @@
+ public:
+ virtual bool HasCi(void);
+ ///< Returns true if this device has a Common Interface.
++#ifdef USE_MCLI
++ virtual bool HasInternalCam(void) { return false; }
++ ///< Returns true if this device handles encrypted channels itself
++ ///< without VDR assistance. This can be e.g. when the device is a
++ ///< client that gets the stream from another VDR instance that has
++ ///< already decrypted the stream. In this case ProvidesChannel()
++ ///< shall check whether the channel can be decrypted.
++#endif /* MCLI */
+ void SetCamSlot(cCamSlot *CamSlot);
+ ///< Sets the given CamSlot to be used with this device.
+ cCamSlot *CamSlot(void) const { return camSlot; }
+@@ -519,6 +575,9 @@
+ cTsToPes tsToPesVideo;
+ cTsToPes tsToPesAudio;
+ cTsToPes tsToPesSubtitle;
++#ifdef USE_TTXTSUBS
++ cTsToPes tsToPesTeletext;
++#endif /* TTXTSUBS */
+ bool isPlayingVideo;
+ protected:
+ const cPatPmtParser *PatPmtParser(void) const { return &patPmtParser; }
+diff -ruN vdr-1.7.14/dvbdevice.c vdr-1.7.14.ExtP_NG/dvbdevice.c
+--- vdr-1.7.14/dvbdevice.c 2010-03-07 14:58:24.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/dvbdevice.c 2010-04-10 15:45:09.854742151 +0200
+@@ -246,6 +246,9 @@
+ class cDvbTuner : public cThread {
+ private:
+ enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
++#ifdef USE_ROTOR
++ bool SendDiseqc;
++#endif /* ROTOR */
+ int device;
+ int fd_frontend;
+ int adapter, frontend;
+@@ -259,6 +262,9 @@
+ cMutex mutex;
+ cCondVar locked;
+ cCondVar newSet;
++#ifdef USE_ROTOR
++ dvb_diseqc_master_cmd diseqc_cmd;
++#endif /* ROTOR */
+ bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0);
+ bool SetFrontend(void);
+ virtual void Action(void);
+@@ -268,6 +274,9 @@
+ const cChannel *GetTransponder(void) const { return &channel; }
+ bool IsTunedTo(const cChannel *Channel) const;
+ void Set(const cChannel *Channel);
++#ifdef USE_ROTOR
++ bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd);
++#endif /* ROTOR */
+ bool Locked(int TimeoutMs = 0);
+ };
+
+@@ -275,6 +284,9 @@
+ {
+ device = Device;
+ fd_frontend = Fd_Frontend;
++#ifdef USE_ROTOR
++ SendDiseqc=false;
++#endif /* ROTOR */
+ adapter = Adapter;
+ frontend = Frontend;
+ frontendType = FrontendType;
+@@ -329,6 +341,19 @@
+ return tunerStatus >= tsLocked;
+ }
+
++#ifdef USE_ROTOR
++bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
++{
++ cMutexLock MutexLock(&mutex);
++ if ((frontendType!=SYS_DVBS2 && frontendType!=SYS_DVBS) || SendDiseqc)
++ return false;
++ diseqc_cmd=cmd;
++ SendDiseqc=true;
++ newSet.Broadcast();
++ return true;
++}
++#endif /* ROTOR */
++
+ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
+ {
+ if (TimeoutMs) {
+@@ -520,6 +545,12 @@
+ if (GetFrontendStatus(NewStatus, 10))
+ Status = NewStatus;
+ cMutexLock MutexLock(&mutex);
++#ifdef USE_ROTOR
++ if (SendDiseqc) {
++ CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &diseqc_cmd));
++ SendDiseqc=false;
++ }
++#endif /* ROTOR */
+ switch (tunerStatus) {
+ case tsIdle:
+ break;
+@@ -898,10 +929,25 @@
+
+ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
+ {
++#ifdef USE_CHANNELPROVIDE
++ if (Setup.LocalChannelProvide != 1)
++ return false;
++#endif /* CHANNELPROVIDE */
+ bool result = false;
+ bool hasPriority = Priority < 0 || Priority > this->Priority();
+ bool needsDetachReceivers = false;
+
++#ifdef USE_CHANNELBIND
++ if (Setup.ChannelBindingByRid && Channel->Rid()) {
++ if (0 == ((unsigned) Channel->Rid() & (1<<(unsigned) CardIndex())) ) {
++ #if 0
++ printf("device %d doesn't provide channel %s", CardIndex(), *Channel->ToText());
++ #endif
++ return false;
++ }
++ }
++#endif /* CHANNELBIND */
++
+ if (ProvidesTransponder(Channel)) {
+ result = hasPriority;
+ if (Priority >= 0 && Receiving(true)) {
+@@ -956,6 +1002,13 @@
+ return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
+ }
+
++#ifdef USE_ROTOR
++bool cDvbDevice::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
++{
++ return dvbTuner->SendDiseqcCmd(cmd);
++}
++#endif /* ROTOR */
++
+ void cDvbDevice::SetTransferModeForDolbyDigital(int Mode)
+ {
+ setTransferModeForDolbyDigital = Mode;
+diff -ruN vdr-1.7.14/dvbdevice.h vdr-1.7.14.ExtP_NG/dvbdevice.h
+--- vdr-1.7.14/dvbdevice.h 2010-02-21 15:06:08.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/dvbdevice.h 2010-04-10 15:45:09.863735987 +0200
+@@ -146,6 +146,9 @@
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+ public:
+ virtual bool HasLock(int TimeoutMs = 0);
++#ifdef USE_ROTOR
++ virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd);
++#endif /* ROTOR */
+
+ // PID handle facilities
+
+diff -ruN vdr-1.7.14/dvbplayer.c vdr-1.7.14.ExtP_NG/dvbplayer.c
+--- vdr-1.7.14/dvbplayer.c 2010-03-07 15:24:26.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/dvbplayer.c 2010-04-10 15:45:09.881743306 +0200
+@@ -204,6 +204,9 @@
+ cNonBlockingFileReader *nonBlockingFileReader;
+ cRingBufferFrame *ringBuffer;
+ cPtsIndex ptsIndex;
++#ifdef USE_JUMPPLAY
++ cMarksReload marks;
++#endif /* JUMPPLAY */
+ cFileName *fileName;
+ cIndexFile *index;
+ cUnbufferedFile *replayFile;
+@@ -250,7 +253,11 @@
+ int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
+
+ cDvbPlayer::cDvbPlayer(const char *FileName)
++#ifdef USE_JUMPPLAY
++:cThread("dvbplayer"), marks(FileName)
++#else
+ :cThread("dvbplayer")
++#endif /* JUMPPLAY */
+ {
+ nonBlockingFileReader = NULL;
+ ringBuffer = NULL;
+@@ -360,6 +367,12 @@
+ if (index) {
+ int Index = ptsIndex.FindIndex(DeviceGetSTC());
+ if (Index >= 0) {
++#ifdef USE_JUMPPLAY
++ // set resume position to 0 if replay stops at the first mark
++ if (Setup.PlayJump && marks.First() &&
++ abs(Index - marks.First()->position) <= int(round(RESUMEBACKUP * framesPerSecond)))
++ Index = 0;
++#endif /* JUMPPLAY */
+ Index -= int(round(RESUMEBACKUP * framesPerSecond));
+ if (Index > 0)
+ Index = index->GetNextIFrame(Index, false);
+@@ -386,11 +399,30 @@
+ {
+ uchar *p = NULL;
+ int pc = 0;
++#ifdef USE_JUMPPLAY
++ bool cutIn = false;
++ int total = -1;
++#endif /* JUMPPLAY */
+
+ readIndex = Resume();
+ if (readIndex >= 0)
+ isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
+
++#ifdef USE_JUMPPLAY
++ if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) {
++ int Index = marks.First()->position;
++ uint16_t FileNumber;
++ off_t FileOffset;
++ if (index->Get(Index, &FileNumber, &FileOffset) &&
++ NextFile(FileNumber, FileOffset)) {
++ isyslog("PlayJump: start replay at first mark %d (%s)",
++ Index, *IndexToHMSF(Index, true, framesPerSecond));
++ readIndex = Index;
++ }
++ }
++
++ bool LastMarkPause = false;
++#endif /* JUMPPLAY */
+ nonBlockingFileReader = new cNonBlockingFileReader;
+ int Length = 0;
+ bool Sleep = false;
+@@ -415,7 +447,11 @@
+
+ // Read the next frame from the file:
+
++#ifdef USE_JUMPPLAY
++ if (playMode != pmStill && playMode != pmPause && !LastMarkPause) {
++#else
+ if (playMode != pmStill && playMode != pmPause) {
++#endif /* JUMPPLAY */
+ if (!readFrame && (replayFile || readIndex >= 0)) {
+ if (!nonBlockingFileReader->Reading()) {
+ if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
+@@ -452,6 +488,46 @@
+ else if (index) {
+ uint16_t FileNumber;
+ off_t FileOffset;
++#ifdef USE_JUMPPLAY
++ if (Setup.PlayJump || Setup.PauseLastMark) {
++ // check for end mark - jump to next mark or pause
++ readIndex++;
++ marks.Reload();
++ cMark *m = marks.Get(readIndex);
++ if (m && (m->Index() & 0x01) != 0) {
++ m = marks.Next(m);
++ int Index;
++ if (m)
++ Index = m->position;
++ else if (Setup.PauseLastMark) {
++ // pause at last mark
++ isyslog("PauseLastMark: pause at position %d (%s)",
++ readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
++ LastMarkPause = true;
++ Index = -1;
++ }
++ else if (total == index->Last())
++ // at last mark jump to end of recording
++ Index = index->Last() - 1;
++ else
++ // jump but stay off end of live-recordings
++ Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), true);
++ // don't jump in edited recordings
++ if (Setup.PlayJump && Index > readIndex &&
++ Index > index->GetNextIFrame(readIndex, true)) {
++ isyslog("PlayJump: %d frames to %d (%s)",
++ Index - readIndex, Index,
++ *IndexToHMSF(Index, true, framesPerSecond));
++ readIndex = Index;
++ cutIn = true;
++ }
++ }
++ readIndex--;
++ }
++ // for detecting growing length of live-recordings
++ if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent) && readIndependent)
++ total = index->Last();
++#endif /* JUMPPLAY */
+ if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset))
+ readIndex++;
+ else
+@@ -496,6 +572,15 @@
+ // Store the frame in the buffer:
+
+ if (readFrame) {
++#ifdef USE_JUMPPLAY
++ if (cutIn) {
++ if (isPesRecording)
++ cRemux::SetBrokenLink(readFrame->Data(), readFrame->Count());
++ else
++ TsSetTeiOnBrokenPackets(readFrame->Data(), readFrame->Count());
++ cutIn = false;
++ }
++#endif /* JUMPPLAY */
+ if (ringBuffer->Put(readFrame))
+ readFrame = NULL;
+ else
+@@ -561,8 +646,19 @@
+ p = NULL;
+ }
+ }
++#ifdef USE_JUMPPLAY
++ else {
++ if (LastMarkPause) {
++ LastMarkPause = false;
++ playMode = pmPause;
++ }
++#else
+ else
++#endif /* JUMPPLAY */
+ Sleep = true;
++#ifdef USE_JUMPPLAY
++ }
++#endif /* JUMPPLAY */
+
+ // Handle hitting begin/end of recording:
+
+diff -ruN vdr-1.7.14/eit.c vdr-1.7.14.ExtP_NG/eit.c
+--- vdr-1.7.14/eit.c 2010-01-08 16:17:09.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/eit.c 2010-04-10 15:45:09.958741231 +0200
+@@ -24,8 +24,31 @@
+ class cEIT : public SI::EIT {
+ public:
+ cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus = false);
++#ifdef USE_NOEPG
++private:
++ bool allowedEPG(tChannelID kanalID);
++#endif /* NOEPG */
+ };
+
++#ifdef USE_NOEPG
++bool cEIT::allowedEPG(tChannelID kanalID) {
++ bool rc;
++
++ if (Setup.noEPGMode == 1) {
++ rc = false;
++ if (strstr(::Setup.noEPGList, kanalID.ToString()) != NULL)
++ rc = true;
++ }
++ else {
++ rc = true;
++ if (strstr(::Setup.noEPGList, kanalID.ToString()) != NULL)
++ rc = false;
++ }
++
++ return rc;
++}
++#endif /* NOEPG */
++
+ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus)
+ :SI::EIT(Data, false)
+ {
+@@ -37,6 +60,14 @@
+ if (!channel)
+ return; // only collect data for known channels
+
++#ifdef USE_NOEPG
++ // only use epg from channels not blocked by noEPG-patch
++ tChannelID kanalID;
++ kanalID = channel->GetChannelID();
++ if (!allowedEPG(kanalID))
++ return;
++#endif /* NOEPG */
++
+ cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
+
+ bool Empty = true;
+@@ -83,7 +114,87 @@
+ uchar TableID = pEvent->TableID();
+ if (TableID == 0x00) {
+ if (pEvent->Version() == getVersionNumber())
++#ifdef USE_DDEPGENTRY
++ {
++ if(Setup.MixEpgAction == 0)
++ continue;
++
++ //printf("in");
++ //printf("%s", pEvent->GetTimeString());
++ // to use the info of the original epg, update the extern one,
++ // if it has less info
++ SI::Descriptor *d;
++ SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL;
++ //SI::ExtendedEventDescriptor *eed = NULL;
++ SI::ShortEventDescriptor *ShortEventDescriptor = NULL;
++ //SI::ShortEventDescriptor *sed = NULL;
++ //SI::TimeShiftedEventDescriptor *tsed = NULL;
++ //cLinkChannels *LinkChannels = NULL;
++ for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2));)
++ {
++ if(d->getDescriptorTag() == SI::ShortEventDescriptorTag)
++ {
++ int LanguagePreferenceShort = -1;
++ SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d;
++ if (I18nIsPreferredLanguage(Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor)
++ {
++ delete ShortEventDescriptor;
++ ShortEventDescriptor = sed;
++ d = NULL; // so that it is not deleted
++ }
++ }
++ else if(d->getDescriptorTag() == SI::ExtendedEventDescriptorTag)
++ {
++ int LanguagePreferenceExt = -1;
++ bool UseExtendedEventDescriptor = false;
++ SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d;
++ if (I18nIsPreferredLanguage(Setup.EPGLanguages, 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;
++ }
++ delete d;
++ }
++ if(pEvent)
++ {
++
++ if(ShortEventDescriptor)
++ {
++ char buffer[256];
++ if(ShortEventDescriptor->text.getText(buffer, sizeof(buffer)) && pEvent->ShortText() && (strlen(ShortEventDescriptor->text.getText(buffer, sizeof(buffer))) > strlen(pEvent->ShortText())))
++ {
++ pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer, sizeof(buffer)));
++ pEvent->FixEpgBugs();
++ }
++ }
++ if(ExtendedEventDescriptors)
++ {
++ char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ") + 1];
++ //pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));
++
++ if(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ") && pEvent->Description() && (strlen(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ")) > strlen(pEvent->Description())))
++ {
++ pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));
++ pEvent->FixEpgBugs();
++ }
++ }
++ }
++ delete ExtendedEventDescriptors;
++ delete ShortEventDescriptor;
+ continue;
++ }
++#else
++ continue;
++#endif /* DDEPGENTRY */
+ HasExternalData = ExternalData = true;
+ }
+ // If the new event has a higher table ID, let's skip it.
+@@ -108,7 +219,11 @@
+ if (newEvent)
+ pSchedule->AddEvent(newEvent);
+ if (Tid == 0x4E) { // we trust only the present/following info on the actual TS
++#ifdef USE_DDEPGENTRY
++ if (Setup.DisableVPS == 0 && SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning)
++#else
+ if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning)
++#endif /* DDEPGENTRY */
+ pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel);
+ }
+ if (OnlyRunningStatus)
+@@ -297,6 +412,82 @@
+ if (LinkChannels)
+ channel->SetLinkChannels(LinkChannels);
+ Modified = true;
++
++#ifdef USE_DDEPGENTRY
++ //to avoid double epg-entrys from ext and int epg sources :EW
++ if (pEvent && pEvent->TableID() != 0x00)
++ {
++ cEvent *pPreviousEvent = (cEvent *)pSchedule->GetPreviousEvent(pEvent);
++
++ if (pPreviousEvent)
++ {
++ if(Setup.DoubleEpgAction == 0)
++ {
++ pPreviousEvent->SetStartTime(pEvent->StartTime());
++ pPreviousEvent->SetDuration(pEvent->Duration());
++
++ if(Setup.DisableVPS == 0)
++ {
++ if(channel)
++ pPreviousEvent->SetRunningStatus(pEvent->RunningStatus(), channel);
++ else
++ pPreviousEvent->SetRunningStatus(pEvent->RunningStatus());
++ }
++
++ // to use the info of the original epg, update the extern one,
++ // if it has less info
++ char buffer_short_intern[256];
++ char buffer_short_extern[256];
++ int len_short_intern = 0;
++ int len_short_extern = 0;
++
++ if (pEvent->ShortText())
++ len_short_intern = snprintf (buffer_short_intern, sizeof(buffer_short_intern), "%s", pEvent->ShortText());
++
++ if (pPreviousEvent->ShortText())
++ len_short_extern = snprintf (buffer_short_extern, sizeof(buffer_short_extern), "%s",pPreviousEvent->ShortText());
++
++ if(len_short_intern > 0)
++ {
++ if(len_short_extern < 1)
++ pPreviousEvent->SetShortText(buffer_short_intern);
++ else if (len_short_intern > len_short_extern)
++ pPreviousEvent->SetShortText(buffer_short_intern);
++ }
++
++ if(pEvent->Description())
++ {
++ char buffer_title_intern[4096];
++ char buffer_title_extern[4096];
++ int len_title_intern = 0;
++ int len_title_extern = 0;
++
++ if (pEvent->Description())
++ len_title_intern = snprintf (buffer_title_intern, sizeof(buffer_title_intern), "%s", pEvent->Description());
++
++ if (pPreviousEvent->Description())
++ len_title_extern = snprintf (buffer_title_extern, sizeof(buffer_title_extern), "%s", pPreviousEvent->Description());
++
++ if(len_title_intern > 0)
++ {
++ if(len_title_extern < 1)
++ pPreviousEvent->SetDescription(buffer_title_intern);
++ else if (len_title_intern > len_title_extern)
++ pPreviousEvent->SetDescription(buffer_title_intern);
++ }
++ }
++
++ if(pPreviousEvent->Vps() == 0 && pEvent->Vps() != 0)
++ pPreviousEvent->SetVps(pEvent->Vps());
++
++ pSchedule->DelEvent(pEvent);
++ pPreviousEvent->FixEpgBugs();
++ }
++ else
++ pSchedule->DelEvent(pPreviousEvent);
++ }
++ }
++#endif /* DDEPGENTRY */
+ }
+ if (Tid == 0x4E) {
+ if (Empty && getSectionNumber() == 0)
+diff -ruN vdr-1.7.14/eitscan.c vdr-1.7.14.ExtP_NG/eitscan.c
+--- vdr-1.7.14/eitscan.c 2010-02-07 13:12:05.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/eitscan.c 2010-04-10 15:45:09.980739226 +0200
+@@ -151,9 +151,17 @@
+ if (Device->ProvidesTransponder(Channel)) {
+ if (!Device->Receiving()) {
+ bool MaySwitchTransponder = Device->MaySwitchTransponder();
++#ifdef USE_LNBSHARE
++ if (MaySwitchTransponder && Device->GetMaxBadPriority(Channel) == -2 || Device->ProvidesTransponderExclusively(Channel) && Device->GetMaxBadPriority(Channel) <= -1 && now - lastActivity > Setup.EPGScanTimeout * 3600) {
++#else
+ if (MaySwitchTransponder || Device->ProvidesTransponderExclusively(Channel) && now - lastActivity > Setup.EPGScanTimeout * 3600) {
++#endif /* LNBSHARE */
+ if (!MaySwitchTransponder) {
++#ifdef USE_LNBSHARE
++ if ((Device == cDevice::ActualDevice() || Device->GetMaxBadPriority(Channel) == -1) && !currentChannel) {
++#else
+ if (Device == cDevice::ActualDevice() && !currentChannel) {
++#endif /* LNBSHARE */
+ cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
+ currentChannel = Device->CurrentChannel();
+ Skins.Message(mtInfo, tr("Starting EPG scan"));
+diff -ruN vdr-1.7.14/epg.c vdr-1.7.14.ExtP_NG/epg.c
+--- vdr-1.7.14/epg.c 2010-02-28 15:24:55.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/epg.c 2010-04-10 15:45:10.017741115 +0200
+@@ -930,6 +930,31 @@
+ return pe;
+ }
+
++#ifdef USE_DDEPGENTRY
++const cEvent *cSchedule::GetPreviousEvent(cEvent *Event) const
++{
++
++ if(!Event || Event->Duration() == 0 || Event->StartTime() == 0)
++ return NULL;
++ // Returns either the event info to the previous/following event to the given EventID or, if that one can't be found NULL :EW
++ cEvent *pt = NULL;
++ int epgTimeDelta = Setup.DoubleEpgTimeDelta * 60 + 1;
++ for (pt = events.First(); pt; pt = events.Next(pt))
++ if(pt && pt->TableID() == 0x00)
++ if ((Event->StartTime() - pt->StartTime()) > - epgTimeDelta && (Event->StartTime() - pt->StartTime()) < epgTimeDelta)
++ {
++ if((pt->Duration() + (pt->Duration()/ 5) + 1) > Event->Duration() && (pt->Duration() - (pt->Duration()/ 5) - 1) < Event->Duration())
++ return pt;
++ else if (pt->Title() && Event->Title() && (strcmp(pt->Title(), ".") != 0 && strcmp(Event->Title(), ".") != 0))
++ {
++ if (strstr(pt->Title(), Event->Title()) != NULL || strstr(Event->Title(), pt->Title()) != NULL)
++ return pt;
++ }
++ }
++ return NULL;
++}
++#endif /* DDEPGENTRY */
++
+ void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel)
+ {
+ hasRunning = false;
+diff -ruN vdr-1.7.14/epg.h vdr-1.7.14.ExtP_NG/epg.h
+--- vdr-1.7.14/epg.h 2010-01-08 16:20:34.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/epg.h 2010-04-10 15:45:10.027736473 +0200
+@@ -163,6 +163,9 @@
+ void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
+ void Cleanup(time_t Time);
+ void Cleanup(void);
++#ifdef USE_DDEPGENTRY
++ const cEvent *GetPreviousEvent(cEvent *Event) const; //:EW
++#endif /* DDEPGENTRY */
+ cEvent *AddEvent(cEvent *Event);
+ void DelEvent(cEvent *Event);
+ void HashEvent(cEvent *Event);
+diff -ruN vdr-1.7.14/iconpatch.c vdr-1.7.14.ExtP_NG/iconpatch.c
+--- vdr-1.7.14/iconpatch.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/iconpatch.c 2010-04-10 15:45:10.336740261 +0200
+@@ -0,0 +1,31 @@
++#ifdef USE_WAREAGLEICON
++
++#include "iconpatch.h"
++
++#include <langinfo.h>
++#include <locale.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++bool IsLangUtf8(void)
++{
++ char *CodeSet = NULL;
++ if (setlocale(LC_CTYPE, ""))
++ CodeSet = nl_langinfo(CODESET);
++ else {
++ char *LangEnv = getenv("LANG"); // last resort in case locale stuff isn't installed
++ if (LangEnv) {
++ CodeSet = strchr(LangEnv, '.');
++ if (CodeSet)
++ CodeSet++; // skip the dot
++ }
++ }
++
++ if (CodeSet && strcasestr(CodeSet, "UTF-8") != 0)
++ return true;
++
++ return false;
++}
++
++#endif /* WAREAGLEICON */
+diff -ruN vdr-1.7.14/iconpatch.h vdr-1.7.14.ExtP_NG/iconpatch.h
+--- vdr-1.7.14/iconpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/iconpatch.h 2010-04-10 15:45:10.348737591 +0200
+@@ -0,0 +1,73 @@
++#ifdef USE_WAREAGLEICON
++/*
++ * iconpatch.h: Information of iconpatch
++ *
++ * Diese Datei ist die Übersichtsdatei für den Iconpatch.
++ * Hier werden kleine Infos abgelegt.
++ * Der Iconpatch ändert die Dateien:
++ * iconpatch.h
++ * menu.c
++ * recording.c
++ * fontosd.c
++ *
++ */
++
++// Iconpatch-Variablen - Anfang
++#define ICON_NUMBERSIGN "\x23"
++#define ICON_ASTERISK "\x2A"
++#define ICON_GREATER "\x3E"
++#define ICON_EXCLAM "\x21"
++#define ICON_PLUSMINUS "\xB1"
++
++#define ICON_RESUME "\x80"
++#define ICON_DVD "\x81"
++#define ICON_FOLDER "\x82"
++#define ICON_BLANK "\x83"
++#define ICON_CUTTING "\x84"
++#define ICON_MOVE_FILE "\x85"
++#define ICON_MOVE_FOLDER "\x86"
++#define ICON_BAR_START "\x87"
++#define ICON_BAR_FILLED "\x88"
++#define ICON_BAR_CLEAR "\x89"
++#define ICON_BAR_END "\x8A"
++#define ICON_REC "\x8B"
++#define ICON_CLOCK "\x8C"
++#define ICON_TV_CRYPTED "\x8D"
++#define ICON_RADIO "\x8E"
++#define ICON_TV "\x8F"
++#define ICON_NEW "\x90"
++#define ICON_ARROW "\x91"
++#define ICON_RUNNING "\x92"
++#define ICON_VPS "\x93"
++#define ICON_CLOCK_UH "\x94"
++#define ICON_CLOCK_LH "\x95"
++
++// UTF-8 Icons
++#define ICON_RESUME_UTF8 "\uE000"
++#define ICON_DVD_UTF8 "\uE001"
++#define ICON_FOLDER_UTF8 "\uE002"
++#define ICON_BLANK_UTF8 "\uE003"
++#define ICON_CUTTING_UTF8 "\uE004"
++#define ICON_MOVE_FILE_UTF8 "\uE005"
++#define ICON_MOVE_FOLDER_UTF8 "\uE006"
++#define ICON_BAR_START_UTF8 "\uE007"
++#define ICON_BAR_FILLED_UTF8 "\uE008"
++#define ICON_BAR_EMPTY_UTF8 "\uE009"
++#define ICON_BAR_CLOSE_UTF8 "\uE00A"
++#define ICON_REC_UTF8 "\uE00B"
++#define ICON_CLOCK_UTF8 "\uE00C"
++#define ICON_TV_CRYPTED_UTF8 "\uE00D"
++#define ICON_RADIO_UTF8 "\uE00E"
++#define ICON_TV_UTF8 "\uE00F"
++#define ICON_NEW_UTF8 "\uE010"
++#define ICON_ARROW_UTF8 "\uE011"
++#define ICON_RUNNING_UTF8 "\uE012"
++#define ICON_VPS_UTF8 "\uE013"
++#define ICON_CLOCK_UH_UTF8 "\uE014"
++#define ICON_CLOCK_LH_UTF8 "\uE015"
++
++// Iconpatch-Variablen - Ende
++
++bool IsLangUtf8(void);
++
++#endif /* WAREAGLEICON */
+diff -ruN vdr-1.7.14/lirc.c vdr-1.7.14.ExtP_NG/lirc.c
+--- vdr-1.7.14/lirc.c 2006-05-28 10:48:13.000000000 +0200
++++ vdr-1.7.14.ExtP_NG/lirc.c 2010-04-10 15:45:10.452737940 +0200
+@@ -12,6 +12,10 @@
+ #include "lirc.h"
+ #include <netinet/in.h>
+ #include <sys/socket.h>
++#ifdef USE_LIRCSETTINGS
++#include "config.h"
++#endif /* LIRCSETTINGS */
++
+
+ #define REPEATDELAY 350 // ms
+ #define REPEATFREQ 100 // ms
+@@ -94,7 +98,11 @@
+ continue;
+ }
+ if (count == 0) {
++#ifdef USE_LIRCSETTINGS
++ if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < (unsigned int)Setup.LircRepeatDelay)
++#else
+ if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < REPEATDELAY)
++#endif /* LIRCSETTINGS */
+ continue; // skip keys coming in too fast
+ if (repeat)
+ Put(LastKeyName, false, true);
+@@ -104,18 +112,34 @@
+ timeout = -1;
+ }
+ else {
++#ifdef USE_LIRCSETTINGS
++ if (LastTime.Elapsed() < (unsigned int)Setup.LircRepeatFreq)
++#else
+ if (LastTime.Elapsed() < REPEATFREQ)
++#endif /* LIRCSETTINGS */
+ continue; // repeat function kicks in after a short delay (after last key instead of first key)
++#ifdef USE_LIRCSETTINGS
++ if (FirstTime.Elapsed() < (unsigned int)Setup.LircRepeatDelay)
++#else
+ if (FirstTime.Elapsed() < REPEATDELAY)
++#endif /* LIRCSETTINGS */
+ continue; // skip keys coming in too fast (for count != 0 as well)
+ repeat = true;
++#ifdef USE_LIRCSETTINGS
++ timeout = Setup.LircRepeatDelay;
++#else
+ timeout = REPEATDELAY;
++#endif /* LIRCSETTINGS */
+ }
+ LastTime.Set();
+ Put(KeyName, repeat);
+ }
+ else if (repeat) { // the last one was a repeat, so let's generate a release
++#ifdef USE_LIRCSETTINGS
++ if (LastTime.Elapsed() >= (unsigned int)Setup.LircRepeatTimeout) {
++#else
+ if (LastTime.Elapsed() >= REPEATTIMEOUT) {
++#endif /* LIRCSETTINGS */
+ Put(LastKeyName, false, true);
+ repeat = false;
+ *LastKeyName = 0;
+diff -ruN vdr-1.7.14/mainmenuitemsprovider.h vdr-1.7.14.ExtP_NG/mainmenuitemsprovider.h
+--- vdr-1.7.14/mainmenuitemsprovider.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/mainmenuitemsprovider.h 2010-04-10 15:45:10.477763751 +0200
+@@ -0,0 +1,62 @@
++#ifdef USE_MENUORG
++/*
++ * vdr-menuorg - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
++ * details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __MAINMENUITEMSPROVIDER_H
++#define __MAINMENUITEMSPROVIDER_H
++
++#include <vector>
++
++class cOsdItem;
++class cOsdMenu;
++
++class IMenuItemDefinition
++{
++ public:
++ virtual ~IMenuItemDefinition() {};
++ virtual bool IsCustomOsdItem() = 0;
++ virtual bool IsPluginItem() = 0;
++ virtual bool IsSeparatorItem() = 0;
++ virtual cOsdItem* CustomOsdItem() = 0;
++ virtual const char* PluginMenuEntry() = 0;
++ virtual bool IsSelected() = 0;
++ virtual int PluginIndex() = 0;
++};
++
++typedef std::vector<IMenuItemDefinition*> MenuItemDefinitions;
++
++#define MENU_ITEMS_PROVIDER_SERVICE_ID "MenuOrgPatch-v0.4.2::MainMenuItemsProvider"
++
++class IMainMenuItemsProvider
++{
++ public:
++ virtual ~IMainMenuItemsProvider() {};
++ virtual bool IsCustomMenuAvailable() = 0;
++ virtual MenuItemDefinitions* MainMenuItems() = 0;
++ virtual void EnterRootMenu() = 0;
++ virtual void EnterSubMenu(cOsdItem* item) = 0;
++ virtual bool LeaveSubMenu() = 0;
++ virtual cOsdMenu* Execute(cOsdItem* item) = 0;
++};
++
++#endif //__MAINMENUITEMSPROVIDER_H
++#endif /* MENUORG */
+diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
+--- vdr-1.7.14/menu.c 2010-03-12 17:03:07.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/menu.c 2010-04-10 15:45:10.637743132 +0200
+@@ -8,6 +8,9 @@
+ */
+
+ #include "menu.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
+ #include <ctype.h>
+ #include <limits.h>
+ #include <math.h>
+@@ -31,6 +34,9 @@
+ #include "timers.h"
+ #include "transfer.h"
+ #include "videodir.h"
++#ifdef USE_MENUORG
++#include "menuorgpatch.h"
++#endif /* MENUORG */
+
+ #define MAXWAIT4EPGINFO 3 // seconds
+ #define MODETIMEOUT 3 // seconds
+@@ -203,6 +209,9 @@
+ public:
+ cMenuEditChannel(cChannel *Channel, bool New = false);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuEditChannel"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuEditChannel::cMenuEditChannel(cChannel *Channel, bool New)
+@@ -249,6 +258,9 @@
+ Add(new cMenuEditIntItem( tr("Tid"), &data.tid, 0));
+ Add(new cMenuEditIntItem( tr("Rid"), &data.rid, 0));
+ XXX*/
++#ifdef USE_CHANNELBIND
++ Add(new cMenuEditIntItem( tr("Rid"), &data.rid, 0)); // channel binding patch
++#endif /* CHANNELBIND */
+ // Parameters for specific types of sources:
+ sourceParam = SourceParams.Get(**cSource::ToString(data.source));
+ if (sourceParam) {
+@@ -349,6 +361,16 @@
+ if (!channel->GroupSep()) {
+ if (sortMode == csmProvider)
+ buffer = cString::sprintf("%d\t%s - %s", channel->Number(), channel->Provider(), channel->Name());
++#ifdef USE_WAREAGLEICON
++ else if (Setup.WarEagleIcons) {
++ if (channel->Vpid() == 1 || channel->Vpid() == 0)
++ buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_RADIO_UTF8 : ICON_RADIO, channel->Name());
++ else if (channel->Ca() == 0)
++ buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_TV_UTF8 : ICON_TV, channel->Name());
++ else
++ buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_TV_CRYPTED_UTF8 : ICON_TV_CRYPTED, channel->Name());
++ }
++#endif /* WAREAGLEICON */
+ else
+ buffer = cString::sprintf("%d\t%s", channel->Number(), channel->Name());
+ }
+@@ -379,6 +401,9 @@
+ cMenuChannels(void);
+ ~cMenuChannels();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuChannels"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuChannels::cMenuChannels(void)
+@@ -917,6 +942,17 @@
+ Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
+ Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
++#ifdef USE_PINPLUGIN
++ if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++ else {
++ char* buf = 0;
++ if (asprintf(&buf, "%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no")) > 0) {
++ Add(new cOsdItem(buf));
++ free(buf);
++ }
++ }
++
++#endif /* PINPLUGIN */
+ Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
+ SetFirstDayItem();
+ }
+@@ -1017,8 +1053,14 @@
+ class cMenuTimerItem : public cOsdItem {
+ private:
+ cTimer *timer;
++#ifdef USE_TIMERINFO
++ char diskStatus;
++#endif /* TIMERINFO */
+ public:
+ cMenuTimerItem(cTimer *Timer);
++#ifdef USE_TIMERINFO
++ void SetDiskStatus(char DiskStatus);
++#endif /* TIMERINFO */
+ virtual int Compare(const cListObject &ListObject) const;
+ virtual void Set(void);
+ cTimer *Timer(void) { return timer; }
+@@ -1027,6 +1069,9 @@
+ cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
+ {
+ timer = Timer;
++#ifdef USE_TIMERINFO
++ diskStatus = ' ';
++#endif /* TIMERINFO */
+ Set();
+ }
+
+@@ -1057,8 +1102,31 @@
+ File++;
+ else
+ File = timer->File();
++#ifdef USE_WAREAGLEICON
++#ifdef USE_TIMERINFO
++ cCharSetConv csc("ISO-8859-1", cCharSetConv::SystemCharacterTable());
++ char diskStatusString[2] = { diskStatus, 0 };
++ SetText(cString::sprintf("%s%s\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++ csc.Convert(diskStatusString),
++#else
++ SetText(cString::sprintf("%s\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#endif /* TIMERINFO */
++#else
++#ifdef USE_TIMERINFO
++ cCharSetConv csc("ISO-8859-1", cCharSetConv::SystemCharacterTable());
++ char diskStatusString[2] = { diskStatus, 0 };
++ SetText(cString::sprintf("%s%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++ csc.Convert(diskStatusString),
++#else
+ SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#endif /* TIMERINFO */
++#endif /* WAREAGLEICON */
++#ifdef USE_WAREAGLEICON
++ !(timer->HasFlags(tfActive)) ? " " : timer->FirstDay() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_ARROW_UTF8 : ICON_ARROW : "!" : timer->Recording() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_REC_UTF8 : ICON_REC : "#" : Setup.WarEagleIcons ? IsLangUtf8() ? ICON_CLOCK_UTF8 : ICON_CLOCK : ">",
++
++#else
+ !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
++#endif /* WAREAGLEICON */
+ timer->Channel()->Number(),
+ *name,
+ *name && **name ? " " : "",
+@@ -1070,6 +1138,58 @@
+ File));
+ }
+
++#ifdef USE_TIMERINFO
++void cMenuTimerItem::SetDiskStatus(char DiskStatus)
++{
++ diskStatus = DiskStatus;
++ Set();
++}
++
++// --- cTimerEntry -----------------------------------------------------------
++
++class cTimerEntry : public cListObject {
++private:
++ cMenuTimerItem *item;
++ const cTimer *timer;
++ time_t start;
++public:
++ cTimerEntry(cMenuTimerItem *item) : item(item), timer(item->Timer()), start(timer->StartTime()) {}
++ cTimerEntry(const cTimer *timer, time_t start) : item(NULL), timer(timer), start(start) {}
++ virtual int Compare(const cListObject &ListObject) const;
++ bool active(void) const { return timer->HasFlags(tfActive); }
++ time_t startTime(void) const { return start; }
++ int priority(void) const { return timer->Priority(); }
++ int duration(void) const;
++ bool repTimer(void) const { return !timer->IsSingleEvent(); }
++ bool isDummy(void) const { return item == NULL; }
++ const cTimer *Timer(void) const { return timer; }
++ void SetDiskStatus(char DiskStatus);
++ };
++
++int cTimerEntry::Compare(const cListObject &ListObject) const
++{
++ cTimerEntry *entry = (cTimerEntry *)&ListObject;
++ int r = startTime() - entry->startTime();
++ if (r == 0)
++ r = entry->priority() - priority();
++ return r;
++}
++
++int cTimerEntry::duration(void) const
++{
++ int dur = (timer->Stop() / 100 * 60 + timer->Stop() % 100) -
++ (timer->Start() / 100 * 60 + timer->Start() % 100);
++ if (dur < 0)
++ dur += 24 * 60;
++ return dur;
++}
++
++void cTimerEntry::SetDiskStatus(char DiskStatus)
++{
++ if (item)
++ item->SetDiskStatus(DiskStatus);
++}
++#endif /* TIMERINFO */
+ // --- cMenuTimers -----------------------------------------------------------
+
+ class cMenuTimers : public cOsdMenu {
+@@ -1082,14 +1202,25 @@
+ eOSState Info(void);
+ cTimer *CurrentTimer(void);
+ void SetHelpKeys(void);
++#ifdef USE_TIMERINFO
++ void ActualiseDiskStatus(void);
++ bool actualiseDiskStatus;
++#endif /* TIMERINFO */
+ public:
+ cMenuTimers(void);
+ virtual ~cMenuTimers();
++#ifdef USE_TIMERINFO
++ virtual void Display(void);
++#endif /* TIMERINFO */
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+ cMenuTimers::cMenuTimers(void)
++#ifdef USE_TIMERINFO
++:cOsdMenu(tr("Timers"), 3, CHNUMWIDTH, 10, 6, 6)
++#else
+ :cOsdMenu(tr("Timers"), 2, CHNUMWIDTH, 10, 6, 6)
++#endif /* TIMERINFO */
+ {
+ helpKeys = -1;
+ for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
+@@ -1100,6 +1231,9 @@
+ SetCurrent(First());
+ SetHelpKeys();
+ Timers.IncBeingEdited();
++#ifdef USE_TIMERINFO
++ actualiseDiskStatus = true;
++#endif /* TIMERINFO */
+ }
+
+ cMenuTimers::~cMenuTimers()
+@@ -1138,7 +1272,11 @@
+ timer->OnOff();
+ timer->SetEventFromSchedule();
+ RefreshCurrent();
++#ifdef USE_TIMERINFO
++ Display();
++#else
+ DisplayCurrent(true);
++#endif /* TIMERINFO */
+ if (timer->FirstDay())
+ isyslog("timer %s first day set to %s", *timer->ToDescr(), *timer->PrintFirstDay());
+ else
+@@ -1197,6 +1335,68 @@
+ return osContinue;
+ }
+
++#ifdef USE_TIMERINFO
++void cMenuTimers::ActualiseDiskStatus(void)
++{
++ if (!actualiseDiskStatus || !Count())
++ return;
++
++ // compute free disk space
++ int freeMB, freeMinutes, runshortMinutes;
++ VideoDiskSpace(&freeMB);
++ freeMinutes = int(double(freeMB) * 1.1 / MB_PER_MINUTE); // overestimate by 10 percent
++ runshortMinutes = freeMinutes / 5; // 20 Percent
++
++ // fill entries list
++ cTimerEntry *entry;
++ cList<cTimerEntry> entries;
++ for (cOsdItem *item = First(); item; item = Next(item))
++ entries.Add(new cTimerEntry((cMenuTimerItem *)item));
++
++ // search last start time
++ time_t last = 0;
++ for (entry = entries.First(); entry; entry = entries.Next(entry))
++ last = max(entry->startTime(), last);
++
++ // add entries for repeating timers
++ for (entry = entries.First(); entry; entry = entries.Next(entry))
++ if (entry->repTimer() && !entry->isDummy())
++ for (time_t start = cTimer::IncDay(entry->startTime(), 1);
++ start <= last;
++ start = cTimer::IncDay(start, 1))
++ if (entry->Timer()->DayMatches(start))
++ entries.Add(new cTimerEntry(entry->Timer(), start));
++
++ // set the disk-status
++ entries.Sort();
++ for (entry = entries.First(); entry; entry = entries.Next(entry)) {
++ char status = ' ';
++ if (entry->active()) {
++ freeMinutes -= entry->duration();
++ status = freeMinutes > runshortMinutes ? '+' : freeMinutes > 0 ? 177 /* +/- */ : '-';
++ }
++ entry->SetDiskStatus(status);
++#ifdef DEBUG_TIMER_INFO
++ dsyslog("timer-info: %c | %d | %s | %s | %3d | %+5d -> %+5d",
++ status,
++ entry->startTime(),
++ entry->active() ? "aktiv " : "n.akt.",
++ entry->repTimer() ? entry->isDummy() ? " dummy " : "mehrmalig" : "einmalig ",
++ entry->duration(),
++ entry->active() ? freeMinutes + entry->duration() : freeMinutes,
++ freeMinutes);
++#endif
++ }
++
++ actualiseDiskStatus = false;
++}
++
++void cMenuTimers::Display(void)
++{
++ ActualiseDiskStatus();
++ cOsdMenu::Display();
++}
++#endif /* TIMERINFO */
+ eOSState cMenuTimers::ProcessKey(eKeys Key)
+ {
+ int TimerNumber = HasSubMenu() ? Count() : -1;
+@@ -1205,18 +1405,36 @@
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk: return Edit();
++#ifdef USE_TIMERINFO
++ case kRed: actualiseDiskStatus = true;
++ state = OnOff(); break; // must go through SetHelpKeys()!
++#else
+ case kRed: state = OnOff(); break; // must go through SetHelpKeys()!
++#endif /* TIMERINFO */
+ case kGreen: return New();
++#ifdef USE_TIMERINFO
++ case kYellow: actualiseDiskStatus = true;
++ state = Delete(); break;
++#else
+ case kYellow: state = Delete(); break;
++#endif /* TIMERINFO */
+ case kInfo:
+ case kBlue: return Info();
+ break;
+ default: break;
+ }
+ }
++#ifdef USE_TIMERINFO
++ if (TimerNumber >= 0 && !HasSubMenu()) {
++ if (Timers.Get(TimerNumber)) // a newly created timer was confirmed with Ok
++ Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
++ Sort();
++ actualiseDiskStatus = true;
++#else
+ if (TimerNumber >= 0 && !HasSubMenu() && Timers.Get(TimerNumber)) {
+ // a newly created timer was confirmed with Ok
+ Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
++#endif /* TIMERINFO */
+ Display();
+ }
+ if (Key != kNone)
+@@ -1246,6 +1464,9 @@
+ {
+ cOsdMenu::Display();
+ DisplayMenu()->SetEvent(event);
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdSetEvent(event);
++#endif /* GRAPHTFT */
+ if (event->Description())
+ cStatus::MsgOsdTextItem(event->Description());
+ }
+@@ -1293,7 +1514,12 @@
+ const cChannel *channel;
+ bool withDate;
+ int timerMatch;
++#ifdef USE_LIEMIEXT
++ bool withBar;
++ cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false, bool WithBar = false);
++#else
+ cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false);
++#endif /* LIEMIEXT */
+ static void SetSortMode(eScheduleSortMode SortMode) { sortMode = SortMode; }
+ static void IncSortMode(void) { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); }
+ static eScheduleSortMode SortMode(void) { return sortMode; }
+@@ -1303,12 +1529,19 @@
+
+ cMenuScheduleItem::eScheduleSortMode cMenuScheduleItem::sortMode = ssmAllThis;
+
++#ifdef USE_LIEMIEXT
++cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate, bool WithBar)
++#else
+ cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate)
++#endif /* LIEMIEXT */
+ {
+ event = Event;
+ channel = Channel;
+ withDate = WithDate;
+ timerMatch = tmNone;
++#ifdef USE_LIEMIEXT
++ withBar = WithBar;
++#endif /* LIEMIEXT */
+ Update(true);
+ }
+
+@@ -1323,7 +1556,30 @@
+ return r;
+ }
+
++#ifdef USE_WAREAGLEICON
++static const char *TimerMatchChars[9] =
++{
++ " ", "t", "T",
++ ICON_BLANK, ICON_CLOCK_UH, ICON_CLOCK,
++ ICON_BLANK_UTF8, ICON_CLOCK_UH_UTF8, ICON_CLOCK_UTF8
++};
++#else
+ static const char *TimerMatchChars = " tT";
++#endif /* WAREAGLEICON */
++
++#ifdef USE_LIEMIEXT
++static const char * const ProgressBar[7] =
++{
++ "[ ]",
++ "[| ]",
++ "[|| ]",
++ "[||| ]",
++ "[|||| ]",
++ "[||||| ]",
++ "[||||||]"
++};
++
++#endif /* LIEMIEXT */
+
+ bool cMenuScheduleItem::Update(bool Force)
+ {
+@@ -1332,17 +1588,54 @@
+ Timers.GetMatch(event, &timerMatch);
+ if (Force || timerMatch != OldTimerMatch) {
+ cString buffer;
++#ifdef USE_WAREAGLEICON
++ const char *t = Setup.WarEagleIcons ? IsLangUtf8() ? TimerMatchChars[timerMatch+6] : TimerMatchChars[timerMatch+3] : TimerMatchChars[timerMatch];
++ const char *v = event->Vps() && (event->Vps() - event->StartTime()) ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_VPS_UTF8 : ICON_VPS : "V" : " ";
++ const char *r = event->SeenWithin(30) && event->IsRunning() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_RUNNING_UTF8 : ICON_RUNNING : "*" : " ";
++#else
+ char t = TimerMatchChars[timerMatch];
+ char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
+ char r = event->SeenWithin(30) && event->IsRunning() ? '*' : ' ';
++#endif /* WAREAGLEICON */
+ const char *csn = channel ? channel->ShortName(true) : NULL;
+ cString eds = event->GetDateString();
+ if (channel && withDate)
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%d\t%.*s\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#else
+ buffer = cString::sprintf("%d\t%.*s\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
+ else if (channel)
++#ifdef USE_LIEMIEXT
++ if (Setup.ShowProgressBar && withBar) {
++ int progress = (int)roundf( (float)(time(NULL) - event->StartTime()) / (float)(event->Duration()) * 6.0 );
++ if (progress < 0) progress = 0;
++ else if (progress > 6) progress = 6;
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), ProgressBar[progress], t, v, r, event->Title());
++#else
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), ProgressBar[progress], t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++ }
++ else
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#else
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++#else
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#else
+ buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++#endif /* LIEMIEXT */
+ else
++#ifdef USE_WAREAGLEICON
++ buffer = cString::sprintf("%.*s\t%s\t%s%s%s\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#else
+ buffer = cString::sprintf("%.*s\t%s\t%c%c%c\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
+ SetText(buffer);
+ result = true;
+ }
+@@ -1368,13 +1661,21 @@
+ static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
+ static const cEvent *ScheduleEvent(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return now ? "MenuWhatsOnNow" : "MenuWhatsOnNext"; }
++ virtual void Display(void);
++#endif /* GRAPHTFT */
+ };
+
+ int cMenuWhatsOn::currentChannel = 0;
+ const cEvent *cMenuWhatsOn::scheduleEvent = NULL;
+
+ cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr)
++#ifdef USE_LIEMIEXT
++:cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6, 4, 4)
++#else
+ :cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6, 4)
++#endif /* LIEMIEXT */
+ {
+ now = Now;
+ helpKeys = -1;
+@@ -1386,7 +1687,11 @@
+ if (Schedule) {
+ const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
+ if (Event)
++#ifdef USE_LIEMIEXT
++ Add(new cMenuScheduleItem(Event, Channel, false, Now), Channel->Number() == CurrentChannelNr);
++#else
+ Add(new cMenuScheduleItem(Event, Channel), Channel->Number() == CurrentChannelNr);
++#endif /* LIEMIEXT */
+ }
+ }
+ }
+@@ -1395,6 +1700,20 @@
+ SetHelpKeys();
+ }
+
++#ifdef USE_GRAPHTFT
++void cMenuWhatsOn::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0) {
++ int ni = 0;
++ for (cOsdItem *item = First(); item; item = Next(item)) {
++ cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++ }
++ }
++}
++#endif /* GRAPHTFT */
++
+ bool cMenuWhatsOn::Update(void)
+ {
+ bool result = false;
+@@ -1535,6 +1854,10 @@
+ cMenuSchedule(void);
+ virtual ~cMenuSchedule();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSchedule"; }
++ virtual void Display(void);
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSchedule::cMenuSchedule(void)
+@@ -1560,6 +1883,20 @@
+ cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared
+ }
+
++#ifdef USE_GRAPHTFT
++void cMenuSchedule::Display(void)
++{
++ cOsdMenu::Display();
++
++ if (Count() > 0) {
++ int ni = 0;
++ for (cOsdItem *item = First(); item; item = Next(item)) {
++ cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++ }
++ }
++}
++#endif /* GRAPHTFT */
++
+ void cMenuSchedule::PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel)
+ {
+ Clear();
+@@ -1915,6 +2252,9 @@
+ cMenuCam(cCamSlot *CamSlot);
+ virtual ~cMenuCam();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuCam"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuCam::cMenuCam(cCamSlot *CamSlot)
+@@ -2094,6 +2434,9 @@
+ cMenuRecording(const cRecording *Recording, bool WithButtons = false);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuRecording"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons)
+@@ -2109,6 +2452,9 @@
+ {
+ cOsdMenu::Display();
+ DisplayMenu()->SetRecording(recording);
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdSetRecording(recording);
++#endif /* GRAPHTFT */
+ if (recording->Info()->Description())
+ cStatus::MsgOsdTextItem(recording->Info()->Description());
+ }
+@@ -2169,7 +2515,11 @@
+ fileName = strdup(Recording->FileName());
+ name = NULL;
+ totalEntries = newEntries = 0;
++#ifdef USE_LIEMIEXT
++ SetText(Recording->Title('\t', true, Level, false));
++#else
+ SetText(Recording->Title('\t', true, Level));
++#endif /* LIEMIEXT */
+ if (*Text() == '\t')
+ name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t'
+ }
+@@ -2185,13 +2535,183 @@
+ totalEntries++;
+ if (New)
+ newEntries++;
++#ifdef USE_LIEMIEXT
++ switch (Setup.ShowRecTime + Setup.ShowRecDate + Setup.ShowRecLength) {
++ case 0:
++ SetText(cString::sprintf("%s", name));
++ break;
++ case 1:
++ SetText(cString::sprintf("%d\t%s", totalEntries, name));
++ break;
++ case 2:
++ default:
++ SetText(cString::sprintf("%d\t%d\t%s", totalEntries, newEntries, name));
++ break;
++ case 3:
++ SetText(cString::sprintf("%d\t%d\t\t%s", totalEntries, newEntries, name));
++ break;
++ }
++}
++
++// --- cMenuRenameRecording --------------------------------------------------
++
++class cMenuRenameRecording : public cOsdMenu {
++private:
++ char name[MaxFileName];
++ cMenuEditStrItem *file;
++ cOsdItem *marksItem, *resumeItem;
++ bool isResume, isMarks;
++ cRecording *recording;
++ void SetHelpKeys(void);
++ eOSState SetFolder(void);
++public:
++ cMenuRenameRecording(cRecording *Recording);
++ virtual eOSState ProcessKey(eKeys Key);
++};
++
++cMenuRenameRecording::cMenuRenameRecording(cRecording *Recording)
++:cOsdMenu(tr("Rename recording"), 12)
++{
++ cMarks marks;
++
++ file = NULL;
++ recording = Recording;
++
++ if (recording) {
++ Utf8Strn0Cpy(name, recording->Name(), sizeof(name));
++ Add(file = new cMenuEditStrItem(tr("File"), name, sizeof(name)));
++
++ Add(new cOsdItem("", osUnknown, false));
++
++ Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Date"), *DayDateTime(recording->start)), osUnknown, false));
++
++ cChannel *channel = Channels.GetByChannelID(((cRecordingInfo *)recording->Info())->ChannelID());
++ if (channel)
++ Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Channel"), *ChannelString(channel, 0)), osUnknown, false));
++
++ int recLen = cIndexFile::Length(recording->FileName(), recording->IsPesRecording());
++ if (recLen >= 0)
++ Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Length"), *IndexToHMSF(recLen, false, recording->FramesPerSecond())), osUnknown, false));
++ else
++ recLen = 0;
++
++ int dirSize = DirSizeMB(recording->FileName());
++ double seconds = recLen / recording->FramesPerSecond();
++ cString bitRate = seconds ? cString::sprintf(" (%.2f MBit/s)", 8.0 * dirSize / seconds) : cString("");
++ Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Format"), recording->IsPesRecording() ? tr("PES") : tr("TS")), osUnknown, false));
++ Add(new cOsdItem((dirSize > 9999) ? cString::sprintf("%s:\t%.2f GB%s", tr("Size"), dirSize / 1024.0, *bitRate) : cString::sprintf("%s:\t%d MB%s", tr("Size"), dirSize, *bitRate), osUnknown, false));
++
++ Add(new cOsdItem("", osUnknown, false));
++
++ isMarks = marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()) && marks.Count();
++ marksItem = new cOsdItem(tr("Delete marks information?"), osUser1, isMarks);
++ Add(marksItem);
++
++ cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
++ isResume = (ResumeFile.Read() != -1);
++ resumeItem = new cOsdItem(tr("Delete resume information?"), osUser2, isResume);
++ Add(resumeItem);
++ }
++
++ SetHelpKeys();
++}
++
++void cMenuRenameRecording::SetHelpKeys(void)
++{
++ SetHelp(tr("Button$Folder"));
++}
++
++eOSState cMenuRenameRecording::SetFolder(void)
++{
++ cMenuFolder *mf = (cMenuFolder *)SubMenu();
++ if (mf) {
++ cString Folder = mf->GetFolder();
++ char *p = strrchr(name, FOLDERDELIMCHAR);
++ if (p)
++ p++;
++ else
++ p = name;
++ if (!isempty(*Folder))
++ strn0cpy(name, cString::sprintf("%s%c%s", *Folder, FOLDERDELIMCHAR, p), sizeof(name));
++ else if (p != name)
++ memmove(name, p, strlen(p) + 1);
++ SetCurrent(file);
++ Display();
++ }
++ return CloseSubMenu();
++}
++
++eOSState cMenuRenameRecording::ProcessKey(eKeys Key)
++{
++ eOSState state = cOsdMenu::ProcessKey(Key);
++
++ if (state == osUnknown) {
++ switch (Key) {
++ case kOk:
++ if (recording->Rename(name)) {
++ Recordings.ChangeState();
++ Recordings.TouchUpdate();
++ return osRecordings;
++ }
++ else
++ Skins.Message(mtError, tr("Error while accessing recording!"));
++ break;
++ case kRed:
++ return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, name));
++ break;
++ default:
++ break;
++ }
++ if (Key != kNone)
++ SetHelpKeys();
++ return osContinue;
++ }
++ else if (state == osEnd && HasSubMenu())
++ state = SetFolder();
++ else if (state == osUser1) {
++ if (isMarks && Interface->Confirm(tr("Delete marks information?"))) {
++ cMarks marks;
++ marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
++ cMark *mark = marks.First();
++ while (mark) {
++ cMark *nextmark = marks.Next(mark);
++ marks.Del(mark);
++ mark = nextmark;
++ }
++ marks.Save();
++ isMarks = false;
++ marksItem->SetSelectable(isMarks);
++ SetCurrent(First());
++ Display();
++ }
++ return osContinue;
++ }
++ else if (state == osUser2) {
++ if (isResume && Interface->Confirm(tr("Delete resume information?"))) {
++ cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
++ ResumeFile.Delete();
++ isResume = false;
++ resumeItem->SetSelectable(isResume);
++ SetCurrent(First());
++ Display();
++ }
++ return osContinue;
++ }
++
++ return state;
++#else
+ SetText(cString::sprintf("%d\t%d\t%s", totalEntries, newEntries, name));
++#endif /* LIEMIEXT */
+ }
+
+ // --- cMenuRecordings -------------------------------------------------------
+
+ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
++#ifdef USE_LIEMIEXT
++:cOsdMenu(Base ? Base : tr("Recordings"), 9, 7, 7)
++#else
+ :cOsdMenu(Base ? Base : tr("Recordings"), 9, 7)
++#endif /* LIEMIEXT */
+ {
+ base = Base ? strdup(Base) : NULL;
+ level = Setup.RecordingDirs ? Level : -1;
+@@ -2269,7 +2789,13 @@
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR)) {
+ cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
++#ifdef USE_PINPLUGIN
++ if ((*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0))
++ && (!cStatus::MsgReplayProtected(GetRecording(Item), Item->Name(), base,
++ Item->IsDirectory(), true))) {
++#else
+ if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
++#endif /* PINPLUGIN */
+ Add(Item);
+ LastItem = Item;
+ free(LastItemText);
+@@ -2319,6 +2845,11 @@
+ {
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri) {
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgReplayProtected(GetRecording(ri), ri->Name(), base,
++ ri->IsDirectory()) == true)
++ return osContinue;
++#endif /* PINPLUGIN */
+ if (ri->IsDirectory())
+ Open();
+ else {
+@@ -2426,12 +2957,34 @@
+ return osContinue;
+ }
+
++#ifdef USE_LIEMIEXT
++eOSState cMenuRecordings::Rename(void)
++{
++ if (HasSubMenu() || Count() == 0)
++ return osContinue;
++ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
++ if (ri && !ri->IsDirectory()) {
++ cRecording *recording = GetRecording(ri);
++ if (recording)
++ return AddSubMenu(new cMenuRenameRecording(recording));
++ }
++ return osContinue;
++}
++#endif /* LIEMIEXT */
++
+ eOSState cMenuRecordings::ProcessKey(eKeys Key)
+ {
+ bool HadSubMenu = HasSubMenu();
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
++#ifdef USE_SORTRECORDS
++ const char *RecordingsSortModeTexts[MAXSORTMODES];
++ RecordingsSortModeTexts[0] = tr("main dir alphabetically, subdirs flexible");
++ RecordingsSortModeTexts[1] = tr("main dir by date, subdirs flexible");
++ RecordingsSortModeTexts[2] = tr("all alphabetically");
++ RecordingsSortModeTexts[3] = tr("all by date");
++#endif /* SORTRECORDS */
+ switch (Key) {
+ case kPlay:
+ case kOk: return Play();
+@@ -2440,7 +2993,26 @@
+ case kYellow: return Delete();
+ case kInfo:
+ case kBlue: return Info();
++#ifdef USE_SORTRECORDS
++ case k0: Setup.RecordingsSortMode = ++Setup.RecordingsSortMode % MAXSORTMODES;
++ Set(true);
++ Skins.Message(mtStatus, cString::sprintf("%s %d: %s", tr("Sorting"), Setup.RecordingsSortMode, RecordingsSortModeTexts[Setup.RecordingsSortMode]));
++ return osContinue;
++ case k1...k7: return Commands(Key);
++ case k8: return Rename();
++ case k9: Recordings.ToggleSortOrder();
++ Set(true);
++ return osContinue;
++#elif defined (USE_LIEMIEXT)
++ case k0: DirOrderState = !DirOrderState;
++ Set(true);
++ return osContinue;
++ case k8: return Rename();
++ case k9:
++ case k1...k7: return Commands(Key);
++#else
+ case k1...k9: return Commands(Key);
++#endif /* LIEMIEXT & SORTRECORDS */
+ case kNone: if (Recordings.StateChanged(recordingsState))
+ Set(true);
+ break;
+@@ -2505,6 +3077,9 @@
+ cMenuSetupOSD(void);
+ virtual ~cMenuSetupOSD();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupOsd"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupOSD::cMenuSetupOSD(void)
+@@ -2546,6 +3121,9 @@
+ Add(new cMenuEditStraItem(tr("Setup.OSD$Skin"), &skinIndex, numSkins, skinDescriptions));
+ if (themes.NumThemes())
+ Add(new cMenuEditStraItem(tr("Setup.OSD$Theme"), &themeIndex, themes.NumThemes(), themes.Descriptions()));
++#ifdef USE_WAREAGLEICON
++ Add(new cMenuEditBoolItem(tr("Setup.OSD$WarEagle icons"), &data.WarEagleIcons));
++#endif /* WAREAGLEICON */
+ Add(new cMenuEditPrcItem( tr("Setup.OSD$Left (%)"), &data.OSDLeftP, 0.0, 0.5));
+ Add(new cMenuEditPrcItem( tr("Setup.OSD$Top (%)"), &data.OSDTopP, 0.0, 0.5));
+ Add(new cMenuEditPrcItem( tr("Setup.OSD$Width (%)"), &data.OSDWidthP, 0.5, 1.0));
+@@ -2568,6 +3146,12 @@
+ Add(new cMenuEditBoolItem(tr("Setup.OSD$Menu key closes"), &data.MenuKeyCloses));
+ Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"), &data.RecordingDirs));
+ Add(new cMenuEditBoolItem(tr("Setup.OSD$Folders in timer menu"), &data.FoldersInTimerMenu));
++#ifdef USE_LIEMIEXT
++ Add(new cMenuEditBoolItem(tr("Setup.OSD$Main menu command position"), &data.MenuCmdPosition, tr("bottom"), tr("top")));
++#endif /* LIEMIEXT */
++#ifdef USE_VALIDINPUT
++ Add(new cMenuEditBoolItem(tr("Setup.OSD$Show valid input"), &data.ShowValidInput));
++#endif /* VALIDINPUT */
+ SetCurrent(Get(current));
+ Display();
+ }
+@@ -2639,12 +3223,18 @@
+
+ class cMenuSetupEPG : public cMenuSetupBase {
+ private:
++#ifdef USE_NOEPG
++ const char *noEPGModes[2];
++#endif /* NOEPG */
+ int originalNumLanguages;
+ int numLanguages;
+ void Setup(void);
+ public:
+ cMenuSetupEPG(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupEpg"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupEPG::cMenuSetupEPG(void)
+@@ -2661,11 +3251,19 @@
+ {
+ int current = Current();
+
++#ifdef USE_NOEPG
++ noEPGModes[0] = tr("blacklist");
++ noEPGModes[1] = tr("whitelist");
++#endif /* NOEPG */
++
+ 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 cMenuEditIntItem( tr("Setup.EPG$EPG linger time (min)"), &data.EPGLinger, 0));
++#ifdef USE_LIEMIEXT
++ Add(new cMenuEditBoolItem(tr("Setup.EPG$Show progress bar"), &data.ShowProgressBar));
++#endif /* LIEMIEXT */
+ Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"), &data.SetSystemTime));
+ if (data.SetSystemTime)
+ Add(new cMenuEditTranItem(tr("Setup.EPG$Use time from transponder"), &data.TimeTransponder, &data.TimeSource));
+@@ -2674,6 +3272,15 @@
+ for (int i = 0; i < numLanguages; i++)
+ // TRANSLATORS: note the singular!
+ Add(new cMenuEditStraItem(tr("Setup.EPG$Preferred language"), &data.EPGLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
++#ifdef USE_DDEPGENTRY
++ Add(new cMenuEditIntItem(tr("Setup.EPG$Period for double EPG search(min)"), &data.DoubleEpgTimeDelta));
++ Add(new cMenuEditBoolItem(tr("Setup.EPG$extern double Epg entry"), &data.DoubleEpgAction, "adjust", "delete"));
++ Add(new cMenuEditBoolItem(tr("Setup.EPG$Mix intern and extern EPG"), &data.MixEpgAction));
++ Add(new cMenuEditBoolItem(tr("Setup.EPG$Disable running VPS event"), &data.DisableVPS));
++#endif /* DDEPGENTRY */
++#ifdef USE_NOEPG
++ Add(new cMenuEditStraItem(tr("Setup.EPG$Mode of noEPG-Patch"), &data.noEPGMode, 2, noEPGModes));
++#endif /* NOEPG */
+
+ SetCurrent(Get(current));
+ Display();
+@@ -2740,6 +3347,9 @@
+ public:
+ cMenuSetupDVB(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupDvb"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupDVB::cMenuSetupDVB(void)
+@@ -2770,12 +3380,18 @@
+
+ Clear();
+
++#ifdef USE_CHANNELPROVIDE
++ Add(new cMenuEditBoolItem(tr("Setup.DVB$Use DVB receivers"), &data.LocalChannelProvide));
++#endif /* CHANNELPROVIDE */
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
+ Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"), &data.VideoFormat, "4:3", "16:9"));
+ if (data.VideoFormat == 0)
+ Add(new cMenuEditStraItem(tr("Setup.DVB$Video display format"), &data.VideoDisplayFormat, 3, videoDisplayFormatTexts));
+ Add(new cMenuEditBoolItem(tr("Setup.DVB$Use Dolby Digital"), &data.UseDolbyDigital));
+ Add(new cMenuEditStraItem(tr("Setup.DVB$Update channels"), &data.UpdateChannels, 6, updateChannelsTexts));
++#ifdef USE_CHANNELBIND
++ Add(new cMenuEditBoolItem(tr("Setup.DVB$channel binding by Rid"),&data.ChannelBindingByRid));
++#endif /* CHANNELBIND */
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Audio languages"), &numAudioLanguages, 0, I18nLanguages()->Size()));
+ for (int i = 0; i < numAudioLanguages; i++)
+ Add(new cMenuEditStraItem(tr("Setup.DVB$Audio language"), &data.AudioLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
+@@ -2788,6 +3404,9 @@
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle foreground transparency"), &data.SubtitleFgTransparency, 0, 9));
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle background transparency"), &data.SubtitleBgTransparency, 0, 10));
+ }
++#ifdef USE_TTXTSUBS
++ Add(new cMenuEditBoolItem(tr("Setup.DVB$Enable teletext support"), &data.SupportTeletext));
++#endif /* TTXTSUBS */
+
+ SetCurrent(Get(current));
+ Display();
+@@ -2869,6 +3488,9 @@
+ public:
+ cMenuSetupLNB(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupLnb"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupLNB::cMenuSetupLNB(void)
+@@ -2883,6 +3505,23 @@
+
+ Clear();
+
++#ifdef USE_LNBSHARE
++ int numSatDevices = 0;
++ for (int i = 0; i < cDevice::NumDevices(); i++) {
++ if (cDevice::GetDevice(i)->ProvidesSource(cSource::stSat)) numSatDevices++;
++ }
++ if (numSatDevices > 1) {
++ char tmp[40];
++ for (int i = 1; i <= cDevice::NumDevices(); i++) {
++ if (cDevice::GetDevice(i - 1)->ProvidesSource(cSource::stSat)) {
++ snprintf( tmp, 40, tr("Setup.LNB$DVB device %d uses LNB No."), i);
++ Add(new cMenuEditIntItem( tmp, &data.CardUsesLNBnr[i - 1], 1, numSatDevices ));
++ }
++ }
++ }
++ Add(new cMenuEditBoolItem(tr("Setup.LNB$Log LNB usage"), &data.VerboseLNBlog));
++#endif /* LNBSHARE */
++
+ Add(new cMenuEditBoolItem(tr("Setup.LNB$Use DiSEqC"), &data.DiSEqC));
+ if (!data.DiSEqC) {
+ Add(new cMenuEditIntItem( tr("Setup.LNB$SLOF (MHz)"), &data.LnbSLOF));
+@@ -2899,6 +3538,10 @@
+ int oldDiSEqC = data.DiSEqC;
+ eOSState state = cMenuSetupBase::ProcessKey(Key);
+
++#ifdef USE_LNBSHARE
++ if (Key == kOk) cDevice::SetLNBnr();
++#endif /* LNBSHARE */
++
+ if (Key != kNone && data.DiSEqC != oldDiSEqC)
+ Setup();
+ return state;
+@@ -2949,6 +3592,9 @@
+ public:
+ cMenuSetupCAM(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupCam"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupCAM::cMenuSetupCAM(void)
+@@ -3025,12 +3671,58 @@
+ private:
+ const char *pauseKeyHandlingTexts[3];
+ const char *delTimeshiftRecTexts[3];
++#ifdef USE_SORTRECORDS
++ const char *RecordingsSortModeTexts[MAXSORTMODES];
++#endif /* SORTRECORDS */
++#ifdef USE_DVLVIDPREFER
++ void Set(void);
++ int tmpNVidPrefer,
++ tmpUseVidPrefer;
++#endif /* DVLVIDPREFER */
+ public:
+ cMenuSetupRecord(void);
++#ifdef USE_DVLVIDPREFER
++ eOSState ProcessKey(eKeys key);
++#endif /* DVLVIDPREFER */
+ };
+
+ cMenuSetupRecord::cMenuSetupRecord(void)
+ {
++#ifdef USE_DVLVIDPREFER
++ Set();
++}
++
++eOSState cMenuSetupRecord::ProcessKey(eKeys key)
++{
++ eOSState s = cMenuSetupBase::ProcessKey(key);;
++
++ if (key != kNone) {
++ if (tmpNVidPrefer != data.nVidPrefer || tmpUseVidPrefer != data.UseVidPrefer) {
++ int cur = Current();
++
++ tmpNVidPrefer = data.nVidPrefer;
++ tmpUseVidPrefer = data.UseVidPrefer;
++
++ Clear();
++ Set();
++ SetCurrent(Get(cur));
++ Display();
++ cMenuSetupBase::ProcessKey(kNone);
++ return osContinue;
++ }
++ }
++ return s;
++}
++
++void cMenuSetupRecord::Set(void)
++{
++#endif /* DVLVIDPREFER */
++#ifdef USE_SORTRECORDS
++ RecordingsSortModeTexts[0] = tr("main dir alphabetically, subdirs flexible");
++ RecordingsSortModeTexts[1] = tr("main dir by date, subdirs flexible");
++ RecordingsSortModeTexts[2] = tr("all alphabetically");
++ RecordingsSortModeTexts[3] = tr("all by date");
++#endif /* SORTRECORDS */
+ pauseKeyHandlingTexts[0] = tr("do not pause live video");
+ pauseKeyHandlingTexts[1] = tr("confirm pause live video");
+ pauseKeyHandlingTexts[2] = tr("pause live video");
+@@ -3046,14 +3738,49 @@
+ Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"), &data.PausePriority, 0, MAXPRIORITY));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"), &data.PauseLifetime, 0, MAXLIFETIME));
++#ifdef USE_DVLVIDPREFER
++ tmpNVidPrefer = data.nVidPrefer;
++ tmpUseVidPrefer = data.UseVidPrefer;
++
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Video directory policy"), &data.UseVidPrefer));
++ if (data.UseVidPrefer != 0) {
++ char tmp[ 64 ];
++ Add(new cMenuEditIntItem(tr("Setup.Recording$Number of video directories"), &data.nVidPrefer, 1, DVLVIDPREFER_MAX));
++ for (int zz = 0; zz < data.nVidPrefer; zz++) {
++ sprintf(tmp, tr("Setup.Recording$Video %d priority"), zz);
++ Add(new cMenuEditIntItem(tmp, &data.VidPreferPrio[ zz ], 0, 99));
++ sprintf(tmp, tr("Setup.Recording$Video %d min. free MB"), zz);
++ Add(new cMenuEditIntItem(tmp, &data.VidPreferSize[ zz ], -1, 99999));
++ }
++ }
++#endif /* DVLVIDPREFER */
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"), &data.UseSubtitle));
++#ifdef USE_DVLFRIENDLYFNAMES
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Friendly filenames"), &data.UseFriendlyFNames));
++#endif /* DVLFRIENDLYFNAMES */
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Use VPS"), &data.UseVps));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$VPS margin (s)"), &data.VpsMargin, 0));
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Mark instant recording"), &data.MarkInstantRecord));
+ Add(new cMenuEditStrItem( tr("Setup.Recording$Name instant recording"), data.NameInstantRecord, sizeof(data.NameInstantRecord)));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"), &data.InstantRecordTime, 1, MAXINSTANTRECTIME));
+ Add(new cMenuEditIntItem( tr("Setup.Recording$Max. video file size (MB)"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZETS));
++#ifdef USE_HARDLINKCUTTER
++ Add(new cMenuEditIntItem( tr("Setup.Recording$Max. recording size (GB)"), &data.MaxRecordingSize, MINRECORDINGSIZE, MAXRECORDINGSIZE));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Hard Link Cutter"), &data.HardLinkCutter));
++#endif /* HARDLINKCUTTER */
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"), &data.SplitEditedFiles));
++#ifdef USE_LIEMIEXT
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Show date"), &data.ShowRecDate));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Show time"), &data.ShowRecTime));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Show length"), &data.ShowRecLength));
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++ Add(new cMenuEditStraItem(tr("Setup.Recording$Sort recordings by"), &data.RecordingsSortMode, MAXSORTMODES, RecordingsSortModeTexts));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Sort directories before recordings"), &data.RecordingsSortDirsFirst));
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Cutter auto delete"), &data.CutterAutoDelete));
++#endif /* CUTTERQUEUE */
+ Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift recording"),&data.DelTimeshiftRec, 3, delTimeshiftRecTexts));
+ }
+
+@@ -3072,6 +3799,17 @@
+ Add(new cMenuEditBoolItem(tr("Setup.Replay$Multi speed mode"), &data.MultiSpeedMode));
+ Add(new cMenuEditBoolItem(tr("Setup.Replay$Show replay mode"), &data.ShowReplayMode));
+ Add(new cMenuEditIntItem(tr("Setup.Replay$Resume ID"), &data.ResumeID, 0, 99));
++#ifdef USE_JUMPINGSECONDS
++ Add(new cMenuEditIntItem( tr("Setup.Recording$Jump Seconds"), &data.JumpSeconds));
++ Add(new cMenuEditIntItem( tr("Setup.Recording$Jump Seconds Slow"), &data.JumpSecondsSlow));
++ Add(new cMenuEditIntItem( tr("Setup.Recording$Jump Seconds (Repeat)"), &data.JumpSecondsRepeat));
++#endif /* JUMPINGSECONDS */
++#ifdef USE_JUMPPLAY
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$Jump&Play"), &data.JumpPlay));
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$Play&Jump"), &data.PlayJump));
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause at last mark"), &data.PauseLastMark));
++ Add(new cMenuEditBoolItem(tr("Setup.Replay$Reload marks"), &data.ReloadMarks));
++#endif /* JUMPPLAY */
+ }
+
+ void cMenuSetupReplay::Store(void)
+@@ -3084,13 +3822,48 @@
+ // --- cMenuSetupMisc --------------------------------------------------------
+
+ class cMenuSetupMisc : public cMenuSetupBase {
++#ifdef USE_VOLCTRL
++private:
++ const char *lrChannelGroupsTexts[3];
++ const char *lrForwardRewindTexts[3];
++ void Setup(void);
++#endif /* VOLCTRL */
+ public:
+ cMenuSetupMisc(void);
++#ifdef USE_VOLCTRL
++ virtual eOSState ProcessKey(eKeys Key);
++#endif /* VOLCTRL */
+ };
+
+ cMenuSetupMisc::cMenuSetupMisc(void)
+ {
++#ifdef USE_VOLCTRL
++ lrChannelGroupsTexts[0] = tr("no");
++ lrChannelGroupsTexts[1] = tr("Setup.Miscellaneous$only in channelinfo");
++ lrChannelGroupsTexts[2] = tr("yes");
++ lrForwardRewindTexts[0] = tr("no");
++ lrForwardRewindTexts[1] = tr("Setup.Miscellaneous$only in progress display");
++ lrForwardRewindTexts[2] = tr("yes");
++#endif /* VOLCTRL */
+ SetSection(tr("Miscellaneous"));
++#ifdef USE_VOLCTRL
++ Setup();
++}
++
++eOSState cMenuSetupMisc::ProcessKey(eKeys Key)
++{
++ int newLRVolumeControl = data.LRVolumeControl;
++ eOSState state = cMenuSetupBase::ProcessKey(Key);
++ if (Key != kNone && data.LRVolumeControl != newLRVolumeControl)
++ Setup();
++ return state;
++}
++
++void cMenuSetupMisc::Setup(void)
++{
++ int current = Current();
++ Clear();
++#endif /* VOLCTRL */
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. event timeout (min)"), &data.MinEventTimeout));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. user inactivity (min)"), &data.MinUserInactivity));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout));
+@@ -3098,8 +3871,22 @@
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Channel entry timeout (ms)"), &data.ChannelEntryTimeout, 0));
+ Add(new cMenuEditChanItem(tr("Setup.Miscellaneous$Initial channel"), &data.InitialChannel, tr("Setup.Miscellaneous$as before")));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Initial volume"), &data.InitialVolume, -1, 255, tr("Setup.Miscellaneous$as before")));
++#ifdef USE_VOLCTRL
++ Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Volume ctrl with left/right"), &data.LRVolumeControl));
++ if (data.LRVolumeControl) {
++ Add(new cMenuEditStraItem(tr("Setup.Miscellaneous$Channelgroups with left/right"), &data.LRChannelGroups, 3, lrChannelGroupsTexts));
++ Add(new cMenuEditStraItem(tr("Setup.Miscellaneous$Search fwd/back with left/right"), &data.LRForwardRewind, 3, lrForwardRewindTexts));
++ }
++ SetCurrent(Get(current));
++ Display();
++#endif /* VOLCTRL */
+ Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Channels wrap"), &data.ChannelsWrap));
+ Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Emergency exit"), &data.EmergencyExit));
++#ifdef USE_LIRCSETTINGS
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat delay"), &data.LircRepeatDelay, 0, 1000));
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat freq"), &data.LircRepeatFreq, 0, 1000));
++ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat timeout"), &data.LircRepeatTimeout, 0, 5000));
++#endif /* LIRCSETTINGS */
+ }
+
+ // --- cMenuSetupPluginItem --------------------------------------------------
+@@ -3124,6 +3911,9 @@
+ public:
+ cMenuSetupPlugins(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetupPlugins"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetupPlugins::cMenuSetupPlugins(void)
+@@ -3173,6 +3963,9 @@
+ public:
+ cMenuSetup(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuSetup"; }
++#endif /* GRAPHTFT */
+ };
+
+ cMenuSetup::cMenuSetup(void)
+@@ -3262,24 +4055,65 @@
+ cMenuMain::cMenuMain(eOSState State)
+ :cOsdMenu("")
+ {
++#ifdef USE_SETUP
++ // Load Menu Configuration
++ cString menuXML = cString::sprintf("%s/setup/vdr-menu.%s.xml", cPlugin::ConfigDirectory(), Setup.OSDLanguage);
++ if (access(menuXML, 04) == -1)
++ menuXML = cString::sprintf("%s/setup/vdr-menu.xml", cPlugin::ConfigDirectory());
++ subMenu.LoadXml(menuXML);
++ nrDynamicMenuEntries = 0;
++#endif /* SETUP */
++
+ replaying = false;
+ stopReplayItem = NULL;
+ cancelEditingItem = NULL;
+ stopRecordingItem = NULL;
+ recordControlsState = 0;
++
++#ifdef USE_MENUORG
++ MenuOrgPatch::EnterRootMenu();
++#endif /* MENUORG */
+ Set();
+
+ // Initial submenus:
++#ifdef USE_MAINMENUHOOKS
++ cOsdMenu *menu = NULL;
++#endif /* MAINMENUHOOKS */
+
+ switch (State) {
++#ifdef USE_MAINMENUHOOKS
++ case osSchedule:
++ if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu))
++ menu = new cMenuSchedule;
++ break;
++ case osChannels:
++ if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu))
++ menu = new cMenuChannels;
++ break;
++ case osTimers:
++ if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu))
++ menu = new cMenuTimers;
++ break;
++ case osRecordings:
++ if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu))
++ menu = new cMenuRecordings(NULL, 0, true);
++ break;
++ case osSetup: menu = new cMenuSetup; break;
++ case osCommands: menu = new cMenuCommands(tr("Commands"), &Commands); break;
++#else
+ case osSchedule: AddSubMenu(new cMenuSchedule); break;
+ case osChannels: AddSubMenu(new cMenuChannels); break;
+ case osTimers: AddSubMenu(new cMenuTimers); break;
+ case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, true)); break;
+ case osSetup: AddSubMenu(new cMenuSetup); break;
+ case osCommands: AddSubMenu(new cMenuCommands(tr("Commands"), &Commands)); break;
++#endif /* MAINMENUHOOKS */
+ default: break;
+ }
++#ifdef USE_MAINMENUHOOKS
++ if (menu)
++ AddSubMenu(menu);
++#endif /* MAINMENUHOOKS */
+ }
+
+ cOsdObject *cMenuMain::PluginOsdObject(void)
+@@ -3289,37 +4123,156 @@
+ return o;
+ }
+
++#ifdef USE_SETUP
++void cMenuMain::Set(int current)
++#else
+ void cMenuMain::Set(void)
++#endif /* SETUP */
+ {
+ Clear();
+ SetTitle("VDR");
+ SetHasHotkeys();
+
++#ifdef USE_MENUORG
++ if (MenuOrgPatch::IsCustomMenuAvailable()) {
++ MenuItemDefinitions* menuItems = MenuOrgPatch::MainMenuItems();
++ for (MenuItemDefinitions::iterator i = menuItems->begin(); i != menuItems->end(); i++) {
++ cOsdItem* osdItem = NULL;
++ if ((*i)->IsCustomOsdItem()) {
++ osdItem = (*i)->CustomOsdItem();
++ if (osdItem && !(*i)->IsSeparatorItem())
++ osdItem->SetText(hk(osdItem->Text()));
++ }
++ else if ((*i)->IsPluginItem()) {
++ const char *item = (*i)->PluginMenuEntry();
++ if (item)
++ osdItem = new cMenuPluginItem(hk(item), (*i)->PluginIndex());
++ }
++ if (osdItem) {
++ Add(osdItem);
++ if ((*i)->IsSelected())
++ SetCurrent(osdItem);
++ }
++ }
++ }
++ else {
++#endif /* MENUORG */
++#ifdef USE_SETUP
++ stopReplayItem = NULL;
++ cancelEditingItem = NULL;
++ stopRecordingItem = NULL;
++
++ // remember initial dynamic MenuEntries added
++ nrDynamicMenuEntries = Count();
++ for (cSubMenuNode *node = subMenu.GetMenuTree()->First(); node; node = subMenu.GetMenuTree()->Next(node)) {
++ cSubMenuNode::Type type = node->GetType();
++ if (type==cSubMenuNode::PLUGIN) {
++ const char *item = node->GetPluginMainMenuEntry();
++#ifdef USE_PINPLUGIN
++ if (item && !cStatus::MsgPluginProtected(cPluginManager::GetPlugin(node->GetPluginIndex()), true))
++#else
++ if (item)
++#endif /* PINPLUGIN */
++ Add(new cMenuPluginItem(hk(item), node->GetPluginIndex()));
++ }
++ else if (type==cSubMenuNode::MENU) {
++ cString item = cString::sprintf("%s%s", node->GetName(), *subMenu.GetMenuSuffix());
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected(item, true))
++ Add(new cOsdItem(hk(item), osUnknown, node));
++#else
++ Add(new cOsdItem(hk(item)));
++#endif /* PINPLUGIN */
++ }
++ else if ((type==cSubMenuNode::COMMAND) || (type==cSubMenuNode::THREAD)) {
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected(node->GetName(), true))
++ Add(new cOsdItem(hk(node->GetName()), osUnknown, node));
++#else
++ Add(new cOsdItem(hk(node->GetName())));
++#endif /* PINPLUGIN */
++ }
++ else if (type==cSubMenuNode::SYSTEM) {
++ const char *item = node->GetName();
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgMenuItemProtected(item, true))
++ ; // nothing to do ;)
++ else
++#endif /* PINPLUGIN */
++ if (strcmp(item, "Schedule") == 0)
++ Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ else if (strcmp(item, "Channels") == 0)
++ Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ else if (strcmp(item, "Timers") == 0)
++ Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ else if (strcmp(item, "Recordings") == 0)
++ Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++ else if (strcmp(item, "Setup") == 0) {
++ cString itemSetup = cString::sprintf("%s%s", tr("Setup"), *subMenu.GetMenuSuffix());
++ Add(new cOsdItem(hk(itemSetup), osSetup));
++ }
++ else if (strcmp(item, "Commands") == 0 && Commands.Count() > 0) {
++ cString itemCommands = cString::sprintf("%s%s", tr("Commands"), *subMenu.GetMenuSuffix());
++ Add(new cOsdItem(hk(itemCommands), osCommands));
++ }
++ }
++ }
++ if (current >=0 && current<Count()) {
++ SetCurrent(Get(current));
++ }
++
++#else /* NO SETUP */
++
+ // Basic menu items:
+
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected("Schedule", true)) Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++ if (!cStatus::MsgMenuItemProtected("Channels", true)) Add(new cOsdItem(hk(tr("Channels")), osChannels));
++ if (!cStatus::MsgMenuItemProtected("Timers", true)) Add(new cOsdItem(hk(tr("Timers")), osTimers));
++ if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++#else
+ Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+ Add(new cOsdItem(hk(tr("Channels")), osChannels));
+ Add(new cOsdItem(hk(tr("Timers")), osTimers));
+ Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++#endif /* PINPLUGIN */
+
+ // Plugins:
+
+ for (int i = 0; ; i++) {
+ cPlugin *p = cPluginManager::GetPlugin(i);
+ if (p) {
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgPluginProtected(p, true)) {
++#endif /* PINPLUGIN */
+ const char *item = p->MainMenuEntry();
+ if (item)
+ Add(new cMenuPluginItem(hk(item), i));
+ }
++#ifdef USE_PINPLUGIN
++ }
++#endif /* PINPLUGIN */
+ else
+ break;
+ }
+
+ // More basic menu items:
+
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup));
++#else
+ Add(new cOsdItem(hk(tr("Setup")), osSetup));
++#endif /* PINPLUGIN */
+ if (Commands.Count())
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgMenuItemProtected("Commands", true))
++#endif /* PINPLUGIN */
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
++#endif /* SETUP */
++
++#ifdef USE_MENUORG
++ }
++#endif /* MENUORG */
+
+ Update(true);
+
+@@ -3329,13 +4282,29 @@
+ bool cMenuMain::Update(bool Force)
+ {
+ bool result = false;
++
++#ifdef USE_SETUP
++ cOsdItem *fMenu = NULL;
++ if (Force && subMenu.isTopMenu()) {
++ fMenu = First();
++ nrDynamicMenuEntries = 0;
++ }
+
++ if (subMenu.isTopMenu()) {
++#endif /* SETUP */
+ // Title with disk usage:
+ if (FreeDiskSpace.HasChanged(Force)) {
+ //XXX -> skin function!!!
+ SetTitle(cString::sprintf("%s - %s", tr("VDR"), FreeDiskSpace.FreeDiskSpaceString()));
+ result = true;
+ }
++#ifdef USE_SETUP
++ }
++ else {
++ SetTitle(cString::sprintf("%s - %s", tr("VDR"), subMenu.GetParentMenuTitel()));
++ result = true;
++ }
++#endif /* SETUP */
+
+ bool NewReplaying = cControl::Control() != NULL;
+ if (Force || NewReplaying != replaying) {
+@@ -3343,6 +4312,9 @@
+ // Replay control:
+ if (replaying && !stopReplayItem)
+ // TRANSLATORS: note the leading blank!
++#ifdef USE_LIEMIEXT
++ if (Setup.MenuCmdPosition) Ins(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay)); else
++#endif /* LIEMIEXT */
+ Add(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay));
+ else if (stopReplayItem && !replaying) {
+ Del(stopReplayItem->Index());
+@@ -3357,6 +4329,9 @@
+ bool CutterActive = cCutter::Active();
+ if (CutterActive && !cancelEditingItem) {
+ // TRANSLATORS: note the leading blank!
++#ifdef USE_LIEMIEXT
++ if (Setup.MenuCmdPosition) Ins(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit)); else
++#endif /* LIEMIEXT */
+ Add(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit));
+ result = true;
+ }
+@@ -3377,6 +4352,9 @@
+ while ((s = cRecordControls::GetInstantId(s)) != NULL) {
+ cOsdItem *item = new cOsdItem(osStopRecord);
+ item->SetText(cString::sprintf("%s%s", tr(STOP_RECORDING), s));
++#ifdef USE_LIEMIEXT
++ if (Setup.MenuCmdPosition) Ins(item); else
++#endif /* LIEMIEXT */
+ Add(item);
+ if (!stopRecordingItem)
+ stopRecordingItem = item;
+@@ -3384,6 +4362,12 @@
+ result = true;
+ }
+
++#ifdef USE_SETUP
++ // adjust nrDynamicMenuEntries
++ if (fMenu != NULL)
++ nrDynamicMenuEntries = fMenu->Index();
++#endif /* SETUP */
++
+ return result;
+ }
+
+@@ -3394,13 +4378,53 @@
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ HadSubMenu |= HasSubMenu();
+
++#ifdef USE_MAINMENUHOOKS
++ cOsdMenu *menu = NULL;
++#endif /* MAINMENUHOOKS */
++#ifdef USE_PINPLUGIN
++ cOsdItem* item = Get(Current());
++
++ if (item && item->Text() && state != osContinue && state != osUnknown && state != osBack)
++ if (cStatus::MsgMenuItemProtected(item->Text()))
++ return osContinue;
++#endif /* PINPLUGIN */
++
+ switch (state) {
++#ifdef USE_MAINMENUHOOKS
++ case osSchedule:
++ if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu))
++ menu = new cMenuSchedule;
++ else
++ state = osContinue;
++ break;
++ case osChannels:
++ if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu))
++ menu = new cMenuChannels;
++ else
++ state = osContinue;
++ break;
++ case osTimers:
++ if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu))
++ menu = new cMenuTimers;
++ else
++ state = osContinue;
++ break;
++ case osRecordings:
++ if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu))
++ menu = new cMenuRecordings;
++ else
++ state = osContinue;
++ break;
++ case osSetup: menu = new cMenuSetup; break;
++ case osCommands: menu = new cMenuCommands(tr("Commands"), &Commands); break;
++#else
+ case osSchedule: return AddSubMenu(new cMenuSchedule);
+ case osChannels: return AddSubMenu(new cMenuChannels);
+ case osTimers: return AddSubMenu(new cMenuTimers);
+ case osRecordings: return AddSubMenu(new cMenuRecordings);
+ case osSetup: return AddSubMenu(new cMenuSetup);
+ case osCommands: return AddSubMenu(new cMenuCommands(tr("Commands"), &Commands));
++#endif /* MAINMENUHOOKS */
+ case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
+ cOsdItem *item = Get(Current());
+ if (item) {
+@@ -3419,6 +4443,9 @@
+ if (item) {
+ cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+ if (p) {
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgPluginProtected(p)) {
++#endif /* PINPLUGIN */
+ cOsdObject *menu = p->MainMenuAction();
+ if (menu) {
+ if (menu->IsMenu())
+@@ -3428,11 +4455,63 @@
+ return osPlugin;
+ }
+ }
++#ifdef USE_PINPLUGIN
+ }
++#endif /* PINPLUGIN */
+ }
++ }
+ state = osEnd;
+ }
+ break;
++#ifdef USE_MENUORG
++ case osBack: {
++ if (MenuOrgPatch::IsCustomMenuAvailable())
++ {
++ bool leavingMenuSucceeded = MenuOrgPatch::LeaveSubMenu();
++ Set();
++ stopReplayItem = NULL;
++ cancelEditingItem = NULL;
++ stopRecordingItem = NULL;
++ recordControlsState = 0;
++ Update(true);
++ Display();
++ if (leavingMenuSucceeded)
++ return osContinue;
++ else
++ return osEnd;
++ }
++ }
++ break;
++ case osUser1: {
++ if (MenuOrgPatch::IsCustomMenuAvailable()) {
++ MenuOrgPatch::EnterSubMenu(Get(Current()));
++ Set();
++ return osContinue;
++ }
++ }
++ break;
++ case osUser2: {
++ if (MenuOrgPatch::IsCustomMenuAvailable()) {
++ cOsdMenu* osdMenu = MenuOrgPatch::Execute(Get(Current()));
++ if (osdMenu)
++ return AddSubMenu(osdMenu);
++ return osEnd;
++ }
++ }
++ break;
++#endif /* MENUORG */
++#ifdef USE_SETUP
++ case osBack: {
++ int newCurrent = 0;
++ if (subMenu.Up(&newCurrent)) {
++ Set(newCurrent);
++ return osContinue;
++ }
++ else
++ return osEnd;
++ }
++ break;
++#endif /* SETUP */
+ default: switch (Key) {
+ case kRecord:
+ case kRed: if (!HadSubMenu)
+@@ -3449,9 +4528,63 @@
+ case kBlue: if (!HadSubMenu)
+ state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osContinue;
+ break;
++#ifdef USE_SETUP
++ case kOk: if (state == osUnknown) {
++ cString buffer;
++ int index = Current()-nrDynamicMenuEntries;
++ cSubMenuNode *node = subMenu.GetNode(index);
++
++ if (node != NULL) {
++ if (node->GetType() == cSubMenuNode::MENU) {
++#ifdef USE_PINPLUGIN
++ subMenu.Down(node, Current());
++#else
++ subMenu.Down(index);
++#endif /* PINPLUGIN */
++ }
++ else if (node->GetType() == cSubMenuNode::COMMAND) {
++ bool confirmed = true;
++ if (node->CommandConfirm()) {
++ buffer = cString::sprintf("%s?", node->GetName());
++ confirmed = Interface->Confirm(buffer);
++ }
++ if (confirmed) {
++ const char *Result = subMenu.ExecuteCommand(node->GetCommand());
++ if (Result)
++ return AddSubMenu(new cMenuText(node->GetName(), Result, fontFix));
++ return osEnd;
++ }
++ }
++ else if (node->GetType() == cSubMenuNode::THREAD) {
++ bool confirmed = true;
++ if (node->CommandConfirm()) {
++ buffer = cString::sprintf("%s?", node->GetName());
++ confirmed = Interface->Confirm(buffer);
++ }
++ if (confirmed) {
++ buffer = cString::sprintf("%s", node->GetCommand());
++ cExecCmdThread *execcmd = new cExecCmdThread(node->GetCommand());
++ if (execcmd->Start())
++ dsyslog("executing command '%s'", *buffer);
++ else
++ esyslog("ERROR: can't execute command '%s'", *buffer);
++ return osEnd;
++ }
++ }
++ }
++
++ Set();
++ return osContinue;
++ }
++ break;
++#endif /* SETUP */
+ default: break;
+ }
+ }
++#ifdef USE_MAINMENUHOOKS
++ if (menu)
++ return AddSubMenu(menu);
++#endif /* MAINMENUHOOKS */
+ if (!HasSubMenu() && Update(HadSubMenu))
+ Display();
+ if (Key != kNone) {
+@@ -3598,6 +4731,9 @@
+ if (Direction) {
+ while (Channel) {
+ Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgChannelProtected(0, Channel) == false)
++#endif /* PINPLUGIN */
+ if (!Channel && Setup.ChannelsWrap)
+ Channel = Direction > 0 ? Channels.First() : Channels.Last();
+ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
+@@ -3658,6 +4794,13 @@
+ case kLeft:
+ case kRight|k_Repeat:
+ case kRight:
++#ifdef USE_VOLCTRL
++ if (Setup.LRVolumeControl && !Setup.LRChannelGroups) {
++ cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++ break;
++ }
++ // else fall through
++#endif /* VOLCTRL */
+ case kNext|k_Repeat:
+ case kNext:
+ case kPrev|k_Repeat:
+@@ -3817,6 +4960,17 @@
+ eOSState cDisplayVolume::ProcessKey(eKeys Key)
+ {
+ switch (Key) {
++#ifdef USE_VOLCTRL
++ case kLeft|k_Repeat:
++ case kLeft:
++ case kRight|k_Repeat:
++ case kRight:
++ if (Setup.LRVolumeControl) {
++ cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++ break;
++ }
++ // else fall through
++#endif /* VOLCTRL */
+ case kVolUp|k_Repeat:
+ case kVolUp:
+ case kVolDn|k_Repeat:
+@@ -4064,8 +5218,16 @@
+
+ // --- cRecordControl --------------------------------------------------------
+
++#ifdef USE_ALTERNATECHANNEL
++cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause, cChannel *Channel)
++#else
+ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
++#endif /* ALTERNATECHANNEL */
+ {
++#ifdef USE_DVLRECSCRIPTADDON
++ const cChannel *recChan = NULL;
++ char *chanName = NULL;
++#endif /* DVLRECSCRIPTADDON */
+ // We're going to manipulate an event here, so we need to prevent
+ // others from modifying any EPG data:
+ cSchedulesLock SchedulesLock;
+@@ -4110,12 +5272,29 @@
+ return;
+ }
+
++#ifdef USE_DVLRECSCRIPTADDON
++ if (timer)
++ if ((recChan = timer->Channel()) != NULL)
++ chanName = strdup(recChan->Name());
++ if (chanName != NULL) {
++ cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName, chanName);
++ free(chanName);
++ }
++ else
++#endif /* DVLRECSCRIPTADDON */
+ cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
+ isyslog("record %s", fileName);
+ if (MakeDirs(fileName, true)) {
++#ifdef USE_ALTERNATECHANNEL
++ const cChannel *ch = Channel ? Channel : timer->Channel();
++ if (ch)
++ recorder = new cRecorder(fileName, ch, timer->Priority());
++ if (ch && device->AttachReceiver(recorder)) {
++#else
+ const cChannel *ch = timer->Channel();
+ recorder = new cRecorder(fileName, ch, timer->Priority());
+ if (device->AttachReceiver(recorder)) {
++#endif /* ALTERNATECHANNEL */
+ Recording.WriteInfo();
+ cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
+ if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
+@@ -4124,7 +5303,12 @@
+ return;
+ }
+ else
++#ifdef USE_ALTERNATECHANNEL
++ if (ch)
+ DELETENULL(recorder);
++#else
++ DELETENULL(recorder);
++#endif /* ALTERNATECHANNEL */
+ }
+ if (!Timer) {
+ Timers.Del(timer);
+@@ -4172,12 +5356,26 @@
+ void cRecordControl::Stop(bool ExecuteUserCommand)
+ {
+ if (timer) {
++#ifdef USE_DVLRECSCRIPTADDON
++ char *chanName = NULL;
++ const cChannel *recChan = NULL;
++
++ recChan = timer -> Channel();
++ if (recChan != NULL)
++ chanName = strdup(recChan -> Name());
++#endif /* DVLRECSCRIPTADDON */
+ DELETENULL(recorder);
+ timer->SetRecording(false);
+ timer = NULL;
+ cStatus::MsgRecording(device, NULL, fileName, false);
+ if (ExecuteUserCommand)
++#ifdef USE_DVLRECSCRIPTADDON
++ cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName, chanName);
++ if (chanName != NULL)
++ free(chanName);
++#else
+ cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
++#endif /* DVLRECSCRIPTADDON */
+ }
+ }
+
+@@ -4223,8 +5421,32 @@
+ if (channel) {
+ int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ cDevice *device = cDevice::GetDevice(channel, Priority, false);
++
++#ifdef USE_ALTERNATECHANNEL
++ if (!device && channel->AlternativeChannelID().Valid()) {// check for alternatives
++ dsyslog("prepare to use alternative channel for channel %d", channel->Number());
++ channel = Channels.GetByChannelID(channel->AlternativeChannelID());
++ device = cDevice::GetDevice(channel, Priority, false);
++ if (device)
++ dsyslog("use of alternative channel %d successfully initiated", channel->Number());
++ }
++#endif /* ALTERNATECHANNEL */
++
+ if (device) {
+ dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
++#ifdef USE_LNBSHARE
++ cDevice *tmpDevice;
++ while ((tmpDevice = device->GetBadDevice(channel))) {
++ if (tmpDevice->Replaying() == false) {
++ Stop(tmpDevice);
++ if (tmpDevice->CardIndex() == tmpDevice->ActualDevice()->CardIndex())
++ tmpDevice->SwitchChannelForced(channel, true);
++ else
++ tmpDevice->SwitchChannelForced(channel, false);
++ } else
++ tmpDevice->SwitchChannelForced(channel, false);
++ }
++#endif /* LNBSHARE */
+ if (!device->SwitchChannel(channel, false)) {
+ ShutdownHandler.RequestEmergencyExit();
+ return false;
+@@ -4232,7 +5454,14 @@
+ if (!Timer || Timer->Matches()) {
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+ if (!RecordControls[i]) {
++#ifdef USE_ALTERNATECHANNEL
++ RecordControls[i] = new cRecordControl(device, Timer, Pause, channel);
++#else
+ RecordControls[i] = new cRecordControl(device, Timer, Pause);
++#endif /* ALTERNATECHANNEL */
++#ifdef USE_PINPLUGIN
++ cStatus::MsgRecordingFile(RecordControls[i]->FileName());
++#endif /* PINPLUGIN */
+ return RecordControls[i]->Process(time(NULL));
+ }
+ }
+@@ -4268,6 +5497,21 @@
+ }
+ }
+
++#ifdef USE_LNBSHARE
++void cRecordControls::Stop(cDevice *Device)
++{
++ ChangeState();
++ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
++ if (RecordControls[i]) {
++ if (RecordControls[i]->Device() == Device) {
++ isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);
++ RecordControls[i]->Stop();
++ }
++ }
++ }
++}
++#endif /* LNBSHARE */
++
+ bool cRecordControls::PauseLiveVideo(void)
+ {
+ Skins.Message(mtStatus, tr("Pausing live video..."));
+@@ -4365,12 +5609,22 @@
+
+ // --- cReplayControl --------------------------------------------------------
+
++#ifdef USE_LIEMIEXT
++#define REPLAYCONTROLSKIPLIMIT 9 // s
++#define REPLAYCONTROLSKIPSECONDS 90 // s
++#define REPLAYCONTROLSKIPTIMEOUT 5000 // ms
++#endif /* LIEMIEXT */
++
+ cReplayControl *cReplayControl::currentReplayControl = NULL;
+ char *cReplayControl::fileName = NULL;
+ char *cReplayControl::title = NULL;
+
+ cReplayControl::cReplayControl(void)
++#ifdef USE_JUMPPLAY
++:cDvbPlayerControl(fileName), marks(fileName)
++#else
+ :cDvbPlayerControl(fileName)
++#endif /* JUMPPLAY */
+ {
+ currentReplayControl = this;
+ displayReplay = NULL;
+@@ -4378,11 +5632,18 @@
+ lastCurrent = lastTotal = -1;
+ lastPlay = lastForward = false;
+ lastSpeed = -2; // an invalid value
++#ifdef USE_LIEMIEXT
++ lastSkipKey = kNone;
++ lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++ lastSkipTimeout.Set(0);
++#endif /* LIEMIEXT */
+ timeoutShow = 0;
+ timeSearchActive = false;
+ cRecording Recording(fileName);
+ cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true);
++#ifndef USE_JUMPPLAY
+ marks.Load(fileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
++#endif /* JUMPPLAY */
+ SetTrackDescriptions(false);
+ }
+
+@@ -4642,8 +5903,16 @@
+ ShowTimed(2);
+ bool Play, Forward;
+ int Speed;
++#ifdef USE_JUMPPLAY
++ if (GetReplayMode(Play, Forward, Speed) && !Play) {
++#else
+ if (GetReplayMode(Play, Forward, Speed) && !Play)
++#endif /* JUMPPLAY */
+ Goto(Current, true);
++#ifdef USE_JUMPPLAY
++ displayFrames = true;
++ }
++#endif /* JUMPPLAY */
+ }
+ marks.Save();
+ }
+@@ -4656,8 +5925,22 @@
+ if (GetIndex(Current, Total)) {
+ cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
+ if (m) {
++#ifdef USE_JUMPPLAY
++ bool Play2, Forward2;
++ int Speed;
++ if (Setup.JumpPlay && GetReplayMode(Play2, Forward2, Speed) &&
++ Play2 && Forward && m->position < Total - SecondsToFrames(3, FramesPerSecond())) {
++ Goto(m->position);
++ Play();
++ }
++ else {
++ Goto(m->position, true);
++ displayFrames = true;
++ }
++#else
+ Goto(m->position, true);
+ displayFrames = true;
++#endif /* JUMPPLAY */
+ }
+ }
+ }
+@@ -4690,7 +5973,11 @@
+ {
+ if (fileName) {
+ Hide();
++#ifdef USE_CUTTERQUEUE
++ if (!cCutter::Active() || Interface->Confirm(tr("Cutter already running - Add to cutting queue?"))) {
++#else
+ if (!cCutter::Active()) {
++#endif /* CUTTERQUEUE */
+ if (!marks.Count())
+ Skins.Message(mtError, tr("No editing marks defined!"));
+ else if (!cCutter::Start(fileName))
+@@ -4712,7 +5999,11 @@
+ if (!m)
+ m = marks.GetNext(Current);
+ if (m) {
++#ifdef USE_JUMPPLAY
++ if ((m->Index() & 0x01) != 0 && !Setup.PlayJump)
++#else
+ if ((m->Index() & 0x01) != 0)
++#endif /* JUMPPLAY */
+ m = marks.Next(m);
+ if (m) {
+ Goto(m->position - SecondsToFrames(3, FramesPerSecond()));
+@@ -4734,6 +6025,9 @@
+ {
+ if (!Active())
+ return osEnd;
++#ifdef USE_JUMPPLAY
++ marks.Reload();
++#endif /* JUMPPLAY */
+ if (visible) {
+ if (timeoutShow && time(NULL) > timeoutShow) {
+ Hide();
+@@ -4752,6 +6046,22 @@
+ return osContinue;
+ }
+ bool DoShowMode = true;
++#ifdef USE_VOLCTRL
++ if (Setup.LRVolumeControl && (!Setup.LRForwardRewind || (Setup.LRForwardRewind == 1 && !visible))) {
++ switch (Key) {
++ // Left/Right volume control
++ case kLeft|k_Repeat:
++ case kLeft:
++ case kRight|k_Repeat:
++ case kRight:
++ cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++ return osContinue;
++ break;
++ default:
++ break;
++ }
++ }
++#endif /* VOLCTRL */
+ switch (Key) {
+ // Positioning:
+ case kPlay:
+@@ -4769,25 +6079,73 @@
+ case kFastFwd:
+ case kRight: Forward(); break;
+ case kRed: TimeSearch(); break;
++#ifdef USE_JUMPINGSECONDS
++ case kGreen|k_Repeat:
++ SkipSeconds(-(Setup.JumpSecondsRepeat)); break;
++ case kGreen: SkipSeconds(-(Setup.JumpSeconds)); break;
++ case k1|k_Repeat:
++ case k1: SkipSeconds(-Setup.JumpSecondsSlow); break;
++ case k3|k_Repeat:
++ case k3: SkipSeconds( Setup.JumpSecondsSlow); break;
++ case kYellow|k_Repeat:
++ SkipSeconds(Setup.JumpSecondsRepeat); break;
++ case kYellow: SkipSeconds(Setup.JumpSeconds); break;
++#else
+ case kGreen|k_Repeat:
+ case kGreen: SkipSeconds(-60); break;
+ case kYellow|k_Repeat:
+ case kYellow: SkipSeconds( 60); break;
++#endif /* JUMPINGSECONDS */
++#ifdef USE_LIEMIEXT
++#ifndef USE_JUMPINGSECONDS
++ case k1|k_Repeat:
++ case k1: SkipSeconds(-20); break;
++ case k3|k_Repeat:
++ case k3: SkipSeconds( 20); break;
++#endif /* JUMPINGSECONDS */
++ case kPrev|k_Repeat:
++ case kPrev: if (lastSkipTimeout.TimedOut()) {
++ lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++ lastSkipKey = kPrev;
++ }
++ else if (RAWKEY(lastSkipKey) != kPrev && lastSkipSeconds > (2 * REPLAYCONTROLSKIPLIMIT)) {
++ lastSkipSeconds /= 2;
++ lastSkipKey = kNone;
++ }
++ lastSkipTimeout.Set(REPLAYCONTROLSKIPTIMEOUT);
++ SkipSeconds(-lastSkipSeconds); break;
++ case kNext|k_Repeat:
++ case kNext: if (lastSkipTimeout.TimedOut()) {
++ lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++ lastSkipKey = kNext;
++ }
++ else if (RAWKEY(lastSkipKey) != kNext && lastSkipSeconds > (2 * REPLAYCONTROLSKIPLIMIT)) {
++ lastSkipSeconds /= 2;
++ lastSkipKey = kNone;
++ }
++ lastSkipTimeout.Set(REPLAYCONTROLSKIPTIMEOUT);
++ SkipSeconds(lastSkipSeconds); break;
++#endif /* LIEMIEXT */
+ case kStop:
+ case kBlue: Hide();
+ Stop();
+ return osEnd;
++
+ default: {
+ DoShowMode = false;
+ switch (Key) {
+ // Editing:
+ case kMarkToggle: MarkToggle(); break;
++#ifndef USE_LIEMIEXT
+ case kPrev|k_Repeat:
+ case kPrev:
++#endif /* LIEMIEXT */
+ case kMarkJumpBack|k_Repeat:
+ case kMarkJumpBack: MarkJump(false); break;
++#ifndef USE_LIEMIEXT
+ case kNext|k_Repeat:
+ case kNext:
++#endif /* LIEMIEXT */
+ case kMarkJumpForward|k_Repeat:
+ case kMarkJumpForward: MarkJump(true); break;
+ case kMarkMoveBack|k_Repeat:
+diff -ruN vdr-1.7.14/menu.h vdr-1.7.14.ExtP_NG/menu.h
+--- vdr-1.7.14/menu.h 2010-03-06 17:15:59.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/menu.h 2010-04-10 15:45:10.646737633 +0200
+@@ -18,6 +18,9 @@
+ #include "menuitems.h"
+ #include "recorder.h"
+ #include "skins.h"
++#ifdef USE_SETUP
++#include "submenu.h"
++#endif /* SETUP */
+
+ class cMenuText : public cOsdMenu {
+ private:
+@@ -51,6 +54,9 @@
+ cMenuFolder(const char *Title, cNestedItemList *NestedItemList, const char *Path = NULL);
+ cString GetFolder(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuText"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cMenuCommands : public cOsdMenu {
+@@ -84,6 +90,9 @@
+ cMenuEditTimer(cTimer *Timer, bool New = false);
+ virtual ~cMenuEditTimer();
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuTimerEdit"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cMenuEvent : public cOsdMenu {
+@@ -93,22 +102,37 @@
+ cMenuEvent(const cEvent *Event, bool CanSwitch = false, bool Buttons = false);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuEvent"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cMenuMain : public cOsdMenu {
+ private:
++#ifdef USE_SETUP
++ int nrDynamicMenuEntries;
++#endif /* SETUP */
+ bool replaying;
+ cOsdItem *stopReplayItem;
+ cOsdItem *cancelEditingItem;
+ cOsdItem *stopRecordingItem;
+ int recordControlsState;
+ static cOsdObject *pluginOsdObject;
++#ifdef USE_SETUP
++ void Set(int current=0);
++ bool Update(bool Force = false);
++ cSubMenu subMenu;
++#else
+ void Set(void);
+ bool Update(bool Force = false);
++#endif /* SETUP */
+ public:
+ cMenuMain(eOSState State = osUnknown);
+ virtual eOSState ProcessKey(eKeys Key);
+ static cOsdObject *PluginOsdObject(void);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuMain"; }
++#endif /* GRAPHTFT */
+ };
+
+ class cDisplayChannel : public cOsdObject {
+@@ -204,6 +228,9 @@
+ eOSState Delete(void);
+ eOSState Info(void);
+ eOSState Commands(eKeys Key = kNone);
++#ifdef USE_LIEMIEXT
++ eOSState Rename(void);
++#endif /* LIEMIEXT */
+ protected:
+ cRecording *GetRecording(cMenuRecordingItem *Item);
+ public:
+@@ -222,7 +249,11 @@
+ char *fileName;
+ bool GetEvent(void);
+ public:
++#ifdef USE_ALTERNATECHANNEL
++ cRecordControl(cDevice *Device, cTimer *Timer = NULL, bool Pause = false, cChannel *Channel = NULL);
++#else
+ cRecordControl(cDevice *Device, cTimer *Timer = NULL, bool Pause = false);
++#endif /* ALTERNATECHANNEL */
+ virtual ~cRecordControl();
+ bool Process(time_t t);
+ cDevice *Device(void) { return device; }
+@@ -239,6 +270,9 @@
+ public:
+ static bool Start(cTimer *Timer = NULL, bool Pause = false);
+ static void Stop(const char *InstantId);
++#ifdef USE_LNBSHARE
++ static void Stop(cDevice *Device); // LNB Sharing
++#endif /* LNBSHARE */
+ static bool PauseLiveVideo(void);
+ static const char *GetInstantId(const char *LastInstantId);
+ static cRecordControl *GetRecordControl(const char *FileName);
+@@ -253,11 +287,20 @@
+ class cReplayControl : public cDvbPlayerControl {
+ private:
+ cSkinDisplayReplay *displayReplay;
++#ifdef USE_JUMPPLAY
++ cMarksReload marks;
++#else
+ cMarks marks;
++#endif /* JUMPPLAY */
+ bool visible, modeOnly, shown, displayFrames;
+ int lastCurrent, lastTotal;
+ bool lastPlay, lastForward;
+ int lastSpeed;
++#ifdef USE_LIEMIEXT
++ int lastSkipSeconds;
++ eKeys lastSkipKey;
++ cTimeMs lastSkipTimeout;
++#endif /* LIEMIEXT */
+ time_t timeoutShow;
+ bool timeSearchActive, timeSearchHide;
+ int timeSearchTime, timeSearchPos;
+diff -ruN vdr-1.7.14/menuitems.c vdr-1.7.14.ExtP_NG/menuitems.c
+--- vdr-1.7.14/menuitems.c 2010-02-16 15:44:35.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/menuitems.c 2010-04-10 15:45:10.665741204 +0200
+@@ -33,9 +33,21 @@
+ free(name);
+ }
+
++#ifdef USE_VALIDINPUT
++void cMenuEditItem::SetValue(const char *Value, bool HasPre, bool HasSucc)
++#else
+ void cMenuEditItem::SetValue(const char *Value)
++#endif /* VALIDINPUT */
+ {
+ cString buffer = cString::sprintf("%s:\t%s", name, Value);
++#ifdef USE_VALIDINPUT
++ if (Setup.ShowValidInput) {
++ if (HasPre && HasSucc) buffer = cString::sprintf("%s:\t<%s>", name, Value);
++ else if (HasPre) buffer = cString::sprintf("%s:\t<%s", name, Value);
++ else if (HasSucc) buffer = cString::sprintf("%s:\t%s>", name, Value);
++ }
++#endif /* VALIDINPUT */
++
+ SetText(buffer);
+ cStatus::MsgOsdCurrentItem(buffer);
+ }
+@@ -127,7 +139,11 @@
+ {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
++#ifdef USE_VALIDINPUT
++ SetValue(buf, *value, !*value);
++#else
+ SetValue(buf);
++#endif /* VALIDINPUT */
+ }
+
+ // --- cMenuEditBitItem ------------------------------------------------------
+@@ -698,7 +714,11 @@
+
+ void cMenuEditStraItem::Set(void)
+ {
++#ifdef USE_VALIDINPUT
++ SetValue(strings[*value], (*value > min), (*value < max));
++#else
+ SetValue(strings[*value]);
++#endif /* VALIDINPUT */
+ }
+
+ // --- cMenuEditChanItem -----------------------------------------------------
+diff -ruN vdr-1.7.14/menuitems.h vdr-1.7.14.ExtP_NG/menuitems.h
+--- vdr-1.7.14/menuitems.h 2010-02-21 14:58:21.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/menuitems.h 2010-04-10 15:45:10.674735161 +0200
+@@ -22,7 +22,11 @@
+ public:
+ cMenuEditItem(const char *Name);
+ ~cMenuEditItem();
++#ifdef USE_VALIDINPUT
++ void SetValue(const char *Value, bool HasPre=false, bool HasSucc=false);
++#else
+ void SetValue(const char *Value);
++#endif /* VALIDINPUT */
+ };
+
+ class cMenuEditIntItem : public cMenuEditItem {
+@@ -198,6 +202,9 @@
+ cMenuSetupPage(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ void SetPlugin(cPlugin *Plugin);
++#ifdef USE_GRAPHTFT
++ const char* MenuKind() { return "MenuSetupPage"; }
++#endif /* GRAPHTFT */
+ };
+
+ #endif //__MENUITEMS_H
+diff -ruN vdr-1.7.14/menuorgpatch.h vdr-1.7.14.ExtP_NG/menuorgpatch.h
+--- vdr-1.7.14/menuorgpatch.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/menuorgpatch.h 2010-04-10 15:45:10.686736443 +0200
+@@ -0,0 +1,102 @@
++#ifdef USE_MENUORG
++/*
++ * vdr-menuorg - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
++ * details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __MENUORGPATCH_H
++#define __MENUORGPATCH_H
++
++#include "mainmenuitemsprovider.h"
++
++class MenuOrgPatch
++{
++ private:
++ static IMainMenuItemsProvider* _mainMenuItemsProvider;
++
++ private:
++ static IMainMenuItemsProvider* MainMenuItemsProvider()
++ {
++ if (!_mainMenuItemsProvider)
++ {
++ IMainMenuItemsProvider* mainMenuItemsProvider;
++
++ if (cPluginManager::CallFirstService(MENU_ITEMS_PROVIDER_SERVICE_ID, &mainMenuItemsProvider))
++ {
++ _mainMenuItemsProvider = mainMenuItemsProvider;
++ }
++ }
++ return _mainMenuItemsProvider;
++ }
++
++ public:
++ static bool IsCustomMenuAvailable()
++ {
++ return (MainMenuItemsProvider() != NULL) && (MainMenuItemsProvider()->IsCustomMenuAvailable());
++ }
++
++ static void EnterRootMenu()
++ {
++ if (MainMenuItemsProvider())
++ {
++ MainMenuItemsProvider()->EnterRootMenu();
++ }
++ }
++
++ static bool LeaveSubMenu()
++ {
++ if (MainMenuItemsProvider())
++ {
++ return MainMenuItemsProvider()->LeaveSubMenu();
++ }
++ return false;
++ }
++
++ static void EnterSubMenu(cOsdItem* item)
++ {
++ if (MainMenuItemsProvider())
++ {
++ MainMenuItemsProvider()->EnterSubMenu(item);
++ }
++ }
++
++ static MenuItemDefinitions* MainMenuItems()
++ {
++ if (MainMenuItemsProvider())
++ {
++ return MainMenuItemsProvider()->MainMenuItems();
++ }
++ return NULL;
++ }
++
++ static cOsdMenu* Execute(cOsdItem* item)
++ {
++ if (MainMenuItemsProvider())
++ {
++ return MainMenuItemsProvider()->Execute(item);
++ }
++ return NULL;
++ }
++};
++
++IMainMenuItemsProvider* MenuOrgPatch::_mainMenuItemsProvider = NULL;
++
++#endif //__MENUORGPATCH_H
++#endif /* MENUORG */
+diff -ruN vdr-1.7.14/osd.c vdr-1.7.14.ExtP_NG/osd.c
+--- vdr-1.7.14/osd.c 2010-01-22 16:58:39.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/osd.c 2010-04-10 15:45:10.754742652 +0200
+@@ -743,6 +743,9 @@
+ int cOsd::osdWidth = 0;
+ int cOsd::osdHeight = 0;
+ cVector<cOsd *> cOsd::Osds;
++#ifdef USE_PINPLUGIN
++bool cOsd::pinValid = false;
++#endif /* PINPLUGIN */
+
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+@@ -753,6 +756,9 @@
+ width = height = 0;
+ level = Level;
+ active = false;
++#ifdef USE_YAEPG
++ vidWin.bpp = 0;
++#endif /* YAEPG */
+ for (int i = 0; i < Osds.Size(); i++) {
+ if (Osds[i]->level > level) {
+ Osds.Insert(this, i);
+diff -ruN vdr-1.7.14/osd.h vdr-1.7.14.ExtP_NG/osd.h
+--- vdr-1.7.14/osd.h 2010-01-17 14:23:50.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/osd.h 2010-04-10 15:45:10.767741548 +0200
+@@ -270,6 +270,10 @@
+ int left, top, width, height;
+ uint level;
+ bool active;
++#ifdef USE_YAEPG
++public:
++ tArea vidWin;
++#endif /* YAEPG */
+ protected:
+ cOsd(int Left, int Top, uint Level);
+ ///< Initializes the OSD with the given coordinates.
+@@ -401,6 +405,9 @@
+ ///< 7: vertical, falling, upper
+ virtual void Flush(void);
+ ///< Actually commits all data to the OSD hardware.
++#ifdef USE_PINPLUGIN
++ static bool pinValid;
++#endif /* PINPLUGIN */
+ };
+
+ class cOsdProvider {
+diff -ruN vdr-1.7.14/osdbase.c vdr-1.7.14.ExtP_NG/osdbase.c
+--- vdr-1.7.14/osdbase.c 2010-01-17 12:36:12.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/osdbase.c 2010-04-10 15:45:10.777738279 +0200
+@@ -22,6 +22,9 @@
+ state = State;
+ selectable = true;
+ fresh = true;
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ subMenu = 0;
++#endif /* SETUP & PINPLUGIN */
+ }
+
+ cOsdItem::cOsdItem(const char *Text, eOSState State, bool Selectable)
+@@ -31,8 +34,23 @@
+ selectable = Selectable;
+ fresh = true;
+ SetText(Text);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ subMenu = 0;
++#endif /* SETUP & PINPLUGIN */
+ }
+
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++cOsdItem::cOsdItem(const char *Text, eOSState State, cSubMenuNode* SubMenu)
++{
++ text = NULL;
++ state = State;
++ selectable = true;
++ fresh = true;
++ SetText(Text);
++ subMenu = SubMenu;
++}
++#endif /* SETUP & PINPLUGIN */
++
+ cOsdItem::~cOsdItem()
+ {
+ free(text);
+@@ -77,6 +95,9 @@
+ {
+ isMenu = true;
+ digit = 0;
++#ifdef USE_LIEMIEXT
++ key_nr = -1;
++#endif /* LIEMIEXT */
+ hasHotkeys = false;
+ title = NULL;
+ SetTitle(Title);
+@@ -97,6 +118,9 @@
+ free(status);
+ displayMenu->Clear();
+ cStatus::MsgOsdClear();
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdMenuDestroy();
++#endif /* GRAPHTFT */
+ if (!--displayMenuCount)
+ DELETENULL(displayMenu);
+ }
+@@ -119,7 +143,11 @@
+ digit = -1; // prevents automatic hotkeys - input already has them
+ if (digit >= 0) {
+ digit++;
++#ifdef USE_LIEMIEXT
++ buffer = cString::sprintf(" %2d%s %s", digit, (digit > 9) ? "" : " ", s);
++#else
+ buffer = cString::sprintf(" %c %s", (digit < 10) ? '0' + digit : ' ' , s);
++#endif /* LIEMIEXT */
+ s = buffer;
+ }
+ }
+@@ -202,6 +230,9 @@
+ displayMenu->SetMessage(mtStatus, NULL);
+ displayMenu->Clear();
+ cStatus::MsgOsdClear();
++#ifdef USE_GRAPHTFT
++ cStatus::MsgOsdMenuDisplay(MenuKind());
++#endif /* GRAPHTFT */
+ displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
+ displayMenu->SetTitle(title);
+ cStatus::MsgOsdTitle(title);
+@@ -449,20 +480,79 @@
+ }
+ }
+
++#ifdef USE_LIEMIEXT
++#define MENUKEY_TIMEOUT 1500
++#endif /* LIEMIEXT */
++
+ eOSState cOsdMenu::HotKey(eKeys Key)
+ {
++#ifdef USE_LIEMIEXT
++ bool match = false;
++ bool highlight = false;
++ int item_nr;
++ int i;
++
++ if (Key == kNone) {
++ if (lastActivity.TimedOut())
++ Key = kOk;
++ else
++ return osContinue;
++ }
++ else {
++ lastActivity.Set(MENUKEY_TIMEOUT);
++ }
++ for (cOsdItem *item = Last(); item; item = Prev(item)) {
++#else
+ for (cOsdItem *item = First(); item; item = Next(item)) {
++#endif /* LIEMIEXT */
+ const char *s = item->Text();
++#ifdef USE_LIEMIEXT
++ i = 0;
++ item_nr = 0;
++ if (s && (s = skipspace(s)) != '\0' && '0' <= s[i] && s[i] <= '9') {
++ do {
++ item_nr = item_nr * 10 + (s[i] - '0');
++ }
++ while ( !((s[++i] == '\t')||(s[i] == ' ')) && (s[i] != '\0') && ('0' <= s[i]) && (s[i] <= '9'));
++ if ((Key == kOk) && (item_nr == key_nr)) {
++#else
+ if (s && (s = skipspace(s)) != NULL) {
+ if (*s == Key - k1 + '1') {
++#endif /* LIEMIEXT */
+ current = item->Index();
+ RefreshCurrent();
+ Display();
+ cRemote::Put(kOk, true);
++#ifdef USE_LIEMIEXT
++ key_nr = -1;
++#endif /* LIEMIEXT */
+ break;
+ }
++#ifdef USE_LIEMIEXT
++ else if (Key != kOk) {
++ if (!highlight && (item_nr == (Key - k0))) {
++ highlight = true;
++ current = item->Index();
++ }
++ if (!match && (key_nr == -1) && ((item_nr / 10) == (Key - k0))) {
++ match = true;
++ key_nr = (Key - k0);
++ }
++ else if (((key_nr == -1) && (item_nr == (Key - k0))) || (!match && (key_nr >= 0) && (item_nr == (10 * key_nr + Key - k0)))) {
++ current = item->Index();
++ cRemote::Put(kOk, true);
++ key_nr = -1;
++ break;
++ }
++ }
++#endif /* LIEMIEXT */
+ }
+ }
++#ifdef USE_LIEMIEXT
++ if ((!match) && (Key != kNone)) {
++ key_nr = -1;
++ }
++#endif /* LIEMIEXT */
+ return osContinue;
+ }
+
+@@ -501,8 +591,13 @@
+ }
+ }
+ switch (Key) {
++#ifdef USE_LIEMIEXT
++ case kNone:
++ case k0...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
++#else
+ case k0: return osUnknown;
+ case k1...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
++#endif /* LIEMIEXT */
+ case kUp|k_Repeat:
+ case kUp: CursorUp(); break;
+ case kDown|k_Repeat:
+diff -ruN vdr-1.7.14/osdbase.h vdr-1.7.14.ExtP_NG/osdbase.h
+--- vdr-1.7.14/osdbase.h 2010-01-16 15:25:31.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/osdbase.h 2010-04-10 15:45:10.786736628 +0200
+@@ -15,6 +15,10 @@
+ #include "skins.h"
+ #include "tools.h"
+
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++#include "submenu.h"
++#endif /* SETUP & PINPLUGIN */
++
+ enum eOSState { osUnknown,
+ osContinue,
+ osSchedule,
+@@ -51,16 +55,26 @@
+ char *text;
+ eOSState state;
+ bool selectable;
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ cSubMenuNode* subMenu;
++#endif /* SETUP & PINPLUGIN */
+ protected:
+ bool fresh;
+ public:
+ cOsdItem(eOSState State = osUnknown);
+ cOsdItem(const char *Text, eOSState State = osUnknown, bool Selectable = true);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ cOsdItem(const char *Text, eOSState State, cSubMenuNode* SubMenu);
++#endif /* SETUP & PINPLUGIN */
+ virtual ~cOsdItem();
+ bool Selectable(void) const { return selectable; }
+ void SetText(const char *Text, bool Copy = true);
+ void SetSelectable(bool Selectable);
+ void SetFresh(bool Fresh);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++ void SetSubMenu(cSubMenuNode* SubMenu) { subMenu = SubMenu; }
++ cSubMenuNode* SubMenu() { return subMenu; }
++#endif /* SETUP & PINPLUGIN */
+ const char *Text(void) const { return text; }
+ virtual void Set(void) {}
+ virtual eOSState ProcessKey(eKeys Key);
+@@ -95,6 +109,10 @@
+ char *status;
+ int digit;
+ bool hasHotkeys;
++#ifdef USE_LIEMIEXT
++ int key_nr;
++ cTimeMs lastActivity;
++#endif /* LIEMIEXT */
+ protected:
+ void SetDisplayMenu(void);
+ cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
+@@ -131,6 +149,9 @@
+ void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++ virtual const char* MenuKind() { return "MenuUnknown"; }
++#endif /* GRAPHTFT */
+ };
+
+ #endif //__OSDBASE_H
+diff -ruN vdr-1.7.14/pat.c vdr-1.7.14.ExtP_NG/pat.c
+--- vdr-1.7.14/pat.c 2010-03-06 13:00:30.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/pat.c 2010-04-10 15:45:10.802741457 +0200
+@@ -13,6 +13,9 @@
+ #include "libsi/section.h"
+ #include "libsi/descriptor.h"
+ #include "thread.h"
++#ifdef USE_TTXTSUBS
++#include "vdrttxtsubshooks.h"
++#endif /* TTXTSUBS */
+
+ #define PMT_SCAN_TIMEOUT 10 // seconds
+
+@@ -341,6 +344,10 @@
+ char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" };
+ char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" };
+ int Tpid = 0;
++#ifdef USE_TTXTSUBS
++ tTeletextSubtitlePage TeletextSubtitlePages[MAXTXTPAGES];
++ int NumTPages = 0;
++#endif /* TTXTSUBS */
+ int NumApids = 0;
+ int NumDpids = 0;
+ int NumSpids = 0;
+@@ -426,8 +433,27 @@
+ NumSpids++;
+ }
+ break;
++#ifdef USE_TTXTSUBS
++ case SI::TeletextDescriptorTag: {
++#else
+ case SI::TeletextDescriptorTag:
++#endif /* TTXTSUBS */
+ Tpid = esPid;
++#ifdef USE_TTXTSUBS
++ SI::TeletextDescriptor *sd = (SI::TeletextDescriptor *)d;
++ SI::TeletextDescriptor::Teletext ttxt;
++ for (SI::Loop::Iterator it; sd->teletextLoop.getNext(ttxt, it); ) {
++ bool isSubtitlePage = (ttxt.getTeletextType() == 0x02) || (ttxt.getTeletextType() == 0x05);
++ if ((NumTPages < MAXTXTPAGES) && ttxt.languageCode[0] && isSubtitlePage) {
++ strn0cpy(TeletextSubtitlePages[NumTPages].ttxtLanguage, I18nNormalizeLanguageCode(ttxt.languageCode), MAXLANGCODE1);
++ TeletextSubtitlePages[NumTPages].ttxtPage = ttxt.getTeletextPageNumber();
++ TeletextSubtitlePages[NumTPages].ttxtMagazine = ttxt.getTeletextMagazineNumber();
++ TeletextSubtitlePages[NumTPages].ttxtType = ttxt.getTeletextType();
++ NumTPages++;
++ }
++ }
++ }
++#endif /* TTXTSUBS */
+ break;
+ case SI::ISO639LanguageDescriptorTag: {
+ SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
+diff -ruN vdr-1.7.14/plugin.c vdr-1.7.14.ExtP_NG/plugin.c
+--- vdr-1.7.14/plugin.c 2010-01-06 12:36:46.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/plugin.c 2010-04-10 15:45:10.848736538 +0200
+@@ -317,6 +317,14 @@
+ char *p = strchr(s, ' ');
+ if (p)
+ *p = 0;
++#ifdef USE_PLUGINMISSING
++ struct stat st;
++ if (stat (cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), &st) && errno == ENOENT) {
++ esyslog("WARN: missing plugin '%s'", s);
++ fprintf(stderr, "vdr: missing plugin '%s'\n", s);
++ }
++ else
++#endif /* PLUGINMISSING */
+ dlls.Add(new cDll(cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), Args));
+ free(s);
+ }
+@@ -325,7 +333,11 @@
+ {
+ for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
+ if (!dll->Load(Log))
++#ifdef USE_PLUGINMISSING
++ ;
++#else
+ return false;
++#endif /* PLUGINMISSING */
+ }
+ return true;
+ }
+diff -ruN vdr-1.7.14/plugin.h vdr-1.7.14.ExtP_NG/plugin.h
+--- vdr-1.7.14/plugin.h 2007-08-04 11:56:26.000000000 +0200
++++ vdr-1.7.14.ExtP_NG/plugin.h 2010-04-10 15:45:10.861747307 +0200
+@@ -45,6 +45,9 @@
+
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
++#ifdef USE_MCLI
++ virtual cOsdObject *AltMenuAction(void) { return NULL; };
++#endif /* MCLI */
+
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+diff -ruN vdr-1.7.14/po/ca_ES.po vdr-1.7.14.ExtP_NG/po/ca_ES.po
+--- vdr-1.7.14/po/ca_ES.po 2010-03-12 17:41:03.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/ca_ES.po 2010-04-10 15:45:12.406741501 +0200
+@@ -39,6 +39,9 @@
+ msgid "none"
+ msgstr "cap"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarització"
+
+@@ -913,6 +916,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Actualitza canals"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Idiomes d'àudio"
+
+diff -ruN vdr-1.7.14/po/cs_CZ.po vdr-1.7.14.ExtP_NG/po/cs_CZ.po
+--- vdr-1.7.14/po/cs_CZ.po 2010-03-12 17:41:03.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/cs_CZ.po 2010-04-10 15:45:12.435736716 +0200
+@@ -37,6 +37,9 @@
+ msgid "none"
+ msgstr "¾ádný"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarizace"
+
+@@ -911,6 +914,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Aktualizace kanálù"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Jazyky zvuku"
+
+diff -ruN vdr-1.7.14/po/da_DK.po vdr-1.7.14.ExtP_NG/po/da_DK.po
+--- vdr-1.7.14/po/da_DK.po 2010-03-12 17:41:03.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/da_DK.po 2010-04-10 15:45:12.467742106 +0200
+@@ -36,6 +36,9 @@
+ msgid "none"
+ msgstr "ingen"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarisation"
+
+@@ -910,6 +913,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Opdatér kanaler"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Audio sprog (ant.)"
+
+diff -ruN vdr-1.7.14/po/de_DE.po vdr-1.7.14.ExtP_NG/po/de_DE.po
+--- vdr-1.7.14/po/de_DE.po 2010-03-12 17:41:03.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/de_DE.po 2010-04-10 15:45:12.504739261 +0200
+@@ -36,6 +36,9 @@
+ msgid "none"
+ msgstr "keine"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarisation"
+
+@@ -842,6 +845,24 @@
+ msgid "Button$Scan"
+ msgstr "Scan"
+
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Zeitspanne für dop. EPG-Suche(min)"
++
++msgid "Setup.EPG$extern double Epg entry"
++msgstr "Doppelten externen EPG-Eintrag"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Internen und externen EPG mischen"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Erk. des lauf. VPS-Events abschalten"
++
++msgid "Setup.Recording$Jump Seconds"
++msgstr "Sprungweite in Sekunden"
++
++msgid "Setup.Recording$Jump Seconds (repeated)"
++msgstr "Sprungweite in Sek. (wiederh.)"
++
+ msgid "Setup.EPG$EPG scan timeout (h)"
+ msgstr "Zeit bis zur EPG-Aktualisierung (h)"
+
+@@ -895,6 +916,9 @@
+ msgid "DVB"
+ msgstr "DVB"
+
++msgid "Setup.DVB$Use DVB receivers"
++msgstr "DVB Empfangsteile benutzen"
++
+ msgid "Setup.DVB$Primary DVB interface"
+ msgstr "Primäres DVB-Interface"
+
+@@ -910,6 +934,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Kanäle aktualisieren"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr "Kanal per Rid zuordnen"
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Audio-Sprachen"
+
+@@ -934,6 +961,9 @@
+ msgid "Setup.DVB$Subtitle background transparency"
+ msgstr "Untertitel-Transparenz Hintergrund"
+
++msgid "Setup.DVB$Record Teletext Subtitles"
++msgstr "Teletext-Untertitel aufnehmen"
++
+ msgid "LNB"
+ msgstr "LNB"
+
+@@ -1051,6 +1081,18 @@
+ msgid "Replay"
+ msgstr "Wiedergabe"
+
++msgid "Setup.Replay$Jump&Play"
++msgstr "Wiedergabe nach Sprung"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Sprung bei Schnittmarke"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pause bei letzter Marke"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Marken aktualisieren"
++
+ msgid "Setup.Replay$Multi speed mode"
+ msgstr "Mehrstufiger Vor-/Rücklauf"
+
+@@ -1315,3 +1357,57 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR wird in %s Minuten ausschalten"
++
++msgid "Rename recording"
++msgstr "Aufzeichnung umbenennen"
++
++msgid "Date"
++msgstr "Datum"
++
++msgid "Length"
++msgstr "Länge"
++
++msgid "Size"
++msgstr "Größe"
++
++msgid "Format"
++msgstr "Format"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++msgid "Delete marks information?"
++msgstr "Marks löschen?"
++
++msgid "Delete resume information?"
++msgstr "Resume löschen?"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Befehle Position im Hauptmenü"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Zeitbalken anzeigen"
++
++msgid "Setup.Recording$Show date"
++msgstr "Aufnahmedatum anzeigen"
++
++msgid "Setup.Recording$Show time"
++msgstr "AufnahmeZeit anzeigen"
++
++msgid "Setup.Recording$Show length"
++msgstr "Länge der Aufnahme anzeigen"
++
++msgid "Channel locked by LNB!"
++msgstr "Kanal durch LNB gesperrt!"
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "DVB-Empfänger %d nutzt LNB Nr."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "LNB-Nutzung protokollieren"
++
++msgid "Childlock"
++msgstr "Kindersicherung"
+diff -ruN vdr-1.7.14/po/el_GR.po vdr-1.7.14.ExtP_NG/po/el_GR.po
+--- vdr-1.7.14/po/el_GR.po 2010-03-12 17:41:03.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/el_GR.po 2010-04-10 15:45:12.539740766 +0200
+@@ -36,6 +36,9 @@
+ msgid "none"
+ msgstr "êáíÝíá"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Ðüëùóç"
+
+@@ -910,6 +913,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "ÅíçìÝñùóç êáíáëéþí"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Ãëþóóåò Þ÷ïõ"
+
+diff -ruN vdr-1.7.14/po/es_ES.po vdr-1.7.14.ExtP_NG/po/es_ES.po
+--- vdr-1.7.14/po/es_ES.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/es_ES.po 2010-04-10 15:45:12.568741800 +0200
+@@ -37,6 +37,9 @@
+ msgid "none"
+ msgstr "ninguno"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarización"
+
+@@ -911,6 +914,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Actualizar canales"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Idiomas de audio"
+
+diff -ruN vdr-1.7.14/po/et_EE.po vdr-1.7.14.ExtP_NG/po/et_EE.po
+--- vdr-1.7.14/po/et_EE.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/et_EE.po 2010-04-10 15:45:12.591740207 +0200
+@@ -36,6 +36,9 @@
+ msgid "none"
+ msgstr "puudu"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarisatsioon"
+
+@@ -910,6 +913,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Kanalite uuendamine"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Audio keeli"
+
+@@ -1315,3 +1321,45 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR lülitub välja %s minuti pärast"
++
++msgid "Rename recording"
++msgstr "Ümbernimetamine"
++
++msgid "Date"
++msgstr "Kuupäev"
++
++msgid "Length"
++msgstr "Kestus"
++
++msgid "Size"
++msgstr "Suurus"
++
++msgid "Format"
++msgstr "Formaat"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++msgid "Delete marks information?"
++msgstr "Kustutada märkide info?"
++
++msgid "Delete resume information?"
++msgstr "Kustutada jätkamise info?"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Käsu asukoht peamenüüs"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Edenemisriba"
++
++msgid "Setup.Recording$Show date"
++msgstr "Salvestuse kuupäev"
++
++msgid "Setup.Recording$Show time"
++msgstr "Salvestuse kellaaeg"
++
++msgid "Setup.Recording$Show length"
++msgstr "Salvestuse kestus"
+diff -ruN vdr-1.7.14/po/fi_FI.po vdr-1.7.14.ExtP_NG/po/fi_FI.po
+--- vdr-1.7.14/po/fi_FI.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/fi_FI.po 2010-04-10 15:45:12.620745376 +0200
+@@ -39,6 +39,9 @@
+ msgid "none"
+ msgstr "tyhjä"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarisaatio"
+
+@@ -913,6 +916,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Päivitä kanavat"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Äänen kielet"
+
+@@ -1318,3 +1324,48 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR sammuu %s minuutin kuluttua"
++
++msgid "Rename recording"
++msgstr "Nimeä tallenne"
++
++msgid "Date"
++msgstr "Päiväys"
++
++msgid "Length"
++msgstr "Pituus"
++
++msgid "Size"
++msgstr "Koko"
++
++msgid "Format"
++msgstr "Tiedostomuoto"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++msgid "Delete marks information?"
++msgstr "Poista tallenteen merkinnät?"
++
++msgid "Delete resume information?"
++msgstr "Poista tallenteen paluutiedot?"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Komentojen sijainti päävalikossa"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Näytä aikajana"
++
++msgid "Setup.Recording$Show date"
++msgstr "Näytä tallenteen päiväys"
++
++msgid "Setup.Recording$Show time"
++msgstr "Näytä tallenteen ajankohta"
++
++msgid "Setup.Recording$Show length"
++msgstr "Näytä tallenteen kesto"
++
++msgid "Parameters"
++msgstr "Parametrit"
+diff -ruN vdr-1.7.14/po/fr_FR.po vdr-1.7.14.ExtP_NG/po/fr_FR.po
+--- vdr-1.7.14/po/fr_FR.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/fr_FR.po 2010-04-10 15:45:12.650738547 +0200
+@@ -42,6 +42,9 @@
+ msgid "none"
+ msgstr "aucun"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarisation"
+
+@@ -848,6 +851,18 @@
+ msgid "Button$Scan"
+ msgstr "Scan"
+
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Intervalle de recherche du double EPG(min)"
++
++msgid "Setup.EPG$extern double Epg entry"
++msgstr "Entrée EPG externe en double"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Mixer EPG interne et externe"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Désactiver événement VPS"
++
+ msgid "Setup.EPG$EPG scan timeout (h)"
+ msgstr "Inactivité avant rech. EPG (h)"
+
+@@ -916,6 +931,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Mettre à jour les chaînes"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Langues audio"
+
+@@ -1057,6 +1075,18 @@
+ msgid "Replay"
+ msgstr "Lecture"
+
++msgid "Setup.Replay$Jump&Play"
++msgstr "Lecture après saut"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Saut sur les marques de découpes"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pause après la dernière marque"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Actualiser les marques"
++
+ msgid "Setup.Replay$Multi speed mode"
+ msgstr "Mode multi-vitesses"
+
+@@ -1321,3 +1351,48 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR s'arrêtera dans %s minutes"
++
++msgid "Rename recording"
++msgstr "Renommer l'enregistrement"
++
++msgid "Date"
++msgstr "Date"
++
++msgid "Length"
++msgstr "Longueur"
++
++msgid "Size"
++msgstr "Taille"
++
++msgid "Format"
++msgstr "Format"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++msgid "Delete marks information?"
++msgstr "Effacer les informations de marquage"
++
++msgid "Delete resume information?"
++msgstr "Effacer les informations de reprise"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Position des commandes dans le menu"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Montrer la barre de progression"
++
++msgid "Setup.Recording$Show date"
++msgstr "Montrer la date d'enregistrement"
++
++msgid "Setup.Recording$Show time"
++msgstr "Montrer l'heure d'enregistrement"
++
++msgid "Setup.Recording$Show length"
++msgstr "Montrer la longueur de l'enregistrement"
++
++msgid "Parameters"
++msgstr "Paramètres"
+diff -ruN vdr-1.7.14/po/hr_HR.po vdr-1.7.14.ExtP_NG/po/hr_HR.po
+--- vdr-1.7.14/po/hr_HR.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/hr_HR.po 2010-04-10 15:45:12.676740196 +0200
+@@ -38,6 +38,9 @@
+ msgid "none"
+ msgstr "ni¹ta"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarizacija"
+
+@@ -912,6 +915,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Aktualiziraj programe"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Audio jezici"
+
+diff -ruN vdr-1.7.14/po/hu_HU.po vdr-1.7.14.ExtP_NG/po/hu_HU.po
+--- vdr-1.7.14/po/hu_HU.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/hu_HU.po 2010-04-10 15:45:12.697741656 +0200
+@@ -39,6 +39,9 @@
+ msgid "none"
+ msgstr "semmi"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarizáció"
+
+@@ -913,6 +916,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Adók aktualizálása"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Audio nyelvek"
+
+diff -ruN vdr-1.7.14/po/it_IT.po vdr-1.7.14.ExtP_NG/po/it_IT.po
+--- vdr-1.7.14/po/it_IT.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/it_IT.po 2010-04-10 15:45:12.723739553 +0200
+@@ -43,6 +43,9 @@
+ msgid "none"
+ msgstr "nessuno"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarizzazione"
+
+@@ -917,6 +920,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Aggiornamento canali"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Lingue audio"
+
+diff -ruN vdr-1.7.14/po/nl_NL.po vdr-1.7.14.ExtP_NG/po/nl_NL.po
+--- vdr-1.7.14/po/nl_NL.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/nl_NL.po 2010-04-10 15:45:12.785740553 +0200
+@@ -40,6 +40,9 @@
+ msgid "none"
+ msgstr "geen"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarisatie"
+
+@@ -914,6 +917,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Kanalen actualiseren"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Audio talen"
+
+diff -ruN vdr-1.7.14/po/nn_NO.po vdr-1.7.14.ExtP_NG/po/nn_NO.po
+--- vdr-1.7.14/po/nn_NO.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/nn_NO.po 2010-04-10 15:45:12.809737908 +0200
+@@ -37,6 +37,9 @@
+ msgid "none"
+ msgstr ""
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarisasjon"
+
+@@ -911,6 +914,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr ""
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr ""
+
+diff -ruN vdr-1.7.14/po/pl_PL.po vdr-1.7.14.ExtP_NG/po/pl_PL.po
+--- vdr-1.7.14/po/pl_PL.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/pl_PL.po 2010-04-10 15:45:12.835738610 +0200
+@@ -37,6 +37,9 @@
+ msgid "none"
+ msgstr "brak"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polaryzacja"
+
+@@ -911,6 +914,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Aktualizuj kana³y"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Jêzyków d¼wiêku"
+
+diff -ruN vdr-1.7.14/po/pt_PT.po vdr-1.7.14.ExtP_NG/po/pt_PT.po
+--- vdr-1.7.14/po/pt_PT.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/pt_PT.po 2010-04-10 15:45:12.870742039 +0200
+@@ -36,6 +36,9 @@
+ msgid "none"
+ msgstr "nenhum"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarização"
+
+@@ -910,6 +913,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Actualizar canais"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Idiomas de audio"
+
+diff -ruN vdr-1.7.14/po/ro_RO.po vdr-1.7.14.ExtP_NG/po/ro_RO.po
+--- vdr-1.7.14/po/ro_RO.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/ro_RO.po 2010-04-10 15:45:12.889740524 +0200
+@@ -39,6 +39,9 @@
+ msgid "none"
+ msgstr "niciuna(ul)"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarizare"
+
+@@ -913,6 +916,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Actualizare canale"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Limbi sunet"
+
+diff -ruN vdr-1.7.14/po/ru_RU.po vdr-1.7.14.ExtP_NG/po/ru_RU.po
+--- vdr-1.7.14/po/ru_RU.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/ru_RU.po 2010-04-10 15:45:12.921738012 +0200
+@@ -37,6 +37,9 @@
+ msgid "none"
+ msgstr "ÝØçÕÓÞ"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "¿ÞÛïàØ×ÐæØï"
+
+@@ -911,6 +914,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "¾ÑÝÞÒÛïâì ÝÐáâàÞÙÚØ ÚÐÝÐÛÞÒ"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "¿àÕÔßÞçØâÐÕÜëÕ ï×ëÚØ (×ÒãÚ)"
+
+@@ -1316,3 +1322,45 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR ÒëÚÛîçØâáï çÕàÕ× %s ÜØÝãâ"
++
++msgid "Rename recording"
++msgstr "¿ÕàÕØÜÕÝÞÒÐâì ×Ðߨáì"
++
++msgid "Date"
++msgstr ""
++
++msgid "Length"
++msgstr ""
++
++msgid "Size"
++msgstr ""
++
++msgid "Format"
++msgstr ""
++
++msgid "PES"
++msgstr ""
++
++msgid "TS"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr ""
++
++msgid "Delete resume information?"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "ÀÐ×ÜÕéÕÝØÕ ÚÞÜÐÝÔ Ò ÓÛÐÒÝÞÜ ÜÕÝî"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr "¿ÞÚÐ×ëÒÐâì ÔÐâã"
++
++msgid "Setup.Recording$Show time"
++msgstr "¿ÞÚÐ×ëÒÐâì ÒàÕÜï ×ÐߨáØ"
++
++msgid "Setup.Recording$Show length"
++msgstr "¿ÞÚÐ×ëÒÐâì ßàÞÔÞÛÖØâÕÛìÝÞáâì ×ÐߨáØ"
+diff -ruN vdr-1.7.14/po/sl_SI.po vdr-1.7.14.ExtP_NG/po/sl_SI.po
+--- vdr-1.7.14/po/sl_SI.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/sl_SI.po 2010-04-10 15:45:12.971742442 +0200
+@@ -37,6 +37,9 @@
+ msgid "none"
+ msgstr "nobeden"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarizacija"
+
+@@ -911,6 +914,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Posodobi kanale"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Jeziki za zvok"
+
+diff -ruN vdr-1.7.14/po/sv_SE.po vdr-1.7.14.ExtP_NG/po/sv_SE.po
+--- vdr-1.7.14/po/sv_SE.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/sv_SE.po 2010-04-10 15:45:12.996740450 +0200
+@@ -39,6 +39,9 @@
+ msgid "none"
+ msgstr "ingen"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Polarisation"
+
+@@ -913,6 +916,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Uppdatera kanaler"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Antal ljudspråk"
+
+diff -ruN vdr-1.7.14/po/tr_TR.po vdr-1.7.14.ExtP_NG/po/tr_TR.po
+--- vdr-1.7.14/po/tr_TR.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/tr_TR.po 2010-04-10 15:45:13.021741692 +0200
+@@ -36,6 +36,9 @@
+ msgid "none"
+ msgstr "hiç"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "Kutuplaþma"
+
+@@ -910,6 +913,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Kanallarý yenile"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Audio dilleri"
+
+diff -ruN vdr-1.7.14/po/uk_UA.po vdr-1.7.14.ExtP_NG/po/uk_UA.po
+--- vdr-1.7.14/po/uk_UA.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/uk_UA.po 2010-04-10 15:45:13.061743013 +0200
+@@ -36,6 +36,9 @@
+ msgid "none"
+ msgstr "нічого"
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "ПолÑризаціÑ"
+
+@@ -910,6 +913,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "Оновлювати наÑтройки каналів"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "Бажані мови (звук)"
+
+diff -ruN vdr-1.7.14/po/zh_CN.po vdr-1.7.14.ExtP_NG/po/zh_CN.po
+--- vdr-1.7.14/po/zh_CN.po 2010-03-12 17:41:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/po/zh_CN.po 2010-04-10 15:45:13.092741645 +0200
+@@ -39,6 +39,9 @@
+ msgid "none"
+ msgstr "æ— "
+
++msgid "Rid"
++msgstr "Rid"
++
+ msgid "Polarization"
+ msgstr "æžåŒ–æ–¹å¼"
+
+@@ -913,6 +916,9 @@
+ msgid "Setup.DVB$Update channels"
+ msgstr "更新频é“"
+
++msgid "Setup.DVB$channel binding by Rid"
++msgstr ""
++
+ msgid "Setup.DVB$Audio languages"
+ msgstr "声é“语言"
+
+diff -ruN vdr-1.7.14/receiver.c vdr-1.7.14.ExtP_NG/receiver.c
+--- vdr-1.7.14/receiver.c 2010-02-28 15:25:32.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/receiver.c 2010-04-10 15:45:11.056767270 +0200
+@@ -82,7 +82,12 @@
+ (Channel->Ppid() == Channel->Vpid() || AddPid(Channel->Ppid())) &&
+ AddPids(Channel->Apids()) &&
+ AddPids(Channel->Dpids()) &&
++#ifdef USE_TTXTSUBS
++ AddPids(Channel->Spids()) &&
++ (!Setup.SupportTeletext || AddPid(Channel->Tpid()));
++#else
+ AddPids(Channel->Spids());
++#endif /* TTXTSUBS */
+ }
+ return true;
+ }
+diff -ruN vdr-1.7.14/recorder.c vdr-1.7.14.ExtP_NG/recorder.c
+--- vdr-1.7.14/recorder.c 2010-01-29 17:37:22.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/recorder.c 2010-04-10 15:45:11.083738273 +0200
+@@ -87,7 +87,11 @@
+ bool cRecorder::NextFile(void)
+ {
+ if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
++#ifdef USE_HARDLINKCUTTER
++ if (fileSize > fileName->MaxFileSize() || RunningLowOnDiskSpace()) {
++#else
+ if (fileSize > MEGABYTE(off_t(Setup.MaxVideoFileSize)) || RunningLowOnDiskSpace()) {
++#endif /* HARDLINKCUTTER */
+ recordFile = fileName->NextFile();
+ fileSize = 0;
+ }
+diff -ruN vdr-1.7.14/recording.c vdr-1.7.14.ExtP_NG/recording.c
+--- vdr-1.7.14/recording.c 2010-03-07 15:06:04.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/recording.c 2010-04-10 15:45:11.124740932 +0200
+@@ -8,6 +8,9 @@
+ */
+
+ #include "recording.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
+ #include <ctype.h>
+ #include <dirent.h>
+ #include <errno.h>
+@@ -63,7 +66,13 @@
+ #define MAX_LINK_LEVEL 6
+
+ bool VfatFileSystem = false;
++#ifdef USE_LIEMIEXT
++bool DirOrderState = false;
++#endif /* LIEMIEXT */
+ int InstanceId = 0;
++#ifdef USE_DVLFRIENDLYFNAMES
++char *MakeFriendlyFilename(char **buf);
++#endif /* DVLFRIENDLYFNAMES */
+
+ cRecordings DeletedRecordings(true);
+
+@@ -601,7 +610,14 @@
+ {
+ resume = RESUME_NOT_INITIALIZED;
+ titleBuffer = NULL;
++#ifdef USE_SORTRECORDS
++ for (int i = 0; i < MAXSORTMODES; i++) {
++ sortBuffer[i] = NULL;
++ lastDirsFirst[i] = -1;
++ }
++#else
+ sortBuffer = NULL;
++#endif /* SORTRECORDS */
+ fileName = NULL;
+ name = NULL;
+ fileSizeMB = -1; // unknown
+@@ -638,6 +654,11 @@
+ break;
+ }
+ if (Timer->IsSingleEvent()) {
++#ifdef USE_DVLFRIENDLYFNAMES
++ if (Setup.UseFriendlyFNames == 1)
++ Timer -> SetFile(MakeFriendlyFilename(&name));
++ else
++#endif /* DVLFRIENDLYFNAMES */
+ Timer->SetFile(name); // this was an instant recording, so let's set the actual data
+ Timers.SetModified();
+ }
+@@ -648,6 +669,10 @@
+ name = strdup(cString::sprintf("%s~%s", Timer->File(), Subtitle));
+ // substitute characters that would cause problems in file names:
+ strreplace(name, '\n', ' ');
++#ifdef USE_DVLFRIENDLYFNAMES
++ if (Setup.UseFriendlyFNames == 1)
++ MakeFriendlyFilename(&name);
++#endif /* DVLFRIENDLYFNAMES */
+ start = Timer->StartTime();
+ priority = Timer->Priority();
+ lifetime = Timer->Lifetime();
+@@ -670,7 +695,14 @@
+ framesPerSecond = DEFAULTFRAMESPERSECOND;
+ deleted = 0;
+ titleBuffer = NULL;
++#ifdef USE_SORTRECORDS
++ for (int i = 0; i < MAXSORTMODES; i++) {
++ sortBuffer[i] = NULL;
++ lastDirsFirst[i] = -1;
++ }
++#else
+ sortBuffer = NULL;
++#endif /* SORTRECORDS */
+ FileName = fileName = strdup(FileName);
+ if (*(fileName + strlen(fileName) - 1) == '/')
+ *(fileName + strlen(fileName) - 1) = 0;
+@@ -772,7 +804,13 @@
+ cRecording::~cRecording()
+ {
+ free(titleBuffer);
++#ifdef USE_SORTRECORDS
++ for (int i = 0; i < MAXSORTMODES; i++) {
++ free(sortBuffer[i]);
++ }
++#else
+ free(sortBuffer);
++#endif /* SORTRECORDS */
+ free(fileName);
+ free(name);
+ delete info;
+@@ -794,21 +832,46 @@
+ t++;
+ }
+ if (s1 && s2)
++#ifdef USE_SORTRECORDS
++ if (Setup.RecordingsSortDirsFirst)
++ *s1 = 'b';
++
++ if ((Setup.RecordingsSortMode <= 1 && s1 != s && !strchr(".-$ª·", *(s1 - 1))) ||
++ (Setup.RecordingsSortMode == 1 && s1 == s) ||
++ (Setup.RecordingsSortMode == 3))
++#endif /* SORTRECORDS */
+ memmove(s1 + 1, s2, t - s2 + 1);
+ return s;
+ }
+
+ char *cRecording::SortName(void) const
+ {
++#ifdef USE_SORTRECORDS
++ if (!sortBuffer[Setup.RecordingsSortMode] ||
++ lastDirsFirst[Setup.RecordingsSortMode] != Setup.RecordingsSortDirsFirst) {
++ free(sortBuffer[Setup.RecordingsSortMode]);
++ lastDirsFirst[Setup.RecordingsSortMode] = Setup.RecordingsSortDirsFirst;
++ char *s = StripEpisodeName(strdup(FileName() + strlen(VideoDirectory)));
++#else
+ if (!sortBuffer) {
+ char *s = StripEpisodeName(strdup(FileName() + strlen(VideoDirectory) + 1));
++#endif /* SORTRECORDS */
+ strreplace(s, '/', 'a'); // some locales ignore '/' when sorting
+ int l = strxfrm(NULL, s, 0) + 1;
++#ifdef USE_SORTRECORDS
++ sortBuffer[Setup.RecordingsSortMode] = MALLOC(char, l);
++ strxfrm(sortBuffer[Setup.RecordingsSortMode], s, l);
++#else
+ sortBuffer = MALLOC(char, l);
+ strxfrm(sortBuffer, s, l);
++#endif /* SORTRECORDS */
+ free(s);
+ }
++#ifdef USE_SORTRECORDS
++ return sortBuffer[Setup.RecordingsSortMode];
++#else
+ return sortBuffer;
++#endif /* SORTRECORDS */
+ }
+
+ int cRecording::GetResume(void) const
+@@ -823,7 +886,15 @@
+ int cRecording::Compare(const cListObject &ListObject) const
+ {
+ cRecording *r = (cRecording *)&ListObject;
++#ifdef USE_LIEMIEXT
++ if (DirOrderState)
++ return strcasecmp(FileName(), r->FileName());
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++ return Recordings.GetSortOrder() * strcasecmp(SortName(), r->SortName());
++#else
+ return strcasecmp(SortName(), r->SortName());
++#endif /* USE_SORTRECORDS */
+ }
+
+ const char *cRecording::FileName(void) const
+@@ -841,9 +912,17 @@
+ return fileName;
+ }
+
++#ifdef USE_LIEMIEXT
++const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level, bool Original) const
++#else
+ const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) const
++#endif /* LIEMIEXT */
+ {
++#ifdef USE_WAREAGLEICON
++ const char *New = NewIndicator && IsNew() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_NEW_UTF8 : ICON_NEW : "*" : " ";
++#else
+ char New = NewIndicator && IsNew() ? '*' : ' ';
++#endif /* WAREAGLEICON */
+ free(titleBuffer);
+ titleBuffer = NULL;
+ if (Level < 0 || Level == HierarchyLevels()) {
+@@ -854,7 +933,14 @@
+ s++;
+ else
+ s = name;
++#ifdef USE_LIEMIEXT
++ if (Original) {
++#endif /* LIEMIEXT */
++#ifdef USE_WAREAGLEICON
++ titleBuffer = strdup(cString::sprintf("%02d.%02d.%02d%c%02d:%02d%s%c%s",
++#else
+ titleBuffer = strdup(cString::sprintf("%02d.%02d.%02d%c%02d:%02d%c%c%s",
++#endif /* WAREAGLEICON */
+ t->tm_mday,
+ t->tm_mon + 1,
+ t->tm_year % 100,
+@@ -864,6 +950,33 @@
+ New,
+ Delimiter,
+ s));
++#ifdef USE_LIEMIEXT
++ }
++ else {
++ cString RecLength("---");
++ if (Setup.ShowRecLength && FileName()) {
++ int length = cIndexFile::Length(FileName(), IsPesRecording());
++ if (length >= 0)
++ RecLength = cString::sprintf("%d'", length / SecondsToFrames(60, framesPerSecond));
++ }
++ cString RecDate = cString::sprintf("%02d.%02d.%02d", t->tm_mday, t->tm_mon + 1, t->tm_year % 100);
++ cString RecTime = cString::sprintf("%02d:%02d", t->tm_hour, t->tm_min);
++ cString RecDelimiter = cString::sprintf("%c", Delimiter);
++#ifdef USE_WAREAGLEICON
++ titleBuffer = strdup(cString::sprintf("%s%s%s%s%s%s%s%s",
++#else
++ titleBuffer = strdup(cString::sprintf("%s%s%s%c%s%s%s%s",
++#endif /* WAREAGLEICON */
++ (Setup.ShowRecDate ? *RecDate : ""),
++ (Setup.ShowRecDate && Setup.ShowRecTime ? *RecDelimiter : ""),
++ (Setup.ShowRecTime ? *RecTime : ""),
++ New,
++ (Setup.ShowRecTime || Setup.ShowRecDate ? *RecDelimiter : ""),
++ (Setup.ShowRecLength ? *RecLength : ""),
++ (Setup.ShowRecLength ? *RecDelimiter : ""),
++ s));
++ }
++#endif /* LIEMIEXT */
+ // let's not display a trailing FOLDERDELIMCHAR:
+ if (!NewIndicator)
+ stripspace(titleBuffer);
+@@ -892,6 +1005,17 @@
+ return titleBuffer;
+ }
+
++#ifdef USE_CUTTIME
++void cRecording::SetStartTime(time_t Start)
++{
++ start=Start;
++ if (fileName) {
++ free(fileName);
++ fileName = NULL;
++ }
++}
++#endif /* CUTTIME */
++
+ const char *cRecording::PrefixFileName(char Prefix)
+ {
+ cString p = PrefixVideoFileName(FileName(), Prefix);
+@@ -1000,6 +1124,52 @@
+ resume = RESUME_NOT_INITIALIZED;
+ }
+
++#ifdef USE_LIEMIEXT
++bool cRecording::Rename(const char *newName)
++{
++ bool result = false;
++ struct tm tm_r;
++ struct tm *t = localtime_r(&start, &tm_r);
++ char *localNewName = ExchangeChars(strdup(newName), true);
++ const char *fmt = isPesRecording ? NAMEFORMATPES : NAMEFORMATTS;
++ int ch = isPesRecording ? priority : channel;
++ int ri = isPesRecording ? lifetime : instanceId;
++ char *newFileName = strdup(cString::sprintf(fmt, VideoDirectory, localNewName, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri));
++ free(localNewName);
++ if (strcmp(FileName(), newFileName)) {
++ if (access(newFileName, F_OK) == 0) {
++ isyslog("recording %s already exists", newFileName);
++ }
++ else {
++ isyslog("renaming recording %s to %s", FileName(), newFileName);
++ result = MakeDirs(newFileName, true);
++ if (result)
++ result = RenameVideoFile(FileName(), newFileName);
++ if (result) {
++ free(fileName);
++ fileName = strdup(newFileName);
++ free(name);
++ name = strdup(newName);
++#ifdef USE_SORTRECORDS
++ for (int i = 0; i < MAXSORTMODES; i++) {
++ free(sortBuffer[i]);
++ sortBuffer[i] = NULL;
++ }
++#else
++ free(sortBuffer);
++ sortBuffer = NULL;
++#endif /* SORTRECORDS */
++ free(titleBuffer);
++ titleBuffer = NULL;
++ }
++ }
++ }
++ free(newFileName);
++ return result;
++}
++
++#endif /* LIEMIEXT */
++
+ // --- cRecordings -----------------------------------------------------------
+
+ cRecordings Recordings;
+@@ -1012,6 +1182,9 @@
+ deleted = Deleted;
+ lastUpdate = 0;
+ state = 0;
++#ifdef USE_SORTRECORDS
++ SortOrder = 1;
++#endif /* SORTRECORDS */
+ }
+
+ cRecordings::~cRecordings()
+@@ -1298,14 +1471,74 @@
+ return NULL;
+ }
+
++#ifdef USE_JUMPPLAY
++// --- cMarksReload ----------------------------------------------------------
++
++#define MARKS_RELOAD_MS 10000
++
++time_t cMarksReload::lastsavetime = 0;
++
++cMarksReload::cMarksReload(const char *RecordingFileName)
++:recDir(RecordingFileName)
++{
++ struct stat sbuf;
++ cRecording rec(recDir);
++ if (Load(recDir, rec.FramesPerSecond(), rec.IsPesRecording()) &&
++ stat(FileName(), &sbuf) == 0)
++ lastmodtime = sbuf.st_mtime;
++ else
++ lastmodtime = 0;
++ nextreload.Set(MARKS_RELOAD_MS - cTimeMs::Now() % MARKS_RELOAD_MS);
++}
++
++bool cMarksReload::Reload(void)
++{
++ // Check the timestamp of marks.vdr in 10 seconds intervals
++ // Independent but synchronized reloading of marks in two threads
++ if ((Setup.ReloadMarks && nextreload.TimedOut()) ||
++ lastsavetime > lastmodtime) {
++ nextreload.Set(MARKS_RELOAD_MS - cTimeMs::Now() % MARKS_RELOAD_MS);
++ struct stat sbuf;
++ if (stat(FileName(), &sbuf) == 0 && sbuf.st_mtime != lastmodtime) {
++ lastmodtime = sbuf.st_mtime;
++ cRecording rec(recDir);
++ if (Load(recDir, rec.FramesPerSecond(), rec.IsPesRecording()))
++ return true;
++ }
++ }
++ return false;
++}
++
++bool cMarksReload::Save(void)
++{
++ bool ok = cMarks::Save();
++ struct stat sbuf;
++ if (ok && stat(FileName(), &sbuf) == 0)
++ lastsavetime = lastmodtime = sbuf.st_mtime;
++ return ok;
++}
++#endif /* JUMPPLAY */
++
+ // --- cRecordingUserCommand -------------------------------------------------
+
+ const char *cRecordingUserCommand::command = NULL;
+
++#ifdef USE_DVLRECSCRIPTADDON
++void cRecordingUserCommand::InvokeCommand(const char *State, const char *RecordingFileName, char *chanName)
++#else
+ void cRecordingUserCommand::InvokeCommand(const char *State, const char *RecordingFileName)
++#endif /* DVLRECSCRIPTADDON */
+ {
+ if (command) {
++#ifdef USE_DVLRECSCRIPTADDON
++ cString cmd;
++ if (chanName != NULL)
++ cmd = cString::sprintf("%s %s \"%s\" \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"), chanName);
++ else
++ cmd = cString::sprintf("%s %s \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"));
++#else
+ cString cmd = cString::sprintf("%s %s \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"));
++#endif /* DVLRECSCRIPTADDON */
+ isyslog("executing '%s'", *cmd);
+ SystemExec(cmd);
+ }
+@@ -1777,6 +2010,18 @@
+ return false;
+ }
+
++#ifdef USE_LIEMIEXT
++int cIndexFile::Length(const char *FileName, bool IsPesRecording)
++{
++ struct stat buf;
++ cString fullname = cString::sprintf("%s%s", FileName, IsPesRecording ? INDEXFILESUFFIX ".vdr" : INDEXFILESUFFIX);
++ if (FileName && *fullname && access(fullname, R_OK) == 0 && stat(fullname, &buf) == 0)
++ return buf.st_size ? (buf.st_size - 1) / sizeof(tIndexTs) + 1 : 0;
++ return -1;
++}
++
++#endif /* LIEMIEXT */
++
+ // --- cFileName -------------------------------------------------------------
+
+ #define MAXFILESPERRECORDINGPES 255
+@@ -1935,6 +2180,22 @@
+ return NULL;
+ }
+
++#ifdef USE_HARDLINKCUTTER
++off_t cFileName::MaxFileSize() {
++ const int maxVideoFileSize = isPesRecording ? MAXVIDEOFILESIZEPES : MAXVIDEOFILESIZETS;
++ const int setupMaxVideoFileSize = min(maxVideoFileSize, Setup.MaxVideoFileSize);
++ const int maxFileNumber = isPesRecording ? 255 : 65535;
++
++ const off_t smallFiles = (maxFileNumber * off_t(maxVideoFileSize) - 1024 * Setup.MaxRecordingSize)
++ / max(maxVideoFileSize - setupMaxVideoFileSize, 1);
++
++ if (fileNumber <= smallFiles)
++ return MEGABYTE(off_t(setupMaxVideoFileSize));
++
++ return MEGABYTE(off_t(maxVideoFileSize));
++}
++#endif /* HARDLINKCUTTER */
++
+ cUnbufferedFile *cFileName::NextFile(void)
+ {
+ return SetOffset(fileNumber + 1);
+@@ -1986,3 +2247,113 @@
+ LOG_ERROR;
+ return r;
+ }
++
++#ifdef USE_DVLFRIENDLYFNAMES
++char *MakeFriendlyFilename(char **buf)
++{
++ char *b, *x, *y;
++
++ if (buf == NULL || *buf == NULL)
++ return(NULL);
++
++ b = (char *)malloc(strlen(*buf) * 2);
++ x = *buf;
++ y = b;
++
++ while (*x != 0) {
++ switch (*x) {
++ case 'Ä':
++ *y = 'A';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'ä':
++ *y = 'a';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'Ö':
++ *y = 'O';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'ö':
++ *y = 'o';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'Ü':
++ *y = 'U';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'ü':
++ *y = 'u';
++ y++;
++ *y = 'e';
++ y++; x++;
++ break;
++
++ case 'ß':
++ *y = 's';
++ y++;
++ *y = 's';
++ y++; x++;
++ break;
++
++ // chars to replace
++ case ':':
++ case ';':
++ case '?':
++ case ' ':
++ case '\t':
++ *y = '_';
++ y++; x++;
++ break;
++
++ // chars to simply strip
++ case '\"':
++ case '*':
++ case '{':
++ case '}':
++ case '[':
++ case ']':
++ case '=':
++ case '<':
++ case '>':
++ case '#':
++ case '`':
++ case '|':
++ case '\\':
++ case '\n':
++ case '\r':
++ x++;
++ break;
++
++ default:
++ *y = *x;
++ y++; x++;
++ break;
++ }
++ }
++ *y = 0;
++
++ x = strdup(b);
++ free(b);
++
++ free(*buf);
++ *buf = x;
++
++ return(*buf);
++}
++#endif /* DVLFRIENDLYFNAMES */
+diff -ruN vdr-1.7.14/recording.h vdr-1.7.14.ExtP_NG/recording.h
+--- vdr-1.7.14/recording.h 2010-03-07 15:06:15.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/recording.h 2010-04-10 15:45:11.134737044 +0200
+@@ -23,6 +23,9 @@
+ #define TIMERMACRO_EPISODE "EPISODE"
+
+ extern bool VfatFileSystem;
++#ifdef USE_LIEMIEXT
++extern bool DirOrderState;
++#endif /* LIEMIEXT */
+ extern int InstanceId;
+
+ void RemoveDeletedRecordings(void);
+@@ -67,6 +70,9 @@
+ const cEvent *GetEvent(void) const { return event; }
+ const char *Title(void) const { return event->Title(); }
+ const char *ShortText(void) const { return event->ShortText(); }
++#ifdef USE_GRAPHTFT
++ tEventID EventID(void) const { return event->EventID(); }
++#endif /* GRAPHTFT */
+ const char *Description(void) const { return event->Description(); }
+ const cComponents *Components(void) const { return event->Components(); }
+ const char *Aux(void) const { return aux; }
+@@ -78,12 +84,22 @@
+ bool Write(void) const;
+ };
+
++#ifdef USE_SORTRECORDS
++#define SORTRECORDINGSVERSNUM 3
++#define MAXSORTMODES 4
++#endif /* SORTRECORDS */
++
+ class cRecording : public cListObject {
+ friend class cRecordings;
+ private:
+ mutable int resume;
+ mutable char *titleBuffer;
++#ifdef USE_SORTRECORDS
++ mutable char *sortBuffer[MAXSORTMODES];
++ mutable char lastDirsFirst[MAXSORTMODES];
++#else
+ mutable char *sortBuffer;
++#endif /* SORTRECORDS */
+ mutable char *fileName;
+ mutable char *name;
+ mutable int fileSizeMB;
+@@ -108,8 +124,15 @@
+ virtual int Compare(const cListObject &ListObject) const;
+ const char *Name(void) const { return name; }
+ const char *FileName(void) const;
++#ifdef USE_LIEMIEXT
++ const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1, bool Original = true) const;
++#else
+ const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const;
++#endif /* LIEMIEXT */
+ const cRecordingInfo *Info(void) const { return info; }
++#ifdef USE_CUTTIME
++ void SetStartTime(time_t Start);
++#endif /* CUTTIME */
+ const char *PrefixFileName(char Prefix);
+ int HierarchyLevels(void) const;
+ void ResetResume(void) const;
+@@ -128,6 +151,11 @@
+ // Changes the file name so that it will be visible in the "Recordings" menu again and
+ // not processed by cRemoveDeletedRecordingsThread.
+ // Returns false in case of error
++#ifdef USE_LIEMIEXT
++ bool Rename(const char *newName);
++ // Changes the file name
++ // Returns false in case of error
++#endif /* LIEMIEXT */
+ };
+
+ class cRecordings : public cList<cRecording>, public cThread {
+@@ -136,6 +164,9 @@
+ bool deleted;
+ time_t lastUpdate;
+ int state;
++#ifdef USE_SORTRECORDS
++ int SortOrder;
++#endif /* SORTRECORDS */
+ const char *UpdateFileName(void);
+ void Refresh(bool Foreground = false);
+ void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0);
+@@ -166,6 +197,10 @@
+ void AddByName(const char *FileName, bool TriggerUpdate = true);
+ void DelByName(const char *FileName);
+ int TotalFileSizeMB(void); ///< Only for deleted recordings!
++#ifdef USE_SORTRECORDS
++ void ToggleSortOrder(void) { SortOrder *= -1; }
++ const int GetSortOrder(void) { return SortOrder; }
++#endif /* SORTRECORDS */
+ };
+
+ extern cRecordings Recordings;
+@@ -198,6 +233,21 @@
+ cMark *GetNext(int Position);
+ };
+
++#ifdef USE_JUMPPLAY
++class cMarksReload : public cMarks {
++private:
++ cString recDir;
++ cTimeMs nextreload;
++ time_t lastmodtime;
++ static time_t lastsavetime;
++public:
++ cMarksReload(const char *RecordingFileName);
++ bool Reload(void);
++ bool Save(void);
++ };
++
++#endif /* JUMPPLAY */
++
+ #define RUC_BEFORERECORDING "before"
+ #define RUC_AFTERRECORDING "after"
+ #define RUC_EDITEDRECORDING "edited"
+@@ -207,7 +257,11 @@
+ static const char *command;
+ public:
+ static void SetCommand(const char *Command) { command = Command; }
++#ifdef USE_DVLRECSCRIPTADDON
++ static void InvokeCommand(const char *State, const char *RecordingFileName, char *chanName = NULL);
++#else
+ static void InvokeCommand(const char *State, const char *RecordingFileName);
++#endif /* DVLRECSCRIPTADDON */
+ };
+
+ // The maximum size of a single frame (up to HDTV 1920x1080):
+@@ -220,9 +274,23 @@
+ // before the next independent frame, to have a complete Group Of Pictures):
+ #define MAXVIDEOFILESIZETS 1048570 // MB
+ #define MAXVIDEOFILESIZEPES 2000 // MB
++#ifdef USE_HARDLINKCUTTER
++#define MINVIDEOFILESIZE 1 // MB
++#else
+ #define MINVIDEOFILESIZE 100 // MB
++#endif /* HARDLINKCUTTER */
+ #define MAXVIDEOFILESIZEDEFAULT MAXVIDEOFILESIZEPES
+
++#ifdef USE_HARDLINKCUTTER
++#define MINRECORDINGSIZE 25 // GB
++#define MAXRECORDINGSIZE 500 // GB
++#define DEFAULTRECORDINGSIZE 100 // GB
++// Dynamic recording size:
++// Keep recording file size at Setup.MaxVideoFileSize for as long as possible,
++// but switch to MAXVIDEOFILESIZE early enough, so that Setup.MaxRecordingSize
++// will be reached, before recording to file 65535.vdr
++#endif /* HARDLINKCUTTER */
++
+ struct tIndexTs;
+ class cIndexFileGenerator;
+
+@@ -252,6 +320,10 @@
+ bool StoreResume(int Index) { return resumeFile.Save(Index); }
+ bool IsStillRecording(void);
+ void Delete(void);
++#ifdef USE_LIEMIEXT
++ static int Length(const char *FileName, bool IsPesRecording = false);
++ ///< Calculates the recording length without reading the index.
++#endif /* LIEMIEXT */
+ };
+
+ class cFileName {
+@@ -271,6 +343,10 @@
+ cUnbufferedFile *Open(void);
+ void Close(void);
+ cUnbufferedFile *SetOffset(int Number, off_t Offset = 0); // yes, Number is int for easier internal calculating
++#ifdef USE_HARDLINKCUTTER
++ off_t MaxFileSize();
++ // Dynamic file size for this file
++#endif /* HARDLINKCUTTER */
+ cUnbufferedFile *NextFile(void);
+ };
+
+diff -ruN vdr-1.7.14/remux.c vdr-1.7.14.ExtP_NG/remux.c
+--- vdr-1.7.14/remux.c 2010-02-28 15:42:07.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/remux.c 2010-04-10 15:45:11.187744435 +0200
+@@ -215,6 +215,32 @@
+ return i;
+ }
+
++#ifdef USE_TTXTSUBS
++int cPatPmtGenerator::MakeTeletextDescriptor(uchar *Target, const tTeletextSubtitlePage *pages, int pageCount)
++{
++ int i = 0, j = 0;
++ Target[i++] = SI::TeletextDescriptorTag;
++ int l = i;
++ Target[i++] = 0x00; // length
++ for (int n = 0; n < pageCount; n++) {
++ const char* Language = pages[n].ttxtLanguage;
++ Target[i++] = *Language++;
++ Target[i++] = *Language++;
++ Target[i++] = *Language++;
++ Target[i++] = (pages[n].ttxtType << 3) + pages[n].ttxtMagazine;
++ Target[i++] = pages[n].ttxtPage;
++ j++;
++ }
++ if (j > 0) {
++ Target[l] = j * 5; // update length
++ IncEsInfoLength(i);
++ return i;
++ }
++ return 0;
++}
++
++#endif /* TTXTSUBS */
++
+ int cPatPmtGenerator::MakeLanguageDescriptor(uchar *Target, const char *Language)
+ {
+ int i = 0;
+@@ -296,6 +322,9 @@
+ if (Channel) {
+ int Vpid = Channel->Vpid();
+ int Ppid = Channel->Ppid();
++#ifdef USE_TTXTSUBS
++ int Tpid = Channel->Tpid();
++#endif /* TTXTSUBS */
+ uchar *p = buf;
+ int i = 0;
+ p[i++] = 0x02; // table id
+@@ -330,6 +359,12 @@
+ i += MakeStream(buf + i, 0x06, Channel->Spid(n));
+ i += MakeSubtitlingDescriptor(buf + i, Channel->Slang(n), Channel->SubtitlingType(n), Channel->CompositionPageId(n), Channel->AncillaryPageId(n));
+ }
++#ifdef USE_TTXTSUBS
++ if (Tpid) {
++ i += MakeStream(buf + i, 0x06, Tpid);
++ i += MakeTeletextDescriptor(buf + i, Channel->TeletextSubtitlePages(), Channel->TotalTeletextSubtitlePages());
++ }
++#endif /* TTXTSUBS */
+
+ int sl = i - SectionLength - 2 + 4; // -2 = SectionLength storage, +4 = length of CRC
+ buf[SectionLength] |= (sl >> 8) & 0x0F;
+@@ -403,6 +438,9 @@
+ pmtPid = -1;
+ vpid = vtype = 0;
+ ppid = 0;
++#ifdef USE_TTXTSUBS
++ tpid = 0;
++#endif /* TTXTSUBS */
+ }
+
+ void cPatPmtParser::ParsePat(const uchar *Data, int Length)
+@@ -488,11 +526,17 @@
+ int NumSpids = 0;
+ vpid = vtype = 0;
+ ppid = 0;
++#ifdef USE_TTXTSUBS
++ tpid = 0;
++#endif /* TTXTSUBS */
+ apids[0] = 0;
+ dpids[0] = 0;
+ spids[0] = 0;
+ atypes[0] = 0;
+ dtypes[0] = 0;
++#ifdef USE_TTXTSUBS
++ totalTtxtSubtitlePages = 0;
++#endif /* TTXTSUBS */
+ SI::PMT::Stream stream;
+ for (SI::Loop::Iterator it; Pmt.streamLoop.getNext(stream, it); ) {
+ dbgpatpmt(" stream type = %02X, pid = %d", stream.getStreamType(), stream.getPid());
+@@ -586,6 +630,30 @@
+ spids[NumSpids]= 0;
+ }
+ break;
++#ifdef USE_TTXTSUBS
++ case SI::TeletextDescriptorTag: {
++ dbgpatpmt(" teletext");
++ tpid = stream.getPid();
++ SI::TeletextDescriptor *sd = (SI::TeletextDescriptor *)d;
++ SI::TeletextDescriptor::Teletext ttxt;
++ if (totalTtxtSubtitlePages < MAXTXTPAGES) {
++ for (SI::Loop::Iterator it; sd->teletextLoop.getNext(ttxt, it); ) {
++ bool isSubtitlePage = (ttxt.getTeletextType() == 0x02) || (ttxt.getTeletextType() == 0x05);
++ if (isSubtitlePage && ttxt.languageCode[0]) {
++ dbgpatpmt(" '%s:%x.%x'", ttxt.languageCode, ttxt.getTeletextMagazineNumber(), ttxt.getTeletextPageNumber());
++ strn0cpy(teletextSubtitlePages[totalTtxtSubtitlePages].ttxtLanguage, I18nNormalizeLanguageCode(ttxt.languageCode), MAXLANGCODE1);
++ teletextSubtitlePages[totalTtxtSubtitlePages].ttxtPage = ttxt.getTeletextPageNumber();
++ teletextSubtitlePages[totalTtxtSubtitlePages].ttxtMagazine = ttxt.getTeletextMagazineNumber();
++ teletextSubtitlePages[totalTtxtSubtitlePages].ttxtType = ttxt.getTeletextType();
++ totalTtxtSubtitlePages++;
++ if (totalTtxtSubtitlePages >= MAXTXTPAGES)
++ break;
++ }
++ }
++ }
++ }
++ break;
++#endif /* TTXTSUBS */
+ case SI::ISO639LanguageDescriptorTag: {
+ SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
+ dbgpatpmt(" '%s'", ld->languageCode);
+diff -ruN vdr-1.7.14/remux.h vdr-1.7.14.ExtP_NG/remux.h
+--- vdr-1.7.14/remux.h 2010-01-29 17:51:26.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/remux.h 2010-04-10 15:45:11.199740063 +0200
+@@ -170,6 +170,9 @@
+ int MakeStream(uchar *Target, uchar Type, int Pid);
+ int MakeAC3Descriptor(uchar *Target);
+ int MakeSubtitlingDescriptor(uchar *Target, const char *Language, uchar SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId);
++#ifdef USE_TTXTSUBS
++ int MakeTeletextDescriptor(uchar *Target, const tTeletextSubtitlePage *pages, int pageCount);
++#endif /* TTXTSUBS */
+ int MakeLanguageDescriptor(uchar *Target, const char *Language);
+ int MakeCRC(uchar *Target, const uchar *Data, int Length);
+ void GeneratePmtPid(const cChannel *Channel);
+@@ -215,6 +218,9 @@
+ int vpid;
+ int ppid;
+ int vtype;
++#ifdef USE_TTXTSUBS
++ int tpid;
++#endif /* TTXTSUBS */
+ int apids[MAXAPIDS + 1]; // list is zero-terminated
+ int atypes[MAXAPIDS + 1]; // list is zero-terminated
+ char alangs[MAXAPIDS][MAXLANGCODE2];
+@@ -227,6 +233,10 @@
+ uint16_t compositionPageIds[MAXSPIDS];
+ uint16_t ancillaryPageIds[MAXSPIDS];
+ bool updatePrimaryDevice;
++#ifdef USE_TTXTSUBS
++ int totalTtxtSubtitlePages;
++ tTeletextSubtitlePage teletextSubtitlePages[MAXTXTPAGES];
++#endif /* TTXTSUBS */
+ protected:
+ int SectionLength(const uchar *Data, int Length) { return (Length >= 3) ? ((int(Data[1]) & 0x0F) << 8)| Data[2] : 0; }
+ public:
+@@ -259,6 +269,11 @@
+ int Vtype(void) const { return vtype; }
+ ///< Returns the video stream type as defined by the current PMT, or 0 if no video
+ ///< stream type has been detected, yet.
++#ifdef USE_TTXTSUBS
++ int Tpid(void) { return tpid; }
++ ///< Returns the teletext pid as defined by the current PMT, or 0 if no teletext
++ ///< pid has been detected, yet.
++#endif /* TTXTSUBS */
+ const int *Apids(void) const { return apids; }
+ const int *Dpids(void) const { return dpids; }
+ const int *Spids(void) const { return spids; }
+@@ -273,6 +288,10 @@
+ uchar SubtitlingType(int i) const { return (0 <= i && i < MAXSPIDS) ? subtitlingTypes[i] : uchar(0); }
+ uint16_t CompositionPageId(int i) const { return (0 <= i && i < MAXSPIDS) ? compositionPageIds[i] : uint16_t(0); }
+ uint16_t AncillaryPageId(int i) const { return (0 <= i && i < MAXSPIDS) ? ancillaryPageIds[i] : uint16_t(0); }
++#ifdef USE_TTXTSUBS
++ const tTeletextSubtitlePage *TeletextSubtitlePages() const { return teletextSubtitlePages; }
++ int TotalTeletextSubtitlePages() const { return totalTtxtSubtitlePages; }
++#endif /* TTXTSUBS */
+ };
+
+ // TS to PES converter:
+diff -ruN vdr-1.7.14/sections.c vdr-1.7.14.ExtP_NG/sections.c
+--- vdr-1.7.14/sections.c 2007-10-14 14:52:07.000000000 +0200
++++ vdr-1.7.14.ExtP_NG/sections.c 2010-04-10 15:45:11.300735330 +0200
+@@ -198,7 +198,11 @@
+ if (fh) {
+ // Read section data:
+ unsigned char buf[4096]; // max. allowed size for any EIT section
++#ifdef USE_MCLI
++ int r = device->ReadFilter(fh->handle, buf, sizeof(buf));
++#else
+ int r = safe_read(fh->handle, buf, sizeof(buf));
++#endif /* MCLI */
+ if (!DeviceHasLock)
+ continue; // we do the read anyway, to flush any data that might have come from a different transponder
+ if (r > 3) { // minimum number of bytes necessary to get section length
+diff -ruN vdr-1.7.14/skins.c vdr-1.7.14.ExtP_NG/skins.c
+--- vdr-1.7.14/skins.c 2009-06-06 17:12:31.000000000 +0200
++++ vdr-1.7.14.ExtP_NG/skins.c 2010-04-10 15:45:11.417738004 +0200
+@@ -238,7 +238,11 @@
+ }
+ cSkinDisplay::Current()->SetMessage(Type, s);
+ cSkinDisplay::Current()->Flush();
++#ifdef USE_STATUS_EXTENSION
++ cStatus::MsgOsdStatusMessage(Type, s);
++#else
+ cStatus::MsgOsdStatusMessage(s);
++#endif /* STATUS_EXTENSION */
+ eKeys k = kNone;
+ if (Type != mtStatus) {
+ k = Interface->Wait(Seconds);
+@@ -249,7 +253,11 @@
+ }
+ else {
+ cSkinDisplay::Current()->SetMessage(Type, NULL);
++#ifdef USE_STATUS_EXTENSION
++ cStatus::MsgOsdStatusMessage(Type, NULL);
++#else
+ cStatus::MsgOsdStatusMessage(NULL);
++#endif
+ }
+ }
+ else if (!s && displayMessage) {
+diff -ruN vdr-1.7.14/status.c vdr-1.7.14.ExtP_NG/status.c
+--- vdr-1.7.14/status.c 2008-02-16 15:46:31.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/status.c 2010-04-10 15:45:11.565737350 +0200
+@@ -83,6 +83,17 @@
+ sm->OsdTitle(Title);
+ }
+
++#ifdef USE_STATUS_EXTENSION
++void cStatus::MsgOsdStatusMessage(eMessageType type, const char *Message)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ {
++ sm->OsdStatusMessage(type, Message);
++ sm->OsdStatusMessage(Message); // For comaptibilty
++ }
++}
++#endif /* STATUS_EXTENSION */
++
+ void cStatus::MsgOsdStatusMessage(const char *Message)
+ {
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+@@ -124,3 +135,88 @@
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++#ifdef USE_GRAPHTFT
++
++void cStatus::MsgOsdSetEvent(const cEvent* event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdSetEvent(event);
++}
++
++void cStatus::MsgOsdSetRecording(const cRecording* recording)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdSetRecording(recording);
++}
++
++void cStatus::MsgOsdMenuDisplay(const char* kind)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdMenuDisplay(kind);
++}
++
++void cStatus::MsgOsdMenuDestroy()
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdMenuDestroy();
++}
++void cStatus::MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->OsdEventItem(Event, Text, Index, Count);
++}
++#endif /* GRAPHTFT */
++#ifdef USE_PINPLUGIN
++
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ChannelProtected(Device, Channel) == true)
++ return true;
++
++ return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->PluginProtected(Plugin, menuView) == true)
++ return true;
++ return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key, const cOsdObject* Interact)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ sm->UserAction(key, Interact);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView)
++{
++ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++ if (sm->MenuItemProtected(Name, menuView) == true)
++ return true;
++ return false;
++}
++#endif /* PINPLUGIN */
+diff -ruN vdr-1.7.14/status.h vdr-1.7.14.ExtP_NG/status.h
+--- vdr-1.7.14/status.h 2008-02-16 16:00:33.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/status.h 2010-04-10 15:45:11.579737535 +0200
+@@ -14,6 +14,9 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#ifdef USE_PINPLUGIN
++#include "plugin.h"
++#endif
+
+ enum eTimerChange { tcMod, tcAdd, tcDel };
+
+@@ -64,6 +67,11 @@
+ virtual void OsdStatusMessage(const char *Message) {}
+ // Message has been displayed in the status line of the menu.
+ // If Message is NULL, the status line has been cleared.
++#ifdef USE_STATUS_EXTENSION
++ virtual void OsdStatusMessage(eMessageType type, const char *Message) {}
++ // Message has been displayed in the status line of the menu.
++ // If Message is NULL, the status line has been cleared.
++#endif /* STATUS_EXTENSION */
+ virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) {}
+ // The help keys have been set to the given values (may be NULL).
+ virtual void OsdItem(const char *Text, int Index) {}
+@@ -80,6 +88,38 @@
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
++#ifdef USE_GRAPHTFT
++ virtual void OsdSetRecording(const cRecording* recording) {}
++ // The OSD displays the recording information.
++ virtual void OsdSetEvent(const cEvent* event) {}
++ // The OSD displays the event information.
++ virtual void OsdMenuDisplay(const char* kind) {}
++ // report menu creation
++ virtual void OsdMenuDestroy() {}
++ // report menu destruvtion
++ virtual void OsdEventItem(const cEvent* Event, const char *Text, int Index, int Count) {}
++ // The OSD displays the given single line Event as menu item at Index.
++#endif /* GRAPHTFT */
++#ifdef USE_PINPLUGIN
++ virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel) { return false; }
++ // Checks if a channel is protected.
++ virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false) { return false; }
++ // Checks if a recording is protected.
++ virtual void RecordingFile(const char* FileName) {}
++ // The given DVB device has started recording to FileName. FileName is the name of the
++ // recording directory
++ virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {}
++ // The given timer is created
++ virtual bool PluginProtected(cPlugin* Plugin, int menuView = false) { return false; }
++ // Checks if a plugin is protected.
++ virtual void UserAction(const eKeys key, const cOsdObject* Interact) {}
++ // report user action
++ virtual bool MenuItemProtected(const char* Name, int menuView = false) { return false; }
++ // Checks if a menu entry is protected.
++#endif /* PINPLUGIn */
++
++
+ public:
+ cStatus(void);
+ virtual ~cStatus();
+@@ -94,6 +134,9 @@
+ static void MsgSetSubtitleTrack(int Index, const char * const *Tracks);
+ static void MsgOsdClear(void);
+ static void MsgOsdTitle(const char *Title);
++#ifdef USE_STATUS_EXTENSION
++ static void MsgOsdStatusMessage(eMessageType type, const char *Message);
++#endif /* STATUS_EXTENSION */
+ static void MsgOsdStatusMessage(const char *Message);
+ static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
+ static void MsgOsdItem(const char *Text, int Index);
+@@ -101,6 +144,23 @@
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++#ifdef USE_GRAPHTFT
++ static void MsgOsdSetEvent(const cEvent* event);
++ static void MsgOsdSetRecording(const cRecording* recording);
++ static void MsgOsdMenuDisplay(const char* kind);
++ static void MsgOsdMenuDestroy();
++ static void MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count);
++#endif /* GRAPHTFT */
++#ifdef USE_PINPLUGIN
++ static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel);
++ static bool MsgReplayProtected(const cRecording* Recording, const char* Name,
++ const char* Base, bool isDirectory, int menuView = false);
++ static void MsgRecordingFile(const char* FileName);
++ static void MsgTimerCreation(cTimer* Timer, const cEvent *Event);
++ static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false);
++ static void MsgUserAction(const eKeys key, const cOsdObject* Interact);
++ static bool MsgMenuItemProtected(const char* Name, int menuView = false);
++#endif /* PINPLUGIN */
+ };
+
+ #endif //__STATUS_H
+diff -ruN vdr-1.7.14/submenu.c vdr-1.7.14.ExtP_NG/submenu.c
+--- vdr-1.7.14/submenu.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/submenu.c 2010-04-10 15:45:11.593741591 +0200
+@@ -0,0 +1,949 @@
++#ifdef USE_SETUP
++/****************************************************************************
++ * DESCRIPTION:
++ * Submenu
++ *
++ * $Id: vdr-1.3.44-Setup-0.3.0.diff,v 1.1 2006/03/04 09:58:47 ralf Exp $
++ *
++ * Contact: ranga@teddycats.de
++ *
++ * Copyright (C) 2004, 2005 by Ralf Dotzert
++ *
++ * modified for the VDR Extensions Patch by zulu @vdr-portal
++ ****************************************************************************/
++
++#ifndef SUBMENU_H
++#include "submenu.h"
++#include "plugin.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
++
++static const char* TAG_SYSTEM = "system";
++static const char* TAG_PLUGIN = "plugin";
++static const char* TAG_COMMAND = "command";
++static const char* TAG_THREAD = "thread";
++static const char* TAG_MENU = "menu";
++static const char* TAG_UNDEFINED = "undefined";
++static const char* TRUE_STR = "yes";
++
++
++//################################################################################
++//# SubMenuNode
++//################################################################################
++
++cSubMenuNode::cSubMenuNode(TiXmlElement *xml, int level, cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu)
++{
++ init();
++ _parentMenu = parentMenu;
++ _currentMenu = currentMenu;
++ _level = level;
++
++ if (xml != NULL && xml->Type() == TiXmlNode::ELEMENT) {
++ const char *tag = xml->Value();
++
++ if (cSubMenuNode::IsType(tag) != cSubMenuNode::UNDEFINED) {
++ SetType(tag);
++ SetName(xml->Attribute("name"));
++ if ((_type == COMMAND) || (_type == THREAD)) {
++ SetCommand(xml->Attribute("execute"));
++ const char *confirmStr = xml->Attribute("confirm");
++ if (confirmStr != NULL && strcmp(confirmStr, TRUE_STR) == 0)
++ _commandConfirm = true;
++ }
++ else if (_type == PLUGIN) { // Add Plugin Index
++ SetCustomTitle(xml->Attribute("title"));
++ SetPlugin();
++ }
++ else if (_type == MENU && xml->NoChildren() == false) {
++ xml = xml->FirstChildElement();
++ do {
++ cSubMenuNode *node = new cSubMenuNode(xml, level+1, &_subMenus, currentMenu);
++ _subMenus.Add(node);
++ } while ((xml=xml->NextSiblingElement()) != NULL);
++ }
++ }
++ }
++ else
++ throw "Invalid XML Node";
++}
++
++/**
++ * Construct new Node empty Node
++ *
++ *
++ */
++cSubMenuNode::cSubMenuNode(cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu)
++{
++ init();
++ _parentMenu = parentMenu;
++ _currentMenu = currentMenu;
++
++}
++
++
++/**
++ *
++ */
++void cSubMenuNode::init()
++{
++ _name = NULL;
++ _command = NULL;
++ _title = NULL;
++ _pluginMainMenuEntry = NULL;
++ _type = UNDEFINED;
++ _level = 0;
++ _parentMenu = NULL;
++ _currentMenu = NULL;
++ _pluginIndex = 0;
++ _commandConfirm = false;
++}
++
++
++cSubMenuNode::~ cSubMenuNode()
++{
++ if (_name != NULL)
++ free((void*)_name);
++ if (_command != NULL)
++ free((void*)_command);
++ if (_title != NULL)
++ free((void*)_title);
++ if (_pluginMainMenuEntry != NULL)
++ free((void*)_pluginMainMenuEntry);
++}
++
++/**
++ *
++ */
++void cSubMenuNode::SetPlugin()
++{
++ bool found = false;
++ for (int i = 0; ; i++) {
++ cPlugin *p = cPluginManager::GetPlugin(i);
++ if (p) {
++ if (strcmp(_name, p->Name()) == 0 && p->MainMenuEntry() != NULL) {
++ SetPluginMainMenuEntry(p->MainMenuEntry());
++ _pluginIndex = i;
++ found = true;
++ break;
++ }
++ }
++ else
++ break;
++ }
++
++ if (!found)
++ _type = UNDEFINED;
++}
++
++
++bool cSubMenuNode::SaveXml(TiXmlElement *root)
++{
++ bool ok = true;
++
++ if (root!=NULL) {
++ TiXmlElement *e = NULL;
++ switch(_type) {
++ case SYSTEM:
++ e = new TiXmlElement(TAG_SYSTEM);
++ e->SetAttribute("name", GetName());
++ break;
++ case COMMAND:
++ e = new TiXmlElement(TAG_COMMAND);
++ e->SetAttribute("name", GetName());
++ e->SetAttribute("execute", GetCommand());
++ if (_commandConfirm)
++ e->SetAttribute("confirm", TRUE_STR);
++ break;
++ case THREAD:
++ e = new TiXmlElement(TAG_THREAD);
++ e->SetAttribute("name", GetName());
++ e->SetAttribute("execute", GetCommand());
++ if (_commandConfirm)
++ e->SetAttribute("confirm", TRUE_STR);
++ break;
++ case PLUGIN:
++ e = new TiXmlElement(TAG_PLUGIN);
++ e->SetAttribute("name", GetName());
++ if (GetCustomTitle() != NULL && strcmp(GetCustomTitle(), "") != 0)
++ e->SetAttribute("title", GetCustomTitle());
++ break;
++ case MENU:
++ e = new TiXmlElement(TAG_MENU);
++ e->SetAttribute("name", GetName());
++ break;
++ case UNDEFINED:
++ default:
++ ok = false;
++ break;
++ }
++ if (ok) {
++ root->LinkEndChild(e);
++ if (HasSubMenus())
++ for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++ node->SaveXml(e);
++ }
++ }
++
++ return(ok);
++}
++
++
++cSubMenuNode::Type cSubMenuNode::IsType(const char *name)
++{
++ Type type = UNDEFINED;
++
++ if (strcmp(name ,TAG_SYSTEM) == 0)
++ type = cSubMenuNode::SYSTEM;
++ else if (strcmp(name ,TAG_PLUGIN) == 0)
++ type = cSubMenuNode::PLUGIN;
++ else if (strcmp(name ,TAG_COMMAND) == 0)
++ type = cSubMenuNode::COMMAND;
++ else if (strcmp(name ,TAG_THREAD) == 0)
++ type = cSubMenuNode::THREAD;
++ else if (strcmp(name ,TAG_MENU) == 0)
++ type = cSubMenuNode::MENU;
++
++ return(type);
++}
++
++void cSubMenuNode::SetType(const char *name)
++{
++ _type = IsType(name);
++}
++
++void cSubMenuNode::SetType(enum Type type)
++{
++ _type = type;
++}
++
++
++cSubMenuNode::Type cSubMenuNode::GetType()
++{
++ return(_type);
++}
++
++const char *cSubMenuNode::GetTypeAsString()
++{
++ const char *str=NULL;
++ switch(_type) {
++ case SYSTEM:
++ str = TAG_SYSTEM;
++ break;
++ case COMMAND:
++ str = TAG_COMMAND;
++ break;
++ case THREAD:
++ str = TAG_THREAD;
++ break;
++ case PLUGIN:
++ str = TAG_PLUGIN;
++ break;
++ case MENU:
++ str = TAG_MENU;
++ break;
++ case UNDEFINED:
++ str = TAG_UNDEFINED;
++ default:
++ break;
++ }
++
++ return(str);
++}
++
++void cSubMenuNode::SetCommand(const char *command)
++{
++ if (_command != NULL)
++ free((void*)_command);
++
++ if (command != NULL)
++ _command = strdup(command);
++ else
++ _command = NULL;
++}
++
++const char *cSubMenuNode::GetCommand()
++{
++ return(_command);
++}
++
++bool cSubMenuNode::CommandConfirm()
++{
++ return(_commandConfirm);
++}
++
++void cSubMenuNode::SetCommandConfirm(int val)
++{
++ if (val == 1)
++ _commandConfirm = true;
++ else
++ _commandConfirm = false;
++}
++
++void cSubMenuNode::SetCustomTitle(const char *title)
++{
++ if (_title != NULL)
++ free((void*)_title);
++
++ if (title != NULL)
++ _title = strdup(title);
++ else
++ _title = NULL;
++}
++
++const char *cSubMenuNode::GetCustomTitle()
++{
++ return(_title);
++}
++
++void cSubMenuNode::SetName(const char *name)
++{
++ if (_name)
++ free ((void*)_name);
++
++ if (name != NULL)
++ _name = strdup(name);
++ else
++ _name = NULL;
++}
++
++const char *cSubMenuNode::GetName()
++{
++ return(_name);
++}
++
++int cSubMenuNode::GetLevel()
++{
++ return(_level);
++}
++
++void cSubMenuNode::SetLevel(int level)
++{
++ _level = level;
++ if (HasSubMenus()) { //Adjust Levels of Subnodes
++ for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++ node->SetLevel(level+1);
++ }
++}
++
++int cSubMenuNode::GetPluginIndex()
++{
++ return(_pluginIndex);
++}
++
++void cSubMenuNode::SetPluginIndex(int index)
++{
++ _pluginIndex = index;
++}
++
++void cSubMenuNode::SetPluginMainMenuEntry(const char *mainMenuEntry)
++{
++ if (_pluginMainMenuEntry != NULL)
++ free((void*)_pluginMainMenuEntry);
++
++ if (_title != NULL && strcmp(_title, "") != 0)
++ _pluginMainMenuEntry = strdup(_title);
++ else if (mainMenuEntry != NULL)
++ _pluginMainMenuEntry = strdup(mainMenuEntry);
++ else
++ _pluginMainMenuEntry = NULL;
++}
++
++const char *cSubMenuNode::GetPluginMainMenuEntry()
++{
++ return(_pluginMainMenuEntry);
++}
++
++
++cSubMenuNodes *cSubMenuNode::GetParentMenu()
++{
++ return(_parentMenu);
++}
++
++void cSubMenuNode::SetParentMenu(cSubMenuNodes *parent)
++{
++ _parentMenu = parent;
++}
++
++cSubMenuNodes *cSubMenuNode::GetCurrentMenu()
++{
++ return(_currentMenu);
++}
++
++void cSubMenuNode::SetCurrentMenu(cSubMenuNodes *current)
++{
++ _currentMenu = current;
++}
++
++
++cSubMenuNodes *cSubMenuNode::GetSubMenus()
++{
++ return(&_subMenus);
++}
++
++bool cSubMenuNode::HasSubMenus()
++{
++ if (_subMenus.Count() > 0)
++ return(true);
++ else
++ return(false);
++}
++
++
++void cSubMenuNode::Print(int index)
++{
++ for (int i = 0; i < index; i++)
++ printf(" ");
++
++ printf("Name=%s Type=%s Level=%d", _name, GetTypeAsString(), _level);
++ if (_type == COMMAND || _type == THREAD)
++ printf(" Command=%s", _command);
++ else if (_type == PLUGIN && _title != NULL)
++ printf(" Title=%s", _title);
++ printf("\n");
++
++ for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++ node->Print(index+4);
++}
++
++
++//################################################################################
++//#
++//################################################################################
++cSubMenu::cSubMenu()
++{
++ _commandResult = NULL;
++ _currentMenuTree = &_menuTree;
++ _currentParentMenuTree = NULL;
++#ifdef USE_PINPLUGIN
++ _currentParentIndex = -1;
++#endif /* PINPLUGIN */
++ _nodeArray = NULL;
++ _nrNodes = 0;
++}
++
++
++cSubMenu::~cSubMenu()
++{
++ if (_commandResult)
++ free(_commandResult);
++ if (_nodeArray)
++ free(_nodeArray);
++ _nrNodes = 0;
++}
++
++
++bool cSubMenu::LoadXml(cString fname)
++{
++ TiXmlDocument xmlDoc = TiXmlDocument(fname);
++ TiXmlElement *root = NULL;
++ cSubMenuNode *node = NULL;
++
++ bool ok = true;
++ // Clear previously loaded Menu
++ _menuTree.Clear();
++ _fname = fname;
++
++ if ((ok = xmlDoc.LoadFile())) {
++ if ((root = xmlDoc.FirstChildElement("menus")) != NULL) {
++ cString tmp = root->Attribute("suffix");
++#ifdef USE_WAREAGLEICON
++ if (strcmp(tmp, "ICON_FOLDER") == 0) tmp = cString::sprintf(" %s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER);
++ else if (strcmp(tmp, "ICON_MOVE_FOLDER") == 0) tmp = cString::sprintf(" %s", IsLangUtf8() ? ICON_MOVE_FOLDER_UTF8 : ICON_MOVE_FOLDER);
++#endif /* WAREAGLEICON */
++ if (*tmp)
++ _menuSuffix = tmp;
++ else
++ _menuSuffix = cString::sprintf(" ");
++
++ if ((root = root->FirstChildElement()) != NULL) {
++ do {
++ try {
++ node = new cSubMenuNode(root, 0, &_menuTree, NULL);
++ _menuTree.Add(node);
++ }
++ catch (char *message) {
++ esyslog("ERROR: while decoding XML Node");
++ ok = false;
++ }
++ } while (ok == true && (root = root->NextSiblingElement()) != NULL);
++ addMissingPlugins();
++ removeUndefinedNodes();
++ }
++ }
++ else {
++ esyslog("ERROR: in %s, missing Tag <menus>\n", *fname);
++ ok = false;
++ }
++ }
++ else {
++ esyslog("ERROR: in %s : %s Col=%d Row=%d\n",
++ *fname,
++ xmlDoc.ErrorDesc(),
++ xmlDoc.ErrorCol(),
++ xmlDoc.ErrorRow());
++ ok = false;
++ }
++
++ return(ok);
++}
++
++
++bool cSubMenu::SaveXml()
++{
++ return(SaveXml(_fname));
++}
++
++
++bool cSubMenu::SaveXml(cString fname)
++{
++ bool ok = true;
++
++ if (*_fname) {
++ TiXmlDocument xml = TiXmlDocument(fname);
++ TiXmlComment comment;
++ comment.SetValue("\n\
++- VDR Menu-Configuration File\n\
++-\n\
++-\n\
++- Example:\n\
++-\n\
++ <menus>\n\
++ <system name=\"Schedule\" />\n\
++ <system name=\"Channels\" />\n\
++ <system name=\"Timers\" />\n\
++ <system name=\"Recordings\" />\n\
++ <menu name=\"System\">\n\
++ <system name=\"Setup\" />\n\
++ <system name=\"Commands\" />\n\
++ <plugin name=\"setup\" title=\"My Setup\" />\n\
++ <command name=\"myCommand1\" execute=\"/usr/bin/mycommand1\" />\n\
++ <command name=\"myCommand2\" execute=\"/usr/bin/mycommand2\" confirm=\"yes\" />\n\
++ <thread name=\"myCommand3\" execute=\"/usr/bin/mycommand3\" confirm=\"yes\" />\n\
++ <plugin name=\"epgsearch\" title=\"myProgram\" />\n\
++ <menu name=\"mySubSubMenu\">\n\
++ ...\n\
++ </menu>\n\
++ </menu>\n\
++ <menu name=\"Suche\">\n\
++ <plugin name=\"epgsearch\" />\n\
++ ...\n\
++ </menu>\n\
++ </menus>\n\
++");
++
++ TiXmlElement root("menus");
++ root.SetAttribute("suffix", _menuSuffix);
++ for (cSubMenuNode *node = _menuTree.First(); node; node = _menuTree.Next(node))
++ node->SaveXml(&root);
++
++ if (xml.InsertEndChild(comment) != NULL && xml.InsertEndChild(root) != NULL)
++ ok = xml.SaveFile(fname);
++ }
++ else
++ ok = false;
++
++ return(ok);
++}
++
++
++cSubMenuNodes *cSubMenu::GetMenuTree()
++{
++ return(_currentMenuTree);
++}
++
++
++void cSubMenu::PrintMenuTree()
++{
++ for (cSubMenuNode *node = _menuTree.First(); node; node = _menuTree.Next(node))
++ node->Print();
++}
++
++
++int cSubMenu::GetNrOfNodes()
++{
++ if (_nrNodes == 0) {
++ if ((_nrNodes = countNodes(&_menuTree)) > 0) {
++ _nodeArray = (cSubMenuNode**) malloc(sizeof(cSubMenuNode*)*_nrNodes);
++ int index = 0;
++ tree2Array(&_menuTree, index);
++ }
++ }
++
++ return(_nrNodes);
++}
++
++
++/**
++ * returns the specified node within the current menu
++ * @param index position in the current menu
++ * @return node or null if not found
++ */
++cSubMenuNode *cSubMenu::GetNode(int index)
++{
++ cSubMenuNode *node = NULL;
++ if (_currentMenuTree == NULL || (node=_currentMenuTree->Get(index)) == NULL)
++ esyslog("ERROR: illegal call of cSubMenu::GetNode(%d)", index);
++
++ return(node);
++}
++
++
++/**
++ * Get the specified Node
++ * @param index specfies the absolut indes in the list of all nodes
++ * @return node or NULL if not found
++ */
++cSubMenuNode *cSubMenu::GetAbsNode(int index)
++{
++ cSubMenuNode *node = NULL;
++ GetNrOfNodes();
++ if (_nrNodes > 0 && index >= 0 && index < _nrNodes)
++ node = _nodeArray[index];
++
++ return(node);
++}
++
++
++#ifdef USE_PINPLUGIN
++bool cSubMenu::Down(cSubMenuNode *node, int currentIndex)
++#else
++bool cSubMenu::Down(int index)
++#endif /* PINPLUGIN */
++{
++ bool ok = true;
++#ifdef USE_PINPLUGIN
++ if (_currentMenuTree != NULL && node && node->GetType() == cSubMenuNode::MENU) {
++#else
++ cSubMenuNode *node = NULL;
++
++ if (_currentMenuTree != NULL && (node=_currentMenuTree->Get(index)) != NULL && node->GetType() == cSubMenuNode::MENU) {
++#endif /* PINPLUGIN */
++ _currentParentMenuTree = _currentMenuTree;
++#ifdef USE_PINPLUGIN
++ _currentParentIndex = currentIndex;
++#endif /* PINPLUGIN */
++ _currentMenuTree = node->GetSubMenus();
++ }
++ else {
++ ok = false;
++#ifdef USE_PINPLUGIN
++ esyslog("ERROR: illegal call of cSubMenu::Down");
++#else
++ esyslog("ERROR: illegal call of cSubMenu::Down(%d)", index);
++#endif /* PINPLUGIN */
++ }
++
++ return(ok);
++}
++
++bool cSubMenu::Up(int *parentIndex)
++{
++ bool ok = true;
++
++ if (_currentMenuTree != NULL && parentIndex != NULL) {
++#ifndef USE_PINPLUGIN
++ cSubMenuNode *node = NULL;
++#endif /* PINPLUGIN */
++ *parentIndex = 0;
++#ifdef USE_PINPLUGIN
++ if (_currentParentIndex >= 0)
++ *parentIndex = _currentParentIndex;
++#else
++ if (_currentParentMenuTree != NULL)
++ for (int i = 0; (node = _currentParentMenuTree->Get(i)) != NULL; i++) {
++ if (_currentMenuTree == node->GetSubMenus()) {
++ *parentIndex = i;
++ break;
++ }
++ }
++#endif /* PINPLUGIN */
++
++ _currentMenuTree = _currentParentMenuTree;
++ if (_currentMenuTree != NULL)
++ _currentParentMenuTree = _currentMenuTree->Get(0)->GetParentMenu();
++ else
++ ok = false;
++ }
++ else {
++ ok = false;
++ esyslog("ERROR: illegal call of cSubMenu::Up()");
++ }
++
++ return(ok);
++}
++
++const char *cSubMenu::ExecuteCommand(const char *cmd)
++{
++ free(_commandResult);
++ _commandResult = NULL;
++
++ dsyslog("executing command '%s'", cmd);
++ FILE *p = popen(cmd, "r");
++ if (p) {
++ int l = 0;
++ int c;
++ while ((c = fgetc(p)) != EOF) {
++ if (l % 20 == 0)
++ _commandResult = (char *)realloc(_commandResult, l + 21);
++ _commandResult[l++] = c;
++ }
++ if (_commandResult)
++ _commandResult[l] = 0;
++ pclose(p);
++ }
++ else
++ esyslog("ERROR: can't open pipe for command '%s'", cmd);
++
++ return _commandResult;
++}
++
++/**
++ * Move Menu Entry to new Position
++ * @param index index of menu entry to move
++ * @param toIndex index of destination
++ * @param where After ore before the destination index
++ */
++void cSubMenu::MoveMenu(int index, int toIndex, enum Where where)
++{
++ if (index < 0 || index > _nrNodes || // invalid index is ignored
++ toIndex < 0 || toIndex > _nrNodes || index == toIndex)
++ return;
++
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ cSubMenuNode *destNode = GetAbsNode(toIndex);
++
++ if (where == cSubMenu::INTO && destNode->GetType() != cSubMenuNode::MENU)
++ return;
++
++ if (where == cSubMenu::INTO) {
++ if (destNode->GetType() == cSubMenuNode::MENU) {
++ srcNode->GetCurrentMenu()->Del(srcNode, false);
++ srcNode->SetLevel(destNode->GetLevel()+1);
++ srcNode->SetParentMenu(destNode->GetCurrentMenu());
++ srcNode->SetCurrentMenu(destNode->GetSubMenus());
++
++ destNode->GetSubMenus()->Add(srcNode);
++ reloadNodeArray();
++ }
++ }
++ else {
++ srcNode->GetCurrentMenu()->Del(srcNode, false);
++ srcNode->SetLevel(destNode->GetLevel());
++ srcNode->SetParentMenu(destNode->GetParentMenu());
++ srcNode->SetCurrentMenu(destNode->GetCurrentMenu());
++
++ if (where == cSubMenu::BEHIND) {
++ destNode->GetCurrentMenu()->Add(srcNode, GetAbsNode(toIndex));
++ reloadNodeArray();
++ }
++ else {
++ destNode->GetCurrentMenu()->Ins(srcNode, GetAbsNode(toIndex));
++ reloadNodeArray();
++ }
++ }
++}
++
++/**
++ * Create a new Menu Entry
++ * @param index index of destination
++ * @param menuTitle Titel of new Menu entry
++ */
++void cSubMenu::CreateMenu(int index, const char *menuTitle)
++{
++ if (index >= 0 && index < _nrNodes) {
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ if (srcNode != NULL) {
++ cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++ newNode->SetLevel(srcNode->GetLevel());
++ newNode->SetName(menuTitle);
++ newNode->SetType(cSubMenuNode::MENU);
++ newNode->SetParentMenu(srcNode->GetParentMenu());
++ newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++ srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++ reloadNodeArray();
++ }
++ }
++}
++
++/**
++ * delete the specified entry, or subtree if the specified entry is a menu
++ * @param index destion index
++ */
++void cSubMenu::DeleteMenu(int index)
++{
++ if (index >= 0 && index < _nrNodes) {
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ srcNode->GetCurrentMenu()->Del(srcNode, true);
++ reloadNodeArray();
++ }
++}
++
++
++// Private Methods
++
++int cSubMenu::countNodes(cSubMenuNodes *tree)
++{
++ int count = 0;
++ if (tree != NULL) {
++ for (cSubMenuNode *node = tree->First(); node; node = tree->Next(node)) {
++ count++;
++ if (node->HasSubMenus())
++ count += countNodes(node->GetSubMenus());
++ }
++ }
++ return(count);
++}
++
++
++void cSubMenu::tree2Array(cSubMenuNodes *tree, int &index)
++{
++ if (tree != NULL) {
++ for (cSubMenuNode *node = tree->First(); node; node = tree->Next(node)) {
++ _nodeArray[index++]=node;
++ if (node->HasSubMenus())
++ tree2Array(node->GetSubMenus(), index);
++ }
++ }
++
++}
++
++bool cSubMenu::IsPluginInMenu(const char *name)
++{
++ bool found = false;
++ for (int i = 0; i < _nrNodes && found == false; i++) {
++ cSubMenuNode *node = GetAbsNode(i);
++ if (node != NULL && node->GetType() == cSubMenuNode::PLUGIN && strcmp(name, node->GetName()) == 0)
++ found = true;
++ }
++ return(found);
++}
++
++/**
++ * Adds the given plugin to the Menu-Tree if not allready in List
++ * @param name specifies the name of the plugin
++ */
++void cSubMenu::AddPlugin(const char *name)
++{
++ if (! IsPluginInMenu(name)) {
++ cSubMenuNode *node = new cSubMenuNode(&_menuTree, NULL);
++ node->SetName(name);
++ node->SetType("plugin");
++ node->SetPlugin();
++ _menuTree.Add(node);
++ }
++}
++
++void cSubMenu::addMissingPlugins()
++{
++ _nrNodes = GetNrOfNodes();
++ for (int i = 0; ; i++) {
++ cPlugin *p = cPluginManager::GetPlugin(i);
++ if (p)
++ AddPlugin(p->Name());
++ else
++ break;
++ }
++ reloadNodeArray();
++}
++
++/**
++ * Adds the given command to the Menu-Tree
++ * @param name specifies the name of the command
++ */
++void cSubMenu::CreateCommand(int index, const char *name, const char *execute, int confirm)
++{
++ if (index >= 0 && index < _nrNodes) {
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ if (srcNode != NULL) {
++ cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++ newNode->SetLevel(srcNode->GetLevel());
++ newNode->SetName(name);
++ newNode->SetType("command");
++ newNode->SetCommand(execute);
++ newNode->SetCommandConfirm(confirm);
++ newNode->SetParentMenu(srcNode->GetParentMenu());
++ newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++ srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++ reloadNodeArray();
++ }
++ }
++}
++
++void cSubMenu::CreateThread(int index, const char *name, const char *execute, int confirm)
++{
++ if (index >= 0 && index < _nrNodes) {
++ cSubMenuNode *srcNode = GetAbsNode(index);
++ if (srcNode != NULL) {
++ cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++ newNode->SetLevel(srcNode->GetLevel());
++ newNode->SetName(name);
++ newNode->SetType("thread");
++ newNode->SetCommand(execute);
++ newNode->SetCommandConfirm(confirm);
++ newNode->SetParentMenu(srcNode->GetParentMenu());
++ newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++ srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++ reloadNodeArray();
++ }
++ }
++}
++
++/**
++ * reloads the internal Array of Nodes
++ */
++void cSubMenu::reloadNodeArray()
++{
++ if (_nrNodes > 0)
++ free(_nodeArray);
++ _nodeArray = NULL;
++ _nrNodes = 0;
++ _nrNodes = GetNrOfNodes();
++}
++
++/**
++ * remove Undefined Nodes
++ */
++void cSubMenu::removeUndefinedNodes()
++{
++ bool remove = false;
++
++ reloadNodeArray();
++ for (int i = 0; i < _nrNodes; i++) {
++ cSubMenuNode *node = GetAbsNode(i);
++ if (node != NULL && node->GetType() == cSubMenuNode::UNDEFINED) {
++ cSubMenuNodes *pMenu = node->GetCurrentMenu();
++ pMenu->Del(node, true);
++ remove = true;
++ }
++ }
++ if (remove)
++ reloadNodeArray();
++}
++
++
++/**
++* Retrieves the Menutitel of the parent Menu
++*/
++const char *cSubMenu::GetParentMenuTitel()
++{
++ const char *result = "";
++
++ if (_currentMenuTree != NULL && _currentParentMenuTree != NULL) {
++ cSubMenuNode *node = NULL;
++ for (int i = 0; (node = _currentParentMenuTree->Get(i)) != NULL; i++) {
++ if (_currentMenuTree == node->GetSubMenus()) {
++ result = node->GetName();
++ break;
++ }
++ }
++ }
++
++ return(result);
++}
++
++#endif
++#endif /* SETUP */
+diff -ruN vdr-1.7.14/submenu.h vdr-1.7.14.ExtP_NG/submenu.h
+--- vdr-1.7.14/submenu.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/submenu.h 2010-04-10 15:45:11.601737459 +0200
+@@ -0,0 +1,159 @@
++#ifdef USE_SETUP
++/****************************************************************************
++ * DESCRIPTION:
++ * Submenu
++ *
++ * $Id: vdr-1.3.44-Setup-0.3.0.diff,v 1.1 2006/03/04 09:58:47 ralf Exp $
++ *
++ * Contact: ranga@teddycats.de
++ *
++ * Copyright (C) 2004, 2005 by Ralf Dotzert
++ *
++ * modified for the VDR Extensions Patch by zulu @vdr-portal
++ ****************************************************************************/
++
++#ifndef SUBMENU_H
++#define SUBMENU_H
++
++#include "thread.h"
++#include "tools.h"
++#include "tinystr.h"
++
++class cSubMenuNode;
++class cSubMenuNodes;
++class cSubMenu;
++
++
++class cSubMenuNodes : public cList<cSubMenuNode> {};
++
++// execute cmd thread
++class cExecCmdThread : public cThread {
++private:
++ cString ExecCmd;
++protected:
++ virtual void Action(void) {
++ if (system(ExecCmd) == 0)
++ esyslog("%s - finished", *ExecCmd);
++ delete(this);
++ };
++public:
++ cExecCmdThread(char *cmd) {
++ ExecCmd = cString::sprintf("%s", cmd);
++ }
++ cExecCmdThread(const char *cmd) {
++ ExecCmd = cString::sprintf("%s", cmd);
++ }
++ ~cExecCmdThread() {
++ };
++ };
++
++//################################################################################
++//# SubMenuNode
++//################################################################################
++class cSubMenuNode : public cListObject {
++public:
++ enum Type { UNDEFINED, SYSTEM, COMMAND, THREAD, PLUGIN, MENU };
++ cSubMenuNode(TiXmlElement *xml, int level, cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu);
++ cSubMenuNode(cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu);
++ ~cSubMenuNode();
++ bool SaveXml(TiXmlElement *root);
++ static cSubMenuNode::Type IsType(const char *name);
++ void SetType(const char *name);
++ void SetType(enum Type type);
++ void SetPlugin();
++ cSubMenuNode::Type GetType();
++ const char *GetTypeAsString();
++ void SetCommand(const char *command);
++ bool CommandConfirm();
++ void SetCommandConfirm(int val);
++ const char *GetCommand();
++ void SetCustomTitle(const char *title);
++ const char *GetCustomTitle();
++ void SetName(const char *name);
++ const char*GetName();
++ int GetLevel();
++ void SetLevel(int level);
++ int GetPluginIndex();
++ void SetPluginIndex(int index);
++ void SetPluginMainMenuEntry(const char *mainMenuEntry);
++ const char *GetPluginMainMenuEntry();
++ cSubMenuNodes *GetParentMenu();
++ void SetParentMenu(cSubMenuNodes *parent);
++ cSubMenuNodes *GetCurrentMenu();
++ void SetCurrentMenu(cSubMenuNodes *current);
++ cSubMenuNodes *GetSubMenus();
++ bool HasSubMenus();
++ void Print(int index = 0);
++private:
++ Type _type;
++ int _level;
++ // Plugin Variables
++ int _pluginIndex;
++ const char *_pluginMainMenuEntry;
++ // common
++ const char *_name;
++ const char *_command;
++ bool _commandConfirm;
++ const char *_title;
++ cSubMenuNodes _subMenus;
++ cSubMenuNodes *_parentMenu;
++ cSubMenuNodes *_currentMenu;
++ void init();
++ };
++
++
++//################################################################################
++//# SubMenu Class
++//################################################################################
++class cSubMenu {
++public:
++ cSubMenu();
++ ~cSubMenu();
++ enum Where { BEFORE, BEHIND, INTO};
++ bool LoadXml(cString fname);
++ bool SaveXml(cString fname);
++ bool SaveXml();
++ cSubMenuNodes *GetMenuTree();
++ bool Up(int *ParentIndex);
++#ifdef USE_PINPLUGIN
++ bool Down(cSubMenuNode* node, int currentIndex);
++#else
++ bool Down(int index);
++#endif /* PINPLUGIN */
++ int GetNrOfNodes();
++ cSubMenuNode* GetAbsNode(int index);
++ cSubMenuNode* GetNode(int index);
++ void PrintMenuTree();
++ bool IsPluginInMenu(const char *name);
++ void AddPlugin(const char *name);
++ void CreateCommand(int index, const char *name, const char *execute, int confirm);
++ void CreateThread(int index, const char *name, const char *execute, int confirm);
++ const char *ExecuteCommand(const char *command);
++ void MoveMenu(int index, int toindex, enum Where);
++ void CreateMenu(int index, const char *menuTitle);
++ void DeleteMenu(int index);
++ cString GetMenuSuffix() { return _menuSuffix; }
++ void SetMenuSuffix(char *suffix) { _menuSuffix = suffix; }
++ bool isTopMenu() { return (_currentParentMenuTree == NULL); }
++ const char *GetParentMenuTitel();
++private:
++ cSubMenuNodes _menuTree;
++ cSubMenuNodes *_currentMenuTree;
++ cSubMenuNodes *_currentParentMenuTree;
++#ifdef USE_PINPLUGIN
++ int _currentParentIndex;
++#endif /* PINPLUGIN */
++ cString _fname;
++ char *_commandResult;
++ int _nrNodes;
++ cSubMenuNode **_nodeArray;
++ cString _menuSuffix;
++ int countNodes(cSubMenuNodes *tree);
++ void tree2Array(cSubMenuNodes *tree, int &index);
++ void addMissingPlugins();
++ void reloadNodeArray();
++ void removeUndefinedNodes();
++ };
++
++#endif //__SUBMENU_H
++#endif /* SETUP */
+diff -ruN vdr-1.7.14/svdrp.c vdr-1.7.14.ExtP_NG/svdrp.c
+--- vdr-1.7.14/svdrp.c 2010-01-17 13:23:31.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/svdrp.c 2010-04-10 15:45:11.637740914 +0200
+@@ -304,6 +304,10 @@
+ "REMO [ on | off ]\n"
+ " Turns the remote control on or off. Without a parameter, the current\n"
+ " status of the remote control is reported.",
++#ifdef USE_LIEMIEXT
++ "RENR <number> <new name>\n"
++ " Rename recording. Number must be the Number as returned by LSTR command.",
++#endif /* LIEMIEXT */
+ "SCAN\n"
+ " Forces an EPG scan. If this is a single DVB device system, the scan\n"
+ " will be done on the primary device unless it is currently recording.",
+@@ -1493,6 +1497,38 @@
+ Reply(250, "EPG scan triggered");
+ }
+
++#ifdef USE_LIEMIEXT
++void cSVDRP::CmdRENR(const char *Option)
++{
++ bool recordings = Recordings.Update(true);
++ if (recordings) {
++ if (*Option) {
++ char *tail;
++ int n = strtol(Option, &tail, 10);
++ cRecording *recording = Recordings.Get(n - 1);
++ if (recording && tail && tail != Option) {
++ char *oldName = strdup(recording->Name());
++ tail = skipspace(tail);
++ if (recording->Rename(tail)) {
++ Reply(250, "Renamed \"%s\" to \"%s\"", oldName, recording->Name());
++ Recordings.ChangeState();
++ Recordings.TouchUpdate();
++ }
++ else
++ Reply(501, "Renaming \"%s\" to \"%s\" failed", oldName, tail);
++ free(oldName);
++ }
++ else
++ Reply(501, "Recording not found or wrong syntax");
++ }
++ else
++ Reply(501, "Missing Input settings");
++ }
++ else
++ Reply(550, "No recordings available");
++}
++#endif /* LIEMIEXT */
++
+ void cSVDRP::CmdSTAT(const char *Option)
+ {
+ if (*Option) {
+@@ -1608,6 +1644,9 @@
+ else if (CMD("PLUG")) CmdPLUG(s);
+ else if (CMD("PUTE")) CmdPUTE(s);
+ else if (CMD("REMO")) CmdREMO(s);
++#ifdef USE_LIEMIEXT
++ else if (CMD("RENR")) CmdRENR(s);
++#endif /* LIEMIEXT */
+ else if (CMD("SCAN")) CmdSCAN(s);
+ else if (CMD("STAT")) CmdSTAT(s);
+ else if (CMD("UPDT")) CmdUPDT(s);
+diff -ruN vdr-1.7.14/svdrp.h vdr-1.7.14.ExtP_NG/svdrp.h
+--- vdr-1.7.14/svdrp.h 2007-04-30 14:28:28.000000000 +0200
++++ vdr-1.7.14.ExtP_NG/svdrp.h 2010-04-10 15:45:11.646738241 +0200
+@@ -79,6 +79,9 @@
+ void CmdPLUG(const char *Option);
+ void CmdPUTE(const char *Option);
+ void CmdREMO(const char *Option);
++#ifdef USE_LIEMIEXT
++ void CmdRENR(const char *Option);
++#endif /* LIEMIEXT */
+ void CmdSCAN(const char *Option);
+ void CmdSTAT(const char *Option);
+ void CmdUPDT(const char *Option);
+diff -ruN vdr-1.7.14/timers.c vdr-1.7.14.ExtP_NG/timers.c
+--- vdr-1.7.14/timers.c 2010-01-16 12:18:53.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/timers.c 2010-04-10 15:45:11.733740689 +0200
+@@ -46,6 +46,9 @@
+ stop -= 2400;
+ priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+ lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++#ifdef USE_PINPLUGIN
++ fskProtection = 0;
++#endif /* PINPLUGIN */
+ *file = 0;
+ aux = NULL;
+ event = NULL;
+@@ -84,6 +87,9 @@
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
++#ifdef USE_PINPLUGIN
++ fskProtection = 0;
++#endif /* PINPLUGIN */
+ *file = 0;
+ const char *Title = Event->Title();
+ if (!isempty(Title))
+@@ -95,6 +101,9 @@
+ }
+ aux = NULL;
+ event = NULL; // let SetEvent() be called to get a log message
++#ifdef USE_PINPLUGIN
++ cStatus::MsgTimerCreation(this, Event);
++#endif /* PINPLUGIN */
+ }
+
+ cTimer::cTimer(const cTimer &Timer)
+@@ -129,6 +138,9 @@
+ stop = Timer.stop;
+ priority = Timer.priority;
+ lifetime = Timer.lifetime;
++#ifdef USE_PINPLUGIN
++ fskProtection = Timer.fskProtection;
++#endif /* PINPLUGIN */
+ strncpy(file, Timer.file, sizeof(file));
+ free(aux);
+ aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -323,6 +335,9 @@
+ result = false;
+ }
+ }
++#ifdef USE_PINPLUGIN
++ fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>");
++#endif /* PINPLUGIN */
+ free(channelbuffer);
+ free(daybuffer);
+ free(filebuffer);
+@@ -632,6 +647,37 @@
+ Matches(); // refresh start and end time
+ }
+
++#ifdef USE_PINPLUGIN
++void cTimer::SetFskProtection(int aFlag)
++{
++ char* p;
++ char* tmp = 0;
++
++ fskProtection = aFlag;
++
++ if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // add protection info to aux
++
++ if (aux) { tmp = strdup(aux); free(aux); }
++ if (asprintf(&aux,"%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "") < 0 )
++ aux = NULL;
++ }
++ else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++ {
++ // remove protection info to aux
++
++ if (asprintf(&tmp, "%.*s%s", p-aux, aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>")) >= 0 ) {
++ free(aux);
++ aux = strdup(tmp);
++ }
++ }
++
++ if (tmp)
++ free(tmp);
++}
++#endif /* PINPLUGIN */
++
+ // --- cTimers ---------------------------------------------------------------
+
+ cTimers Timers;
+diff -ruN vdr-1.7.14/timers.h vdr-1.7.14.ExtP_NG/timers.h
+--- vdr-1.7.14/timers.h 2008-02-16 15:33:23.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/timers.h 2010-04-10 15:45:11.743736984 +0200
+@@ -37,6 +37,9 @@
+ int start;
+ int stop;
+ int priority;
++#ifdef USE_PINPLUGIN
++ int fskProtection;
++#endif /* PINPLUGIN */
+ int lifetime;
+ mutable char file[MaxFileName];
+ char *aux;
+@@ -58,6 +61,9 @@
+ int Start(void) const { return start; }
+ int Stop(void) const { return stop; }
+ int Priority(void) const { return priority; }
++#ifdef USE_PINPLUGIN
++ int FskProtection(void) const { return fskProtection; }
++#endif /* PINPLUGIN */
+ int Lifetime(void) const { return lifetime; }
+ const char *File(void) const { return file; }
+ time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -86,6 +92,9 @@
+ void SetInVpsMargin(bool InVpsMargin);
+ void SetPriority(int Priority);
+ void SetFlags(uint Flags);
++#ifdef USE_PINPLUGIN
++ void SetFskProtection(int aFlag);
++#endif /* PINPLUGIN */
+ void ClrFlags(uint Flags);
+ void InvFlags(uint Flags);
+ bool HasFlags(uint Flags) const;
+diff -ruN vdr-1.7.14/tinystr.c vdr-1.7.14.ExtP_NG/tinystr.c
+--- vdr-1.7.14/tinystr.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/tinystr.c 2010-04-10 15:45:11.756737883 +0200
+@@ -0,0 +1,301 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original file by Yves Berquin.
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++
++#ifndef TIXML_USE_STL
++
++
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++
++#include "tinystr.h"
++
++// TiXmlString constructor, based on a C string
++TiXmlString::TiXmlString (const char* instring)
++{
++ unsigned newlen;
++ char * newstring;
++
++ if (!instring)
++ {
++ allocated = 0;
++ cstring = NULL;
++ current_length = 0;
++ return;
++ }
++ newlen = strlen (instring) + 1;
++ newstring = new char [newlen];
++ memcpy (newstring, instring, newlen);
++ // strcpy (newstring, instring);
++ allocated = newlen;
++ cstring = newstring;
++ current_length = newlen - 1;
++}
++
++// TiXmlString copy constructor
++TiXmlString::TiXmlString (const TiXmlString& copy)
++{
++ unsigned newlen;
++ char * newstring;
++
++ // Prevent copy to self!
++ if ( &copy == this )
++ return;
++
++ if (! copy . allocated)
++ {
++ allocated = 0;
++ cstring = NULL;
++ current_length = 0;
++ return;
++ }
++ newlen = copy . length () + 1;
++ newstring = new char [newlen];
++ // strcpy (newstring, copy . cstring);
++ memcpy (newstring, copy . cstring, newlen);
++ allocated = newlen;
++ cstring = newstring;
++ current_length = newlen - 1;
++}
++
++// TiXmlString = operator. Safe when assign own content
++void TiXmlString ::operator = (const char * content)
++{
++ unsigned newlen;
++ char * newstring;
++
++ if (! content)
++ {
++ empty_it ();
++ return;
++ }
++ newlen = strlen (content) + 1;
++ newstring = new char [newlen];
++ // strcpy (newstring, content);
++ memcpy (newstring, content, newlen);
++ empty_it ();
++ allocated = newlen;
++ cstring = newstring;
++ current_length = newlen - 1;
++}
++
++// = operator. Safe when assign own content
++void TiXmlString ::operator = (const TiXmlString & copy)
++{
++ unsigned newlen;
++ char * newstring;
++
++ if (! copy . length ())
++ {
++ empty_it ();
++ return;
++ }
++ newlen = copy . length () + 1;
++ newstring = new char [newlen];
++ // strcpy (newstring, copy . c_str ());
++ memcpy (newstring, copy . c_str (), newlen);
++ empty_it ();
++ allocated = newlen;
++ cstring = newstring;
++ current_length = newlen - 1;
++}
++
++
++// append a const char * to an existing TiXmlString
++void TiXmlString::append( const char* str, int len )
++{
++ char * new_string;
++ unsigned new_alloc, new_size, size_suffix;
++
++ // don't use strlen - it can overrun the len passed in!
++ const char* p = str;
++ size_suffix = 0;
++
++ while ( *p && size_suffix < (unsigned)len )
++ {
++ ++p;
++ ++size_suffix;
++ }
++ if ( !size_suffix)
++ return;
++
++ new_size = length () + size_suffix + 1;
++ // check if we need to expand
++ if (new_size > allocated)
++ {
++ // compute new size
++ new_alloc = assign_new_size (new_size);
++
++ // allocate new buffer
++ new_string = new char [new_alloc];
++ new_string [0] = 0;
++
++ // copy the previous allocated buffer into this one
++ if (allocated && cstring)
++ // strcpy (new_string, cstring);
++ memcpy (new_string, cstring, length ());
++
++ // append the suffix. It does exist, otherwize we wouldn't be expanding
++ // strncat (new_string, str, len);
++ memcpy (new_string + length (),
++ str,
++ size_suffix);
++
++ // return previsously allocated buffer if any
++ if (allocated && cstring)
++ delete [] cstring;
++
++ // update member variables
++ cstring = new_string;
++ allocated = new_alloc;
++ }
++ else
++ {
++ // we know we can safely append the new string
++ // strncat (cstring, str, len);
++ memcpy (cstring + length (),
++ str,
++ size_suffix);
++ }
++ current_length = new_size - 1;
++ cstring [current_length] = 0;
++}
++
++
++// append a const char * to an existing TiXmlString
++void TiXmlString::append( const char * suffix )
++{
++ char * new_string;
++ unsigned new_alloc, new_size;
++
++ new_size = length () + strlen (suffix) + 1;
++ // check if we need to expand
++ if (new_size > allocated)
++ {
++ // compute new size
++ new_alloc = assign_new_size (new_size);
++
++ // allocate new buffer
++ new_string = new char [new_alloc];
++ new_string [0] = 0;
++
++ // copy the previous allocated buffer into this one
++ if (allocated && cstring)
++ memcpy (new_string, cstring, 1 + length ());
++ // strcpy (new_string, cstring);
++
++ // append the suffix. It does exist, otherwize we wouldn't be expanding
++ // strcat (new_string, suffix);
++ memcpy (new_string + length (),
++ suffix,
++ strlen (suffix) + 1);
++
++ // return previsously allocated buffer if any
++ if (allocated && cstring)
++ delete [] cstring;
++
++ // update member variables
++ cstring = new_string;
++ allocated = new_alloc;
++ }
++ else
++ {
++ // we know we can safely append the new string
++ // strcat (cstring, suffix);
++ memcpy (cstring + length (),
++ suffix,
++ strlen (suffix) + 1);
++ }
++ current_length = new_size - 1;
++}
++
++// Check for TiXmlString equuivalence
++//bool TiXmlString::operator == (const TiXmlString & compare) const
++//{
++// return (! strcmp (c_str (), compare . c_str ()));
++//}
++
++//unsigned TiXmlString::length () const
++//{
++// if (allocated)
++// // return strlen (cstring);
++// return current_length;
++// return 0;
++//}
++
++
++unsigned TiXmlString::find (char tofind, unsigned offset) const
++{
++ char * lookup;
++
++ if (offset >= length ())
++ return (unsigned) notfound;
++ for (lookup = cstring + offset; * lookup; lookup++)
++ if (* lookup == tofind)
++ return lookup - cstring;
++ return (unsigned) notfound;
++}
++
++
++bool TiXmlString::operator == (const TiXmlString & compare) const
++{
++ if ( allocated && compare.allocated )
++ {
++ assert( cstring );
++ assert( compare.cstring );
++ return ( strcmp( cstring, compare.cstring ) == 0 );
++ }
++ return false;
++}
++
++
++bool TiXmlString::operator < (const TiXmlString & compare) const
++{
++ if ( allocated && compare.allocated )
++ {
++ assert( cstring );
++ assert( compare.cstring );
++ return ( strcmp( cstring, compare.cstring ) > 0 );
++ }
++ return false;
++}
++
++
++bool TiXmlString::operator > (const TiXmlString & compare) const
++{
++ if ( allocated && compare.allocated )
++ {
++ assert( cstring );
++ assert( compare.cstring );
++ return ( strcmp( cstring, compare.cstring ) < 0 );
++ }
++ return false;
++}
++
++
++#endif // TIXML_USE_STL
++#endif /* SETUP */
+diff -ruN vdr-1.7.14/tinystr.h vdr-1.7.14.ExtP_NG/tinystr.h
+--- vdr-1.7.14/tinystr.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/tinystr.h 2010-04-10 15:45:11.768736847 +0200
+@@ -0,0 +1,244 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original file by Yves Berquin.
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++
++
++#ifndef TIXML_USE_STL
++
++#ifndef TIXML_STRING_INCLUDED
++#define TIXML_STRING_INCLUDED
++
++#ifdef _MSC_VER
++#pragma warning( disable : 4786 ) // Debugger truncating names.
++#endif
++
++#include <assert.h>
++
++/*
++ TiXmlString is an emulation of the std::string template.
++ Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
++ Only the member functions relevant to the TinyXML project have been implemented.
++ The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
++ a string and there's no more room, we allocate a buffer twice as big as we need.
++*/
++class TiXmlString
++{
++ public :
++ // TiXmlString constructor, based on a string
++ TiXmlString (const char * instring);
++
++ // TiXmlString empty constructor
++ TiXmlString ()
++ {
++ allocated = 0;
++ cstring = NULL;
++ current_length = 0;
++ }
++
++ // TiXmlString copy constructor
++ TiXmlString (const TiXmlString& copy);
++
++ // TiXmlString destructor
++ ~ TiXmlString ()
++ {
++ empty_it ();
++ }
++
++ // Convert a TiXmlString into a classical char *
++ const char * c_str () const
++ {
++ if (allocated)
++ return cstring;
++ return "";
++ }
++
++ // Return the length of a TiXmlString
++ unsigned length () const
++ {
++ return ( allocated ) ? current_length : 0;
++ }
++
++ // TiXmlString = operator
++ void operator = (const char * content);
++
++ // = operator
++ void operator = (const TiXmlString & copy);
++
++ // += operator. Maps to append
++ TiXmlString& operator += (const char * suffix)
++ {
++ append (suffix);
++ return *this;
++ }
++
++ // += operator. Maps to append
++ TiXmlString& operator += (char single)
++ {
++ append (single);
++ return *this;
++ }
++
++ // += operator. Maps to append
++ TiXmlString& operator += (TiXmlString & suffix)
++ {
++ append (suffix);
++ return *this;
++ }
++ bool operator == (const TiXmlString & compare) const;
++ bool operator < (const TiXmlString & compare) const;
++ bool operator > (const TiXmlString & compare) const;
++
++ // Checks if a TiXmlString is empty
++ bool empty () const
++ {
++ return length () ? false : true;
++ }
++
++ // single char extraction
++ const char& at (unsigned index) const
++ {
++ assert( index < length ());
++ return cstring [index];
++ }
++
++ // find a char in a string. Return TiXmlString::notfound if not found
++ unsigned find (char lookup) const
++ {
++ return find (lookup, 0);
++ }
++
++ // find a char in a string from an offset. Return TiXmlString::notfound if not found
++ unsigned find (char tofind, unsigned offset) const;
++
++ /* Function to reserve a big amount of data when we know we'll need it. Be aware that this
++ function clears the content of the TiXmlString if any exists.
++ */
++ void reserve (unsigned size)
++ {
++ empty_it ();
++ if (size)
++ {
++ allocated = size;
++ cstring = new char [size];
++ cstring [0] = 0;
++ current_length = 0;
++ }
++ }
++
++ // [] operator
++ char& operator [] (unsigned index) const
++ {
++ assert( index < length ());
++ return cstring [index];
++ }
++
++ // Error value for find primitive
++ enum { notfound = 0xffffffff,
++ npos = notfound };
++
++ void append (const char *str, int len );
++
++ protected :
++
++ // The base string
++ char * cstring;
++ // Number of chars allocated
++ unsigned allocated;
++ // Current string size
++ unsigned current_length;
++
++ // New size computation. It is simplistic right now : it returns twice the amount
++ // we need
++ unsigned assign_new_size (unsigned minimum_to_allocate)
++ {
++ return minimum_to_allocate * 2;
++ }
++
++ // Internal function that clears the content of a TiXmlString
++ void empty_it ()
++ {
++ if (cstring)
++ delete [] cstring;
++ cstring = NULL;
++ allocated = 0;
++ current_length = 0;
++ }
++
++ void append (const char *suffix );
++
++ // append function for another TiXmlString
++ void append (const TiXmlString & suffix)
++ {
++ append (suffix . c_str ());
++ }
++
++ // append for a single char.
++ void append (char single)
++ {
++ if ( cstring && current_length < (allocated-1) )
++ {
++ cstring[ current_length ] = single;
++ ++current_length;
++ cstring[ current_length ] = 0;
++ }
++ else
++ {
++ char smallstr [2];
++ smallstr [0] = single;
++ smallstr [1] = 0;
++ append (smallstr);
++ }
++ }
++
++} ;
++
++/*
++ TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
++ Only the operators that we need for TinyXML have been developped.
++*/
++class TiXmlOutStream : public TiXmlString
++{
++public :
++ TiXmlOutStream () : TiXmlString () {}
++
++ // TiXmlOutStream << operator. Maps to TiXmlString::append
++ TiXmlOutStream & operator << (const char * in)
++ {
++ append (in);
++ return (* this);
++ }
++
++ // TiXmlOutStream << operator. Maps to TiXmlString::append
++ TiXmlOutStream & operator << (const TiXmlString & in)
++ {
++ append (in . c_str ());
++ return (* this);
++ }
++} ;
++
++#endif // TIXML_STRING_INCLUDED
++#endif // TIXML_USE_STL
++#endif /* SETUP */
+diff -ruN vdr-1.7.14/tinyxml.c vdr-1.7.14.ExtP_NG/tinyxml.c
+--- vdr-1.7.14/tinyxml.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/tinyxml.c 2010-04-10 15:45:11.784736808 +0200
+@@ -0,0 +1,1429 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include <ctype.h>
++#include "tinyxml.h"
++
++#ifdef TIXML_USE_STL
++#include <sstream>
++#endif
++
++
++bool TiXmlBase::condenseWhiteSpace = true;
++
++void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_OSTREAM* stream )
++{
++ TIXML_STRING buffer;
++ PutString( str, &buffer );
++ (*stream) << buffer;
++}
++
++void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString )
++{
++ int i=0;
++
++ while( i<(int)str.length() )
++ {
++ unsigned char c = (unsigned char) str[i];
++
++ if ( c == '&'
++ && i < ( (int)str.length() - 2 )
++ && str[i+1] == '#'
++ && str[i+2] == 'x' )
++ {
++ // Hexadecimal character reference.
++ // Pass through unchanged.
++ // &#xA9; -- copyright symbol, for example.
++ //
++ // The -1 is a bug fix from Rob Laveaux. It keeps
++ // an overflow from happening if there is no ';'.
++ // There are actually 2 ways to exit this loop -
++ // while fails (error case) and break (semicolon found).
++ // However, there is no mechanism (currently) for
++ // this function to return an error.
++ while ( i<(int)str.length()-1 )
++ {
++ outString->append( str.c_str() + i, 1 );
++ ++i;
++ if ( str[i] == ';' )
++ break;
++ }
++ }
++ else if ( c == '&' )
++ {
++ outString->append( entity[0].str, entity[0].strLength );
++ ++i;
++ }
++ else if ( c == '<' )
++ {
++ outString->append( entity[1].str, entity[1].strLength );
++ ++i;
++ }
++ else if ( c == '>' )
++ {
++ outString->append( entity[2].str, entity[2].strLength );
++ ++i;
++ }
++ else if ( c == '\"' )
++ {
++ outString->append( entity[3].str, entity[3].strLength );
++ ++i;
++ }
++ else if ( c == '\'' )
++ {
++ outString->append( entity[4].str, entity[4].strLength );
++ ++i;
++ }
++ else if ( c < 32 )
++ {
++ // Easy pass at non-alpha/numeric/symbol
++ // Below 32 is symbolic.
++ char buf[ 32 ];
++ sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
++ outString->append( buf, strlen( buf ) );
++ ++i;
++ }
++ else
++ {
++ //char realc = (char) c;
++ //outString->append( &realc, 1 );
++ *outString += (char) c; // somewhat more efficient function call.
++ ++i;
++ }
++ }
++}
++
++
++// <-- Strange class for a bug fix. Search for STL_STRING_BUG
++TiXmlBase::StringToBuffer::StringToBuffer( const TIXML_STRING& str )
++{
++ buffer = new char[ str.length()+1 ];
++ if ( buffer )
++ {
++ strcpy( buffer, str.c_str() );
++ }
++}
++
++
++TiXmlBase::StringToBuffer::~StringToBuffer()
++{
++ delete [] buffer;
++}
++// End strange bug fix. -->
++
++
++TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
++{
++ parent = 0;
++ type = _type;
++ firstChild = 0;
++ lastChild = 0;
++ prev = 0;
++ next = 0;
++}
++
++
++TiXmlNode::~TiXmlNode()
++{
++ TiXmlNode* node = firstChild;
++ TiXmlNode* temp = 0;
++
++ while ( node )
++ {
++ temp = node;
++ node = node->next;
++ delete temp;
++ }
++}
++
++
++void TiXmlNode::CopyTo( TiXmlNode* target ) const
++{
++ target->SetValue (value.c_str() );
++ target->userData = userData;
++}
++
++
++void TiXmlNode::Clear()
++{
++ TiXmlNode* node = firstChild;
++ TiXmlNode* temp = 0;
++
++ while ( node )
++ {
++ temp = node;
++ node = node->next;
++ delete temp;
++ }
++
++ firstChild = 0;
++ lastChild = 0;
++}
++
++
++TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
++{
++ node->parent = this;
++
++ node->prev = lastChild;
++ node->next = 0;
++
++ if ( lastChild )
++ lastChild->next = node;
++ else
++ firstChild = node; // it was an empty list.
++
++ lastChild = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
++{
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++
++ return LinkEndChild( node );
++}
++
++
++TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
++{
++ if ( !beforeThis || beforeThis->parent != this )
++ return 0;
++
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++ node->parent = this;
++
++ node->next = beforeThis;
++ node->prev = beforeThis->prev;
++ if ( beforeThis->prev )
++ {
++ beforeThis->prev->next = node;
++ }
++ else
++ {
++ assert( firstChild == beforeThis );
++ firstChild = node;
++ }
++ beforeThis->prev = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
++{
++ if ( !afterThis || afterThis->parent != this )
++ return 0;
++
++ TiXmlNode* node = addThis.Clone();
++ if ( !node )
++ return 0;
++ node->parent = this;
++
++ node->prev = afterThis;
++ node->next = afterThis->next;
++ if ( afterThis->next )
++ {
++ afterThis->next->prev = node;
++ }
++ else
++ {
++ assert( lastChild == afterThis );
++ lastChild = node;
++ }
++ afterThis->next = node;
++ return node;
++}
++
++
++TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
++{
++ if ( replaceThis->parent != this )
++ return 0;
++
++ TiXmlNode* node = withThis.Clone();
++ if ( !node )
++ return 0;
++
++ node->next = replaceThis->next;
++ node->prev = replaceThis->prev;
++
++ if ( replaceThis->next )
++ replaceThis->next->prev = node;
++ else
++ lastChild = node;
++
++ if ( replaceThis->prev )
++ replaceThis->prev->next = node;
++ else
++ firstChild = node;
++
++ delete replaceThis;
++ node->parent = this;
++ return node;
++}
++
++
++bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
++{
++ if ( removeThis->parent != this )
++ {
++ assert( 0 );
++ return false;
++ }
++
++ if ( removeThis->next )
++ removeThis->next->prev = removeThis->prev;
++ else
++ lastChild = removeThis->prev;
++
++ if ( removeThis->prev )
++ removeThis->prev->next = removeThis->next;
++ else
++ firstChild = removeThis->next;
++
++ delete removeThis;
++ return true;
++}
++
++TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
++{
++ TiXmlNode* node;
++ for ( node = firstChild; node; node = node->next )
++ {
++ if ( node->SValue() == TIXML_STRING( _value ))
++ return node;
++ }
++ return 0;
++}
++
++TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
++{
++ TiXmlNode* node;
++ for ( node = lastChild; node; node = node->prev )
++ {
++ if ( node->SValue() == TIXML_STRING (_value))
++ return node;
++ }
++ return 0;
++}
++
++TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) const
++{
++ if ( !previous )
++ {
++ return FirstChild();
++ }
++ else
++ {
++ assert( previous->parent == this );
++ return previous->NextSibling();
++ }
++}
++
++TiXmlNode* TiXmlNode::IterateChildren( const char * val, TiXmlNode* previous ) const
++{
++ if ( !previous )
++ {
++ return FirstChild( val );
++ }
++ else
++ {
++ assert( previous->parent == this );
++ return previous->NextSibling( val );
++ }
++}
++
++TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const
++{
++ TiXmlNode* node;
++ for ( node = next; node; node = node->next )
++ {
++ if ( node->SValue() == TIXML_STRING (_value))
++ return node;
++ }
++ return 0;
++}
++
++
++TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
++{
++ TiXmlNode* node;
++ for ( node = prev; node; node = node->prev )
++ {
++ if ( node->SValue() == TIXML_STRING (_value))
++ return node;
++ }
++ return 0;
++}
++
++void TiXmlElement::RemoveAttribute( const char * name )
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++ if ( node )
++ {
++ attributeSet.Remove( node );
++ delete node;
++ }
++}
++
++TiXmlElement* TiXmlNode::FirstChildElement() const
++{
++ TiXmlNode* node;
++
++ for ( node = FirstChild();
++ node;
++ node = node->NextSibling() )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
++{
++ TiXmlNode* node;
++
++ for ( node = FirstChild( _value );
++ node;
++ node = node->NextSibling( _value ) )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++
++TiXmlElement* TiXmlNode::NextSiblingElement() const
++{
++ TiXmlNode* node;
++
++ for ( node = NextSibling();
++ node;
++ node = node->NextSibling() )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
++{
++ TiXmlNode* node;
++
++ for ( node = NextSibling( _value );
++ node;
++ node = node->NextSibling( _value ) )
++ {
++ if ( node->ToElement() )
++ return node->ToElement();
++ }
++ return 0;
++}
++
++
++
++TiXmlDocument* TiXmlNode::GetDocument() const
++{
++ const TiXmlNode* node;
++
++ for( node = this; node; node = node->parent )
++ {
++ if ( node->ToDocument() )
++ return node->ToDocument();
++ }
++ return 0;
++}
++
++
++TiXmlElement::TiXmlElement (const char * _value)
++ : TiXmlNode( TiXmlNode::ELEMENT )
++{
++ firstChild = lastChild = 0;
++ value = _value;
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlElement::TiXmlElement( const std::string& _value )
++ : TiXmlNode( TiXmlNode::ELEMENT )
++{
++ firstChild = lastChild = 0;
++ value = _value;
++}
++#endif
++
++
++TiXmlElement::TiXmlElement( const TiXmlElement& copy)
++ : TiXmlNode( TiXmlNode::ELEMENT )
++{
++ firstChild = lastChild = 0;
++ copy.CopyTo( this );
++}
++
++
++void TiXmlElement::operator=( const TiXmlElement& base )
++{
++ ClearThis();
++ base.CopyTo( this );
++}
++
++
++TiXmlElement::~TiXmlElement()
++{
++ ClearThis();
++}
++
++
++void TiXmlElement::ClearThis()
++{
++ Clear();
++ while( attributeSet.First() )
++ {
++ TiXmlAttribute* node = attributeSet.First();
++ attributeSet.Remove( node );
++ delete node;
++ }
++}
++
++
++const char * TiXmlElement::Attribute( const char * name ) const
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++
++ if ( node )
++ return node->Value();
++
++ return 0;
++}
++
++
++const char * TiXmlElement::Attribute( const char * name, int* i ) const
++{
++ const char * s = Attribute( name );
++ if ( i )
++ {
++ if ( s )
++ *i = atoi( s );
++ else
++ *i = 0;
++ }
++ return s;
++}
++
++
++const char * TiXmlElement::Attribute( const char * name, double* d ) const
++{
++ const char * s = Attribute( name );
++ if ( d )
++ {
++ if ( s )
++ *d = atof( s );
++ else
++ *d = 0;
++ }
++ return s;
++}
++
++
++int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++ if ( !node )
++ return TIXML_NO_ATTRIBUTE;
++
++ return node->QueryIntValue( ival );
++}
++
++
++int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++ if ( !node )
++ return TIXML_NO_ATTRIBUTE;
++
++ return node->QueryDoubleValue( dval );
++}
++
++
++void TiXmlElement::SetAttribute( const char * name, int val )
++{
++ char buf[64];
++ sprintf( buf, "%d", val );
++ SetAttribute( name, buf );
++}
++
++
++void TiXmlElement::SetDoubleAttribute( const char * name, double val )
++{
++ char buf[128];
++ sprintf( buf, "%f", val );
++ SetAttribute( name, buf );
++}
++
++
++void TiXmlElement::SetAttribute( const char * name, const char * _value )
++{
++ TiXmlAttribute* node = attributeSet.Find( name );
++ if ( node )
++ {
++ node->SetValue( _value );
++ return;
++ }
++
++ TiXmlAttribute* attrib = new TiXmlAttribute( name, _value );
++ if ( attrib )
++ {
++ attributeSet.Add( attrib );
++ }
++ else
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ }
++}
++
++void TiXmlElement::Print( FILE* cfile, int depth ) const
++{
++ int i;
++ for ( i=0; i<depth; i++ )
++ {
++ fprintf( cfile, " " );
++ }
++
++ fprintf( cfile, "<%s", value.c_str() );
++
++ TiXmlAttribute* attrib;
++ for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
++ {
++ fprintf( cfile, " " );
++ attrib->Print( cfile, depth );
++ }
++
++ // There are 3 different formatting approaches:
++ // 1) An element without children is printed as a <foo /> node
++ // 2) An element with only a text child is printed as <foo> text </foo>
++ // 3) An element with children is printed on multiple lines.
++ TiXmlNode* node;
++ if ( !firstChild )
++ {
++ fprintf( cfile, " />" );
++ }
++ else if ( firstChild == lastChild && firstChild->ToText() )
++ {
++ fprintf( cfile, ">" );
++ firstChild->Print( cfile, depth + 1 );
++ fprintf( cfile, "</%s>", value.c_str() );
++ }
++ else
++ {
++ fprintf( cfile, ">" );
++
++ for ( node = firstChild; node; node=node->NextSibling() )
++ {
++ if ( !node->ToText() )
++ {
++ fprintf( cfile, "\n" );
++ }
++ node->Print( cfile, depth+1 );
++ }
++ fprintf( cfile, "\n" );
++ for( i=0; i<depth; ++i )
++ fprintf( cfile, " " );
++ fprintf( cfile, "</%s>", value.c_str() );
++ }
++}
++
++void TiXmlElement::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ (*stream) << "<" << value;
++
++ TiXmlAttribute* attrib;
++ for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
++ {
++ (*stream) << " ";
++ attrib->StreamOut( stream );
++ }
++
++ // If this node has children, give it a closing tag. Else
++ // make it an empty tag.
++ TiXmlNode* node;
++ if ( firstChild )
++ {
++ (*stream) << ">";
++
++ for ( node = firstChild; node; node=node->NextSibling() )
++ {
++ node->StreamOut( stream );
++ }
++ (*stream) << "</" << value << ">";
++ }
++ else
++ {
++ (*stream) << " />";
++ }
++}
++
++
++void TiXmlElement::CopyTo( TiXmlElement* target ) const
++{
++ // superclass:
++ TiXmlNode::CopyTo( target );
++
++ // Element class:
++ // Clone the attributes, then clone the children.
++ TiXmlAttribute* attribute = 0;
++ for( attribute = attributeSet.First();
++ attribute;
++ attribute = attribute->Next() )
++ {
++ target->SetAttribute( attribute->Name(), attribute->Value() );
++ }
++
++ TiXmlNode* node = 0;
++ for ( node = firstChild; node; node = node->NextSibling() )
++ {
++ target->LinkEndChild( node->Clone() );
++ }
++}
++
++
++TiXmlNode* TiXmlElement::Clone() const
++{
++ TiXmlElement* clone = new TiXmlElement( Value() );
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++ tabsize = 4;
++ ClearError();
++}
++
++TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++ tabsize = 4;
++ value = documentName;
++ ClearError();
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++ tabsize = 4;
++ value = documentName;
++ ClearError();
++}
++#endif
++
++
++TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++ copy.CopyTo( this );
++}
++
++
++void TiXmlDocument::operator=( const TiXmlDocument& copy )
++{
++ Clear();
++ copy.CopyTo( this );
++}
++
++
++bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
++{
++ // See STL_STRING_BUG below.
++ StringToBuffer buf( value );
++
++ if ( buf.buffer && LoadFile( buf.buffer, encoding ) )
++ return true;
++
++ return false;
++}
++
++
++bool TiXmlDocument::SaveFile() const
++{
++ // See STL_STRING_BUG below.
++ StringToBuffer buf( value );
++
++ if ( buf.buffer && SaveFile( buf.buffer ) )
++ return true;
++
++ return false;
++}
++
++bool TiXmlDocument::LoadFile( const char* filename, TiXmlEncoding encoding )
++{
++ // Delete the existing data:
++ Clear();
++ location.Clear();
++
++ // There was a really terrifying little bug here. The code:
++ // value = filename
++ // in the STL case, cause the assignment method of the std::string to
++ // be called. What is strange, is that the std::string had the same
++ // address as it's c_str() method, and so bad things happen. Looks
++ // like a bug in the Microsoft STL implementation.
++ // See STL_STRING_BUG above.
++ // Fixed with the StringToBuffer class.
++ value = filename;
++
++ FILE* file = fopen( value.c_str (), "r" );
++
++ if ( file )
++ {
++ // Get the file size, so we can pre-allocate the string. HUGE speed impact.
++ long length = 0;
++ fseek( file, 0, SEEK_END );
++ length = ftell( file );
++ fseek( file, 0, SEEK_SET );
++
++ // Strange case, but good to handle up front.
++ if ( length == 0 )
++ {
++ fclose( file );
++ return false;
++ }
++
++ // If we have a file, assume it is all one big XML file, and read it in.
++ // The document parser may decide the document ends sooner than the entire file, however.
++ TIXML_STRING data;
++ data.reserve( length );
++
++ const int BUF_SIZE = 2048;
++ char buf[BUF_SIZE];
++
++ while( fgets( buf, BUF_SIZE, file ) )
++ {
++ data += buf;
++ }
++ fclose( file );
++
++ Parse( data.c_str(), 0, encoding );
++
++ if ( Error() )
++ return false;
++ else
++ return true;
++ }
++ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return false;
++}
++
++bool TiXmlDocument::SaveFile( const char * filename ) const
++{
++ // The old c stuff lives on...
++ FILE* fp = fopen( filename, "w" );
++ if ( fp )
++ {
++ Print( fp, 0 );
++ fclose( fp );
++ return true;
++ }
++ return false;
++}
++
++
++void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
++{
++ TiXmlNode::CopyTo( target );
++
++ target->error = error;
++ target->errorDesc = errorDesc.c_str ();
++
++ TiXmlNode* node = 0;
++ for ( node = firstChild; node; node = node->NextSibling() )
++ {
++ target->LinkEndChild( node->Clone() );
++ }
++}
++
++
++TiXmlNode* TiXmlDocument::Clone() const
++{
++ TiXmlDocument* clone = new TiXmlDocument();
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlDocument::Print( FILE* cfile, int depth ) const
++{
++ TiXmlNode* node;
++ for ( node=FirstChild(); node; node=node->NextSibling() )
++ {
++ node->Print( cfile, depth );
++ fprintf( cfile, "\n" );
++ }
++}
++
++void TiXmlDocument::StreamOut( TIXML_OSTREAM * out ) const
++{
++ TiXmlNode* node;
++ for ( node=FirstChild(); node; node=node->NextSibling() )
++ {
++ node->StreamOut( out );
++
++ // Special rule for streams: stop after the root element.
++ // The stream in code will only read one element, so don't
++ // write more than one.
++ if ( node->ToElement() )
++ break;
++ }
++}
++
++
++TiXmlAttribute* TiXmlAttribute::Next() const
++{
++ // We are using knowledge of the sentinel. The sentinel
++ // have a value or name.
++ if ( next->value.empty() && next->name.empty() )
++ return 0;
++ return next;
++}
++
++
++TiXmlAttribute* TiXmlAttribute::Previous() const
++{
++ // We are using knowledge of the sentinel. The sentinel
++ // have a value or name.
++ if ( prev->value.empty() && prev->name.empty() )
++ return 0;
++ return prev;
++}
++
++
++void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const
++{
++ TIXML_STRING n, v;
++
++ PutString( name, &n );
++ PutString( value, &v );
++
++ if (value.find ('\"') == TIXML_STRING::npos)
++ fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
++ else
++ fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
++}
++
++
++void TiXmlAttribute::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ if (value.find( '\"' ) != TIXML_STRING::npos)
++ {
++ PutString( name, stream );
++ (*stream) << "=" << "'";
++ PutString( value, stream );
++ (*stream) << "'";
++ }
++ else
++ {
++ PutString( name, stream );
++ (*stream) << "=" << "\"";
++ PutString( value, stream );
++ (*stream) << "\"";
++ }
++}
++
++int TiXmlAttribute::QueryIntValue( int* ival ) const
++{
++ if ( sscanf( value.c_str(), "%d", ival ) == 1 )
++ return TIXML_SUCCESS;
++ return TIXML_WRONG_TYPE;
++}
++
++int TiXmlAttribute::QueryDoubleValue( double* dval ) const
++{
++ if ( sscanf( value.c_str(), "%lf", dval ) == 1 )
++ return TIXML_SUCCESS;
++ return TIXML_WRONG_TYPE;
++}
++
++void TiXmlAttribute::SetIntValue( int _value )
++{
++ char buf [64];
++ sprintf (buf, "%d", _value);
++ SetValue (buf);
++}
++
++void TiXmlAttribute::SetDoubleValue( double _value )
++{
++ char buf [64];
++ sprintf (buf, "%lf", _value);
++ SetValue (buf);
++}
++
++const int TiXmlAttribute::IntValue() const
++{
++ return atoi (value.c_str ());
++}
++
++const double TiXmlAttribute::DoubleValue() const
++{
++ return atof (value.c_str ());
++}
++
++
++TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT )
++{
++ copy.CopyTo( this );
++}
++
++
++void TiXmlComment::operator=( const TiXmlComment& base )
++{
++ Clear();
++ base.CopyTo( this );
++}
++
++
++void TiXmlComment::Print( FILE* cfile, int depth ) const
++{
++ for ( int i=0; i<depth; i++ )
++ {
++ fputs( " ", cfile );
++ }
++ fprintf( cfile, "<!--%s-->", value.c_str() );
++}
++
++void TiXmlComment::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ (*stream) << "<!--";
++ //PutString( value, stream );
++ (*stream) << value;
++ (*stream) << "-->";
++}
++
++
++void TiXmlComment::CopyTo( TiXmlComment* target ) const
++{
++ TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlComment::Clone() const
++{
++ TiXmlComment* clone = new TiXmlComment();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlText::Print( FILE* cfile, int /*depth*/ ) const
++{
++ TIXML_STRING buffer;
++ PutString( value, &buffer );
++ fprintf( cfile, "%s", buffer.c_str() );
++}
++
++
++void TiXmlText::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ PutString( value, stream );
++}
++
++
++void TiXmlText::CopyTo( TiXmlText* target ) const
++{
++ TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlText::Clone() const
++{
++ TiXmlText* clone = 0;
++ clone = new TiXmlText( "" );
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++TiXmlDeclaration::TiXmlDeclaration( const char * _version,
++ const char * _encoding,
++ const char * _standalone )
++ : TiXmlNode( TiXmlNode::DECLARATION )
++{
++ version = _version;
++ encoding = _encoding;
++ standalone = _standalone;
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
++ const std::string& _encoding,
++ const std::string& _standalone )
++ : TiXmlNode( TiXmlNode::DECLARATION )
++{
++ version = _version;
++ encoding = _encoding;
++ standalone = _standalone;
++}
++#endif
++
++
++TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
++ : TiXmlNode( TiXmlNode::DECLARATION )
++{
++ copy.CopyTo( this );
++}
++
++
++void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
++{
++ Clear();
++ copy.CopyTo( this );
++}
++
++
++void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/ ) const
++{
++ fprintf (cfile, "<?xml ");
++
++ if ( !version.empty() )
++ fprintf (cfile, "version=\"%s\" ", version.c_str ());
++ if ( !encoding.empty() )
++ fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
++ if ( !standalone.empty() )
++ fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
++ fprintf (cfile, "?>");
++}
++
++void TiXmlDeclaration::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ (*stream) << "<?xml ";
++
++ if ( !version.empty() )
++ {
++ (*stream) << "version=\"";
++ PutString( version, stream );
++ (*stream) << "\" ";
++ }
++ if ( !encoding.empty() )
++ {
++ (*stream) << "encoding=\"";
++ PutString( encoding, stream );
++ (*stream ) << "\" ";
++ }
++ if ( !standalone.empty() )
++ {
++ (*stream) << "standalone=\"";
++ PutString( standalone, stream );
++ (*stream) << "\" ";
++ }
++ (*stream) << "?>";
++}
++
++
++void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
++{
++ TiXmlNode::CopyTo( target );
++
++ target->version = version;
++ target->encoding = encoding;
++ target->standalone = standalone;
++}
++
++
++TiXmlNode* TiXmlDeclaration::Clone() const
++{
++ TiXmlDeclaration* clone = new TiXmlDeclaration();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++void TiXmlUnknown::Print( FILE* cfile, int depth ) const
++{
++ for ( int i=0; i<depth; i++ )
++ fprintf( cfile, " " );
++ fprintf( cfile, "<%s>", value.c_str() );
++}
++
++
++void TiXmlUnknown::StreamOut( TIXML_OSTREAM * stream ) const
++{
++ (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown.
++}
++
++
++void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
++{
++ TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlUnknown::Clone() const
++{
++ TiXmlUnknown* clone = new TiXmlUnknown();
++
++ if ( !clone )
++ return 0;
++
++ CopyTo( clone );
++ return clone;
++}
++
++
++TiXmlAttributeSet::TiXmlAttributeSet()
++{
++ sentinel.next = &sentinel;
++ sentinel.prev = &sentinel;
++}
++
++
++TiXmlAttributeSet::~TiXmlAttributeSet()
++{
++ assert( sentinel.next == &sentinel );
++ assert( sentinel.prev == &sentinel );
++}
++
++
++void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
++{
++ assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set.
++
++ addMe->next = &sentinel;
++ addMe->prev = sentinel.prev;
++
++ sentinel.prev->next = addMe;
++ sentinel.prev = addMe;
++}
++
++void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
++{
++ TiXmlAttribute* node;
++
++ for( node = sentinel.next; node != &sentinel; node = node->next )
++ {
++ if ( node == removeMe )
++ {
++ node->prev->next = node->next;
++ node->next->prev = node->prev;
++ node->next = 0;
++ node->prev = 0;
++ return;
++ }
++ }
++ assert( 0 ); // we tried to remove a non-linked attribute.
++}
++
++TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) const
++{
++ TiXmlAttribute* node;
++
++ for( node = sentinel.next; node != &sentinel; node = node->next )
++ {
++ if ( node->name == name )
++ return node;
++ }
++ return 0;
++}
++
++
++#ifdef TIXML_USE_STL
++TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base)
++{
++ TIXML_STRING tag;
++ tag.reserve( 8 * 1000 );
++ base.StreamIn( &in, &tag );
++
++ base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
++ return in;
++}
++#endif
++
++
++TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base)
++{
++ base.StreamOut (& out);
++ return out;
++}
++
++
++#ifdef TIXML_USE_STL
++std::string & operator<< (std::string& out, const TiXmlNode& base )
++{
++ std::ostringstream os_stream( std::ostringstream::out );
++ base.StreamOut( &os_stream );
++
++ out.append( os_stream.str() );
++ return out;
++}
++#endif
++
++
++TiXmlHandle TiXmlHandle::FirstChild() const
++{
++ if ( node )
++ {
++ TiXmlNode* child = node->FirstChild();
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
++{
++ if ( node )
++ {
++ TiXmlNode* child = node->FirstChild( value );
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChildElement() const
++{
++ if ( node )
++ {
++ TiXmlElement* child = node->FirstChildElement();
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const
++{
++ if ( node )
++ {
++ TiXmlElement* child = node->FirstChildElement( value );
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::Child( int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlNode* child = node->FirstChild();
++ for ( i=0;
++ child && i<count;
++ child = child->NextSibling(), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlNode* child = node->FirstChild( value );
++ for ( i=0;
++ child && i<count;
++ child = child->NextSibling( value ), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::ChildElement( int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlElement* child = node->FirstChildElement();
++ for ( i=0;
++ child && i<count;
++ child = child->NextSiblingElement(), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const
++{
++ if ( node )
++ {
++ int i;
++ TiXmlElement* child = node->FirstChildElement( value );
++ for ( i=0;
++ child && i<count;
++ child = child->NextSiblingElement( value ), ++i )
++ {
++ // nothing
++ }
++ if ( child )
++ return TiXmlHandle( child );
++ }
++ return TiXmlHandle( 0 );
++}
++#endif /* SETUP */
+diff -ruN vdr-1.7.14/tinyxml.h vdr-1.7.14.ExtP_NG/tinyxml.h
+--- vdr-1.7.14/tinyxml.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/tinyxml.h 2010-04-10 15:45:11.801740955 +0200
+@@ -0,0 +1,1372 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++
++#ifndef TINYXML_INCLUDED
++#define TINYXML_INCLUDED
++
++#ifdef _MSC_VER
++#pragma warning( disable : 4530 )
++#pragma warning( disable : 4786 )
++#endif
++
++#include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <assert.h>
++
++// Help out windows:
++#if defined( _DEBUG ) && !defined( DEBUG )
++#define DEBUG
++#endif
++
++#if defined( DEBUG ) && defined( _MSC_VER )
++#include <windows.h>
++#define TIXML_LOG OutputDebugString
++#else
++#define TIXML_LOG printf
++#endif
++
++#ifdef TIXML_USE_STL
++ #include <string>
++ #include <iostream>
++ #define TIXML_STRING std::string
++ #define TIXML_ISTREAM std::istream
++ #define TIXML_OSTREAM std::ostream
++#else
++ #include "tinystr.h"
++ #define TIXML_STRING TiXmlString
++ #define TIXML_OSTREAM TiXmlOutStream
++#endif
++
++class TiXmlDocument;
++class TiXmlElement;
++class TiXmlComment;
++class TiXmlUnknown;
++class TiXmlAttribute;
++class TiXmlText;
++class TiXmlDeclaration;
++class TiXmlParsingData;
++
++const int TIXML_MAJOR_VERSION = 2;
++const int TIXML_MINOR_VERSION = 3;
++const int TIXML_PATCH_VERSION = 2;
++
++/* Internal structure for tracking location of items
++ in the XML file.
++*/
++struct TiXmlCursor
++{
++ TiXmlCursor() { Clear(); }
++ void Clear() { row = col = -1; }
++
++ int row; // 0 based.
++ int col; // 0 based.
++};
++
++
++// Only used by Attribute::Query functions
++enum
++{
++ TIXML_SUCCESS,
++ TIXML_NO_ATTRIBUTE,
++ TIXML_WRONG_TYPE
++};
++
++
++// Used by the parsing routines.
++enum TiXmlEncoding
++{
++ TIXML_ENCODING_UNKNOWN,
++ TIXML_ENCODING_UTF8,
++ TIXML_ENCODING_LEGACY
++};
++
++const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
++
++/** TiXmlBase is a base class for every class in TinyXml.
++ It does little except to establish that TinyXml classes
++ can be printed and provide some utility functions.
++
++ In XML, the document and elements can contain
++ other elements and other types of nodes.
++
++ @verbatim
++ A Document can contain: Element (container or leaf)
++ Comment (leaf)
++ Unknown (leaf)
++ Declaration( leaf )
++
++ An Element can contain: Element (container or leaf)
++ Text (leaf)
++ Attributes (not on tree)
++ Comment (leaf)
++ Unknown (leaf)
++
++ A Decleration contains: Attributes (not on tree)
++ @endverbatim
++*/
++class TiXmlBase
++{
++ friend class TiXmlNode;
++ friend class TiXmlElement;
++ friend class TiXmlDocument;
++
++public:
++ TiXmlBase() : userData(0) {}
++ virtual ~TiXmlBase() {}
++
++ /** All TinyXml classes can print themselves to a filestream.
++ This is a formatted print, and will insert tabs and newlines.
++
++ (For an unformatted stream, use the << operator.)
++ */
++ virtual void Print( FILE* cfile, int depth ) const = 0;
++
++ /** The world does not agree on whether white space should be kept or
++ not. In order to make everyone happy, these global, static functions
++ are provided to set whether or not TinyXml will condense all white space
++ into a single space or not. The default is to condense. Note changing this
++ values is not thread safe.
++ */
++ static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; }
++
++ /// Return the current white space setting.
++ static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; }
++
++ /** Return the position, in the original source file, of this node or attribute.
++ The row and column are 1-based. (That is the first row and first column is
++ 1,1). If the returns values are 0 or less, then the parser does not have
++ a row and column value.
++
++ Generally, the row and column value will be set when the TiXmlDocument::Load(),
++ TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
++ when the DOM was created from operator>>.
++
++ The values reflect the initial load. Once the DOM is modified programmatically
++ (by adding or changing nodes and attributes) the new values will NOT update to
++ reflect changes in the document.
++
++ There is a minor performance cost to computing the row and column. Computation
++ can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
++
++ @sa TiXmlDocument::SetTabSize()
++ */
++ int Row() const { return location.row + 1; }
++ int Column() const { return location.col + 1; } ///< See Row()
++
++ void SetUserData( void* user ) { userData = user; }
++ void* GetUserData() { return userData; }
++
++ // Table that returs, for a given lead byte, the total number of bytes
++ // in the UTF-8 sequence.
++ static const int utf8ByteTable[256];
++
++ virtual const char* Parse( const char* p,
++ TiXmlParsingData* data,
++ TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
++
++protected:
++
++ // See STL_STRING_BUG
++ // Utility class to overcome a bug.
++ class StringToBuffer
++ {
++ public:
++ StringToBuffer( const TIXML_STRING& str );
++ ~StringToBuffer();
++ char* buffer;
++ };
++
++ static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
++ inline static bool IsWhiteSpace( char c )
++ {
++ return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' );
++ }
++
++ virtual void StreamOut (TIXML_OSTREAM *) const = 0;
++
++ #ifdef TIXML_USE_STL
++ static bool StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ static bool StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag );
++ #endif
++
++ /* Reads an XML name into the string provided. Returns
++ a pointer just past the last character of the name,
++ or 0 if the function has an error.
++ */
++ static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
++
++ /* Reads text. Returns a pointer past the given end tag.
++ Wickedly complex options, but it keeps the (sensitive) code in one place.
++ */
++ static const char* ReadText( const char* in, // where to start
++ TIXML_STRING* text, // the string read
++ bool ignoreWhiteSpace, // whether to keep the white space
++ const char* endTag, // what ends this text
++ bool ignoreCase, // whether to ignore case in the end tag
++ TiXmlEncoding encoding ); // the current encoding
++
++ // If an entity has been found, transform it into a character.
++ static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
++
++ // Get a character, while interpreting entities.
++ // The length can be from 0 to 4 bytes.
++ inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
++ {
++ assert( p );
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ *length = utf8ByteTable[ *((unsigned char*)p) ];
++ assert( *length >= 0 && *length < 5 );
++ }
++ else
++ {
++ *length = 1;
++ }
++
++ if ( *length == 1 )
++ {
++ if ( *p == '&' )
++ return GetEntity( p, _value, length, encoding );
++ *_value = *p;
++ return p+1;
++ }
++ else if ( *length )
++ {
++ strncpy( _value, p, *length );
++ return p + (*length);
++ }
++ else
++ {
++ // Not valid text.
++ return 0;
++ }
++ }
++
++ // Puts a string to a stream, expanding entities as it goes.
++ // Note this should not contian the '<', '>', etc, or they will be transformed into entities!
++ static void PutString( const TIXML_STRING& str, TIXML_OSTREAM* out );
++
++ static void PutString( const TIXML_STRING& str, TIXML_STRING* out );
++
++ // Return true if the next characters in the stream are any of the endTag sequences.
++ // Ignore case only works for english, and should only be relied on when comparing
++ // to Engilish words: StringEqual( p, "version", true ) is fine.
++ static bool StringEqual( const char* p,
++ const char* endTag,
++ bool ignoreCase,
++ TiXmlEncoding encoding );
++
++
++ enum
++ {
++ TIXML_NO_ERROR = 0,
++ TIXML_ERROR,
++ TIXML_ERROR_OPENING_FILE,
++ TIXML_ERROR_OUT_OF_MEMORY,
++ TIXML_ERROR_PARSING_ELEMENT,
++ TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
++ TIXML_ERROR_READING_ELEMENT_VALUE,
++ TIXML_ERROR_READING_ATTRIBUTES,
++ TIXML_ERROR_PARSING_EMPTY,
++ TIXML_ERROR_READING_END_TAG,
++ TIXML_ERROR_PARSING_UNKNOWN,
++ TIXML_ERROR_PARSING_COMMENT,
++ TIXML_ERROR_PARSING_DECLARATION,
++ TIXML_ERROR_DOCUMENT_EMPTY,
++ TIXML_ERROR_EMBEDDED_NULL,
++
++ TIXML_ERROR_STRING_COUNT
++ };
++ static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
++
++ TiXmlCursor location;
++
++ /// Field containing a generic user pointer
++ void* userData;
++
++ // None of these methods are reliable for any language except English.
++ // Good for approximation, not great for accuracy.
++ static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
++ static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
++ inline static int ToLower( int v, TiXmlEncoding encoding )
++ {
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ if ( v < 128 ) return tolower( v );
++ return v;
++ }
++ else
++ {
++ return tolower( v );
++ }
++ }
++ static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
++
++private:
++ TiXmlBase( const TiXmlBase& ); // not implemented.
++ void operator=( const TiXmlBase& base ); // not allowed.
++
++ struct Entity
++ {
++ const char* str;
++ unsigned int strLength;
++ char chr;
++ };
++ enum
++ {
++ NUM_ENTITY = 5,
++ MAX_ENTITY_LENGTH = 6
++
++ };
++ static Entity entity[ NUM_ENTITY ];
++ static bool condenseWhiteSpace;
++};
++
++
++/** The parent class for everything in the Document Object Model.
++ (Except for attributes).
++ Nodes have siblings, a parent, and children. A node can be
++ in a document, or stand on its own. The type of a TiXmlNode
++ can be queried, and it can be cast to its more defined type.
++*/
++class TiXmlNode : public TiXmlBase
++{
++ friend class TiXmlDocument;
++ friend class TiXmlElement;
++
++public:
++ #ifdef TIXML_USE_STL
++
++ /** An input stream operator, for every class. Tolerant of newlines and
++ formatting, but doesn't expect them.
++ */
++ friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
++
++ /** An output stream operator, for every class. Note that this outputs
++ without any newlines or formatting, as opposed to Print(), which
++ includes tabs and new lines.
++
++ The operator<< and operator>> are not completely symmetric. Writing
++ a node to a stream is very well defined. You'll get a nice stream
++ of output, without any extra whitespace or newlines.
++
++ But reading is not as well defined. (As it always is.) If you create
++ a TiXmlElement (for example) and read that from an input stream,
++ the text needs to define an element or junk will result. This is
++ true of all input streams, but it's worth keeping in mind.
++
++ A TiXmlDocument will read nodes until it reads a root element, and
++ all the children of that root element.
++ */
++ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
++
++ /// Appends the XML node or attribute to a std::string.
++ friend std::string& operator<< (std::string& out, const TiXmlNode& base );
++
++ #else
++ // Used internally, not part of the public API.
++ friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base);
++ #endif
++
++ /** The types of XML nodes supported by TinyXml. (All the
++ unsupported types are picked up by UNKNOWN.)
++ */
++ enum NodeType
++ {
++ DOCUMENT,
++ ELEMENT,
++ COMMENT,
++ UNKNOWN,
++ TEXT,
++ DECLARATION,
++ TYPECOUNT
++ };
++
++ virtual ~TiXmlNode();
++
++ /** The meaning of 'value' changes for the specific type of
++ TiXmlNode.
++ @verbatim
++ Document: filename of the xml file
++ Element: name of the element
++ Comment: the comment text
++ Unknown: the tag contents
++ Text: the text string
++ @endverbatim
++
++ The subclasses will wrap this function.
++ */
++ const char * Value() const { return value.c_str (); }
++
++ /** Changes the value of the node. Defined as:
++ @verbatim
++ Document: filename of the xml file
++ Element: name of the element
++ Comment: the comment text
++ Unknown: the tag contents
++ Text: the text string
++ @endverbatim
++ */
++ void SetValue(const char * _value) { value = _value;}
++
++ #ifdef TIXML_USE_STL
++ /// STL std::string form.
++ void SetValue( const std::string& _value )
++ {
++ StringToBuffer buf( _value );
++ SetValue( buf.buffer ? buf.buffer : "" );
++ }
++ #endif
++
++ /// Delete all the children of this node. Does not affect 'this'.
++ void Clear();
++
++ /// One step up the DOM.
++ TiXmlNode* Parent() const { return parent; }
++
++ TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children.
++ TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found.
++
++ TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children.
++ TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children.
++
++ #ifdef TIXML_USE_STL
++ TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form.
++ TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /** An alternate way to walk the children of a node.
++ One way to iterate over nodes is:
++ @verbatim
++ for( child = parent->FirstChild(); child; child = child->NextSibling() )
++ @endverbatim
++
++ IterateChildren does the same thing with the syntax:
++ @verbatim
++ child = 0;
++ while( child = parent->IterateChildren( child ) )
++ @endverbatim
++
++ IterateChildren takes the previous child as input and finds
++ the next one. If the previous child is null, it returns the
++ first. IterateChildren will return null when done.
++ */
++ TiXmlNode* IterateChildren( TiXmlNode* previous ) const;
++
++ /// This flavor of IterateChildren searches for children with a particular 'value'
++ TiXmlNode* IterateChildren( const char * value, TiXmlNode* previous ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlNode* IterateChildren( const std::string& _value, TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
++ #endif
++
++ /** Add a new node related to this. Adds a child past the LastChild.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
++
++
++ /** Add a new node related to this. Adds a child past the LastChild.
++
++ NOTE: the node to be added is passed by pointer, and will be
++ henceforth owned (and deleted) by tinyXml. This method is efficient
++ and avoids an extra copy, but should be used with care as it
++ uses a different memory model than the other insert functions.
++
++ @sa InsertEndChild
++ */
++ TiXmlNode* LinkEndChild( TiXmlNode* addThis );
++
++ /** Add a new node related to this. Adds a child before the specified child.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
++
++ /** Add a new node related to this. Adds a child after the specified child.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis );
++
++ /** Replace a child of this node.
++ Returns a pointer to the new object or NULL if an error occured.
++ */
++ TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
++
++ /// Delete a child of this node.
++ bool RemoveChild( TiXmlNode* removeThis );
++
++ /// Navigate to a sibling node.
++ TiXmlNode* PreviousSibling() const { return prev; }
++
++ /// Navigate to a sibling node.
++ TiXmlNode* PreviousSibling( const char * ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
++ TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /// Navigate to a sibling node.
++ TiXmlNode* NextSibling() const { return next; }
++
++ /// Navigate to a sibling node with the given 'value'.
++ TiXmlNode* NextSibling( const char * ) const;
++
++ /** Convenience function to get through elements.
++ Calls NextSibling and ToElement. Will skip all non-Element
++ nodes. Returns 0 if there is not another element.
++ */
++ TiXmlElement* NextSiblingElement() const;
++
++ /** Convenience function to get through elements.
++ Calls NextSibling and ToElement. Will skip all non-Element
++ nodes. Returns 0 if there is not another element.
++ */
++ TiXmlElement* NextSiblingElement( const char * ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /// Convenience function to get through elements.
++ TiXmlElement* FirstChildElement() const;
++
++ /// Convenience function to get through elements.
++ TiXmlElement* FirstChildElement( const char * value ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
++ #endif
++
++ /** Query the type (as an enumerated value, above) of this node.
++ The possible types are: DOCUMENT, ELEMENT, COMMENT,
++ UNKNOWN, TEXT, and DECLARATION.
++ */
++ virtual int Type() const { return type; }
++
++ /** Return a pointer to the Document this node lives in.
++ Returns null if not in a document.
++ */
++ TiXmlDocument* GetDocument() const;
++
++ /// Returns true if this node has no children.
++ bool NoChildren() const { return !firstChild; }
++
++ TiXmlDocument* ToDocument() const { return ( this && type == DOCUMENT ) ? (TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlElement* ToElement() const { return ( this && type == ELEMENT ) ? (TiXmlElement*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlComment* ToComment() const { return ( this && type == COMMENT ) ? (TiXmlComment*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlUnknown* ToUnknown() const { return ( this && type == UNKNOWN ) ? (TiXmlUnknown*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlText* ToText() const { return ( this && type == TEXT ) ? (TiXmlText*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++ TiXmlDeclaration* ToDeclaration() const { return ( this && type == DECLARATION ) ? (TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++ /** Create an exact duplicate of this node and return it. The memory must be deleted
++ by the caller.
++ */
++ virtual TiXmlNode* Clone() const = 0;
++
++protected:
++ TiXmlNode( NodeType _type );
++
++ // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
++ // and the assignment operator.
++ void CopyTo( TiXmlNode* target ) const;
++
++ #ifdef TIXML_USE_STL
++ // The real work of the input operator.
++ virtual void StreamIn( TIXML_ISTREAM* in, TIXML_STRING* tag ) = 0;
++ #endif
++
++ // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
++ TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
++
++ // Internal Value function returning a TIXML_STRING
++ const TIXML_STRING& SValue() const { return value ; }
++
++ TiXmlNode* parent;
++ NodeType type;
++
++ TiXmlNode* firstChild;
++ TiXmlNode* lastChild;
++
++ TIXML_STRING value;
++
++ TiXmlNode* prev;
++ TiXmlNode* next;
++
++private:
++ TiXmlNode( const TiXmlNode& ); // not implemented.
++ void operator=( const TiXmlNode& base ); // not allowed.
++};
++
++
++/** An attribute is a name-value pair. Elements have an arbitrary
++ number of attributes, each with a unique name.
++
++ @note The attributes are not TiXmlNodes, since they are not
++ part of the tinyXML document object model. There are other
++ suggested ways to look at this problem.
++*/
++class TiXmlAttribute : public TiXmlBase
++{
++ friend class TiXmlAttributeSet;
++
++public:
++ /// Construct an empty attribute.
++ TiXmlAttribute() : TiXmlBase()
++ {
++ document = 0;
++ prev = next = 0;
++ }
++
++ #ifdef TIXML_USE_STL
++ /// std::string constructor.
++ TiXmlAttribute( const std::string& _name, const std::string& _value )
++ {
++ name = _name;
++ value = _value;
++ document = 0;
++ prev = next = 0;
++ }
++ #endif
++
++ /// Construct an attribute with a name and value.
++ TiXmlAttribute( const char * _name, const char * _value )
++ {
++ name = _name;
++ value = _value;
++ document = 0;
++ prev = next = 0;
++ }
++
++ const char* Name() const { return name.c_str (); } ///< Return the name of this attribute.
++ const char* Value() const { return value.c_str (); } ///< Return the value of this attribute.
++ const int IntValue() const; ///< Return the value of this attribute, converted to an integer.
++ const double DoubleValue() const; ///< Return the value of this attribute, converted to a double.
++
++ /** QueryIntValue examines the value string. It is an alternative to the
++ IntValue() method with richer error checking.
++ If the value is an integer, it is stored in 'value' and
++ the call returns TIXML_SUCCESS. If it is not
++ an integer, it returns TIXML_WRONG_TYPE.
++
++ A specialized but useful call. Note that for success it returns 0,
++ which is the opposite of almost all other TinyXml calls.
++ */
++ int QueryIntValue( int* value ) const;
++ /// QueryDoubleValue examines the value string. See QueryIntValue().
++ int QueryDoubleValue( double* value ) const;
++
++ void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute.
++ void SetValue( const char* _value ) { value = _value; } ///< Set the value.
++
++ void SetIntValue( int value ); ///< Set the value from an integer.
++ void SetDoubleValue( double value ); ///< Set the value from a double.
++
++ #ifdef TIXML_USE_STL
++ /// STL std::string form.
++ void SetName( const std::string& _name )
++ {
++ StringToBuffer buf( _name );
++ SetName ( buf.buffer ? buf.buffer : "error" );
++ }
++ /// STL std::string form.
++ void SetValue( const std::string& _value )
++ {
++ StringToBuffer buf( _value );
++ SetValue( buf.buffer ? buf.buffer : "error" );
++ }
++ #endif
++
++ /// Get the next sibling attribute in the DOM. Returns null at end.
++ TiXmlAttribute* Next() const;
++ /// Get the previous sibling attribute in the DOM. Returns null at beginning.
++ TiXmlAttribute* Previous() const;
++
++ bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
++ bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; }
++ bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; }
++
++ /* Attribute parsing starts: first letter of the name
++ returns: the next char after the value end quote
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++ // Prints this Attribute to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual void StreamOut( TIXML_OSTREAM * out ) const;
++ // [internal use]
++ // Set the document pointer so the attribute can report errors.
++ void SetDocument( TiXmlDocument* doc ) { document = doc; }
++
++private:
++ TiXmlAttribute( const TiXmlAttribute& ); // not implemented.
++ void operator=( const TiXmlAttribute& base ); // not allowed.
++
++ TiXmlDocument* document; // A pointer back to a document, for error reporting.
++ TIXML_STRING name;
++ TIXML_STRING value;
++ TiXmlAttribute* prev;
++ TiXmlAttribute* next;
++};
++
++
++/* A class used to manage a group of attributes.
++ It is only used internally, both by the ELEMENT and the DECLARATION.
++
++ The set can be changed transparent to the Element and Declaration
++ classes that use it, but NOT transparent to the Attribute
++ which has to implement a next() and previous() method. Which makes
++ it a bit problematic and prevents the use of STL.
++
++ This version is implemented with circular lists because:
++ - I like circular lists
++ - it demonstrates some independence from the (typical) doubly linked list.
++*/
++class TiXmlAttributeSet
++{
++public:
++ TiXmlAttributeSet();
++ ~TiXmlAttributeSet();
++
++ void Add( TiXmlAttribute* attribute );
++ void Remove( TiXmlAttribute* attribute );
++
++ TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
++ TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
++ TiXmlAttribute* Find( const char * name ) const;
++
++private:
++ TiXmlAttribute sentinel;
++};
++
++
++/** The element is a container class. It has a value, the element name,
++ and can contain other elements, text, comments, and unknowns.
++ Elements also contain an arbitrary number of attributes.
++*/
++class TiXmlElement : public TiXmlNode
++{
++public:
++ /// Construct an element.
++ TiXmlElement (const char * in_value);
++
++ #ifdef TIXML_USE_STL
++ /// std::string constructor.
++ TiXmlElement( const std::string& _value );
++ #endif
++
++ TiXmlElement( const TiXmlElement& );
++
++ void operator=( const TiXmlElement& base );
++
++ virtual ~TiXmlElement();
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ */
++ const char* Attribute( const char* name ) const;
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ If the attribute exists and can be converted to an integer,
++ the integer value will be put in the return 'i', if 'i'
++ is non-null.
++ */
++ const char* Attribute( const char* name, int* i ) const;
++
++ /** Given an attribute name, Attribute() returns the value
++ for the attribute of that name, or null if none exists.
++ If the attribute exists and can be converted to an double,
++ the double value will be put in the return 'd', if 'd'
++ is non-null.
++ */
++ const char* Attribute( const char* name, double* d ) const;
++
++ /** QueryIntAttribute examines the attribute - it is an alternative to the
++ Attribute() method with richer error checking.
++ If the attribute is an integer, it is stored in 'value' and
++ the call returns TIXML_SUCCESS. If it is not
++ an integer, it returns TIXML_WRONG_TYPE. If the attribute
++ does not exist, then TIXML_NO_ATTRIBUTE is returned.
++ */
++ int QueryIntAttribute( const char* name, int* value ) const;
++ /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
++ int QueryDoubleAttribute( const char* name, double* value ) const;
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetAttribute( const char* name, const char * value );
++
++ #ifdef TIXML_USE_STL
++ const char* Attribute( const std::string& name ) const { return Attribute( name.c_str() ); }
++ const char* Attribute( const std::string& name, int* i ) const { return Attribute( name.c_str(), i ); }
++ const char* Attribute( const std::string& name, double* d ) const { return Attribute( name.c_str(), d ); }
++ int QueryIntAttribute( const std::string& name, int* value ) const { return QueryIntAttribute( name.c_str(), value ); }
++ int QueryDoubleAttribute( const std::string& name, double* value ) const { return QueryDoubleAttribute( name.c_str(), value ); }
++
++ /// STL std::string form.
++ void SetAttribute( const std::string& name, const std::string& _value )
++ {
++ StringToBuffer n( name );
++ StringToBuffer v( _value );
++ if ( n.buffer && v.buffer )
++ SetAttribute (n.buffer, v.buffer );
++ }
++ ///< STL std::string form.
++ void SetAttribute( const std::string& name, int _value )
++ {
++ StringToBuffer n( name );
++ if ( n.buffer )
++ SetAttribute (n.buffer, _value);
++ }
++ #endif
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetAttribute( const char * name, int value );
++
++ /** Sets an attribute of name to a given value. The attribute
++ will be created if it does not exist, or changed if it does.
++ */
++ void SetDoubleAttribute( const char * name, double value );
++
++ /** Deletes an attribute with the given name.
++ */
++ void RemoveAttribute( const char * name );
++ #ifdef TIXML_USE_STL
++ void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form.
++ #endif
++
++ TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element.
++ TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element.
++
++ /// Creates a new Element and returns it - the returned element is a copy.
++ virtual TiXmlNode* Clone() const;
++ // Print the Element to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ /* Attribtue parsing starts: next char past '<'
++ returns: next char past '>'
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++
++ void CopyTo( TiXmlElement* target ) const;
++ void ClearThis(); // like clear, but initializes 'this' object as well
++
++ // Used to be public [internal use]
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++ virtual void StreamOut( TIXML_OSTREAM * out ) const;
++
++ /* [internal use]
++ Reads the "value" of the element -- another element, or text.
++ This should terminate with the current end tag.
++ */
++ const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
++
++private:
++
++ TiXmlAttributeSet attributeSet;
++};
++
++
++/** An XML comment.
++*/
++class TiXmlComment : public TiXmlNode
++{
++public:
++ /// Constructs an empty comment.
++ TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {}
++ TiXmlComment( const TiXmlComment& );
++ void operator=( const TiXmlComment& base );
++
++ virtual ~TiXmlComment() {}
++
++ /// Returns a copy of this Comment.
++ virtual TiXmlNode* Clone() const;
++ /// Write this Comment to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ /* Attribtue parsing starts: at the ! of the !--
++ returns: next char past '>'
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++ void CopyTo( TiXmlComment* target ) const;
++
++ // used to be public
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++ virtual void StreamOut( TIXML_OSTREAM * out ) const;
++
++private:
++
++};
++
++
++/** XML text. Contained in an element.
++*/
++class TiXmlText : public TiXmlNode
++{
++ friend class TiXmlElement;
++public:
++ /// Constructor.
++ TiXmlText (const char * initValue) : TiXmlNode (TiXmlNode::TEXT)
++ {
++ SetValue( initValue );
++ }
++ virtual ~TiXmlText() {}
++
++ #ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT)
++ {
++ SetValue( initValue );
++ }
++ #endif
++
++ TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); }
++ void operator=( const TiXmlText& base ) { base.CopyTo( this ); }
++
++ /// Write this text object to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected :
++ /// [internal use] Creates a new Element and returns it.
++ virtual TiXmlNode* Clone() const;
++ void CopyTo( TiXmlText* target ) const;
++
++ virtual void StreamOut ( TIXML_OSTREAM * out ) const;
++ bool Blank() const; // returns true if all white space and new lines
++ // [internal use]
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++
++private:
++};
++
++
++/** In correct XML the declaration is the first entry in the file.
++ @verbatim
++ <?xml version="1.0" standalone="yes"?>
++ @endverbatim
++
++ TinyXml will happily read or write files without a declaration,
++ however. There are 3 possible attributes to the declaration:
++ version, encoding, and standalone.
++
++ Note: In this version of the code, the attributes are
++ handled as special cases, not generic attributes, simply
++ because there can only be at most 3 and they are always the same.
++*/
++class TiXmlDeclaration : public TiXmlNode
++{
++public:
++ /// Construct an empty declaration.
++ TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {}
++
++#ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlDeclaration( const std::string& _version,
++ const std::string& _encoding,
++ const std::string& _standalone );
++#endif
++
++ /// Construct.
++ TiXmlDeclaration( const char* _version,
++ const char* _encoding,
++ const char* _standalone );
++
++ TiXmlDeclaration( const TiXmlDeclaration& copy );
++ void operator=( const TiXmlDeclaration& copy );
++
++ virtual ~TiXmlDeclaration() {}
++
++ /// Version. Will return an empty string if none was found.
++ const char *Version() const { return version.c_str (); }
++ /// Encoding. Will return an empty string if none was found.
++ const char *Encoding() const { return encoding.c_str (); }
++ /// Is this a standalone document?
++ const char *Standalone() const { return standalone.c_str (); }
++
++ /// Creates a copy of this Declaration and returns it.
++ virtual TiXmlNode* Clone() const;
++ /// Print this declaration to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++ void CopyTo( TiXmlDeclaration* target ) const;
++ // used to be public
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++ virtual void StreamOut ( TIXML_OSTREAM * out) const;
++
++private:
++
++ TIXML_STRING version;
++ TIXML_STRING encoding;
++ TIXML_STRING standalone;
++};
++
++
++/** Any tag that tinyXml doesn't recognize is saved as an
++ unknown. It is a tag of text, but should not be modified.
++ It will be written back to the XML, unchanged, when the file
++ is saved.
++
++ DTD tags get thrown into TiXmlUnknowns.
++*/
++class TiXmlUnknown : public TiXmlNode
++{
++public:
++ TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {}
++ virtual ~TiXmlUnknown() {}
++
++ TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); }
++ void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); }
++
++ /// Creates a copy of this Unknown and returns it.
++ virtual TiXmlNode* Clone() const;
++ /// Print this Unknown to a FILE stream.
++ virtual void Print( FILE* cfile, int depth ) const;
++
++ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++ void CopyTo( TiXmlUnknown* target ) const;
++
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++ virtual void StreamOut ( TIXML_OSTREAM * out ) const;
++
++private:
++
++};
++
++
++/** Always the top level node. A document binds together all the
++ XML pieces. It can be saved, loaded, and printed to the screen.
++ The 'value' of a document node is the xml file name.
++*/
++class TiXmlDocument : public TiXmlNode
++{
++public:
++ /// Create an empty document, that has no name.
++ TiXmlDocument();
++ /// Create a document with a name. The name of the document is also the filename of the xml.
++ TiXmlDocument( const char * documentName );
++
++ #ifdef TIXML_USE_STL
++ /// Constructor.
++ TiXmlDocument( const std::string& documentName );
++ #endif
++
++ TiXmlDocument( const TiXmlDocument& copy );
++ void operator=( const TiXmlDocument& copy );
++
++ virtual ~TiXmlDocument() {}
++
++ /** Load a file using the current document value.
++ Returns true if successful. Will delete any existing
++ document data before loading.
++ */
++ bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++ /// Save a file using the current document value. Returns true if successful.
++ bool SaveFile() const;
++ /// Load a file using the given filename. Returns true if successful.
++ bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++ /// Save a file using the given filename. Returns true if successful.
++ bool SaveFile( const char * filename ) const;
++
++ #ifdef TIXML_USE_STL
++ bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version.
++ {
++ StringToBuffer f( filename );
++ return ( f.buffer && LoadFile( f.buffer, encoding ));
++ }
++ bool SaveFile( const std::string& filename ) const ///< STL std::string version.
++ {
++ StringToBuffer f( filename );
++ return ( f.buffer && SaveFile( f.buffer ));
++ }
++ #endif
++
++ /** Parse the given null terminated block of xml data. Passing in an encoding to this
++ method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
++ to use that encoding, regardless of what TinyXml might otherwise try to detect.
++ */
++ virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++
++ /** Get the root element -- the only top level element -- of the document.
++ In well formed XML, there should only be one. TinyXml is tolerant of
++ multiple elements at the document level.
++ */
++ TiXmlElement* RootElement() const { return FirstChildElement(); }
++
++ /** If an error occurs, Error will be set to true. Also,
++ - The ErrorId() will contain the integer identifier of the error (not generally useful)
++ - The ErrorDesc() method will return the name of the error. (very useful)
++ - The ErrorRow() and ErrorCol() will return the location of the error (if known)
++ */
++ bool Error() const { return error; }
++
++ /// Contains a textual (english) description of the error if one occurs.
++ const char * ErrorDesc() const { return errorDesc.c_str (); }
++
++ /** Generally, you probably want the error string ( ErrorDesc() ). But if you
++ prefer the ErrorId, this function will fetch it.
++ */
++ const int ErrorId() const { return errorId; }
++
++ /** Returns the location (if known) of the error. The first column is column 1,
++ and the first row is row 1. A value of 0 means the row and column wasn't applicable
++ (memory errors, for example, have no row/column) or the parser lost the error. (An
++ error in the error reporting, in that case.)
++
++ @sa SetTabSize, Row, Column
++ */
++ int ErrorRow() { return errorLocation.row+1; }
++ int ErrorCol() { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
++
++ /** By calling this method, with a tab size
++ greater than 0, the row and column of each node and attribute is stored
++ when the file is loaded. Very useful for tracking the DOM back in to
++ the source file.
++
++ The tab size is required for calculating the location of nodes. If not
++ set, the default of 4 is used. The tabsize is set per document. Setting
++ the tabsize to 0 disables row/column tracking.
++
++ Note that row and column tracking is not supported when using operator>>.
++
++ The tab size needs to be enabled before the parse or load. Correct usage:
++ @verbatim
++ TiXmlDocument doc;
++ doc.SetTabSize( 8 );
++ doc.Load( "myfile.xml" );
++ @endverbatim
++
++ @sa Row, Column
++ */
++ void SetTabSize( int _tabsize ) { tabsize = _tabsize; }
++
++ int TabSize() const { return tabsize; }
++
++ /** If you have handled the error, it can be reset with this call. The error
++ state is automatically cleared if you Parse a new XML block.
++ */
++ void ClearError() { error = false;
++ errorId = 0;
++ errorDesc = "";
++ errorLocation.row = errorLocation.col = 0;
++ //errorLocation.last = 0;
++ }
++
++ /** Dump the document to standard out. */
++ void Print() const { Print( stdout, 0 ); }
++
++ /// Print this Document to a FILE stream.
++ virtual void Print( FILE* cfile, int depth = 0 ) const;
++ // [internal use]
++ void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
++
++protected :
++ virtual void StreamOut ( TIXML_OSTREAM * out) const;
++ // [internal use]
++ virtual TiXmlNode* Clone() const;
++ #ifdef TIXML_USE_STL
++ virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++ #endif
++
++private:
++ void CopyTo( TiXmlDocument* target ) const;
++
++ bool error;
++ int errorId;
++ TIXML_STRING errorDesc;
++ int tabsize;
++ TiXmlCursor errorLocation;
++};
++
++
++/**
++ A TiXmlHandle is a class that wraps a node pointer with null checks; this is
++ an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
++ DOM structure. It is a separate utility class.
++
++ Take an example:
++ @verbatim
++ <Document>
++ <Element attributeA = "valueA">
++ <Child attributeB = "value1" />
++ <Child attributeB = "value2" />
++ </Element>
++ <Document>
++ @endverbatim
++
++ Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
++ easy to write a *lot* of code that looks like:
++
++ @verbatim
++ TiXmlElement* root = document.FirstChildElement( "Document" );
++ if ( root )
++ {
++ TiXmlElement* element = root->FirstChildElement( "Element" );
++ if ( element )
++ {
++ TiXmlElement* child = element->FirstChildElement( "Child" );
++ if ( child )
++ {
++ TiXmlElement* child2 = child->NextSiblingElement( "Child" );
++ if ( child2 )
++ {
++ // Finally do something useful.
++ @endverbatim
++
++ And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
++ of such code. A TiXmlHandle checks for null pointers so it is perfectly safe
++ and correct to use:
++
++ @verbatim
++ TiXmlHandle docHandle( &document );
++ TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).Element();
++ if ( child2 )
++ {
++ // do something useful
++ @endverbatim
++
++ Which is MUCH more concise and useful.
++
++ It is also safe to copy handles - internally they are nothing more than node pointers.
++ @verbatim
++ TiXmlHandle handleCopy = handle;
++ @endverbatim
++
++ What they should not be used for is iteration:
++
++ @verbatim
++ int i=0;
++ while ( true )
++ {
++ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).Element();
++ if ( !child )
++ break;
++ // do something
++ ++i;
++ }
++ @endverbatim
++
++ It seems reasonable, but it is in fact two embedded while loops. The Child method is
++ a linear walk to find the element, so this code would iterate much more than it needs
++ to. Instead, prefer:
++
++ @verbatim
++ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).Element();
++
++ for( child; child; child=child->NextSiblingElement() )
++ {
++ // do something
++ }
++ @endverbatim
++*/
++class TiXmlHandle
++{
++public:
++ /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
++ TiXmlHandle( TiXmlNode* node ) { this->node = node; }
++ /// Copy constructor
++ TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; }
++ TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; }
++
++ /// Return a handle to the first child node.
++ TiXmlHandle FirstChild() const;
++ /// Return a handle to the first child node with the given name.
++ TiXmlHandle FirstChild( const char * value ) const;
++ /// Return a handle to the first child element.
++ TiXmlHandle FirstChildElement() const;
++ /// Return a handle to the first child element with the given name.
++ TiXmlHandle FirstChildElement( const char * value ) const;
++
++ /** Return a handle to the "index" child with the given name.
++ The first child is 0, the second 1, etc.
++ */
++ TiXmlHandle Child( const char* value, int index ) const;
++ /** Return a handle to the "index" child.
++ The first child is 0, the second 1, etc.
++ */
++ TiXmlHandle Child( int index ) const;
++ /** Return a handle to the "index" child element with the given name.
++ The first child element is 0, the second 1, etc. Note that only TiXmlElements
++ are indexed: other types are not counted.
++ */
++ TiXmlHandle ChildElement( const char* value, int index ) const;
++ /** Return a handle to the "index" child element.
++ The first child element is 0, the second 1, etc. Note that only TiXmlElements
++ are indexed: other types are not counted.
++ */
++ TiXmlHandle ChildElement( int index ) const;
++
++ #ifdef TIXML_USE_STL
++ TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); }
++ TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); }
++
++ TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); }
++ TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); }
++ #endif
++
++ /// Return the handle as a TiXmlNode. This may return null.
++ TiXmlNode* Node() const { return node; }
++ /// Return the handle as a TiXmlElement. This may return null.
++ TiXmlElement* Element() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
++ /// Return the handle as a TiXmlText. This may return null.
++ TiXmlText* Text() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
++ /// Return the handle as a TiXmlUnknown. This may return null;
++ TiXmlUnknown* Unknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
++
++private:
++ TiXmlNode* node;
++};
++
++
++#endif
++#endif /* SETUP */
+diff -ruN vdr-1.7.14/tinyxmlerror.c vdr-1.7.14.ExtP_NG/tinyxmlerror.c
+--- vdr-1.7.14/tinyxmlerror.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/tinyxmlerror.c 2010-04-10 15:45:11.808773087 +0200
+@@ -0,0 +1,53 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++
++// The goal of the seperate error file is to make the first
++// step towards localization. tinyxml (currently) only supports
++// latin-1, but at least the error messages could now be translated.
++//
++// It also cleans up the code a bit.
++//
++
++const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =
++{
++ "No error",
++ "Error",
++ "Failed to open file",
++ "Memory allocation failed.",
++ "Error parsing Element.",
++ "Failed to read Element name",
++ "Error reading Element value.",
++ "Error reading Attributes.",
++ "Error: empty tag.",
++ "Error reading end tag.",
++ "Error parsing Unknown.",
++ "Error parsing Comment.",
++ "Error parsing Declaration.",
++ "Error document empty.",
++ "Error null (0) or unexpected EOF found in input stream.",
++};
++#endif /* SETUP */
+diff -ruN vdr-1.7.14/tinyxmlparser.c vdr-1.7.14.ExtP_NG/tinyxmlparser.c
+--- vdr-1.7.14/tinyxmlparser.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/tinyxmlparser.c 2010-04-10 15:45:11.828740629 +0200
+@@ -0,0 +1,1494 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++#include <ctype.h>
++
++//#define DEBUG_PARSER
++
++// Note tha "PutString" hardcodes the same list. This
++// is less flexible than it appears. Changing the entries
++// or order will break putstring.
++TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] =
++{
++ { "&amp;", 5, '&' },
++ { "&lt;", 4, '<' },
++ { "&gt;", 4, '>' },
++ { "&quot;", 6, '\"' },
++ { "&apos;", 6, '\'' }
++};
++
++// Bunch of unicode info at:
++// http://www.unicode.org/faq/utf_bom.html
++// Including the basic of this table, which determines the #bytes in the
++// sequence from the lead byte. 1 placed for invalid sequences --
++// although the result will be junk, pass it through as much as possible.
++// Beware of the non-characters in UTF-8:
++// ef bb bf (Microsoft "lead bytes")
++// ef bf be
++// ef bf bf
++
++
++
++const int TiXmlBase::utf8ByteTable[256] =
++{
++ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0
++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0
++ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte
++ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
++ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte
++ 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
++};
++
++
++void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
++{
++ const unsigned long BYTE_MASK = 0xBF;
++ const unsigned long BYTE_MARK = 0x80;
++ const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
++
++ if (input < 0x80)
++ *length = 1;
++ else if ( input < 0x800 )
++ *length = 2;
++ else if ( input < 0x10000 )
++ *length = 3;
++ else if ( input < 0x200000 )
++ *length = 4;
++ else
++ { *length = 0; return; } // This code won't covert this correctly anyway.
++
++ output += *length;
++
++ // Scary scary fall throughs.
++ switch (*length)
++ {
++ case 4:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 3:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 2:
++ --output;
++ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
++ input >>= 6;
++ case 1:
++ --output;
++ *output = (char)(input | FIRST_BYTE_MARK[*length]);
++ }
++}
++
++
++/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding encoding )
++{
++ // This will only work for low-ascii, everything else is assumed to be a valid
++ // letter. I'm not sure this is the best approach, but it is quite tricky trying
++ // to figure out alhabetical vs. not across encoding. So take a very
++ // conservative approach.
++
++// if ( encoding == TIXML_ENCODING_UTF8 )
++// {
++ if ( anyByte < 127 )
++ return isalpha( anyByte );
++ else
++ return 1; // What else to do? The unicode set is huge...get the english ones right.
++// }
++// else
++// {
++// return isalpha( anyByte );
++// }
++}
++
++
++/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding )
++{
++ // This will only work for low-ascii, everything else is assumed to be a valid
++ // letter. I'm not sure this is the best approach, but it is quite tricky trying
++ // to figure out alhabetical vs. not across encoding. So take a very
++ // conservative approach.
++
++// if ( encoding == TIXML_ENCODING_UTF8 )
++// {
++ if ( anyByte < 127 )
++ return isalnum( anyByte );
++ else
++ return 1; // What else to do? The unicode set is huge...get the english ones right.
++// }
++// else
++// {
++// return isalnum( anyByte );
++// }
++}
++
++
++class TiXmlParsingData
++{
++ friend class TiXmlDocument;
++ public:
++ void Stamp( const char* now, TiXmlEncoding encoding );
++
++ const TiXmlCursor& Cursor() { return cursor; }
++
++ private:
++ // Only used by the document!
++ TiXmlParsingData( const char* start, int _tabsize, int row, int col )
++ {
++ assert( start );
++ stamp = start;
++ tabsize = _tabsize;
++ cursor.row = row;
++ cursor.col = col;
++ }
++
++ TiXmlCursor cursor;
++ const char* stamp;
++ int tabsize;
++};
++
++
++void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding )
++{
++ assert( now );
++
++ // Do nothing if the tabsize is 0.
++ if ( tabsize < 1 )
++ {
++ return;
++ }
++
++ // Get the current row, column.
++ int row = cursor.row;
++ int col = cursor.col;
++ const char* p = stamp;
++ assert( p );
++
++ while ( p < now )
++ {
++ // Code contributed by Fletcher Dunn: (modified by lee)
++ switch (*p) {
++ case 0:
++ // We *should* never get here, but in case we do, don't
++ // advance past the terminating null character, ever
++ return;
++
++ case '\r':
++ // bump down to the next line
++ ++row;
++ col = 0;
++ // Eat the character
++ ++p;
++
++ // Check for \r\n sequence, and treat this as a single character
++ if (*p == '\n') {
++ ++p;
++ }
++ break;
++
++ case '\n':
++ // bump down to the next line
++ ++row;
++ col = 0;
++
++ // Eat the character
++ ++p;
++
++ // Check for \n\r sequence, and treat this as a single
++ // character. (Yes, this bizarre thing does occur still
++ // on some arcane platforms...)
++ if (*p == '\r') {
++ ++p;
++ }
++ break;
++
++ case '\t':
++ // Eat the character
++ ++p;
++
++ // Skip to next tab stop
++ col = (col / tabsize + 1) * tabsize;
++ break;
++
++ case (char)(0xef):
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ if ( *(p+1) && *(p+2) )
++ {
++ // In these cases, don't advance the column. These are
++ // 0-width spaces.
++ if ( *(p+1)==(char)(0xbb) && *(p+2)==(char)(0xbf) )
++ p += 3;
++ else if ( *(p+1)==(char)(0xbf) && *(p+2)==(char)(0xbe) )
++ p += 3;
++ else if ( *(p+1)==(char)(0xbf) && *(p+2)==(char)(0xbf) )
++ p += 3;
++ else
++ { p +=3; ++col; } // A normal character.
++ }
++ }
++ else
++ {
++ ++p;
++ ++col;
++ }
++ break;
++
++ default:
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ // Eat the 1 to 4 byte utf8 character.
++ int step = TiXmlBase::utf8ByteTable[*((unsigned char*)p)];
++ if ( step == 0 )
++ step = 1; // Error case from bad encoding, but handle gracefully.
++ p += step;
++
++ // Just advance one column, of course.
++ ++col;
++ }
++ else
++ {
++ ++p;
++ ++col;
++ }
++ break;
++ }
++ }
++ cursor.row = row;
++ cursor.col = col;
++ assert( cursor.row >= -1 );
++ assert( cursor.col >= -1 );
++ stamp = p;
++ assert( stamp );
++}
++
++
++const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
++{
++ if ( !p || !*p )
++ {
++ return 0;
++ }
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ while ( *p )
++ {
++ // Skip the stupid Microsoft UTF-8 Byte order marks
++ if ( *(p+0)==(char) 0xef
++ && *(p+1)==(char) 0xbb
++ && *(p+2)==(char) 0xbf )
++ {
++ p += 3;
++ continue;
++ }
++ else if(*(p+0)==(char) 0xef
++ && *(p+1)==(char) 0xbf
++ && *(p+2)==(char) 0xbe )
++ {
++ p += 3;
++ continue;
++ }
++ else if(*(p+0)==(char) 0xef
++ && *(p+1)==(char) 0xbf
++ && *(p+2)==(char) 0xbf )
++ {
++ p += 3;
++ continue;
++ }
++
++ if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space.
++ ++p;
++ else
++ break;
++ }
++ }
++ else
++ {
++ while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' )
++ ++p;
++ }
++
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++/*static*/ bool TiXmlBase::StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ for( ;; )
++ {
++ if ( !in->good() ) return false;
++
++ int c = in->peek();
++ // At this scope, we can't get to a document. So fail silently.
++ if ( !IsWhiteSpace( c ) || c <= 0 )
++ return true;
++
++ *tag += (char) in->get();
++ }
++}
++
++/*static*/ bool TiXmlBase::StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag )
++{
++ //assert( character > 0 && character < 128 ); // else it won't work in utf-8
++ while ( in->good() )
++ {
++ int c = in->peek();
++ if ( c == character )
++ return true;
++ if ( c <= 0 ) // Silent failure: can't get document at this scope
++ return false;
++
++ in->get();
++ *tag += (char) c;
++ }
++ return false;
++}
++#endif
++
++const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )
++{
++ *name = "";
++ assert( p );
++
++ // Names start with letters or underscores.
++ // Of course, in unicode, tinyxml has no idea what a letter *is*. The
++ // algorithm is generous.
++ //
++ // After that, they can be letters, underscores, numbers,
++ // hyphens, or colons. (Colons are valid ony for namespaces,
++ // but tinyxml can't tell namespaces from names.)
++ if ( p && *p
++ && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )
++ {
++ while( p && *p
++ && ( IsAlphaNum( (unsigned char ) *p, encoding )
++ || *p == '_'
++ || *p == '-'
++ || *p == '.'
++ || *p == ':' ) )
++ {
++ (*name) += *p;
++ ++p;
++ }
++ return p;
++ }
++ return 0;
++}
++
++const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding )
++{
++ // Presume an entity, and pull it out.
++ TIXML_STRING ent;
++ int i;
++ *length = 0;
++
++ if ( *(p+1) && *(p+1) == '#' && *(p+2) )
++ {
++ unsigned long ucs = 0;
++ unsigned delta = 0;
++ unsigned mult = 1;
++
++ if ( *(p+2) == 'x' )
++ {
++ // Hexadecimal.
++ if ( !*(p+3) ) return 0;
++
++ const char* q = p+3;
++ q = strchr( q, ';' );
++
++ if ( !q || !*q ) return 0;
++
++ delta = q-p;
++ --q;
++
++ while ( *q != 'x' )
++ {
++ if ( *q >= '0' && *q <= '9' )
++ ucs += mult * (*q - '0');
++ else if ( *q >= 'a' && *q <= 'f' )
++ ucs += mult * (*q - 'a' + 10);
++ else if ( *q >= 'A' && *q <= 'F' )
++ ucs += mult * (*q - 'A' + 10 );
++ else
++ return 0;
++ mult *= 16;
++ --q;
++ }
++ }
++ else
++ {
++ // Decimal.
++ if ( !*(p+2) ) return 0;
++
++ const char* q = p+2;
++ q = strchr( q, ';' );
++
++ if ( !q || !*q ) return 0;
++
++ delta = q-p;
++ --q;
++
++ while ( *q != '#' )
++ {
++ if ( *q >= '0' && *q <= '9' )
++ ucs += mult * (*q - '0');
++ else
++ return 0;
++ mult *= 10;
++ --q;
++ }
++ }
++ if ( encoding == TIXML_ENCODING_UTF8 )
++ {
++ // convert the UCS to UTF-8
++ ConvertUTF32ToUTF8( ucs, value, length );
++ }
++ else
++ {
++ *value = (char)ucs;
++ *length = 1;
++ }
++ return p + delta + 1;
++ }
++
++ // Now try to match it.
++ for( i=0; i<NUM_ENTITY; ++i )
++ {
++ if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )
++ {
++ assert( strlen( entity[i].str ) == entity[i].strLength );
++ *value = entity[i].chr;
++ *length = 1;
++ return ( p + entity[i].strLength );
++ }
++ }
++
++ // So it wasn't an entity, its unrecognized, or something like that.
++ *value = *p; // Don't put back the last one, since we return it!
++ return p+1;
++}
++
++
++bool TiXmlBase::StringEqual( const char* p,
++ const char* tag,
++ bool ignoreCase,
++ TiXmlEncoding encoding )
++{
++ assert( p );
++ assert( tag );
++ if ( !p || !*p )
++ {
++ assert( 0 );
++ return false;
++ }
++
++ const char* q = p;
++
++ if ( ignoreCase )
++ {
++ while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
++ {
++ ++q;
++ ++tag;
++ }
++
++ if ( *tag == 0 )
++ return true;
++ }
++ else
++ {
++ while ( *q && *tag && *q == *tag )
++ {
++ ++q;
++ ++tag;
++ }
++
++ if ( *tag == 0 ) // Have we found the end of the tag, and everything equal?
++ return true;
++ }
++ return false;
++}
++
++const char* TiXmlBase::ReadText( const char* p,
++ TIXML_STRING * text,
++ bool trimWhiteSpace,
++ const char* endTag,
++ bool caseInsensitive,
++ TiXmlEncoding encoding )
++{
++ *text = "";
++ if ( !trimWhiteSpace // certain tags always keep whitespace
++ || !condenseWhiteSpace ) // if true, whitespace is always kept
++ {
++ // Keep all the white space.
++ while ( p && *p
++ && !StringEqual( p, endTag, caseInsensitive, encoding )
++ )
++ {
++ int len;
++ char cArr[4] = { 0, 0, 0, 0 };
++ p = GetChar( p, cArr, &len, encoding );
++ text->append( cArr, len );
++ }
++ }
++ else
++ {
++ bool whitespace = false;
++
++ // Remove leading white space:
++ p = SkipWhiteSpace( p, encoding );
++ while ( p && *p
++ && !StringEqual( p, endTag, caseInsensitive, encoding ) )
++ {
++ if ( *p == '\r' || *p == '\n' )
++ {
++ whitespace = true;
++ ++p;
++ }
++ else if ( IsWhiteSpace( *p ) )
++ {
++ whitespace = true;
++ ++p;
++ }
++ else
++ {
++ // If we've found whitespace, add it before the
++ // new character. Any whitespace just becomes a space.
++ if ( whitespace )
++ {
++ (*text) += ' ';
++ whitespace = false;
++ }
++ int len;
++ char cArr[4] = { 0, 0, 0, 0 };
++ p = GetChar( p, cArr, &len, encoding );
++ if ( len == 1 )
++ (*text) += cArr[0]; // more efficient
++ else
++ text->append( cArr, len );
++ }
++ }
++ }
++ return p + strlen( endTag );
++}
++
++#ifdef TIXML_USE_STL
++
++void TiXmlDocument::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ // The basic issue with a document is that we don't know what we're
++ // streaming. Read something presumed to be a tag (and hope), then
++ // identify it, and call the appropriate stream method on the tag.
++ //
++ // This "pre-streaming" will never read the closing ">" so the
++ // sub-tag can orient itself.
++
++ if ( !StreamTo( in, '<', tag ) )
++ {
++ SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ while ( in->good() )
++ {
++ int tagIndex = (int) tag->length();
++ while ( in->good() && in->peek() != '>' )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ break;
++ }
++ (*tag) += (char) c;
++ }
++
++ if ( in->good() )
++ {
++ // We now have something we presume to be a node of
++ // some sort. Identify it, and call the node to
++ // continue streaming.
++ TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );
++
++ if ( node )
++ {
++ node->StreamIn( in, tag );
++ bool isElement = node->ToElement() != 0;
++ delete node;
++ node = 0;
++
++ // If this is the root element, we're done. Parsing will be
++ // done by the >> operator.
++ if ( isElement )
++ {
++ return;
++ }
++ }
++ else
++ {
++ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ }
++ }
++ // We should have returned sooner.
++ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
++}
++
++#endif
++
++const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )
++{
++ ClearError();
++
++ // Parse away, at the document level. Since a document
++ // contains nothing but other tags, most of what happens
++ // here is skipping white space.
++ if ( !p || !*p )
++ {
++ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ // Note that, for a document, this needs to come
++ // before the while space skip, so that parsing
++ // starts from the pointer we are given.
++ location.Clear();
++ if ( prevData )
++ {
++ location.row = prevData->cursor.row;
++ location.col = prevData->cursor.col;
++ }
++ else
++ {
++ location.row = 0;
++ location.col = 0;
++ }
++ TiXmlParsingData data( p, TabSize(), location.row, location.col );
++ location = data.Cursor();
++
++ if ( encoding == TIXML_ENCODING_UNKNOWN )
++ {
++ // Check for the Microsoft UTF-8 lead bytes.
++ if ( *(p+0) && *(p+0) == (char)(0xef)
++ && *(p+1) && *(p+1) == (char)(0xbb)
++ && *(p+2) && *(p+2) == (char)(0xbf) )
++ {
++ encoding = TIXML_ENCODING_UTF8;
++ }
++ }
++
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p )
++ {
++ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return 0;
++ }
++
++ while ( p && *p )
++ {
++ TiXmlNode* node = Identify( p, encoding );
++ if ( node )
++ {
++ p = node->Parse( p, &data, encoding );
++ LinkEndChild( node );
++ }
++ else
++ {
++ break;
++ }
++
++ // Did we get encoding info?
++ if ( encoding == TIXML_ENCODING_UNKNOWN
++ && node->ToDeclaration() )
++ {
++ TiXmlDeclaration* dec = node->ToDeclaration();
++ const char* enc = dec->Encoding();
++ assert( enc );
++
++ if ( *enc == 0 )
++ encoding = TIXML_ENCODING_UTF8;
++ else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
++ encoding = TIXML_ENCODING_UTF8;
++ else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
++ encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
++ else
++ encoding = TIXML_ENCODING_LEGACY;
++ }
++
++ p = SkipWhiteSpace( p, encoding );
++ }
++
++ // All is well.
++ return p;
++}
++
++void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ // The first error in a chain is more accurate - don't set again!
++ if ( error )
++ return;
++
++ assert( err > 0 && err < TIXML_ERROR_STRING_COUNT );
++ error = true;
++ errorId = err;
++ errorDesc = errorString[ errorId ];
++
++ errorLocation.Clear();
++ if ( pError && data )
++ {
++ //TiXmlParsingData data( pError, prevData );
++ data->Stamp( pError, encoding );
++ errorLocation = data->Cursor();
++ }
++}
++
++
++TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
++{
++ TiXmlNode* returnNode = 0;
++
++ p = SkipWhiteSpace( p, encoding );
++ if( !p || !*p || *p != '<' )
++ {
++ return 0;
++ }
++
++ TiXmlDocument* doc = GetDocument();
++ p = SkipWhiteSpace( p, encoding );
++
++ if ( !p || !*p )
++ {
++ return 0;
++ }
++
++ // What is this thing?
++ // - Elements start with a letter or underscore, but xml is reserved.
++ // - Comments: <!--
++ // - Decleration: <?xml
++ // - Everthing else is unknown to tinyxml.
++ //
++
++ const char* xmlHeader = { "<?xml" };
++ const char* commentHeader = { "<!--" };
++ const char* dtdHeader = { "<!" };
++
++ if ( StringEqual( p, xmlHeader, true, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Declaration\n" );
++ #endif
++ returnNode = new TiXmlDeclaration();
++ }
++ else if ( StringEqual( p, commentHeader, false, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Comment\n" );
++ #endif
++ returnNode = new TiXmlComment();
++ }
++ else if ( StringEqual( p, dtdHeader, false, encoding ) )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Unknown(1)\n" );
++ #endif
++ returnNode = new TiXmlUnknown();
++ }
++ else if ( IsAlpha( *(p+1), encoding )
++ || *(p+1) == '_' )
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Element\n" );
++ #endif
++ returnNode = new TiXmlElement( "" );
++ }
++ else
++ {
++ #ifdef DEBUG_PARSER
++ TIXML_LOG( "XML parsing Unknown(2)\n" );
++ #endif
++ returnNode = new TiXmlUnknown();
++ }
++
++ if ( returnNode )
++ {
++ // Set the parent, so it can report errors
++ returnNode->parent = this;
++ }
++ else
++ {
++ if ( doc )
++ doc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
++ }
++ return returnNode;
++}
++
++#ifdef TIXML_USE_STL
++
++void TiXmlElement::StreamIn (TIXML_ISTREAM * in, TIXML_STRING * tag)
++{
++ // We're called with some amount of pre-parsing. That is, some of "this"
++ // element is in "tag". Go ahead and stream to the closing ">"
++ while( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c ;
++
++ if ( c == '>' )
++ break;
++ }
++
++ if ( tag->length() < 3 ) return;
++
++ // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
++ // If not, identify and stream.
++
++ if ( tag->at( tag->length() - 1 ) == '>'
++ && tag->at( tag->length() - 2 ) == '/' )
++ {
++ // All good!
++ return;
++ }
++ else if ( tag->at( tag->length() - 1 ) == '>' )
++ {
++ // There is more. Could be:
++ // text
++ // closing tag
++ // another node.
++ for ( ;; )
++ {
++ StreamWhiteSpace( in, tag );
++
++ // Do we have text?
++ if ( in->good() && in->peek() != '<' )
++ {
++ // Yep, text.
++ TiXmlText text( "" );
++ text.StreamIn( in, tag );
++
++ // What follows text is a closing tag or another node.
++ // Go around again and figure it out.
++ continue;
++ }
++
++ // We now have either a closing tag...or another node.
++ // We should be at a "<", regardless.
++ if ( !in->good() ) return;
++ assert( in->peek() == '<' );
++ int tagIndex = tag->length();
++
++ bool closingTag = false;
++ bool firstCharFound = false;
++
++ for( ;; )
++ {
++ if ( !in->good() )
++ return;
++
++ int c = in->peek();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ if ( c == '>' )
++ break;
++
++ *tag += (char) c;
++ in->get();
++
++ if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )
++ {
++ firstCharFound = true;
++ if ( c == '/' )
++ closingTag = true;
++ }
++ }
++ // If it was a closing tag, then read in the closing '>' to clean up the input stream.
++ // If it was not, the streaming will be done by the tag.
++ if ( closingTag )
++ {
++ if ( !in->good() )
++ return;
++
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ assert( c == '>' );
++ *tag += (char) c;
++
++ // We are done, once we've found our closing tag.
++ return;
++ }
++ else
++ {
++ // If not a closing tag, id it, and stream.
++ const char* tagloc = tag->c_str() + tagIndex;
++ TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );
++ if ( !node )
++ return;
++ node->StreamIn( in, tag );
++ delete node;
++ node = 0;
++
++ // No return: go around from the beginning: text, closing tag, or node.
++ }
++ }
++ }
++}
++#endif
++
++const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ p = SkipWhiteSpace( p, encoding );
++ TiXmlDocument* document = GetDocument();
++
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
++ return 0;
++ }
++
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++
++ if ( *p != '<' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
++ return 0;
++ }
++
++ p = SkipWhiteSpace( p+1, encoding );
++
++ // Read the name.
++ const char* pErr = p;
++
++ p = ReadName( p, &value, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
++ return 0;
++ }
++
++ TIXML_STRING endTag ("</");
++ endTag += value;
++ endTag += ">";
++
++ // Check for and read attributes. Also look for an empty
++ // tag or an end tag.
++ while ( p && *p )
++ {
++ pErr = p;
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
++ return 0;
++ }
++ if ( *p == '/' )
++ {
++ ++p;
++ // Empty tag.
++ if ( *p != '>' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
++ return 0;
++ }
++ return (p+1);
++ }
++ else if ( *p == '>' )
++ {
++ // Done with attributes (if there were any.)
++ // Read the value -- which can include other
++ // elements -- read the end tag, and return.
++ ++p;
++ p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens.
++ if ( !p || !*p )
++ return 0;
++
++ // We should find the end tag now
++ if ( StringEqual( p, endTag.c_str(), false, encoding ) )
++ {
++ p += endTag.length();
++ return p;
++ }
++ else
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
++ return 0;
++ }
++ }
++ else
++ {
++ // Try to read an attribute:
++ TiXmlAttribute* attrib = new TiXmlAttribute();
++ if ( !attrib )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding );
++ return 0;
++ }
++
++ attrib->SetDocument( document );
++ const char* pErr = p;
++ p = attrib->Parse( p, data, encoding );
++
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
++ delete attrib;
++ return 0;
++ }
++
++ // Handle the strange case of double attributes:
++ TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
++ if ( node )
++ {
++ node->SetValue( attrib->Value() );
++ delete attrib;
++ return 0;
++ }
++
++ attributeSet.Add( attrib );
++ }
++ }
++ return p;
++}
++
++
++const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++
++ const char* pWithWhiteSpace = p;
++ // Read in text and elements in any order.
++ p = SkipWhiteSpace( p, encoding );
++ while ( p && *p )
++ {
++ if ( *p != '<' )
++ {
++ // Take what we have, make a text element.
++ TiXmlText* textNode = new TiXmlText( "" );
++
++ if ( !textNode )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding );
++ return 0;
++ }
++
++ if ( TiXmlBase::IsWhiteSpaceCondensed() )
++ {
++ p = textNode->Parse( p, data, encoding );
++ }
++ else
++ {
++ // Special case: we want to keep the white space
++ // so that leading spaces aren't removed.
++ p = textNode->Parse( pWithWhiteSpace, data, encoding );
++ }
++
++ if ( !textNode->Blank() )
++ LinkEndChild( textNode );
++ else
++ delete textNode;
++ }
++ else
++ {
++ // We hit a '<'
++ // Have we hit a new element or an end tag?
++ if ( StringEqual( p, "</", false, encoding ) )
++ {
++ return p;
++ }
++ else
++ {
++ TiXmlNode* node = Identify( p, encoding );
++ if ( node )
++ {
++ p = node->Parse( p, data, encoding );
++ LinkEndChild( node );
++ }
++ else
++ {
++ return 0;
++ }
++ }
++ }
++ p = SkipWhiteSpace( p, encoding );
++ }
++
++ if ( !p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );
++ }
++ return p;
++}
++
++
++#ifdef TIXML_USE_STL
++void TiXmlUnknown::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c;
++
++ if ( c == '>' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++
++const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++ p = SkipWhiteSpace( p, encoding );
++
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ if ( !p || !*p || *p != '<' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );
++ return 0;
++ }
++ ++p;
++ value = "";
++
++ while ( p && *p && *p != '>' )
++ {
++ value += *p;
++ ++p;
++ }
++
++ if ( !p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
++ }
++ if ( *p == '>' )
++ return p+1;
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlComment::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ (*tag) += (char) c;
++
++ if ( c == '>'
++ && tag->at( tag->length() - 2 ) == '-'
++ && tag->at( tag->length() - 3 ) == '-' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++
++const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ TiXmlDocument* document = GetDocument();
++ value = "";
++
++ p = SkipWhiteSpace( p, encoding );
++
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ const char* startTag = "<!--";
++ const char* endTag = "-->";
++
++ if ( !StringEqual( p, startTag, false, encoding ) )
++ {
++ document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );
++ return 0;
++ }
++ p += strlen( startTag );
++ p = ReadText( p, &value, false, endTag, false, encoding );
++ return p;
++}
++
++
++const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p ) return 0;
++
++ int tabsize = 4;
++ if ( document )
++ tabsize = document->TabSize();
++
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ // Read the name, the '=' and the value.
++ const char* pErr = p;
++ p = ReadName( p, &name, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
++ return 0;
++ }
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p || *p != '=' )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++ return 0;
++ }
++
++ ++p; // skip '='
++ p = SkipWhiteSpace( p, encoding );
++ if ( !p || !*p )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++ return 0;
++ }
++
++ const char* end;
++
++ if ( *p == '\'' )
++ {
++ ++p;
++ end = "\'";
++ p = ReadText( p, &value, false, end, false, encoding );
++ }
++ else if ( *p == '"' )
++ {
++ ++p;
++ end = "\"";
++ p = ReadText( p, &value, false, end, false, encoding );
++ }
++ else
++ {
++ // All attribute values should be in single or double quotes.
++ // But this is such a common error that the parser will try
++ // its best, even without them.
++ value = "";
++ while ( p && *p // existence
++ && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace
++ && *p != '/' && *p != '>' ) // tag end
++ {
++ value += *p;
++ ++p;
++ }
++ }
++ return p;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlText::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->peek();
++ if ( c == '<' )
++ return;
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++
++ (*tag) += (char) c;
++ in->get();
++ }
++}
++#endif
++
++const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++ value = "";
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, encoding );
++ location = data->Cursor();
++ }
++ bool ignoreWhite = true;
++
++ const char* end = "<";
++ p = ReadText( p, &value, ignoreWhite, end, false, encoding );
++ if ( p )
++ return p-1; // don't truncate the '<'
++ return 0;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlDeclaration::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++ while ( in->good() )
++ {
++ int c = in->get();
++ if ( c <= 0 )
++ {
++ TiXmlDocument* document = GetDocument();
++ if ( document )
++ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++ return;
++ }
++ (*tag) += (char) c;
++
++ if ( c == '>' )
++ {
++ // All is well.
++ return;
++ }
++ }
++}
++#endif
++
++const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding )
++{
++ p = SkipWhiteSpace( p, _encoding );
++ // Find the beginning, find the end, and look for
++ // the stuff in-between.
++ TiXmlDocument* document = GetDocument();
++ if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) )
++ {
++ if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );
++ return 0;
++ }
++// TiXmlParsingData data( p, prevData );
++ if ( data )
++ {
++ data->Stamp( p, _encoding );
++ location = data->Cursor();
++ }
++ p += 5;
++
++ version = "";
++ encoding = "";
++ standalone = "";
++
++ while ( p && *p )
++ {
++ if ( *p == '>' )
++ {
++ ++p;
++ return p;
++ }
++
++ p = SkipWhiteSpace( p, _encoding );
++ if ( StringEqual( p, "version", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ version = attrib.Value();
++ }
++ else if ( StringEqual( p, "encoding", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ encoding = attrib.Value();
++ }
++ else if ( StringEqual( p, "standalone", true, _encoding ) )
++ {
++ TiXmlAttribute attrib;
++ p = attrib.Parse( p, data, _encoding );
++ standalone = attrib.Value();
++ }
++ else
++ {
++ // Read over whatever it is.
++ while( p && *p && *p != '>' && !IsWhiteSpace( *p ) )
++ ++p;
++ }
++ }
++ return 0;
++}
++
++bool TiXmlText::Blank() const
++{
++ for ( unsigned i=0; i<value.length(); i++ )
++ if ( !IsWhiteSpace( value[i] ) )
++ return false;
++ return true;
++}
++#endif /* SETUP */
+diff -ruN vdr-1.7.14/vdr.c vdr-1.7.14.ExtP_NG/vdr.c
+--- vdr-1.7.14/vdr.c 2010-02-21 15:08:09.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/vdr.c 2010-04-10 15:45:12.079739794 +0200
+@@ -587,7 +587,12 @@
+ RecordingCommands.Load(AddDirectory(ConfigDirectory, "reccmds.conf"));
+ SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true);
+ Keys.Load(AddDirectory(ConfigDirectory, "remote.conf"));
++#ifdef USE_ALTERNATECHANNEL
++ KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true) &&
++ Channels.LoadAlternativeChannels(AddDirectory(ConfigDirectory, "channel_alternative.conf"));
++#else
+ KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true);
++#endif /* ALTERNATECHANNEL */
+ Folders.Load(AddDirectory(ConfigDirectory, "folders.conf"));
+
+ if (!*cFont::GetFontFileName(Setup.FontOsd)) {
+@@ -919,6 +924,21 @@
+ Recordings.Update();
+ DeletedRecordings.Update();
+ }
++#ifdef USE_MCLI
++ cPlugin *mcliPlugin = cPluginManager::GetPlugin("mcli");
++ if (mcliPlugin) {
++ if (!ShutdownHandler.countdown) { // if kPower has been pressed, cMenuShutdown takes precedence over other menus
++ cOsdObject *MyMenu = mcliPlugin->AltMenuAction();
++ if (MyMenu) { // is there any cam-menu waiting?
++ DELETE_MENU;
++ if (cControl::Control())
++ cControl::Control()->Hide();
++ Menu = MyMenu;
++ Menu->Show();
++ }
++ }
++ }
++#endif /* MCLI */
+ // CAM control:
+ if (!Menu && !cOsd::IsOpen())
+ Menu = CamControl();
+@@ -929,6 +949,9 @@
+ cOsdObject *Interact = Menu ? Menu : cControl::Control();
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
+ if (ISREALKEY(key)) {
++#ifdef USE_PINPLUGIN
++ cStatus::MsgUserAction(key, Interact);
++#endif /* PINPLUGIN */
+ EITScanner.Activity();
+ // Cancel shutdown countdown:
+ if (ShutdownHandler.countdown)
+@@ -1001,10 +1024,16 @@
+ cControl::Control()->Hide();
+ cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+ if (plugin) {
++#ifdef USE_PINPLUGIN
++ if (!cStatus::MsgPluginProtected(plugin)) {
++#endif /* PINPLUGIN */
+ Menu = plugin->MainMenuAction();
+ if (Menu)
+ Menu->Show();
+ }
++#ifdef USE_PINPLUGIN
++ }
++#endif /* PINPLUGIN */
+ else
+ esyslog("ERROR: unknown plugin '%s'", PluginName);
+ }
+@@ -1194,13 +1223,26 @@
+ Channels.SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]);
+ break;
+ }
++#ifdef USE_VOLCTRL
++ // Left/Right volume control
++#else
+ // Direct Channel Select:
+ case k1 ... k9:
+ // Left/Right rotates through channel groups:
++#endif /* VOLCTRL */
+ case kLeft|k_Repeat:
+ case kLeft:
+ case kRight|k_Repeat:
+ case kRight:
++#ifdef USE_VOLCTRL
++ if (Setup.LRVolumeControl && Setup.LRChannelGroups < 2) {
++ cRemote::Put(NORMALKEY(key) == kLeft ? kVolDn : kVolUp, true);
++ break;
++ }
++ // else fall through
++ // Direct Channel Select:
++ case k1 ... k9:
++#endif /* VOLCTRL */
+ // Previous/Next rotates through channel groups:
+ case kPrev|k_Repeat:
+ case kPrev:
+@@ -1218,9 +1260,15 @@
+ // Instant resume of the last viewed recording:
+ case kPlay:
+ if (cReplayControl::LastReplayed()) {
++#ifdef USE_PINPLUGIN
++ if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) {
++#endif /* PINPLUGIN */
+ cControl::Shutdown();
+ cControl::Launch(new cReplayControl);
+ }
++#ifdef USE_PINPLUGIN
++ }
++#endif /* PINPLUGIN */
+ break;
+ default: break;
+ }
+diff -ruN vdr-1.7.14/vdrttxtsubshooks.c vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.c
+--- vdr-1.7.14/vdrttxtsubshooks.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.c 2010-04-10 15:45:12.093736132 +0200
+@@ -0,0 +1,62 @@
++/*
++ * vdr-ttxtsubs - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2003 - 2008 Ragnar Sundblad <ragge@nada.kth.se>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your option)
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
++ * details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <stdint.h>
++
++#include "vdrttxtsubshooks.h"
++
++// XXX Really should be a list...
++static cVDRTtxtsubsHookListener *gListener;
++
++// ------ class cVDRTtxtsubsHookProxy ------
++
++class cVDRTtxtsubsHookProxy : public cVDRTtxtsubsHookListener
++{
++ public:
++ virtual void HideOSD(void) { if(gListener) gListener->HideOSD(); };
++ virtual void ShowOSD(void) { if(gListener) gListener->ShowOSD(); };
++ virtual void PlayerTeletextData(uint8_t *p, int length, bool IsPesRecording, const struct tTeletextSubtitlePage teletextSubtitlePages[] = NULL, int pageCount = 0)
++ { if(gListener) gListener->PlayerTeletextData(p, length, IsPesRecording, teletextSubtitlePages, pageCount); };
++ virtual int ManualPageNumber(const cChannel *channel)
++ { if(gListener) return gListener->ManualPageNumber(channel); else return 0; };
++};
++
++
++// ------ class cVDRTtxtsubsHookListener ------
++
++cVDRTtxtsubsHookListener::~cVDRTtxtsubsHookListener()
++{
++ gListener = 0;
++}
++
++void cVDRTtxtsubsHookListener::HookAttach(void)
++{
++ gListener = this;
++ //printf("cVDRTtxtsubsHookListener::HookAttach\n");
++}
++
++static cVDRTtxtsubsHookProxy gProxy;
++
++cVDRTtxtsubsHookListener *cVDRTtxtsubsHookListener::Hook(void)
++{
++ return &gProxy;
++}
+diff -ruN vdr-1.7.14/vdrttxtsubshooks.h vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.h
+--- vdr-1.7.14/vdrttxtsubshooks.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.h 2010-04-10 15:45:12.108759292 +0200
+@@ -0,0 +1,46 @@
++/*
++ * vdr-ttxtsubs - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2003 - 2008 Ragnar Sundblad <ragge@nada.kth.se>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your option)
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
++ * details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#ifndef __VDRTTXTSUBSHOOKS_H
++#define __VDRTTXTSUBSHOOKS_H
++
++#define TTXTSUBSVERSNUM 2
++
++class cDevice;
++class cChannel;
++struct tTeletextSubtitlePage;
++
++class cVDRTtxtsubsHookListener {
++ public:
++ cVDRTtxtsubsHookListener(void) {};
++ virtual ~cVDRTtxtsubsHookListener();
++
++ void HookAttach(void);
++
++ virtual void HideOSD(void) {};
++ virtual void ShowOSD(void) {};
++ virtual void PlayerTeletextData(uint8_t *p, int length, bool IsPesRecording = true, const struct tTeletextSubtitlePage teletextSubtitlePages[] = NULL, int pageCount = 0) {};
++ virtual int ManualPageNumber(const cChannel *channel) { return 0; };
++
++ // used by VDR to call hook listeners
++ static cVDRTtxtsubsHookListener *Hook(void);
++};
++
++#endif
+diff -ruN vdr-1.7.14/videodir.c vdr-1.7.14.ExtP_NG/videodir.c
+--- vdr-1.7.14/videodir.c 2008-02-16 14:00:03.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/videodir.c 2010-04-10 15:45:12.125736672 +0200
+@@ -36,6 +36,11 @@
+ bool Next(void);
+ void Store(void);
+ const char *Adjust(const char *FileName);
++#ifdef USE_DVLVIDPREFER
++ char *GetVidPath(int nVid);
++ bool GetPreferedVideoDir(void);
++ bool IsVidDirOK(int nVid, int *freeMB = NULL);
++#endif /* DVLVIDPREFER */
+ };
+
+ cVideoDirectory::cVideoDirectory(void)
+@@ -117,6 +122,9 @@
+ if ((Flags & O_CREAT) != 0) {
+ cVideoDirectory Dir;
+ if (Dir.IsDistributed()) {
++#ifdef USE_DVLVIDPREFER
++ if (Setup.UseVidPrefer == 0) {
++#endif /* DVLVIDPREFER */
+ // Find the directory with the most free space:
+ int MaxFree = Dir.FreeMB();
+ while (Dir.Next()) {
+@@ -126,14 +134,24 @@
+ MaxFree = Free;
+ }
+ }
++#ifdef USE_DVLVIDPREFER
++ }
++ else Dir.GetPreferedVideoDir();
++#endif /* DVLVIDPREFER */
+ if (Dir.Stored()) {
+ ActualFileName = Dir.Adjust(FileName);
+ if (!MakeDirs(ActualFileName, false))
+ return NULL; // errno has been set by MakeDirs()
++#ifdef USE_DVLVIDPREFER
++ if (strcmp(ActualFileName, FileName) != 0) {
++#endif /* DVLVIDPREFER */
+ if (symlink(ActualFileName, FileName) < 0) {
+ LOG_ERROR_STR(FileName);
+ return NULL;
+ }
++#ifdef USE_DVLVIDPREFER
++ }
++#endif /* DVLVIDPREFER */
+ ActualFileName = strdup(ActualFileName); // must survive Dir!
+ }
+ }
+@@ -168,6 +186,123 @@
+ return RemoveFileOrDir(FileName, true);
+ }
+
++#ifdef USE_HARDLINKCUTTER
++static bool StatNearestDir(const char *FileName, struct stat *Stat)
++{
++ cString Name(FileName);
++ char *p;
++ while ((p = strrchr((char*)(const char*)Name + 1, '/')) != NULL) {
++ *p = 0; // truncate at last '/'
++ if (stat(Name, Stat) == 0) {
++ isyslog("StatNearestDir: Stating %s", (const char*)Name);
++ return true;
++ }
++ }
++ return false;
++}
++
++bool HardLinkVideoFile(const char *OldName, const char *NewName)
++{
++ // Incoming name must be in base video directory:
++ if (strstr(OldName, VideoDirectory) != OldName) {
++ esyslog("ERROR: %s not in %s", OldName, VideoDirectory);
++ return false;
++ }
++ if (strstr(NewName, VideoDirectory) != NewName) {
++ esyslog("ERROR: %s not in %s", NewName, VideoDirectory);
++ return false;
++ }
++
++ const char *ActualNewName = NewName;
++ cString ActualOldName(ReadLink(OldName), true);
++
++ // Some safety checks:
++ struct stat StatOldName;
++ if (lstat(ActualOldName, &StatOldName) == 0) {
++ if (S_ISLNK(StatOldName.st_mode)) {
++ esyslog("HardLinkVideoFile: Failed to resolve symbolic link %s", (const char*)ActualOldName);
++ return false;
++ }
++ }
++ else {
++ esyslog("HardLinkVideoFile: lstat failed on %s", (const char*)ActualOldName);
++ return false;
++ }
++ isyslog("HardLinkVideoFile: %s is on %i", (const char*)ActualOldName, (int)StatOldName.st_dev);
++
++ // Find the video directory where ActualOldName is located
++
++ cVideoDirectory Dir;
++ struct stat StatDir;
++ if (!StatNearestDir(NewName, &StatDir)) {
++ esyslog("HardLinkVideoFile: stat failed on %s", NewName);
++ return false;
++ }
++
++ isyslog("HardLinkVideoFile: %s is on %i", NewName, (int)StatDir.st_dev);
++ if (StatDir.st_dev != StatOldName.st_dev) {
++ // Not yet found.
++
++ if (!Dir.IsDistributed()) {
++ esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
++ return false;
++ }
++
++ // Search in video01 and upwards
++ bool found = false;
++ while (Dir.Next()) {
++ Dir.Store();
++ const char *TmpNewName = Dir.Adjust(NewName);
++ if (StatNearestDir(TmpNewName, &StatDir) && StatDir.st_dev == StatOldName.st_dev) {
++ isyslog("HardLinkVideoFile: %s is on %i (match)", TmpNewName, (int)StatDir.st_dev);
++ ActualNewName = TmpNewName;
++ found = true;
++ break;
++ }
++ isyslog("HardLinkVideoFile: %s is on %i", TmpNewName, (int)StatDir.st_dev);
++ }
++ if (ActualNewName == NewName) {
++ esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
++ return false;
++ }
++
++ // Looking good, we have a match. Create necessary folders.
++ if (!MakeDirs(ActualNewName, false))
++ return false;
++ // There's no guarantee that the directory of ActualNewName
++ // is on the same device as the dir that StatNearestDir found.
++ // But worst case is that the link fails.
++ }
++
++#ifdef HARDLINK_TEST_ONLY
++ // Do the hard link to *.vdr_ for testing only
++ char *name = NULL;
++ asprintf(&name, "%s_",ActualNewName);
++ link(ActualOldName, name);
++ free(name);
++ return false;
++#endif // HARDLINK_TEST_ONLY
++
++ // Try creating the hard link
++ if (link(ActualOldName, ActualNewName) != 0) {
++ // Failed to hard link. Maybe not allowed on file system.
++ LOG_ERROR_STR(ActualNewName);
++ isyslog("HardLinkVideoFile: failed to hard link from %s to %s", (const char*)ActualOldName, ActualNewName);
++ return false;
++ }
++
++ if (ActualNewName != NewName) {
++ // video01 and up. Do the remaining symlink
++ if (symlink(ActualNewName, NewName) < 0) {
++ LOG_ERROR_STR(NewName);
++ return false;
++ }
++ }
++ return true;
++}
++
++#endif /* HARDLINKCUTTER */
++
+ bool VideoFileSpaceAvailable(int SizeMB)
+ {
+ cVideoDirectory Dir;
+@@ -232,6 +367,129 @@
+ } while (Dir.Next());
+ }
+
++#ifdef USE_DVLVIDPREFER
++// returns path to nVid'th video directory or NULL if not existing
++char *cVideoDirectory::GetVidPath(int nVid)
++{
++ char *b = strdup(VideoDirectory);
++ int l = strlen(b), di, n;
++
++ while (l-- > 0 && isdigit(b[ l ]));
++
++ l++;
++ di = strlen(b) - l;
++
++ // di == number of digits
++ n = atoi(&b[ l ]);
++ if (n != 0)
++ return NULL;
++
++ // add requested number to dir name
++ sprintf(&b[ l ], "%0*d", di, nVid);
++
++ if (DirectoryOk(b) == true)
++ return b;
++
++ free(b);
++ return NULL;
++}
++
++// checks if a video dir is 'valid'
++bool cVideoDirectory::IsVidDirOK(int nVid, int *freeMB)
++{
++ char *dn;
++ int fMB;
++
++ if (nVid >= Setup.nVidPrefer)
++ return false;
++
++ if (Setup.VidPreferSize[ nVid ] == -1)
++ return false;
++
++ dn = GetVidPath(nVid);
++ if (dn == NULL)
++ return false;
++
++ fMB = FreeDiskSpaceMB(dn, NULL);
++ if (freeMB != NULL)
++ *freeMB = fMB;
++
++ free(dn);
++
++ if (Setup.VidPreferSize[ nVid ] >= fMB)
++ return false;
++ return true;
++}
++
++
++// calculates which video dir to use
++bool cVideoDirectory::GetPreferedVideoDir(void)
++{
++ cVideoDirectory d;
++ int nDirs = 1,
++ vidUse = Setup.nVidPrefer;
++ int i, top, topFree, x;
++
++ if (name == NULL)
++ return(false);
++
++ // count available video dirs
++ while (d.Next() == true)
++ nDirs++;
++
++ if (vidUse > nDirs)
++ vidUse = nDirs;
++
++ // check for prefered video dir
++ for (i = 0, top = -1, topFree = 0; i < vidUse; i++) {
++ if (IsVidDirOK(i, &x) == true) {
++ if (top == -1) {
++ // nothing set yet, use first 'ok' dir
++ top = i;
++ topFree = x;
++ }
++ else {
++ // check if we got a higher priority
++ if (Setup.VidPreferPrio[ i ] >= Setup.VidPreferPrio[ top ]) {
++ top = i;
++ topFree = x;
++ }
++ // check if we got same priority but more space
++ else if (Setup.VidPreferPrio[ i ] == Setup.VidPreferPrio[ top ] && x >= topFree) {
++ top = i;
++ topFree = x;
++ }
++ }
++ }
++ }
++
++ if (top == -1) {
++ isyslog("VidPrefer: no prefered video directory could be determined!");
++
++ // something went wrong here...
++ // let VDR determine the video directory
++ int MaxFree = FreeMB();
++
++ while (Next()) {
++ int Free = FreeDiskSpaceMB(Name());
++
++ if (Free > MaxFree) {
++ Store();
++ MaxFree = Free;
++ }
++ }
++ }
++ else {
++ isyslog("VidPrefer: prefered video directory '%d' set.", top);
++ if (stored != NULL)
++ free(stored);
++ stored = GetVidPath(top);
++ }
++
++ return true;
++}
++#endif /* DVLVIDPREFER */
++
+ bool IsOnVideoDirectoryFileSystem(const char *FileName)
+ {
+ cVideoDirectory Dir;
+diff -ruN vdr-1.7.14/videodir.h vdr-1.7.14.ExtP_NG/videodir.h
+--- vdr-1.7.14/videodir.h 2008-02-16 13:53:11.000000000 +0100
++++ vdr-1.7.14.ExtP_NG/videodir.h 2010-04-10 15:45:12.148736552 +0200
+@@ -19,6 +19,9 @@
+ int CloseVideoFile(cUnbufferedFile *File);
+ bool RenameVideoFile(const char *OldName, const char *NewName);
+ bool RemoveVideoFile(const char *FileName);
++#ifdef USE_HARDLINKCUTTER
++bool HardLinkVideoFile(const char *OldName, const char *NewName);
++#endif /* HARDLINKCUTTER */
+ bool VideoFileSpaceAvailable(int SizeMB);
+ int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent
+ cString PrefixVideoFileName(const char *FileName, char Prefix);
diff --git a/vdr/extensions/vdr-1.7.14_extensions.diff b/vdr/extensions/vdr-1.7.14_extensions.diff
index 2d08e23..023d712 100755
--- a/vdr/extensions/vdr-1.7.14_extensions.diff
+++ b/vdr/extensions/vdr-1.7.14_extensions.diff
@@ -1,6 +1,6 @@
diff -ruN vdr-1.7.14/Make.config.template vdr-1.7.14.ExtP_NG/Make.config.template
--- vdr-1.7.14/Make.config.template 2010-02-06 15:50:03.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/Make.config.template 2010-03-14 14:02:05.346985224 +0100
++++ vdr-1.7.14.ExtP_NG/Make.config.template 2010-04-10 15:45:10.488736701 +0200
@@ -41,8 +41,185 @@
## Define if you want vdr to not run as root
#VDR_USER = vdr
@@ -90,7 +90,7 @@ diff -ruN vdr-1.7.14/Make.config.template vdr-1.7.14.ExtP_NG/Make.config.templat
+DEFINES += -DUSE_DVLSCRIPTADDON
+endif
+
-+ifdef DVLVDIPREFER
++ifdef DVLVIDPREFER
+DEFINES += -DUSE_DVLVIDPREFER
+endif
+
@@ -189,7 +189,7 @@ diff -ruN vdr-1.7.14/Make.config.template vdr-1.7.14.ExtP_NG/Make.config.templat
+endif
diff -ruN vdr-1.7.14/Makefile vdr-1.7.14.ExtP_NG/Makefile
--- vdr-1.7.14/Makefile 2010-02-21 12:44:38.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/Makefile 2010-03-14 14:02:05.369985286 +0100
++++ vdr-1.7.14.ExtP_NG/Makefile 2010-04-10 15:45:10.520736498 +0200
@@ -44,6 +44,18 @@
skinclassic.o skins.o skinsttng.o sourceparams.o sources.o spu.o status.o svdrp.o themes.o thread.o\
timers.o tools.o transfer.o vdr.o videodir.o
@@ -211,7 +211,7 @@ diff -ruN vdr-1.7.14/Makefile vdr-1.7.14.ExtP_NG/Makefile
endif
diff -ruN vdr-1.7.14/channels.c vdr-1.7.14.ExtP_NG/channels.c
--- vdr-1.7.14/channels.c 2010-02-21 14:36:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/channels.c 2010-03-14 14:02:04.541995571 +0100
++++ vdr-1.7.14.ExtP_NG/channels.c 2010-04-10 15:45:09.286739173 +0200
@@ -12,6 +12,9 @@
#include "device.h"
#include "epg.h"
@@ -434,7 +434,7 @@ diff -ruN vdr-1.7.14/channels.c vdr-1.7.14.ExtP_NG/channels.c
channelsHashSid.Add(Channel, Channel->Sid());
diff -ruN vdr-1.7.14/channels.h vdr-1.7.14.ExtP_NG/channels.h
--- vdr-1.7.14/channels.h 2010-03-07 14:47:13.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/channels.h 2010-03-14 14:02:04.585995558 +0100
++++ vdr-1.7.14.ExtP_NG/channels.h 2010-04-10 15:45:09.370737290 +0200
@@ -35,6 +35,9 @@
#define MAXDPIDS 16 // dolby (AC3 + DTS)
#define MAXSPIDS 32 // subtitles
@@ -543,7 +543,7 @@ diff -ruN vdr-1.7.14/channels.h vdr-1.7.14.ExtP_NG/channels.h
int GetNextGroup(int Idx); // Get next channel group
diff -ruN vdr-1.7.14/ci.c vdr-1.7.14.ExtP_NG/ci.c
--- vdr-1.7.14/ci.c 2010-01-02 11:39:50.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/ci.c 2010-03-14 14:02:04.608999551 +0100
++++ vdr-1.7.14.ExtP_NG/ci.c 2010-04-10 15:45:09.396741389 +0200
@@ -1911,6 +1911,10 @@
AddPid(Channel->Sid(), *Apid, STREAM_TYPE_AUDIO);
for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
@@ -569,7 +569,7 @@ diff -ruN vdr-1.7.14/ci.c vdr-1.7.14.ExtP_NG/ci.c
do {
diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
--- vdr-1.7.14/config.c 2010-03-12 17:41:37.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/config.c 2010-03-14 14:02:04.634993963 +0100
++++ vdr-1.7.14.ExtP_NG/config.c 2010-04-10 15:45:09.424743485 +0200
@@ -313,6 +313,12 @@
strcpy(OSDLanguage, ""); // default is taken from environment
strcpy(OSDSkin, "sttng");
@@ -632,7 +632,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
SplitEditedFiles = 0;
DelTimeshiftRec = 0;
MinEventTimeout = 30;
-@@ -391,18 +418,80 @@
+@@ -391,18 +418,83 @@
MultiSpeedMode = 0;
ShowReplayMode = 0;
ResumeID = 0;
@@ -681,6 +681,9 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
+ RecordingsSortMode = 0;
+ RecordingsSortDirsFirst = 0;
+#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ CutterAutoDelete = 0;
++#endif /* CUTTERQUEUE */
+#ifdef USE_DVLFRIENDLYFNAMES
+ UseFriendlyFNames = 0; // default = disabled
+#endif /* DVLFRIENDLYFNAMES */
@@ -713,7 +716,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
return *this;
}
-@@ -503,6 +592,12 @@
+@@ -503,6 +595,12 @@
if (!strcasecmp(Name, "OSDLanguage")) { strn0cpy(OSDLanguage, Value, sizeof(OSDLanguage)); I18nSetLocale(OSDLanguage); }
else if (!strcasecmp(Name, "OSDSkin")) Utf8Strn0Cpy(OSDSkin, Value, MaxSkinName);
else if (!strcasecmp(Name, "OSDTheme")) Utf8Strn0Cpy(OSDTheme, Value, MaxThemeName);
@@ -726,7 +729,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value);
else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value);
else if (!strcasecmp(Name, "TimeoutRequChInfo")) TimeoutRequChInfo = atoi(Value);
-@@ -521,13 +616,27 @@
+@@ -521,13 +619,27 @@
else if (!strcasecmp(Name, "TimeTransponder")) TimeTransponder = atoi(Value);
else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
@@ -754,7 +757,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value);
else if (!strcasecmp(Name, "EPGLinger")) EPGLinger = atoi(Value);
-@@ -548,6 +657,9 @@
+@@ -548,6 +660,9 @@
else if (!strcasecmp(Name, "VideoDisplayFormat")) VideoDisplayFormat = atoi(Value);
else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value);
else if (!strcasecmp(Name, "UpdateChannels")) UpdateChannels = atoi(Value);
@@ -764,7 +767,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
else if (!strcasecmp(Name, "UseDolbyDigital")) UseDolbyDigital = atoi(Value);
else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value);
else if (!strcasecmp(Name, "ChannelInfoTime")) ChannelInfoTime = atoi(Value);
-@@ -573,6 +685,10 @@
+@@ -573,6 +688,10 @@
else if (!strcasecmp(Name, "FontSmlSize")) FontSmlSize = atoi(Value);
else if (!strcasecmp(Name, "FontFixSize")) FontFixSize = atoi(Value);
else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value);
@@ -775,7 +778,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value);
else if (!strcasecmp(Name, "DelTimeshiftRec")) DelTimeshiftRec = atoi(Value);
else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value);
-@@ -581,15 +697,99 @@
+@@ -581,15 +700,102 @@
else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value);
else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value);
else if (!strcasecmp(Name, "ResumeID")) ResumeID = atoi(Value);
@@ -823,6 +826,9 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
+ else if (!strcasecmp(Name, "RecordingsSortMode")) RecordingsSortMode = atoi(Value);
+ else if (!strcasecmp(Name, "RecordingsSortDirsFirst")) RecordingsSortDirsFirst = atoi(Value);
+#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ else if (!strcasecmp(Name, "CutterAutoDelete")) CutterAutoDelete = atoi(Value);
++#endif /* CUTTERQUEUE */
+#ifdef USE_DVLFRIENDLYFNAMES
+ else if (strcasecmp(Name, "UseFriendlyFNames") == 0) UseFriendlyFNames = atoi(Value);
+#endif /* DVLFRIENDLYFNAMES */
@@ -875,7 +881,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
return true;
}
-@@ -598,6 +798,12 @@
+@@ -598,6 +804,12 @@
Store("OSDLanguage", OSDLanguage);
Store("OSDSkin", OSDSkin);
Store("OSDTheme", OSDTheme);
@@ -888,7 +894,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
Store("PrimaryDVB", PrimaryDVB);
Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch);
Store("TimeoutRequChInfo", TimeoutRequChInfo);
-@@ -616,13 +822,27 @@
+@@ -616,13 +828,27 @@
Store("TimeTransponder", TimeTransponder);
Store("MarginStart", MarginStart);
Store("MarginStop", MarginStop);
@@ -916,7 +922,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
Store("EPGScanTimeout", EPGScanTimeout);
Store("EPGBugfixLevel", EPGBugfixLevel);
Store("EPGLinger", EPGLinger);
-@@ -643,6 +863,9 @@
+@@ -643,6 +869,9 @@
Store("VideoDisplayFormat", VideoDisplayFormat);
Store("VideoFormat", VideoFormat);
Store("UpdateChannels", UpdateChannels);
@@ -926,7 +932,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
Store("UseDolbyDigital", UseDolbyDigital);
Store("ChannelInfoPos", ChannelInfoPos);
Store("ChannelInfoTime", ChannelInfoTime);
-@@ -676,13 +899,73 @@
+@@ -676,13 +905,75 @@
Store("MultiSpeedMode", MultiSpeedMode);
Store("ShowReplayMode", ShowReplayMode);
Store("ResumeID", ResumeID);
@@ -981,7 +987,9 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
+ Store("RecordingsSortMode", RecordingsSortMode);
+ Store("RecordingsSortDirsFirst", RecordingsSortDirsFirst);
+#endif /* SORTRECORDS */
-+
++#ifdef USE_CUTTERQUEUE
++ Store("CutterAutoDelete", CutterAutoDelete);
++#endif /* CUTTERQUEUE */
+#ifdef USE_DVLFRIENDLYFNAMES
+ Store ("UseFriendlyFNames", UseFriendlyFNames);
+#endif /* DVLFRIENDLYFNAMES */
@@ -1002,7 +1010,7 @@ diff -ruN vdr-1.7.14/config.c vdr-1.7.14.ExtP_NG/config.c
diff -ruN vdr-1.7.14/config.h vdr-1.7.14.ExtP_NG/config.h
--- vdr-1.7.14/config.h 2010-03-12 17:02:53.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/config.h 2010-03-14 14:02:04.646994485 +0100
++++ vdr-1.7.14.ExtP_NG/config.h 2010-04-10 15:45:09.438738230 +0200
@@ -30,15 +30,39 @@
#define APIVERSION "1.7.14"
#define APIVERSNUM 10714 // Version * 10000 + Major * 100 + Minor
@@ -1102,7 +1110,7 @@ diff -ruN vdr-1.7.14/config.h vdr-1.7.14.ExtP_NG/config.h
int SplitEditedFiles;
int DelTimeshiftRec;
int MinEventTimeout, MinUserInactivity;
-@@ -283,15 +332,67 @@
+@@ -283,15 +332,70 @@
int MultiSpeedMode;
int ShowReplayMode;
int ResumeID;
@@ -1149,6 +1157,9 @@ diff -ruN vdr-1.7.14/config.h vdr-1.7.14.ExtP_NG/config.h
+ int RecordingsSortMode;
+ int RecordingsSortDirsFirst;
+#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ int CutterAutoDelete;
++#endif /* CUTTERQUEUE */
+#ifdef USE_DVLFRIENDLYFNAMES
+ int UseFriendlyFNames;
+#endif /* DVLFRIENDLYFNAMES */
@@ -1172,7 +1183,7 @@ diff -ruN vdr-1.7.14/config.h vdr-1.7.14.ExtP_NG/config.h
bool Save(void);
diff -ruN vdr-1.7.14/cutter.c vdr-1.7.14.ExtP_NG/cutter.c
--- vdr-1.7.14/cutter.c 2010-01-02 14:08:08.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/cutter.c 2010-03-14 14:02:04.715992847 +0100
++++ vdr-1.7.14.ExtP_NG/cutter.c 2010-04-10 15:45:09.627745454 +0200
@@ -15,6 +15,19 @@
// --- cCuttingThread --------------------------------------------------------
@@ -1424,7 +1435,7 @@ diff -ruN vdr-1.7.14/cutter.c vdr-1.7.14.ExtP_NG/cutter.c
}
Recordings.TouchUpdate();
}
-@@ -194,18 +366,80 @@
+@@ -194,18 +366,87 @@
// --- cCutter ---------------------------------------------------------------
@@ -1494,18 +1505,25 @@ diff -ruN vdr-1.7.14/cutter.c vdr-1.7.14.ExtP_NG/cutter.c
+
+#ifdef USE_CUTTERQUEUE
+ if(!(Recordings.GetByName(FileName))) {
-+ // Should _not_ remove any cutted recordings
-+ // (original recording already deleted ?)
-+ // so, just pop item from queue and return.
-+ esyslog("can't cut non-existing recording %s", FileName);
-+ cutterQueue.Del(cutterQueue.First());
-+ return true; // might be already queued recording
++ // Update Recordings, maybe its not initialized(if vdr --edit is used)
++ Recordings.Update(true);
++ if(!(Recordings.GetByName(FileName))) {
++ // Update Recordings, maybe its not initialized(if vdr --edit is used)
++ Recordings.Update(true);
++ if(!(Recordings.GetByName(FileName))) {
++ // Should _not_ remove any cutted recordings
++ // (original recording already deleted ?)
++ // so, just pop item from queue and return.
++ esyslog("can't cut non-existing recording %s", FileName);
++ cutterQueue.Del(cutterQueue.First());
++ return true; // might be already queued recording
++ }
+ }
+#endif /* CUTTERQUEUE */
if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
// XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
// remove a possible deleted recording with the same name to avoid symlink mixups:
-@@ -231,6 +465,10 @@
+@@ -231,6 +472,10 @@
void cCutter::Stop(void)
{
@@ -1516,7 +1534,7 @@ diff -ruN vdr-1.7.14/cutter.c vdr-1.7.14.ExtP_NG/cutter.c
bool Interrupted = cuttingThread && cuttingThread->Active();
const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
delete cuttingThread;
-@@ -242,11 +480,20 @@
+@@ -242,11 +487,20 @@
esyslog("ERROR: '%s' during editing process", Error);
RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
Recordings.DelByName(editedVersionName);
@@ -1537,30 +1555,29 @@ diff -ruN vdr-1.7.14/cutter.c vdr-1.7.14.ExtP_NG/cutter.c
if (cuttingThread) {
if (cuttingThread->Active())
return true;
-@@ -257,12 +504,42 @@
+@@ -257,12 +511,41 @@
free(editedVersionName);
editedVersionName = NULL;
ended = true;
+#ifdef USE_CUTTERQUEUE
-+ /* Remove original (if cutting was successful) */
-+ if(!error) {
-+ cRecording *recording = Recordings.GetByName(*cutterQueue.First());
-+ if (!recording) {
-+ esyslog("ERROR: Can't found '%s' after editing process", cutterQueue.First()->Value());
-+ } else {
-+ if (recording->Delete()) {
-+ //Recordings.Del(recording);
-+ //cReplayControl::ClearLastReplayed(ri->FileName());
-+ Recordings.DelByName(recording->FileName());
-+ } else {
-+ esyslog("ERROR: Can't delete '%s' after editing process", cutterQueue.First()->Value());
-+ }
-+ }
- }
-+ lastCuttingEndTime = cTimeMs::Now();
-+ cutterQueue.Del(cutterQueue.First());
++ if (Setup.CutterAutoDelete) {
++ /* Remove original (if cutting was successful) */
++ if(!error) {
++ cRecording *recording = Recordings.GetByName(*cutterQueue.First());
++ if (!recording)
++ esyslog("ERROR: Can't found '%s' after editing process", cutterQueue.First()->Value());
++ else {
++ if (recording->Delete())
++ Recordings.DelByName(recording->FileName());
++ else
++ esyslog("ERROR: Can't delete '%s' after editing process", cutterQueue.First()->Value());
++ }
++ }
++ lastCuttingEndTime = cTimeMs::Now();
++ }
++ cutterQueue.Del(cutterQueue.First());
+#endif /* CUTTERQUEUE */
-+ }
+ }
+#ifdef USE_CUTTERQUEUE
+ if(!cuttingThread && cutterQueue.First()) {
+ /* start next cutting from queue*/
@@ -1580,7 +1597,7 @@ diff -ruN vdr-1.7.14/cutter.c vdr-1.7.14.ExtP_NG/cutter.c
bool result = error;
error = false;
return result;
-@@ -270,6 +547,9 @@
+@@ -270,6 +553,9 @@
bool cCutter::Ended(void)
{
@@ -1592,7 +1609,7 @@ diff -ruN vdr-1.7.14/cutter.c vdr-1.7.14.ExtP_NG/cutter.c
return result;
diff -ruN vdr-1.7.14/cutter.h vdr-1.7.14.ExtP_NG/cutter.h
--- vdr-1.7.14/cutter.h 2010-01-02 13:09:54.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/cutter.h 2010-03-14 14:02:04.729992477 +0100
++++ vdr-1.7.14.ExtP_NG/cutter.h 2010-04-10 15:45:09.656737920 +0200
@@ -11,6 +11,9 @@
#define __CUTTER_H
@@ -1615,7 +1632,7 @@ diff -ruN vdr-1.7.14/cutter.h vdr-1.7.14.ExtP_NG/cutter.h
static void Stop(void);
diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
--- vdr-1.7.14/device.c 2010-02-07 12:54:42.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/device.c 2010-03-14 14:02:04.750995187 +0100
++++ vdr-1.7.14.ExtP_NG/device.c 2010-04-10 15:45:09.684741026 +0200
@@ -18,6 +18,12 @@
#include "receiver.h"
#include "status.h"
@@ -1949,7 +1966,7 @@ diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
if (LiveView) {
StopReplay();
DELETENULL(liveSubtitle);
-@@ -698,11 +916,39 @@
+@@ -698,11 +914,39 @@
eSetChannelResult Result = scrOk;
@@ -1989,7 +2006,7 @@ diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
cControl::Launch(new cTransferControl(Device, Channel));
-@@ -720,6 +966,12 @@
+@@ -720,6 +964,12 @@
sectionHandler->SetStatus(false);
sectionHandler->SetChannel(NULL);
}
@@ -2002,7 +2019,7 @@ diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
// Tell the camSlot about the channel switch and add all PIDs of this
// channel to it, for possible later decryption:
if (camSlot)
-@@ -1012,6 +1264,10 @@
+@@ -1012,6 +1262,10 @@
int LanguagePreference = INT_MAX; // higher than the maximum possible value
for (int i = ttSubtitleFirst; i <= ttSubtitleLast; i++) {
const tTrackId *TrackId = GetTrack(eTrackType(i));
@@ -2013,7 +2030,7 @@ diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
if (TrackId && TrackId->id && I18nIsPreferredLanguage(Setup.SubtitleLanguages, TrackId->language, LanguagePreference))
PreferredTrack = eTrackType(i);
}
-@@ -1223,6 +1479,15 @@
+@@ -1223,6 +1477,15 @@
}
break;
case 0xBD: { // private stream 1
@@ -2029,7 +2046,7 @@ diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
int PayloadOffset = Data[8] + 9;
// Compatibility mode for old subtitles plugin:
-@@ -1382,6 +1647,9 @@
+@@ -1382,6 +1645,9 @@
tsToPesVideo.Reset();
tsToPesAudio.Reset();
tsToPesSubtitle.Reset();
@@ -2039,7 +2056,7 @@ diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
}
else if (Length < TS_SIZE) {
esyslog("ERROR: skipped %d bytes of TS fragment", Length);
-@@ -1427,6 +1695,19 @@
+@@ -1427,6 +1693,19 @@
if (!VideoOnly || HasIBPTrickSpeed())
PlayTsSubtitle(Data, TS_SIZE);
}
@@ -2061,7 +2078,7 @@ diff -ruN vdr-1.7.14/device.c vdr-1.7.14.ExtP_NG/device.c
else if (Pid == patPmtParser.Ppid()) {
diff -ruN vdr-1.7.14/device.h vdr-1.7.14.ExtP_NG/device.h
--- vdr-1.7.14/device.h 2010-02-06 15:34:41.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/device.h 2010-03-14 14:02:04.772993966 +0100
++++ vdr-1.7.14.ExtP_NG/device.h 2010-04-10 15:45:09.706728067 +0200
@@ -24,6 +24,9 @@
#include "spu.h"
#include "thread.h"
@@ -2183,7 +2200,7 @@ diff -ruN vdr-1.7.14/device.h vdr-1.7.14.ExtP_NG/device.h
const cPatPmtParser *PatPmtParser(void) const { return &patPmtParser; }
diff -ruN vdr-1.7.14/dvbdevice.c vdr-1.7.14.ExtP_NG/dvbdevice.c
--- vdr-1.7.14/dvbdevice.c 2010-03-07 14:58:24.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/dvbdevice.c 2010-03-14 14:02:04.858993520 +0100
++++ vdr-1.7.14.ExtP_NG/dvbdevice.c 2010-04-10 15:45:09.854742151 +0200
@@ -246,6 +246,9 @@
class cDvbTuner : public cThread {
private:
@@ -2299,7 +2316,7 @@ diff -ruN vdr-1.7.14/dvbdevice.c vdr-1.7.14.ExtP_NG/dvbdevice.c
setTransferModeForDolbyDigital = Mode;
diff -ruN vdr-1.7.14/dvbdevice.h vdr-1.7.14.ExtP_NG/dvbdevice.h
--- vdr-1.7.14/dvbdevice.h 2010-02-21 15:06:08.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/dvbdevice.h 2010-03-14 14:02:04.865989999 +0100
++++ vdr-1.7.14.ExtP_NG/dvbdevice.h 2010-04-10 15:45:09.863735987 +0200
@@ -146,6 +146,9 @@
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
public:
@@ -2312,7 +2329,7 @@ diff -ruN vdr-1.7.14/dvbdevice.h vdr-1.7.14.ExtP_NG/dvbdevice.h
diff -ruN vdr-1.7.14/dvbplayer.c vdr-1.7.14.ExtP_NG/dvbplayer.c
--- vdr-1.7.14/dvbplayer.c 2010-03-07 15:24:26.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/dvbplayer.c 2010-03-14 14:02:04.881992796 +0100
++++ vdr-1.7.14.ExtP_NG/dvbplayer.c 2010-04-10 15:45:09.881743306 +0200
@@ -204,6 +204,9 @@
cNonBlockingFileReader *nonBlockingFileReader;
cRingBufferFrame *ringBuffer;
@@ -2476,7 +2493,7 @@ diff -ruN vdr-1.7.14/dvbplayer.c vdr-1.7.14.ExtP_NG/dvbplayer.c
diff -ruN vdr-1.7.14/eit.c vdr-1.7.14.ExtP_NG/eit.c
--- vdr-1.7.14/eit.c 2010-01-08 16:17:09.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/eit.c 2010-03-14 14:02:04.949989942 +0100
++++ vdr-1.7.14.ExtP_NG/eit.c 2010-04-10 15:45:09.958741231 +0200
@@ -24,8 +24,31 @@
class cEIT : public SI::EIT {
public:
@@ -2709,7 +2726,7 @@ diff -ruN vdr-1.7.14/eit.c vdr-1.7.14.ExtP_NG/eit.c
if (Empty && getSectionNumber() == 0)
diff -ruN vdr-1.7.14/eitscan.c vdr-1.7.14.ExtP_NG/eitscan.c
--- vdr-1.7.14/eitscan.c 2010-02-07 13:12:05.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/eitscan.c 2010-03-14 14:02:04.967989369 +0100
++++ vdr-1.7.14.ExtP_NG/eitscan.c 2010-04-10 15:45:09.980739226 +0200
@@ -151,9 +151,17 @@
if (Device->ProvidesTransponder(Channel)) {
if (!Device->Receiving()) {
@@ -2730,7 +2747,7 @@ diff -ruN vdr-1.7.14/eitscan.c vdr-1.7.14.ExtP_NG/eitscan.c
Skins.Message(mtInfo, tr("Starting EPG scan"));
diff -ruN vdr-1.7.14/epg.c vdr-1.7.14.ExtP_NG/epg.c
--- vdr-1.7.14/epg.c 2010-02-28 15:24:55.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/epg.c 2010-03-14 14:02:04.997993332 +0100
++++ vdr-1.7.14.ExtP_NG/epg.c 2010-04-10 15:45:10.017741115 +0200
@@ -930,6 +930,31 @@
return pe;
}
@@ -2765,7 +2782,7 @@ diff -ruN vdr-1.7.14/epg.c vdr-1.7.14.ExtP_NG/epg.c
hasRunning = false;
diff -ruN vdr-1.7.14/epg.h vdr-1.7.14.ExtP_NG/epg.h
--- vdr-1.7.14/epg.h 2010-01-08 16:20:34.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/epg.h 2010-03-14 14:02:05.006989800 +0100
++++ vdr-1.7.14.ExtP_NG/epg.h 2010-04-10 15:45:10.027736473 +0200
@@ -163,6 +163,9 @@
void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
void Cleanup(time_t Time);
@@ -2778,7 +2795,7 @@ diff -ruN vdr-1.7.14/epg.h vdr-1.7.14.ExtP_NG/epg.h
void HashEvent(cEvent *Event);
diff -ruN vdr-1.7.14/iconpatch.c vdr-1.7.14.ExtP_NG/iconpatch.c
--- vdr-1.7.14/iconpatch.c 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/iconpatch.c 2010-03-14 14:02:05.215987343 +0100
++++ vdr-1.7.14.ExtP_NG/iconpatch.c 2010-04-10 15:45:10.336740261 +0200
@@ -0,0 +1,31 @@
+#ifdef USE_WAREAGLEICON
+
@@ -2813,7 +2830,7 @@ diff -ruN vdr-1.7.14/iconpatch.c vdr-1.7.14.ExtP_NG/iconpatch.c
+#endif /* WAREAGLEICON */
diff -ruN vdr-1.7.14/iconpatch.h vdr-1.7.14.ExtP_NG/iconpatch.h
--- vdr-1.7.14/iconpatch.h 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/iconpatch.h 2010-03-14 14:02:05.226986089 +0100
++++ vdr-1.7.14.ExtP_NG/iconpatch.h 2010-04-10 15:45:10.348737591 +0200
@@ -0,0 +1,73 @@
+#ifdef USE_WAREAGLEICON
+/*
@@ -2890,7 +2907,7 @@ diff -ruN vdr-1.7.14/iconpatch.h vdr-1.7.14.ExtP_NG/iconpatch.h
+#endif /* WAREAGLEICON */
diff -ruN vdr-1.7.14/lirc.c vdr-1.7.14.ExtP_NG/lirc.c
--- vdr-1.7.14/lirc.c 2006-05-28 10:48:13.000000000 +0200
-+++ vdr-1.7.14.ExtP_NG/lirc.c 2010-03-14 14:02:05.310986139 +0100
++++ vdr-1.7.14.ExtP_NG/lirc.c 2010-04-10 15:45:10.452737940 +0200
@@ -12,6 +12,10 @@
#include "lirc.h"
#include <netinet/in.h>
@@ -2951,7 +2968,7 @@ diff -ruN vdr-1.7.14/lirc.c vdr-1.7.14.ExtP_NG/lirc.c
*LastKeyName = 0;
diff -ruN vdr-1.7.14/mainmenuitemsprovider.h vdr-1.7.14.ExtP_NG/mainmenuitemsprovider.h
--- vdr-1.7.14/mainmenuitemsprovider.h 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/mainmenuitemsprovider.h 2010-03-14 14:02:05.333986270 +0100
++++ vdr-1.7.14.ExtP_NG/mainmenuitemsprovider.h 2010-04-10 15:45:10.477763751 +0200
@@ -0,0 +1,62 @@
+#ifdef USE_MENUORG
+/*
@@ -3017,7 +3034,7 @@ diff -ruN vdr-1.7.14/mainmenuitemsprovider.h vdr-1.7.14.ExtP_NG/mainmenuitemspro
+#endif /* MENUORG */
diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
--- vdr-1.7.14/menu.c 2010-03-12 17:03:07.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/menu.c 2010-03-14 14:02:05.449985065 +0100
++++ vdr-1.7.14.ExtP_NG/menu.c 2010-04-10 15:45:10.637743132 +0200
@@ -8,6 +8,9 @@
*/
@@ -3103,7 +3120,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
SetFirstDayItem();
}
-@@ -1017,8 +1054,14 @@
+@@ -1017,8 +1053,14 @@
class cMenuTimerItem : public cOsdItem {
private:
cTimer *timer;
@@ -3118,7 +3135,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
virtual int Compare(const cListObject &ListObject) const;
virtual void Set(void);
cTimer *Timer(void) { return timer; }
-@@ -1027,6 +1070,9 @@
+@@ -1027,6 +1069,9 @@
cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
{
timer = Timer;
@@ -3128,7 +3145,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Set();
}
-@@ -1057,8 +1103,31 @@
+@@ -1057,8 +1102,31 @@
File++;
else
File = timer->File();
@@ -3160,7 +3177,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
timer->Channel()->Number(),
*name,
*name && **name ? " " : "",
-@@ -1070,6 +1139,58 @@
+@@ -1070,6 +1138,58 @@
File));
}
@@ -3219,7 +3236,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
// --- cMenuTimers -----------------------------------------------------------
class cMenuTimers : public cOsdMenu {
-@@ -1082,14 +1203,25 @@
+@@ -1082,14 +1202,25 @@
eOSState Info(void);
cTimer *CurrentTimer(void);
void SetHelpKeys(void);
@@ -3245,7 +3262,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
{
helpKeys = -1;
for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
-@@ -1100,6 +1232,9 @@
+@@ -1100,6 +1231,9 @@
SetCurrent(First());
SetHelpKeys();
Timers.IncBeingEdited();
@@ -3255,7 +3272,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
cMenuTimers::~cMenuTimers()
-@@ -1138,7 +1273,11 @@
+@@ -1138,7 +1272,11 @@
timer->OnOff();
timer->SetEventFromSchedule();
RefreshCurrent();
@@ -3267,7 +3284,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (timer->FirstDay())
isyslog("timer %s first day set to %s", *timer->ToDescr(), *timer->PrintFirstDay());
else
-@@ -1197,6 +1336,68 @@
+@@ -1197,6 +1335,68 @@
return osContinue;
}
@@ -3336,7 +3353,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
eOSState cMenuTimers::ProcessKey(eKeys Key)
{
int TimerNumber = HasSubMenu() ? Count() : -1;
-@@ -1205,18 +1406,36 @@
+@@ -1205,18 +1405,36 @@
if (state == osUnknown) {
switch (Key) {
case kOk: return Edit();
@@ -3373,7 +3390,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Display();
}
if (Key != kNone)
-@@ -1246,6 +1465,9 @@
+@@ -1246,6 +1464,9 @@
{
cOsdMenu::Display();
DisplayMenu()->SetEvent(event);
@@ -3383,7 +3400,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (event->Description())
cStatus::MsgOsdTextItem(event->Description());
}
-@@ -1293,7 +1515,12 @@
+@@ -1293,7 +1514,12 @@
const cChannel *channel;
bool withDate;
int timerMatch;
@@ -3396,7 +3413,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
static void SetSortMode(eScheduleSortMode SortMode) { sortMode = SortMode; }
static void IncSortMode(void) { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); }
static eScheduleSortMode SortMode(void) { return sortMode; }
-@@ -1303,7 +1530,11 @@
+@@ -1303,12 +1529,19 @@
cMenuScheduleItem::eScheduleSortMode cMenuScheduleItem::sortMode = ssmAllThis;
@@ -3408,7 +3425,15 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
{
event = Event;
channel = Channel;
-@@ -1323,7 +1554,30 @@
+ withDate = WithDate;
+ timerMatch = tmNone;
++#ifdef USE_LIEMIEXT
++ withBar = WithBar;
++#endif /* LIEMIEXT */
+ Update(true);
+ }
+
+@@ -1323,7 +1556,30 @@
return r;
}
@@ -3439,7 +3464,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
bool cMenuScheduleItem::Update(bool Force)
{
-@@ -1332,17 +1586,54 @@
+@@ -1332,17 +1588,54 @@
Timers.GetMatch(event, &timerMatch);
if (Force || timerMatch != OldTimerMatch) {
cString buffer;
@@ -3476,13 +3501,13 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
+#ifdef USE_WAREAGLEICON
+ buffer = cString::sprintf("%d\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
+#else
- buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++ buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
+#endif /* WAREAGLEICON */
+#else
+#ifdef USE_WAREAGLEICON
+ buffer = cString::sprintf("%d\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
+#else
-+ buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
+ buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
+#endif /* WAREAGLEICON */
+#endif /* LIEMIEXT */
else
@@ -3494,7 +3519,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
SetText(buffer);
result = true;
}
-@@ -1368,13 +1659,21 @@
+@@ -1368,13 +1661,21 @@
static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
static const cEvent *ScheduleEvent(void);
virtual eOSState ProcessKey(eKeys Key);
@@ -3516,7 +3541,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
{
now = Now;
helpKeys = -1;
-@@ -1386,7 +1685,11 @@
+@@ -1386,7 +1687,11 @@
if (Schedule) {
const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
if (Event)
@@ -3528,7 +3553,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
}
}
-@@ -1395,6 +1698,20 @@
+@@ -1395,6 +1700,20 @@
SetHelpKeys();
}
@@ -3549,7 +3574,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
bool cMenuWhatsOn::Update(void)
{
bool result = false;
-@@ -1535,6 +1852,10 @@
+@@ -1535,6 +1854,10 @@
cMenuSchedule(void);
virtual ~cMenuSchedule();
virtual eOSState ProcessKey(eKeys Key);
@@ -3560,7 +3585,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuSchedule::cMenuSchedule(void)
-@@ -1560,6 +1881,20 @@
+@@ -1560,6 +1883,20 @@
cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared
}
@@ -3581,7 +3606,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
void cMenuSchedule::PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel)
{
Clear();
-@@ -1915,6 +2250,9 @@
+@@ -1915,6 +2252,9 @@
cMenuCam(cCamSlot *CamSlot);
virtual ~cMenuCam();
virtual eOSState ProcessKey(eKeys Key);
@@ -3591,7 +3616,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuCam::cMenuCam(cCamSlot *CamSlot)
-@@ -2094,6 +2432,9 @@
+@@ -2094,6 +2434,9 @@
cMenuRecording(const cRecording *Recording, bool WithButtons = false);
virtual void Display(void);
virtual eOSState ProcessKey(eKeys Key);
@@ -3601,7 +3626,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons)
-@@ -2109,6 +2450,9 @@
+@@ -2109,6 +2452,9 @@
{
cOsdMenu::Display();
DisplayMenu()->SetRecording(recording);
@@ -3611,7 +3636,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (recording->Info()->Description())
cStatus::MsgOsdTextItem(recording->Info()->Description());
}
-@@ -2169,7 +2513,11 @@
+@@ -2169,7 +2515,11 @@
fileName = strdup(Recording->FileName());
name = NULL;
totalEntries = newEntries = 0;
@@ -3623,7 +3648,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (*Text() == '\t')
name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t'
}
-@@ -2185,13 +2533,183 @@
+@@ -2185,13 +2535,183 @@
totalEntries++;
if (New)
newEntries++;
@@ -3807,7 +3832,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
{
base = Base ? strdup(Base) : NULL;
level = Setup.RecordingDirs ? Level : -1;
-@@ -2269,7 +2787,13 @@
+@@ -2269,7 +2789,13 @@
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR)) {
cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
@@ -3821,7 +3846,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Add(Item);
LastItem = Item;
free(LastItemText);
-@@ -2319,6 +2843,11 @@
+@@ -2319,6 +2845,11 @@
{
cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
if (ri) {
@@ -3833,7 +3858,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (ri->IsDirectory())
Open();
else {
-@@ -2426,12 +2955,34 @@
+@@ -2426,12 +2957,34 @@
return osContinue;
}
@@ -3868,7 +3893,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
switch (Key) {
case kPlay:
case kOk: return Play();
-@@ -2440,7 +2991,26 @@
+@@ -2440,7 +2993,26 @@
case kYellow: return Delete();
case kInfo:
case kBlue: return Info();
@@ -3895,7 +3920,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
case kNone: if (Recordings.StateChanged(recordingsState))
Set(true);
break;
-@@ -2505,6 +3075,9 @@
+@@ -2505,6 +3077,9 @@
cMenuSetupOSD(void);
virtual ~cMenuSetupOSD();
virtual eOSState ProcessKey(eKeys Key);
@@ -3905,7 +3930,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuSetupOSD::cMenuSetupOSD(void)
-@@ -2546,6 +3119,9 @@
+@@ -2546,6 +3121,9 @@
Add(new cMenuEditStraItem(tr("Setup.OSD$Skin"), &skinIndex, numSkins, skinDescriptions));
if (themes.NumThemes())
Add(new cMenuEditStraItem(tr("Setup.OSD$Theme"), &themeIndex, themes.NumThemes(), themes.Descriptions()));
@@ -3915,7 +3940,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Add(new cMenuEditPrcItem( tr("Setup.OSD$Left (%)"), &data.OSDLeftP, 0.0, 0.5));
Add(new cMenuEditPrcItem( tr("Setup.OSD$Top (%)"), &data.OSDTopP, 0.0, 0.5));
Add(new cMenuEditPrcItem( tr("Setup.OSD$Width (%)"), &data.OSDWidthP, 0.5, 1.0));
-@@ -2568,6 +3144,12 @@
+@@ -2568,6 +3146,12 @@
Add(new cMenuEditBoolItem(tr("Setup.OSD$Menu key closes"), &data.MenuKeyCloses));
Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"), &data.RecordingDirs));
Add(new cMenuEditBoolItem(tr("Setup.OSD$Folders in timer menu"), &data.FoldersInTimerMenu));
@@ -3928,7 +3953,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
SetCurrent(Get(current));
Display();
}
-@@ -2639,12 +3221,18 @@
+@@ -2639,12 +3223,18 @@
class cMenuSetupEPG : public cMenuSetupBase {
private:
@@ -3947,7 +3972,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuSetupEPG::cMenuSetupEPG(void)
-@@ -2661,11 +3249,19 @@
+@@ -2661,11 +3251,19 @@
{
int current = Current();
@@ -3967,7 +3992,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"), &data.SetSystemTime));
if (data.SetSystemTime)
Add(new cMenuEditTranItem(tr("Setup.EPG$Use time from transponder"), &data.TimeTransponder, &data.TimeSource));
-@@ -2674,6 +3270,15 @@
+@@ -2674,6 +3272,15 @@
for (int i = 0; i < numLanguages; i++)
// TRANSLATORS: note the singular!
Add(new cMenuEditStraItem(tr("Setup.EPG$Preferred language"), &data.EPGLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
@@ -3983,7 +4008,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
SetCurrent(Get(current));
Display();
-@@ -2740,6 +3345,9 @@
+@@ -2740,6 +3347,9 @@
public:
cMenuSetupDVB(void);
virtual eOSState ProcessKey(eKeys Key);
@@ -3993,7 +4018,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuSetupDVB::cMenuSetupDVB(void)
-@@ -2770,12 +3378,18 @@
+@@ -2770,12 +3380,18 @@
Clear();
@@ -4012,7 +4037,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Add(new cMenuEditIntItem( tr("Setup.DVB$Audio languages"), &numAudioLanguages, 0, I18nLanguages()->Size()));
for (int i = 0; i < numAudioLanguages; i++)
Add(new cMenuEditStraItem(tr("Setup.DVB$Audio language"), &data.AudioLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
-@@ -2788,6 +3402,9 @@
+@@ -2788,6 +3404,9 @@
Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle foreground transparency"), &data.SubtitleFgTransparency, 0, 9));
Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle background transparency"), &data.SubtitleBgTransparency, 0, 10));
}
@@ -4022,7 +4047,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
SetCurrent(Get(current));
Display();
-@@ -2869,6 +3486,9 @@
+@@ -2869,6 +3488,9 @@
public:
cMenuSetupLNB(void);
virtual eOSState ProcessKey(eKeys Key);
@@ -4032,7 +4057,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuSetupLNB::cMenuSetupLNB(void)
-@@ -2883,6 +3503,23 @@
+@@ -2883,6 +3505,23 @@
Clear();
@@ -4056,7 +4081,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Add(new cMenuEditBoolItem(tr("Setup.LNB$Use DiSEqC"), &data.DiSEqC));
if (!data.DiSEqC) {
Add(new cMenuEditIntItem( tr("Setup.LNB$SLOF (MHz)"), &data.LnbSLOF));
-@@ -2899,6 +3536,10 @@
+@@ -2899,6 +3538,10 @@
int oldDiSEqC = data.DiSEqC;
eOSState state = cMenuSetupBase::ProcessKey(Key);
@@ -4067,7 +4092,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (Key != kNone && data.DiSEqC != oldDiSEqC)
Setup();
return state;
-@@ -2949,6 +3590,9 @@
+@@ -2949,6 +3592,9 @@
public:
cMenuSetupCAM(void);
virtual eOSState ProcessKey(eKeys Key);
@@ -4077,7 +4102,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuSetupCAM::cMenuSetupCAM(void)
-@@ -3025,12 +3669,57 @@
+@@ -3025,12 +3671,58 @@
private:
const char *pauseKeyHandlingTexts[3];
const char *delTimeshiftRecTexts[3];
@@ -4125,6 +4150,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
+}
+
+void cMenuSetupRecord::Set(void)
++{
+#endif /* DVLVIDPREFER */
+#ifdef USE_SORTRECORDS
+ RecordingsSortModeTexts[0] = tr("main dir alphabetically, subdirs flexible");
@@ -4135,7 +4161,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
pauseKeyHandlingTexts[0] = tr("do not pause live video");
pauseKeyHandlingTexts[1] = tr("confirm pause live video");
pauseKeyHandlingTexts[2] = tr("pause live video");
-@@ -3046,14 +3735,46 @@
+@@ -3046,14 +3738,49 @@
Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"), &data.PausePriority, 0, MAXPRIORITY));
Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"), &data.PauseLifetime, 0, MAXLIFETIME));
@@ -4179,10 +4205,13 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
+ Add(new cMenuEditStraItem(tr("Setup.Recording$Sort recordings by"), &data.RecordingsSortMode, MAXSORTMODES, RecordingsSortModeTexts));
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Sort directories before recordings"), &data.RecordingsSortDirsFirst));
+#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Cutter auto delete"), &data.CutterAutoDelete));
++#endif /* CUTTERQUEUE */
Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift recording"),&data.DelTimeshiftRec, 3, delTimeshiftRecTexts));
}
-@@ -3072,6 +3793,17 @@
+@@ -3072,6 +3799,17 @@
Add(new cMenuEditBoolItem(tr("Setup.Replay$Multi speed mode"), &data.MultiSpeedMode));
Add(new cMenuEditBoolItem(tr("Setup.Replay$Show replay mode"), &data.ShowReplayMode));
Add(new cMenuEditIntItem(tr("Setup.Replay$Resume ID"), &data.ResumeID, 0, 99));
@@ -4200,7 +4229,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
void cMenuSetupReplay::Store(void)
-@@ -3084,13 +3816,48 @@
+@@ -3084,13 +3822,48 @@
// --- cMenuSetupMisc --------------------------------------------------------
class cMenuSetupMisc : public cMenuSetupBase {
@@ -4249,7 +4278,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. event timeout (min)"), &data.MinEventTimeout));
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. user inactivity (min)"), &data.MinUserInactivity));
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout));
-@@ -3098,8 +3865,22 @@
+@@ -3098,8 +3871,22 @@
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Channel entry timeout (ms)"), &data.ChannelEntryTimeout, 0));
Add(new cMenuEditChanItem(tr("Setup.Miscellaneous$Initial channel"), &data.InitialChannel, tr("Setup.Miscellaneous$as before")));
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Initial volume"), &data.InitialVolume, -1, 255, tr("Setup.Miscellaneous$as before")));
@@ -4272,7 +4301,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
// --- cMenuSetupPluginItem --------------------------------------------------
-@@ -3124,6 +3905,9 @@
+@@ -3124,6 +3911,9 @@
public:
cMenuSetupPlugins(void);
virtual eOSState ProcessKey(eKeys Key);
@@ -4282,7 +4311,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuSetupPlugins::cMenuSetupPlugins(void)
-@@ -3173,6 +3957,9 @@
+@@ -3173,6 +3963,9 @@
public:
cMenuSetup(void);
virtual eOSState ProcessKey(eKeys Key);
@@ -4292,7 +4321,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
};
cMenuSetup::cMenuSetup(void)
-@@ -3262,24 +4049,65 @@
+@@ -3262,24 +4055,65 @@
cMenuMain::cMenuMain(eOSState State)
:cOsdMenu("")
{
@@ -4358,7 +4387,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
cOsdObject *cMenuMain::PluginOsdObject(void)
-@@ -3289,37 +4117,156 @@
+@@ -3289,37 +4123,156 @@
return o;
}
@@ -4515,7 +4544,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Update(true);
-@@ -3329,13 +4276,29 @@
+@@ -3329,13 +4282,29 @@
bool cMenuMain::Update(bool Force)
{
bool result = false;
@@ -4545,7 +4574,17 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
bool NewReplaying = cControl::Control() != NULL;
if (Force || NewReplaying != replaying) {
-@@ -3357,6 +4320,9 @@
+@@ -3343,6 +4312,9 @@
+ // Replay control:
+ if (replaying && !stopReplayItem)
+ // TRANSLATORS: note the leading blank!
++#ifdef USE_LIEMIEXT
++ if (Setup.MenuCmdPosition) Ins(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay)); else
++#endif /* LIEMIEXT */
+ Add(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay));
+ else if (stopReplayItem && !replaying) {
+ Del(stopReplayItem->Index());
+@@ -3357,6 +4329,9 @@
bool CutterActive = cCutter::Active();
if (CutterActive && !cancelEditingItem) {
// TRANSLATORS: note the leading blank!
@@ -4555,17 +4594,17 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Add(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit));
result = true;
}
-@@ -3376,6 +4342,9 @@
- const char *s = NULL;
+@@ -3377,6 +4352,9 @@
while ((s = cRecordControls::GetInstantId(s)) != NULL) {
cOsdItem *item = new cOsdItem(osStopRecord);
+ item->SetText(cString::sprintf("%s%s", tr(STOP_RECORDING), s));
+#ifdef USE_LIEMIEXT
+ if (Setup.MenuCmdPosition) Ins(item); else
+#endif /* LIEMIEXT */
- item->SetText(cString::sprintf("%s%s", tr(STOP_RECORDING), s));
Add(item);
if (!stopRecordingItem)
-@@ -3384,6 +4353,12 @@
+ stopRecordingItem = item;
+@@ -3384,6 +4362,12 @@
result = true;
}
@@ -4578,7 +4617,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
return result;
}
-@@ -3394,13 +4369,53 @@
+@@ -3394,13 +4378,53 @@
eOSState state = cOsdMenu::ProcessKey(Key);
HadSubMenu |= HasSubMenu();
@@ -4632,7 +4671,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
cOsdItem *item = Get(Current());
if (item) {
-@@ -3419,6 +4434,9 @@
+@@ -3419,6 +4443,9 @@
if (item) {
cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
if (p) {
@@ -4642,7 +4681,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
cOsdObject *menu = p->MainMenuAction();
if (menu) {
if (menu->IsMenu())
-@@ -3428,11 +4446,63 @@
+@@ -3428,11 +4455,63 @@
return osPlugin;
}
}
@@ -4706,7 +4745,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
default: switch (Key) {
case kRecord:
case kRed: if (!HadSubMenu)
-@@ -3449,9 +4519,63 @@
+@@ -3449,9 +4528,63 @@
case kBlue: if (!HadSubMenu)
state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osContinue;
break;
@@ -4770,7 +4809,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (!HasSubMenu() && Update(HadSubMenu))
Display();
if (Key != kNone) {
-@@ -3598,6 +4722,9 @@
+@@ -3598,6 +4731,9 @@
if (Direction) {
while (Channel) {
Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
@@ -4780,7 +4819,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (!Channel && Setup.ChannelsWrap)
Channel = Direction > 0 ? Channels.First() : Channels.Last();
if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
-@@ -3658,6 +4785,13 @@
+@@ -3658,6 +4794,13 @@
case kLeft:
case kRight|k_Repeat:
case kRight:
@@ -4794,7 +4833,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
case kNext|k_Repeat:
case kNext:
case kPrev|k_Repeat:
-@@ -3817,6 +4951,17 @@
+@@ -3817,6 +4960,17 @@
eOSState cDisplayVolume::ProcessKey(eKeys Key)
{
switch (Key) {
@@ -4812,7 +4851,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
case kVolUp|k_Repeat:
case kVolUp:
case kVolDn|k_Repeat:
-@@ -4064,8 +5209,16 @@
+@@ -4064,8 +5218,16 @@
// --- cRecordControl --------------------------------------------------------
@@ -4829,7 +4868,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
// We're going to manipulate an event here, so we need to prevent
// others from modifying any EPG data:
cSchedulesLock SchedulesLock;
-@@ -4110,12 +5263,29 @@
+@@ -4110,12 +5272,29 @@
return;
}
@@ -4859,7 +4898,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
Recording.WriteInfo();
cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
-@@ -4124,7 +5294,12 @@
+@@ -4124,7 +5303,12 @@
return;
}
else
@@ -4872,7 +4911,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
if (!Timer) {
Timers.Del(timer);
-@@ -4172,12 +5347,26 @@
+@@ -4172,12 +5356,26 @@
void cRecordControl::Stop(bool ExecuteUserCommand)
{
if (timer) {
@@ -4899,7 +4938,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
}
-@@ -4223,8 +5412,32 @@
+@@ -4223,8 +5421,32 @@
if (channel) {
int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
cDevice *device = cDevice::GetDevice(channel, Priority, false);
@@ -4932,7 +4971,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (!device->SwitchChannel(channel, false)) {
ShutdownHandler.RequestEmergencyExit();
return false;
-@@ -4232,7 +5445,14 @@
+@@ -4232,7 +5454,14 @@
if (!Timer || Timer->Matches()) {
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (!RecordControls[i]) {
@@ -4947,7 +4986,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
return RecordControls[i]->Process(time(NULL));
}
}
-@@ -4268,6 +5488,21 @@
+@@ -4268,6 +5497,21 @@
}
}
@@ -4969,7 +5008,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
bool cRecordControls::PauseLiveVideo(void)
{
Skins.Message(mtStatus, tr("Pausing live video..."));
-@@ -4365,12 +5600,22 @@
+@@ -4365,12 +5609,22 @@
// --- cReplayControl --------------------------------------------------------
@@ -4992,7 +5031,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
{
currentReplayControl = this;
displayReplay = NULL;
-@@ -4378,11 +5623,18 @@
+@@ -4378,11 +5632,18 @@
lastCurrent = lastTotal = -1;
lastPlay = lastForward = false;
lastSpeed = -2; // an invalid value
@@ -5011,7 +5050,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
SetTrackDescriptions(false);
}
-@@ -4642,8 +5894,16 @@
+@@ -4642,8 +5903,16 @@
ShowTimed(2);
bool Play, Forward;
int Speed;
@@ -5028,7 +5067,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
marks.Save();
}
-@@ -4656,8 +5916,22 @@
+@@ -4656,8 +5925,22 @@
if (GetIndex(Current, Total)) {
cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
if (m) {
@@ -5051,7 +5090,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
}
}
}
-@@ -4690,7 +5964,11 @@
+@@ -4690,7 +5973,11 @@
{
if (fileName) {
Hide();
@@ -5063,7 +5102,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (!marks.Count())
Skins.Message(mtError, tr("No editing marks defined!"));
else if (!cCutter::Start(fileName))
-@@ -4712,7 +5990,11 @@
+@@ -4712,7 +5999,11 @@
if (!m)
m = marks.GetNext(Current);
if (m) {
@@ -5075,7 +5114,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
m = marks.Next(m);
if (m) {
Goto(m->position - SecondsToFrames(3, FramesPerSecond()));
-@@ -4734,6 +6016,9 @@
+@@ -4734,6 +6025,9 @@
{
if (!Active())
return osEnd;
@@ -5085,7 +5124,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
if (visible) {
if (timeoutShow && time(NULL) > timeoutShow) {
Hide();
-@@ -4752,6 +6037,22 @@
+@@ -4752,6 +6046,22 @@
return osContinue;
}
bool DoShowMode = true;
@@ -5108,7 +5147,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
switch (Key) {
// Positioning:
case kPlay:
-@@ -4769,25 +6070,73 @@
+@@ -4769,25 +6079,73 @@
case kFastFwd:
case kRight: Forward(); break;
case kRed: TimeSearch(); break;
@@ -5184,7 +5223,7 @@ diff -ruN vdr-1.7.14/menu.c vdr-1.7.14.ExtP_NG/menu.c
case kMarkMoveBack|k_Repeat:
diff -ruN vdr-1.7.14/menu.h vdr-1.7.14.ExtP_NG/menu.h
--- vdr-1.7.14/menu.h 2010-03-06 17:15:59.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/menu.h 2010-03-14 14:02:05.457983500 +0100
++++ vdr-1.7.14.ExtP_NG/menu.h 2010-04-10 15:45:10.646737633 +0200
@@ -18,6 +18,9 @@
#include "menuitems.h"
#include "recorder.h"
@@ -5308,7 +5347,7 @@ diff -ruN vdr-1.7.14/menu.h vdr-1.7.14.ExtP_NG/menu.h
int timeSearchTime, timeSearchPos;
diff -ruN vdr-1.7.14/menuitems.c vdr-1.7.14.ExtP_NG/menuitems.c
--- vdr-1.7.14/menuitems.c 2010-02-16 15:44:35.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/menuitems.c 2010-03-14 14:02:05.473983960 +0100
++++ vdr-1.7.14.ExtP_NG/menuitems.c 2010-04-10 15:45:10.665741204 +0200
@@ -33,9 +33,21 @@
free(name);
}
@@ -5357,7 +5396,7 @@ diff -ruN vdr-1.7.14/menuitems.c vdr-1.7.14.ExtP_NG/menuitems.c
// --- cMenuEditChanItem -----------------------------------------------------
diff -ruN vdr-1.7.14/menuitems.h vdr-1.7.14.ExtP_NG/menuitems.h
--- vdr-1.7.14/menuitems.h 2010-02-21 14:58:21.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/menuitems.h 2010-03-14 14:02:05.480986531 +0100
++++ vdr-1.7.14.ExtP_NG/menuitems.h 2010-04-10 15:45:10.674735161 +0200
@@ -22,7 +22,11 @@
public:
cMenuEditItem(const char *Name);
@@ -5382,7 +5421,7 @@ diff -ruN vdr-1.7.14/menuitems.h vdr-1.7.14.ExtP_NG/menuitems.h
#endif //__MENUITEMS_H
diff -ruN vdr-1.7.14/menuorgpatch.h vdr-1.7.14.ExtP_NG/menuorgpatch.h
--- vdr-1.7.14/menuorgpatch.h 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/menuorgpatch.h 2010-03-14 14:02:05.491985437 +0100
++++ vdr-1.7.14.ExtP_NG/menuorgpatch.h 2010-04-10 15:45:10.686736443 +0200
@@ -0,0 +1,102 @@
+#ifdef USE_MENUORG
+/*
@@ -5488,7 +5527,7 @@ diff -ruN vdr-1.7.14/menuorgpatch.h vdr-1.7.14.ExtP_NG/menuorgpatch.h
+#endif /* MENUORG */
diff -ruN vdr-1.7.14/osd.c vdr-1.7.14.ExtP_NG/osd.c
--- vdr-1.7.14/osd.c 2010-01-22 16:58:39.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/osd.c 2010-03-14 14:02:05.544981887 +0100
++++ vdr-1.7.14.ExtP_NG/osd.c 2010-04-10 15:45:10.754742652 +0200
@@ -743,6 +743,9 @@
int cOsd::osdWidth = 0;
int cOsd::osdHeight = 0;
@@ -5511,7 +5550,7 @@ diff -ruN vdr-1.7.14/osd.c vdr-1.7.14.ExtP_NG/osd.c
Osds.Insert(this, i);
diff -ruN vdr-1.7.14/osd.h vdr-1.7.14.ExtP_NG/osd.h
--- vdr-1.7.14/osd.h 2010-01-17 14:23:50.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/osd.h 2010-03-14 14:02:05.556982734 +0100
++++ vdr-1.7.14.ExtP_NG/osd.h 2010-04-10 15:45:10.767741548 +0200
@@ -270,6 +270,10 @@
int left, top, width, height;
uint level;
@@ -5535,7 +5574,7 @@ diff -ruN vdr-1.7.14/osd.h vdr-1.7.14.ExtP_NG/osd.h
class cOsdProvider {
diff -ruN vdr-1.7.14/osdbase.c vdr-1.7.14.ExtP_NG/osdbase.c
--- vdr-1.7.14/osdbase.c 2010-01-17 12:36:12.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/osdbase.c 2010-03-14 14:02:05.566981514 +0100
++++ vdr-1.7.14.ExtP_NG/osdbase.c 2010-04-10 15:45:10.777738279 +0200
@@ -22,6 +22,9 @@
state = State;
selectable = true;
@@ -5708,7 +5747,7 @@ diff -ruN vdr-1.7.14/osdbase.c vdr-1.7.14.ExtP_NG/osdbase.c
case kDown|k_Repeat:
diff -ruN vdr-1.7.14/osdbase.h vdr-1.7.14.ExtP_NG/osdbase.h
--- vdr-1.7.14/osdbase.h 2010-01-16 15:25:31.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/osdbase.h 2010-03-14 14:02:05.587980830 +0100
++++ vdr-1.7.14.ExtP_NG/osdbase.h 2010-04-10 15:45:10.786736628 +0200
@@ -15,6 +15,10 @@
#include "skins.h"
#include "tools.h"
@@ -5770,7 +5809,7 @@ diff -ruN vdr-1.7.14/osdbase.h vdr-1.7.14.ExtP_NG/osdbase.h
#endif //__OSDBASE_H
diff -ruN vdr-1.7.14/pat.c vdr-1.7.14.ExtP_NG/pat.c
--- vdr-1.7.14/pat.c 2010-03-06 13:00:30.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/pat.c 2010-03-14 14:02:05.598984754 +0100
++++ vdr-1.7.14.ExtP_NG/pat.c 2010-04-10 15:45:10.802741457 +0200
@@ -13,6 +13,9 @@
#include "libsi/section.h"
#include "libsi/descriptor.h"
@@ -5822,7 +5861,7 @@ diff -ruN vdr-1.7.14/pat.c vdr-1.7.14.ExtP_NG/pat.c
SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
diff -ruN vdr-1.7.14/plugin.c vdr-1.7.14.ExtP_NG/plugin.c
--- vdr-1.7.14/plugin.c 2010-01-06 12:36:46.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/plugin.c 2010-03-14 14:02:05.642980882 +0100
++++ vdr-1.7.14.ExtP_NG/plugin.c 2010-04-10 15:45:10.848736538 +0200
@@ -317,6 +317,14 @@
char *p = strchr(s, ' ');
if (p)
@@ -5852,7 +5891,7 @@ diff -ruN vdr-1.7.14/plugin.c vdr-1.7.14.ExtP_NG/plugin.c
}
diff -ruN vdr-1.7.14/plugin.h vdr-1.7.14.ExtP_NG/plugin.h
--- vdr-1.7.14/plugin.h 2007-08-04 11:56:26.000000000 +0200
-+++ vdr-1.7.14.ExtP_NG/plugin.h 2010-03-14 14:02:05.651981254 +0100
++++ vdr-1.7.14.ExtP_NG/plugin.h 2010-04-10 15:45:10.861747307 +0200
@@ -45,6 +45,9 @@
virtual const char *MainMenuEntry(void);
@@ -5865,7 +5904,7 @@ diff -ruN vdr-1.7.14/plugin.h vdr-1.7.14.ExtP_NG/plugin.h
virtual bool SetupParse(const char *Name, const char *Value);
diff -ruN vdr-1.7.14/po/ca_ES.po vdr-1.7.14.ExtP_NG/po/ca_ES.po
--- vdr-1.7.14/po/ca_ES.po 2010-03-12 17:41:03.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/ca_ES.po 2010-03-14 14:02:06.837965477 +0100
++++ vdr-1.7.14.ExtP_NG/po/ca_ES.po 2010-04-10 15:45:12.406741501 +0200
@@ -39,6 +39,9 @@
msgid "none"
msgstr "cap"
@@ -5888,7 +5927,7 @@ diff -ruN vdr-1.7.14/po/ca_ES.po vdr-1.7.14.ExtP_NG/po/ca_ES.po
diff -ruN vdr-1.7.14/po/cs_CZ.po vdr-1.7.14.ExtP_NG/po/cs_CZ.po
--- vdr-1.7.14/po/cs_CZ.po 2010-03-12 17:41:03.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/cs_CZ.po 2010-03-14 14:02:06.849965394 +0100
++++ vdr-1.7.14.ExtP_NG/po/cs_CZ.po 2010-04-10 15:45:12.435736716 +0200
@@ -37,6 +37,9 @@
msgid "none"
msgstr "¾ádný"
@@ -5911,7 +5950,7 @@ diff -ruN vdr-1.7.14/po/cs_CZ.po vdr-1.7.14.ExtP_NG/po/cs_CZ.po
diff -ruN vdr-1.7.14/po/da_DK.po vdr-1.7.14.ExtP_NG/po/da_DK.po
--- vdr-1.7.14/po/da_DK.po 2010-03-12 17:41:03.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/da_DK.po 2010-03-14 14:02:06.862968584 +0100
++++ vdr-1.7.14.ExtP_NG/po/da_DK.po 2010-04-10 15:45:12.467742106 +0200
@@ -36,6 +36,9 @@
msgid "none"
msgstr "ingen"
@@ -5934,7 +5973,7 @@ diff -ruN vdr-1.7.14/po/da_DK.po vdr-1.7.14.ExtP_NG/po/da_DK.po
diff -ruN vdr-1.7.14/po/de_DE.po vdr-1.7.14.ExtP_NG/po/de_DE.po
--- vdr-1.7.14/po/de_DE.po 2010-03-12 17:41:03.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/de_DE.po 2010-03-14 14:02:06.874967630 +0100
++++ vdr-1.7.14.ExtP_NG/po/de_DE.po 2010-04-10 15:45:12.504739261 +0200
@@ -36,6 +36,9 @@
msgid "none"
msgstr "keine"
@@ -6079,7 +6118,7 @@ diff -ruN vdr-1.7.14/po/de_DE.po vdr-1.7.14.ExtP_NG/po/de_DE.po
+msgstr "Kindersicherung"
diff -ruN vdr-1.7.14/po/el_GR.po vdr-1.7.14.ExtP_NG/po/el_GR.po
--- vdr-1.7.14/po/el_GR.po 2010-03-12 17:41:03.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/el_GR.po 2010-03-14 14:02:06.887969497 +0100
++++ vdr-1.7.14.ExtP_NG/po/el_GR.po 2010-04-10 15:45:12.539740766 +0200
@@ -36,6 +36,9 @@
msgid "none"
msgstr "êáíÝíá"
@@ -6102,7 +6141,7 @@ diff -ruN vdr-1.7.14/po/el_GR.po vdr-1.7.14.ExtP_NG/po/el_GR.po
diff -ruN vdr-1.7.14/po/es_ES.po vdr-1.7.14.ExtP_NG/po/es_ES.po
--- vdr-1.7.14/po/es_ES.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/es_ES.po 2010-03-14 14:02:06.899966033 +0100
++++ vdr-1.7.14.ExtP_NG/po/es_ES.po 2010-04-10 15:45:12.568741800 +0200
@@ -37,6 +37,9 @@
msgid "none"
msgstr "ninguno"
@@ -6125,7 +6164,7 @@ diff -ruN vdr-1.7.14/po/es_ES.po vdr-1.7.14.ExtP_NG/po/es_ES.po
diff -ruN vdr-1.7.14/po/et_EE.po vdr-1.7.14.ExtP_NG/po/et_EE.po
--- vdr-1.7.14/po/et_EE.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/et_EE.po 2010-03-14 14:02:06.912967810 +0100
++++ vdr-1.7.14.ExtP_NG/po/et_EE.po 2010-04-10 15:45:12.591740207 +0200
@@ -36,6 +36,9 @@
msgid "none"
msgstr "puudu"
@@ -6194,7 +6233,7 @@ diff -ruN vdr-1.7.14/po/et_EE.po vdr-1.7.14.ExtP_NG/po/et_EE.po
+msgstr "Salvestuse kestus"
diff -ruN vdr-1.7.14/po/fi_FI.po vdr-1.7.14.ExtP_NG/po/fi_FI.po
--- vdr-1.7.14/po/fi_FI.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/fi_FI.po 2010-03-14 14:02:06.924963607 +0100
++++ vdr-1.7.14.ExtP_NG/po/fi_FI.po 2010-04-10 15:45:12.620745376 +0200
@@ -39,6 +39,9 @@
msgid "none"
msgstr "tyhjä"
@@ -6266,7 +6305,7 @@ diff -ruN vdr-1.7.14/po/fi_FI.po vdr-1.7.14.ExtP_NG/po/fi_FI.po
+msgstr "Parametrit"
diff -ruN vdr-1.7.14/po/fr_FR.po vdr-1.7.14.ExtP_NG/po/fr_FR.po
--- vdr-1.7.14/po/fr_FR.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/fr_FR.po 2010-03-14 14:02:06.937969373 +0100
++++ vdr-1.7.14.ExtP_NG/po/fr_FR.po 2010-04-10 15:45:12.650738547 +0200
@@ -42,6 +42,9 @@
msgid "none"
msgstr "aucun"
@@ -6376,7 +6415,7 @@ diff -ruN vdr-1.7.14/po/fr_FR.po vdr-1.7.14.ExtP_NG/po/fr_FR.po
+msgstr "Paramètres"
diff -ruN vdr-1.7.14/po/hr_HR.po vdr-1.7.14.ExtP_NG/po/hr_HR.po
--- vdr-1.7.14/po/hr_HR.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/hr_HR.po 2010-03-14 14:02:06.949965110 +0100
++++ vdr-1.7.14.ExtP_NG/po/hr_HR.po 2010-04-10 15:45:12.676740196 +0200
@@ -38,6 +38,9 @@
msgid "none"
msgstr "ni¹ta"
@@ -6399,7 +6438,7 @@ diff -ruN vdr-1.7.14/po/hr_HR.po vdr-1.7.14.ExtP_NG/po/hr_HR.po
diff -ruN vdr-1.7.14/po/hu_HU.po vdr-1.7.14.ExtP_NG/po/hu_HU.po
--- vdr-1.7.14/po/hu_HU.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/hu_HU.po 2010-03-14 14:02:06.961966317 +0100
++++ vdr-1.7.14.ExtP_NG/po/hu_HU.po 2010-04-10 15:45:12.697741656 +0200
@@ -39,6 +39,9 @@
msgid "none"
msgstr "semmi"
@@ -6422,7 +6461,7 @@ diff -ruN vdr-1.7.14/po/hu_HU.po vdr-1.7.14.ExtP_NG/po/hu_HU.po
diff -ruN vdr-1.7.14/po/it_IT.po vdr-1.7.14.ExtP_NG/po/it_IT.po
--- vdr-1.7.14/po/it_IT.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/it_IT.po 2010-03-14 14:02:06.973964558 +0100
++++ vdr-1.7.14.ExtP_NG/po/it_IT.po 2010-04-10 15:45:12.723739553 +0200
@@ -43,6 +43,9 @@
msgid "none"
msgstr "nessuno"
@@ -6445,7 +6484,7 @@ diff -ruN vdr-1.7.14/po/it_IT.po vdr-1.7.14.ExtP_NG/po/it_IT.po
diff -ruN vdr-1.7.14/po/nl_NL.po vdr-1.7.14.ExtP_NG/po/nl_NL.po
--- vdr-1.7.14/po/nl_NL.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/nl_NL.po 2010-03-14 14:02:06.998963388 +0100
++++ vdr-1.7.14.ExtP_NG/po/nl_NL.po 2010-04-10 15:45:12.785740553 +0200
@@ -40,6 +40,9 @@
msgid "none"
msgstr "geen"
@@ -6468,7 +6507,7 @@ diff -ruN vdr-1.7.14/po/nl_NL.po vdr-1.7.14.ExtP_NG/po/nl_NL.po
diff -ruN vdr-1.7.14/po/nn_NO.po vdr-1.7.14.ExtP_NG/po/nn_NO.po
--- vdr-1.7.14/po/nn_NO.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/nn_NO.po 2010-03-14 14:02:07.009962551 +0100
++++ vdr-1.7.14.ExtP_NG/po/nn_NO.po 2010-04-10 15:45:12.809737908 +0200
@@ -37,6 +37,9 @@
msgid "none"
msgstr ""
@@ -6491,7 +6530,7 @@ diff -ruN vdr-1.7.14/po/nn_NO.po vdr-1.7.14.ExtP_NG/po/nn_NO.po
diff -ruN vdr-1.7.14/po/pl_PL.po vdr-1.7.14.ExtP_NG/po/pl_PL.po
--- vdr-1.7.14/po/pl_PL.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/pl_PL.po 2010-03-14 14:02:07.023967981 +0100
++++ vdr-1.7.14.ExtP_NG/po/pl_PL.po 2010-04-10 15:45:12.835738610 +0200
@@ -37,6 +37,9 @@
msgid "none"
msgstr "brak"
@@ -6514,7 +6553,7 @@ diff -ruN vdr-1.7.14/po/pl_PL.po vdr-1.7.14.ExtP_NG/po/pl_PL.po
diff -ruN vdr-1.7.14/po/pt_PT.po vdr-1.7.14.ExtP_NG/po/pt_PT.po
--- vdr-1.7.14/po/pt_PT.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/pt_PT.po 2010-03-14 14:02:07.035967399 +0100
++++ vdr-1.7.14.ExtP_NG/po/pt_PT.po 2010-04-10 15:45:12.870742039 +0200
@@ -36,6 +36,9 @@
msgid "none"
msgstr "nenhum"
@@ -6537,7 +6576,7 @@ diff -ruN vdr-1.7.14/po/pt_PT.po vdr-1.7.14.ExtP_NG/po/pt_PT.po
diff -ruN vdr-1.7.14/po/ro_RO.po vdr-1.7.14.ExtP_NG/po/ro_RO.po
--- vdr-1.7.14/po/ro_RO.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/ro_RO.po 2010-03-14 14:02:07.049963749 +0100
++++ vdr-1.7.14.ExtP_NG/po/ro_RO.po 2010-04-10 15:45:12.889740524 +0200
@@ -39,6 +39,9 @@
msgid "none"
msgstr "niciuna(ul)"
@@ -6560,7 +6599,7 @@ diff -ruN vdr-1.7.14/po/ro_RO.po vdr-1.7.14.ExtP_NG/po/ro_RO.po
diff -ruN vdr-1.7.14/po/ru_RU.po vdr-1.7.14.ExtP_NG/po/ru_RU.po
--- vdr-1.7.14/po/ru_RU.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/ru_RU.po 2010-03-14 14:02:07.062965052 +0100
++++ vdr-1.7.14.ExtP_NG/po/ru_RU.po 2010-04-10 15:45:12.921738012 +0200
@@ -37,6 +37,9 @@
msgid "none"
msgstr "ÝØçÕÓÞ"
@@ -6629,7 +6668,7 @@ diff -ruN vdr-1.7.14/po/ru_RU.po vdr-1.7.14.ExtP_NG/po/ru_RU.po
+msgstr "¿ÞÚÐ×ëÒÐâì ßàÞÔÞÛÖØâÕÛìÝÞáâì ×ÐߨáØ"
diff -ruN vdr-1.7.14/po/sl_SI.po vdr-1.7.14.ExtP_NG/po/sl_SI.po
--- vdr-1.7.14/po/sl_SI.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/sl_SI.po 2010-03-14 14:02:07.086965624 +0100
++++ vdr-1.7.14.ExtP_NG/po/sl_SI.po 2010-04-10 15:45:12.971742442 +0200
@@ -37,6 +37,9 @@
msgid "none"
msgstr "nobeden"
@@ -6652,7 +6691,7 @@ diff -ruN vdr-1.7.14/po/sl_SI.po vdr-1.7.14.ExtP_NG/po/sl_SI.po
diff -ruN vdr-1.7.14/po/sv_SE.po vdr-1.7.14.ExtP_NG/po/sv_SE.po
--- vdr-1.7.14/po/sv_SE.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/sv_SE.po 2010-03-14 14:02:07.099963332 +0100
++++ vdr-1.7.14.ExtP_NG/po/sv_SE.po 2010-04-10 15:45:12.996740450 +0200
@@ -39,6 +39,9 @@
msgid "none"
msgstr "ingen"
@@ -6675,7 +6714,7 @@ diff -ruN vdr-1.7.14/po/sv_SE.po vdr-1.7.14.ExtP_NG/po/sv_SE.po
diff -ruN vdr-1.7.14/po/tr_TR.po vdr-1.7.14.ExtP_NG/po/tr_TR.po
--- vdr-1.7.14/po/tr_TR.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/tr_TR.po 2010-03-14 14:02:07.113963530 +0100
++++ vdr-1.7.14.ExtP_NG/po/tr_TR.po 2010-04-10 15:45:13.021741692 +0200
@@ -36,6 +36,9 @@
msgid "none"
msgstr "hiç"
@@ -6698,7 +6737,7 @@ diff -ruN vdr-1.7.14/po/tr_TR.po vdr-1.7.14.ExtP_NG/po/tr_TR.po
diff -ruN vdr-1.7.14/po/uk_UA.po vdr-1.7.14.ExtP_NG/po/uk_UA.po
--- vdr-1.7.14/po/uk_UA.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/uk_UA.po 2010-03-14 14:02:07.128967016 +0100
++++ vdr-1.7.14.ExtP_NG/po/uk_UA.po 2010-04-10 15:45:13.061743013 +0200
@@ -36,6 +36,9 @@
msgid "none"
msgstr "нічого"
@@ -6721,7 +6760,7 @@ diff -ruN vdr-1.7.14/po/uk_UA.po vdr-1.7.14.ExtP_NG/po/uk_UA.po
diff -ruN vdr-1.7.14/po/zh_CN.po vdr-1.7.14.ExtP_NG/po/zh_CN.po
--- vdr-1.7.14/po/zh_CN.po 2010-03-12 17:41:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/po/zh_CN.po 2010-03-14 14:02:07.141965253 +0100
++++ vdr-1.7.14.ExtP_NG/po/zh_CN.po 2010-04-10 15:45:13.092741645 +0200
@@ -39,6 +39,9 @@
msgid "none"
msgstr "æ— "
@@ -6744,7 +6783,7 @@ diff -ruN vdr-1.7.14/po/zh_CN.po vdr-1.7.14.ExtP_NG/po/zh_CN.po
diff -ruN vdr-1.7.14/receiver.c vdr-1.7.14.ExtP_NG/receiver.c
--- vdr-1.7.14/receiver.c 2010-02-28 15:25:32.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/receiver.c 2010-03-14 14:02:05.740980502 +0100
++++ vdr-1.7.14.ExtP_NG/receiver.c 2010-04-10 15:45:11.056767270 +0200
@@ -82,7 +82,12 @@
(Channel->Ppid() == Channel->Vpid() || AddPid(Channel->Ppid())) &&
AddPids(Channel->Apids()) &&
@@ -6760,7 +6799,7 @@ diff -ruN vdr-1.7.14/receiver.c vdr-1.7.14.ExtP_NG/receiver.c
}
diff -ruN vdr-1.7.14/recorder.c vdr-1.7.14.ExtP_NG/recorder.c
--- vdr-1.7.14/recorder.c 2010-01-29 17:37:22.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/recorder.c 2010-03-14 14:02:05.765979560 +0100
++++ vdr-1.7.14.ExtP_NG/recorder.c 2010-04-10 15:45:11.083738273 +0200
@@ -87,7 +87,11 @@
bool cRecorder::NextFile(void)
{
@@ -6775,7 +6814,7 @@ diff -ruN vdr-1.7.14/recorder.c vdr-1.7.14.ExtP_NG/recorder.c
}
diff -ruN vdr-1.7.14/recording.c vdr-1.7.14.ExtP_NG/recording.c
--- vdr-1.7.14/recording.c 2010-03-07 15:06:04.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/recording.c 2010-03-14 14:02:05.802979054 +0100
++++ vdr-1.7.14.ExtP_NG/recording.c 2010-04-10 15:45:11.124740932 +0200
@@ -8,6 +8,9 @@
*/
@@ -7311,7 +7350,7 @@ diff -ruN vdr-1.7.14/recording.c vdr-1.7.14.ExtP_NG/recording.c
+#endif /* DVLFRIENDLYFNAMES */
diff -ruN vdr-1.7.14/recording.h vdr-1.7.14.ExtP_NG/recording.h
--- vdr-1.7.14/recording.h 2010-03-07 15:06:15.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/recording.h 2010-03-14 14:02:05.812978705 +0100
++++ vdr-1.7.14.ExtP_NG/recording.h 2010-04-10 15:45:11.134737044 +0200
@@ -23,6 +23,9 @@
#define TIMERMACRO_EPISODE "EPISODE"
@@ -7486,7 +7525,7 @@ diff -ruN vdr-1.7.14/recording.h vdr-1.7.14.ExtP_NG/recording.h
diff -ruN vdr-1.7.14/remux.c vdr-1.7.14.ExtP_NG/remux.c
--- vdr-1.7.14/remux.c 2010-02-28 15:42:07.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/remux.c 2010-03-14 14:02:05.853979909 +0100
++++ vdr-1.7.14.ExtP_NG/remux.c 2010-04-10 15:45:11.187744435 +0200
@@ -215,6 +215,32 @@
return i;
}
@@ -7604,7 +7643,7 @@ diff -ruN vdr-1.7.14/remux.c vdr-1.7.14.ExtP_NG/remux.c
dbgpatpmt(" '%s'", ld->languageCode);
diff -ruN vdr-1.7.14/remux.h vdr-1.7.14.ExtP_NG/remux.h
--- vdr-1.7.14/remux.h 2010-01-29 17:51:26.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/remux.h 2010-03-14 14:02:05.863978011 +0100
++++ vdr-1.7.14.ExtP_NG/remux.h 2010-04-10 15:45:11.199740063 +0200
@@ -170,6 +170,9 @@
int MakeStream(uchar *Target, uchar Type, int Pid);
int MakeAC3Descriptor(uchar *Target);
@@ -7661,7 +7700,7 @@ diff -ruN vdr-1.7.14/remux.h vdr-1.7.14.ExtP_NG/remux.h
// TS to PES converter:
diff -ruN vdr-1.7.14/sections.c vdr-1.7.14.ExtP_NG/sections.c
--- vdr-1.7.14/sections.c 2007-10-14 14:52:07.000000000 +0200
-+++ vdr-1.7.14.ExtP_NG/sections.c 2010-03-14 14:02:05.930976827 +0100
++++ vdr-1.7.14.ExtP_NG/sections.c 2010-04-10 15:45:11.300735330 +0200
@@ -198,7 +198,11 @@
if (fh) {
// Read section data:
@@ -7676,7 +7715,7 @@ diff -ruN vdr-1.7.14/sections.c vdr-1.7.14.ExtP_NG/sections.c
if (r > 3) { // minimum number of bytes necessary to get section length
diff -ruN vdr-1.7.14/skins.c vdr-1.7.14.ExtP_NG/skins.c
--- vdr-1.7.14/skins.c 2009-06-06 17:12:31.000000000 +0200
-+++ vdr-1.7.14.ExtP_NG/skins.c 2010-03-14 14:02:06.000978845 +0100
++++ vdr-1.7.14.ExtP_NG/skins.c 2010-04-10 15:45:11.417738004 +0200
@@ -238,7 +238,11 @@
}
cSkinDisplay::Current()->SetMessage(Type, s);
@@ -7703,7 +7742,7 @@ diff -ruN vdr-1.7.14/skins.c vdr-1.7.14.ExtP_NG/skins.c
else if (!s && displayMessage) {
diff -ruN vdr-1.7.14/status.c vdr-1.7.14.ExtP_NG/status.c
--- vdr-1.7.14/status.c 2008-02-16 15:46:31.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/status.c 2010-03-14 14:02:06.131975916 +0100
++++ vdr-1.7.14.ExtP_NG/status.c 2010-04-10 15:45:11.565737350 +0200
@@ -83,6 +83,17 @@
sm->OsdTitle(Title);
}
@@ -7813,7 +7852,7 @@ diff -ruN vdr-1.7.14/status.c vdr-1.7.14.ExtP_NG/status.c
+#endif /* PINPLUGIN */
diff -ruN vdr-1.7.14/status.h vdr-1.7.14.ExtP_NG/status.h
--- vdr-1.7.14/status.h 2008-02-16 16:00:33.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/status.h 2010-03-14 14:02:06.143975475 +0100
++++ vdr-1.7.14.ExtP_NG/status.h 2010-04-10 15:45:11.579737535 +0200
@@ -14,6 +14,9 @@
#include "device.h"
#include "player.h"
@@ -7911,7 +7950,7 @@ diff -ruN vdr-1.7.14/status.h vdr-1.7.14.ExtP_NG/status.h
#endif //__STATUS_H
diff -ruN vdr-1.7.14/submenu.c vdr-1.7.14.ExtP_NG/submenu.c
--- vdr-1.7.14/submenu.c 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/submenu.c 2010-03-14 14:02:06.157974094 +0100
++++ vdr-1.7.14.ExtP_NG/submenu.c 2010-04-10 15:45:11.593741591 +0200
@@ -0,0 +1,949 @@
+#ifdef USE_SETUP
+/****************************************************************************
@@ -8864,7 +8903,7 @@ diff -ruN vdr-1.7.14/submenu.c vdr-1.7.14.ExtP_NG/submenu.c
+#endif /* SETUP */
diff -ruN vdr-1.7.14/submenu.h vdr-1.7.14.ExtP_NG/submenu.h
--- vdr-1.7.14/submenu.h 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/submenu.h 2010-03-14 14:02:06.165975085 +0100
++++ vdr-1.7.14.ExtP_NG/submenu.h 2010-04-10 15:45:11.601737459 +0200
@@ -0,0 +1,159 @@
+#ifdef USE_SETUP
+/****************************************************************************
@@ -9027,7 +9066,7 @@ diff -ruN vdr-1.7.14/submenu.h vdr-1.7.14.ExtP_NG/submenu.h
+#endif /* SETUP */
diff -ruN vdr-1.7.14/svdrp.c vdr-1.7.14.ExtP_NG/svdrp.c
--- vdr-1.7.14/svdrp.c 2010-01-17 13:23:31.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/svdrp.c 2010-03-14 14:02:06.200977291 +0100
++++ vdr-1.7.14.ExtP_NG/svdrp.c 2010-04-10 15:45:11.637740914 +0200
@@ -304,6 +304,10 @@
"REMO [ on | off ]\n"
" Turns the remote control on or off. Without a parameter, the current\n"
@@ -9090,7 +9129,7 @@ diff -ruN vdr-1.7.14/svdrp.c vdr-1.7.14.ExtP_NG/svdrp.c
else if (CMD("UPDT")) CmdUPDT(s);
diff -ruN vdr-1.7.14/svdrp.h vdr-1.7.14.ExtP_NG/svdrp.h
--- vdr-1.7.14/svdrp.h 2007-04-30 14:28:28.000000000 +0200
-+++ vdr-1.7.14.ExtP_NG/svdrp.h 2010-03-14 14:02:06.207974563 +0100
++++ vdr-1.7.14.ExtP_NG/svdrp.h 2010-04-10 15:45:11.646738241 +0200
@@ -79,6 +79,9 @@
void CmdPLUG(const char *Option);
void CmdPUTE(const char *Option);
@@ -9103,7 +9142,7 @@ diff -ruN vdr-1.7.14/svdrp.h vdr-1.7.14.ExtP_NG/svdrp.h
void CmdUPDT(const char *Option);
diff -ruN vdr-1.7.14/timers.c vdr-1.7.14.ExtP_NG/timers.c
--- vdr-1.7.14/timers.c 2010-01-16 12:18:53.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/timers.c 2010-03-14 14:02:06.311972154 +0100
++++ vdr-1.7.14.ExtP_NG/timers.c 2010-04-10 15:45:11.733740689 +0200
@@ -46,6 +46,9 @@
stop -= 2400;
priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
@@ -9194,7 +9233,7 @@ diff -ruN vdr-1.7.14/timers.c vdr-1.7.14.ExtP_NG/timers.c
cTimers Timers;
diff -ruN vdr-1.7.14/timers.h vdr-1.7.14.ExtP_NG/timers.h
--- vdr-1.7.14/timers.h 2008-02-16 15:33:23.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/timers.h 2010-03-14 14:02:06.323973088 +0100
++++ vdr-1.7.14.ExtP_NG/timers.h 2010-04-10 15:45:11.743736984 +0200
@@ -37,6 +37,9 @@
int start;
int stop;
@@ -9227,7 +9266,7 @@ diff -ruN vdr-1.7.14/timers.h vdr-1.7.14.ExtP_NG/timers.h
bool HasFlags(uint Flags) const;
diff -ruN vdr-1.7.14/tinystr.c vdr-1.7.14.ExtP_NG/tinystr.c
--- vdr-1.7.14/tinystr.c 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/tinystr.c 2010-03-14 14:02:06.341972566 +0100
++++ vdr-1.7.14.ExtP_NG/tinystr.c 2010-04-10 15:45:11.756737883 +0200
@@ -0,0 +1,301 @@
+#ifdef USE_SETUP
+/*
@@ -9532,7 +9571,7 @@ diff -ruN vdr-1.7.14/tinystr.c vdr-1.7.14.ExtP_NG/tinystr.c
+#endif /* SETUP */
diff -ruN vdr-1.7.14/tinystr.h vdr-1.7.14.ExtP_NG/tinystr.h
--- vdr-1.7.14/tinystr.h 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/tinystr.h 2010-03-14 14:02:06.352972566 +0100
++++ vdr-1.7.14.ExtP_NG/tinystr.h 2010-04-10 15:45:11.768736847 +0200
@@ -0,0 +1,244 @@
+#ifdef USE_SETUP
+/*
@@ -9780,7 +9819,7 @@ diff -ruN vdr-1.7.14/tinystr.h vdr-1.7.14.ExtP_NG/tinystr.h
+#endif /* SETUP */
diff -ruN vdr-1.7.14/tinyxml.c vdr-1.7.14.ExtP_NG/tinyxml.c
--- vdr-1.7.14/tinyxml.c 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/tinyxml.c 2010-03-14 14:02:06.368981995 +0100
++++ vdr-1.7.14.ExtP_NG/tinyxml.c 2010-04-10 15:45:11.784736808 +0200
@@ -0,0 +1,1429 @@
+#ifdef USE_SETUP
+/*
@@ -11213,7 +11252,7 @@ diff -ruN vdr-1.7.14/tinyxml.c vdr-1.7.14.ExtP_NG/tinyxml.c
+#endif /* SETUP */
diff -ruN vdr-1.7.14/tinyxml.h vdr-1.7.14.ExtP_NG/tinyxml.h
--- vdr-1.7.14/tinyxml.h 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/tinyxml.h 2010-03-14 14:02:06.398975605 +0100
++++ vdr-1.7.14.ExtP_NG/tinyxml.h 2010-04-10 15:45:11.801740955 +0200
@@ -0,0 +1,1372 @@
+#ifdef USE_SETUP
+/*
@@ -12589,7 +12628,7 @@ diff -ruN vdr-1.7.14/tinyxml.h vdr-1.7.14.ExtP_NG/tinyxml.h
+#endif /* SETUP */
diff -ruN vdr-1.7.14/tinyxmlerror.c vdr-1.7.14.ExtP_NG/tinyxmlerror.c
--- vdr-1.7.14/tinyxmlerror.c 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/tinyxmlerror.c 2010-03-14 14:02:06.413972412 +0100
++++ vdr-1.7.14.ExtP_NG/tinyxmlerror.c 2010-04-10 15:45:11.808773087 +0200
@@ -0,0 +1,53 @@
+#ifdef USE_SETUP
+/*
@@ -12646,7 +12685,7 @@ diff -ruN vdr-1.7.14/tinyxmlerror.c vdr-1.7.14.ExtP_NG/tinyxmlerror.c
+#endif /* SETUP */
diff -ruN vdr-1.7.14/tinyxmlparser.c vdr-1.7.14.ExtP_NG/tinyxmlparser.c
--- vdr-1.7.14/tinyxmlparser.c 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/tinyxmlparser.c 2010-03-14 14:02:06.429972930 +0100
++++ vdr-1.7.14.ExtP_NG/tinyxmlparser.c 2010-04-10 15:45:11.828740629 +0200
@@ -0,0 +1,1494 @@
+#ifdef USE_SETUP
+/*
@@ -14144,7 +14183,7 @@ diff -ruN vdr-1.7.14/tinyxmlparser.c vdr-1.7.14.ExtP_NG/tinyxmlparser.c
+#endif /* SETUP */
diff -ruN vdr-1.7.14/vdr.c vdr-1.7.14.ExtP_NG/vdr.c
--- vdr-1.7.14/vdr.c 2010-02-21 15:08:09.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/vdr.c 2010-03-14 14:02:06.592971751 +0100
++++ vdr-1.7.14.ExtP_NG/vdr.c 2010-04-10 15:45:12.079739794 +0200
@@ -587,7 +587,12 @@
RecordingCommands.Load(AddDirectory(ConfigDirectory, "reccmds.conf"));
SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true);
@@ -14252,7 +14291,7 @@ diff -ruN vdr-1.7.14/vdr.c vdr-1.7.14.ExtP_NG/vdr.c
}
diff -ruN vdr-1.7.14/vdrttxtsubshooks.c vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.c
--- vdr-1.7.14/vdrttxtsubshooks.c 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.c 2010-03-14 14:02:06.599969246 +0100
++++ vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.c 2010-04-10 15:45:12.093736132 +0200
@@ -0,0 +1,62 @@
+/*
+ * vdr-ttxtsubs - A plugin for the Linux Video Disk Recorder
@@ -14318,7 +14357,7 @@ diff -ruN vdr-1.7.14/vdrttxtsubshooks.c vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.c
+}
diff -ruN vdr-1.7.14/vdrttxtsubshooks.h vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.h
--- vdr-1.7.14/vdrttxtsubshooks.h 1970-01-01 01:00:00.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.h 2010-03-14 14:02:06.613969643 +0100
++++ vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.h 2010-04-10 15:45:12.108759292 +0200
@@ -0,0 +1,46 @@
+/*
+ * vdr-ttxtsubs - A plugin for the Linux Video Disk Recorder
@@ -14368,7 +14407,7 @@ diff -ruN vdr-1.7.14/vdrttxtsubshooks.h vdr-1.7.14.ExtP_NG/vdrttxtsubshooks.h
+#endif
diff -ruN vdr-1.7.14/videodir.c vdr-1.7.14.ExtP_NG/videodir.c
--- vdr-1.7.14/videodir.c 2008-02-16 14:00:03.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/videodir.c 2010-03-14 14:02:06.627969737 +0100
++++ vdr-1.7.14.ExtP_NG/videodir.c 2010-04-10 15:45:12.125736672 +0200
@@ -36,6 +36,11 @@
bool Next(void);
void Store(void);
@@ -14672,7 +14711,7 @@ diff -ruN vdr-1.7.14/videodir.c vdr-1.7.14.ExtP_NG/videodir.c
cVideoDirectory Dir;
diff -ruN vdr-1.7.14/videodir.h vdr-1.7.14.ExtP_NG/videodir.h
--- vdr-1.7.14/videodir.h 2008-02-16 13:53:11.000000000 +0100
-+++ vdr-1.7.14.ExtP_NG/videodir.h 2010-03-14 14:02:06.636971442 +0100
++++ vdr-1.7.14.ExtP_NG/videodir.h 2010-04-10 15:45:12.148736552 +0200
@@ -19,6 +19,9 @@
int CloseVideoFile(cUnbufferedFile *File);
bool RenameVideoFile(const char *OldName, const char *NewName);