--- /dev/null
+/*\r
+ Copyright 2004-2005 Brian Walton\r
+\r
+ This file is part of VOMP.\r
+\r
+ VOMP is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU General Public License as published by\r
+ the Free Software Foundation; either version 2 of the License, or\r
+ (at your option) any later version.\r
+\r
+ VOMP is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU General Public License for more details.\r
+\r
+ You should have received a copy of the GNU General Public License\r
+ along with VOMP; if not, write to the Free Software\r
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+*/\r
+/*\r
+ vepg presents a 2 dimensional electronic programme guide with channels down \r
+ the y axis and time along the x axis.\r
+ Programmes are layed on the x axis as alterate coloured blocks with as much \r
+ of the programme title as will fit inside the block shown as text.\r
+ Up and down commands step through the channels whilst left and right commands \r
+ move through the programmes of the currently selected channel.\r
+ When a programme is selected, it highlights in the grid and full programe details \r
+ (start time, title and description) are displayed in an area at te top left of the screen.\r
+ Any currently programmed timers will display in the grid and in the orogramme detail window as red\r
+ It is possible to select a programme to be recorded by pressing the record button.\r
+ The video stream currently being viewed is shown as quarter screen in the top right.\r
+*/\r
+ \r
+#include "vepg.h"\r
+\r
+VEpg::VEpg(VVideoLive* v, UINT currentChannel)\r
+{\r
+ for(UINT listIndex = 0; listIndex < 7; listIndex++)\r
+ {\r
+ // initialise array of pointers to eventlist structures\r
+ eventLista[listIndex] = NULL;\r
+ }\r
+ \r
+\r
+// Create pallet on which to paint our epg view and position it in centre of screen.\r
+// Need to reduce size to deal with overscanning TVs.\r
+ videoLive = v;\r
+// create(640, 530);\r
+//TODO: have size for ntsc\r
+ if (Video::getInstance()->getFormat() == Video::PAL)\r
+ {\r
+ create(632, 520);\r
+ setScreenPos(44, 30);\r
+ }\r
+ else\r
+ {\r
+ // what can we do about poor ntsc?\r
+ create(512, 384);\r
+ setScreenPos(64, 51);\r
+ }\r
+ \r
+// beautify\r
+ Colour transparent = Colour(0, 0, 0, 0);\r
+ setBackgroundColour(transparent);\r
+// initialise variables and pointers\r
+ eventList = NULL;\r
+ chanList = VDR::getInstance()->getChannelsList(VDR::VIDEO); //TODO want to be able to display video and radio together\r
+ e = 0;\r
+ progTitle.setSurface(surface);\r
+ progTitle.setSurfaceOffset(0,0);\r
+ progTitle.setDimensions(300,(Surface::getFontHeight() + 6) * 2 + 2); //paragraph line seperation is 6 pixels\r
+ progTitle.setBackgroundColour(Colour::TITLEBARBACKGROUND);\r
+ progInfo.setSurface(surface);\r
+ progInfo.setSurfaceOffset(0, progTitle.getOffsetY() + progTitle.getHeight());\r
+ progInfo.setDimensions(300,(Surface::getFontHeight() + 6) * 8 + 2);\r
+ chanName.setSurface(surface);\r
+ chanName.setDimensions(510, (Surface::getFontHeight() + 4));\r
+ chanName.setSurfaceOffset(305,230);\r
+ chanName.setBackgroundColour(Colour(0, 0, 0, 90));\r
+// create area to display list of channels\r
+ chanListbox.setSurface(surface); // add channel list\r
+ chanListbox.setSurfaceOffset(0, progInfo.getOffsetY() + progInfo.getHeight() + Surface::getFontHeight() + 8); // position channel list\r
+ chanListbox.setDimensions(150, (Surface::getFontHeight() + 1) * 7 + 5); //listbox line seperation is 1 pixel\r
+// populate channel list\r
+ Channel* chan;\r
+ int first = 1;\r
+ for (UINT i = 0; i < chanList->size(); i++)\r
+ {\r
+ chan = (*chanList)[i];\r
+ if (i == currentChannel)\r
+ first = 1;\r
+ chan->index = chanListbox.addOption(chan->name, first);\r
+ first = 0;\r
+ }\r
+ listTop = chanListbox.getTopOption();\r
+ chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work\r
+ chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);\r
+ time(<ime); // set ltime to now\r
+ ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?\r
+ time(&selTime); // set selTime to now\r
+ updateEventList(); // get list of programmes\r
+}\r
+\r
+VEpg::~VEpg()\r
+{\r
+ for(int listIndex = 0; listIndex < 7; listIndex++)\r
+ {\r
+ if (eventLista[listIndex])\r
+ {\r
+ (eventLista)[listIndex]->clear();\r
+ delete eventLista[listIndex];\r
+ }\r
+ }\r
+// delete [] eventLista;\r
+\r
+// destroy dynamically allocated memory\r
+}\r
+\r
+void VEpg::setInfo(Event* event)\r
+{\r
+ time_t t;\r
+ struct tm* btime; // to hold programme start and end time\r
+ char* timeString = new char[9]; // to hold programme start and end time\r
+ int length = strlen(event->title); // calculate length of programme title string\r
+ char* title = new char[length + 15]; // create string to hold start time, end time and programme title\r
+ btime = localtime((time_t*)&event->time); //get programme start time\r
+ strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -\r
+ strcpy(title, timeString); // put it in our buffer\r
+ t = event->time + event->duration; //get programme end time\r
+ btime = localtime(&t);\r
+ strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -\r
+ strcat(title, timeString); // put it in our buffer\r
+ strcat(title, event->title); // then add the programme title\r
+ progTitle.setText(title); // sput this sring in our text box\r
+ length = strlen(event->description);\r
+ char* info = new char[length + 1]; // create programme detail string\r
+ strcpy(info, event->description);\r
+ progInfo.setText(info); // show programme detail string\r
+// destroy dynamically allocated memory\r
+ delete info;\r
+ delete title;\r
+ delete timeString;\r
+}\r
+\r
+void VEpg::draw()\r
+{\r
+ View::draw(); // draw pallet\r
+ chanListbox.draw();\r
+ drawgrid();\r
+ chanName.draw(); // TODO this should be dealt with by vvideolive\r
+ progTitle.draw();\r
+ progInfo.draw();\r
+\r
+// Display the status and key stuff at the bottom\r
+ int keyx = chanListbox.getOffsetX();\r
+ int keyy = chanListbox.getOffsetY() + chanListbox.getHeight() + 2;\r
+ surface->fillblt(keyx, keyy, 610, Surface::getFontHeight() * 2 + 14, surface->rgba(100, 100, 100, 255));\r
+ \r
+ WSymbol w;\r
+ w.setSurface(surface);\r
+ \r
+ w.nextSymbol = WSymbol::LEFTARROW;\r
+ w.setSurfaceOffset(keyx + 1, keyy + 20);\r
+ w.draw();\r
+ \r
+ w.nextSymbol = WSymbol::UP;\r
+ w.setSurfaceOffset(keyx + 26, keyy + 3);\r
+ w.draw();\r
+\r
+ w.nextSymbol = WSymbol::DOWN;\r
+ w.setSurfaceOffset(keyx + 26, keyy + 36);\r
+ w.draw();\r
+ \r
+ w.nextSymbol = WSymbol::RIGHTARROW;\r
+ w.setSurfaceOffset(keyx + 50, keyy + 20);\r
+ w.draw();\r
+ \r
+ drawText("OK", keyx + 18, keyy + 20, Colour::LIGHTTEXT);\r
+ \r
+ surface->fillblt(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, surface->rgba(200, 0, 0, 255));\r
+ drawText("Page up", keyx + 74, keyy + 5, Colour::LIGHTTEXT);\r
+ \r
+ surface->fillblt(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, surface->rgba(0, 200, 0, 255));\r
+ drawText("Page down", keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
+ \r
+ surface->fillblt(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, surface->rgba(200, 200, 0, 255));\r
+ drawText("-24 hours", keyx + 182, keyy + 5, Colour::LIGHTTEXT);\r
+ \r
+ surface->fillblt(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, surface->rgba( 0, 0, 200, 255));\r
+ drawText("+24 hours", keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
+ \r
+ surface->fillblt(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
+ drawText("Guide / Back: Close", keyx + 292 , keyy + 5, Colour::LIGHTTEXT);\r
+ \r
+ surface->fillblt(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
+ Colour red = Colour(130, 0, 0);\r
+ drawText("Rec: Set timer", keyx + 292, keyy + Surface::getFontHeight() + 9, red);\r
+ \r
+ surface->fillblt(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
+ w.nextSymbol = WSymbol::PLAY;\r
+ w.setSurfaceOffset(keyx + 476, keyy + 5);\r
+ w.draw();\r
+ drawText("Sel channel", keyx + 496, keyy + 5, Colour::LIGHTTEXT);\r
+ \r
+ surface->fillblt(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
+ drawText("Go: Preview", keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
+}\r
+\r
+int VEpg::handleCommand(int command)\r
+{\r
+ switch(command)\r
+ {\r
+ case Remote::DF_UP:\r
+ case Remote::UP:\r
+ { // cursor up the channel list\r
+ chanListbox.up();\r
+ draw();\r
+ show();\r
+ return 2;\r
+ }\r
+ case Remote::DF_DOWN:\r
+ case Remote::DOWN:\r
+ { // cursor down the channel list\r
+ chanListbox.down();\r
+ draw();\r
+ show();\r
+ return 2;\r
+ }\r
+ case Remote::DF_LEFT:\r
+ case Remote::LEFT:\r
+ { // cursor left through time\r
+ selTime = thisEvent.time - 1;\r
+ draw();\r
+ show();\r
+ return 2;\r
+ }\r
+ case Remote::DF_RIGHT:\r
+ case Remote::RIGHT:\r
+ { \r
+ // cursor right through time\r
+ selTime = thisEvent.time + thisEvent.duration;\r
+ draw();\r
+ show();\r
+ return 2;\r
+ }\r
+ case Remote::RED:\r
+ { \r
+ // cursor up one page\r
+ chanListbox.pageUp();\r
+ draw();\r
+ show();\r
+ return 2;\r
+ }\r
+ case Remote::GREEN:\r
+ { \r
+ // cursor down one page\r
+ chanListbox.pageDown();\r
+ draw();\r
+ show();\r
+ return 2;\r
+ }\r
+ case Remote::BLUE:\r
+ { \r
+ // step forward 24 hours\r
+ selTime += 24 * 60 * 60;\r
+ draw();\r
+ show();\r
+ return 2;\r
+ }\r
+ case Remote::YELLOW:\r
+ { \r
+ // step forward 24 hours\r
+ selTime -= 24 * 60 * 60;\r
+ draw();\r
+ show();\r
+ return 2;\r
+ }\r
+ case Remote::RECORD:\r
+ {\r
+ //TODO\r
+ return 2;\r
+ }\r
+ case Remote::PLAY:\r
+ case Remote::GO:\r
+ case Remote::OK:\r
+ { // select programme and display menu TODO currently just changes to selected channel\r
+ Message* m = new Message();\r
+ m->from = this;\r
+ m->to = videoLive;\r
+ m->message = Message::CHANNEL_CHANGE;\r
+ m->parameter = (*chanList)[chanListbox.getCurrentOption()]->number;\r
+ ViewMan::getInstance()->postMessage(m);\r
+ if(command == Remote::GO)\r
+ return 2;\r
+ // GO just changes channel in preview, PLAY changes channel and returns to normal TV\r
+ }\r
+ case Remote::BACK:\r
+ case Remote::GUIDE:\r
+ {\r
+ // return to normal TV mode TODO vvideolive should handle this (via message?)\r
+ Video::getInstance()->setMode(Video::NORMAL);\r
+ videoLive->setEpgMode(FALSE);\r
+ return 4;\r
+ }\r
+ case Remote::CHANNELUP:\r
+ {\r
+ // change up channel on live TV TODO vvideolive should handle this\r
+ Message* m = new Message();\r
+ m->from = this;\r
+ m->to = videoLive;\r
+ m->message = Message::CHANNEL_UP;\r
+ ViewMan::getInstance()->postMessage(m);\r
+ return 2;\r
+ }\r
+ case Remote::CHANNELDOWN:\r
+ { // change down channel on live TV TODO vvideolive should handle this\r
+ Message* m = new Message();\r
+ m->from = this;\r
+ m->to = videoLive;\r
+ m->message = Message::CHANNEL_DOWN;\r
+ ViewMan::getInstance()->postMessage(m);\r
+ return 2;\r
+ }\r
+ }\r
+ // stop command getting to any more views\r
+ return 1;\r
+}\r
+\r
+void VEpg::drawgrid() // redraws grid and select programme\r
+{\r
+// draw the grid of programmes\r
+ char timeString[20];\r
+ time_t t;\r
+ time(&t); // set t = now\r
+ if(selTime < t)\r
+ selTime = t; // don't allow cursor in the past\r
+ if(listTop != chanListbox.getTopOption())\r
+ {\r
+ // chanListbox has scrolled TODO speed up by changing only rows that have changed\r
+ listTop = chanListbox.getTopOption();\r
+ updateEventList();\r
+ }\r
+ if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))\r
+ {\r
+ // we have cursored back before left time of window\r
+ //TODO check that this and above don't happen together\r
+ ltime = prevHour(&selTime);\r
+ updateEventList();\r
+ }\r
+ // draw time scale\r
+ t = ltime;\r
+ struct tm* tms;\r
+ tms = localtime(&t);\r
+ strftime(timeString, 19, "%a %e %b", tms);\r
+ int timey = chanListbox.getOffsetY() - Surface::getFontHeight() - 3;\r
+ int timex = 135;\r
+ drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date\r
+ strftime(timeString, 19, "%H:%M", tms);\r
+ drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time\r
+ surface->fillblt(155, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
+ t = t + 3600;\r
+ tms = localtime(&t);\r
+ strftime(timeString, 19, "%H:%M", tms);\r
+ drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time\r
+ surface->fillblt(335, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
+ t = t + 3600;\r
+ tms = localtime(&t);\r
+ strftime(timeString, 19, "%H:%M", tms);\r
+ drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time\r
+ surface->fillblt(515, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
+ // pointer to selTime\r
+ surface->fillblt(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 50, 50, 255));\r
+ \r
+ // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?\r
+ Event* event;\r
+ Event noevent; // an event to use if there are gaps in the epg\r
+ thisEvent.setdescription("There are no programme details available for this period");\r
+ thisEvent.duration = WINDOW_WIDTH * 60;\r
+ thisEvent.time = ltime;\r
+ thisEvent.settitle("No programme details");\r
+ thisEvent.id = 0;\r
+ bool swapColour = FALSE; // alternate cell colour\r
+ bool currentRow = FALSE;\r
+ int y = chanListbox.getOffsetY() + 5; // vertical position of cell\r
+ Colour bg, fg; // background colour of cells in grid\r
+ // for each displayed channel, find programmes that fall in 2.5 hour time window\r
+ for(int listIndex = 0; listIndex < 7; listIndex++)\r
+ {\r
+ if (listTop + listIndex >= chanListbox.getBottomOption())\r
+ continue; // ensure nothing populates grid below last channel\r
+ currentRow = (listTop + listIndex == chanListbox.getCurrentOption());\r
+ noevent.time = ltime;\r
+ noevent.duration = WINDOW_WIDTH * 60;\r
+ noevent.settitle("");\r
+ paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes\r
+ if (currentRow)\r
+ {\r
+ thisEvent.setdescription("There are no programme details available for this period");\r
+ thisEvent.duration = WINDOW_WIDTH * 60;\r
+ thisEvent.time = ltime;\r
+ thisEvent.settitle("No programme details");\r
+ thisEvent.id = 0;\r
+ }\r
+ if (eventLista[listIndex])\r
+ {\r
+ sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());\r
+ for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel\r
+ {\r
+ fg = Colour::LIGHTTEXT;\r
+ event = (*eventLista[listIndex])[e];\r
+ if (event)\r
+ {\r
+ UINT end = event->time + event->duration; // programme end time\r
+ if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window\r
+ continue; // that's enough of this channel's events\r
+ if(end <= UINT(ltime)) // programme ends before LHS of window\r
+ continue; // this event is before the window - let's try the next event\r
+ // this event is one we are interested in\r
+ bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour\r
+ swapColour = !swapColour; // it wil be the other colour next time\r
+ if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)\r
+ {\r
+ // this is the selected programme\r
+ thisEvent.setdescription(event->description);\r
+ thisEvent.duration = event->duration;\r
+ thisEvent.time = event->time;\r
+ thisEvent.settitle(event->title);\r
+ thisEvent.id = event->id;\r
+ if(thisEvent.id == 0)\r
+ thisEvent.id = 1;\r
+ bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
+ fg = Colour::DARKTEXT;\r
+ }\r
+ else\r
+ {\r
+ if (currentRow && thisEvent.id == 0)\r
+ {\r
+ if (end <= UINT(selTime) && end > UINT(thisEvent.time))\r
+ thisEvent.time = end;\r
+ if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)\r
+ thisEvent.duration = event->time - thisEvent.time;\r
+ } \r
+ }\r
+ paintCell(event, y, bg, fg);\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // no event list for this channel. Already painted noevent colour so just highlight if selected\r
+ if (currentRow)\r
+ {\r
+ bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
+ fg = Colour::DARKTEXT;\r
+ paintCell(&thisEvent, y, bg, fg);\r
+ }\r
+ else\r
+ {\r
+ bg = Colour::NOPROGRAMME;\r
+ fg = Colour::LIGHTTEXT;\r
+ noevent.settitle("No programme details");\r
+ paintCell(&noevent, y, bg, fg);\r
+ }\r
+ }\r
+ y += Surface::getFontHeight() + 1;\r
+ }\r
+ setInfo(&thisEvent);\r
+}\r
+\r
+void VEpg::updateEventList() \r
+{\r
+ Channel* chan;\r
+ for(UINT listIndex = 0; listIndex < 7; listIndex++)\r
+ {\r
+ if (eventLista[listIndex])\r
+ {\r
+ if ((eventLista[listIndex])->empty())\r
+ (eventLista)[listIndex]->clear();\r
+ }\r
+ if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))\r
+ continue;\r
+ chan = (*chanList)[listTop + listIndex];\r
+ eventLista[listIndex] = VDR::getInstance()->getChannelSchedule(chan->number, ltime - 1, WINDOW_WIDTH * 60 + 2); // ltime - 1 to get prog before window (allows cursor left past ltime). + 2 to get prog after window\r
+ }\r
+}\r
+\r
+void VEpg::setCurrentChannel(char* chname)\r
+{\r
+ chanName.setText(chname);\r
+ chanName.draw();\r
+ show();\r
+}\r
+\r
+void VEpg::paintCell(Event* event, int yOffset, Colour bg, Colour fg)\r
+{\r
+ int w, x, y, h;\r
+ y =yOffset;\r
+ h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height\r
+ UINT end = event->time + event->duration; // programme end time\r
+ if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window\r
+ {\r
+ x = 155; // LHS of window\r
+ if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))\r
+ w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window\r
+ else\r
+ w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme\r
+ }\r
+ if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window\r
+ {\r
+ x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);\r
+ w = MINUTE_SCALE * event->duration / 60;\r
+ //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)\r
+ // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window\r
+ }\r
+ if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)\r
+ w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window\r
+ surface->fillblt(x, y, w, h, surface->rgba(bg.red, bg.green, bg.blue, bg.alpha));\r
+ char* tt = new char[strlen(event->title) + 1];\r
+ strcpy (tt, event->title);\r
+ int textWidth = 0;\r
+ for (UINT textPos = 0; textPos < strlen(tt); textPos++)\r
+ {\r
+ int thisCharWidth = surface->getCharWidth(tt[textPos]);\r
+ if (textWidth + thisCharWidth > w) // text will not fit in cell\r
+ {\r
+ textWidth = textPos;\r
+ break;\r
+ }\r
+ textWidth += thisCharWidth;\r
+ }\r
+ char* tT = new char[textWidth];\r
+ if(textWidth > 1)\r
+ {\r
+ strncpy(tT, tt, textWidth - 1);\r
+ tT[textWidth - 1] = '\0';\r
+ surface->drawText(tT, x+2, y, fg.red, fg.green, fg.blue);\r
+ }\r
+ delete tT;\r
+ \r
+}\r
+\r
+time_t VEpg::prevHour(time_t* t)\r
+{\r
+ struct tm* tms;\r
+ tms = localtime(t);\r
+ tms->tm_sec = 0;\r
+ tms->tm_min = 0;\r
+ return mktime(tms);\r
+}\r
+\r
viewman = ViewMan::getInstance();
chanList = tchanList;
currentChannel = 0;
+ previousChannel = 0;
unavailable = 0;
unavailableView = NULL;
streamType = tstreamType;
+ vlb = (VLiveBanner*)1; // Can't be NULL because then that is sent to ViewMan::remove and it takes the top view off .. FIXME!
+ epgmode=false;
if (streamType == VDR::RADIO) player = new PlayerVideo(Command::getInstance(), 0, 1);
else player = new PlayerVideo(Command::getInstance(), 0, 0);
{
if (unavailable) showUnavailable(0);
else stop();
- upChannel();
- play();
+ channelChange(OFFSET, UP);
return 2;
}
case Remote::DF_DOWN:
{
if (unavailable) showUnavailable(0);
else stop();
- downChannel();
- play();
+ channelChange(OFFSET, DOWN);
return 2;
}
case Remote::OK:
{
- doBanner();
+ doBanner(true);
+ return 2;
+ }
+ case Remote::GUIDE:
+ case Remote::RED:
+ {
+ if (!epgmode)
+ {
+ showEPG();
+ }
+ epgmode=!epgmode;
return 2;
}
return 1;
}
+void VVideoLive::channelChange(UCHAR changeType, UINT newData)
+{
+ UINT newChannel = 0;
+
+ if (changeType == INDEX)
+ {
+ newChannel = newData;
+ }
+ else if (changeType == NUMBER)
+ {
+ newChannel = channelIndexFromNumber(newData);
+ }
+ else if (changeType == OFFSET)
+ {
+ if (newData == UP) newChannel = upChannel();
+ else newChannel = downChannel();
+ }
+ else if (changeType == PREVIOUS)
+ {
+ newChannel = previousChannel;
+ }
+ else
+ {
+ return; // bad input!
+ }
+
+ previousChannel = currentChannel;
+ currentChannel = newChannel;
+
+ if (unavailable) showUnavailable(0);
+ else stop(1);
+ if(epgmode)
+ if(vepg)
+ vepg->setCurrentChannel((*chanList)[currentChannel]->name);
+ play();
+}
+
void VVideoLive::processMessage(Message* m)
{
if (m->message == Message::CHANNEL_CHANGE)
{
- // check channel number is valid
- currentChannel = channelIndexFromNumber(m->parameter);
- if (unavailable) showUnavailable(0);
- else stop();
- play();
+ channelChange(NUMBER, m->parameter);
}
else if (m->message == Message::CHANNEL_UP)
{
- // from vlivebanner
- if (unavailable) showUnavailable(0);
- else stop(1);
- upChannel();
- play(1);
- vlb->setChannel((*chanList)[currentChannel]);
- vlb->draw();
- vlb->show();
+ // this message is from vlivebanner
+ channelChange(OFFSET, UP);
+ if(!epgmode)
+ {
+ vlb->setChannel((*chanList)[currentChannel]);
+ vlb->draw();
+ vlb->show();
+ }
}
else if (m->message == Message::CHANNEL_DOWN)
{
- // from vlivebanner
- if (unavailable) showUnavailable(0);
- else stop(1);
- downChannel();
- play(1);
- vlb->setChannel((*chanList)[currentChannel]);
- vlb->draw();
- vlb->show();
+ // this message is from vlivebanner
+ channelChange(OFFSET, DOWN);
+ if(!epgmode)
+ {
+ vlb->setChannel((*chanList)[currentChannel]);
+ vlb->draw();
+ vlb->show();
+ }
}
else if (m->message == Message::STREAM_END)
{
}
}
-void VVideoLive::doBanner()
+void VVideoLive::doBanner(bool bannerTakesCommands)
{
- vlb = new VLiveBanner(this, (*chanList)[currentChannel]);
+ if(epgmode)
+ return;
+ vlb = new VLiveBanner(this, (*chanList)[currentChannel], bannerTakesCommands);
viewman->timedDelete(vlb, 4, 0);
Message* m = new Message();
}
}
-void VVideoLive::setChannel(int number)
-{
- currentChannel = channelIndexFromNumber(number);
- play();
-}
-
void VVideoLive::play(int noShowVLB)
{
showUnavailable(0);
if (!available)
{
showUnavailable(1);
- if (!noShowVLB) doBanner();
+ if (!noShowVLB) doBanner(false);
}
else
{
- if (!noShowVLB) doBanner();
+ if (!noShowVLB) doBanner(false);
player->play();
}
}
void VVideoLive::stop(int noRemoveVLB)
{
+printf("1\n");
if (unavailable) return;
+printf("2\n");
if (!noRemoveVLB) viewman->removeView(vlb, 1); // if live banner is present, remove it. won't cause damage if its not present
+printf("3\n");
player->stop();
+printf("4\n");
vdr->stopStreaming();
+printf("5\n");
}
-int VVideoLive::upChannel()
+UINT VVideoLive::upChannel()
{
- if (currentChannel == (chanList->size() - 1)) return 0; // at the end
-
- ++currentChannel;
- return 1;
+ if (currentChannel == (chanList->size() - 1)) // at the end
+ return 0; // so go to start
+ else
+ return currentChannel + 1;
}
-int VVideoLive::downChannel()
+UINT VVideoLive::downChannel()
{
- if (currentChannel == 0) return 0; // at the start
-
- --currentChannel;
- return 1;
+ if (currentChannel == 0) // at the start
+ return chanList->size() - 1; // so go to end
+ else
+ return currentChannel - 1;
}
UINT VVideoLive::channelIndexFromNumber(int number)
return 0;
}
+UINT VVideoLive::getCurrentChannelIndex()
+{
+ return channelIndexFromNumber(currentChannel);
+}
+
+void VVideoLive::showEPG()
+{
+ if (unavailable) showUnavailable(0);
+ vepg = new VEpg(this, currentChannel);
+ ViewMan::getInstance()->addNoLock(vepg);
+ Video::getInstance()->setMode(Video::QUARTER);
+ Video::getInstance()->setPosition(170, 5); //TODO need to deal with 4:3 switching
+ vepg->draw();
+ vepg->show();
+}
+
+void VVideoLive::setEpgMode(bool mode)
+{
+ epgmode = mode;
+}
+