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 // DrawStyle transparent = DrawStyle(0, 0, 0, 0);
110 // setBackgroundColour(transparent);
112 // progTitle.setSurface(surface);
113 progTitle.setPosition(0,0);
114 progTitle.setSize(300,(getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels
115 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
116 progTitle.setTextPos(5, 16);
120 // progInfo.setSurface(surface);
121 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
122 progInfo.setPosition(0, progTitle.getY2());
123 progInfo.setSize(300,((getFontHeight() + 4) * summaryLines) + summaryLowerPadding);
127 // chanName.setSurface(surface);
128 chanName.setSize(510, (getFontHeight() + 4));
129 chanName.setPosition(305, chanNameYpos);
130 DrawStyle 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() + getFontHeight() + 8); // position channel list
137 chanListbox.setSize(150, ((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 DrawStyle transparent = DrawStyle(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 DrawStyle ref1 = DrawStyle(100, 100, 100, 255); //to do global definition for skin
241 rectangle(keyx, keyy, 605, 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, DrawStyle::LIGHTTEXT);
264 DrawStyle ref2 = DrawStyle(200, 0, 0, 255);
265 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, ref2);
266 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
268 DrawStyle ref3 = DrawStyle(0, 200, 0, 255);
269 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref3);
270 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
272 DrawStyle ref4 = DrawStyle(200, 200, 0, 255);
273 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, ref4);
274 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
276 DrawStyle ref5 = DrawStyle(0, 0, 200, 255);
277 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref5);
278 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
280 DrawStyle ref6 = DrawStyle(180, 180, 180, 255);
281 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, ref6);
282 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
284 DrawStyle ref7 = DrawStyle(180, 180, 180, 255);
285 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, ref7);
286 DrawStyle red = DrawStyle(130, 0, 0);
287 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
289 DrawStyle ref8 = DrawStyle(180, 180, 180, 255);
290 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, ref8);
291 w.nextSymbol = WSymbol::PLAY;
292 w.setPosition(keyx + 476, keyy + 5);
294 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
296 DrawStyle ref9 = DrawStyle(180, 180, 180, 255);
297 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, ref9);
298 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::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() - getFontHeight() - 3,
314 155 + WINDOW_WIDTH * MINUTE_SCALE,
315 chanListbox.getHeight() + 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 DrawStyle white = DrawStyle(255, 255, 255, 255);
537 strftime(timeString, 19, "%a %d %b", tms);
538 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
540 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
541 strftime(timeString, 19, "%H:%M", tms);
542 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
544 rectangle(155, timey + getFontHeight(), 2, 7, white);
547 strftime(timeString, 19, "%H:%M", tms);
548 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
549 rectangle(335, timey + getFontHeight(), 2, 7, white);
552 strftime(timeString, 19, "%H:%M", tms);
553 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
554 rectangle(515, timey + getFontHeight(), 2, 7, white);
555 // pointer to selTime
556 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
560 if ((t >= ltime) && (t < (ltime + 9000)))
562 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * 7) + 7 + 2, DrawStyle::RED);
565 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
567 Event noevent; // an event to use if there are gaps in the epg
568 thisEvent.setdescription(tr("There are no programme details available for this period"));
569 thisEvent.duration = WINDOW_WIDTH * 60;
570 thisEvent.time = ltime;
571 thisEvent.settitle(tr("No programme details"));
573 bool swapColour = false; // alternate cell colour
574 bool currentRow = false;
575 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
576 DrawStyle bg, fg; // background colour of cells in grid
577 // for each displayed channel, find programmes that fall in 2.5 hour time window
578 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
580 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
581 continue; // ensure nothing populates grid below last channel
582 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
583 noevent.time = ltime;
584 noevent.duration = WINDOW_WIDTH * 60;
585 noevent.settitle("");
586 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
589 thisEvent.setdescription(tr("There are no programme details available for this period"));
590 thisEvent.duration = WINDOW_WIDTH * 60;
591 thisEvent.time = ltime;
592 thisEvent.settitle(tr("No programme details"));
595 if (eventLista[listIndex])
597 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
598 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
600 fg = DrawStyle::LIGHTTEXT;
601 event = (*eventLista[listIndex])[e];
604 UINT end = event->time + event->duration; // programme end time
605 if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window
606 continue; // that's enough of this channel's events
607 if(end <= UINT(ltime)) // programme ends before LHS of window
608 continue; // this event is before the window - let's try the next event
609 // this event is one we are interested in
610 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
611 swapColour = !swapColour; // it wil be the other colour next time
612 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
614 // this is the selected programme
615 thisEvent.setdescription(event->description);
616 thisEvent.duration = event->duration;
617 thisEvent.time = event->time;
618 thisEvent.settitle(event->title);
619 thisEvent.id = event->id;
620 if(thisEvent.id == 0)
622 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
623 fg = DrawStyle::DARKTEXT;
627 if (currentRow && thisEvent.id == 0)
629 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
630 thisEvent.time = end;
631 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
632 thisEvent.duration = event->time - thisEvent.time;
635 paintCell(event, y, bg, fg);
641 // no event list for this channel. Already painted noevent colour so just highlight if selected
644 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
645 fg = DrawStyle::DARKTEXT;
646 paintCell(&thisEvent, y, bg, fg);
650 bg = DrawStyle::NOPROGRAMME;
651 fg = DrawStyle::LIGHTTEXT;
652 noevent.settitle(tr("No programme details"));
653 paintCell(&noevent, y, bg, fg);
656 y += getFontHeight() + 2;
661 void VEpg::updateEventList()
663 if (!chanList) return;
665 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
667 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
669 chan = (*chanList)[listTop + listIndex];
671 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
675 void VEpg::setCurrentChannel()
677 chanName.setText((*chanList)[currentChannelIndex]->name);
680 chanName.getRootBoxRegion(&r);
681 boxstack->update(this, &r);
684 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
687 w = x = 0; // keep compiler happy
690 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
691 UINT end = event->time + event->duration; // programme end time
692 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
694 x = 155; // LHS of window
695 if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))
696 w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window
698 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
700 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window
702 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
703 w = MINUTE_SCALE * event->duration / 60;
704 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
705 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
707 if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)
708 w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window
709 rectangle(x, y, w, h, bg);
710 char* tt = new char[strlen(event->title) + 1];
711 strcpy (tt, event->title);
713 unsigned int cur_length=1;
714 unsigned int text_max=strlen(tt);
716 for (textPos = 0; textPos <text_max; textPos+=cur_length)
718 wchar_t cur_char=getWChar(tt+textPos,&cur_length);
719 float thisCharWidth = charWidth(cur_char);
720 if (textWidth + thisCharWidth > w) // text will not fit in cell
724 textWidth += thisCharWidth;
726 char* tT = new char[textPos+1];
729 strncpy(tT, tt, textPos );
731 surface->drawText(tT, x+2, y, fg);
737 time_t VEpg::prevHour(time_t* t)
746 void VEpg::processMessage(Message* m)
748 if (m->message == Message::MOUSE_MOVE)
750 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
753 boxstack->update(this);
756 else if (m->message == Message::MOUSE_LBDOWN)
758 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
760 boxstack->handleCommand(Remote::OK); //simulate OK press
764 //check if press is outside this view! then simulate cancel
765 int x=(m->parameter>>16)-getScreenX();
766 int y=(m->parameter&0xFFFF)-getScreenY();
767 int keyx = chanListbox.getRootBoxOffsetX();
768 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
770 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
772 boxstack->handleCommand(Remote::BACK); //simulate cancel press
774 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
776 boxstack->handleCommand(Remote::RED);
778 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
780 boxstack->handleCommand(Remote::GREEN);
782 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
784 boxstack->handleCommand(Remote::YELLOW);
786 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
788 boxstack->handleCommand(Remote::BLUE);
790 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
792 boxstack->handleCommand(Remote::BACK);
794 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
796 boxstack->handleCommand(Remote::RECORD);
798 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
800 boxstack->handleCommand(Remote::PLAY);
802 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
804 boxstack->handleCommand(Remote::GO);
806 else if ( x>=(chanListbox.getRootBoxOffsetX())
807 && y>=(chanListbox.getRootBoxOffsetY() + 5)
808 // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE)
809 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
810 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
813 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
814 int row=cy/(getFontHeight()+2);
815 int clistTop = chanListbox.getTopOption();
816 chanListbox.hintSetCurrent(clistTop+row);
818 time_t ttime = cx*60/MINUTE_SCALE+ltime;
819 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
823 boxstack->update(this);