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"
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
165 void VEpg::preDelete()
167 Timers::getInstance()->cancelTimer(this, 1);
175 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
177 if (eventLista[listIndex])
179 (eventLista)[listIndex]->clear();
180 delete eventLista[listIndex];
183 // delete [] eventLista; // FIXME
185 // destroy dynamically allocated memory
188 VEpg* VEpg::getInstance()
193 void VEpg::setInfo(Event* event)
196 struct tm* btime; // to hold programme start and end time
197 char timeString[9]; // to hold programme start and end time
198 int length = strlen(event->title); // calculate length of programme title string
199 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
200 btime = localtime((time_t*)&event->time); //get programme start time
202 strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -
204 strftime(timeString, 9, "%H:%M - ", btime); // and format it as hh:mm -
206 strcpy(title, timeString); // put it in our buffer
207 t = event->time + event->duration; //get programme end time
208 btime = localtime(&t);
210 strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -
212 strftime(timeString, 7, "%H:%M ", btime); // and format it as hh:mm -
214 strcat(title, timeString); // put it in our buffer
215 strcat(title, event->title); // then add the programme title
216 progTitle.setText(title); // sput this sring in our text box
217 length = strlen(event->description);
218 char* info = new char[length + 1]; // create programme detail string
219 strcpy(info, event->description);
220 progInfo.setText(info); // show programme detail string
221 // destroy dynamically allocated memory
228 // View::draw(); // draw pallet
230 Colour transparent = Colour(0, 0, 0, 0);
231 fillColour(transparent);
234 // Moved all the dynamic data drawing to a seperate function
236 // Display the status and key stuff at the bottom
238 UINT keyx = chanListbox.getRootBoxOffsetX();
239 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
240 Colour ref1 = Colour(100, 100, 100, 255);
241 rectangle(keyx, keyy, 605, Surface::getFontHeight() * 2 + 14, ref1);
246 w.nextSymbol = WSymbol::LEFTARROW;
247 w.setPosition(keyx + 1, keyy + 20);
250 w.nextSymbol = WSymbol::UP;
251 w.setPosition(keyx + 26, keyy + 3);
254 w.nextSymbol = WSymbol::DOWN;
255 w.setPosition(keyx + 26, keyy + 36);
258 w.nextSymbol = WSymbol::RIGHTARROW;
259 w.setPosition(keyx + 50, keyy + 20);
262 drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT);
264 Colour ref2 = Colour(200, 0, 0, 255);
265 rectangle(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, ref2);
266 drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT);
268 Colour ref3 = Colour(0, 200, 0, 255);
269 rectangle(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref3);
270 drawText(tr("Page down"), keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
272 Colour ref4 = Colour(200, 200, 0, 255);
273 rectangle(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, ref4);
274 drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT);
276 Colour ref5 = Colour(0, 0, 200, 255);
277 rectangle(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref5);
278 drawText(tr("+24 hours"), keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
280 Colour ref6 = Colour(180, 180, 180, 255);
281 rectangle(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, ref6);
282 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT);
284 Colour ref7 = Colour(180, 180, 180, 255);
285 rectangle(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, ref7);
286 Colour red = Colour(130, 0, 0);
287 drawText(tr("Rec: Set timer"), keyx + 292, keyy + Surface::getFontHeight() + 9, red);
289 Colour ref8 = Colour(180, 180, 180, 255);
290 rectangle(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, ref8);
291 w.nextSymbol = WSymbol::PLAY;
292 w.setPosition(keyx + 476, keyy + 5);
294 drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT);
296 Colour ref9 = Colour(180, 180, 180, 255);
297 rectangle(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, ref9);
298 drawText(tr("Go: Preview"), keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
301 // Draw all the dynamic data
305 void VEpg::drawData()
307 // Not doing View::draw() every time causes
308 // things not to be cleared off the surface properly
309 // So, blank out the data area first
312 chanListbox.getRootBoxOffsetX(),
313 chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3,
314 155 + WINDOW_WIDTH * MINUTE_SCALE,
315 chanListbox.getHeight() + Surface::getFontHeight() + 4,
320 chanName.draw(); // TODO this should be dealt with by vvideolive
325 // Set timer to redraw to move the current time bar
329 if (dt == 0) dt = 60;
331 Timers::getInstance()->setTimerT(this, 1, dt);
334 void VEpg::timercall(int clientReference)
337 boxstack->update(this);
340 int VEpg::handleCommand(int command)
346 { // cursor up the channel list
349 boxstack->update(this);
352 case Remote::DF_DOWN:
354 { // cursor down the channel list
355 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
359 boxstack->update(this);
360 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
364 case Remote::DF_LEFT:
366 { // cursor left through time
367 selTime = thisEvent.time - 1;
369 boxstack->update(this);
372 case Remote::DF_RIGHT:
375 // cursor right through time
376 selTime = thisEvent.time + thisEvent.duration;
378 boxstack->update(this);
383 // cursor up one page
384 chanListbox.pageUp();
386 boxstack->update(this);
391 // cursor down one page
392 chanListbox.pageDown();
394 boxstack->update(this);
399 // step forward 24 hours
400 selTime += 24 * 60 * 60;
402 boxstack->update(this);
407 // step forward 24 hours
408 selTime -= 24 * 60 * 60;
410 boxstack->update(this);
415 if (!chanList) return 2;
416 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
417 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
420 boxstack->update(vs);
427 if (!chanList) return 2;
429 // select programme and display menu TODO currently just changes to selected channel
431 currentChannelIndex = chanListbox.getCurrentOption();
435 Message* m = new Message(); // Must be done after this view deleted
438 m->message = Message::CHANNEL_CHANGE;
439 m->parameter = (*chanList)[currentChannelIndex]->number;
440 Command::getInstance()->postMessageNoLock(m);
445 if(command == Remote::GO)
447 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
452 // return to normal TV mode
453 if (parent) // ptr check done in case being tested from videorec
455 Message* m = new Message(); // Must be done after this view deleted
458 m->message = Message::EPG_CLOSE;
459 Command::getInstance()->postMessageNoLock(m);
463 case Remote::CHANNELUP:
465 if (currentChannelIndex == (chanList->size() - 1)) // at the end
466 currentChannelIndex = 0;
468 ++currentChannelIndex;
472 Message* m = new Message(); // Must be done after this view deleted
475 m->message = Message::CHANNEL_CHANGE;
476 m->parameter = (*chanList)[currentChannelIndex]->number;
477 Command::getInstance()->postMessageNoLock(m);
484 case Remote::CHANNELDOWN:
486 if (currentChannelIndex == 0) // at the start
487 currentChannelIndex = chanList->size() - 1; // so go to end
489 --currentChannelIndex;
493 Message* m = new Message(); // Must be done after this view deleted
496 m->message = Message::CHANNEL_CHANGE;
497 m->parameter = (*chanList)[currentChannelIndex]->number;
498 Command::getInstance()->postMessageNoLock(m);
506 // stop command getting to any more views
510 void VEpg::drawgrid() // redraws grid and select programme
512 // draw the grid of programmes
515 time(&t); // set t = now
517 selTime = t; // don't allow cursor in the past
518 if(listTop != chanListbox.getTopOption())
520 // chanListbox has scrolled TODO speed up by changing only rows that have changed
521 listTop = chanListbox.getTopOption();
524 if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))
526 // we have cursored back before left time of window
527 //TODO check that this and above don't happen together
528 ltime = prevHour(&selTime);
532 Colour white = Colour(255, 255, 255, 255);
537 strftime(timeString, 19, "%a %d %b", tms);
538 int timey = chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3;
540 drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date
541 strftime(timeString, 19, "%H:%M", tms);
542 drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time
543 rectangle(155, timey + Surface::getFontHeight(), 2, 7, white);
546 strftime(timeString, 19, "%H:%M", tms);
547 drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time
548 rectangle(335, timey + Surface::getFontHeight(), 2, 7, white);
551 strftime(timeString, 19, "%H:%M", tms);
552 drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time
553 rectangle(515, timey + Surface::getFontHeight(), 2, 7, white);
554 // pointer to selTime
555 //rectangle(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, Colour(255, 50, 50, 255));
559 if ((t >= ltime) && (t < (ltime + 9000)))
561 rectangle(155 + (t - ltime) / 20, timey + Surface::getFontHeight(), 2, ((Surface::getFontHeight() + 2) * 7) + 7 + 2, Colour::RED);
564 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
566 Event noevent; // an event to use if there are gaps in the epg
567 thisEvent.setdescription(tr("There are no programme details available for this period"));
568 thisEvent.duration = WINDOW_WIDTH * 60;
569 thisEvent.time = ltime;
570 thisEvent.settitle(tr("No programme details"));
572 bool swapColour = FALSE; // alternate cell colour
573 bool currentRow = FALSE;
574 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
575 Colour bg, fg; // background colour of cells in grid
576 // for each displayed channel, find programmes that fall in 2.5 hour time window
577 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
579 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
580 continue; // ensure nothing populates grid below last channel
581 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
582 noevent.time = ltime;
583 noevent.duration = WINDOW_WIDTH * 60;
584 noevent.settitle("");
585 paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
588 thisEvent.setdescription(tr("There are no programme details available for this period"));
589 thisEvent.duration = WINDOW_WIDTH * 60;
590 thisEvent.time = ltime;
591 thisEvent.settitle(tr("No programme details"));
594 if (eventLista[listIndex])
596 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
597 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
599 fg = Colour::LIGHTTEXT;
600 event = (*eventLista[listIndex])[e];
603 UINT end = event->time + event->duration; // programme end time
604 if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window
605 continue; // that's enough of this channel's events
606 if(end <= UINT(ltime)) // programme ends before LHS of window
607 continue; // this event is before the window - let's try the next event
608 // this event is one we are interested in
609 bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour
610 swapColour = !swapColour; // it wil be the other colour next time
611 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
613 // this is the selected programme
614 thisEvent.setdescription(event->description);
615 thisEvent.duration = event->duration;
616 thisEvent.time = event->time;
617 thisEvent.settitle(event->title);
618 thisEvent.id = event->id;
619 if(thisEvent.id == 0)
621 bg = Colour::SELECTHIGHLIGHT; // highlight cell
622 fg = Colour::DARKTEXT;
626 if (currentRow && thisEvent.id == 0)
628 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
629 thisEvent.time = end;
630 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
631 thisEvent.duration = event->time - thisEvent.time;
634 paintCell(event, y, bg, fg);
640 // no event list for this channel. Already painted noevent colour so just highlight if selected
643 bg = Colour::SELECTHIGHLIGHT; // highlight cell
644 fg = Colour::DARKTEXT;
645 paintCell(&thisEvent, y, bg, fg);
649 bg = Colour::NOPROGRAMME;
650 fg = Colour::LIGHTTEXT;
651 noevent.settitle(tr("No programme details"));
652 paintCell(&noevent, y, bg, fg);
655 y += Surface::getFontHeight() + 2;
660 void VEpg::updateEventList()
662 if (!chanList) return;
664 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
666 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
668 chan = (*chanList)[listTop + listIndex];
670 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
674 void VEpg::setCurrentChannel()
676 chanName.setText((*chanList)[currentChannelIndex]->name);
679 chanName.getRootBoxRegion(&r);
680 boxstack->update(this, &r);
683 void VEpg::paintCell(Event* event, int yOffset, const Colour& bg, const Colour& fg)
686 w = x = 0; // keep compiler happy
689 h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
690 UINT end = event->time + event->duration; // programme end time
691 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
693 x = 155; // LHS of window
694 if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))
695 w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window
697 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
699 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window
701 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
702 w = MINUTE_SCALE * event->duration / 60;
703 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
704 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
706 if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)
707 w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window
708 rectangle(x, y, w, h, bg);
709 char* tt = new char[strlen(event->title) + 1];
710 strcpy (tt, event->title);
712 for (UINT textPos = 0; textPos < strlen(tt); textPos++)
714 int thisCharWidth = surface->getCharWidth(tt[textPos]);
715 if (textWidth + thisCharWidth > w) // text will not fit in cell
720 textWidth += thisCharWidth;
722 char* tT = new char[textWidth];
725 strncpy(tT, tt, textWidth - 1);
726 tT[textWidth - 1] = '\0';
727 surface->drawText(tT, x+2, y, fg.rgba());
733 time_t VEpg::prevHour(time_t* t)
742 void VEpg::processMessage(Message* m)
744 if (m->message == Message::MOUSE_MOVE)
746 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
749 boxstack->update(this);
752 else if (m->message == Message::MOUSE_LBDOWN)
754 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
756 boxstack->handleCommand(Remote::OK); //simulate OK press
760 //check if press is outside this view! then simulate cancel
761 int x=(m->parameter>>16)-getScreenX();
762 int y=(m->parameter&0xFFFF)-getScreenY();
763 int keyx = chanListbox.getRootBoxOffsetX();
764 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
766 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
768 boxstack->handleCommand(Remote::BACK); //simulate cancel press
770 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
772 boxstack->handleCommand(Remote::RED);
774 else if (x>=(keyx+72) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
776 boxstack->handleCommand(Remote::GREEN);
778 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
780 boxstack->handleCommand(Remote::YELLOW);
782 else if (x>=(keyx+180) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
784 boxstack->handleCommand(Remote::BLUE);
786 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+Surface::getFontHeight() + 2))
788 boxstack->handleCommand(Remote::BACK);
790 else if (x>=(keyx+290) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
792 boxstack->handleCommand(Remote::RECORD);
794 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+Surface::getFontHeight() + 2))
796 boxstack->handleCommand(Remote::PLAY);
798 else if (x>=(keyx+474) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
800 boxstack->handleCommand(Remote::GO);
802 else if ( x>=(chanListbox.getRootBoxOffsetX())
803 && y>=(chanListbox.getRootBoxOffsetY() + 5)
804 // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE)
805 &&y<=(chanListbox.getRootBoxOffsetY() - Surface::getFontHeight()
806 - 3+(int)chanListbox.getHeight() + Surface::getFontHeight() + 3)
809 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
810 int row=cy/(Surface::getFontHeight()+2);
811 int clistTop = chanListbox.getTopOption();
812 chanListbox.hintSetCurrent(clistTop+row);
814 time_t ttime = cx*60/MINUTE_SCALE+ltime;
815 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
819 boxstack->update(this);