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.
34 #include "vchannellist.h"
35 #include "messagequeue.h"
37 #include "vepgsettimer.h"
48 VEpg* VEpg::instance = NULL;
50 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ChannelList* tchanList)
53 currentChannelIndex = tcurrentChannelIndex;
55 // PAL / NTSC sizes -----------------------
58 //int summaryLines, summaryLowerPadding;
60 int fontHeight=getFontHeight() + 4;
61 //UINT gridRows; // is a class member
64 if (Video::getInstance()->getFormat() == Video::PAL)
81 //summaryLowerPadding = 28;
85 int screenwidthhalf=Video::getInstance()->getScreenWidth()/2;
86 int screenheighthalf=Video::getInstance()->getScreenHeight()/2;
87 // summaryLines = ((float)screenheighthalf)/((float)fontHeight))-1;
88 // summaryLowerPadding = screenheighthalf-summaryLines*(fontHeight);
89 gridRows = (screenheighthalf-fontHeight*3-50)/fontHeight;
92 // initialise variables and pointers
93 boxstack = BoxStack::getInstance();
99 eventLista.resize(gridRows);
100 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
102 // initialise array of pointers to eventlist structures
103 eventLista[listIndex] = NULL;
106 // Create pallet on which to paint our epg view and position it in centre of screen.
107 // Need to reduce size to deal with overscanning TVs.
109 setSize(Video::getInstance()->getScreenWidth(), Video::getInstance()->getScreenHeight());
114 // DrawStyle transparent = DrawStyle(0, 0, 0, 0);
115 // setBackgroundColour(transparent);
117 // progTitle.setSurface(surface);
118 progTitle.setPosition(0,0);
119 progTitle.setSize(screenwidthhalf,(fontHeight) * 1 + ypos); //paragraph line seperation is 4 pixels
120 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
121 progTitle.setTextPos(xpos, ypos);
123 progTitle.setParaMode(false);
126 // progInfo.setSurface(surface);
127 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
128 progInfo.setPosition(0, progTitle.getY2());
129 progInfo.setSize(screenwidthhalf,screenheighthalf-progTitle.getY2()+10);
130 progInfo.setTextPos(xpos, 0);
134 // chanName.setSurface(surface);
135 chanName.setSize(510, (fontHeight));
136 chanName.setPosition(screenwidthhalf, screenheighthalf - 2*fontHeight);
137 DrawStyle t1(0, 0, 0, 90);
138 chanName.setBackgroundColour(t1);
139 chanName.setParaMode(false);
142 // create area to display list of channels
143 // chanListbox.setSurface(surface); // add channel list
144 chanListbox.setPosition(0, progInfo.getY2() + fontHeight + 4); // position channel list
145 chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
146 chanListbox.setGap(2);
147 chanListbox.addColumn(xpos);
150 // populate channel list
155 for (UINT i = 0; i < chanList->size(); i++)
157 chan = (*chanList)[i];
158 if (i == currentChannelIndex)
160 chan->index = chanListbox.addOption(chan->name, 0, first);
163 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
166 window_x= chanListbox.getRootBoxOffsetX() + chanListbox.getWidth() + 5;
167 window_width=(Video::getInstance()->getScreenWidth() - window_x +3)/3;
169 listTop = chanListbox.getTopOption();
170 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
171 time(<ime); // set ltime to now
172 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
173 time(&selTime); // set selTime to now
174 updateEventList(); // get list of programmes
176 vdisplay.mode=Window;
177 vdisplay.fallbackMode=Quarter;
178 vdisplay.x=Video::getInstance()->getScreenWidth()/2;
180 vdisplay.width=Video::getInstance()->getScreenWidth()/2;
181 vdisplay.height=Video::getInstance()->getScreenHeight()/2;
184 void VEpg::preDelete()
186 Timers::getInstance()->cancelTimer(this, 1);
194 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
196 if (eventLista[listIndex])
198 (eventLista)[listIndex]->clear();
199 delete eventLista[listIndex];
202 // delete [] eventLista; // FIXME
204 // destroy dynamically allocated memory
207 VEpg* VEpg::getInstance()
212 void VEpg::setInfo(Event* event)
215 struct tm btime; // to hold programme start and end time
216 char timeString[9]; // to hold programme start and end time
217 int length = strlen(event->title.c_str()); // calculate length of programme title string
218 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
219 time_t eventtime = event->time;
220 LOCALTIME_R(&eventtime, &btime); //get programme start time
222 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
224 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
226 strcpy(title, timeString); // put it in our buffer
227 t = event->time + event->duration; //get programme end time
228 LOCALTIME_R(&t, &btime);
230 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
232 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
234 strcat(title, timeString); // put it in our buffer
236 strcat(title, event->title.c_str()); // then add the programme title
237 progTitle.setText(title); // sput this sring in our text box
238 length = event->description.length();
239 char* info = new char[length + 1]; // create programme detail string
240 strcpy(info, event->description.c_str());
241 progInfo.setText(info); // show programme detail string
242 // destroy dynamically allocated memory
249 // View::draw(); // draw pallet
251 DrawStyle transparent = DrawStyle(0, 0, 0, 0);
252 fillColour(transparent);
255 // Moved all the dynamic data drawing to a seperate function
257 // Display the status and key stuff at the bottom
259 UINT keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
260 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
261 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
262 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
267 w.nextSymbol = WSymbol::LEFTARROW;
268 w.setPosition(keyx + 1, keyy + 20);
271 w.nextSymbol = WSymbol::UP;
272 w.setPosition(keyx + 26, keyy + 3);
275 w.nextSymbol = WSymbol::DOWN;
276 w.setPosition(keyx + 26, keyy + 36);
279 w.nextSymbol = WSymbol::RIGHTARROW;
280 w.setPosition(keyx + 50, keyy + 20);
283 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
285 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
286 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
288 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
289 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
291 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
292 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
294 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
295 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
297 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
298 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
300 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
301 DrawStyle red = DrawStyle(130, 0, 0);
302 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
304 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
305 w.nextSymbol = WSymbol::PLAY;
306 w.setPosition(keyx + 476, keyy + 5);
308 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
310 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
311 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
314 // Draw all the dynamic data
318 void VEpg::drawData()
320 // Not doing View::draw() every time causes
321 // things not to be cleared off the surface properly
322 // So, blank out the data area first
323 //int screenwidth=Video::getInstance()->getScreenWidth();
325 chanListbox.getRootBoxOffsetX(),
326 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
327 window_width * MINUTE_SCALE,
328 chanListbox.getHeight() + getFontHeight() + 4,
333 chanName.draw(); // TODO this should be dealt with by vvideolive
338 // Set timer to redraw to move the current time bar
342 if (dt == 0) dt = 60;
344 Timers::getInstance()->setTimerT(this, 1, dt);
347 void VEpg::timercall(int /*clientReference*/)
350 boxstack->update(this);
353 int VEpg::handleCommand(int command)
358 { // cursor up the channel list
361 boxstack->update(this);
365 { // cursor down the channel list
366 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
370 boxstack->update(this);
371 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
376 { // cursor left through time
377 selTime = thisEvent.time - 1;
379 boxstack->update(this);
384 // cursor right through time
385 selTime = thisEvent.time + thisEvent.duration;
387 boxstack->update(this);
392 // cursor up one page
393 chanListbox.pageUp();
395 boxstack->update(this);
400 // cursor down one page
401 chanListbox.pageDown();
403 boxstack->update(this);
408 // step forward 24 hours
409 selTime += 24 * 60 * 60;
411 boxstack->update(this);
416 // step forward 24 hours
417 selTime -= 24 * 60 * 60;
419 boxstack->update(this);
424 if (!chanList) return 2;
425 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
426 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
429 boxstack->update(vs);
436 if (!chanList) return 2;
438 // select programme and display menu TODO currently just changes to selected channel
440 currentChannelIndex = chanListbox.getCurrentOption();
444 Message* m = new Message(); // Must be done after this view deleted
447 m->message = Message::CHANNEL_CHANGE;
448 m->parameter = (*chanList)[currentChannelIndex]->number;
449 MessageQueue::getInstance()->postMessage(m);
454 if(command == Input::GO)
456 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
463 case Input::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 MessageQueue::getInstance()->postMessage(m);
484 case Input::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 MessageQueue::getInstance()->postMessage(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 + (int)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 LOCALTIME_R(&t, &tms);
538 strftime(timeString, 19, "%a %d %b", &tms);
539 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
541 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
542 strftime(timeString, 19, "%H:%M", &tms);
543 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
545 rectangle(155, timey + getFontHeight(), 2, 7, white);
547 LOCALTIME_R(&t, &tms);
548 strftime(timeString, 19, "%H:%M", &tms);
549 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
550 rectangle(335, timey + getFontHeight(), 2, 7, white);
552 LOCALTIME_R(&t, &tms);
553 strftime(timeString, 19, "%H:%M", &tms);
554 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
555 rectangle(515, timey + getFontHeight(), 2, 7, white);
556 // pointer to selTime
557 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
561 if ((t >= ltime) && (t < (ltime + 9000)))
563 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
566 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
568 Event noevent; // an event to use if there are gaps in the epg
569 thisEvent.description = tr("There are no programme details available for this period");
570 thisEvent.duration = window_width * 60;
571 thisEvent.time = ltime;
572 thisEvent.title = tr("No programme details");
574 bool swapColour = false; // alternate cell colour
575 bool currentRow = false;
576 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
577 DrawStyle bg, fg; // background colour of cells in grid
578 // for each displayed channel, find programmes that fall in 2.5 hour time window
579 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
581 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
582 continue; // ensure nothing populates grid below last channel
584 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
585 noevent.time = ltime;
586 noevent.duration = window_width * 60;
588 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
591 thisEvent.description = tr("There are no programme details available for this period");
592 thisEvent.duration = window_width * 60;
593 thisEvent.time = ltime;
594 thisEvent.title = tr("No programme details");
597 if (eventLista[listIndex])
599 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
600 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
602 fg = DrawStyle::LIGHTTEXT;
603 event = (*eventLista[listIndex])[e];
606 UINT end = event->time + event->duration; // programme end time
607 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
608 continue; // that's enough of this channel's events
609 if(end <= UINT(ltime)) // programme ends before LHS of window
610 continue; // this event is before the window - let's try the next event
611 // this event is one we are interested in
612 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
613 swapColour = !swapColour; // it wil be the other colour next time
614 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
616 // this is the selected programme
617 thisEvent.description = event->description;
618 thisEvent.duration = event->duration;
619 thisEvent.time = event->time;
620 thisEvent.title = event->title;
621 thisEvent.id = event->id;
622 if(thisEvent.id == 0)
624 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
625 fg = DrawStyle::DARKTEXT;
629 if (currentRow && thisEvent.id == 0)
631 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
632 thisEvent.time = end;
633 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
634 thisEvent.duration = event->time - thisEvent.time;
637 paintCell(event, y, bg, fg);
643 // no event list for this channel. Already painted noevent colour so just highlight if selected
646 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
647 fg = DrawStyle::DARKTEXT;
648 paintCell(&thisEvent, y, bg, fg);
652 bg = DrawStyle::NOPROGRAMME;
653 fg = DrawStyle::LIGHTTEXT;
654 noevent.title = tr("No programme details");
655 paintCell(&noevent, y, bg, fg);
658 y += getFontHeight() + 2;
663 void VEpg::updateEventList()
665 if (!chanList) return;
667 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
669 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
671 chan = (*chanList)[listTop + listIndex];
672 if (eventLista[listIndex])
674 (eventLista)[listIndex]->clear();
675 delete eventLista[listIndex];
677 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
681 void VEpg::setCurrentChannel()
683 chanName.setText((*chanList)[currentChannelIndex]->name);
686 chanName.getRootBoxRegion(&r);
687 boxstack->update(this, &r);
690 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
693 w = x = 0; // keep compiler happy
695 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
696 UINT end = event->time + event->duration; // programme end time
697 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
699 x = 155; // LHS of window
700 if (end > (UINT(ltime) + (window_width * 60)))
701 w = window_width * MINUTE_SCALE; // spans full 2 hour window
703 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
705 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
707 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
708 w = MINUTE_SCALE * event->duration / 60;
709 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
710 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
712 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
713 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
714 rectangle(x, y, w, h, bg);
715 char* tt = new char[strlen(event->title.c_str()) + 1];
716 strcpy (tt, event->title.c_str());
718 unsigned int cur_length=1;
719 unsigned int text_max=strlen(tt);
721 if (Osd::getInstance()->charSet()!=1) mchar=true;
723 memset((void*)&state,0,sizeof(state));
726 for (textPos = 0; textPos <text_max; textPos+=cur_length)
730 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
731 if (cur_length <= 0){
734 } else cur_char= *(tt+textPos);
735 float thisCharWidth = charWidth(cur_char);
736 if (textWidth + thisCharWidth > w) // text will not fit in cell
740 textWidth += thisCharWidth;
742 char* tT = new char[textPos+1];
745 strncpy(tT, tt, textPos );
747 surface->drawText(tT, x+2, y, fg);
753 time_t VEpg::prevHour(time_t* t)
756 LOCALTIME_R(t, &tms);
762 void VEpg::processMessage(Message* m)
764 if (m->message == Message::MOUSE_MOVE)
766 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
769 boxstack->update(this);
772 else if (m->message == Message::MOUSE_LBDOWN)
774 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
776 boxstack->handleCommand(Input::OK); //simulate OK press
780 //check if press is outside this view! then simulate cancel
781 int x=(m->parameter>>16)-getScreenX();
782 int y=(m->parameter&0xFFFF)-getScreenY();
783 int keyx = chanListbox.getRootBoxOffsetX();
784 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
786 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
788 boxstack->handleCommand(Input::BACK); //simulate cancel press
790 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
792 boxstack->handleCommand(Input::RED);
794 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
796 boxstack->handleCommand(Input::GREEN);
798 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
800 boxstack->handleCommand(Input::YELLOW);
802 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
804 boxstack->handleCommand(Input::BLUE);
806 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
808 boxstack->handleCommand(Input::BACK);
810 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
812 boxstack->handleCommand(Input::RECORD);
814 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
816 boxstack->handleCommand(Input::PLAY);
818 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
820 boxstack->handleCommand(Input::GO);
822 else if ( x>=(chanListbox.getRootBoxOffsetX())
823 && y>=(chanListbox.getRootBoxOffsetY() + 5)
824 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
825 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
826 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
829 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
830 int row=cy/(getFontHeight()+2);
831 int clistTop = chanListbox.getTopOption();
832 chanListbox.hintSetCurrent(clistTop+row);
834 time_t ttime = cx*60/MINUTE_SCALE+ltime;
835 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
839 boxstack->update(this);