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
164 void VEpg::preDelete()
166 Timers::getInstance()->cancelTimer(this, 1);
174 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
176 if (eventLista[listIndex])
178 (eventLista)[listIndex]->clear();
179 delete eventLista[listIndex];
182 // delete [] eventLista; // FIXME
184 // destroy dynamically allocated memory
187 VEpg* VEpg::getInstance()
192 void VEpg::setInfo(Event* event)
195 struct tm* btime; // to hold programme start and end time
196 char timeString[9]; // to hold programme start and end time
197 int length = strlen(event->title); // calculate length of programme title string
198 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
199 btime = localtime((time_t*)&event->time); //get programme start time
201 strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -
203 strftime(timeString, 9, "%H:%M - ", btime); // and format it as hh:mm -
205 strcpy(title, timeString); // put it in our buffer
206 t = event->time + event->duration; //get programme end time
207 btime = localtime(&t);
209 strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -
211 strftime(timeString, 7, "%H:%M ", btime); // and format it as hh:mm -
213 strcat(title, timeString); // put it in our buffer
214 strcat(title, event->title); // then add the programme title
215 progTitle.setText(title); // sput this sring in our text box
216 length = strlen(event->description);
217 char* info = new char[length + 1]; // create programme detail string
218 strcpy(info, event->description);
219 progInfo.setText(info); // show programme detail string
220 // destroy dynamically allocated memory
227 // View::draw(); // draw pallet
229 Colour transparent = Colour(0, 0, 0, 0);
230 fillColour(transparent);
233 // Moved all the dynamic data drawing to a seperate function
235 // Display the status and key stuff at the bottom
237 UINT keyx = chanListbox.getRootBoxOffsetX();
238 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
239 Colour ref1 = Colour(100, 100, 100, 255);
240 rectangle(keyx, keyy, 605, Surface::getFontHeight() * 2 + 14, ref1);
245 w.nextSymbol = WSymbol::LEFTARROW;
246 w.setPosition(keyx + 1, keyy + 20);
249 w.nextSymbol = WSymbol::UP;
250 w.setPosition(keyx + 26, keyy + 3);
253 w.nextSymbol = WSymbol::DOWN;
254 w.setPosition(keyx + 26, keyy + 36);
257 w.nextSymbol = WSymbol::RIGHTARROW;
258 w.setPosition(keyx + 50, keyy + 20);
261 drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT);
263 Colour ref2 = Colour(200, 0, 0, 255);
264 rectangle(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, ref2);
265 drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT);
267 Colour ref3 = Colour(0, 200, 0, 255);
268 rectangle(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref3);
269 drawText(tr("Page down"), keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
271 Colour ref4 = Colour(200, 200, 0, 255);
272 rectangle(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, ref4);
273 drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT);
275 Colour ref5 = Colour(0, 0, 200, 255);
276 rectangle(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref5);
277 drawText(tr("+24 hours"), keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
279 Colour ref6 = Colour(180, 180, 180, 255);
280 rectangle(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, ref6);
281 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT);
283 Colour ref7 = Colour(180, 180, 180, 255);
284 rectangle(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, ref7);
285 Colour red = Colour(130, 0, 0);
286 drawText(tr("Rec: Set timer"), keyx + 292, keyy + Surface::getFontHeight() + 9, red);
288 Colour ref8 = Colour(180, 180, 180, 255);
289 rectangle(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, ref8);
290 w.nextSymbol = WSymbol::PLAY;
291 w.setPosition(keyx + 476, keyy + 5);
293 drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT);
295 Colour ref9 = Colour(180, 180, 180, 255);
296 rectangle(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, ref9);
297 drawText(tr("Go: Preview"), keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
300 // Draw all the dynamic data
304 void VEpg::drawData()
306 // Not doing View::draw() every time causes
307 // things not to be cleared off the surface properly
308 // So, blank out the data area first
311 chanListbox.getRootBoxOffsetX(),
312 chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3,
313 155 + WINDOW_WIDTH * MINUTE_SCALE,
314 chanListbox.getHeight() + Surface::getFontHeight() + 4,
319 chanName.draw(); // TODO this should be dealt with by vvideolive
324 // Set timer to redraw to move the current time bar
328 if (dt == 0) dt = 60;
330 Timers::getInstance()->setTimerT(this, 1, dt);
333 void VEpg::timercall(int clientReference)
336 boxstack->update(this);
339 int VEpg::handleCommand(int command)
345 { // cursor up the channel list
348 boxstack->update(this);
351 case Remote::DF_DOWN:
353 { // cursor down the channel list
354 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
358 boxstack->update(this);
359 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
363 case Remote::DF_LEFT:
365 { // cursor left through time
366 selTime = thisEvent.time - 1;
368 boxstack->update(this);
371 case Remote::DF_RIGHT:
374 // cursor right through time
375 selTime = thisEvent.time + thisEvent.duration;
377 boxstack->update(this);
382 // cursor up one page
383 chanListbox.pageUp();
385 boxstack->update(this);
390 // cursor down one page
391 chanListbox.pageDown();
393 boxstack->update(this);
398 // step forward 24 hours
399 selTime += 24 * 60 * 60;
401 boxstack->update(this);
406 // step forward 24 hours
407 selTime -= 24 * 60 * 60;
409 boxstack->update(this);
414 if (!chanList) return 2;
415 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
416 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
419 boxstack->update(vs);
426 if (!chanList) return 2;
428 // select programme and display menu TODO currently just changes to selected channel
430 currentChannelIndex = chanListbox.getCurrentOption();
434 Message* m = new Message(); // Must be done after this view deleted
437 m->message = Message::CHANNEL_CHANGE;
438 m->parameter = (*chanList)[currentChannelIndex]->number;
439 Command::getInstance()->postMessageNoLock(m);
444 if(command == Remote::GO)
446 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
451 // return to normal TV mode
452 if (parent) // ptr check done in case being tested from videorec
454 Message* m = new Message(); // Must be done after this view deleted
457 m->message = Message::EPG_CLOSE;
458 Command::getInstance()->postMessageNoLock(m);
462 case Remote::CHANNELUP:
464 if (currentChannelIndex == (chanList->size() - 1)) // at the end
465 currentChannelIndex = 0;
467 ++currentChannelIndex;
471 Message* m = new Message(); // Must be done after this view deleted
474 m->message = Message::CHANNEL_CHANGE;
475 m->parameter = (*chanList)[currentChannelIndex]->number;
476 Command::getInstance()->postMessageNoLock(m);
483 case Remote::CHANNELDOWN:
485 if (currentChannelIndex == 0) // at the start
486 currentChannelIndex = chanList->size() - 1; // so go to end
488 --currentChannelIndex;
492 Message* m = new Message(); // Must be done after this view deleted
495 m->message = Message::CHANNEL_CHANGE;
496 m->parameter = (*chanList)[currentChannelIndex]->number;
497 Command::getInstance()->postMessageNoLock(m);
505 // stop command getting to any more views
509 void VEpg::drawgrid() // redraws grid and select programme
511 // draw the grid of programmes
514 time(&t); // set t = now
516 selTime = t; // don't allow cursor in the past
517 if(listTop != chanListbox.getTopOption())
519 // chanListbox has scrolled TODO speed up by changing only rows that have changed
520 listTop = chanListbox.getTopOption();
523 if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))
525 // we have cursored back before left time of window
526 //TODO check that this and above don't happen together
527 ltime = prevHour(&selTime);
531 Colour white = Colour(255, 255, 255, 255);
536 strftime(timeString, 19, "%a %d %b", tms);
537 int timey = chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3;
539 drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date
540 strftime(timeString, 19, "%H:%M", tms);
541 drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time
542 rectangle(155, timey + Surface::getFontHeight(), 2, 7, white);
545 strftime(timeString, 19, "%H:%M", tms);
546 drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time
547 rectangle(335, timey + Surface::getFontHeight(), 2, 7, white);
550 strftime(timeString, 19, "%H:%M", tms);
551 drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time
552 rectangle(515, timey + Surface::getFontHeight(), 2, 7, white);
553 // pointer to selTime
554 //rectangle(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, Colour(255, 50, 50, 255));
558 if ((t >= ltime) && (t < (ltime + 9000)))
560 rectangle(155 + (t - ltime) / 20, timey + Surface::getFontHeight(), 2, ((Surface::getFontHeight() + 2) * 7) + 7 + 2, Colour::RED);
563 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
565 Event noevent; // an event to use if there are gaps in the epg
566 thisEvent.setdescription(tr("There are no programme details available for this period"));
567 thisEvent.duration = WINDOW_WIDTH * 60;
568 thisEvent.time = ltime;
569 thisEvent.settitle(tr("No programme details"));
571 bool swapColour = FALSE; // alternate cell colour
572 bool currentRow = FALSE;
573 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
574 Colour bg, fg; // background colour of cells in grid
575 // for each displayed channel, find programmes that fall in 2.5 hour time window
576 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
578 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
579 continue; // ensure nothing populates grid below last channel
580 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
581 noevent.time = ltime;
582 noevent.duration = WINDOW_WIDTH * 60;
583 noevent.settitle("");
584 paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
587 thisEvent.setdescription(tr("There are no programme details available for this period"));
588 thisEvent.duration = WINDOW_WIDTH * 60;
589 thisEvent.time = ltime;
590 thisEvent.settitle(tr("No programme details"));
593 if (eventLista[listIndex])
595 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
596 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
598 fg = Colour::LIGHTTEXT;
599 event = (*eventLista[listIndex])[e];
602 UINT end = event->time + event->duration; // programme end time
603 if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window
604 continue; // that's enough of this channel's events
605 if(end <= UINT(ltime)) // programme ends before LHS of window
606 continue; // this event is before the window - let's try the next event
607 // this event is one we are interested in
608 bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour
609 swapColour = !swapColour; // it wil be the other colour next time
610 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
612 // this is the selected programme
613 thisEvent.setdescription(event->description);
614 thisEvent.duration = event->duration;
615 thisEvent.time = event->time;
616 thisEvent.settitle(event->title);
617 thisEvent.id = event->id;
618 if(thisEvent.id == 0)
620 bg = Colour::SELECTHIGHLIGHT; // highlight cell
621 fg = Colour::DARKTEXT;
625 if (currentRow && thisEvent.id == 0)
627 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
628 thisEvent.time = end;
629 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
630 thisEvent.duration = event->time - thisEvent.time;
633 paintCell(event, y, bg, fg);
639 // no event list for this channel. Already painted noevent colour so just highlight if selected
642 bg = Colour::SELECTHIGHLIGHT; // highlight cell
643 fg = Colour::DARKTEXT;
644 paintCell(&thisEvent, y, bg, fg);
648 bg = Colour::NOPROGRAMME;
649 fg = Colour::LIGHTTEXT;
650 noevent.settitle(tr("No programme details"));
651 paintCell(&noevent, y, bg, fg);
654 y += Surface::getFontHeight() + 2;
659 void VEpg::updateEventList()
661 if (!chanList) return;
663 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
665 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
667 chan = (*chanList)[listTop + listIndex];
669 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
673 void VEpg::setCurrentChannel()
675 chanName.setText((*chanList)[currentChannelIndex]->name);
678 chanName.getRootBoxRegion(&r);
679 boxstack->update(this, &r);
682 void VEpg::paintCell(Event* event, int yOffset, const Colour& bg, const Colour& fg)
685 w = x = 0; // keep compiler happy
688 h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
689 UINT end = event->time + event->duration; // programme end time
690 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
692 x = 155; // LHS of window
693 if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))
694 w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window
696 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
698 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window
700 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
701 w = MINUTE_SCALE * event->duration / 60;
702 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
703 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
705 if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)
706 w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window
707 rectangle(x, y, w, h, bg);
708 char* tt = new char[strlen(event->title) + 1];
709 strcpy (tt, event->title);
711 for (UINT textPos = 0; textPos < strlen(tt); textPos++)
713 int thisCharWidth = surface->getCharWidth(tt[textPos]);
714 if (textWidth + thisCharWidth > w) // text will not fit in cell
719 textWidth += thisCharWidth;
721 char* tT = new char[textWidth];
724 strncpy(tT, tt, textWidth - 1);
725 tT[textWidth - 1] = '\0';
726 surface->drawText(tT, x+2, y, fg.rgba());
732 time_t VEpg::prevHour(time_t* t)
741 void VEpg::processMessage(Message* m)
743 if (m->message == Message::MOUSE_MOVE)
745 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
748 boxstack->update(this);
751 else if (m->message == Message::MOUSE_LBDOWN)
753 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
755 boxstack->handleCommand(Remote::OK); //simulate OK press
759 //check if press is outside this view! then simulate cancel
760 int x=(m->parameter>>16)-getScreenX();
761 int y=(m->parameter&0xFFFF)-getScreenY();
762 int keyx = chanListbox.getRootBoxOffsetX();
763 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
765 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
767 boxstack->handleCommand(Remote::BACK); //simulate cancel press
769 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
771 boxstack->handleCommand(Remote::RED);
773 else if (x>=(keyx+72) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
775 boxstack->handleCommand(Remote::GREEN);
777 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
779 boxstack->handleCommand(Remote::YELLOW);
781 else if (x>=(keyx+180) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
783 boxstack->handleCommand(Remote::BLUE);
785 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+Surface::getFontHeight() + 2))
787 boxstack->handleCommand(Remote::BACK);
789 else if (x>=(keyx+290) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
791 boxstack->handleCommand(Remote::RECORD);
793 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+Surface::getFontHeight() + 2))
795 boxstack->handleCommand(Remote::PLAY);
797 else if (x>=(keyx+474) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
799 boxstack->handleCommand(Remote::GO);
801 else if ( x>=(chanListbox.getRootBoxOffsetX())
802 && y>=(chanListbox.getRootBoxOffsetY() + 5)
803 // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE)
804 &&y<=(chanListbox.getRootBoxOffsetY() - Surface::getFontHeight()
805 - 3+(int)chanListbox.getHeight() + Surface::getFontHeight() + 3)
808 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
809 int row=cy/(Surface::getFontHeight()+2);
810 int clistTop = chanListbox.getTopOption();
811 chanListbox.hintSetCurrent(clistTop+row);
813 time_t ttime = cx*60/MINUTE_SCALE+ltime;
814 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
818 boxstack->update(this);