2 Copyright 2005 Brian Walton
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 vepg presents a 2 dimensional electronic programme guide with channels down
22 the y axis and time along the x axis.
23 Programmes are layed on the x axis as alterate coloured blocks with as much
24 of the programme title as will fit inside the block shown as text.
25 Up and down commands step through the channels whilst left and right commands
26 move through the programmes of the currently selected channel.
27 When a programme is selected, it highlights in the grid and full programe details
28 (start time, title and description) are displayed in an area at te top left of the screen.
29 Any currently programmed timers will display in the grid and in the orogramme detail window as red
30 It is possible to select a programme to be recorded by pressing the record button.
31 The video stream currently being viewed is shown as quarter screen in the top right.
37 #include "vchannellist.h"
40 #include "vepgsettimer.h"
45 #include "vvideolive.h"
50 VEpg* VEpg::instance = NULL;
52 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ULONG streamType)
55 currentChannelIndex = tcurrentChannelIndex;
57 // PAL / NTSC sizes -----------------------
61 int summaryLines, summaryLowerPadding;
63 //UINT gridRows; // is a class member
65 if (Video::getInstance()->getFormat() == Video::PAL)
72 summaryLowerPadding = 18;
83 summaryLowerPadding = 28;
88 // initialise variables and pointers
89 boxstack = BoxStack::getInstance();
92 chanList = VDR::getInstance()->getChannelsList(streamType); //TODO want to be able to display video and radio together
95 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
97 // initialise array of pointers to eventlist structures
98 eventLista[listIndex] = NULL;
101 // Create pallet on which to paint our epg view and position it in centre of screen.
102 // Need to reduce size to deal with overscanning TVs.
104 setSize(xsize, ysize);
106 setPosition(xpos, ypos);
109 // Colour transparent = Colour(0, 0, 0, 0);
110 // setBackgroundColour(transparent);
112 // progTitle.setSurface(surface);
113 progTitle.setPosition(0,0);
114 progTitle.setSize(300,(Surface::getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels
115 progTitle.setBackgroundColour(Colour::TITLEBARBACKGROUND);
116 progTitle.setTextPos(5, 16);
120 // progInfo.setSurface(surface);
121 progInfo.setBackgroundColour(Colour::VIEWBACKGROUND);
122 progInfo.setPosition(0, progTitle.getY2());
123 progInfo.setSize(300,((Surface::getFontHeight() + 4) * summaryLines) + summaryLowerPadding);
127 // chanName.setSurface(surface);
128 chanName.setSize(510, (Surface::getFontHeight() + 4));
129 chanName.setPosition(305, chanNameYpos);
130 Colour t1(0, 0, 0, 90);
131 chanName.setBackgroundColour(t1);
134 // create area to display list of channels
135 // chanListbox.setSurface(surface); // add channel list
136 chanListbox.setPosition(0, progInfo.getY2() + Surface::getFontHeight() + 8); // position channel list
137 chanListbox.setSize(150, ((Surface::getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
138 chanListbox.setGap(2);
141 // populate channel list
146 for (UINT i = 0; i < chanList->size(); i++)
148 chan = (*chanList)[i];
149 if (i == currentChannelIndex)
151 chan->index = chanListbox.addOption(chan->name, 0, first);
154 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
157 listTop = chanListbox.getTopOption();
158 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
159 time(<ime); // set ltime to now
160 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
161 time(&selTime); // set selTime to now
162 updateEventList(); // get list of programmes
167 Timers::getInstance()->cancelTimer(this, 1);
171 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
173 if (eventLista[listIndex])
175 (eventLista)[listIndex]->clear();
176 delete eventLista[listIndex];
179 // delete [] eventLista; // FIXME
181 // destroy dynamically allocated memory
184 VEpg* VEpg::getInstance()
189 void VEpg::setInfo(Event* event)
192 struct tm* btime; // to hold programme start and end time
193 char timeString[9]; // to hold programme start and end time
194 int length = strlen(event->title); // calculate length of programme title string
195 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
196 btime = localtime((time_t*)&event->time); //get programme start time
198 strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -
200 strftime(timeString, 9, "%H:%M - ", btime); // and format it as hh:mm -
202 strcpy(title, timeString); // put it in our buffer
203 t = event->time + event->duration; //get programme end time
204 btime = localtime(&t);
206 strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -
208 strftime(timeString, 7, "%H:%M ", btime); // and format it as hh:mm -
210 strcat(title, timeString); // put it in our buffer
211 strcat(title, event->title); // then add the programme title
212 progTitle.setText(title); // sput this sring in our text box
213 length = strlen(event->description);
214 char* info = new char[length + 1]; // create programme detail string
215 strcpy(info, event->description);
216 progInfo.setText(info); // show programme detail string
217 // destroy dynamically allocated memory
224 // View::draw(); // draw pallet
226 Colour transparent = Colour(0, 0, 0, 0);
227 fillColour(transparent);
230 // Moved all the dynamic data drawing to a seperate function
232 // Display the status and key stuff at the bottom
234 UINT keyx = chanListbox.getRootBoxOffsetX();
235 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
236 Colour ref1 = Colour(100, 100, 100, 255);
237 rectangle(keyx, keyy, 605, Surface::getFontHeight() * 2 + 14, ref1);
242 w.nextSymbol = WSymbol::LEFTARROW;
243 w.setPosition(keyx + 1, keyy + 20);
246 w.nextSymbol = WSymbol::UP;
247 w.setPosition(keyx + 26, keyy + 3);
250 w.nextSymbol = WSymbol::DOWN;
251 w.setPosition(keyx + 26, keyy + 36);
254 w.nextSymbol = WSymbol::RIGHTARROW;
255 w.setPosition(keyx + 50, keyy + 20);
258 drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT);
260 Colour ref2 = Colour(200, 0, 0, 255);
261 rectangle(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, ref2);
262 drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT);
264 Colour ref3 = Colour(0, 200, 0, 255);
265 rectangle(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref3);
266 drawText(tr("Page down"), keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
268 Colour ref4 = Colour(200, 200, 0, 255);
269 rectangle(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, ref4);
270 drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT);
272 Colour ref5 = Colour(0, 0, 200, 255);
273 rectangle(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref5);
274 drawText(tr("+24 hours"), keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
276 Colour ref6 = Colour(180, 180, 180, 255);
277 rectangle(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, ref6);
278 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT);
280 Colour ref7 = Colour(180, 180, 180, 255);
281 rectangle(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, ref7);
282 Colour red = Colour(130, 0, 0);
283 drawText(tr("Rec: Set timer"), keyx + 292, keyy + Surface::getFontHeight() + 9, red);
285 Colour ref8 = Colour(180, 180, 180, 255);
286 rectangle(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, ref8);
287 w.nextSymbol = WSymbol::PLAY;
288 w.setPosition(keyx + 476, keyy + 5);
290 drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT);
292 Colour ref9 = Colour(180, 180, 180, 255);
293 rectangle(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, ref9);
294 drawText(tr("Go: Preview"), keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
297 // Draw all the dynamic data
301 void VEpg::drawData()
303 // Not doing View::draw() every time causes
304 // things not to be cleared off the surface properly
305 // So, blank out the data area first
308 chanListbox.getRootBoxOffsetX(),
309 chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3,
310 155 + WINDOW_WIDTH * MINUTE_SCALE,
311 chanListbox.getHeight() + Surface::getFontHeight() + 4,
316 chanName.draw(); // TODO this should be dealt with by vvideolive
321 // Set timer to redraw to move the current time bar
325 if (dt == 0) dt = 60;
327 Timers::getInstance()->setTimerT(this, 1, dt);
330 void VEpg::timercall(int clientReference)
333 // Put updateView through master mutex since boxstack is not mutex protected
334 Message* m = new Message();
335 m->message = Message::REDRAW;
339 Command::getInstance()->postMessageFromOuterSpace(m);
342 int VEpg::handleCommand(int command)
348 { // cursor up the channel list
351 boxstack->update(this);
354 case Remote::DF_DOWN:
356 { // cursor down the channel list
357 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
361 boxstack->update(this);
362 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
366 case Remote::DF_LEFT:
368 { // cursor left through time
369 selTime = thisEvent.time - 1;
371 boxstack->update(this);
374 case Remote::DF_RIGHT:
377 // cursor right through time
378 selTime = thisEvent.time + thisEvent.duration;
380 boxstack->update(this);
385 // cursor up one page
386 chanListbox.pageUp();
388 boxstack->update(this);
393 // cursor down one page
394 chanListbox.pageDown();
396 boxstack->update(this);
401 // step forward 24 hours
402 selTime += 24 * 60 * 60;
404 boxstack->update(this);
409 // step forward 24 hours
410 selTime -= 24 * 60 * 60;
412 boxstack->update(this);
417 if (!chanList) return 2;
418 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
419 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
422 boxstack->update(vs);
429 if (!chanList) return 2;
431 // select programme and display menu TODO currently just changes to selected channel
433 currentChannelIndex = chanListbox.getCurrentOption();
437 Message* m = new Message(); // Must be done after this view deleted
440 m->message = Message::CHANNEL_CHANGE;
441 m->parameter = (*chanList)[currentChannelIndex]->number;
442 Command::getInstance()->postMessageNoLock(m);
447 if(command == Remote::GO)
449 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
454 // return to normal TV mode
455 if (parent) // ptr check done in case being tested from videorec
457 Message* m = new Message(); // Must be done after this view deleted
460 m->message = Message::EPG_CLOSE;
461 Command::getInstance()->postMessageNoLock(m);
465 case Remote::CHANNELUP:
467 if (currentChannelIndex == (chanList->size() - 1)) // at the end
468 currentChannelIndex = 0;
470 ++currentChannelIndex;
474 Message* m = new Message(); // Must be done after this view deleted
477 m->message = Message::CHANNEL_CHANGE;
478 m->parameter = (*chanList)[currentChannelIndex]->number;
479 Command::getInstance()->postMessageNoLock(m);
486 case Remote::CHANNELDOWN:
488 if (currentChannelIndex == 0) // at the start
489 currentChannelIndex = chanList->size() - 1; // so go to end
491 --currentChannelIndex;
495 Message* m = new Message(); // Must be done after this view deleted
498 m->message = Message::CHANNEL_CHANGE;
499 m->parameter = (*chanList)[currentChannelIndex]->number;
500 Command::getInstance()->postMessageNoLock(m);
508 // stop command getting to any more views
512 void VEpg::drawgrid() // redraws grid and select programme
514 // draw the grid of programmes
517 time(&t); // set t = now
519 selTime = t; // don't allow cursor in the past
520 if(listTop != chanListbox.getTopOption())
522 // chanListbox has scrolled TODO speed up by changing only rows that have changed
523 listTop = chanListbox.getTopOption();
526 if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))
528 // we have cursored back before left time of window
529 //TODO check that this and above don't happen together
530 ltime = prevHour(&selTime);
534 Colour white = Colour(255, 255, 255, 255);
539 strftime(timeString, 19, "%a %d %b", tms);
540 int timey = chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3;
542 drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date
543 strftime(timeString, 19, "%H:%M", tms);
544 drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time
545 rectangle(155, timey + Surface::getFontHeight(), 2, 7, white);
548 strftime(timeString, 19, "%H:%M", tms);
549 drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time
550 rectangle(335, timey + Surface::getFontHeight(), 2, 7, white);
553 strftime(timeString, 19, "%H:%M", tms);
554 drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time
555 rectangle(515, timey + Surface::getFontHeight(), 2, 7, white);
556 // pointer to selTime
557 //rectangle(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, Colour(255, 50, 50, 255));
561 if ((t >= ltime) && (t < (ltime + 9000)))
563 rectangle(155 + (t - ltime) / 20, timey + Surface::getFontHeight(), 2, ((Surface::getFontHeight() + 2) * 7) + 7 + 2, Colour::RED);
566 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
568 Event noevent; // an event to use if there are gaps in the epg
569 thisEvent.setdescription(tr("There are no programme details available for this period"));
570 thisEvent.duration = WINDOW_WIDTH * 60;
571 thisEvent.time = ltime;
572 thisEvent.settitle(tr("No programme details"));
574 bool swapColour = FALSE; // alternate cell colour
575 bool currentRow = FALSE;
576 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
577 Colour bg, fg; // background colour of cells in grid
578 // for each displayed channel, find programmes that fall in 2.5 hour time window
579 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
581 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
582 continue; // ensure nothing populates grid below last channel
583 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
584 noevent.time = ltime;
585 noevent.duration = WINDOW_WIDTH * 60;
586 noevent.settitle("");
587 paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
590 thisEvent.setdescription(tr("There are no programme details available for this period"));
591 thisEvent.duration = WINDOW_WIDTH * 60;
592 thisEvent.time = ltime;
593 thisEvent.settitle(tr("No programme details"));
596 if (eventLista[listIndex])
598 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
599 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
601 fg = Colour::LIGHTTEXT;
602 event = (*eventLista[listIndex])[e];
605 UINT end = event->time + event->duration; // programme end time
606 if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window
607 continue; // that's enough of this channel's events
608 if(end <= UINT(ltime)) // programme ends before LHS of window
609 continue; // this event is before the window - let's try the next event
610 // this event is one we are interested in
611 bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour
612 swapColour = !swapColour; // it wil be the other colour next time
613 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
615 // this is the selected programme
616 thisEvent.setdescription(event->description);
617 thisEvent.duration = event->duration;
618 thisEvent.time = event->time;
619 thisEvent.settitle(event->title);
620 thisEvent.id = event->id;
621 if(thisEvent.id == 0)
623 bg = Colour::SELECTHIGHLIGHT; // highlight cell
624 fg = Colour::DARKTEXT;
628 if (currentRow && thisEvent.id == 0)
630 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
631 thisEvent.time = end;
632 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
633 thisEvent.duration = event->time - thisEvent.time;
636 paintCell(event, y, bg, fg);
642 // no event list for this channel. Already painted noevent colour so just highlight if selected
645 bg = Colour::SELECTHIGHLIGHT; // highlight cell
646 fg = Colour::DARKTEXT;
647 paintCell(&thisEvent, y, bg, fg);
651 bg = Colour::NOPROGRAMME;
652 fg = Colour::LIGHTTEXT;
653 noevent.settitle(tr("No programme details"));
654 paintCell(&noevent, y, bg, fg);
657 y += Surface::getFontHeight() + 2;
662 void VEpg::updateEventList()
664 if (!chanList) return;
666 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
668 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
670 chan = (*chanList)[listTop + listIndex];
672 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
676 void VEpg::setCurrentChannel()
678 chanName.setText((*chanList)[currentChannelIndex]->name);
681 chanName.getRootBoxRegion(&r);
682 boxstack->update(this, &r);
685 void VEpg::paintCell(Event* event, int yOffset, const Colour& bg, const Colour& fg)
688 w = x = 0; // keep compiler happy
691 h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
692 UINT end = event->time + event->duration; // programme end time
693 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
695 x = 155; // LHS of window
696 if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))
697 w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window
699 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
701 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window
703 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
704 w = MINUTE_SCALE * event->duration / 60;
705 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
706 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
708 if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)
709 w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window
710 rectangle(x, y, w, h, bg);
711 char* tt = new char[strlen(event->title) + 1];
712 strcpy (tt, event->title);
714 for (UINT textPos = 0; textPos < strlen(tt); textPos++)
716 int thisCharWidth = surface->getCharWidth(tt[textPos]);
717 if (textWidth + thisCharWidth > w) // text will not fit in cell
722 textWidth += thisCharWidth;
724 char* tT = new char[textWidth];
727 strncpy(tT, tt, textWidth - 1);
728 tT[textWidth - 1] = '\0';
729 surface->drawText(tT, x+2, y, fg.rgba());
735 time_t VEpg::prevHour(time_t* t)
744 void VEpg::processMessage(Message* m)
746 if (m->message == Message::MOUSE_MOVE)
748 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
751 boxstack->update(this);
754 else if (m->message == Message::MOUSE_LBDOWN)
756 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
758 boxstack->handleCommand(Remote::OK); //simulate OK press
762 //check if press is outside this view! then simulate cancel
763 int x=(m->parameter>>16)-getScreenX();
764 int y=(m->parameter&0xFFFF)-getScreenY();
765 int keyx = chanListbox.getRootBoxOffsetX();
766 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
768 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
770 boxstack->handleCommand(Remote::BACK); //simulate cancel press
772 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
774 boxstack->handleCommand(Remote::RED);
776 else if (x>=(keyx+72) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
778 boxstack->handleCommand(Remote::GREEN);
780 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
782 boxstack->handleCommand(Remote::YELLOW);
784 else if (x>=(keyx+180) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
786 boxstack->handleCommand(Remote::BLUE);
788 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+Surface::getFontHeight() + 2))
790 boxstack->handleCommand(Remote::BACK);
792 else if (x>=(keyx+290) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
794 boxstack->handleCommand(Remote::RECORD);
796 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+Surface::getFontHeight() + 2))
798 boxstack->handleCommand(Remote::PLAY);
800 else if (x>=(keyx+474) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
802 boxstack->handleCommand(Remote::GO);
804 else if ( x>=(chanListbox.getRootBoxOffsetX())
805 && y>=(chanListbox.getRootBoxOffsetY() + 5)
806 // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE)
807 &&y<=(chanListbox.getRootBoxOffsetY() - Surface::getFontHeight()
808 - 3+(int)chanListbox.getHeight() + Surface::getFontHeight() + 3)
811 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
812 int row=cy/(Surface::getFontHeight()+2);
813 int clistTop = chanListbox.getTopOption();
814 chanListbox.hintSetCurrent(clistTop+row);
816 time_t ttime = cx*60/MINUTE_SCALE+ltime;
817 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
821 boxstack->update(this);