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(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(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 = ¤tChannel; + numItems = &numItemsChannel; + } else if (state == esGroupsList) { + start = &startGroup; + current = ¤tGroup; + 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 = ¤tChannel; + numItems = &numItemsChannel; + } else if (state == esGroupsList) { + start = &startGroup; + current = ¤tGroup; + 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 channellist; + cList 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