summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Brugger <brougs78@gmx.net>2009-06-06 01:29:22 +0200
committerThomas Günther <tom@toms-cafe.de>2009-06-06 01:29:22 +0200
commit60cd7db51838bb076312fd10e08bca0f4a005f82 (patch)
tree2ce8a57369abbd1ac1fed4a0d6527140808fbc3c
parent5ace1ef884772e4a15d58839074785b89412c8ee (diff)
parentac64ce03ec6b5766691ff2da3af6f51ed800792a (diff)
downloadvdr-plugin-text2skin-60cd7db51838bb076312fd10e08bca0f4a005f82.tar.gz
vdr-plugin-text2skin-60cd7db51838bb076312fd10e08bca0f4a005f82.tar.bz2
Added brougs78-extensions (thanks to Andreas Brugger / references #39)
- added tokens for the next 3 timers: CurrentEventsTitle[123], CurrentEventsStartDateTime[123], CurrentEventsStopDateTime[123], CurrentEventsChannelNumber[123], CurrentEventsChannelName[123], CurrentEventsIsRecording[123] - added audio- and video-tokens: PresentLanguageCode, PresentLanguageDescription, PresentVideoAR and implemented the missing code for the Language-token - added tokens for replay: ReplayName, ReplayDateTime, ReplayShortText, ReplayDescription, ReplayLanguageCode, ReplayLanguageDescription, ReplayVideoAR - additional recording-tokens: RecordingVideoAR, RecordingSize - added a reset for scrolling text (configurable) - added recording-tokens: RecordingLength, RecordingCuttedLength - added tokens: OsdWidth, OsdHeight - switched the search-order for the fonts to priorise the skin-folder (this avoids error-messages in the log) - added the possibility to have a scrollbar in every menu - not fully implemented yet (to position in menu-lists is not necessarily correct, if there are more items with the same osd-text) - added a configuration option for showing the scrollbar in the menus - added token TimerConflicts using the service interface of the epgsearch plugin "Epgsearch-lastconflictinfo-v1.0" - added a test-feature to search for reruns of a program and add the information to the extended epg-info (trigger DEVELOPMENT_FEATURES). This uses a service interface of the epgsearch plugin "Epgsearch-searchresults-v1.0" - the extended epg-info and the recording-info are extended by AUX-Infos (configurable) there is also an option to strip known tags - the tab-widths are scaled for taking into account that different TT-Fonts have a different width than the default font from VDR - added tokens for signal-info: FrontendSTR, FrontendSNR, FrontendHasLock, FrontendHasSignal - added token PresentEventID for EPG-images - added tokens for recordings: RecordingFilename, RecordingPriority, RecordingLifetime - set EditableWidth. This is important for plugins like 'rotor' or 'extrecmenu'
-rw-r--r--HISTORY42
-rw-r--r--Makefile9
-rw-r--r--common.c316
-rw-r--r--common.h46
-rw-r--r--display.c275
-rw-r--r--display.h3
-rw-r--r--i18n.c300
-rw-r--r--menu.c37
-rw-r--r--menu.h1
-rw-r--r--render.c97
-rw-r--r--render.h30
-rw-r--r--setup.c22
-rw-r--r--setup.h10
-rw-r--r--status.c289
-rw-r--r--status.h37
-rw-r--r--text2skin.c24
-rw-r--r--text2skin.h4
-rw-r--r--xml/object.c9
-rw-r--r--xml/string.c11
-rw-r--r--xml/string.h43
20 files changed, 1570 insertions, 35 deletions
diff --git a/HISTORY b/HISTORY
index 82e79af..6b203de 100644
--- a/HISTORY
+++ b/HISTORY
@@ -4,6 +4,48 @@ VDR Plugin 'text2skin' Revision History
____-__-__: Version 1.3
- Added Italian language texts (thanks to Diego Pierotto / closes #134)
+- Added brougs78-extensions (thanks to Andreas Brugger / references #39)
+ - added tokens for the next 3 timers:
+ CurrentEventsTitle[123], CurrentEventsStartDateTime[123],
+ CurrentEventsStopDateTime[123], CurrentEventsChannelNumber[123],
+ CurrentEventsChannelName[123], CurrentEventsIsRecording[123]
+ - added audio- and video-tokens:
+ PresentLanguageCode, PresentLanguageDescription, PresentVideoAR and
+ implemented the missing code for the Language-token
+ - added tokens for replay:
+ ReplayName, ReplayDateTime, ReplayShortText, ReplayDescription,
+ ReplayLanguageCode, ReplayLanguageDescription, ReplayVideoAR
+ - additional recording-tokens:
+ RecordingVideoAR, RecordingSize
+ - added a reset for scrolling text (configurable)
+ - added recording-tokens:
+ RecordingLength, RecordingCuttedLength
+ - added tokens:
+ OsdWidth, OsdHeight
+ - switched the search-order for the fonts to priorise the skin-folder
+ (this avoids error-messages in the log)
+ - added the possibility to have a scrollbar in every menu - not fully
+ implemented yet (to position in menu-lists is not necessarily correct,
+ if there are more items with the same osd-text)
+ - added a configuration option for showing the scrollbar in the menus
+ - added token TimerConflicts using the service interface of the epgsearch
+ plugin "Epgsearch-lastconflictinfo-v1.0"
+ - added a test-feature to search for reruns of a program and add the
+ information to the extended epg-info (trigger DEVELOPMENT_FEATURES).
+ This uses a service interface of the epgsearch plugin
+ "Epgsearch-searchresults-v1.0"
+ - the extended epg-info and the recording-info are extended by AUX-Infos
+ (configurable)
+ there is also an option to strip known tags
+ - the tab-widths are scaled for taking into account that different TT-Fonts
+ have a different width than the default font from VDR
+ - added tokens for signal-info:
+ FrontendSTR, FrontendSNR, FrontendHasLock, FrontendHasSignal
+ - added token PresentEventID for EPG-images
+ - added tokens for recordings:
+ RecordingFilename, RecordingPriority, RecordingLifetime
+ - set EditableWidth. This is important for plugins like 'rotor' or
+ 'extrecmenu'
2009-06-01: Version 1.2
diff --git a/Makefile b/Makefile
index f5720c8..873828d 100644
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,11 @@
# Imlib2 for loading images. BEWARE that you can not use GraphTFT together with
# Text2Skin if you use Imlib2! (That's why I actually implemented ImageMagick)
+#DEBUG=1
+#BENCH=1
+
HAVE_IMAGEMAGICK=1
-#HAVE_IMLIB2=1
+#HAVE_IMLIB2=1 # not recommended
# comment this out if you don't want to use FreeType font rendering
@@ -64,6 +67,10 @@ OBJS = $(PLUGIN).o loader.o display.o render.o common.o bitmap.o \
### Includes and Defines (add further entries here):
+ifdef DEVELOPMENT_FEATURES
+ DEFINES += -DDEVELOPMENT_FEATURES
+endif
+
ifdef HAVE_IMLIB2
DEFINES += -DHAVE_IMLIB2
LIBS += -lImlib2
diff --git a/common.c b/common.c
index 785e74a..728dedb 100644
--- a/common.c
+++ b/common.c
@@ -3,7 +3,13 @@
*/
#include "common.h"
+#include <sstream>
#include <vdr/plugin.h>
+#include <vdr/device.h>
+#include <sys/ioctl.h>
+#include <linux/dvb/frontend.h>
+
+#define FRONTEND_DEVICE "/dev/dvb/adapter%d/frontend%d"
const std::string &SkinPath(void)
{
@@ -69,6 +75,14 @@ const char *ChannelShortName(const cChannel *Channel, int Number)
return buffer;
}
+
+const char *EventType(uint Number)
+{
+ static char buffer[25];
+ buffer[0] = '\0';
+ snprintf(buffer, sizeof(buffer), "%d", Number);
+ return buffer;
+}
/*
const char *ChannelBouquet(const cChannel *Channel, int Number) {
static char buffer[256];
@@ -115,6 +129,271 @@ const cRecording *GetRecordingByName(const char *Name)
return NULL;
}
+const cRecording *GetRecordingByFileName(const char *FileName)
+{
+ return (FileName) ? Recordings.GetByName(FileName) : NULL;
+}
+
+int GetFrontendSTR(void)
+{
+ uint16_t value = 0;
+ char *dev = NULL;
+
+ asprintf(&dev, FRONTEND_DEVICE, cDevice::ActualDevice()->CardIndex(), 0);
+ int fe = open(dev, O_RDONLY | O_NONBLOCK);
+ free(dev);
+ if (fe >= 0) {
+ CHECK(ioctl(fe, FE_READ_SIGNAL_STRENGTH, &value));
+ close(fe);
+ }
+ return value / 655;
+}
+
+int GetFrontendSNR(void)
+{
+ uint16_t value = 0;
+ char *dev = NULL;
+
+ asprintf(&dev, FRONTEND_DEVICE, cDevice::ActualDevice()->CardIndex(), 0);
+ int fe = open(dev, O_RDONLY | O_NONBLOCK);
+ free(dev);
+ if (fe >= 0) {
+ CHECK(ioctl(fe, FE_READ_SNR, &value));
+ close(fe);
+ }
+ return value / 655;
+}
+
+bool GetFrontendHasLock(void)
+{
+ uint16_t value = 0;
+ char *dev = NULL;
+
+ asprintf(&dev, FRONTEND_DEVICE, cDevice::ActualDevice()->CardIndex(), 0);
+ int fe = open(dev, O_RDONLY | O_NONBLOCK);
+ free(dev);
+ if (fe >= 0) {
+ CHECK(ioctl(fe, FE_READ_STATUS, &value));
+ close(fe);
+ }
+ return value & FE_HAS_LOCK;
+}
+
+bool GetFrontendHasSignal(void)
+{
+ uint16_t value = 0;
+ char *dev = NULL;
+
+ asprintf(&dev, FRONTEND_DEVICE, cDevice::ActualDevice()->CardIndex(), 0);
+ int fe = open(dev, O_RDONLY | O_NONBLOCK);
+ free(dev);
+ if (fe >= 0) {
+ CHECK(ioctl(fe, FE_READ_STATUS, &value));
+ close(fe);
+ }
+ return value & FE_HAS_SIGNAL;
+}
+
+std::string AddExtInfoToDescription(const char *Title, const char *ShortText, const char *Description, const char *Aux, bool StripAux)
+{
+ // max. width so lines don't get wrapped
+ #define MAX_CHARS 50
+
+ // prepare the description
+ std::stringstream desc("");
+
+ if (!isempty(Description)) {
+ // it seems that sometimes the description ends with a newline
+ // and sometimes it does not
+ std::string buf(Description);
+ while (!buf.empty() && buf[buf.size() - 1] == '\n') buf.erase(buf.size() - 1);
+ desc << buf << "\n"; // keep one newline
+ }
+
+#ifdef DEVELOPMENT_FEATURES
+ // try to find a rerun of the show using epgsearch-service
+ if (!isempty(Title)) {
+ Epgsearch_searchresults_v1_0 data;
+ data.query = strdup(Title);
+ data.mode = 0;
+ data.channelNr = 0;
+ data.useTitle = true;
+ data.useSubTitle = false;
+ data.useDescription = false;
+ if (cPluginManager::CallFirstService("Epgsearch-searchresults-v1.0", &data)) {
+ cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>* list = data.pResultList;
+ if (list) {
+ // die aktuelle Sendung wird noch als WH angezeigt !!!
+ if (!desc.str().empty()) desc << "\n";
+ desc << tr("RERUNS OF THIS SHOW") << ":\n";
+ int i = 0;
+ for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r =
+ list->First(); r && i < 5; r = list->Next(r)) {
+ i++;
+ std::stringstream buf;
+ buf << " - ";
+ buf << *DayDateTime(r->event->StartTime());
+ buf << ": " << r->event->Title();
+ if (!isempty(r->event->ShortText())) buf << "~" << r->event->ShortText();
+ desc << FitToWidth(buf, MAX_CHARS) << "\n";
+ }
+ delete list;
+ }
+ }
+ }
+#endif // DEVELOPMENT_FEATURES
+
+ // Add the AUX-Info of the Recording
+ if (Aux) {
+ if (StripAux) {
+ std::string auxRaw(Aux);
+ std::string auxEpgsearch = StripXmlTag(auxRaw, "epgsearch");
+ if (!auxEpgsearch.empty()) {
+ if (!desc.str().empty())
+ desc << "\n";
+ desc << tr("AUXILIARY INFOS") << ":\n";
+ std::stringstream buf;
+ buf << " - " << tr("Search timer") << ": " << StripXmlTag(auxRaw, "Search timer");
+ desc << FitToWidth(buf, MAX_CHARS) << "\n";
+ }
+ }
+ else {
+ if (!desc.str().empty())
+ desc << "\n";
+ desc << tr("AUXILIARY INFOS") << ":\n";
+ desc << Aux << "\n";
+ }
+ }
+
+ return desc.str();
+}
+
+int GetRecordingSize(const char *FileName)
+#if VDRVERSNUM >= 10338
+// use VDR's routine
+{
+ const cRecording *rec = GetRecordingByFileName(FileName);
+ return (rec) ? DirSizeMB(FileName) : 0;
+}
+#else
+// use our own approach
+{
+ if (FileName != NULL) {
+ bool bRet = false;
+ long long size = 0;
+ int nFiles;
+ struct stat fileinfo; // Holds file information structure
+ char *cmd = NULL;
+#if VDRVERSNUM >= 10318
+ cReadLine reader;
+#endif
+ asprintf(&cmd, "find '%s' -follow -type f -name '*.*'|sort ", FileName);
+
+ FILE *p = popen(cmd, "r");
+ int ret = 0;
+ if (p) {
+ char *s;
+
+#if VDRVERSNUM >= 10318
+ while ((s = reader.Read(p)) != NULL) {
+#else
+ while ((s = readline(p)) != NULL) {
+#endif
+ if ((ret=stat(s, &fileinfo)) != -1) {
+ size += (long long)fileinfo.st_size;
+ nFiles++;
+ }
+ }
+
+ bRet = true;
+ }
+
+ pclose(p);
+ delete cmd;
+
+ return (int)(size / 1024 / 1024); // [MB]
+ }
+ else
+ return 0;
+}
+#endif
+
+int GetRecordingLength(const char *FileName)
+{
+ // based on the enAIO-Patch for VDR
+ #define INDEXFILESUFFIX "/index.vdr"
+
+ struct tIndex { int offset; uchar type; uchar number; short reserved; };
+ tIndex *index;
+ char RecLength[21];
+ char *filename = NULL;
+ int last = -1;
+ index = NULL;
+ if (FileName) {
+ filename = MALLOC(char, strlen(FileName) + strlen(INDEXFILESUFFIX) + 1);
+ if (filename) {
+ strcpy(filename, FileName);
+ char *pFileExt = filename + strlen(filename);
+ strcpy(pFileExt, INDEXFILESUFFIX);
+ int delta = 0;
+ if (access(filename, R_OK) == 0) {
+ struct stat buf;
+ if (stat(filename, &buf) == 0) {
+ delta = buf.st_size % sizeof(tIndex);
+ if (delta) {
+ delta = sizeof(tIndex) - delta;
+ esyslog("ERROR: invalid file size (%ld) in '%s'", (long)buf.st_size, filename);
+ }
+ last = (buf.st_size + delta) / sizeof(tIndex) - 1;
+ char hour[2], min[3];
+#if VDRVERSNUM >= 10318
+ snprintf(RecLength, sizeof(RecLength), "%s", *IndexToHMSF(last, true));
+#else
+ snprintf(RecLength, sizeof(RecLength), "%s", IndexToHMSF(last, true));
+#endif
+ snprintf(hour, sizeof(hour), "%c", RecLength[0]);
+ snprintf(min, sizeof(min), "%c%c", RecLength[2], RecLength[3]);
+ return (atoi(hour) * 60) + atoi(min);
+ }
+ }
+ free(filename);
+ }
+ }
+
+ return 0;
+}
+
+int GetRecordingCuttedLength(const char *FileName)
+{
+ cMarks marks;
+ double length = 0;
+ int totalLength = GetRecordingLength(FileName);
+ const double diffIFrame = FRAMESPERSEC / 2; // approx. 1/2 sec.
+
+ marks.Load(FileName);
+
+ if (marks.Count()) {
+ int start = 1; // first frame
+ bool isStart = true;
+
+ for (cMark *m = marks.First(); m; m = marks.GetNext(m->position)) {
+ if (isStart)
+ start = m->position;
+ else
+ length += (double)(m->position - start + 1 + diffIFrame) / (FRAMESPERSEC * 60); // [min]
+
+ isStart = !isStart;
+ }
+
+ // if there is no end-mark the last segment goes to the end of the rec.
+ if (!isStart)
+ length += totalLength - (double)(start - 1 - diffIFrame) / (FRAMESPERSEC * 60); // [min]
+ }
+
+ // just to avoid, that the cutted length is bigger than the total length
+ return (int)length > totalLength ? totalLength : (int)length;
+}
+
cxType TimeType(time_t Time, const std::string &Format)
{
static char result[1000];
@@ -263,3 +542,40 @@ void SkipQuotes(std::string &Value) {
else
esyslog("ERROR: text2skin: missing closing %c", quote);
}
+
+std::string FitToWidth(std::string &Line, uint Width)
+{
+ std::string buf(Line);
+ if (buf.size() > Width) {
+ buf.erase(Width - 3);
+ buf.append("...");
+ }
+ return buf;
+}
+
+std::string FitToWidth(std::stringstream &Line, uint Width)
+{
+ std::string buf(Line.str());
+ if (buf.size() > Width) {
+ buf.erase(Width - 3);
+ buf.append("...");
+ }
+ return buf;
+}
+
+std::string StripXmlTag(std::string &Line, const char *Tag)
+{
+ // set the search strings
+ std::stringstream strStart, strStop;
+ strStart << "<" << Tag << ">";
+ strStop << "</" << Tag << ">";
+ // find the strings
+ std::string::size_type locStart = Line.find(strStart.str());
+ std::string::size_type locStop = Line.find(strStop.str());
+ if (locStart == std::string::npos || locStop == std::string::npos)
+ return "";
+ // extract relevant text
+ int pos = locStart + strStart.str().size();
+ int len = locStop - pos;
+ return len < 0 ? "" : Line.substr(pos, len);
+}
diff --git a/common.h b/common.h
index ce87589..f596a01 100644
--- a/common.h
+++ b/common.h
@@ -9,6 +9,7 @@
#include <string>
#include <vdr/osd.h>
#include <vdr/config.h>
+#include <vdr/epg.h>
// from recording.h (VDR <= 1.7.2)
#define FRAMESPERSEC 25
@@ -27,6 +28,9 @@
# define Ddiff(t,x)
#endif
+#define DStartBench(x) uint64_t bench_##x = time_ms()
+#define DShowBench(t,x) fprintf(stderr, "%s took %llu ms\n", t, time_ms() - bench_##x)
+
#if VDRVERSNUM >= 10318
# define time_ms() cTimeMs::Now()
# define Apid1() Apid(0)
@@ -50,10 +54,20 @@ const std::string &SkinPath(void);
const char *ChannelNumber(const cChannel *Channel, int Number);
const char *ChannelName(const cChannel *Channel, int Number);
const char *ChannelShortName(const cChannel *Channel, int Number);
+const char *EventType(uint Number);
//const char *ChannelBouquet(const cChannel *Channel, int Number);
bool StoppedTimer(const char *Name);
const cRecording *GetRecordingByName(const char *Name);
+const cRecording *GetRecordingByFileName(const char *FileName);
+int GetFrontendSTR(void); // Signal strength [%]
+int GetFrontendSNR(void); // Signal to Noise ratio [%]
+bool GetFrontendHasLock(void);
+bool GetFrontendHasSignal(void);
+std::string AddExtInfoToDescription(const char *Title, const char *ShortText, const char *Description, const char *Aux = NULL, bool StripAux = false);
+int GetRecordingSize(const char *FileName); // [MB]
+int GetRecordingLength(const char *FileName); // [min]
+int GetRecordingCuttedLength(const char *FileName); // [min]
cxType TimeType(time_t Time, const std::string &Format);
cxType DurationType(uint Index, const std::string &Format);
@@ -62,5 +76,37 @@ bool ParseVar(const char *Text, const char *Name, std::string &Value);
bool ParseVar(const char *Text, const char *Name, tColor *Value);
void SkipQuotes(std::string &Value);
+std::string FitToWidth(std::string &Line, uint Width);
+std::string FitToWidth(std::stringstream &Line, uint Width);
+std::string StripXmlTag(std::string &Line, const char *Tag);
+
+// Data structure for service "Epgsearch-searchresults-v1.0"
+struct Epgsearch_searchresults_v1_0 {
+// in
+ char* query; // search term
+ int mode; // search mode (0=phrase, 1=and, 2=or, 3=regular expression)
+ int channelNr; // channel number to search in (0=any)
+ bool useTitle; // search in title
+ bool useSubTitle; // search in subtitle
+ bool useDescription; // search in description
+// out
+
+ class cServiceSearchResult : public cListObject {
+ public:
+ const cEvent* event;
+ cServiceSearchResult(const cEvent* Event) : event(Event) {}
+ };
+
+ cList<cServiceSearchResult>* pResultList; // pointer to the results
+};
+
+// Data structure for service "Epgsearch-lastconflictinfo-v1.0"
+struct Epgsearch_lastconflictinfo_v1_0 {
+// in
+// out
+ time_t nextConflict; // next conflict date, 0 if none
+ int relevantConflicts; // number of relevant conflicts
+ int totalConflicts; // total number of conflicts
+};
#endif // VDR_TEXT2SKIN_COMMON_H
diff --git a/display.c b/display.c
index c274080..663fd45 100644
--- a/display.c
+++ b/display.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "xml/string.h"
#include <vdr/menu.h>
+#include <vdr/plugin.h>
// --- cText2SkinDisplayChannel -----------------------------------------------
@@ -241,7 +242,16 @@ cxType cText2SkinDisplayChannel::GetTokenData(const txToken &Token)
case tLanguage: {
#if VDRVERSNUM >= 10318
- // TODO !!!
+ cDevice *dev = cDevice::PrimaryDevice();
+ eTrackType trackType = dev->GetCurrentAudioTrack();
+ const tTrackId *track = dev->GetTrack(trackType);
+ if (track) {
+ std::string buffer(track->language);
+ if (trackType >= ttDolby)
+ buffer.append("DD");
+ return (cxType)buffer.c_str();
+ }
+ return (cxType)false;
#else
int cur;
const char **tracks = cDevice::PrimaryDevice()->GetAudioTracks(&cur);
@@ -582,6 +592,32 @@ cxType cText2SkinDisplayReplay::GetTokenData(const txToken &Token)
}
return false;
+ case tLanguage: {
+#if VDRVERSNUM >= 10318
+ cDevice *dev = cDevice::PrimaryDevice();
+ eTrackType trackType = dev->GetCurrentAudioTrack();
+ const tTrackId *track = dev->GetTrack(trackType);
+ if (track) {
+ std::string buffer(track->language);
+ if (trackType >= ttDolby)
+ buffer.append("DD");
+ return (cxType)buffer.c_str();
+ }
+ return (cxType)false;
+#else
+ int cur;
+ const char **tracks = cDevice::PrimaryDevice()->GetAudioTracks(&cur);
+ if (tracks) {
+ int i = 0;
+ while (tracks[i] != NULL)
+ ++i;
+ if (cur < i)
+ return tracks[cur];
+ }
+#endif
+ }
+ return false;
+
case tMessage:
return mText;
@@ -737,7 +773,9 @@ void cText2SkinDisplayMenu::Clear(void)
mItems.clear();
mCurrentItem = (uint)-1;
mEvent = NULL;
+ ExtPresentDescription = "";
mRecording = NULL;
+ ExtRecordingDescription = "";
mText = "";
cText2SkinRender::Clear();
SetDirty();
@@ -755,6 +793,9 @@ void cText2SkinDisplayMenu::SetTitle(const char *Title)
UpdateLock();
if (Title == NULL) Title = "";
if (mTitle != Title) {
+ mUpdate.timerConflict = true;
+ mUpdate.events = true;
+ mUpdate.resetMarquee = true;
mTitle = Title;
SetDirty();
}
@@ -835,6 +876,9 @@ void cText2SkinDisplayMenu::SetItem(const char *Text, int Index, bool Current, b
mCurrentItem = Index;
SetDirty();
}
+
+ if (Current)
+ mRender->mMenuScrollbar.currentOnScreen = (uint)Index;
UpdateUnlock();
}
@@ -848,6 +892,7 @@ void cText2SkinDisplayMenu::SetEvent(const cEvent *Event)
UpdateLock();
if (mEvent != Event) {
mEvent = Event;
+ ExtPresentDescription = "";
if (mEvent != NULL)
SetDirty();
}
@@ -865,6 +910,7 @@ void cText2SkinDisplayMenu::SetRecording(const cRecording *Recording)
// yet unused
if (mRecording != Recording) {
mRecording = Recording;
+ ExtRecordingDescription = "";
if (mRecording != NULL)
SetDirty();
}
@@ -1046,14 +1092,129 @@ cxType cText2SkinDisplayMenu::GetTokenData(const txToken &Token)
: (cxType)false;
case tPresentDescription:
+ if (mEvent) {
+ if (ExtPresentDescription == "") {
+#if VDRVERSNUM >= 10344
+ // find corresponding timer
+ const char *aux = NULL;
+ for (cTimer *tim = Timers.First(); tim; tim = Timers.Next(tim))
+ if (tim->Event() == mEvent)
+ aux = tim->Aux();
+ ExtPresentDescription = AddExtInfoToDescription(mEvent->Title(), mEvent->ShortText(), mEvent->Description(), aux, Text2SkinSetup.StripAux);
+#else
+ ExtPresentDescription = AddExtInfoToDescription(mEvent->Title(), mEvent->ShortText(), mEvent->Description());
+#endif
+ }
+ return (cxType)ExtPresentDescription;
+ } else
+ return (cxType)false;
+
+#if VDRVERSNUM >= 10318
+ case tPresentLanguageCode:
+ if (mEvent) {
+ const cComponents *components = mEvent->Components();
+ if (components) {
+ int index = Token.Attrib.Number;
+
+ // don't return language-code for the video-stream
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream != 2) // only audio-streams
+ index++;
+ if (i == index) {
+ std::string buffer(c->language);
+ if (c->type == 1)
+ buffer.append("MONO");
+ if ((c->type == 2) || (c->type == 4))
+ buffer.append("DUAL");
+ if (c->type == 5)
+ buffer.append("DD");
+ return (cxType)buffer.c_str();
+ }
+ }
+ }
+ }
+ return false;
+
+ case tPresentLanguageDescription:
+ if (mEvent) {
+ const cComponents *components = mEvent->Components();
+ if (components) {
+ int index = Token.Attrib.Number;
+
+ // don't return language-code for the video-stream
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream != 2) // only audio-streams
+ index++;
+ if (i == index)
+ return (cxType)c->description;
+ }
+ }
+ }
+ return false;
+
+ case tPresentVideoAR:
+ if (mEvent) {
+ const cComponents *components = mEvent->Components();
+ if (components) {
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream == 1) {
+ switch (c->type) {
+ case 1:
+ return "4:3";
+ case 3:
+ return "16:9";
+ }
+ }
+ }
+ }
+ }
+ return false;
+#endif
+
+ case tPresentEventID:
return mEvent != NULL
- ? (cxType)mEvent->Description()
+ ? (cxType)EventType(mEvent->EventID())
: (cxType)false;
case tHasVPS:
case tChannelHasVPS:
return mEvent != NULL && mEvent->Vps() != 0;
+ case tChannelName:
+ if (mEvent) { // extended EPG
+ cChannel *channel = Channels.GetByChannelID(mEvent->ChannelID(), true);
+ return channel != NULL
+ ? (cxType)ChannelName(channel, 0)
+ : (cxType)false;
+ }
+ else if (mRecording) { // recording Info
+ cRecordingInfo *recInfo = const_cast<cRecordingInfo*>(mRecording->Info());
+ cChannel *channel = Channels.GetByChannelID(recInfo->ChannelID(), true);
+ return channel != NULL
+ ? (cxType)ChannelName(channel, 0)
+ : (cxType)false;
+ } else
+ return (cxType)false;
+
+ case tChannelShortName:
+ if (mEvent) { // extended EPG
+ cChannel *channel = Channels.GetByChannelID(mEvent->ChannelID(), true);
+ return channel != NULL
+ ? (cxType)ChannelShortName(channel, 0)
+ : (cxType)false;
+ }
+ else if (mRecording) { // recording Info
+ cRecordingInfo *recInfo = const_cast<cRecordingInfo*>(mRecording->Info());
+ cChannel *channel = Channels.GetByChannelID(recInfo->ChannelID(), true);
+ return channel != NULL
+ ? (cxType)ChannelShortName(channel, 0)
+ : (cxType)false;
+ } else
+ return (cxType)false;
+
case tPresentHasVPS:
return mEvent != NULL && mEvent->Vps() != 0 && mEvent->Vps() != mEvent->StartTime();
@@ -1074,6 +1235,21 @@ cxType cText2SkinDisplayMenu::GetTokenData(const txToken &Token)
? (cxType)mRecording->Name()
: (cxType)false;
+ case tRecordingFilename:
+ return mRecording != NULL
+ ? (cxType)mRecording->FileName()
+ : (cxType)false;
+
+ case tRecordingPriority:
+ return mRecording != NULL
+ ? (cxType)mRecording->priority
+ : (cxType)false;
+
+ case tRecordingLifetime:
+ return mRecording != NULL
+ ? (cxType)mRecording->lifetime
+ : (cxType)false;
+
case tRecordingDateTime:
return mRecording != NULL
? (cxType)TimeType(mRecording->start, Token.Attrib.Text)
@@ -1090,31 +1266,96 @@ cxType cText2SkinDisplayMenu::GetTokenData(const txToken &Token)
: (cxType)false;
case tRecordingDescription:
- return mRecording != NULL
- ? (cxType)mRecording->Info()->Description()
- : (cxType)false;
+ if (mRecording) {
+ if (ExtRecordingDescription == "")
+#if VDRVERSNUM >= 10344
+ ExtRecordingDescription = AddExtInfoToDescription(mRecording->Info()->Title(), mRecording->Info()->ShortText(), mRecording->Info()->Description(), Text2SkinSetup.ShowAux ? mRecording->Info()->Aux() : NULL, Text2SkinSetup.StripAux);
+#else
+ ExtRecordingDescription = AddExtInfoToDescription(mRecording->Info()->Title(), mRecording->Info()->ShortText(), mRecording->Info()->Description());
+#endif
+ return (cxType)ExtRecordingDescription;
+ } else
+ return (cxType)false;
case tRecordingLanguageCode:
- if (mRecording != NULL) {
- const tComponent *c
- = mRecording->Info()->Components()->Component(Token.Attrib.Number);
- return c != NULL
- ? (cxType)c->language
- : (cxType)false;
+ if (mRecording) {
+ const cComponents *components = mRecording->Info()->Components();
+ if (components) {
+ int index = Token.Attrib.Number;
+
+ // don't return language-code for the video-stream
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream != 2) // only audio-streams
+ index++;
+ if (i == index) {
+ std::string buffer(c->language);
+ if (c->type == 1)
+ buffer.append("MONO");
+ if ((c->type == 2) || (c->type == 4))
+ buffer.append("DUAL");
+ if (c->type == 5)
+ buffer.append("DD");
+ return (cxType)buffer.c_str();
+ }
+ }
+ }
}
return false;
case tRecordingLanguageDescription:
- if (mRecording != NULL) {
- const tComponent *c
- = mRecording->Info()->Components()->Component(Token.Attrib.Number);
- return c != NULL
- ? (cxType)c->description
- : (cxType)false;
+ if (mRecording) {
+ const cComponents *components = mRecording->Info()->Components();
+ if (components) {
+ int index = Token.Attrib.Number;
+
+ // don't return language-code for the video-stream
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream != 2) // only audio-streams
+ index++;
+ if (i == index)
+ return (cxType)c->description;
+ }
+ }
+ }
+ return false;
+
+ case tRecordingVideoAR:
+ if (mRecording) {
+ const cComponents *components = mRecording->Info()->Components();
+ if (components) {
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream == 1) {
+ switch (c->type) {
+ case 1:
+ return "4:3";
+ case 3:
+ return "16:9";
+ }
+ }
+ }
+ }
}
return false;
#endif
+ case tRecordingSize:
+ return mRecording != NULL
+ ? (cxType)GetRecordingSize(mRecording->FileName())
+ : (cxType)false;
+
+ case tRecordingLength:
+ return mRecording != NULL
+ ? (cxType)GetRecordingLength(mRecording->FileName())
+ : (cxType)false;
+
+ case tRecordingCuttedLength:
+ return mRecording != NULL
+ ? (cxType)GetRecordingCuttedLength(mRecording->FileName())
+ : (cxType)false;
+
default:
return cText2SkinRender::GetTokenData(Token);
}
diff --git a/display.h b/display.h
index 9f592c2..23c8d89 100644
--- a/display.h
+++ b/display.h
@@ -142,8 +142,10 @@ private:
// detailed event view
const cEvent *mEvent;
+ std::string ExtPresentDescription;
// detailed recording
const cRecording *mRecording;
+ std::string ExtRecordingDescription;
// long text
std::string mText;
@@ -169,6 +171,7 @@ protected:
virtual void SetEditableWidth(int Width) { cSkinDisplayMenu::SetEditableWidth(Width); }
virtual int MaxItems(void) { return mMaxItems; }
virtual void SetMaxItems(int MaxItems) { mMaxItems = MaxItems; }
+ virtual int GetMaxItems(void) { return mMaxItems; }
public:
cText2SkinDisplayMenu(cText2SkinLoader *Loader);
diff --git a/i18n.c b/i18n.c
index a190a49..c36fb95 100644
--- a/i18n.c
+++ b/i18n.c
@@ -105,6 +105,306 @@ const tI18nPhrase Phrases[] = {
"",
#endif
},
+ { "RERUNS OF THIS SHOW",
+ "WIEDERHOLUNGEN DIESER SENDUNG",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "Scale factor of the tab-widths [%]",
+ "Skalierungsfaktor für die Tabolatorweiten [%]",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "Scrolling behaviour",
+ "Scroll-Verhalten",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "Show scrollbar in the menus",
+ "Zeige Bildlaufleiste in Menüs",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "Reset Marquee for new item",
+ "Marquee für neues Item zurücksetzen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "Use 'epgsearch' to check timer-conflicts",
+ "Timerkonflikte mit 'epgsearch' überprüfen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "to the left",
+ "nach links",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "left and right",
+ "links und rechts",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "AUXILIARY INFOS",
+ "ZUSATZINFOS",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "Auxiliary infos in recordings/timers",
+ "Zusatzinfos bei Aufnahmen/Timer anzeigen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { " Extract known tags",
+ " Bekannte Tags extrahieren",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
+ { "Search timer",
+ "Suchtimer",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+#if VDRVERSNUM >= 10313
+ "",
+#endif
+#if VDRVERSNUM >= 10316
+ "",
+#endif
+ },
{ "Max. image cache size",
"Max. Größe des Bildspeichers",
"",
diff --git a/menu.c b/menu.c
index 6bc478d..200278a 100644
--- a/menu.c
+++ b/menu.c
@@ -9,19 +9,52 @@
cText2SkinSetupPage::cText2SkinSetupPage(void) {
mData = Text2SkinSetup;
+ Setup();
+}
+
+void cText2SkinSetupPage::Setup(void) {
+ int current = Current();
+ Clear();
+
+ Add(new cMenuEditBoolItem(tr("Show scrollbar in the menus"), &mData.MenuScrollbar, tr("no"), tr("yes")));
+ Add(new cMenuEditBoolItem(tr("Scrolling behaviour"), &mData.MarqueeLeftRight, tr("to the left"), tr("left and right")));
+ Add(new cMenuEditBoolItem(tr("Reset Marquee for new item"), &mData.MarqueeReset, tr("no"), tr("yes")));
+#if VDRVERSNUM >= 10344
+ Add(new cMenuEditBoolItem(tr("Show auxiliary infos of recordings"), &mData.ShowAux, tr("no"), tr("yes")));
+ if (mData.ShowAux)
+ Add(new cMenuEditBoolItem(tr(" Extract known tags"), &mData.StripAux, tr("no"), tr("yes")));
+#endif
+#if VDRVERSNUM >= 10330
+ Add(new cMenuEditBoolItem(tr("Use 'timeline' to check timer-conflicts"), &mData.CheckTimerConflict, tr("no"), tr("yes")));
+#endif
Add(new cOsdItem(tr("Flush image cache"), osUser1));
Add(new cMenuEditIntItem(tr("Max. image cache size"), &mData.MaxCacheFill));
+
+ SetCurrent(Get(current));
+ Display();
}
cText2SkinSetupPage::~cText2SkinSetupPage() {
}
void cText2SkinSetupPage::Store(void) {
+ SetupStore("MenuScrollbar", mData.MenuScrollbar);
+ SetupStore("MarqueeLeftRight", mData.MarqueeLeftRight);
+ SetupStore("MarqueeReset", mData.MarqueeReset);
+#if VDRVERSNUM >= 10344
+ SetupStore("ShowAux", mData.ShowAux);
+ SetupStore("StripAux", mData.StripAux);
+#endif
+#if VDRVERSNUM >= 10330
+ SetupStore("CheckTimerConflict", mData.CheckTimerConflict);
+#endif
SetupStore("MaxCacheFill", mData.MaxCacheFill);
Text2SkinSetup = mData;
}
eOSState cText2SkinSetupPage::ProcessKey(eKeys Key) {
+ int oldShowAux = mData.ShowAux;
+
eOSState state = cMenuSetupPage::ProcessKey(Key);
if (state == osUser1) {
Skins.Message(mtInfo, tr("Flushing image cache..."));
@@ -29,6 +62,10 @@ eOSState cText2SkinSetupPage::ProcessKey(eKeys Key) {
Skins.Message(mtInfo, NULL);
return osContinue;
}
+
+ if (mData.ShowAux != oldShowAux)
+ Setup();
+
return state;
}
diff --git a/menu.h b/menu.h
index 5cd1563..6186761 100644
--- a/menu.h
+++ b/menu.h
@@ -17,6 +17,7 @@ public:
cText2SkinSetupPage(void);
virtual ~cText2SkinSetupPage();
+ void Setup(void);
virtual void Store(void);
eOSState ProcessKey(eKeys Key);
};
diff --git a/render.c b/render.c
index 7610d2e..1c09863 100644
--- a/render.c
+++ b/render.c
@@ -145,15 +145,22 @@ void cText2SkinRender::Action(void)
void cText2SkinRender::Update(void)
{
+ //DStartBench(malen);
+ //DStartBench(ges);
Dbench(update);
for (uint i = 0; i < mDisplay->Objects(); ++i)
DrawObject(mDisplay->GetObject(i));
+ //DShowBench("---\t", malen);
+ //DStartBench(flushen);
Dbench(flush);
mScreen->Flush();
Ddiff("flush only", flush);
Ddiff("complete flush", update);
+ //DShowBench("===\t", flushen);
+ //DShowBench("=== ges\t", ges);
+ //printf("====\t%d\n", mDisplay->Objects());
}
void cText2SkinRender::DrawObject(const cxObject *Object)
@@ -220,7 +227,9 @@ void cText2SkinRender::DrawObject(const cxObject *Object)
uint itemheight = item->Size().h;
uint maxitems = areasize.h / itemheight;
uint yoffset = 0;
+ bool initialEditableWidthSet = false;
+ mMenuScrollbar.maxItems = maxitems;
SetMaxItems(maxitems); //Dprintf("setmaxitems %d\n", maxitems);
for (uint i = 0; i < maxitems; ++i, yoffset += itemheight) {
for (uint j = 1; j < Object->Objects(); ++j) {
@@ -253,14 +262,36 @@ void cText2SkinRender::DrawObject(const cxObject *Object)
++n;
nexttab = GetTab(n);
}
-
+
+ // set initial EditableWidth
+ // this is for plugins like 'extrecmenu' and 'rotor'
+ if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && !initialEditableWidthSet) {
+ initialEditableWidthSet = true;
+ SetEditableWidth(obj.Size().w);
+ }
+
if (t >= 0 && nexttab > 0 && nexttab < obj.mPos1.x + obj.Size().w - 1)
// there is a "next tab" with text
obj.mPos2.x = Object->mPos1.x + o->mPos1.x + nexttab;
else {
// there is no "next tab", use the rightmost edge
obj.mPos2.x += Object->mPos1.x;
- SetEditableWidth(obj.Size().w);
+ /* not used anymore due to change to fontOsd
+ but could be usefull if someone uses a differnt font
+
+ if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && t == 1) {
+ // VDR assumes, that the font in the menu is fontOsd,
+ // so the EditableWidth is not necessarily correct
+ // for TTF
+ const cFont *defFont = cFont::GetFont(fontOsd);
+ const char *dummy = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
+ int editableWidth = obj.Size().w;
+ if (defFont != obj.Font())
+ editableWidth = (int)(editableWidth * defFont->Width(dummy) / (1.1 * obj.Font()->Width(dummy)));
+ SetEditableWidth(editableWidth);
+ } */
+ if ((obj.Type() == cxObject::text || obj.Type() == cxObject::marquee || obj.Type() == cxObject::blink) && t == 1)
+ SetEditableWidth(obj.Size().w);
}
obj.mPos2.y += Object->mPos1.y + yoffset;
@@ -358,7 +389,21 @@ void cText2SkinRender::DrawMarquee(const txPoint &Pos, const txSize &Size, const
state = tState();
state.text = Text;
}
-
+
+ if (Text2SkinSetup.MarqueeReset && mUpdate.resetMarquee && mUpdate.currentItem.find(Text, 0) != std::string::npos) {
+ state.offset = 0;
+ state.direction = 1;
+ state.nexttime = 0;
+ state.scrolling = false;
+ mUpdate.foundFirstItem = true;
+ }
+ else {
+ if (mUpdate.foundFirstItem) {
+ mUpdate.resetMarquee = false;
+ mUpdate.foundFirstItem = false;
+ }
+ }
+
if (state.nexttime == 0)
state.nexttime = mNow + 1500;
else if (mNow >= state.nexttime) {
@@ -375,7 +420,7 @@ void cText2SkinRender::DrawMarquee(const txPoint &Pos, const txSize &Size, const
++state.direction;
nextin = 1500;
} else
- --state.offset;
+ Text2SkinSetup.MarqueeLeftRight ? --state.offset : state.offset = 0;
}
state.nexttime = mNow + nextin;
}
@@ -544,6 +589,26 @@ void cText2SkinRender::DrawScrollbar(const txPoint &Pos, const txSize &Size, con
DrawRectangle(sp, ss, Fg);
}
}
+ else if (mMenuScrollbar.Available()) {
+ DrawRectangle(Pos, Size, Bg);
+ txPoint sbPoint = Pos;
+ txSize sbSize = Size;
+ if (sbSize.h > sbSize.w) {
+ // -1 to get at least 1 pixel height
+ double top = double(mMenuScrollbar.Top()) / mMenuScrollbar.total * (sbSize.h - 1);
+ double bottom = double(mMenuScrollbar.Bottom()) / mMenuScrollbar.total * (sbSize.h - 1);
+ sbPoint.y += (uint)top;
+ sbSize.h -= (uint)top + (uint)bottom;
+ }
+ else {
+ // -1 to get at least 1 pixel height
+ double left = double(mMenuScrollbar.Top()) / mMenuScrollbar.total * (sbSize.w - 1);
+ double right = double(mMenuScrollbar.Bottom()) / mMenuScrollbar.total * (sbSize.w - 1);
+ sbPoint.x += (uint)left;
+ sbSize.w -= (uint)left + (uint)right;
+ }
+ DrawRectangle(sbPoint, sbSize, Fg);
+ }
}
txPoint cText2SkinRender::Transform(const txPoint &Pos)
@@ -567,7 +632,9 @@ bool cText2SkinRender::ItemColor(const std::string &Color, tColor &Result)
std::string cText2SkinRender::ImagePath(const std::string &Filename)
{
if (mRender)
- return mRender->mBasePath + "/" + Filename;
+ return (*Filename.data() == '/')
+ ? Filename
+ : mRender->mBasePath + "/" + Filename;
return "";
}
@@ -655,12 +722,26 @@ cxType cText2SkinRender::GetTokenData(const txToken &Token)
case tDateTime: return TimeType(time(NULL), Token.Attrib.Text);
- case tCanScrollUp: return mScroller != NULL && mScroller->CanScrollUp();
+ case tCanScrollUp:
+ if (mScroller)
+ return mScroller->CanScrollUp();
+ if (mMenuScrollbar.Available())
+ return mMenuScrollbar.CanScrollUp();
+ return false;
+
+ case tCanScrollDown:
+ if (mScroller)
+ return mScroller->CanScrollDown();
+ if (mMenuScrollbar.Available())
+ return mMenuScrollbar.CanScrollDown();
+ return false;
- case tCanScrollDown: return mScroller != NULL && mScroller->CanScrollDown();
-
case tIsRecording: return cRecordControls::Active();
+ case tOsdWidth: return (cxType)mBaseSize.w;
+
+ case tOsdHeight: return (cxType)mBaseSize.h;
+
#if VDRVERSNUM >=10318
case tAudioTrack: {
cDevice *dev = cDevice::PrimaryDevice();
diff --git a/render.h b/render.h
index 6797e1a..7f257a1 100644
--- a/render.h
+++ b/render.h
@@ -7,6 +7,7 @@
#include "common.h"
#include "scroller.h"
+#include "setup.h"
#include "xml/skin.h"
#include "xml/type.h"
#include <vdr/osd.h>
@@ -47,6 +48,7 @@ private:
std::string mBasePath;
bool mDirty;
+ uint mMaxItems;
cSkin *mFallback;
// update thread
@@ -115,6 +117,7 @@ protected:
virtual bool HasTabText(int Index, int n) { return false; }
virtual void SetEditableWidth(int Width) {}
virtual void SetMaxItems(int MaxItems) {}
+ inline int GetmMaxItems(void) { return mMaxItems; }
// functions for display renderer to control behaviour
void Flush(bool Force = false);
@@ -134,6 +137,33 @@ public:
static bool ItemColor(const std::string &Color, tColor &Result);
static std::string ImagePath(const std::string &Filename);
static cxType GetToken(const txToken &Token);
+
+ // provide scrollbar in every menu
+ struct tMenuScrollbar {
+ uint current; // overall (0 ... toal-1)
+ uint currentOnScreen; // on the current screen (0 ... maxItems-1)
+ uint total;
+ uint maxItems; // viewable on current screen
+ std::vector<std::string> items;
+
+ tMenuScrollbar(void) : current(0), currentOnScreen(0), total(0), maxItems(0) {}
+ bool Available(void) { return Text2SkinSetup.MenuScrollbar ? total > maxItems : false; }
+ uint Top(void) { return current - currentOnScreen; }
+ uint Bottom(void) { return total - Top() - maxItems; }
+ bool CanScrollUp(void) { return Text2SkinSetup.MenuScrollbar ? Top() > 0 : false; }
+ bool CanScrollDown(void) { return Text2SkinSetup.MenuScrollbar ? Bottom() > 0 : false; }
+ } mMenuScrollbar;
+
+ // update infos (e.g. timerConflict)
+ struct tUpdate {
+ bool timerConflict;
+ bool events;
+ std::string currentItem;
+ bool resetMarquee;
+ bool foundFirstItem;
+
+ tUpdate(void) : timerConflict(true), events(true), currentItem(""), resetMarquee(true), foundFirstItem(false) {}
+ } mUpdate;
};
inline void cText2SkinRender::Flush(bool Force)
diff --git a/setup.c b/setup.c
index 575bda6..9abc238 100644
--- a/setup.c
+++ b/setup.c
@@ -9,11 +9,31 @@ cText2SkinSetup Text2SkinSetup;
// --- cText2SkinSetup --------------------------------------------------------
cText2SkinSetup::cText2SkinSetup(void) {
+ MenuScrollbar = false;
+ MarqueeLeftRight = true;
+ MarqueeReset = false;
+#if VDRVERSNUM >= 10344
+ ShowAux = true;
+ StripAux = true;
+#endif
+#if VDRVERSNUM >= 10330
+ CheckTimerConflict = false;
+#endif
MaxCacheFill = 25;
}
bool cText2SkinSetup::SetupParse(const char *Name, const char *Value) {
- if (strcmp(Name, "MaxCacheFill") == 0) MaxCacheFill = atoi(Value);
+ if (strcmp(Name, "MenuScrollbar") == 0) MenuScrollbar = atoi(Value);
+ else if (strcmp(Name, "MarqueeLeftRight") == 0) MarqueeLeftRight = atoi(Value);
+ else if (strcmp(Name, "MarqueeReset") == 0) MarqueeReset = atoi(Value);
+#if VDRVERSNUM >= 10344
+ else if (strcmp(Name, "ShowAux") == 0) ShowAux = atoi(Value);
+ else if (strcmp(Name, "StripAux") == 0) StripAux = atoi(Value);
+#endif
+#if VDRVERSNUM >= 10330
+ else if (strcmp(Name, "CheckTimerConflict") == 0) CheckTimerConflict = atoi(Value);
+#endif
+ else if (strcmp(Name, "MaxCacheFill") == 0) MaxCacheFill = atoi(Value);
else return false;
return true;
}
diff --git a/setup.h b/setup.h
index bf9e8e1..ae77b1c 100644
--- a/setup.h
+++ b/setup.h
@@ -13,6 +13,16 @@ public:
bool SetupParse(const char *Name, const char *Value);
+ int MenuScrollbar;
+ int MarqueeLeftRight;
+ int MarqueeReset;
+#if VDRVERSNUM >= 10344
+ int ShowAux;
+ int StripAux;
+#endif
+#if VDRVERSNUM >= 10330
+ int CheckTimerConflict;
+#endif
int MaxCacheFill;
};
diff --git a/status.c b/status.c
index 282fac6..9b25a7c 100644
--- a/status.c
+++ b/status.c
@@ -4,6 +4,10 @@
#include "status.h"
#include "render.h"
+#include "menu.h"
+#include <vdr/timers.h>
+#include <vdr/plugin.h>
+#include <vdr/menu.h>
const std::string ReplayNames[__REPLAY_COUNT__] =
{ "", "normal", "mp3", "mplayer", "dvd", "vcd", "image" };
@@ -51,9 +55,18 @@ void cText2SkinStatus::Replaying(const cControl* /*Control*/, const char *Name,
mReplayIsLoop = Name[1] == 'L';
mReplayIsShuffle = Name[2] == 'S';
}
- }
- else if (GetRecordingByName(Name) != NULL)
+ }
+#if VDRVERSNUM >= 10338
+ else if (const cRecording *rec = GetRecordingByFileName(FileName)) {
+ mReplay = rec;
mReplayMode = replayNormal;
+ }
+#else
+ else if (const cRecording *rec = GetRecordingByName(Name)) {
+ mReplay = rec;
+ mReplayMode = replayNormal;
+ }
+#endif
else if (strcmp(Name, "DVD") == 0)
mReplayMode = replayDVD;
else if (strcmp(Name, "VCD") == 0)
@@ -118,14 +131,286 @@ void cText2SkinStatus::OsdClear(void)
#endif
cxString::Reparse();
}
+
+ if (mRender != NULL)
+ mRender->mMenuScrollbar.total = 0;
+}
+
+void cText2SkinStatus::OsdCurrentItem(const char *Text)
+{
+ if (mRender && Text) {
+ // update infos
+ cText2SkinRender::tUpdate *u = &mRender->mUpdate;
+ //static std::string lastItem;
+
+ //lastItem = u->currentItem;
+ u->currentItem = Text;
+ u->resetMarquee = true;
+ u->foundFirstItem = false;
+
+ // find current item in scrollbar
+ if (Text2SkinSetup.MenuScrollbar) {
+ cText2SkinRender::tMenuScrollbar *sb = &mRender->mMenuScrollbar;
+ for (uint i = 0; i < sb->total; i++) {
+ if (sb->items[i] == Text) {
+ sb->current = i;
+ break;
+ }
+ }
+ }
+ }
+}
+
+void cText2SkinStatus::OsdItem(const char *Text, int Index)
+{
+ if (mRender && Text2SkinSetup.MenuScrollbar && Text) {
+ uint curr = (uint)Index;
+ cText2SkinRender::tMenuScrollbar *sb = &mRender->mMenuScrollbar;
+
+ if (curr < sb->items.size())
+ sb->items[curr] = Text;
+ else {
+ sb->items.push_back(Text);
+ sb->total = curr + 1;
+ }
+
+ if (curr + 1 > sb->total)
+ sb->total = curr + 1;
+ }
+}
+
+void cText2SkinStatus::UpdateEvents(void)
+{
+ if (mRender->mUpdate.events) {
+ mRender->mUpdate.events = false;
+
+ mEvents.Clear();
+ Timers.IncBeingEdited();
+
+ for (cTimer *tim = Timers.First(); tim; tim = Timers.Next(tim)) {
+ if (tim->HasFlags(tfActive)) {
+ int i = 0;
+ cTimer dummy;
+ dummy = *tim;
+
+ do {
+ mEvents.Add(new tEvent(&dummy));
+
+ if (!dummy.IsSingleEvent()) { // add 4 additional rep. timer
+ int j = 0;
+ do {
+ j++; // just to avoid a endless loop
+ dummy.Skip();
+ dummy.Matches(); // Refresh start- and end-time
+ } while (!dummy.DayMatches(dummy.StartTime()) && (j < 7));
+ }
+
+ i++;
+ } while (!dummy.IsSingleEvent() && i < 5);
+ }
+ }
+
+ Timers.DecBeingEdited();
+ mEvents.Sort();
+ }
}
cxType cText2SkinStatus::GetTokenData(const txToken &Token)
{
+ int event = 0;
+
switch (Token.Type) {
case tReplayMode:
return ReplayNames[mReplayMode];
+ case tFrontendSTR:
+ return GetFrontendSTR();
+
+ case tFrontendSNR:
+ return GetFrontendSNR();
+
+ case tFrontendHasLock:
+ return GetFrontendHasLock();
+
+ case tFrontendHasSignal:
+ return GetFrontendHasSignal();
+
+ case tCurrentEventsTitle3:
+ event++;
+ case tCurrentEventsTitle2:
+ event++;
+ case tCurrentEventsTitle1:
+ UpdateEvents();
+ return mEvents.Count() > event
+ ? (cxType)mEvents.Get(event)->title.c_str()
+ : (cxType)false;
+
+ case tCurrentEventsStartDateTime3:
+ event++;
+ case tCurrentEventsStartDateTime2:
+ event++;
+ case tCurrentEventsStartDateTime1:
+ UpdateEvents();
+ return mEvents.Count() > event
+ ? (cxType)TimeType(mEvents.Get(event)->startTime, Token.Attrib.Text)
+ : (cxType)false;
+
+ case tCurrentEventsStopDateTime3:
+ event++;
+ case tCurrentEventsStopDateTime2:
+ event++;
+ case tCurrentEventsStopDateTime1:
+ UpdateEvents();
+ return mEvents.Count() > event
+ ? (cxType)TimeType(mEvents.Get(event)->stopTime, Token.Attrib.Text)
+ : (cxType)false;
+
+ case tCurrentEventsChannelNumber3:
+ event++;
+ case tCurrentEventsChannelNumber2:
+ event++;
+ case tCurrentEventsChannelNumber1:
+ UpdateEvents();
+ return mEvents.Count() > event
+ ? (cxType)mEvents.Get(event)->channelNumber
+ : (cxType)false;
+
+ case tCurrentEventsChannelName3:
+ event++;
+ case tCurrentEventsChannelName2:
+ event++;
+ case tCurrentEventsChannelName1:
+ UpdateEvents();
+ return mEvents.Count() > event
+ ? (cxType)mEvents.Get(event)->channelName.c_str()
+ : (cxType)false;
+
+ case tCurrentEventsIsRecording3:
+ event++;
+ case tCurrentEventsIsRecording2:
+ event++;
+ case tCurrentEventsIsRecording1:
+ UpdateEvents();
+ return mEvents.Count() > event
+ ? (cxType)mEvents.Get(event)->isRecording
+ : (cxType)false;
+
+ case tTimerConflicts:
+#if VDRVERSNUM >= 10330
+ if (Text2SkinSetup.CheckTimerConflict) {
+ if (mRender->mUpdate.timerConflict) {
+ Epgsearch_lastconflictinfo_v1_0 conflict;
+ mRender->mUpdate.timerConflict = false;
+
+ if (cPluginManager::CallFirstService("Epgsearch-lastconflictinfo-v1.0", &conflict))
+ mTimerConflicts = conflict.relevantConflicts;
+ else
+ mTimerConflicts = 0;
+ }
+ return mTimerConflicts;
+ } else
+#endif
+ return 0;
+
+#if VDRVERSNUM >= 10325
+#if VDRVERSNUM >= 10338
+ case tReplayName:
+ return mReplay != NULL
+ ? (cxType)mReplay->Name()
+ : (cxType)false;
+
+ case tReplayDateTime:
+ return mReplay != NULL
+ ? (cxType)TimeType(mReplay->start, Token.Attrib.Text)
+ : (cxType)false;
+
+ case tReplayShortText:
+ return mReplay != NULL
+ ? (cxType)mReplay->Info()->ShortText()
+ : (cxType)false;
+
+ case tReplayDescription:
+ return mReplay != NULL
+ ? (cxType)mReplay->Info()->Description()
+ : (cxType)false;
+#else
+ case tReplayName:
+ return (cxType)false;
+
+ case tReplayDateTime:
+ return (cxType)false;
+
+ case tReplayShortText:
+ return (cxType)false;
+
+ case tReplayDescription:
+ return (cxType)false;
+#endif
+
+ case tReplayLanguageCode:
+ if (mReplay) {
+ const cComponents *components = mReplay->Info()->Components();
+ if (components) {
+ int index = Token.Attrib.Number;
+
+ // don't return language-code for the video-stream
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream != 2) // only audio-streams
+ index++;
+ {
+ std::string buffer(c->language);
+ if (c->type == 1)
+ buffer.append("MONO");
+ if ((c->type == 2) || (c->type == 4))
+ buffer.append("DUAL");
+ if (c->type == 5)
+ buffer.append("DD");
+ return (cxType)buffer.c_str();
+ }
+ }
+ }
+ }
+ return false;
+
+ case tReplayLanguageDescription:
+ if (mReplay) {
+ const cComponents *components = mReplay->Info()->Components();
+ if (components) {
+ int index = Token.Attrib.Number;
+
+ // don't return language-code for the video-stream
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream != 2) // only audio-streams
+ index++;
+ if (i == index)
+ return (cxType)c->description;
+ }
+ }
+ }
+ return false;
+
+ case tReplayVideoAR:
+ if (mReplay) {
+ const cComponents *components = mReplay->Info()->Components();
+ if (components) {
+ for (int i = 0; i < components->NumComponents(); i++) {
+ const tComponent *c = components->Component(i);
+ if (c->stream == 1) {
+ switch (c->type) {
+ case 1:
+ return "4:3";
+ case 3:
+ return "16:9";
+ }
+ }
+ }
+ }
+ }
+ return false;
+#endif
+
case tCurrentRecording:
Dprintf("token attrib type is: %d, number: %d\n", Token.Attrib.Type, Token.Attrib.Number);
if (Token.Attrib.Type == aNumber) {
diff --git a/status.h b/status.h
index 16728e8..515214c 100644
--- a/status.h
+++ b/status.h
@@ -27,16 +27,50 @@ public:
typedef std::string tRecordingInfo;
typedef std::vector<tRecordingInfo> tRecordings;
+ struct tEvent : public cListObject {
+ time_t startTime;
+ time_t stopTime;
+ int channelNumber;
+ std::string channelName;
+ int priority;
+ bool isRecording;
+ std::string title;
+
+ tEvent(cTimer *timer) :
+ startTime(timer->StartTime()),
+ stopTime(timer->StopTime()),
+ channelNumber(timer->Channel()->Number()),
+ channelName(timer->Channel()->Name()),
+ priority(timer->Priority()),
+ isRecording(timer->Recording()),
+ title(timer->File()) {}
+
+ virtual int Compare(const cListObject &listObj) const {
+ tEvent *e = (tEvent *)&listObj;
+ int r = startTime - e->startTime;
+ if (r == 0)
+ r = e->priority - priority;
+ return r;
+ }
+ };
+
+ typedef std::vector<tEvent> tEvents;
+
private:
+ void UpdateEvents(void);
+
cText2SkinRender *mRender;
eReplayMode mReplayMode;
bool mReplayIsLoop;
bool mReplayIsShuffle;
tRecordings mRecordings;
+ const cRecording *mReplay;
+ cList<tEvent> mEvents;
cMutex mRecordingsLock;
uint mCurrentRecording;
uint mNextRecording;
int mLastLanguage;
+ int mTimerConflicts;
protected:
virtual void Replaying(const cControl *Control, const char *Name,
@@ -45,6 +79,9 @@ protected:
const char *FileName, bool On);
virtual void OsdClear(void);
+ virtual void OsdCurrentItem(const char *Text);
+ virtual void OsdItem(const char *Text, int Index);
+
public:
cText2SkinStatus(void);
diff --git a/text2skin.c b/text2skin.c
index f45496d..30c806b 100644
--- a/text2skin.c
+++ b/text2skin.c
@@ -7,6 +7,7 @@
*/
#include "text2skin.h"
+#include "bitmap.h"
#include "setup.h"
#include "menu.h"
#include "i18n.h"
@@ -23,6 +24,29 @@ cText2SkinPlugin::cText2SkinPlugin(void) {
cText2SkinPlugin::~cText2SkinPlugin() {
}
+#if VDRVERSNUM >= 10331
+const char **cText2SkinPlugin::SVDRPHelpPages(void)
+{
+ static const char *HelpPages[] = {
+ "FLUS\n"
+ " Flush the image cache (useful if images have changed and the"
+ " current version should be loaded).",
+ NULL
+ };
+ return HelpPages;
+}
+
+cString cText2SkinPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ if (strcasecmp(Command, "FLUS") == 0) {
+ // we use the default reply code here
+ cText2SkinBitmap::FlushCache();
+ return "image cache flushed.";
+ }
+ return NULL;
+}
+#endif
+
bool cText2SkinPlugin::Start(void) {
#if VDRVERSNUM < 10507
RegisterI18n(Phrases);
diff --git a/text2skin.h b/text2skin.h
index b40ea54..19fa341 100644
--- a/text2skin.h
+++ b/text2skin.h
@@ -21,6 +21,10 @@ public:
virtual ~cText2SkinPlugin();
virtual const char *Version(void) { return VERSION; }
virtual const char *Description(void) { return tr(DESCRIPTION); }
+#if VDRVERSNUM >= 10331
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+#endif
virtual bool Start(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
diff --git a/xml/object.c b/xml/object.c
index 6ea0ab6..4a3beec 100644
--- a/xml/object.c
+++ b/xml/object.c
@@ -144,14 +144,13 @@ const cFont *cxObject::Font(void) const
{
const cFont *font;
- if ((font = cText2SkinFont::Load(SkinPath() + "/fonts", mFontFace, mFontSize, mFontWidth))
- != NULL)
- return font;
-
if ((font = cText2SkinFont::Load(SkinPath() + "/" + mSkin->Name(), mFontFace, mFontSize,
mFontWidth)) != NULL)
return font;
-
+
+ if ((font = cText2SkinFont::Load(SkinPath() + "/fonts", mFontFace, mFontSize, mFontWidth)) != NULL)
+ return font;
+
return cFont::GetFont(fontOsd);
}
diff --git a/xml/string.c b/xml/string.c
index afc0c7a..3f671bd 100644
--- a/xml/string.c
+++ b/xml/string.c
@@ -11,7 +11,12 @@ static const char *Tokens[__COUNT_TOKEN__] = {
// Channel Display
"ChannelNumber", "ChannelName", "ChannelShortName", "ChannelBouquet", "ChannelPortal",
"ChannelSource", "ChannelID", "PresentStartDateTime", "PresentVPSDateTime",
+ "CurrentEventsTitle1", "CurrentEventsStartDateTime1", "CurrentEventsStopDateTime1", "CurrentEventsChannelNumber1", "CurrentEventsChannelName1", "CurrentEventsIsRecording1",
+ "CurrentEventsTitle2", "CurrentEventsStartDateTime2", "CurrentEventsStopDateTime2", "CurrentEventsChannelNumber2", "CurrentEventsChannelName2", "CurrentEventsIsRecording2",
+ "CurrentEventsTitle3", "CurrentEventsStartDateTime3", "CurrentEventsStopDateTime3", "CurrentEventsChannelNumber3", "CurrentEventsChannelName3", "CurrentEventsIsRecording3",
+ "TimerConflicts",
"PresentEndDateTime", "PresentDuration", "PresentProgress", "PresentRemaining",
+ "PresentLanguageCode", "PresentLanguageDescription", "PresentVideoAR", "PresentEventID",
"PresentTitle", "PresentShortText", "PresentDescription", "FollowingStartDateTime",
"FollowingVPSDateTime", "FollowingEndDateTime", "FollowingDuration",
"FollowingTitle", "FollowingShortText", "FollowingDescription", "Language",
@@ -30,6 +35,8 @@ static const char *Tokens[__COUNT_TOKEN__] = {
// Replay Display
"ReplayTitle", "ReplayPositionIndex", "ReplayDurationIndex", "ReplayPrompt",
+ "ReplayName", "ReplayDateTime", "ReplayShortText", "ReplayDescription",
+ "ReplayLanguageCode", "ReplayLanguageDescription", "ReplayVideoAR",
"IsPlaying", "ReplayIsPlaying", "IsFastForward", "ReplayIsFastForward", "IsFastRewind",
"ReplayIsFastRewind", "IsSlowForward", "ReplayIsSlowForward", "IsSlowRewind",
"ReplayIsSlowRewind", "IsPausing", "ReplayIsPausing",
@@ -38,8 +45,10 @@ static const char *Tokens[__COUNT_TOKEN__] = {
// Menu Page
"MenuTitle", "MenuGroup", "IsMenuGroup", "MenuItem", "IsMenuItem", "MenuCurrent",
- "IsMenuCurrent", "MenuText", "RecordingName", "RecordingDateTime", "RecordingTitle",
+ "IsMenuCurrent", "MenuText", "RecordingName", "RecordingFilename", "RecordingDateTime", "RecordingTitle",
"RecordingShortText", "RecordingDescription", "RecordingLanguageCode",
+ "FrontendSTR", "FrontendSNR", "FrontendHasLock", "FrontendHasSignal", "RecordingPriority", "RecordingLifetime",
+ "RecordingVideoAR", "RecordingSize", "RecordingLength", "RecordingCuttedLength", "OsdWidth", "OsdHeight",
"RecordingLanguageDescription", "ButtonRed", "ButtonGreen",
"ButtonYellow", "ButtonBlue", "CanScrollUp", "CanScrollDown"
};
diff --git a/xml/string.h b/xml/string.h
index 7c3f30f..a459253 100644
--- a/xml/string.h
+++ b/xml/string.h
@@ -26,10 +26,33 @@ enum exToken {
// next 9 also in Menu
tPresentStartDateTime,
tPresentVPSDateTime,
+ tCurrentEventsTitle1,
+ tCurrentEventsStartDateTime1,
+ tCurrentEventsStopDateTime1,
+ tCurrentEventsChannelNumber1,
+ tCurrentEventsChannelName1,
+ tCurrentEventsIsRecording1,
+ tCurrentEventsTitle2,
+ tCurrentEventsStartDateTime2,
+ tCurrentEventsStopDateTime2,
+ tCurrentEventsChannelNumber2,
+ tCurrentEventsChannelName2,
+ tCurrentEventsIsRecording2,
+ tCurrentEventsTitle3,
+ tCurrentEventsStartDateTime3,
+ tCurrentEventsStopDateTime3,
+ tCurrentEventsChannelNumber3,
+ tCurrentEventsChannelName3,
+ tCurrentEventsIsRecording3,
+ tTimerConflicts,
tPresentEndDateTime,
tPresentDuration,
tPresentProgress,
tPresentRemaining,
+ tPresentLanguageCode,
+ tPresentLanguageDescription,
+ tPresentVideoAR,
+ tPresentEventID,
tPresentTitle,
tPresentShortText,
tPresentDescription,
@@ -85,6 +108,13 @@ enum exToken {
tReplayPositionIndex,
tReplayDurationIndex,
tReplayPrompt,
+ tReplayName,
+ tReplayDateTime,
+ tReplayShortText,
+ tReplayDescription,
+ tReplayLanguageCode,
+ tReplayLanguageDescription,
+ tReplayVideoAR,
tIsPlaying,
tReplayIsPlaying, // alias
tIsFastForward,
@@ -115,11 +145,24 @@ enum exToken {
tMenuText,
// Recordings Page
tRecordingName,
+ tRecordingFilename,
tRecordingDateTime,
tRecordingTitle,
tRecordingShortText,
tRecordingDescription,
tRecordingLanguageCode,
+ tFrontendSTR,
+ tFrontendSNR,
+ tFrontendHasLock,
+ tFrontendHasSignal,
+ tRecordingPriority,
+ tRecordingLifetime,
+ tRecordingVideoAR,
+ tRecordingSize,
+ tRecordingLength,
+ tRecordingCuttedLength,
+ tOsdWidth,
+ tOsdHeight,
tRecordingLanguageDescription,
// next four also in Channel and Replay display (if supported by vdr/plugin)
tButtonRed,