vdr-plugin-tvguide/tvguideosd.c

805 lines
24 KiB
C

#include <stdlib.h>
#include <vector>
#include "config.h"
#include "services/epgsearch.h"
#include "services/remotetimers.h"
#include "tools.h"
#include "setup.h"
#include "tvguideosd.h"
cTvGuideOsd::cTvGuideOsd(void) {
detailView = NULL;
detailViewActive = false;
activeGrid = NULL;
timeLine = NULL;
recMenuView = NULL;
channelJumper = NULL;
}
cTvGuideOsd::~cTvGuideOsd() {
delete timeManager;
columns.Clear();
if (config.displayStatusHeader) {
delete statusHeader;
}
if (detailView)
delete detailView;
delete timeLine;
delete channelGroups;
delete footer;
delete recMenuView;
if (channelJumper)
delete channelJumper;
osdManager.DeleteOsd();
}
void cTvGuideOsd::Show(void) {
int start = cTimeMs::Now();
if (osdManager.CreateOsd()) {
bool themeChanged = config.LoadTheme();
config.SetStyle();
config.setDynamicValues();
bool geoChanged = geoManager.SetGeometry(cOsd::OsdWidth(), cOsd::OsdHeight());
if (themeChanged || geoChanged) {
fontManager.DeleteFonts();
fontManager.SetFonts();
imgCache.Clear();
imgCache.CreateCache();
}
osdManager.SetBackground();
timeManager = new cTimeManager();
timeManager->Now();
SwitchTimers.Load(AddDirectory(cPlugin::ConfigDirectory("epgsearch"), "epgsearchswitchtimers.conf"));
recMenuView = new cRecMenuView();
pRemoteTimers = cPluginManager::CallFirstService("RemoteTimers::RefreshTimers-v1.0", NULL);
if (pRemoteTimers) {
isyslog("tvguide: remotetimers-plugin is available");
}
if (config.useRemoteTimers && pRemoteTimers) {
cString errorMsg;
if (!pRemoteTimers->Service("RemoteTimers::RefreshTimers-v1.0", &errorMsg)) {
esyslog("tvguide: %s", *errorMsg);
}
}
drawOsd();
}
esyslog("tvguide: Rendering took %d ms", int(cTimeMs::Now()-start));
}
void cTvGuideOsd::drawOsd() {
cPixmap::Lock();
int numBack = config.numGrids / 2;
int offset = 0;
const cChannel *newStartChannel;
#if VDRVERSNUM >= 20301
{
LOCK_CHANNELS_READ;
const cChannel *startChannel = Channels->GetByNumber(cDevice::CurrentChannel());
#else
cChannel *startChannel = Channels.GetByNumber(cDevice::CurrentChannel());
#endif
newStartChannel = startChannel;
#if VDRVERSNUM >= 20301
for (; newStartChannel ; newStartChannel = Channels->Prev(newStartChannel)) {
#else
for (; newStartChannel ; newStartChannel = Channels.Prev(newStartChannel)) {
#endif
if (newStartChannel && !newStartChannel->GroupSep()) {
offset++;
}
if (offset == numBack)
break;
}
if (!newStartChannel)
#if VDRVERSNUM >= 20301
newStartChannel = Channels->First();
} //LOCK_CHANNELS_READ
#else
newStartChannel = Channels.First();
#endif
offset--;
if (offset < 0)
offset = 0;
if (config.displayStatusHeader) {
statusHeader = new cStatusHeader();
statusHeader->Draw();
statusHeader->ScaleVideo();
}
timeLine = new cTimeLine(timeManager);
timeLine->DrawDateViewer();
timeLine->DrawTimeline();
timeLine->DrawClock();
channelGroups = new cChannelGroups();
channelGroups->ReadChannelGroups();
footer = new cFooter(channelGroups);
recMenuView->AddFooter(footer);
footer->drawRedButton();
if (config.channelJumpMode == eNumJump) {
footer->drawGreenButton();
footer->drawYellowButton();
}
footer->drawBlueButton(false);
osdManager.Flush();
readChannels(newStartChannel);
drawGridsChannelJump(offset);
osdManager.Flush();
cPixmap::Unlock();
}
void cTvGuideOsd::readChannels(const cChannel *channelStart) {
int i=0;
bool foundEnough = false;
columns.Clear();
if (!channelStart)
return;
#if VDRVERSNUM >= 20301
const cChannels *channels;
{
LOCK_CHANNELS_READ;
channels = Channels;
}
#else
cChannels *channels = &Channels;
#endif
for (const cChannel *channel = channelStart; channel; channel = channels->Next(channel)) {
if (!channel->GroupSep()) {
if (channelGroups->IsInLastGroup(channel)) {
break;
}
cChannelEpg *column = new cChannelEpg(i, channel, timeManager);
if (column->readGrids()) {
columns.Add(column);
i++;
} else {
delete column;
}
}
if (i == config.numGrids) {
foundEnough = true;
break;
}
}
if (!foundEnough) {
int numCurrent = columns.Count();
int numBack = config.numGrids - numCurrent;
int newChannelNumber = columns.First()->getChannel()->Number() - numBack;
const cChannel *newStart = channels->GetByNumber(newChannelNumber);
readChannels(newStart);
}
}
void cTvGuideOsd::drawGridsChannelJump(int offset) {
if (columns.Count() == 0)
return;
activeGrid = columns.Get(offset)->getActive();
if (activeGrid)
activeGrid->SetActive();
if (config.displayStatusHeader) {
statusHeader->DrawInfoText(activeGrid);
}
if (activeGrid && (config.channelJumpMode == eGroupJump)) {
footer->UpdateGroupButtons(activeGrid->column->getChannel());
}
if (config.displayChannelGroups) {
channelGroups->DrawChannelGroups(columns.First()->getChannel(), columns.Last()->getChannel());
}
for (cChannelEpg *column = columns.First(); column; column = columns.Next(column)) {
column->createHeader();
column->drawGrids();
}
}
void cTvGuideOsd::drawGridsTimeJump(bool last) {
if (columns.Count() == 0)
return;
cChannelEpg *colActive = NULL;
if (activeGrid) {
colActive = activeGrid->column;
} else {
colActive = columns.First();
}
for (cChannelEpg *column = columns.First(); column; column = columns.Next(column)) {
column->clearGrids();
column->readGrids();
column->drawGrids();
}
activeGrid = colActive->getActive(last);
if (activeGrid) {
activeGrid->SetActive();
activeGrid->Draw();
if (config.displayStatusHeader) {
statusHeader->DrawInfoText(activeGrid);
}
}
}
void cTvGuideOsd::setNextActiveGrid(cGridElement *next) {
if (!next || !activeGrid) {
return;
}
activeGrid->SetInActive();
activeGrid->Draw();
activeGrid = next;
activeGrid->SetActive();
activeGrid->Draw();
if (config.displayStatusHeader) {
statusHeader->DrawInfoText(activeGrid);
}
}
void cTvGuideOsd::channelForward() {
cChannelEpg *colRight = columns.Next(activeGrid->column);
bool colAdded = false;
if (!colRight) {
const cChannel *channelRight = activeGrid->column->getChannel();
const cChannels *channels;
#if VDRVERSNUM >= 20301
{
LOCK_CHANNELS_READ;
channels = Channels;
}
#else
channels = &Channels;
#endif
while (channelRight = channels->Next(channelRight)) {
if (!channelRight->GroupSep()) {
if (channelGroups->IsInLastGroup(channelRight)) {
break;
}
colRight = new cChannelEpg(config.numGrids - 1, channelRight, timeManager);
if (colRight->readGrids()) {
break;
} else {
delete colRight;
colRight = NULL;
}
}
}
if (colRight) {
colAdded = true;
if (columns.Count() == config.numGrids) {
cChannelEpg *cFirst = columns.First();
columns.Del(cFirst);
}
for (cChannelEpg *column = columns.First(); column; column = columns.Next(column)) {
column->SetNum(column->GetNum() - 1);
column->drawHeader();
column->drawGrids();
}
columns.Add(colRight);
colRight->createHeader();
colRight->drawGrids();
}
}
if (colRight) {
cGridElement *right = colRight->getNeighbor(activeGrid);
if (right) {
setNextActiveGrid(right);
}
}
if (config.displayChannelGroups && colAdded) {
channelGroups->DrawChannelGroups(columns.First()->getChannel(), columns.Last()->getChannel());
}
if (activeGrid && (config.channelJumpMode == eGroupJump)) {
footer->UpdateGroupButtons(activeGrid->column->getChannel());
}
osdManager.Flush();
}
void cTvGuideOsd::channelBack() {
cChannelEpg *colLeft = columns.Prev(activeGrid->column);
bool colAdded = false;
if (!colLeft) {
const cChannel *channelLeft = activeGrid->column->getChannel();
const cChannels *channels;
#if VDRVERSNUM >= 20301
{
LOCK_CHANNELS_READ;
channels = Channels;
}
#else
channels = &Channels;
#endif
while (channelLeft = channels->Prev(channelLeft)) {
if (!channelLeft->GroupSep()) {
colLeft = new cChannelEpg(0, channelLeft, timeManager);
if (colLeft->readGrids()) {
break;
} else {
delete colLeft;
colLeft = NULL;
}
}
}
if (colLeft) {
colAdded = true;
if (columns.Count() == config.numGrids) {
cChannelEpg *cLast = columns.Last();
columns.Del(cLast);
}
for (cChannelEpg *column = columns.First(); column; column = columns.Next(column)) {
column->SetNum(column->GetNum() + 1);
column->drawHeader();
column->drawGrids();
}
columns.Ins(colLeft, columns.First());
colLeft->createHeader();
colLeft->drawGrids();
}
}
if (colLeft) {
cGridElement *left = colLeft->getNeighbor(activeGrid);
if (left) {
setNextActiveGrid(left);
}
}
if (config.displayChannelGroups && colAdded) {
channelGroups->DrawChannelGroups(columns.First()->getChannel(), columns.Last()->getChannel());
}
if (activeGrid && (config.channelJumpMode == eGroupJump)) {
footer->UpdateGroupButtons(activeGrid->column->getChannel());
}
osdManager.Flush();
}
void cTvGuideOsd::timeForward() {
bool actionDone = false;
if ((timeManager->GetEnd() - activeGrid->EndTime())/60 < 30 ) {
ScrollForward();
actionDone = true;
}
cGridElement *next = activeGrid->column->getNext(activeGrid);
if (next) {
if ( (next->EndTime() < timeManager->GetEnd())
|| ( (timeManager->GetEnd() - next->StartTime())/60 > 30 ) ) {
setNextActiveGrid(next);
actionDone = true;
}
}
if (!actionDone) {
ScrollForward();
}
osdManager.Flush();
}
void cTvGuideOsd::ScrollForward() {
timeManager->AddStep(config.stepMinutes);
if (config.useHWAccel) {
drawGridsTimeJump(true);
timeLine->DrawDateViewer();
timeLine->DrawClock();
timeLine->DrawTimeline();
} else {
timeLine->DrawDateViewer();
timeLine->DrawTimeline();
for (cChannelEpg *column = columns.First(); column; column = columns.Next(column)) {
column->AddNewGridsAtEnd();
column->ClearOutdatedStart();
column->drawGrids();
}
}
}
void cTvGuideOsd::timeBack() {
bool actionDone = false;
if ((activeGrid->StartTime() - timeManager->GetStart())/60 < 30 ) {
ScrollBack();
actionDone = true;
}
cGridElement *prev = activeGrid->column->getPrev(activeGrid);
if (prev) {
if ( (prev->StartTime() > timeManager->GetStart())
|| ( (prev->EndTime() - timeManager->GetStart())/60 > 30 )
|| ( prev->isFirst()) ) {
setNextActiveGrid(prev);
actionDone = true;
}
}
if (!actionDone) {
ScrollBack();
}
osdManager.Flush();
}
void cTvGuideOsd::ScrollBack() {
timeManager->DelStep(config.stepMinutes);
if (config.useHWAccel) {
drawGridsTimeJump();
timeLine->DrawDateViewer();
timeLine->DrawClock();
timeLine->DrawTimeline();
} else {
timeLine->DrawDateViewer();
timeLine->DrawTimeline();
for (cChannelEpg *column = columns.First(); column; column = columns.Next(column)) {
column->AddNewGridsAtStart();
column->ClearOutdatedEnd();
column->drawGrids();
}
}
}
void cTvGuideOsd::processKeyUp() {
if (!activeGrid) {
return;
}
if (config.displayMode == eVertical) {
timeBack();
} else if (config.displayMode == eHorizontal) {
channelBack();
}
}
void cTvGuideOsd::processKeyDown() {
if (!activeGrid) {
return;
}
if (config.displayMode == eVertical) {
timeForward();
} else if (config.displayMode == eHorizontal) {
channelForward();
}
}
void cTvGuideOsd::processKeyLeft() {
if (activeGrid == NULL)
return;
if (config.displayMode == eVertical) {
channelBack();
} else if (config.displayMode == eHorizontal) {
timeBack();
}
}
void cTvGuideOsd::processKeyRight() {
if (activeGrid == NULL)
return;
if (config.displayMode == eVertical) {
channelForward();
} else if (config.displayMode == eHorizontal) {
timeForward();
}
}
void cTvGuideOsd::processKeyRed() {
if ((activeGrid == NULL) || activeGrid->IsDummy())
return;
recMenuView->Start(activeGrid->GetEvent());
}
void cTvGuideOsd::processKeyGreen() {
if (activeGrid == NULL)
return;
const cChannel *currentChannel = activeGrid->column->getChannel();
const cChannel *firstChannel = columns.First()->getChannel();
int currentCol = activeGrid->column->GetNum();
const cChannel *prev = NULL;
if (config.channelJumpMode == eGroupJump) {
int prevNum = channelGroups->GetPrevGroupChannelNumber(currentChannel);
if (prevNum) {
#if VDRVERSNUM >= 20301
LOCK_CHANNELS_READ;
prev = Channels->GetByNumber(prevNum);
#else
prev = Channels.GetByNumber(prevNum);
#endif
}
} else if (config.channelJumpMode == eNumJump) {
int i = config.jumpChannels + 1;
#if VDRVERSNUM >= 20301
LOCK_CHANNELS_READ;
for (const cChannel *channel = firstChannel; channel; channel = Channels->Prev(channel)) {
#else
for (const cChannel *channel = firstChannel; channel; channel = Channels.Prev(channel)) {
#endif
if (!channel->GroupSep()) {
prev = channel;
i--;
}
if (i == 0)
break;
}
}
if (prev) {
readChannels(prev);
if (columns.Count() > 0) {
if (config.channelJumpMode == eGroupJump)
drawGridsChannelJump();
else
drawGridsChannelJump(currentCol);
}
osdManager.Flush();
}
}
void cTvGuideOsd::processKeyYellow() {
if (activeGrid == NULL)
return;
const cChannel *currentChannel = activeGrid->column->getChannel();
int currentCol = activeGrid->column->GetNum();
const cChannel *firstChannel = columns.First()->getChannel();
const cChannel *next = NULL;
if (config.channelJumpMode == eGroupJump) {
int nextNum = channelGroups->GetNextGroupChannelNumber(currentChannel);
if (nextNum) {
#if VDRVERSNUM >= 20301
LOCK_CHANNELS_READ;
next = Channels->GetByNumber(nextNum);
#else
next = Channels.GetByNumber(nextNum);
#endif
}
} else if (config.channelJumpMode == eNumJump) {
int i=0;
#if VDRVERSNUM >= 20301
LOCK_CHANNELS_READ;
for (const cChannel *channel = firstChannel; channel; channel = Channels->Next(channel)) {
#else
for (const cChannel *channel = firstChannel; channel; channel = Channels.Next(channel)) {
#endif
if (channelGroups->IsInLastGroup(channel)) {
break;
}
if (!channel->GroupSep()) {
next = channel;
i++;
}
if (i == (config.jumpChannels+1)) {
break;
}
}
}
if (next) {
readChannels(next);
if (columns.Count() > 0) {
if (config.channelJumpMode == eGroupJump)
drawGridsChannelJump();
else
drawGridsChannelJump(currentCol);
}
osdManager.Flush();
}
}
eOSState cTvGuideOsd::processKeyBlue(bool *alreadyUnlocked) {
if (config.blueKeyMode == eBlueKeySwitch) {
return ChannelSwitch(alreadyUnlocked);
} else if (config.blueKeyMode == eBlueKeyEPG) {
DetailedEPG();
} else if (config.blueKeyMode == eBlueKeyFavorites) {
recMenuView->StartFavorites();
}
return osContinue;
}
eOSState cTvGuideOsd::processKeyOk(bool *alreadyUnlocked) {
if (config.blueKeyMode == eBlueKeySwitch) {
DetailedEPG();
} else if (config.blueKeyMode == eBlueKeyEPG) {
return ChannelSwitch(alreadyUnlocked);
} else if (config.blueKeyMode == eBlueKeyFavorites) {
DetailedEPG();
}
return osContinue;
}
eOSState cTvGuideOsd::ChannelSwitch(bool *alreadyUnlocked) {
if (activeGrid == NULL)
return osContinue;
const cChannel *currentChannel = activeGrid->column->getChannel();
if (currentChannel) {
cPixmap::Unlock();
*alreadyUnlocked = true;
cDevice::PrimaryDevice()->SwitchChannel(currentChannel, true);
if (config.closeOnSwitch) {
if (detailView) {
delete detailView;
detailView = NULL;
detailViewActive = false;
}
return osEnd;
}
}
return osContinue;
}
void cTvGuideOsd::DetailedEPG() {
if (!activeGrid->IsDummy()) {
detailViewActive = true;
detailView = new cDetailView(activeGrid->GetEvent(), footer);
footer->SetDetailedViewMode();
osdManager.Flush();
detailView->Start();
osdManager.Flush();
}
}
void cTvGuideOsd::processNumKey(int numKey) {
if (config.numkeyMode == 0) {
//timely jumps with 1,3,4,6,7,9
TimeJump(numKey);
} else {
//jump to specific channel
ChannelJump(numKey);
}
}
void cTvGuideOsd::TimeJump(int mode) {
switch (mode) {
case 1: {
timeManager->DelStep(((config.displayMode == eVertical) ? config.bigStepHours : config.bigStepHoursHorizontal) * 60);
}
break;
case 3: {
timeManager->AddStep(((config.displayMode == eVertical) ? config.bigStepHours : config.bigStepHoursHorizontal) * 60);
}
break;
case 4: {
timeManager->DelStep(((config.displayMode == eVertical) ? config.hugeStepHours : config.hugeStepHoursHorizontal) * 60);
}
break;
case 6: {
timeManager->AddStep(((config.displayMode == eVertical) ? config.hugeStepHours : config.hugeStepHoursHorizontal) * 60);
}
break;
case 7: {
cTimeManager primeChecker;
primeChecker.Now();
time_t prevPrime = primeChecker.getPrevPrimetime(timeManager->GetStart());
if (primeChecker.tooFarInPast(prevPrime))
return;
timeManager->SetTime(prevPrime);
}
break;
case 9: {
cTimeManager primeChecker;
time_t nextPrime = primeChecker.getNextPrimetime(timeManager->GetStart());
timeManager->SetTime(nextPrime);
}
break;
default:
return;
}
drawGridsTimeJump();
timeLine->DrawDateViewer();
timeLine->DrawClock();
timeLine->DrawTimeline();
osdManager.Flush();
}
int cTvGuideOsd::GetLastValidChannel(void) {
return channelGroups->GetLastValidChannel();
}
void cTvGuideOsd::ChannelJump(int num) {
if (!channelJumper) {
int lastValidChannel = GetLastValidChannel();
channelJumper = new cChannelJump(channelGroups, lastValidChannel);
}
channelJumper->Set(num);
channelJumper->DrawText();
osdManager.Flush();
}
void cTvGuideOsd::CheckTimeout(void) {
if (!channelJumper)
return;
if (channelJumper->TimeOut()) {
int newChannelNum = channelJumper->GetChannel();
delete channelJumper;
channelJumper = NULL;
const cChannel *newChannel;
#if VDRVERSNUM >= 20301
{
LOCK_CHANNELS_READ;
newChannel = Channels->GetByNumber(newChannelNum);
}
#else
newChannel = Channels.GetByNumber(newChannelNum);
#endif
if (newChannel) {
readChannels(newChannel);
if (columns.Count() > 0) {
drawGridsChannelJump();
}
}
osdManager.Flush();
}
}
void cTvGuideOsd::SetTimers() {
for (cChannelEpg *column = columns.First(); column; column = columns.Next(column)) {
column->SetTimers();
}
}
eOSState cTvGuideOsd::ProcessKey(eKeys Key) {
eOSState state = osContinue;
cPixmap::Lock();
bool alreadyUnlocked = false;
if (recMenuView->IsActive()) {
state = recMenuView->ProcessKey(Key);
if (state == osEnd) {
SetTimers();
osdManager.Flush();
}
state = osContinue;
} else if (detailViewActive) {
if ((Key & ~k_Repeat) == kRed) {
delete detailView;
detailView = NULL;
detailViewActive = false;
processKeyRed();
} else if ((Key & ~k_Repeat) == kBlue) {
delete detailView;
detailView = NULL;
detailViewActive = false;
if ((config.blueKeyMode == eBlueKeySwitch) || (config.blueKeyMode == eBlueKeyFavorites)) {
state = ChannelSwitch(&alreadyUnlocked);
} else {
osdManager.Flush();
state = osContinue;
}
} else if ((Key & ~k_Repeat) == kOk && (config.blueKeyMode == eBlueKeyEPG)) {
delete detailView;
detailView = NULL;
detailViewActive = false;
state = ChannelSwitch(&alreadyUnlocked);
} else {
state = detailView->ProcessKey(Key);
if (state == osEnd) {
delete detailView;
detailView = NULL;
detailViewActive = false;
osdManager.Flush();
state = osContinue;
}
}
} else {
switch (Key & ~k_Repeat) {
case kUp: processKeyUp(); break;
case kDown: processKeyDown(); break;
case kLeft: processKeyLeft(); break;
case kRight: processKeyRight(); break;
case kRed: processKeyRed(); break;
case kGreen: processKeyGreen(); break;
case kYellow: processKeyYellow(); break;
case kBlue: state = processKeyBlue(&alreadyUnlocked); break;
case kOk: state = processKeyOk(&alreadyUnlocked); break;
case kBack: state = osEnd; break;
case k0 ... k9: processNumKey(Key - k0); break;
case kFastRew: TimeJump(1); break; // Doesnt work, if used from timeshiftmode
case kFastFwd: TimeJump(3); break;
case kPrev: TimeJump(4); break;
case kNext: TimeJump(6); break;
case kNone: if (channelJumper) CheckTimeout(); break;
default: break;
}
if (timeLine->DrawClock()) {
osdManager.Flush();
}
}
if (!alreadyUnlocked) {
cPixmap::Unlock();
}
return state;
}
void cTvGuideOsd::dump() {
esyslog("tvguide: ------Dumping Content---------");
activeGrid->debug();
// int i=1;
for (cChannelEpg *col = columns.First(); col; col = columns.Next(col)) {
col->dumpGrids();
}
}