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"
51 VEpg* VEpg::instance = NULL;
53 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ChannelList* tchanList)
56 currentChannelIndex = tcurrentChannelIndex;
58 // PAL / NTSC sizes -----------------------
61 //int summaryLines, summaryLowerPadding;
63 int fontHeight=getFontHeight() + 4;
64 //UINT gridRows; // is a class member
67 if (Video::getInstance()->getFormat() == Video::PAL)
84 //summaryLowerPadding = 28;
88 int screenwidthhalf=Video::getInstance()->getScreenWidth()/2;
89 int screenheighthalf=Video::getInstance()->getScreenHeight()/2;
90 // summaryLines = ((float)screenheighthalf)/((float)fontHeight))-1;
91 // summaryLowerPadding = screenheighthalf-summaryLines*(fontHeight);
92 gridRows = (screenheighthalf-fontHeight*3-50)/fontHeight;
95 // initialise variables and pointers
96 boxstack = BoxStack::getInstance();
102 eventLista.resize(gridRows);
103 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
105 // initialise array of pointers to eventlist structures
106 eventLista[listIndex] = NULL;
109 // Create pallet on which to paint our epg view and position it in centre of screen.
110 // Need to reduce size to deal with overscanning TVs.
112 setSize(Video::getInstance()->getScreenWidth(), Video::getInstance()->getScreenHeight());
117 // DrawStyle transparent = DrawStyle(0, 0, 0, 0);
118 // setBackgroundColour(transparent);
120 // progTitle.setSurface(surface);
121 progTitle.setPosition(0,0);
122 progTitle.setSize(screenwidthhalf,(fontHeight) * 1 + ypos); //paragraph line seperation is 4 pixels
123 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
124 progTitle.setTextPos(xpos, ypos);
126 progTitle.setParaMode(false);
129 // progInfo.setSurface(surface);
130 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
131 progInfo.setPosition(0, progTitle.getY2());
132 progInfo.setSize(screenwidthhalf,screenheighthalf-progTitle.getY2()+10);
133 progInfo.setTextPos(xpos, 0);
137 // chanName.setSurface(surface);
138 chanName.setSize(510, (fontHeight));
139 chanName.setPosition(screenwidthhalf, screenheighthalf - 2*fontHeight);
140 DrawStyle t1(0, 0, 0, 90);
141 chanName.setBackgroundColour(t1);
142 chanName.setParaMode(false);
145 // create area to display list of channels
146 // chanListbox.setSurface(surface); // add channel list
147 chanListbox.setPosition(0, progInfo.getY2() + fontHeight + 4); // position channel list
148 chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
149 chanListbox.setGap(2);
150 chanListbox.addColumn(xpos);
153 // populate channel list
158 for (UINT i = 0; i < chanList->size(); i++)
160 chan = (*chanList)[i];
161 if (i == currentChannelIndex)
163 chan->index = chanListbox.addOption(chan->name, 0, first);
166 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
169 window_x= chanListbox.getRootBoxOffsetX() + chanListbox.getWidth() + 5;
170 window_width=(Video::getInstance()->getScreenWidth() - window_x +3)/3;
172 listTop = chanListbox.getTopOption();
173 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
174 time(<ime); // set ltime to now
175 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
176 time(&selTime); // set selTime to now
177 updateEventList(); // get list of programmes
179 vdisplay.mode=Window;
180 vdisplay.fallbackMode=Quarter;
181 vdisplay.x=Video::getInstance()->getScreenWidth()/2;
183 vdisplay.width=Video::getInstance()->getScreenWidth()/2;
184 vdisplay.height=Video::getInstance()->getScreenHeight()/2;
187 void VEpg::preDelete()
189 Timers::getInstance()->cancelTimer(this, 1);
197 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
199 if (eventLista[listIndex])
201 (eventLista)[listIndex]->clear();
202 delete eventLista[listIndex];
205 // delete [] eventLista; // FIXME
207 // destroy dynamically allocated memory
210 VEpg* VEpg::getInstance()
215 void VEpg::setInfo(Event* event)
218 struct tm btime; // to hold programme start and end time
219 char timeString[9]; // to hold programme start and end time
220 int length = strlen(event->title); // calculate length of programme title string
221 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
222 time_t eventtime = event->time;
223 localtime_r((time_t*)&eventtime, &btime); //get programme start time
225 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
227 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
229 strcpy(title, timeString); // put it in our buffer
230 t = event->time + event->duration; //get programme end time
231 localtime_r(&t, &btime);
233 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
235 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
237 strcat(title, timeString); // put it in our buffer
239 strcat(title, event->title); // then add the programme title
240 progTitle.setText(title); // sput this sring in our text box
241 length = strlen(event->description);
242 char* info = new char[length + 1]; // create programme detail string
243 strcpy(info, event->description);
244 progInfo.setText(info); // show programme detail string
245 // destroy dynamically allocated memory
252 // View::draw(); // draw pallet
254 DrawStyle transparent = DrawStyle(0, 0, 0, 0);
255 fillColour(transparent);
258 // Moved all the dynamic data drawing to a seperate function
260 // Display the status and key stuff at the bottom
262 UINT keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
263 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
264 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
265 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
270 w.nextSymbol = WSymbol::LEFTARROW;
271 w.setPosition(keyx + 1, keyy + 20);
274 w.nextSymbol = WSymbol::UP;
275 w.setPosition(keyx + 26, keyy + 3);
278 w.nextSymbol = WSymbol::DOWN;
279 w.setPosition(keyx + 26, keyy + 36);
282 w.nextSymbol = WSymbol::RIGHTARROW;
283 w.setPosition(keyx + 50, keyy + 20);
286 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
288 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
289 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
291 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
292 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
294 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
295 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
297 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
298 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
300 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
301 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
303 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
304 DrawStyle red = DrawStyle(130, 0, 0);
305 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
307 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
308 w.nextSymbol = WSymbol::PLAY;
309 w.setPosition(keyx + 476, keyy + 5);
311 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
313 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
314 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
317 // Draw all the dynamic data
321 void VEpg::drawData()
323 // Not doing View::draw() every time causes
324 // things not to be cleared off the surface properly
325 // So, blank out the data area first
326 //int screenwidth=Video::getInstance()->getScreenWidth();
328 chanListbox.getRootBoxOffsetX(),
329 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
330 window_width * MINUTE_SCALE,
331 chanListbox.getHeight() + getFontHeight() + 4,
336 chanName.draw(); // TODO this should be dealt with by vvideolive
341 // Set timer to redraw to move the current time bar
345 if (dt == 0) dt = 60;
347 Timers::getInstance()->setTimerT(this, 1, dt);
350 void VEpg::timercall(int clientReference)
353 boxstack->update(this);
356 int VEpg::handleCommand(int command)
362 { // cursor up the channel list
365 boxstack->update(this);
368 case Remote::DF_DOWN:
370 { // cursor down the channel list
371 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
375 boxstack->update(this);
376 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
380 case Remote::DF_LEFT:
382 { // cursor left through time
383 selTime = thisEvent.time - 1;
385 boxstack->update(this);
388 case Remote::DF_RIGHT:
391 // cursor right through time
392 selTime = thisEvent.time + thisEvent.duration;
394 boxstack->update(this);
399 // cursor up one page
400 chanListbox.pageUp();
402 boxstack->update(this);
407 // cursor down one page
408 chanListbox.pageDown();
410 boxstack->update(this);
415 // step forward 24 hours
416 selTime += 24 * 60 * 60;
418 boxstack->update(this);
423 // step forward 24 hours
424 selTime -= 24 * 60 * 60;
426 boxstack->update(this);
431 if (!chanList) return 2;
432 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
433 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
436 boxstack->update(vs);
443 if (!chanList) return 2;
445 // select programme and display menu TODO currently just changes to selected channel
447 currentChannelIndex = chanListbox.getCurrentOption();
451 Message* m = new Message(); // Must be done after this view deleted
454 m->message = Message::CHANNEL_CHANGE;
455 m->parameter.num = (*chanList)[currentChannelIndex]->number;
456 Command::getInstance()->postMessageNoLock(m);
461 if(command == Remote::GO)
463 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
470 case Remote::CHANNELUP:
472 if (currentChannelIndex == (chanList->size() - 1)) // at the end
473 currentChannelIndex = 0;
475 ++currentChannelIndex;
479 Message* m = new Message(); // Must be done after this view deleted
482 m->message = Message::CHANNEL_CHANGE;
483 m->parameter.num = (*chanList)[currentChannelIndex]->number;
484 Command::getInstance()->postMessageNoLock(m);
491 case Remote::CHANNELDOWN:
493 if (currentChannelIndex == 0) // at the start
494 currentChannelIndex = chanList->size() - 1; // so go to end
496 --currentChannelIndex;
500 Message* m = new Message(); // Must be done after this view deleted
503 m->message = Message::CHANNEL_CHANGE;
504 m->parameter.num = (*chanList)[currentChannelIndex]->number;
505 Command::getInstance()->postMessageNoLock(m);
513 // stop command getting to any more views
517 void VEpg::drawgrid() // redraws grid and select programme
519 // draw the grid of programmes
522 time(&t); // set t = now
524 selTime = t; // don't allow cursor in the past
525 if(listTop != chanListbox.getTopOption())
527 // chanListbox has scrolled TODO speed up by changing only rows that have changed
528 listTop = chanListbox.getTopOption();
531 if ((selTime >= ltime + (int)window_width * 60) || (selTime <= ltime))
533 // we have cursored back before left time of window
534 //TODO check that this and above don't happen together
535 ltime = prevHour(&selTime);
539 DrawStyle white = DrawStyle(255, 255, 255, 255);
544 localtime_r(&t, &tms);
545 strftime(timeString, 19, "%a %d %b", &tms);
546 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
548 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
549 strftime(timeString, 19, "%H:%M", &tms);
550 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
552 rectangle(155, timey + getFontHeight(), 2, 7, white);
554 localtime_r(&t, &tms);
555 strftime(timeString, 19, "%H:%M", &tms);
556 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
557 rectangle(335, timey + getFontHeight(), 2, 7, white);
559 localtime_r(&t, &tms);
560 strftime(timeString, 19, "%H:%M", &tms);
561 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
562 rectangle(515, timey + getFontHeight(), 2, 7, white);
563 // pointer to selTime
564 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
568 if ((t >= ltime) && (t < (ltime + 9000)))
570 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
573 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
575 Event noevent; // an event to use if there are gaps in the epg
576 thisEvent.setdescription(tr("There are no programme details available for this period"));
577 thisEvent.duration = window_width * 60;
578 thisEvent.time = ltime;
579 thisEvent.settitle(tr("No programme details"));
581 bool swapColour = false; // alternate cell colour
582 bool currentRow = false;
583 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
584 DrawStyle bg, fg; // background colour of cells in grid
585 // for each displayed channel, find programmes that fall in 2.5 hour time window
586 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
588 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
589 continue; // ensure nothing populates grid below last channel
591 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
592 noevent.time = ltime;
593 noevent.duration = window_width * 60;
594 noevent.settitle("");
595 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
598 thisEvent.setdescription(tr("There are no programme details available for this period"));
599 thisEvent.duration = window_width * 60;
600 thisEvent.time = ltime;
601 thisEvent.settitle(tr("No programme details"));
604 if (eventLista[listIndex])
606 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
607 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
609 fg = DrawStyle::LIGHTTEXT;
610 event = (*eventLista[listIndex])[e];
613 UINT end = event->time + event->duration; // programme end time
614 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
615 continue; // that's enough of this channel's events
616 if(end <= UINT(ltime)) // programme ends before LHS of window
617 continue; // this event is before the window - let's try the next event
618 // this event is one we are interested in
619 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
620 swapColour = !swapColour; // it wil be the other colour next time
621 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
623 // this is the selected programme
624 thisEvent.setdescription(event->description);
625 thisEvent.duration = event->duration;
626 thisEvent.time = event->time;
627 thisEvent.settitle(event->title);
628 thisEvent.id = event->id;
629 if(thisEvent.id == 0)
631 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
632 fg = DrawStyle::DARKTEXT;
636 if (currentRow && thisEvent.id == 0)
638 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
639 thisEvent.time = end;
640 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
641 thisEvent.duration = event->time - thisEvent.time;
644 paintCell(event, y, bg, fg);
650 // no event list for this channel. Already painted noevent colour so just highlight if selected
653 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
654 fg = DrawStyle::DARKTEXT;
655 paintCell(&thisEvent, y, bg, fg);
659 bg = DrawStyle::NOPROGRAMME;
660 fg = DrawStyle::LIGHTTEXT;
661 noevent.settitle(tr("No programme details"));
662 paintCell(&noevent, y, bg, fg);
665 y += getFontHeight() + 2;
670 void VEpg::updateEventList()
672 if (!chanList) return;
674 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
676 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
678 chan = (*chanList)[listTop + listIndex];
679 if (eventLista[listIndex])
681 (eventLista)[listIndex]->clear();
682 delete eventLista[listIndex];
684 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
688 void VEpg::setCurrentChannel()
690 chanName.setText((*chanList)[currentChannelIndex]->name);
693 chanName.getRootBoxRegion(&r);
694 boxstack->update(this, &r);
697 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
700 w = x = 0; // keep compiler happy
702 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
703 UINT end = event->time + event->duration; // programme end time
704 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
706 x = 155; // LHS of window
707 if (end > (UINT(ltime) + (window_width * 60)))
708 w = window_width * MINUTE_SCALE; // spans full 2 hour window
710 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
712 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
714 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
715 w = MINUTE_SCALE * event->duration / 60;
716 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
717 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
719 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
720 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
721 rectangle(x, y, w, h, bg);
722 char* tt = new char[strlen(event->title) + 1];
723 strcpy (tt, event->title);
725 unsigned int cur_length=1;
726 unsigned int text_max=strlen(tt);
728 if (Osd::getInstance()->charSet()!=1) mchar=true;
730 memset((void*)&state,0,sizeof(state));
733 for (textPos = 0; textPos <text_max; textPos+=cur_length)
737 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
738 if (cur_length <= 0){
741 } else cur_char= *(tt+textPos);
742 float thisCharWidth = charWidth(cur_char);
743 if (textWidth + thisCharWidth > w) // text will not fit in cell
747 textWidth += thisCharWidth;
749 char* tT = new char[textPos+1];
752 strncpy(tT, tt, textPos );
754 surface->drawText(tT, x+2, y, fg);
760 time_t VEpg::prevHour(time_t* t)
763 localtime_r(t, &tms);
769 void VEpg::processMessage(Message* m)
771 if (m->message == Message::MOUSE_MOVE)
773 if (chanListbox.mouseMove((m->parameter.num>>16)-getScreenX(),(m->parameter.num&0xFFFF)-getScreenY()))
776 boxstack->update(this);
779 else if (m->message == Message::MOUSE_LBDOWN)
781 if (chanListbox.mouseLBDOWN((m->parameter.num>>16)-getScreenX(),(m->parameter.num&0xFFFF)-getScreenY()))
783 boxstack->handleCommand(Remote::OK); //simulate OK press
787 //check if press is outside this view! then simulate cancel
788 int x=(m->parameter.num>>16)-getScreenX();
789 int y=(m->parameter.num&0xFFFF)-getScreenY();
790 int keyx = chanListbox.getRootBoxOffsetX();
791 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
793 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
795 boxstack->handleCommand(Remote::BACK); //simulate cancel press
797 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
799 boxstack->handleCommand(Remote::RED);
801 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
803 boxstack->handleCommand(Remote::GREEN);
805 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
807 boxstack->handleCommand(Remote::YELLOW);
809 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
811 boxstack->handleCommand(Remote::BLUE);
813 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
815 boxstack->handleCommand(Remote::BACK);
817 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
819 boxstack->handleCommand(Remote::RECORD);
821 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
823 boxstack->handleCommand(Remote::PLAY);
825 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
827 boxstack->handleCommand(Remote::GO);
829 else if ( x>=(chanListbox.getRootBoxOffsetX())
830 && y>=(chanListbox.getRootBoxOffsetY() + 5)
831 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
832 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
833 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
836 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
837 int row=cy/(getFontHeight()+2);
838 int clistTop = chanListbox.getTopOption();
839 chanListbox.hintSetCurrent(clistTop+row);
841 time_t ttime = cx*60/MINUTE_SCALE+ltime;
842 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
846 boxstack->update(this);