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"
49 VEpg* VEpg::instance = NULL;
51 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ULONG streamType)
54 currentChannelIndex = tcurrentChannelIndex;
56 // PAL / NTSC sizes -----------------------
60 int summaryLines, summaryLowerPadding;
62 //UINT gridRows; // is a class member
64 if (Video::getInstance()->getFormat() == Video::PAL)
71 summaryLowerPadding = 18;
82 summaryLowerPadding = 28;
87 // initialise variables and pointers
88 boxstack = BoxStack::getInstance();
91 chanList = VDR::getInstance()->getChannelsList(streamType); //TODO want to be able to display video and radio together
94 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
96 // initialise array of pointers to eventlist structures
97 eventLista[listIndex] = NULL;
100 // Create pallet on which to paint our epg view and position it in centre of screen.
101 // Need to reduce size to deal with overscanning TVs.
103 setSize(xsize, ysize);
105 setPosition(xpos, ypos);
108 // Colour transparent = Colour(0, 0, 0, 0);
109 // setBackgroundColour(transparent);
111 // progTitle.setSurface(surface);
112 progTitle.setPosition(0,0);
113 progTitle.setSize(300,(Surface::getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels
114 progTitle.setBackgroundColour(Colour::TITLEBARBACKGROUND);
115 progTitle.setTextPos(5, 16);
119 // progInfo.setSurface(surface);
120 progInfo.setBackgroundColour(Colour::VIEWBACKGROUND);
121 progInfo.setPosition(0, progTitle.getY2());
122 progInfo.setSize(300,((Surface::getFontHeight() + 4) * summaryLines) + summaryLowerPadding);
126 // chanName.setSurface(surface);
127 chanName.setSize(510, (Surface::getFontHeight() + 4));
128 chanName.setPosition(305, chanNameYpos);
129 Colour t1(0, 0, 0, 90);
130 chanName.setBackgroundColour(t1);
133 // create area to display list of channels
134 // chanListbox.setSurface(surface); // add channel list
135 chanListbox.setPosition(0, progInfo.getY2() + Surface::getFontHeight() + 8); // position channel list
136 chanListbox.setSize(150, ((Surface::getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
137 chanListbox.setGap(2);
140 // populate channel list
145 for (UINT i = 0; i < chanList->size(); i++)
147 chan = (*chanList)[i];
148 if (i == currentChannelIndex)
150 chan->index = chanListbox.addOption(chan->name, 0, first);
153 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
156 listTop = chanListbox.getTopOption();
157 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
158 time(<ime); // set ltime to now
159 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
160 time(&selTime); // set selTime to now
161 updateEventList(); // get list of programmes
166 Timers::getInstance()->cancelTimer(this, 1);
170 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
172 if (eventLista[listIndex])
174 (eventLista)[listIndex]->clear();
175 delete eventLista[listIndex];
178 // delete [] eventLista; // FIXME
180 // destroy dynamically allocated memory
183 VEpg* VEpg::getInstance()
188 void VEpg::setInfo(Event* event)
191 struct tm* btime; // to hold programme start and end time
192 char timeString[9]; // to hold programme start and end time
193 int length = strlen(event->title); // calculate length of programme title string
194 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
195 btime = localtime((time_t*)&event->time); //get programme start time
197 strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -
199 strftime(timeString, 9, "%H:%M - ", btime); // and format it as hh:mm -
201 strcpy(title, timeString); // put it in our buffer
202 t = event->time + event->duration; //get programme end time
203 btime = localtime(&t);
205 strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -
207 strftime(timeString, 7, "%H:%M ", btime); // and format it as hh:mm -
209 strcat(title, timeString); // put it in our buffer
210 strcat(title, event->title); // then add the programme title
211 progTitle.setText(title); // sput this sring in our text box
212 length = strlen(event->description);
213 char* info = new char[length + 1]; // create programme detail string
214 strcpy(info, event->description);
215 progInfo.setText(info); // show programme detail string
216 // destroy dynamically allocated memory
223 // View::draw(); // draw pallet
225 Colour transparent = Colour(0, 0, 0, 0);
226 fillColour(transparent);
229 // Moved all the dynamic data drawing to a seperate function
231 // Display the status and key stuff at the bottom
233 UINT keyx = chanListbox.getRootBoxOffsetX();
234 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
235 Colour ref1 = Colour(100, 100, 100, 255);
236 rectangle(keyx, keyy, 605, Surface::getFontHeight() * 2 + 14, ref1);
241 w.nextSymbol = WSymbol::LEFTARROW;
242 w.setPosition(keyx + 1, keyy + 20);
245 w.nextSymbol = WSymbol::UP;
246 w.setPosition(keyx + 26, keyy + 3);
249 w.nextSymbol = WSymbol::DOWN;
250 w.setPosition(keyx + 26, keyy + 36);
253 w.nextSymbol = WSymbol::RIGHTARROW;
254 w.setPosition(keyx + 50, keyy + 20);
257 drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT);
259 Colour ref2 = Colour(200, 0, 0, 255);
260 rectangle(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, ref2);
261 drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT);
263 Colour ref3 = Colour(0, 200, 0, 255);
264 rectangle(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref3);
265 drawText(tr("Page down"), keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
267 Colour ref4 = Colour(200, 200, 0, 255);
268 rectangle(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, ref4);
269 drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT);
271 Colour ref5 = Colour(0, 0, 200, 255);
272 rectangle(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref5);
273 drawText(tr("+24 hours"), keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
275 Colour ref6 = Colour(180, 180, 180, 255);
276 rectangle(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, ref6);
277 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT);
279 Colour ref7 = Colour(180, 180, 180, 255);
280 rectangle(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, ref7);
281 Colour red = Colour(130, 0, 0);
282 drawText(tr("Rec: Set timer"), keyx + 292, keyy + Surface::getFontHeight() + 9, red);
284 Colour ref8 = Colour(180, 180, 180, 255);
285 rectangle(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, ref8);
286 w.nextSymbol = WSymbol::PLAY;
287 w.setPosition(keyx + 476, keyy + 5);
289 drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT);
291 Colour ref9 = Colour(180, 180, 180, 255);
292 rectangle(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, ref9);
293 drawText(tr("Go: Preview"), keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
296 // Draw all the dynamic data
300 void VEpg::drawData()
302 // Not doing View::draw() every time causes
303 // things not to be cleared off the surface properly
304 // So, blank out the data area first
307 chanListbox.getRootBoxOffsetX(),
308 chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3,
309 155 + WINDOW_WIDTH * MINUTE_SCALE,
310 chanListbox.getHeight() + Surface::getFontHeight() + 4,
315 chanName.draw(); // TODO this should be dealt with by vvideolive
320 // Set timer to redraw to move the current time bar
324 if (dt == 0) dt = 60;
326 Timers::getInstance()->setTimerT(this, 1, dt);
329 void VEpg::timercall(int clientReference)
332 boxstack->update(this);
335 int VEpg::handleCommand(int command)
341 { // cursor up the channel list
344 boxstack->update(this);
347 case Remote::DF_DOWN:
349 { // cursor down the channel list
350 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
354 boxstack->update(this);
355 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
359 case Remote::DF_LEFT:
361 { // cursor left through time
362 selTime = thisEvent.time - 1;
364 boxstack->update(this);
367 case Remote::DF_RIGHT:
370 // cursor right through time
371 selTime = thisEvent.time + thisEvent.duration;
373 boxstack->update(this);
378 // cursor up one page
379 chanListbox.pageUp();
381 boxstack->update(this);
386 // cursor down one page
387 chanListbox.pageDown();
389 boxstack->update(this);
394 // step forward 24 hours
395 selTime += 24 * 60 * 60;
397 boxstack->update(this);
402 // step forward 24 hours
403 selTime -= 24 * 60 * 60;
405 boxstack->update(this);
410 if (!chanList) return 2;
411 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
412 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
415 boxstack->update(vs);
422 if (!chanList) return 2;
424 // select programme and display menu TODO currently just changes to selected channel
426 currentChannelIndex = chanListbox.getCurrentOption();
430 Message* m = new Message(); // Must be done after this view deleted
433 m->message = Message::CHANNEL_CHANGE;
434 m->parameter = (*chanList)[currentChannelIndex]->number;
435 Command::getInstance()->postMessageNoLock(m);
440 if(command == Remote::GO)
442 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
447 // return to normal TV mode
448 if (parent) // ptr check done in case being tested from videorec
450 Message* m = new Message(); // Must be done after this view deleted
453 m->message = Message::EPG_CLOSE;
454 Command::getInstance()->postMessageNoLock(m);
458 case Remote::CHANNELUP:
460 if (currentChannelIndex == (chanList->size() - 1)) // at the end
461 currentChannelIndex = 0;
463 ++currentChannelIndex;
467 Message* m = new Message(); // Must be done after this view deleted
470 m->message = Message::CHANNEL_CHANGE;
471 m->parameter = (*chanList)[currentChannelIndex]->number;
472 Command::getInstance()->postMessageNoLock(m);
479 case Remote::CHANNELDOWN:
481 if (currentChannelIndex == 0) // at the start
482 currentChannelIndex = chanList->size() - 1; // so go to end
484 --currentChannelIndex;
488 Message* m = new Message(); // Must be done after this view deleted
491 m->message = Message::CHANNEL_CHANGE;
492 m->parameter = (*chanList)[currentChannelIndex]->number;
493 Command::getInstance()->postMessageNoLock(m);
501 // stop command getting to any more views
505 void VEpg::drawgrid() // redraws grid and select programme
507 // draw the grid of programmes
510 time(&t); // set t = now
512 selTime = t; // don't allow cursor in the past
513 if(listTop != chanListbox.getTopOption())
515 // chanListbox has scrolled TODO speed up by changing only rows that have changed
516 listTop = chanListbox.getTopOption();
519 if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))
521 // we have cursored back before left time of window
522 //TODO check that this and above don't happen together
523 ltime = prevHour(&selTime);
527 Colour white = Colour(255, 255, 255, 255);
532 strftime(timeString, 19, "%a %d %b", tms);
533 int timey = chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3;
535 drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date
536 strftime(timeString, 19, "%H:%M", tms);
537 drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time
538 rectangle(155, timey + Surface::getFontHeight(), 2, 7, white);
541 strftime(timeString, 19, "%H:%M", tms);
542 drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time
543 rectangle(335, timey + Surface::getFontHeight(), 2, 7, white);
546 strftime(timeString, 19, "%H:%M", tms);
547 drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time
548 rectangle(515, timey + Surface::getFontHeight(), 2, 7, white);
549 // pointer to selTime
550 //rectangle(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, Colour(255, 50, 50, 255));
554 if ((t >= ltime) && (t < (ltime + 9000)))
556 rectangle(155 + (t - ltime) / 20, timey + Surface::getFontHeight(), 2, ((Surface::getFontHeight() + 2) * 7) + 7 + 2, Colour::RED);
559 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
561 Event noevent; // an event to use if there are gaps in the epg
562 thisEvent.setdescription(tr("There are no programme details available for this period"));
563 thisEvent.duration = WINDOW_WIDTH * 60;
564 thisEvent.time = ltime;
565 thisEvent.settitle(tr("No programme details"));
567 bool swapColour = FALSE; // alternate cell colour
568 bool currentRow = FALSE;
569 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
570 Colour bg, fg; // background colour of cells in grid
571 // for each displayed channel, find programmes that fall in 2.5 hour time window
572 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
574 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
575 continue; // ensure nothing populates grid below last channel
576 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
577 noevent.time = ltime;
578 noevent.duration = WINDOW_WIDTH * 60;
579 noevent.settitle("");
580 paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
583 thisEvent.setdescription(tr("There are no programme details available for this period"));
584 thisEvent.duration = WINDOW_WIDTH * 60;
585 thisEvent.time = ltime;
586 thisEvent.settitle(tr("No programme details"));
589 if (eventLista[listIndex])
591 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
592 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
594 fg = Colour::LIGHTTEXT;
595 event = (*eventLista[listIndex])[e];
598 UINT end = event->time + event->duration; // programme end time
599 if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window
600 continue; // that's enough of this channel's events
601 if(end <= UINT(ltime)) // programme ends before LHS of window
602 continue; // this event is before the window - let's try the next event
603 // this event is one we are interested in
604 bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour
605 swapColour = !swapColour; // it wil be the other colour next time
606 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
608 // this is the selected programme
609 thisEvent.setdescription(event->description);
610 thisEvent.duration = event->duration;
611 thisEvent.time = event->time;
612 thisEvent.settitle(event->title);
613 thisEvent.id = event->id;
614 if(thisEvent.id == 0)
616 bg = Colour::SELECTHIGHLIGHT; // highlight cell
617 fg = Colour::DARKTEXT;
621 if (currentRow && thisEvent.id == 0)
623 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
624 thisEvent.time = end;
625 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
626 thisEvent.duration = event->time - thisEvent.time;
629 paintCell(event, y, bg, fg);
635 // no event list for this channel. Already painted noevent colour so just highlight if selected
638 bg = Colour::SELECTHIGHLIGHT; // highlight cell
639 fg = Colour::DARKTEXT;
640 paintCell(&thisEvent, y, bg, fg);
644 bg = Colour::NOPROGRAMME;
645 fg = Colour::LIGHTTEXT;
646 noevent.settitle(tr("No programme details"));
647 paintCell(&noevent, y, bg, fg);
650 y += Surface::getFontHeight() + 2;
655 void VEpg::updateEventList()
657 if (!chanList) return;
659 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
661 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
663 chan = (*chanList)[listTop + listIndex];
665 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
669 void VEpg::setCurrentChannel()
671 chanName.setText((*chanList)[currentChannelIndex]->name);
674 chanName.getRootBoxRegion(&r);
675 boxstack->update(this, &r);
678 void VEpg::paintCell(Event* event, int yOffset, const Colour& bg, const Colour& fg)
681 w = x = 0; // keep compiler happy
684 h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
685 UINT end = event->time + event->duration; // programme end time
686 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
688 x = 155; // LHS of window
689 if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))
690 w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window
692 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
694 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window
696 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
697 w = MINUTE_SCALE * event->duration / 60;
698 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
699 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
701 if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)
702 w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window
703 rectangle(x, y, w, h, bg);
704 char* tt = new char[strlen(event->title) + 1];
705 strcpy (tt, event->title);
707 for (UINT textPos = 0; textPos < strlen(tt); textPos++)
709 int thisCharWidth = surface->getCharWidth(tt[textPos]);
710 if (textWidth + thisCharWidth > w) // text will not fit in cell
715 textWidth += thisCharWidth;
717 char* tT = new char[textWidth];
720 strncpy(tT, tt, textWidth - 1);
721 tT[textWidth - 1] = '\0';
722 surface->drawText(tT, x+2, y, fg.rgba());
728 time_t VEpg::prevHour(time_t* t)
737 void VEpg::processMessage(Message* m)
739 if (m->message == Message::MOUSE_MOVE)
741 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
744 boxstack->update(this);
747 else if (m->message == Message::MOUSE_LBDOWN)
749 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
751 boxstack->handleCommand(Remote::OK); //simulate OK press
755 //check if press is outside this view! then simulate cancel
756 int x=(m->parameter>>16)-getScreenX();
757 int y=(m->parameter&0xFFFF)-getScreenY();
758 int keyx = chanListbox.getRootBoxOffsetX();
759 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
761 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
763 boxstack->handleCommand(Remote::BACK); //simulate cancel press
765 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
767 boxstack->handleCommand(Remote::RED);
769 else if (x>=(keyx+72) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
771 boxstack->handleCommand(Remote::GREEN);
773 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
775 boxstack->handleCommand(Remote::YELLOW);
777 else if (x>=(keyx+180) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
779 boxstack->handleCommand(Remote::BLUE);
781 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+Surface::getFontHeight() + 2))
783 boxstack->handleCommand(Remote::BACK);
785 else if (x>=(keyx+290) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
787 boxstack->handleCommand(Remote::RECORD);
789 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+Surface::getFontHeight() + 2))
791 boxstack->handleCommand(Remote::PLAY);
793 else if (x>=(keyx+474) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
795 boxstack->handleCommand(Remote::GO);
797 else if ( x>=(chanListbox.getRootBoxOffsetX())
798 && y>=(chanListbox.getRootBoxOffsetY() + 5)
799 // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE)
800 &&y<=(chanListbox.getRootBoxOffsetY() - Surface::getFontHeight()
801 - 3+(int)chanListbox.getHeight() + Surface::getFontHeight() + 3)
804 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
805 int row=cy/(Surface::getFontHeight()+2);
806 int clistTop = chanListbox.getTopOption();
807 chanListbox.hintSetCurrent(clistTop+row);
809 time_t ttime = cx*60/MINUTE_SCALE+ltime;
810 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
814 boxstack->update(this);