summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkamel5 <vdr.kamel5 (at) gmx (dot) net>2019-06-21 14:09:35 +0200
committerkamel5 <vdr.kamel5 (at) gmx (dot) net>2019-06-21 14:09:35 +0200
commit751e903301844f10d37822b7eb1f5b28b94be68e (patch)
tree8daf208a21f85abd693d9e7e9485871fad38c10f
parent8a04a17fc0a48f99903c3f5d2a0a30b238cbd7f7 (diff)
downloadvdr-plugin-skindesigner-751e903301844f10d37822b7eb1f5b28b94be68e.tar.gz
vdr-plugin-skindesigner-751e903301844f10d37822b7eb1f5b28b94be68e.tar.bz2
Add vdr-2.4.0_zapcockpit.patch
-rw-r--r--patches/vdr-2.4.0_zapcockpit.patch1118
1 files changed, 1118 insertions, 0 deletions
diff --git a/patches/vdr-2.4.0_zapcockpit.patch b/patches/vdr-2.4.0_zapcockpit.patch
new file mode 100644
index 0000000..3a2f43f
--- /dev/null
+++ b/patches/vdr-2.4.0_zapcockpit.patch
@@ -0,0 +1,1118 @@
+diff -Nur vdr-2.4.0/config.c vdr-2.4.0.p/config.c
+--- vdr-2.4.0/config.c 2018-02-15 15:40:36.000000000 +0100
++++ vdr-2.4.0.p/config.c 2019-04-04 14:57:48.234702963 +0200
+@@ -417,6 +417,11 @@
+ strcpy(SVDRPDefaultHost, "");
+ ZapTimeout = 3;
+ ChannelEntryTimeout = 1000;
++ ZapcockpitUseGroups = 1;
++ ZapcockpitUseHints = 1;
++ ZapcockpitUseInfo = 1;
++ ZapcockpitHideLastGroup = 0;
++ ZapcockpitShowAllChannels = 0;
+ RcRepeatDelay = 300;
+ RcRepeatDelta = 100;
+ DefaultPriority = 50;
+@@ -645,6 +650,11 @@
+ else if (!strcasecmp(Name, "SVDRPDefaultHost")) strn0cpy(SVDRPDefaultHost, Value, sizeof(SVDRPDefaultHost));
+ else if (!strcasecmp(Name, "ZapTimeout")) ZapTimeout = atoi(Value);
+ else if (!strcasecmp(Name, "ChannelEntryTimeout")) ChannelEntryTimeout= atoi(Value);
++ else if (!strcasecmp(Name, "ZapcockpitUseGroups")) ZapcockpitUseGroups= atoi(Value);
++ else if (!strcasecmp(Name, "ZapcockpitUseHints")) ZapcockpitUseHints = atoi(Value);
++ else if (!strcasecmp(Name, "ZapcockpitUseInfo")) ZapcockpitUseInfo = atoi(Value);
++ else if (!strcasecmp(Name, "ZapcockpitHideLastGroup")) ZapcockpitHideLastGroup = atoi(Value);
++ else if (!strcasecmp(Name, "ZapcockpitShowAllChannels")) ZapcockpitShowAllChannels = atoi(Value);
+ else if (!strcasecmp(Name, "RcRepeatDelay")) RcRepeatDelay = atoi(Value);
+ else if (!strcasecmp(Name, "RcRepeatDelta")) RcRepeatDelta = atoi(Value);
+ else if (!strcasecmp(Name, "DefaultPriority")) DefaultPriority = atoi(Value);
+@@ -777,6 +787,11 @@
+ Store("SVDRPDefaultHost", SVDRPDefaultHost);
+ Store("ZapTimeout", ZapTimeout);
+ Store("ChannelEntryTimeout",ChannelEntryTimeout);
++ Store("ZapcockpitUseGroups",ZapcockpitUseGroups);
++ Store("ZapcockpitUseHints", ZapcockpitUseHints);
++ Store("ZapcockpitUseInfo", ZapcockpitUseInfo);
++ Store("ZapcockpitHideLastGroup", ZapcockpitHideLastGroup);
++ Store("ZapcockpitShowAllChannels", ZapcockpitShowAllChannels);
+ Store("RcRepeatDelay", RcRepeatDelay);
+ Store("RcRepeatDelta", RcRepeatDelta);
+ Store("DefaultPriority", DefaultPriority);
+diff -Nur vdr-2.4.0/config.h vdr-2.4.0.p/config.h
+--- vdr-2.4.0/config.h 2018-03-19 16:06:46.000000000 +0100
++++ vdr-2.4.0.p/config.h 2019-04-04 14:57:48.235702949 +0200
+@@ -293,6 +293,11 @@
+ char SVDRPDefaultHost[HOST_NAME_MAX];
+ int ZapTimeout;
+ int ChannelEntryTimeout;
++ int ZapcockpitUseGroups;
++ int ZapcockpitUseHints;
++ int ZapcockpitUseInfo;
++ int ZapcockpitHideLastGroup;
++ int ZapcockpitShowAllChannels;
+ int RcRepeatDelay;
+ int RcRepeatDelta;
+ int DefaultPriority, DefaultLifetime;
+diff -Nur vdr-2.4.0/menu.c vdr-2.4.0.p/menu.c
+--- vdr-2.4.0/menu.c 2019-04-04 15:47:25.722519143 +0200
++++ vdr-2.4.0.p/menu.c 2019-04-04 15:29:02.650105356 +0200
+@@ -4184,6 +4184,11 @@
+ }
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Zap timeout (s)"), &data.ZapTimeout));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Channel entry timeout (ms)"), &data.ChannelEntryTimeout, 0));
++ Add(new cMenuEditBoolItem( tr("Setup.Miscellaneous$Zapcockpit: 2nd ok shows info"), &data.ZapcockpitUseInfo));
++ Add(new cMenuEditBoolItem( tr("Setup.Miscellaneous$Zapcockpit: Use extended channel group display"), &data.ZapcockpitUseGroups));
++ Add(new cMenuEditBoolItem( tr("Setup.Miscellaneous$Zapcockpit: Use channel hints"), &data.ZapcockpitUseHints));
++ Add(new cMenuEditBoolItem( tr("Setup.Miscellaneous$Zapcockpit: Hide last channel group"), &data.ZapcockpitHideLastGroup));
++ Add(new cMenuEditBoolItem( tr("Setup.Miscellaneous$Zapcockpit: Show \"All Channels\" Item in Group List"), &data.ZapcockpitShowAllChannels));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Remote control repeat delay (ms)"), &data.RcRepeatDelay, 0));
+ Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Remote control repeat delta (ms)"), &data.RcRepeatDelta, 0));
+ Add(new cMenuEditChanItem(tr("Setup.Miscellaneous$Initial channel"), &data.InitialChannel, tr("Setup.Miscellaneous$as before")));
+@@ -4654,7 +4659,7 @@
+ lastTime.Set();
+ }
+
+-cDisplayChannel::cDisplayChannel(eKeys FirstKey)
++cDisplayChannel::cDisplayChannel(eKeys FirstKey, bool processKey)
+ :cOsdObject(true)
+ {
+ currentDisplayChannel = this;
+@@ -4672,7 +4677,8 @@
+ LOCK_CHANNELS_READ;
+ channel = Channels->GetByNumber(cDevice::CurrentChannel());
+ }
+- ProcessKey(FirstKey);
++ if (processKey)
++ ProcessKey(FirstKey);
+ }
+
+ cDisplayChannel::~cDisplayChannel()
+@@ -4922,6 +4928,793 @@
+ return osEnd;
+ }
+
++// --- cGroupListItem -------------------------------------------------------
++const char *cGroupListItem::GroupName(void) {
++ if (channel)
++ return channel->Name();
++ return tr("Setup.Miscellaneous$All Channels");
++}
++
++// --- cDisplayChannelExtended -------------------------------------------------------
++cDisplayChannelExtended::cDisplayChannelExtended(int Number, bool Switched)
++:cDisplayChannel(Number, Switched)
++{
++ state = esDefault;
++ keyRightOpensChannellist = -1;
++ numItemsChannel = 0;
++ currentChannel = -1;
++ startChannel = -1;
++ numItemsGroup = 0;
++ currentGroup = -1;
++ startGroup = -1;
++}
++
++cDisplayChannelExtended::cDisplayChannelExtended(eKeys FirstKey)
++:cDisplayChannel(FirstKey, false)
++{
++ state = esInit;
++ keyRightOpensChannellist = -1;
++ numItemsChannel = 0;
++ currentChannel = -1;
++ startChannel = -1;
++ numItemsGroup = 0;
++ currentGroup = -1;
++ startGroup = -1;
++}
++
++cDisplayChannelExtended::~cDisplayChannelExtended()
++{
++}
++
++eOSState cDisplayChannelExtended::ProcessKey(eKeys Key)
++{
++ cSkinDisplayChannelExtended *displayChannelExtended = dynamic_cast<cSkinDisplayChannelExtended*>(displayChannel);
++ if (!displayChannelExtended)
++ return cDisplayChannel::ProcessKey(Key);
++
++ if (Key != kNone)
++ lastTime.Set();
++
++ bool keyHandeled = false;
++ //number keys are always handled by default state
++ if ((int)Key >= k0 && (int)Key <= k9) {
++ displayChannelExtended->SetViewType(dcDefault);
++ StateNumberKey((int)Key, displayChannelExtended);
++ state = esDefault;
++ } else if (number <= 0) {
++ switch (state) {
++ case esInit:
++ keyHandeled = StateInit((int)Key, displayChannelExtended);
++ break;
++ case esDefault:
++ keyHandeled = StateDefault((int)Key, displayChannelExtended);
++ break;
++ case esChannelInfo:
++ keyHandeled = StateChannelInfo((int)Key, displayChannelExtended);
++ break;
++ case esChannelList:
++ case esChannelListInfo:
++ keyHandeled = StateChannelList((int)Key, displayChannelExtended);
++ break;
++ case esGroupsList:
++ keyHandeled = StateGroupList((int)Key, displayChannelExtended);
++ break;
++ case esGroupsChannelList:
++ case esGroupsChannelListInfo:
++ keyHandeled = StateGroupChannelList((int)Key, displayChannelExtended);
++ break;
++ default:
++ break;
++ }
++ }
++ if (state == esClose)
++ return osEnd;
++ //in extended state, no timeout
++ if (state != esDefault)
++ lastTime.Set();
++
++ //do own flush for all lists
++ if (keyHandeled || (Key == kNone && state > esChannelInfo)) {
++ SetNeedsFastResponse(false);
++ displayChannel->Flush();
++ return osContinue;
++ }
++
++ return cDisplayChannel::ProcessKey(Key);
++}
++
++void cDisplayChannelExtended::StateNumberKey(int key, cSkinDisplayChannelExtended *dcExt)
++{
++ if (!Setup.ZapcockpitUseHints)
++ return;
++ if (number < 0)
++ return;
++ LOCK_CHANNELS_READ;
++ int selectedChannel = number > Channels->MaxNumber() ? key - k0 : number * 10 + key - k0;
++ int candidateStartNumber = selectedChannel * 10;
++ channellist.Clear();
++ const cChannel *candidatesStart = Channels->GetByNumber(candidateStartNumber);
++ int numHints = 0;
++ for (const cChannel *candidate = candidatesStart; candidate; candidate = Channels->Next(candidate)) {
++ if (candidate->GroupSep())
++ continue;
++ numHints++;
++ if (candidate->Number() >= candidateStartNumber + 9)
++ break;
++ }
++ if (numHints == 0)
++ return;
++ dcExt->SetNumChannelHints(numHints);
++ for (const cChannel *candidate = candidatesStart; candidate; candidate = Channels->Next(candidate)) {
++ if (candidate->GroupSep())
++ continue;
++ dcExt->SetChannelHint(candidate);
++ if (candidate->Number() >= candidateStartNumber + 9)
++ break;
++ }
++}
++
++bool cDisplayChannelExtended::StateInit(int key, cSkinDisplayChannelExtended *dcExt)
++{
++ if (keyRightOpensChannellist == -1)
++ keyRightOpensChannellist = dcExt->KeyRightOpensChannellist() ? 1 : 0;
++
++ bool keyHandeled = false;
++ switch (key) {
++ case kLeft|k_Repeat: case kLeft:
++ case kPrev|k_Repeat: case kPrev: {
++ if (!Setup.ZapcockpitUseGroups)
++ return false;
++ cOsdProvider::OsdSizeChanged(osdState); // just to get the current state
++ DisplayChannel();
++ DisplayInfo();
++ if (keyRightOpensChannellist) {
++ InitGroupList(dcExt);
++ state = esGroupsList;
++ } else {
++ InitChannelList(dcExt);
++ state = esChannelList;
++ }
++ keyHandeled = true;
++ break;
++ }
++ case kRight|k_Repeat: case kRight:
++ case kNext|k_Repeat: case kNext: {
++ if (!Setup.ZapcockpitUseGroups)
++ return false;
++ cOsdProvider::OsdSizeChanged(osdState); // just to get the current state
++ DisplayChannel();
++ DisplayInfo();
++ if (keyRightOpensChannellist) {
++ InitChannelList(dcExt);
++ state = esChannelList;
++ } else {
++ InitGroupList(dcExt);
++ state = esGroupsList;
++ }
++ keyHandeled = true;
++ break;
++ }
++ //other keys are handled by cDisplayChannel::ProcessKeys()
++ default:
++ dcExt->SetViewType(dcDefault);
++ state = esDefault;
++ break;
++ }
++ return keyHandeled;
++}
++
++bool cDisplayChannelExtended::StateDefault(int key, cSkinDisplayChannelExtended *dcExt)
++{
++ if (keyRightOpensChannellist == -1)
++ keyRightOpensChannellist = dcExt->KeyRightOpensChannellist() ? 1 : 0;
++ bool keyHandeled = false;
++ switch (key) {
++ //2nd ok opens extended info for current channel
++ case kOk: {
++ if (!Setup.ZapcockpitUseInfo)
++ return false;
++ dcExt->SetViewType(dcChannelInfo);
++ dcExt->SetChannelInfo(channel);
++ state = esChannelInfo;
++ keyHandeled = true;
++ break;
++ }
++ case kLeft|k_Repeat: case kLeft:
++ case kPrev|k_Repeat: case kPrev: {
++ if (!Setup.ZapcockpitUseGroups)
++ return false;
++ if (keyRightOpensChannellist) {
++ InitGroupList(dcExt);
++ state = esGroupsList;
++ } else {
++ InitChannelList(dcExt);
++ state = esChannelList;
++ }
++ keyHandeled = true;
++ break;
++ }
++ case kRight|k_Repeat: case kRight:
++ case kNext|k_Repeat: case kNext: {
++ if (!Setup.ZapcockpitUseGroups)
++ return false;
++ if (keyRightOpensChannellist) {
++ InitChannelList(dcExt);
++ state = esChannelList;
++ } else {
++ InitGroupList(dcExt);
++ state = esGroupsList;
++ }
++ keyHandeled = true;
++ break;
++ }
++ //other keys are handled by cDisplayChannel::ProcessKeys()
++ default:
++ break;
++ }
++ return keyHandeled;
++}
++
++bool cDisplayChannelExtended::StateChannelInfo(int key, cSkinDisplayChannelExtended *dcExt)
++{
++ bool keyHandeled = false;
++ switch (key) {
++ //ok closes here
++ case kOk:
++ state = esDefault;
++ break;
++ //channel switching is handled by default state
++ case kUp|k_Repeat: case kUp:
++ case kDown|k_Repeat: case kDown:
++ case kChanUp|k_Repeat: case kChanUp:
++ case kChanDn|k_Repeat: case kChanDn:
++ dcExt->SetViewType(dcDefault);
++ state = esDefault;
++ break;
++ case kUp|k_Release: case kDown|k_Release:
++ case kChanUp|k_Release: case kChanDn|k_Release:
++ case kNext|k_Release: case kPrev|k_Release:
++ dcExt->SetViewType(dcDefault);
++ state = esDefault;
++ break;
++ case kLeft|k_Repeat: case kLeft:
++ case kPrev|k_Repeat: case kPrev: {
++ if (!Setup.ZapcockpitUseGroups)
++ return false;
++ if (keyRightOpensChannellist) {
++ InitGroupList(dcExt);
++ state = esGroupsList;
++ } else {
++ InitChannelList(dcExt);
++ state = esChannelList;
++ }
++ keyHandeled = true;
++ break;
++ }
++ case kRight|k_Repeat: case kRight:
++ case kNext|k_Repeat: case kNext: {
++ if (!Setup.ZapcockpitUseGroups)
++ return false;
++ if (keyRightOpensChannellist) {
++ InitChannelList(dcExt);
++ state = esChannelList;
++ } else {
++ InitGroupList(dcExt);
++ state = esGroupsList;
++ }
++ keyHandeled = true;
++ break;
++ }
++ default:
++ break;
++ }
++ return keyHandeled;
++}
++
++bool cDisplayChannelExtended::StateChannelList(int key, cSkinDisplayChannelExtended *dcExt)
++{
++ bool keyHandeled = false;
++ switch (key) {
++ //ok switches to the selected channel
++ case kOk: {
++ bool ok = SwitchChannel();
++ dcExt->SetViewType(dcDefault);
++ if (!ok)
++ keyHandeled = true;
++ state = esDefault;
++ break;
++ }
++ //scrolling up / down
++ case kUp|k_Repeat: case kUp:
++ state = esChannelList;
++ dcExt->SetViewType(dcChannelList);
++ CursorUp(dcExt);
++ keyHandeled = true;
++ break;
++ case kDown|k_Repeat: case kDown:
++ state = esChannelList;
++ dcExt->SetViewType(dcChannelList);
++ CursorDown(dcExt);
++ keyHandeled = true;
++ break;
++ case kLeft|k_Repeat: case kLeft: {
++ keyHandeled = true;
++ if (keyRightOpensChannellist) {
++ if (state == esChannelList) {
++ state = esClose;
++ } else if (state == esChannelListInfo) {
++ dcExt->SetViewType(dcChannelList);
++ state = esChannelList;
++ }
++ } else
++ ShowChannellistInfo(dcExt, dcChannelListInfo);
++ break;
++ }
++ //right shows extended info of currently selected channel
++ case kRight|k_Repeat: case kRight: {
++ keyHandeled = true;
++ if (keyRightOpensChannellist)
++ ShowChannellistInfo(dcExt, dcChannelListInfo);
++ else {
++ if (state == esChannelList) {
++ state = esClose;
++ } else if (state == esChannelListInfo) {
++ dcExt->SetViewType(dcChannelList);
++ state = esChannelList;
++ }
++ }
++ break;
++ }
++ default:
++ break;
++ }
++ return keyHandeled;
++}
++
++bool cDisplayChannelExtended::StateGroupList(int key, cSkinDisplayChannelExtended *dcExt)
++{
++ bool keyHandeled = false;
++ switch (key) {
++ //ok switches to first channel in group
++ case kOk: {
++ bool ok = SwitchChannel();
++ dcExt->SetViewType(dcDefault);
++ if (!ok)
++ keyHandeled = true;
++ state = esDefault;
++ break;
++ }
++ //scrolling up / down
++ case kUp|k_Repeat: case kUp:
++ state = esGroupsList;
++ CursorUp(dcExt);
++ dcExt->SetViewType(dcGroupsList);
++ keyHandeled = true;
++ break;
++ case kDown|k_Repeat: case kDown:
++ state = esGroupsList;
++ CursorDown(dcExt);
++ dcExt->SetViewType(dcGroupsList);
++ keyHandeled = true;
++ break;
++ case kLeft|k_Repeat: case kLeft:
++ keyHandeled = true;
++ if (keyRightOpensChannellist) {
++ state = esGroupsChannelList;
++ InitGroupChannelList(dcExt);
++ } else
++ state = esClose;
++ break;
++ case kRight|k_Repeat: case kRight:
++ keyHandeled = true;
++ if (keyRightOpensChannellist)
++ state = esClose;
++ else {
++ state = esGroupsChannelList;
++ InitGroupChannelList(dcExt);
++ }
++ break;
++ default:
++ break;
++ }
++ return keyHandeled;
++}
++
++bool cDisplayChannelExtended::StateGroupChannelList(int key, cSkinDisplayChannelExtended *dcExt)
++{
++ bool keyHandeled = false;
++ switch (key) {
++ //ok switches to the selected channel
++ case kOk: {
++ bool ok = SwitchChannel();
++ dcExt->SetViewType(dcDefault);
++ if (!ok)
++ keyHandeled = true;
++ state = esDefault;
++ break;
++ }
++ //scrolling up / down
++ case kUp|k_Repeat: case kUp:
++ state = esGroupsChannelList;
++ dcExt->SetViewType(dcGroupsChannelList);
++ CursorUp(dcExt);
++ keyHandeled = true;
++ break;
++ case kDown|k_Repeat: case kDown:
++ state = esGroupsChannelList;
++ dcExt->SetViewType(dcGroupsChannelList);
++ CursorDown(dcExt);
++ keyHandeled = true;
++ break;
++ case kLeft|k_Repeat: case kLeft: {
++ keyHandeled = true;
++ if (keyRightOpensChannellist)
++ ShowChannellistInfo(dcExt, dcGroupsChannelListInfo);
++ else {
++ if (state == esGroupsChannelList) {
++ state = esGroupsList;
++ dcExt->SetViewType(dcGroupsList);
++ } else if (state == esGroupsChannelListInfo) {
++ state = esGroupsChannelList;
++ dcExt->SetViewType(dcGroupsChannelList);
++ }
++ }
++ break;
++ }
++ case kRight|k_Repeat: case kRight: {
++ keyHandeled = true;
++ if (keyRightOpensChannellist) {
++ if (state == esGroupsChannelList) {
++ state = esGroupsList;
++ dcExt->SetViewType(dcGroupsList);
++ } else if (state == esGroupsChannelListInfo) {
++ state = esGroupsChannelList;
++ dcExt->SetViewType(dcGroupsChannelList);
++ }
++ } else
++ ShowChannellistInfo(dcExt, dcGroupsChannelListInfo);
++ break;
++ }
++ default:
++ break;
++ }
++ return keyHandeled;
++}
++
++void cDisplayChannelExtended::ShowChannellistInfo(cSkinDisplayChannelExtended *dcExt, eDisplaychannelView newViewType) {
++ if (newViewType == dcChannelListInfo && state != esChannelList)
++ return;
++ if (newViewType == dcGroupsChannelListInfo && state != esGroupsChannelList)
++ return;
++
++ cChannelListItem *li = channellist.Get(currentChannel);
++ if (li) {
++ const cChannel *selected = li->Channel();
++ if (selected) {
++ dcExt->SetViewType(newViewType);
++ dcExt->SetChannelInfo(selected);
++ state = (newViewType == dcChannelListInfo) ? esChannelListInfo : esGroupsChannelListInfo;
++ }
++ }
++}
++
++void cDisplayChannelExtended::InitChannelList(cSkinDisplayChannelExtended *dcExt)
++{
++ dcExt->SetViewType(dcChannelList);
++ numItemsChannel = dcExt->MaxItems();
++ if (numItemsChannel < 1)
++ return;
++ SetChannelList();
++ currentChannel = GetIndexChannel(channel);
++ if (currentChannel < 0)
++ currentChannel = 0;
++ startChannel = max(0, currentChannel - numItemsChannel/2 + 1);
++ DisplayChannelList(dcExt);
++}
++
++void cDisplayChannelExtended::SetChannelList(void)
++{
++ channellist.Clear();
++ const cChannel *lastSep = NULL;
++ if (Setup.ZapcockpitHideLastGroup)
++ lastSep = LastChannelSep();
++ LOCK_CHANNELS_READ;
++ for (const cChannel *c = Channels->First(); c; c = Channels->Next(c)) {
++ if (c->GroupSep()) {
++ if (Setup.ZapcockpitHideLastGroup && c == lastSep)
++ break;
++ else
++ continue;
++ }
++ channellist.Add(new cChannelListItem(c));
++ }
++}
++
++int cDisplayChannelExtended::GetIndexChannel(const cChannel *c)
++{
++ int i=0;
++ for (cChannelListItem *li = channellist.First(); li; li = channellist.Next(li)) {
++ if (li->Channel() == c)
++ return i;
++ i++;
++ }
++ return -1;
++}
++
++void cDisplayChannelExtended::InitGroupList(cSkinDisplayChannelExtended *dcExt)
++{
++ dcExt->SetViewType(dcGroupsList);
++ numItemsGroup = dcExt->MaxItems();
++ if (numItemsGroup < 1)
++ return;
++ SetGroupList();
++ currentGroup = GetIndexGroup(channel);
++ if (currentGroup < 0)
++ currentGroup = 0;
++ startGroup = max(0, numItemsGroup >= grouplist.Count() ? 0 : currentGroup - numItemsGroup/2 + 1);
++ DisplayGroupList(dcExt);
++}
++
++void cDisplayChannelExtended::SetGroupList(void)
++{
++ grouplist.Clear();
++ if (Setup.ZapcockpitShowAllChannels) {
++ cGroupListItem *allChannels = new cGroupListItem(NULL);
++ int totalNumChannels = 0;
++ const cChannel *lastSep = NULL;
++ if (Setup.ZapcockpitHideLastGroup)
++ lastSep = LastChannelSep();
++ LOCK_CHANNELS_READ;
++ for (const cChannel *c = Channels->First(); c; c = Channels->Next(c)) {
++ if (c->GroupSep()) {
++ if (Setup.ZapcockpitHideLastGroup && c == lastSep)
++ break;
++ else
++ continue;
++ }
++ totalNumChannels++;
++ }
++ allChannels->SetNumChannels(totalNumChannels);
++ grouplist.Add(allChannels);
++ }
++
++ const cChannel *lastSep = NULL;
++ if (Setup.ZapcockpitHideLastGroup)
++ lastSep = LastChannelSep();
++ int numChannels = 0;
++ cGroupListItem *item = NULL;
++ LOCK_CHANNELS_READ;
++ for (const cChannel *c = Channels->First(); c; c = Channels->Next(c)) {
++ if (c->GroupSep()) {
++ if (item) {
++ item->SetNumChannels(numChannels);
++ numChannels = 0;
++ }
++ if (Setup.ZapcockpitHideLastGroup && c == lastSep)
++ break;
++ item = new cGroupListItem(c);
++ grouplist.Add(item);
++ } else
++ numChannels++;
++ }
++ if (grouplist.Count() > 0 && numChannels)
++ grouplist.Last()->SetNumChannels(numChannels);
++}
++
++int cDisplayChannelExtended::GetIndexGroup(const cChannel *cur)
++{
++ const cChannel *group = NULL;
++ LOCK_CHANNELS_READ;
++ for (const cChannel *c = cur; c; c = Channels->Prev(c)) {
++ if (c->GroupSep()) {
++ group = c;
++ break;
++ }
++ }
++ if (!group)
++ return -1;
++ int i=0;
++ for (cGroupListItem *li = grouplist.First(); li; li = grouplist.Next(li)) {
++ if (li->Channel() == group)
++ return i;
++ i++;
++ }
++ return -1;
++}
++
++void cDisplayChannelExtended::InitGroupChannelList(cSkinDisplayChannelExtended *dcExt)
++{
++ dcExt->SetViewType(dcGroupsChannelList);
++ numItemsChannel = dcExt->MaxItems();
++ if (numItemsChannel < 1)
++ return;
++ SetGroupChannelList(dcExt);
++ currentChannel = 0;
++ startChannel = 0;
++ DisplayChannelList(dcExt);
++}
++
++void cDisplayChannelExtended::SetGroupChannelList(cSkinDisplayChannelExtended *dcExt)
++{
++ cGroupListItem *curGroup = grouplist.Get(currentGroup);
++ if (!curGroup)
++ return;
++ const cChannel *curChannel = curGroup->Channel();
++ if (!curChannel) {
++ if (Setup.ZapcockpitShowAllChannels)
++ SetChannelList();
++ return;
++ }
++ channellist.Clear();
++ LOCK_CHANNELS_READ;
++ for (const cChannel *c = dynamic_cast<cChannel*>(curChannel->Next()); c; c = Channels->Next(c)) {
++ if (c->GroupSep())
++ break;
++ channellist.Add(new cChannelListItem(c));
++ }
++}
++
++void cDisplayChannelExtended::CursorUp(cSkinDisplayChannelExtended *dcExt)
++{
++ int *start, *current, *numItems;
++ if (state == esChannelList || state == esGroupsChannelList) {
++ start = &startChannel;
++ current = &currentChannel;
++ numItems = &numItemsChannel;
++ } else if (state == esGroupsList) {
++ start = &startGroup;
++ current = &currentGroup;
++ numItems = &numItemsGroup;
++ } else
++ return;
++
++ if (*current == 0) {
++ dcExt->ClearList();
++ int itemsTotal = (state == esChannelList || state == esGroupsChannelList)?channellist.Count():((state == esGroupsList)?grouplist.Count():0);
++ *current = itemsTotal-1;
++ *start = max(0, itemsTotal - *numItems);
++ if (state == esChannelList || state == esGroupsChannelList)
++ DisplayChannelList(dcExt);
++ else if (state == esGroupsList)
++ DisplayGroupList(dcExt);
++ return;
++ }
++ int curRel = *current - *start;
++ if (curRel > 0) {
++ if (state == esChannelList || state == esGroupsChannelList) {
++ const cChannel *prev = channellist.Get(*current-1)->Channel();
++ dcExt->SetChannelList(channellist.Get(*current)->Channel(), curRel, false);
++ dcExt->SetChannelList(prev, curRel-1, true);
++ (*current)--;
++ return;
++ } else if (state = esGroupsList) {
++ cGroupListItem *prev = grouplist.Get(*current-1);
++ cGroupListItem *old = grouplist.Get(*current);
++ dcExt->SetGroupList(old->GroupName(), old->NumChannels(), curRel, false);
++ dcExt->SetGroupList(prev->GroupName(), prev->NumChannels(), curRel-1, true);
++ (*current)--;
++ return;
++ }
++ }
++ dcExt->ClearList();
++ (*current)--;
++ *start = max(0, *start-*numItems);
++
++ if (state == esChannelList || state == esGroupsChannelList)
++ DisplayChannelList(dcExt);
++ else if (state == esGroupsList)
++ DisplayGroupList(dcExt);
++}
++
++void cDisplayChannelExtended::CursorDown(cSkinDisplayChannelExtended *dcExt)
++{
++ int *start, *current, *numItems;
++ if (state == esChannelList || state == esGroupsChannelList) {
++ start = &startChannel;
++ current = &currentChannel;
++ numItems = &numItemsChannel;
++ } else if (state == esGroupsList) {
++ start = &startGroup;
++ current = &currentGroup;
++ numItems = &numItemsGroup;
++ } else
++ return;
++
++ int curRel = *current - *start;
++ if (curRel < *numItems - 1) {
++ if (state == esChannelList || state == esGroupsChannelList) {
++ cChannelListItem *next = channellist.Get(*current+1);
++ if (next) {
++ dcExt->SetChannelList(channellist.Get(*current)->Channel(), curRel, false);
++ dcExt->SetChannelList(next->Channel(), curRel+1, true);
++ (*current)++;
++ return;
++ }
++ } else if (state == esGroupsList) {
++ cGroupListItem *next = grouplist.Get(*current+1);
++ if (next) {
++ cGroupListItem *old = grouplist.Get(*current);
++ dcExt->SetGroupList(old->GroupName(), old->NumChannels(), curRel, false);
++ dcExt->SetGroupList(next->GroupName(), next->NumChannels(), curRel+1, true);
++ (*current)++;
++ return;
++ }
++ }
++ }
++ if (((state == esChannelList || state == esGroupsChannelList) && *current+1 == channellist.Count()) ||
++ (state == esGroupsList && *current+1 == grouplist.Count()))
++ *start = *current = 0;
++ else
++ *start = *current = *current+1;
++ dcExt->ClearList();
++
++ if (state == esChannelList || state == esGroupsChannelList)
++ DisplayChannelList(dcExt);
++ else if (state == esGroupsList)
++ DisplayGroupList(dcExt);
++}
++
++void cDisplayChannelExtended::DisplayChannelList(cSkinDisplayChannelExtended *dcExt)
++{
++ int index = 0;
++ for (cChannelListItem *c = channellist.Get(startChannel); c; c = channellist.Next(c)) {
++ dcExt->SetChannelList(c->Channel(), index, (startChannel + index == currentChannel) ? true : false);
++ if (++index == numItemsChannel)
++ break;
++ }
++}
++
++void cDisplayChannelExtended::DisplayGroupList(cSkinDisplayChannelExtended *dcExt)
++{
++ int index = 0;
++ for (cGroupListItem *g = grouplist.Get(startGroup); g; g = grouplist.Next(g)) {
++ dcExt->SetGroupList(g->GroupName(), g->NumChannels(), index, (startGroup + index == currentGroup) ? true : false);
++ if (++index == numItemsGroup)
++ break;
++ }
++}
++
++bool cDisplayChannelExtended::SwitchChannel(void)
++{
++ const cChannel *newChannel = NULL;
++ if ( state == esChannelList ||
++ state == esChannelListInfo ||
++ state == esGroupsChannelList ||
++ state == esGroupsChannelListInfo ) {
++ cChannelListItem *li = channellist.Get(currentChannel);
++ if (li)
++ newChannel = li->Channel();
++ } else if (state == esGroupsList) {
++ cGroupListItem *item = grouplist.Get(currentGroup);
++ if (!item)
++ return false;
++ const cChannel *cGroup = item->Channel();
++ LOCK_CHANNELS_READ;
++ for (const cChannel *c = cGroup; c; c = Channels->Next(c))
++ if (!c->GroupSep()) {
++ newChannel = c;
++ break;
++ }
++ }
++ if (!newChannel || newChannel == channel)
++ return false;
++ SetTrackDescriptions(newChannel->Number()); // to make them immediately visible in the channel display
++ LOCK_CHANNELS_READ;
++ Channels->SwitchTo(newChannel->Number());
++ SetTrackDescriptions(newChannel->Number()); // switching the channel has cleared them
++ channel = newChannel;
++ return true;
++}
++
++const cChannel *cDisplayChannelExtended::LastChannelSep(void)
++{
++ LOCK_CHANNELS_READ;
++ for (const cChannel *c = Channels->Last(); c; c = Channels->Prev(c))
++ if (c->GroupSep())
++ return c;
++ return NULL;
++}
++
+ // --- cDisplayVolume --------------------------------------------------------
+
+ #define VOLUMETIMEOUT 1000 //ms
+diff -Nur vdr-2.4.0/menu.h vdr-2.4.0.p/menu.h
+--- vdr-2.4.0/menu.h 2018-04-14 12:24:41.000000000 +0200
++++ vdr-2.4.0.p/menu.h 2019-04-04 15:27:27.648451092 +0200
+@@ -119,30 +119,102 @@
+
+ class cDisplayChannel : public cOsdObject {
+ private:
+- cSkinDisplayChannel *displayChannel;
+ int group;
+ bool withInfo;
+- cTimeMs lastTime;
+- int number;
+ bool timeout;
+- int osdState;
+ const cPositioner *positioner;
+- const cChannel *channel;
+ const cEvent *lastPresent;
+ const cEvent *lastFollowing;
+ static cDisplayChannel *currentDisplayChannel;
+- void DisplayChannel(void);
+- void DisplayInfo(void);
+ void Refresh(void);
+ const cChannel *NextAvailableChannel(const cChannel *Channel, int Direction);
++protected:
++ cSkinDisplayChannel *displayChannel;
++ cTimeMs lastTime;
++ int number;
++ const cChannel *channel;
++ int osdState;
++ void DisplayChannel(void);
++ void DisplayInfo(void);
+ public:
+ cDisplayChannel(int Number, bool Switched);
+- cDisplayChannel(eKeys FirstKey);
++ cDisplayChannel(eKeys FirstKey, bool processKey = true);
+ virtual ~cDisplayChannel();
+ virtual eOSState ProcessKey(eKeys Key);
+ static bool IsOpen(void) { return currentDisplayChannel != NULL; }
+ };
+
++enum eExtendedState {
++ esInit = 0,
++ esDefault,
++ esChannelInfo,
++ esChannelList,
++ esChannelListInfo,
++ esGroupsList,
++ esGroupsChannelList,
++ esGroupsChannelListInfo,
++ esClose
++ };
++
++class cChannelListItem : public cListObject {
++private:
++ const cChannel *channel;
++public:
++ cChannelListItem(const cChannel *Channel) { channel = Channel; };
++ virtual ~cChannelListItem(void) { };
++ const cChannel *Channel(void) { return channel; }
++ };
++
++class cGroupListItem : public cListObject {
++private:
++ const cChannel *channel;
++ int numChannels;
++public:
++ cGroupListItem(const cChannel *Channel) { channel = Channel; numChannels = 0; };
++ virtual ~cGroupListItem(void) { };
++ const char *GroupName(void);
++ void SetNumChannels(int NumChannels) { numChannels = NumChannels; };
++ int NumChannels(void) { return numChannels; };
++ const cChannel *Channel(void) { return channel; }
++ };
++
++class cDisplayChannelExtended : public cDisplayChannel {
++private:
++ eExtendedState state;
++ int keyRightOpensChannellist;
++ int numItemsChannel, startChannel, currentChannel;
++ int numItemsGroup, startGroup, currentGroup;
++ cList<cChannelListItem> channellist;
++ cList<cGroupListItem> grouplist;
++ void StateNumberKey(int key, cSkinDisplayChannelExtended *dcExt);
++ bool StateInit(int key, cSkinDisplayChannelExtended *dcExt);
++ bool StateDefault(int key, cSkinDisplayChannelExtended *dcExt);
++ bool StateChannelInfo(int key, cSkinDisplayChannelExtended *dcExt);
++ bool StateChannelList(int key, cSkinDisplayChannelExtended *dcExt);
++ bool StateGroupList(int key, cSkinDisplayChannelExtended *dcExt);
++ bool StateGroupChannelList(int key, cSkinDisplayChannelExtended *dcExt);
++ void ShowChannellistInfo(cSkinDisplayChannelExtended *dcExt, eDisplaychannelView newViewType);
++ void InitChannelList(cSkinDisplayChannelExtended *dcExt);
++ void SetChannelList(void);
++ int GetIndexChannel(const cChannel *c);
++ void InitGroupList(cSkinDisplayChannelExtended *dcExt);
++ void SetGroupList(void);
++ int GetIndexGroup(const cChannel *c);
++ void InitGroupChannelList(cSkinDisplayChannelExtended *dcExt);
++ void SetGroupChannelList(cSkinDisplayChannelExtended *dcExt);
++ void CursorUp(cSkinDisplayChannelExtended *dcExt);
++ void CursorDown(cSkinDisplayChannelExtended *dcExt);
++ void DisplayChannelList(cSkinDisplayChannelExtended *dcExt);
++ void DisplayGroupList(cSkinDisplayChannelExtended *dcExt);
++ bool SwitchChannel(void);
++ const cChannel *LastChannelSep(void);
++public:
++ cDisplayChannelExtended(int Number, bool Switched);
++ cDisplayChannelExtended(eKeys FirstKey);
++ virtual ~cDisplayChannelExtended();
++ virtual eOSState ProcessKey(eKeys Key);
++ };
++
+ class cDisplayVolume : public cOsdObject {
+ private:
+ cSkinDisplayVolume *displayVolume;
+diff -Nur vdr-2.4.0/po/de_DE.po vdr-2.4.0.p/po/de_DE.po
+--- vdr-2.4.0/po/de_DE.po 2019-04-04 15:47:31.199441881 +0200
++++ vdr-2.4.0.p/po/de_DE.po 2019-04-04 15:29:21.903832616 +0200
+@@ -1347,6 +1347,21 @@
+ msgid "Setup.Miscellaneous$Channel entry timeout (ms)"
+ msgstr "Zeitlimit für Kanaleingabe (ms)"
+
++msgid "Setup.Miscellaneous$Zapcockpit: 2nd ok shows info"
++msgstr "Zapcockpit: zweites OK zeigt Info"
++
++msgid "Setup.Miscellaneous$Zapcockpit: Use extended channel group display"
++msgstr "Zapcockpit: Erweiterte Kanalgruppen Anzeige benutzen"
++
++msgid "Setup.Miscellaneous$Zapcockpit: Use channel hints"
++msgstr "Zapcockpit: Kanalhinweise benutzen"
++
++msgid "Setup.Miscellaneous$Zapcockpit: Hide last channel group"
++msgstr "Zapcockpit: letzte Kanalgruppe ausblenden"
++
++msgid "Setup.Miscellaneous$Zapcockpit: Show \"All Channels\" Item in Group List"
++msgstr "Zapcockpit: Zeige \"Alle Kanäle\" in Kanalgruppen Liste"
++
+ msgid "Setup.Miscellaneous$Remote control repeat delay (ms)"
+ msgstr "Fernbedienung Wiederholverzögerung (ms)"
+
+@@ -1419,6 +1434,9 @@
+ msgid "Cancel editing?"
+ msgstr "Bearbeitung abbrechen?"
+
++msgid "Setup.Miscellaneous$All Channels"
++msgstr "Alle Kanäle"
++
+ msgid "No audio available!"
+ msgstr "Kein Audio verfügbar!"
+
+diff -Nur vdr-2.4.0/skins.c vdr-2.4.0.p/skins.c
+--- vdr-2.4.0/skins.c 2019-04-04 15:47:25.665519948 +0200
++++ vdr-2.4.0.p/skins.c 2019-04-04 14:57:48.240702878 +0200
+@@ -79,6 +79,13 @@
+ SetMessage(mtInfo, cString::sprintf(tr("Moving dish to %.1f..."), double(positioner->TargetLongitude()) / 10));
+ }
+
++cSkinDisplayChannelExtended::cSkinDisplayChannelExtended(void)
++: cSkinDisplayChannel()
++{
++
++}
++
++
+ // --- cSkinDisplayMenu ------------------------------------------------------
+
+ cSkinDisplayMenu::cSkinDisplayMenu(void)
+diff -Nur vdr-2.4.0/skins.h vdr-2.4.0.p/skins.h
+--- vdr-2.4.0/skins.h 2017-11-02 16:04:56.000000000 +0100
++++ vdr-2.4.0.p/skins.h 2019-04-04 14:57:48.241702864 +0200
+@@ -101,6 +101,34 @@
+ */
+ };
+
++#define USE_ZAPCOCKPIT 1
++
++enum eDisplaychannelView {
++ dcDefault = 0,
++ dcChannelInfo,
++ dcChannelList,
++ dcChannelListInfo,
++ dcGroupsList,
++ dcGroupsChannelList,
++ dcGroupsChannelListInfo
++ };
++
++class cSkinDisplayChannelExtended : public cSkinDisplayChannel {
++private:
++public:
++ cSkinDisplayChannelExtended(void);
++ virtual void SetViewType(eDisplaychannelView ViewType) = 0;
++ virtual int MaxItems(void) = 0;
++ virtual bool KeyRightOpensChannellist(void) = 0;
++ virtual void SetChannelInfo(const cChannel *Channel) = 0;
++ virtual void SetChannelList(const cChannel *Channel, int Index, bool Current) = 0;
++ virtual void SetGroupList(const char *Group, int NumChannels, int Index, bool Current) = 0;
++ virtual void SetGroupChannelList(const cChannel *Channel, int Index, bool Current) = 0;
++ virtual void ClearList(void) = 0;
++ virtual void SetNumChannelHints(int Num) = 0;
++ virtual void SetChannelHint(const cChannel *Channel) = 0;
++};
++
+ enum eMenuCategory {
+ mcUndefined = -1,
+ mcUnknown = 0,
+diff -Nur vdr-2.4.0/vdr.c vdr-2.4.0.p/vdr.c
+--- vdr-2.4.0/vdr.c 2019-04-04 15:47:25.719519186 +0200
++++ vdr-2.4.0.p/vdr.c 2019-04-04 14:57:48.241702864 +0200
+@@ -1088,7 +1088,7 @@
+ // Channel display:
+ if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {
+ if (!Menu)
+- Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel >= 0);
++ Menu = new cDisplayChannelExtended(cDevice::CurrentChannel(), LastChannel >= 0);
+ LastChannel = cDevice::CurrentChannel();
+ LastChannelChanged = Now;
+ }
+@@ -1287,7 +1287,8 @@
+ case kChanDn|k_Repeat:
+ case kChanDn:
+ if (!Interact) {
+- Menu = new cDisplayChannel(NORMALKEY(key));
++ Menu = new cDisplayChannelExtended(NORMALKEY(key));
++ Menu->ProcessKey(NORMALKEY(key));
+ continue;
+ }
+ else if (cDisplayChannel::IsOpen() || cControl::Control()) {
+@@ -1480,7 +1481,8 @@
+ case kUp:
+ case kDown|k_Repeat:
+ case kDown:
+- Menu = new cDisplayChannel(NORMALKEY(key));
++ Menu = new cDisplayChannelExtended(NORMALKEY(key));
++ Menu->ProcessKey(NORMALKEY(key));
+ break;
+ // Viewing Control:
+ case kOk: LastChannel = -1; break; // forces channel display