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, see <https://www.gnu.org/licenses/>.
20 vepg presents a 2 dimensional electronic programme guide with channels down
21 the y axis and time along the x axis.
22 Programmes are layed on the x axis as alterate coloured blocks with as much
23 of the programme title as will fit inside the block shown as text.
24 Up and down commands step through the channels whilst left and right commands
25 move through the programmes of the currently selected channel.
26 When a programme is selected, it highlights in the grid and full programe details
27 (start time, title and description) are displayed in an area at te top left of the screen.
28 Any currently programmed timers will display in the grid and in the orogramme detail window as red
29 It is possible to select a programme to be recorded by pressing the record button.
30 The video stream currently being viewed is shown as quarter screen in the top right.
36 #include "vchannellist.h"
37 #include "messagequeue.h"
39 #include "vepgsettimer.h"
50 VEpg* VEpg::instance = NULL;
52 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ChannelList* tchanList)
55 currentChannelIndex = tcurrentChannelIndex;
57 // PAL / NTSC sizes -----------------------
60 //int summaryLines, summaryLowerPadding;
62 int fontHeight=getFontHeight() + 4;
63 //UINT gridRows; // is a class member
66 if (Video::getInstance()->getFormat() == Video::PAL)
83 //summaryLowerPadding = 28;
87 int screenwidthhalf=Video::getInstance()->getScreenWidth()/2;
88 int screenheighthalf=Video::getInstance()->getScreenHeight()/2;
89 // summaryLines = ((float)screenheighthalf)/((float)fontHeight))-1;
90 // summaryLowerPadding = screenheighthalf-summaryLines*(fontHeight);
91 gridRows = (screenheighthalf-fontHeight*3-50)/fontHeight;
94 // initialise variables and pointers
95 boxstack = BoxStack::getInstance();
101 eventLista.resize(gridRows);
102 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
104 // initialise array of pointers to eventlist structures
105 eventLista[listIndex] = NULL;
108 // Create pallet on which to paint our epg view and position it in centre of screen.
109 // Need to reduce size to deal with overscanning TVs.
111 setSize(Video::getInstance()->getScreenWidth(), Video::getInstance()->getScreenHeight());
116 // DrawStyle transparent = DrawStyle(0, 0, 0, 0);
117 // setBackgroundColour(transparent);
119 // progTitle.setSurface(surface);
120 progTitle.setPosition(0,0);
121 progTitle.setSize(screenwidthhalf,(fontHeight) * 1 + ypos); //paragraph line seperation is 4 pixels
122 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
123 progTitle.setTextPos(xpos, ypos);
125 progTitle.setParaMode(false);
128 // progInfo.setSurface(surface);
129 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
130 progInfo.setPosition(0, progTitle.getY2());
131 progInfo.setSize(screenwidthhalf,screenheighthalf-progTitle.getY2()+10);
132 progInfo.setTextPos(xpos, 0);
136 // chanName.setSurface(surface);
137 chanName.setSize(510, (fontHeight));
138 chanName.setPosition(screenwidthhalf, screenheighthalf - 2*fontHeight);
139 DrawStyle t1(0, 0, 0, 90);
140 chanName.setBackgroundColour(t1);
141 chanName.setParaMode(false);
144 // create area to display list of channels
145 // chanListbox.setSurface(surface); // add channel list
146 chanListbox.setPosition(0, progInfo.getY2() + fontHeight + 4); // position channel list
147 chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
148 chanListbox.setGap(2);
149 chanListbox.addColumn(xpos);
152 // populate channel list
157 for (UINT i = 0; i < chanList->size(); i++)
159 chan = (*chanList)[i];
160 if (i == currentChannelIndex)
162 chan->index = chanListbox.addOption(chan->name, 0, first);
165 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
168 window_x= chanListbox.getRootBoxOffsetX() + chanListbox.getWidth() + 5;
169 window_width=(Video::getInstance()->getScreenWidth() - window_x +3)/3;
171 listTop = chanListbox.getTopOption();
172 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
173 time(<ime); // set ltime to now
174 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
175 time(&selTime); // set selTime to now
176 updateEventList(); // get list of programmes
178 vdisplay.mode=Window;
179 vdisplay.fallbackMode=Quarter;
180 vdisplay.x=Video::getInstance()->getScreenWidth()/2;
182 vdisplay.width=Video::getInstance()->getScreenWidth()/2;
183 vdisplay.height=Video::getInstance()->getScreenHeight()/2;
186 void VEpg::preDelete()
188 Timers::getInstance()->cancelTimer(this, 1);
196 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
198 if (eventLista[listIndex])
200 (eventLista)[listIndex]->clear();
201 delete eventLista[listIndex];
204 // delete [] eventLista; // FIXME
206 // destroy dynamically allocated memory
209 VEpg* VEpg::getInstance()
214 void VEpg::setInfo(Event* event)
217 struct tm btime; // to hold programme start and end time
218 char timeString[9]; // to hold programme start and end time
219 int length = strlen(event->title); // calculate length of programme title string
220 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
221 time_t eventtime = event->time;
222 LOCALTIME_R((time_t*)&eventtime, &btime); //get programme start time
224 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
226 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
228 strcpy(title, timeString); // put it in our buffer
229 t = event->time + event->duration; //get programme end time
230 LOCALTIME_R(&t, &btime);
232 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
234 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
236 strcat(title, timeString); // put it in our buffer
238 strcat(title, event->title); // then add the programme title
239 progTitle.setText(title); // sput this sring in our text box
240 length = strlen(event->description);
241 char* info = new char[length + 1]; // create programme detail string
242 strcpy(info, event->description);
243 progInfo.setText(info); // show programme detail string
244 // destroy dynamically allocated memory
251 // View::draw(); // draw pallet
253 DrawStyle transparent = DrawStyle(0, 0, 0, 0);
254 fillColour(transparent);
257 // Moved all the dynamic data drawing to a seperate function
259 // Display the status and key stuff at the bottom
261 UINT keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
262 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
263 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
264 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
269 w.nextSymbol = WSymbol::LEFTARROW;
270 w.setPosition(keyx + 1, keyy + 20);
273 w.nextSymbol = WSymbol::UP;
274 w.setPosition(keyx + 26, keyy + 3);
277 w.nextSymbol = WSymbol::DOWN;
278 w.setPosition(keyx + 26, keyy + 36);
281 w.nextSymbol = WSymbol::RIGHTARROW;
282 w.setPosition(keyx + 50, keyy + 20);
285 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
287 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
288 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
290 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
291 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
293 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
294 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
296 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
297 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
299 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
300 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
302 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
303 DrawStyle red = DrawStyle(130, 0, 0);
304 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
306 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
307 w.nextSymbol = WSymbol::PLAY;
308 w.setPosition(keyx + 476, keyy + 5);
310 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
312 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
313 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
316 // Draw all the dynamic data
320 void VEpg::drawData()
322 // Not doing View::draw() every time causes
323 // things not to be cleared off the surface properly
324 // So, blank out the data area first
325 //int screenwidth=Video::getInstance()->getScreenWidth();
327 chanListbox.getRootBoxOffsetX(),
328 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
329 window_width * MINUTE_SCALE,
330 chanListbox.getHeight() + getFontHeight() + 4,
335 chanName.draw(); // TODO this should be dealt with by vvideolive
340 // Set timer to redraw to move the current time bar
344 if (dt == 0) dt = 60;
346 Timers::getInstance()->setTimerT(this, 1, dt);
349 void VEpg::timercall(int clientReference)
352 boxstack->update(this);
355 int VEpg::handleCommand(int command)
361 { // cursor up the channel list
364 boxstack->update(this);
367 case Remote::DF_DOWN:
369 { // cursor down the channel list
370 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
374 boxstack->update(this);
375 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
379 case Remote::DF_LEFT:
381 { // cursor left through time
382 selTime = thisEvent.time - 1;
384 boxstack->update(this);
387 case Remote::DF_RIGHT:
390 // cursor right through time
391 selTime = thisEvent.time + thisEvent.duration;
393 boxstack->update(this);
398 // cursor up one page
399 chanListbox.pageUp();
401 boxstack->update(this);
406 // cursor down one page
407 chanListbox.pageDown();
409 boxstack->update(this);
414 // step forward 24 hours
415 selTime += 24 * 60 * 60;
417 boxstack->update(this);
422 // step forward 24 hours
423 selTime -= 24 * 60 * 60;
425 boxstack->update(this);
430 if (!chanList) return 2;
431 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
432 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
435 boxstack->update(vs);
442 if (!chanList) return 2;
444 // select programme and display menu TODO currently just changes to selected channel
446 currentChannelIndex = chanListbox.getCurrentOption();
450 Message* m = new Message(); // Must be done after this view deleted
453 m->message = Message::CHANNEL_CHANGE;
454 m->parameter = (*chanList)[currentChannelIndex]->number;
455 MessageQueue::getInstance()->postMessage(m);
460 if(command == Remote::GO)
462 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
469 case Remote::CHANNELUP:
471 if (currentChannelIndex == (chanList->size() - 1)) // at the end
472 currentChannelIndex = 0;
474 ++currentChannelIndex;
478 Message* m = new Message(); // Must be done after this view deleted
481 m->message = Message::CHANNEL_CHANGE;
482 m->parameter = (*chanList)[currentChannelIndex]->number;
483 MessageQueue::getInstance()->postMessage(m);
490 case Remote::CHANNELDOWN:
492 if (currentChannelIndex == 0) // at the start
493 currentChannelIndex = chanList->size() - 1; // so go to end
495 --currentChannelIndex;
499 Message* m = new Message(); // Must be done after this view deleted
502 m->message = Message::CHANNEL_CHANGE;
503 m->parameter = (*chanList)[currentChannelIndex]->number;
504 MessageQueue::getInstance()->postMessage(m);
512 // stop command getting to any more views
516 void VEpg::drawgrid() // redraws grid and select programme
518 // draw the grid of programmes
521 time(&t); // set t = now
523 selTime = t; // don't allow cursor in the past
524 if(listTop != chanListbox.getTopOption())
526 // chanListbox has scrolled TODO speed up by changing only rows that have changed
527 listTop = chanListbox.getTopOption();
530 if ((selTime >= ltime + (int)window_width * 60) || (selTime <= ltime))
532 // we have cursored back before left time of window
533 //TODO check that this and above don't happen together
534 ltime = prevHour(&selTime);
538 DrawStyle white = DrawStyle(255, 255, 255, 255);
543 LOCALTIME_R(&t, &tms);
544 strftime(timeString, 19, "%a %d %b", &tms);
545 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
547 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
548 strftime(timeString, 19, "%H:%M", &tms);
549 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
551 rectangle(155, timey + getFontHeight(), 2, 7, white);
553 LOCALTIME_R(&t, &tms);
554 strftime(timeString, 19, "%H:%M", &tms);
555 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
556 rectangle(335, timey + getFontHeight(), 2, 7, white);
558 LOCALTIME_R(&t, &tms);
559 strftime(timeString, 19, "%H:%M", &tms);
560 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
561 rectangle(515, timey + getFontHeight(), 2, 7, white);
562 // pointer to selTime
563 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
567 if ((t >= ltime) && (t < (ltime + 9000)))
569 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
572 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
574 Event noevent; // an event to use if there are gaps in the epg
575 thisEvent.setdescription(tr("There are no programme details available for this period"));
576 thisEvent.duration = window_width * 60;
577 thisEvent.time = ltime;
578 thisEvent.settitle(tr("No programme details"));
580 bool swapColour = false; // alternate cell colour
581 bool currentRow = false;
582 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
583 DrawStyle bg, fg; // background colour of cells in grid
584 // for each displayed channel, find programmes that fall in 2.5 hour time window
585 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
587 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
588 continue; // ensure nothing populates grid below last channel
590 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
591 noevent.time = ltime;
592 noevent.duration = window_width * 60;
593 noevent.settitle("");
594 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
597 thisEvent.setdescription(tr("There are no programme details available for this period"));
598 thisEvent.duration = window_width * 60;
599 thisEvent.time = ltime;
600 thisEvent.settitle(tr("No programme details"));
603 if (eventLista[listIndex])
605 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
606 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
608 fg = DrawStyle::LIGHTTEXT;
609 event = (*eventLista[listIndex])[e];
612 UINT end = event->time + event->duration; // programme end time
613 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
614 continue; // that's enough of this channel's events
615 if(end <= UINT(ltime)) // programme ends before LHS of window
616 continue; // this event is before the window - let's try the next event
617 // this event is one we are interested in
618 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
619 swapColour = !swapColour; // it wil be the other colour next time
620 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
622 // this is the selected programme
623 thisEvent.setdescription(event->description);
624 thisEvent.duration = event->duration;
625 thisEvent.time = event->time;
626 thisEvent.settitle(event->title);
627 thisEvent.id = event->id;
628 if(thisEvent.id == 0)
630 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
631 fg = DrawStyle::DARKTEXT;
635 if (currentRow && thisEvent.id == 0)
637 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
638 thisEvent.time = end;
639 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
640 thisEvent.duration = event->time - thisEvent.time;
643 paintCell(event, y, bg, fg);
649 // no event list for this channel. Already painted noevent colour so just highlight if selected
652 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
653 fg = DrawStyle::DARKTEXT;
654 paintCell(&thisEvent, y, bg, fg);
658 bg = DrawStyle::NOPROGRAMME;
659 fg = DrawStyle::LIGHTTEXT;
660 noevent.settitle(tr("No programme details"));
661 paintCell(&noevent, y, bg, fg);
664 y += getFontHeight() + 2;
669 void VEpg::updateEventList()
671 if (!chanList) return;
673 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
675 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
677 chan = (*chanList)[listTop + listIndex];
678 if (eventLista[listIndex])
680 (eventLista)[listIndex]->clear();
681 delete eventLista[listIndex];
683 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
687 void VEpg::setCurrentChannel()
689 chanName.setText((*chanList)[currentChannelIndex]->name);
692 chanName.getRootBoxRegion(&r);
693 boxstack->update(this, &r);
696 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
699 w = x = 0; // keep compiler happy
701 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
702 UINT end = event->time + event->duration; // programme end time
703 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
705 x = 155; // LHS of window
706 if (end > (UINT(ltime) + (window_width * 60)))
707 w = window_width * MINUTE_SCALE; // spans full 2 hour window
709 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
711 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
713 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
714 w = MINUTE_SCALE * event->duration / 60;
715 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
716 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
718 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
719 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
720 rectangle(x, y, w, h, bg);
721 char* tt = new char[strlen(event->title) + 1];
722 strcpy (tt, event->title);
724 unsigned int cur_length=1;
725 unsigned int text_max=strlen(tt);
727 if (Osd::getInstance()->charSet()!=1) mchar=true;
729 memset((void*)&state,0,sizeof(state));
732 for (textPos = 0; textPos <text_max; textPos+=cur_length)
736 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
737 if (cur_length <= 0){
740 } else cur_char= *(tt+textPos);
741 float thisCharWidth = charWidth(cur_char);
742 if (textWidth + thisCharWidth > w) // text will not fit in cell
746 textWidth += thisCharWidth;
748 char* tT = new char[textPos+1];
751 strncpy(tT, tt, textPos );
753 surface->drawText(tT, x+2, y, fg);
759 time_t VEpg::prevHour(time_t* t)
762 LOCALTIME_R(t, &tms);
768 void VEpg::processMessage(Message* m)
770 if (m->message == Message::MOUSE_MOVE)
772 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
775 boxstack->update(this);
778 else if (m->message == Message::MOUSE_LBDOWN)
780 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
782 boxstack->handleCommand(Remote::OK); //simulate OK press
786 //check if press is outside this view! then simulate cancel
787 int x=(m->parameter>>16)-getScreenX();
788 int y=(m->parameter&0xFFFF)-getScreenY();
789 int keyx = chanListbox.getRootBoxOffsetX();
790 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
792 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
794 boxstack->handleCommand(Remote::BACK); //simulate cancel press
796 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
798 boxstack->handleCommand(Remote::RED);
800 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
802 boxstack->handleCommand(Remote::GREEN);
804 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
806 boxstack->handleCommand(Remote::YELLOW);
808 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
810 boxstack->handleCommand(Remote::BLUE);
812 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
814 boxstack->handleCommand(Remote::BACK);
816 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
818 boxstack->handleCommand(Remote::RECORD);
820 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
822 boxstack->handleCommand(Remote::PLAY);
824 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
826 boxstack->handleCommand(Remote::GO);
828 else if ( x>=(chanListbox.getRootBoxOffsetX())
829 && y>=(chanListbox.getRootBoxOffsetY() + 5)
830 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
831 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
832 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
835 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
836 int row=cy/(getFontHeight()+2);
837 int clistTop = chanListbox.getTopOption();
838 chanListbox.hintSetCurrent(clistTop+row);
840 time_t ttime = cx*60/MINUTE_SCALE+ltime;
841 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
845 boxstack->update(this);